Java知識點回顧(基礎、併發、虛擬機)

一、JAVA基礎

———————————————集合——————————————–

JAVA集合分爲
set (無序不重複),
list(有序可重複),
map(鍵值對),
queue(隊列)

數組保存的是定長的數據,集合保存的是不確定的數據
數組元素可以基本類型的值和對象,集合只能保存對象

Java的集合類主要由兩個接口派生而出:
Collection和Map,Collection和Map是Java集合框架的根接口。

———————————————泛型——————————————

一、泛型的目的
避免在運行時強制類型轉換而出現ClassCastException,即類型轉換異常。

二、泛型的使用
1.泛型類和泛型接口
public class ArrayList<E> extends AbstractList<E> implements List<E> {
}
public interface List<E> extends Collection<E> {
}
2.泛型的方法
public E getkey() {
        return key;
    }
3.泛型構造器
public class Person {
    public <T> Person(T t) {
        System.out.println(t);
    }
}


三、類型通配符
問號(?)被成爲通配符,它的元素類型可以匹配任何類型。
1.上限通配符
它表示集合中的所有元素都是Shape類型或者其子類
List<? extends Shape>
2.下限通配符
它表示集合中的所有元素都是Circle類型或者其父類
List <? super Circle>

———————————————–反射———————————————-

Java反射機制定義
運行狀態中,獲取任意一個類的所有屬性和方法

Java 反射機制的應用場景
1、反編譯
2、框架編寫

———————————————-註解—————————————–

註解(元數據)的定義:
註解即元數據,元數據是對方法、字段、類的描述

註解的作用:
1、跟蹤代碼中的依賴性
2、代碼編譯檢查
3、使代碼更加簡潔

————————————-JAVA IO————————————-

在Java中有輸入、輸出兩種IO流,每種輸入、輸出流又分爲字節流和字符流兩大類。

Java中把不同的輸入/輸出源抽象表述爲"流"。
流是一組有順序的,有起點和終點的字節集合,是對數據傳輸的總稱或抽象。

流的本質是數據傳輸,根據數據傳輸特性將流抽象爲各種類,方便更直觀的進行數據操作。

輸入時是流從數據源流向程序。
輸出時是流從程序傳向數據源,
而數據源可以是內存,文件,網絡或程序等。

關於字節,每個字節(byte)有8bit(位)組成,每種數據類型又幾個字節組成
關於字符,我們可能知道代表一個漢字或者英文字母。

Java採用unicode編碼,2個字節來表示一個字符,一箇中文或英文字符的unicode編碼都佔2個字節

輸入流:InputStream(字節流)、Reader(字符流)
輸出流:OutputStream(字節流)、Writer(字符流)

———————————RandomAccessFile———————————-

RandomAccessFile既可以讀取文件內容,也可以向文件輸出數據。

同時,RandomAccessFile支持“隨機訪問”的方式

程序可以直接跳轉到文件的任意地方來讀寫數據。

應用場景:
1、斷點續傳
2、結合線程池多線程分段下載單個文件

———————————— JAVANIO————————————-

定義:
Java NIO(New IO)是一個可以替代標準Java IO API的IO API(從Java1.4開始)
Java NIO提供了與標準IO不同的IO工作方式。

標準的IO基於字節流和字符流進行操作的,
而NIO是基於通道(Channel)和緩衝區(Buffer)進行操作,
數據總是從通道讀取到緩衝區中,或者從緩衝區寫入通道也類似。

Java NIO 由以下幾個核心部分組成:
Buffer(利用Buffer讀寫數據)
Channel(通道)和流非常相似
Selector 檢查NIO Channel是否處於可讀、可寫。

—————————————-Java異常—————————————–

Java異常是Java提供的一種識別及響應錯誤的一致性機制。

Java異常機制可以使程序中異常處理代碼和正常業務代碼分離,保證程序代碼更加優雅,並提高程序健壯性

Java異常機制用到的幾個關鍵字:trycatchfinallythrowthrows

————————————– 抽象類和接口———————————-

abstract class在Java語言中表示的是一種繼承關係,一個類只能使用一次繼承關係。但是,一個類卻可以實現多個interface。

接口和抽象類的概念不一樣。接口是對動作的抽象,抽象類是對根源的抽象。從設計理念上,接口反映的是 “like-a” 關係,抽象類反映的是 “is-a” 關係。 抽象類表示的是,這個對象是什麼。接口表示的是,這個對象能做什麼

即:
abstract class (名詞),被繼承
interface(動詞),被實現

————————————深拷貝和淺拷貝———————————–

Java中有三種類型的對象拷貝:
淺拷貝(Shallow Copy)、深拷貝(Deep Copy)、延遲拷貝(Lazy Copy)。

什麼是淺拷貝

淺拷貝是按位拷貝對象,它會創建一個新對象,這個對象有着原始對象屬性值的一份精確拷貝。

如果屬性是基本類型,拷貝的就是基本類型的值;
如果屬性是內存地址(引用類型),拷貝的就是內存地址 ,
因此如果其中一個對象改變了這個地址,就會影響到另一個對象。

什麼是深拷貝

深拷貝會拷貝所有的屬性,並拷貝屬性指向的動態分配的內存。
當對象和它所引用的對象一起拷貝時即發生深拷貝。深拷貝相比於淺拷貝速度較慢並且花銷較大。

簡單理解一:

在有指針的情況下,淺拷貝只是增加了一個指針指向已經存在的內存,而深拷貝就是增加一個指針並且申請一個新的內存,使這個增加的指針指向這個新的內存,採用深拷貝的情況下,釋放內存的時候就不會出現在淺拷貝時重複釋放同一內存的錯誤!

簡單理解二 :

淺拷貝 只拷貝指針,深拷貝就是拷貝他的值,重新生成的對像。就像是淺拷貝就是你的影子,深拷貝是你的克隆人,你沒了影子也就沒了,但是克隆人還活着。

————————————— transient———————————

對象序列號需要實現Serilizable接口
將不需要序列化的屬性前添加關鍵字transient,該屬性就不會序列化

拓展:
我們知道在Java中,對象的序列化可以通過實現兩種接口來實現,若實現的是Serializable接口,則所有的序列化將會自動進行。

若實現的是Externalizable接口,則沒有任何東西可以自動序列化,需要在writeExternal方法中進行手工指定所要序列化的變量,這與是否被transient修飾無關。

因此transient只適用於Serializable接口序列化方式

—————————–Java finally與return執行順序———————–

總結:
1finally語句在return語句執行之後return返回之前執行的。
2finally塊中的return語句會覆蓋try塊中的return返回。
3、 如果finally語句中沒有return語句覆蓋返回值,那麼原來的返回值可能因爲finally裏的修改而改變也可能不變。

最後總結:
finally塊的語句在trycatch中的return語句執行之後返回之前執行且finally裏的修改語句可能影響也可能不影響trycatchreturn已經確定的返回值,若finally裏也有return語句則覆蓋trycatch中的return語句直接返回。

————————————Java8新特性———————————–

Lambda表達式和函數式接口
接口的默認方法和靜態方法
方法引用
重複註解
更好的類型推斷
拓寬註解的應用場景

二、JAVA併發

——————————-Java創建線程的三種方式—————————-

1、繼承Thread2、實現Runnable接口
3、通過Callable和Future創建線程

創建線程的三種方式的對比

採用實現Runnable、Callable接口的方式創見多線程時,

優勢是:
線程類只是實現了Runnable接口或Callable接口,還可以繼承其他類。
在這種方式下,多個線程可以共享同一個target對象,所以非常適合多個相同線程來處理同一份資源的情況,從而可以將CPU、代碼和數據分開,形成清晰的模型,較好地體現了面向對象的思想。

劣勢是:
編程稍微複雜,如果要訪問當前線程,則必須使用Thread.currentThread()方法。

使用繼承Thread類的方式創建多線程時

優勢是:
編寫簡單,如果需要訪問當前線程,則無需使用Thread.currentThread()方法,直接使用this即可獲得當前線程。

劣勢是:
線程類已經繼承了Thread類,所以不能再繼承其他父類。

——————————————Java線程池———————————

定義:
在Android中,由於主線程的諸多限制,像網絡請求等一些耗時的操作我們必須在子線程中運行。

我們往往會通過new Thread來開啓一個子線程,待子線程操作完成以後通過Handler切換到主線程中運行。

這麼以來我們無法管理我們所創建的子線程,並且無限制的創建子線程,它們相互之間競爭,很有可能由於佔用過多資源而導致死機或者OOM。

所以在Java中爲我們提供了線程池來管理我們所創建的線程。

線程池的優勢

①降低系統資源消耗,通過重用已存在的線程,降低線程創建和銷燬造成的消耗;
②提高系統響應速度,當有任務到達時,無需等待新線程的創建便能立即執行;
③方便線程併發數的管控,線程若是無限制的創建,不僅會額外消耗大量系統資源,更是佔用過多資源而阻塞系統或oom等狀況,從而降低系統的穩定性。線程池能有效管控線程,統一分配、調優,提供資源使用率;
④更強大的功能,線程池提供了定時、定期以及可控線程數等功能的線程池,使用方便簡單。

關鍵字:ThreadPoolExecutor

四種線程池類
1. newFixedThreadPool(數量固定,都是核心線程)

2.newCachedThreadPool(緩存,核心線程數爲0,都是非核心線程)

3. newScheduledThreadPool(固定核心線程,非核心線程無限制)

4. newSingleThreadExecutor(單核心線程,一個一個任務排隊等待)

—————————————死鎖———————————————

在JAVA編程中,有3種典型的死鎖類型:
靜態的鎖順序死鎖(兩個線程,A方法等B方法,B方法等A方法)
動態的鎖順序死鎖(兩個線程調用同一個方法時,入參顛倒造成的死鎖)
協作對象之間發生的死鎖。(線程1持有A對象鎖並等待B對象鎖,線程2持有B對象鎖並等待A對象鎖。)

避免死鎖:
在寫代碼時,要確保線程在獲取多個鎖時採用一致的順序。同時,要避免在持有鎖的情況下調用外部方法。

—————————————線程同步—————————————-

Java允許多線程併發控制,當多個線程同時操作一個可共享的資源變量時
(如數據的增刪改查),將會導致數據不準確,相互之間產生衝突。

加入同步鎖以避免在該線程沒有完成操作之前,被其他線程的調用,從而保證了該變量的唯一性和準確性。
一共有兩種鎖,來實現線程同步問題,分別是:synchronized和ReentrantLock

synchronized實現同步的基礎:
Java中每個對象都可以作爲鎖。當線程試圖訪問同步代碼時,必須先獲得對象鎖,退出或拋出異常時必須釋放鎖。

Synchronzied實現同步的表現形式分爲:代碼塊同步 和 方法同步。

ReentrantLock,一個可重入的互斥鎖,它具有與使用synchronized方法和語句所訪問的隱式監視器鎖相同的一些基本行爲和語義,但功能更強大。

————————————–線程間通信的兩種方式———————————–

Object類中相關的方法有兩個:
notify方法和三個wait方法
拓展:
http://blog.csdn.net/yang_teng_/article/details/53325280

————————————–volatile關鍵字———————————-

當一個變量定義爲 volatile 之後,將具備兩種特性:

1.保證此變量對所有的線程的可見性
當一個線程修改了這個變量的值,volatile 保證了新值能立即同步到主內存,以及每次使用前立即從主內存刷新。
但普通變量做不到這點,普通變量的值在線程間傳遞均需要通過主內存

2.禁止指令重排序優化。
有volatile修飾的變量,賦值後多執行了一個“load addl $0x0, (%esp)”操作,這個操作相當於一個內存屏障(指令重排序時不能把後面的指令重排序到內存屏障之前的位置)

只有一個CPU訪問內存時,並不需要內存屏障;(什麼是指令重排序:是指CPU採用了允許將多條指令不按程序規定的順序分開發送給各相應電路單元處理)。

更多:
Java語言提供了一種稍弱的同步機制,即volatile變量,用來確保將變量的更新操作通知到其他線程。
當把變量聲明爲volatile類型後,編譯器與運行時都會注意到這個變量是共享的,因此不會將該變量上的操作與其他內存操作一起重排序。
volatile變量不會被緩存在寄存器或者對其他處理器不可見的地方,因此在讀取volatile類型的變量時總會返回最新寫入的值。

  在訪問volatile變量時不會執行加鎖操作,因此也就不會使執行線程阻塞,因此volatile變量是一種比sychronized關鍵字更輕量級的同步機制。

詳見:
https://www.cnblogs.com/zhengbin/p/5654805.html

——————————–樂觀鎖與悲觀鎖——————————-

悲觀鎖,正如其名,它指的是對數據被外界修改持保守態度(悲觀),因此,在整個數據處理過程中,將數據處於鎖定狀態。 

悲觀鎖的實現,往往依靠數據庫提供的鎖機制 (也只有數據庫層提供的鎖機制才能真正保證數據訪問的排他性,否則,即使在本系統中實現了加鎖機制,也無法保證外部系統不會修改數據)

優點與不足

悲觀併發控制實際上是“先取鎖再訪問”的保守策略,爲數據處理的安全提供了保證。
但是在效率方面,處理加鎖的機制會讓數據庫產生額外的開銷,還有增加產生死鎖的機會;
另外,在只讀型事務處理中由於不會產生衝突,也沒必要使用鎖,這樣做只能增加系統負載;還有會降低了並行性,一個事務如果鎖定了某行數據,其他事務就必須等待該事務處理完纔可以處理那行數

樂觀鎖( Optimistic Locking ) 相對悲觀鎖而言,樂觀鎖假設認爲數據一般情況下不會造成衝突,所以在數據進行提交更新的時候,纔會正式對數據的衝突與否進行檢測,如果發現衝突了,則讓返回用戶錯誤的信息,讓用戶決定如何去做。

相對於悲觀鎖,在對數據庫進行處理的時候,樂觀鎖並不會使用數據庫提供的鎖機制。一般的實現樂觀鎖的方式就是記錄數據版本。

優點與不足

樂觀併發控制相信事務之間的數據競爭(data race)的概率是比較小的,因此儘可能直接做下去,直到提交的時候纔去鎖定,所以不會產生任何鎖和死鎖。但如果直接簡單這麼做,還是有可能會遇到不可預期的結果,例如兩個事務都讀取了數據庫的某一行,經過修改以後寫回數據庫,這時就遇到了問題。


詳見:
http://www.hollischuang.com/archives/934

——————————-AbstractQueuedSynchronizer—————————-

AQS用來實現鎖或其他同步組件的基礎框架(注意區別synchronized是在字節碼上加指令方式,通過底層機器語言保證同步)。

AQS使用int類型的volatile變量維護同步狀態(state),使用Node實現FIFO隊列來完成線程的排隊執行。在鎖的實現中通過組合AQS對象的方式使用,利用AQS實現鎖的語義。

AQS與鎖(如Lock)的對比:

鎖是面向使用者的,鎖定義了用戶調用的接口,隱藏了實現細節;

AQS是鎖的實現者,通過用AQS簡化了鎖的實現屏蔽了同步狀態管理,線程的排隊,等待喚醒的底層操作。

簡而言之,鎖是面向使用者,AQS是鎖的具體實現者。

——————————-深入理解ReentrantLock—————————-

重入鎖(ReentrantLock)是一種遞歸無阻塞的同步機制

http://blog.csdn.net/yanyan19880509/article/details/52345422

———————————Java併發集合——————————–

Java併發集合——ArrayBlockingQueue ,LinkedBlockingQueue,ConcurrentHashMap
http://blog.csdn.net/u013277740/article/details/79367237

三、JAVA虛擬機

本部分內容是關於Java虛擬機的一些面試高頻知識點的總結。說到對Java虛擬機的學習,就不得不提下這本書《深入理解Java虛擬機》。

本部分的內容也是基於這本書進行整理的,這本書基本是面試必備。+

關於Java虛擬機,重點考察以下三個方面的內容:

內存區域/內存模型
類加載機制
垃圾收集算法/收集器

目錄

  • 對象的創建、內存佈局和訪問定位
  • Java內存區域與內存模型
  • Java類加載機制及類加載器詳解
  • JVM中垃圾收集算法及垃圾收集器詳解
  • JVM怎麼判斷對象是否已死?

    ———————對象的創建、內存佈局和訪問定位——————-

一、對象的創建

,虛擬機遇到一個new指令時,
1、檢查指令在常量池中代表的類是否已經被加載,解析和初始化過,如果沒有,那必須先執行響應的類加載過程;

2.在類加載檢查功通過後,爲新生對象分配內存。對象所需的內存大小在類加載完成後便可完全確定。

二、對象的內存佈局

分爲3個區域:
對象頭
實例數據(存儲的有效信息)
對齊填充

三、對象的訪問定位

Java程序需要通過棧上了reference數據來操作堆上的具體對象。
目前主流的訪問方式有兩種

句柄(句柄池)
直接指針(對象地址)

——————————-Java內存區域與內存模型 —————————

Java內存區域

方法區(公有):被虛擬機加載的類信息,常量,靜態常量,即編譯後的代碼等數據。

堆(公有): 存放實例對象,Java堆是垃圾收集器管理的主要區域,因此很多時候也被稱爲“GC堆”。

虛擬機棧(線程私有): java方法執行時創建的一個棧幀,每一個方法從調用直至完成的過程,就對應着一個棧幀在虛擬機棧中入棧到出棧的過程。

本地方法棧(線程私有): 與虛擬機棧所發揮的作用相似,
虛擬機棧爲虛擬機執行java方法,而本地方法棧爲虛擬機使用到的Native方法服務。

程序計數器(線程私有): 一塊較小的內存,當前線程所執行的字節碼的行號指示器。
字節碼解釋器工作時,就是通過改變這個計數器的值來選取下一條需要執行的字節碼指令。


Java內存模型

Java內存模型的目的: 讓java程序在各種平臺下都能達到一致的內存訪問效果。

主要目標: 定義程序中各個變量的訪問規則,即在虛擬機中將變量存儲到內存和從內存中取出變量這樣的底層細節。

Java內存模型規定了所有的變量都存儲在主內存中。
每條線程中還有自己的工作內存,線程的工作內存中保存了被該線程所使用到的變量(這些變量是從主內存中拷貝而來)。

線程對變量的所有操作(讀取,賦值)都必須在工作內存中進行。不同線程之間也無法直接訪問對方工作內存中的變量,線程間變量值的傳遞均需要通過主內存來完成。

—————————Java類加載機制及類加載器 ————————-

一、類加載機制
把類從Class文件加載到內存,並對數據進行校驗、轉換解析和初始化,最終形成可以被虛擬機直接使用的Java類型。

在Java語言裏,類型的加載、連接和初始化過程都是在程序運行期間完成的

二、類加載器
類加載機制中加載部分的功能是將類的class文件讀入內存,併爲之創建一個java.lang.Class對象。
這部分功能就是由類加載器來實現的。

——————————-JVM中垃圾收集 ————————–

一、垃圾收集算法

1.標記-清除算法
①首先標記出所有需要回收的對象
②在標記完成後統一回收所有被標記的對象。

2.複製算法
將可用內存按容量大小劃分爲大小相等的兩塊,每次只使用其中的一塊。當一塊內存使用完了,就將還存活着的對象複製到另一塊上面,然後再把已使用過的內存空間一次清理掉

3.標記-整理算法
複製收集算法在對象存活率較高時,就要進行較多的複製操作,效率就會變低。 根據老年代的特點,提出了“標記-整理”算法。
標記過程仍然與”標記-清除“算法一樣,但後續步驟不是直接對可回收對象進行清理,而是讓所有存活的對象都向一端移動,然後直接清理掉邊界以外的內存。

4.分代收集算法
一般是把Java堆分爲新生代和老年代,這樣就可以根據各個年代的特點採用最適當的收集算法。
在新生代中,每次垃圾收集時都發現有大批對象死去,只有少量存活,那就選用複製算法。
在老年代中,因爲對象存活率高、沒有額外空間對它進行分配擔保,就必須採用“標記-清除”或“標記-整理”算法來進行回收。

二、垃圾回收機制的一些知識
1.JVM中的年代
JVM中分爲年輕代(Young generation)和老年代(Tenured generation)。
HotSpot JVM把年輕代分爲了三部分:1個Eden區和2個Survivor區(分別叫fromto)。默認比例爲81

一般情況下,新創建的對象都會被分配到Eden區(一些大對象特殊處理),這些對象經過第一次Minor GC後,如果仍然存活,將會被移到Survivor區。對象在Survivor區中每熬過一次Minor GC,年齡就會增加1歲,當它的年齡增加到一定程度時,就會被移動到年老代中

2.Minor GC和Full GC的區別

Minor GC:指發生在新生代的垃圾收集動作,該動作非常頻繁。
Full GC/Major GC:指發生在老年代的垃圾收集動作,出現了Major GC,經常會伴隨至少一次的Minor GC。Major GC的速度一般會比Minor GC慢10倍以上。

3. 空間分配擔保
在發生Minor GC之前,虛擬機會先檢查老年代最大可用的連續空間是否大於新生代所有對象的總空間,
如果這個條件成立,那麼Minor GC可以 確保是安全的。

如果不成立,則虛擬機會查看HandlePromotionFailure設置值是否允許擔保失敗。
如果允許,那會繼續檢查老年代最大可用的連續空間是否大於歷次晉升到老年代對象的平均大小,

如果大於,則將嘗試進行一次Minor GC,儘管這個Minor GC是有風險的。
如果小於,或者HandlePromotionFailure設置不允許冒險,那這時也要改爲進行一次Full GC。

三、垃圾收集器
1.Serial收集器(它在進行垃圾收集時,必須暫停其他所有的工作線程,直到它收集結束。)
2.ParNew收集器(Serial收集器的多線程版本)
3.Parallel Scavenge收集器(使用複製算法,又是並行的多線程收集器)
4.Serial Old收集器(是Serial收集器的老年代版本,同樣是一個單線程收集器,使用“標記-整理”算法。這個收集器的主要意義也是在於給Client模式下虛擬機使用。)
5.Parallel Old收集器(是Parallel Scavenge收集器的老年代版本,使用多線程和“標記-整理”算法)
6.CMS(Concurrent Mark Sweep)收集器(是HotSpot虛擬機中第一款真正意義上的併發收集器,它第一次實現了讓垃圾收集線程與用戶線程同時工作。)
7.G1收集器(是當今收集器技術發展的最前沿成果之一。是一款面向服務端應用的垃圾收集器)

—————————JVM怎麼判斷對象是否已死? ———————–

一、引用計數法

給對象添加一個引用計數器,每當有一個地方引用它時,計數器值就加1;當引用失效時,計數器值就減1;任何時刻計數器爲0的對象就是不可能被再使用的。

主流的JVM裏面沒有選用引用計數算法來管理內存,其中最主要的原因是它很難解決對象間的互循環引用的問題。

二、可達性分析算法
通過一些列的稱爲“GC Roots”的對象作爲起始點,從這些節點開始向下搜索,搜索所走過的路徑稱爲引用鏈,當一個對象到GC Roots沒有任何引用鏈相連時,則證明此對象是不可用的。
即:
方法運行時,方法中引用的對象;類的靜態變量引用的對象;類中常量引用的對象;Native方法中引用的對象

在可達性分析算法中,要真正宣告一個對象死亡,至少要經歷兩次標記過程:

三、判斷對象是否存活與“引用”有關

在JDK1.2之後,Java對引用的概念進行了擴充,將引用分爲
強引用(Strong Reference)、軟引用(Soft Reference)、弱引用(Weak Reference)、虛引用(Phantom Reference)四種,這四種引用強度依次逐漸減弱。

強引用: 普遍存在的,類似“Object obj = new Object()”這類的引用,只要強引用還存在,垃圾收集器永遠不會回收掉被引用的對象。

軟引用: 有用但非必須的對象。在系統將要發生內存溢出異常之前,將會把這些對象進行第二次回收。

弱引用: 非必須對象。被弱引用關聯的對象只能生存到下一次垃圾收集發生之前。當垃圾收集器工作時,無論當前內存是否足夠,都會回收掉只被弱引用關聯的對象。

虛引用: 一個對象是否有虛引用存在,爲一個對象設置虛引用的唯一目的就是能在這個對象被收集器回收時刻得到一個系統通知。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章