抽象、繼承、封裝、多態性java
修飾符 | 當前類 | 同包 | 子類 | 其它包 |
---|---|---|---|---|
private | √ | |||
default | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
相同點:程序員
&和&&均可以用做邏輯與的運算符,表示邏輯與(and)。編程
不一樣點:數組
&&具備短路的功能,而&不具有短路功能。瀏覽器
當&運算符兩邊的表達式的結果都爲true時,整個運算結果才爲true。而&&運算符第一個表達式爲false時,則結果爲false,再也不計算第二個表達式。安全
&還能夠用做位運算符,當&操做符兩邊的表達式不是boolean類型時,&表示按位與操做,咱們一般使用0x0f來與一個整數進行&運算,來獲取該整數的最低4個bit位,例如:0x31 & 0x0f的結果爲0x01。服務器
Math.round(11.5)的返回值是12,Math.round(-11.5)的返回值是-11cookie
2 << 3(左移3位至關於乘以2的3次方,右移3位至關於除以2的3次方)網絡
數組沒有length()方法,有length的屬性。String有length()方法。session
String 不屬於基礎類型,基礎類型有 8 種:byte、boolean、char、short、int、float、long、double,而 String 屬於對象。
String 類是final類,不能夠被繼承。
兩個對象,一個是靜態區的"xyz",一個是用new建立在堆上的對象。
不對,若是兩個對象x和y知足x.equals(y) == true,它們的哈希碼(hash code)應當相同。
1)功能不一樣 "=="是判斷兩個變量或實例是否是指向同一個內存空間。 "equals"是判斷兩個變量或實例所指向的內存空間的值是否是相同。
2)定義不一樣 "equals"在JAVA中是一個方法。 "=="在JAVA中只是一個運算符合。 例子: Student student1 = new Student()...
3)運行速度不一樣 "=="比"equals"運行速度快,由於"=="只是比較引用。
不對,兩個對象的 hashCode()相同,equals()不必定 true。
String和StringBuffer/StringBuilder,它們能夠儲存和操做字符串。其中String是隻讀字符串,也就意味着String引用的字符串內容是不能被改變的,而StringBuffer/StringBuilder類表示的字符串對象能夠直接進行修改。
StringBuilder是Java 5中引入的,它和StringBuffer的方法徹底相同,區別在於它是在單線程環境下使用的,由於它的全部方面都沒有被synchronized修飾(非同步),所以它的效率也比StringBuffer要高。
indexOf()
:返回指定字符的索引。
charAt()
:返回指定索引處的字符。
replace()
:字符串替換。
trim()
:去除字符串兩端空白。
split()
:分割字符串,返回一個分割後的字符串數組。
getBytes()
:返回字符串的 byte 類型數組。
length()
:返回字符串長度。
toLowerCase()
:將字符串轉成小寫字母。
toUpperCase()
:將字符串轉成大寫字符。
substring()
:截取字符串。
equals()
:字符串比較。
方法的重載和重寫都是實現多態的方式,區別在於前者實現的是編譯時的多態性,然後者實現的是運行時的多態性。重載發生在一個類中,同名的方法若是有不一樣的參數列表(參數類型不一樣、參數個數不一樣或者兩者都不一樣)則視爲重載;重寫發生在子類與父類之間,重寫要求子類被重寫方法與父類被重寫方法有相同的返回類型
構造器不能被繼承,所以不能被重寫,但能夠被重載。
靜態變量是被static修飾符修飾的變量,也稱爲類變量,它屬於類,不屬於類的任何一個對象,一個類無論建立多少個對象,靜態變量在內存中有且僅有一個拷貝。
實例變量必須依存於某一實例,須要先建立對象而後經過對象才能訪問到它。靜態變量能夠實現讓多個對象共享內存。
有兩種方式:
1)實現Cloneable接口並重寫Object類中的clone()方法。
2)實現Serializable接口,經過對象的序列化和反序列化實現克隆,能夠實現真正的深度克隆。
一個內部類對象能夠訪問建立它的外部類對象的成員,包括私有成員。
普通類不能包含抽象方法,抽象類能夠包含抽象方法。
抽象類不能直接實例化,普通類能夠直接實例化。
不能,定義抽象類就是讓其餘類繼承的,若是定義爲 final 該類就不能被繼承,這樣彼此就會產生矛盾,因此 final 不能修飾抽象類。
實現:抽象類的子類使用 extends 來繼承;接口必須使用 implements 來實現接口。
構造函數:抽象類能夠有構造函數;接口不能有。
main 方法:抽象類能夠有 main 方法,而且咱們能運行它;接口不能有 main 方法。
實現數量:類能夠實現不少個接口;可是隻能繼承一個抽象類。
訪問修飾符:接口中的方法默認使用 public 修飾;抽象類中的方法能夠是任意訪問修飾符。
final 修飾的類叫最終類,該類不能被繼承。
final 修飾的方法不能被重寫。
final 修飾的變量叫常量,常量必須初始化,初始化以後值就不能被修改。
final
:修飾符(關鍵字)有三種用法:若是一個類被聲明爲final,意味着它不能再派生出新的子類,即不能被繼承,所以它和abstract是反義詞。將變量聲明爲final,能夠保證它們在使用中不被改變,被聲明爲final的變量必須在聲明時給定初值,而在之後的引用中只能讀取不可修改。被聲明爲final的方法也一樣只能使用,不能在子類中被重寫。
finally
:一般放在try…catch…的後面構造老是執行代碼塊,這就意味着程序不管正常執行仍是發生異常,這裏的代碼只要JVM不關閉都能執行,能夠將釋放外部資源的代碼寫在finally塊中。
finalize
:Object類中定義的方法,Java中容許使用finalize()方法在垃圾收集器將對象從內存中清除出去以前作必要的清理工做。這個方法是由垃圾收集器在銷燬對象時調用的,經過重寫finalize()方法能夠整理系統資源或者執行其餘清理工做。
所謂多態,指的就是父類引用指向子類對象,調用方法時會調用子類的實現而不是父類的實現。多態的實現的關鍵在於「動態綁定」。
clone()
,equals()
,hashCode()
,toString()
,notify()
,notifyAll()
,wait()
,finalize()
,getClass()
泛型即參數化類型,在建立集合時,指定集合元素的類型,此集合只能傳入該類型的參數。類型擦除:java編譯器生成的字節碼不包含泛型信息,因此在編譯時擦除:1.泛型用最頂級父類替換;2.移除。
併發編程中:原子性問題,可見性問題,有序性問題。
volatile
關鍵字能保證可見性,字能禁止指令重排序,可是不能保證原子性。可見性只能保證每次讀取的是最新的值,可是volatile沒辦法保證對變量的操做的原子性。在生成的會變語句中加入Lock關鍵字和內存屏障。
Lock
實現提供了比使用synchronized 方法和語句可得到的更普遍的鎖定操做,它能以更優雅的方式處理線程同步問題。用sychronized修飾的方法或者語句塊在代碼執行完以後鎖自動釋放,而用Lock須要咱們手動釋放鎖。
能,Java 中能夠建立 volatile 類型數組,不過只是一個指向數組的引用,而不是整個數組。個人意思是,若是改變引用指向的數組,將會受到 volatile 的保護,可是若是多個線程同時改變數組的元素,volatile 標示符就不能起到以前的保護做用了
集合,線性結構(數組,隊列,鏈表和棧),樹形結構,圖狀結構。
Java 中的 TreeMap 是使用紅黑樹實現的。
poll()
和remove()
都是從隊列中取出一個元素,可是poll()
在獲取元素失敗的時候會返回空,可是remove()
失敗的時候會拋出異常。
在Java 6中Arrays.sort()
和Collections.sort()
使用的是MergeSort
,而在Java 7中,內部實現換成了TimSort
,其對對象間比較的實現要求更加嚴格。
HashMap是數組+鏈表+紅黑樹(JDK1.8增長了紅黑樹部分)實現的。
HashMap最多隻容許一條記錄的鍵爲null,容許多條記錄的值爲null。
HashMap非線程安全。ConcurrentHashMap線程安全。
解決碰撞:當出現衝突時,運用拉鍊法,將相同的結點連接在一個單鏈表中,散列表長m,則定義一個由m個頭指針組成的指針數組T,地址爲i的結點插入以T(i)爲頭指針的單鏈表中。Java8中,衝突的元素超過限制(8),用紅黑樹替換鏈表。
Vector屬於線程安全級別的,可是大多數狀況下不使用Vector,由於線程安全須要更大的系統開銷。
1) 歷史緣由: Hashtable繼承Dictonary類, HashMap繼承自abstractMap
2) HashMap容許空的鍵值對, 但最多隻有一個空對象,而HashTable不容許。
3) HashTable同步,而HashMap非同步,效率上比HashTable要高
在多線程環境下,使用HashMap進行put操做會引發死循環,致使CPU利用率接近100%,因此在併發狀況下不能使用HashMap。
HashTable使用synchronized來保證線程的安全,可是在線程競爭激烈的狀況下HashTable的效率很是低下。當一個線程訪問HashTable的同步方法,其餘方法訪問HashTable的同步方法時,會進入阻塞或者輪詢狀態。若是線程1使用put進行元素添加,線程2不但不能用put方法添加於元素同是也沒法用get方法來獲取元素,因此競爭越激烈效率越低。
HashTable容器在競爭激烈的併發環境效率低下的緣由是全部訪問HashTable的線程都必須競爭同一把鎖,假如容器有多把鎖,每一把鎖用於鎖住容器中一部分數據,那麼多線程訪問容器裏不一樣數據段的數據時,線程間就不會存在鎖競爭,從而能夠有效提升併發訪問率,這就是ConcurrentHashMap的鎖分段技術。將數據分紅一段一段的存儲,而後給每一段數據配一把鎖,當一個線程佔用鎖訪問其中一段數據的時候,其餘段的數據也能被其餘線程訪問。
ArrrayList 底層的數據結構是數組,支持隨機訪問,而 LinkedList 的底層數據結構書鏈表,不支持隨機訪問。
ArrayList 的時間複雜度是 O(1),而 LinkedList 是 O(n)。
LinkedList是雙向鏈表
Comparable 接口用於定義對象的天然順序,是排序接口,而 comparator 一般用於定義用戶定製的順序,是比較接口。咱們若是須要控制某個類的次序,而該類自己不支持排序(即沒有實現Comparable接口),那麼咱們就能夠創建一個「該類的比較器」來進行排序。Comparable 老是隻有一個,可是能夠有多個 comparator 來定義對象的順序。
Collection是Java集合框架中的基本接口。
Collections是Java集合框架提供的一個工具類,其中包含了大量用於操做或返回集合的靜態方法。
List,Set都是繼承自Collection接口
List特色:元素有放入順序,元素可重複
Set特色:元素無放入順序,元素不可重複,重複元素會覆蓋掉
Set:檢索元素效率低下,刪除和插入效率高,插入和刪除不會引發元素位置改變。
List:和數組相似,List能夠動態增加,查找元素效率高,插入刪除元素效率低,由於會引發其餘元素位置改變。
Arraylist:
優勢:ArrayList是實現了基於動態數組的數據結構,由於地址連續,一旦數據存儲好了,查詢操做效率會比較高(在內存裏是連着放的)。
缺點:由於地址連續, ArrayList要移動數據,因此插入和刪除操做效率比較低。
LinkedList:
優勢:LinkedList基於鏈表的數據結構,地址是任意的,因此在開闢內存空間的時候不須要等一個連續的地址,對於新增和刪除操做add和remove,LinedList比較佔優點。LinkedList 適用於要頭尾操做或插入指定位置的場景。
缺點:由於LinkedList要移動指針,因此查詢操做性能比較低。
適用場景分析:
當須要對數據進行對此訪問的狀況下選用ArrayList,當須要對數據進行屢次增長刪除修改時採用LinkedList。
一、sleep()方法
在指定的毫秒數內讓當前正在執行的線程休眠(暫停執行),此操做受到系統計時器和調度程序精度和準確性的影響。 讓其餘線程有機會繼續執行,但它並不釋放對象鎖。也就是若是有Synchronized同步塊,其餘線程仍然不能訪問共享數據。注意該方法要捕獲異常
好比有兩個線程同時執行(沒有Synchronized),一個線程優先級爲MAX_PRIORITY,另外一個爲MIN_PRIORITY,若是沒有Sleep()方法,只有高優先級的線程執行完成後,低優先級的線程才能執行;但當高優先級的線程sleep(5000)後,低優先級就有機會執行了。
總之,sleep()可使低優先級的線程獲得執行的機會,固然也可讓同優先級、高優先級的線程有執行的機會。
二、yield()方法
yield()方法和sleep()方法相似,也不會釋放「鎖標誌」,區別在於,它沒有參數,即yield()方法只是使當前線程從新回到可執行狀態,因此執行yield()的線程有可能在進入到可執行狀態後立刻又被執行,另外yield()方法只能使同優先級或者高優先級的線程獲得執行機會,這也和sleep()方法不一樣。
三、join()方法
Thread的非靜態方法join()讓一個線程B「加入」到另一個線程A的尾部。在A執行完畢以前,B不能工做。
Thread t = new MyThread(); t.start(); t.join();
保證當前線程中止執行,直到該線程所加入的線程完成爲止。然而,若是它加入的線程沒有存活,則當前線程不須要中止。
雖然二者都是用來暫停當前運行的線程,可是 sleep() 實際上只是短暫停頓,由於它不會釋放鎖,而 wait() 意味着條件等待,這就是爲何該方法要釋放鎖,由於只有這樣,其餘等待的線程才能在知足條件時獲取到該鎖。
1)繼承Thread類,重寫run函數
2)實現Runnable接口,重寫run函數
3)實現Callable接口,重寫call函數
Java的線程是經過java.lang.Thread類來實現的。VM啓動時會有一個由主方法所定義的線程。能夠經過建立Thread的實例來建立新的線程。每一個線程都是經過某個特定Thread對象所對應的方法run()來完成其操做的,方法run()稱爲線程體。經過調用Thread類的start()方法來啓動一個線程。
進程值運行中的程序(獨立性,動態性,併發性),線程指進程中的順序執行流。區別是:1.進程間不共享內存 2.建立進程進行資源分配的代價要大得多,因此多線程在高併發環境中效率高。
線程安全就是多線程訪問時,採用了加鎖機制,當一個線程訪問該類的某個數據時,進行保護,其餘線程不能進行訪問直到該線程讀取完,其餘線程纔可以使用。不會出現數據不一致或者數據污染。
新建狀態:使用 new 關鍵字和 Thread 類或其子類創建一個線程對象後,該線程對象就處於新建狀態。它保持這個狀態直到程序 start() 這個線程。
就緒狀態:當線程對象調用了start()方法以後,該線程就進入就緒狀態。就緒狀態的線程處於就緒隊列中,要等待JVM裏線程調度器的調度。
運行狀態:若是就緒狀態的線程獲取 CPU 資源,就能夠執行 run(),此時線程便處於運行狀態。處於運行狀態的線程最爲複雜,它能夠變爲阻塞狀態、就緒狀態和死亡狀態。
阻塞狀態:若是一個線程執行了sleep(睡眠)、suspend(掛起)等方法,失去所佔用資源以後,該線程就從運行狀態進入阻塞狀態。在睡眠時間已到或得到設備資源後能夠從新進入就緒狀態。能夠分爲三種:
等待阻塞:運行狀態中的線程執行 wait() 方法,使線程進入到等待阻塞狀態。
同步阻塞:線程在獲取 synchronized同步鎖失敗(由於同步鎖被其餘線程佔用)。
其餘阻塞:經過調用線程的 sleep() 或 join() 發出了 I/O請求時,線程就會進入到阻塞狀態。當sleep() 狀態超時,join() 等待線程終止或超時,或者 I/O 處理完畢,線程從新轉入就緒狀態。
死亡狀態:
一個運行狀態的線程完成任務或者其餘終止條件發生時,該線程就切換到終止狀態。
可重入性:
ReenTrantLock和synchronized使用的鎖都是可重入的,二者都是同一個線程每進入一次,鎖的計數器都自增1,因此等到鎖的計數器降低爲0時才能釋放鎖。
鎖的實現:
synchronized是依賴JVM實現的,ReenTrantLock是JDK實現的,相似於操做系統控制實現和用戶本身寫代碼實現。
性能的區別:
synchronized在JDK5優化後,二者性能差很少了。若是兩種方法均可以使用的狀況下,官方建議使用synchronized,由於使用便利。
功能的區別:
synchronized由編譯器加鎖和釋放,默認是非公平鎖,ReenTrantLock手動加鎖和釋放鎖,若是忘記釋放容易引發死鎖,可是對於粒度控制強於synchronized關鍵字。
ReenTrantLock能夠指定是公平鎖仍是非公平鎖,synchronized只能是非公平鎖,所謂的公平鎖就是先等待的線程先獲取鎖。
ReenTrantLock提供了中斷鎖和等待鎖的功能,經過lock.lockInterruptibly()實現中斷鎖,經過lock.tryLock()實現等待鎖。
ReenTrantLock提供了一個Condition類,實現了線程之間的通訊。
同步:調用方須要主動等待結果的返回。
異步:不須要主動等待結果的返回,而是經過其餘手段,好比狀態通知,回調函數等。
阻塞:是指結果返回以前,當前線程被掛起,不作任何事。
非阻塞:是指結果在返回以前,線程能夠作一些其餘事,不會被掛起。
一、在主函數中使用join()方法
t1.start(); t2.start(); t3.start(); t1.join();//不會致使t1和t2和t3的順序執行 t2.join(); t3.join(); System.out.println("Main finished");
二、使用CountDownLatch
public class WithLatch{ public static void main(String[] args){ CountDownLatch latch = new CountDownLatch(3); for(int i=0;i<3;i++){ new ChildThread(i,latch).start(); } try{ latch.await(); }catch(InterruptedException e){ e.printStackTrace(); } System.out.println("Main finished"); } static calss ChildThread extends Thread{ private int id = -1; private CountDownLatch latch = null; public ChildThread(int id, CountDownLatch latch){ this.id = id; this.latch = latch; } public void run(){ try{ Thread.sleep(Math.abs(new Random().nextInt(5000))); System.out.println(String.format("Child Thread %d finished",id)); }catch(InterruptedExcepion e){ e.printStackTrace(); }finally{ latch.countDown(); } } } }
三、使用線程池
public class WithExecutor{ public static void main(String[] args) throws InterruptedExcepion{ ExecutorService pool = Executors.newFixedThreadPool(3); List<Callable<Void>> list = new ArrayList<Callable<Void>>(); for(int i=0;i<3;i++){ list.add(new ChildThread(i)); } try{ pool.invokeAll(list); }finally{ pool.shutdown(); } System.out.println("Main finished"); } static class ChildThread implements Callable<Void>{ private int id = -1; public ChildThread (int id){ this.id = id; } public Void call() throws Exception{ try{ Thread.sleep(Math.abs(new Random().nextInt(5000))); System.out.println(String.format("Child Thread %d finished",id)); }catch(InterruptedException e){ e.printStackTrace(); } return null; } } }
因爲在平時的工做中,線上服務器是分佈式多臺部署的,常常會面臨解決分佈式場景下數據一致性的問題,那麼就要利用分佈式鎖來解決這些問題。
同步和異步最大的區別就在於。一個須要等待,一個不須要等待。同步能夠避免出現死鎖,讀髒數據的發生,通常共享某一資源的時候用,若是每一個人都有修改權限,同時修改一個文件,有可能使一我的讀取另外一我的已經刪除的內容,就會出錯,同步就會按順序來修改。
Error表示系統級的錯誤和程序沒必要處理的異常,是很難恢復的一種嚴重問題;好比內存溢出,不可能期望程序能處理這樣的狀況;
Exception表示須要捕捉或者須要程序進行處理的異常,是一種設計或實現問題;也就是說,它表示若是程序運行正常,從不會發生的狀況。
會執行,在方法返回調用者前執行。
異常表示程序運行過程當中可能出現的非正常狀態,運行時異常表示虛擬機的一般操做中可能遇到的異常,是一種常見運行錯誤,只要程序設計得沒有問題一般就不會發生。
受檢異常跟程序運行的上下文環境有關,即便程序設計無誤,仍然可能因使用的問題而引起。Java編譯器要求方法必須聲明拋出可能發生的受檢異常,可是並不要求必須聲明拋出未被捕獲的運行時異常。
按功能來分:輸入流(input)、輸出流(output)。
按類型來分:字節流和字符流。
字節流和字符流的區別是:字節流按 8 位傳輸以字節爲單位輸入輸出數據,字符流按 16 位傳輸以字符爲單位輸入輸出數據。
BIO:Block IO 同步阻塞式 IO,就是咱們日常使用的傳統 IO,它的特色是模式簡單使用方便,併發處理能力低。
NIO:New IO 同步非阻塞 IO,是傳統 IO 的升級,客戶端和服務器端經過 Channel(通道)通信,實現了多路複用。
AIO:Asynchronous IO 是 NIO 的升級,也叫 NIO2,實現了異步非堵塞 IO ,異步 IO 的操做基於事件和回調機制。
1.OSI模型把網絡通訊的工做分爲7層,分別是物理層、數據鏈路層、網絡層、傳輸層、會話層、表示層和應用層。每一層對於上一層來說是透明的,上層只須要使用下層提供的接口,並不關心下層是如何實現的。
2.TCP/IP參考模型是首先由ARPANET所使用的網絡體系結構。這個體系結構在它的兩個主要協議出現之後被稱爲TCP/IP參考模型(TCP/IP Reference Model)。這一網絡協議共分爲四層:網絡訪問層、互聯網層、傳輸層和應用層。
3.TCP/IP模型的分層及與OSI參考模型的對應關係爲:
網絡訪問層--對應OSI參考模型的物理層和數據鏈路層;
網絡層--對應OSI參考模型的網絡層;
傳輸層--對應OSI參考模型的傳輸層;
應用層--對應OSI參考模型的會話層、表示層和應用層。
在TCP的鏈接中,數據流必須以正確的順序送達對方。TCP的可靠性是經過順序編號和確認(ACK)來實現的。
TCP 鏈接是經過三次握手進行初始化的。三次握手的目的是同步鏈接雙方的序列號和確認號並交換 TCP 窗口大小信息。
第一次是客戶端發起鏈接;第二次表示服務器收到了客戶端的請求;第三次表示客戶端收到了服務器的反饋。
TCP(Tranfer Control Protocol)的縮寫,是一種面向鏈接的保證傳輸的協議,在傳輸數據流前,雙方會先創建一條虛擬的通訊道。能夠不多差錯傳輸數據。
UDP(User DataGram Protocol)的縮寫,是一種無鏈接的協議,使用UDP傳輸數據時,每一個數據段都是一個獨立的信息,包括完整的源地址和目的地,在網絡上以任何可能的 路徑傳到目的地,所以,可否到達目的地,以及到達目的地的時間和內容的完整性都不能保證。
因此TCP比UDP多了創建鏈接的時間。相對UDP而言,TCP具備更高的安全性和可靠性。
TCP協議傳輸的大小不限制,一旦鏈接被創建,雙方能夠按照必定的格式傳輸大量的數據,而UDP是一個不可靠的協議,大小有限制,每次不能超過64K。
cookie 是 Web 服務器發送給瀏覽器的一塊信息。瀏覽器會在本地文件中給每個 Web 服務器存儲 cookie。之後瀏覽器在給特定的 Web 服務器發請求的時候,同時會發送全部爲該服務器存儲的 cookie。
不管客戶端瀏覽器作怎麼樣的設置,session都應該能正常工做。客戶端能夠選擇禁用 cookie,可是, session 仍然是可以工做的,由於客戶端沒法禁用服務端的 session。
JDK:Java Development Kit 的簡稱,java 開發工具包,提供了 java 的開發環境和運行環境。
JRE:Java Runtime Environment 的簡稱,java 運行環境,爲 java 的運行提供了所需環境。
具體來講 JDK 其實包含了 JRE,同時還包含了編譯 java 源碼的編譯器 javac,還包含了不少 java 程序調試和分析的工具。簡單來講:若是你須要運行 java 程序,只需安裝 JRE 就能夠了,若是你須要編寫 java 程序,須要安裝 JDK。
Java中的全部類,必須被裝載到jvm中才能運行,這個裝載工做是由jvm中的類裝載器完成的,類裝載器所作的工做實質是把類文件從硬盤讀取到內存中
1).系統類
2).擴展類
3).由程序員自定義的類
1).隱式裝載, 程序在運行過程當中當碰到經過new 等方式生成對象時,隱式調用類裝載器加載對應的類到jvm中
2).顯式裝載, 經過class.forname()等方法,顯式加載須要的類
一個應用程序老是由n多個類組成,Java程序啓動時,並非一次把全部的類所有加載後再
運行,它老是先把保證程序運行的基礎類一次性加載到jvm中,其它類等到jvm用到的時候再加載,這樣的好處是節省了內存的開銷,由於java最先就是爲嵌入式系統而設計的,內存寶貴,這是一種能夠理解的機制,而用到時再加載這也是java動態性的一種體現
Java中的類裝載器實質上也是類,功能是把類載入jvm中,值得注意的是jvm的類裝載器並非一個,而是三個,層次結構以下:
Bootstrap Loader - 負責加載系統類
ExtClassLoader - 負責加載擴展類
AppClassLoader - 負責加載應用類
爲何要有三個類加載器,一方面是分工,各自負責各自的區塊,另外一方面爲了實現委託模型,下面會談到該模型
1) Bootstrap類加載器 – JRE/lib/rt.jar
2) Extension類加載器 – JRE/lib/ext或者java.ext.dirs指向的目錄
3) Application類加載器 – CLASSPATH環境變量, 由-classpath或-cp選項定義,或者是JAR中的Manifest的classpath屬性定義.
類加載器的工做原理基於三個機制:委託、可見性和單一性
當一個類加載和初始化的時候,類僅在有須要加載的時候被加載。假設你有一個應用須要的類叫做Abc.class,首先加載這個類的請求由 Application類加載器委託給它的父類加載器Extension類加載器,而後再委託給Bootstrap類加載器。Bootstrap類加載器 會先看看rt.jar中有沒有這個類,由於並無這個類,因此這個請求由回到Extension類加載器,它會查看jre/lib/ext目錄下有沒有這 個類,若是這個類被Extension類加載器找到了,那麼它將被加載,而Application類加載器不會加載這個類;而若是這個類沒有被 Extension類加載器找到,那麼再由Application類加載器從classpath中尋找。記住classpath定義的是類文件的加載目 錄,而PATH是定義的是可執行程序如javac,java等的執行路徑。
可見性的原理是子類的加載器能夠看見全部的父類加載器加載的類,而父類加載器看不到子類加載器加載的類。
根據這個機制,父加載器加載過的類不能被子加載器加載第二次。雖然重寫違反委託和單一性機制的類加載器是可能的,但這樣作並不可取。
1)裝載:查找並加載類的二進制數據;
2)連接:
驗證:確保被加載類的正確性;
準備:爲類的靜態變量分配內存,並將其初始化爲默認值;
解析:把類中的符號引用轉換爲直接引用;
3)初始化:爲類的靜態變量賦予正確的初始值;
類何時才被初始化:
1)建立類的實例,也就是new一個對象
2)訪問某個類或接口的靜態變量,或者對該靜態變量賦值
3)調用類的靜態方法
4)反射(Class.forName("com.lyj.load"))
5)初始化一個類的子類(會首先初始化子類的父類)
6)JVM啓動時標明的啓動類,即文件名和類名相同的那個類
1)若是這個類尚未被加載和連接,那先進行加載和連接
2)假如這個類存在直接父類,而且這個類尚未被初始化(注意:在一個類加載器中,類只能初始化一次),那就初始化直接的父類(不適用於接口)
3)加入類中存在初始化語句(如static變量和static塊),那就依次執行這些初始化語句
理論上Java由於有垃圾回收機制(GC)不會存在內存泄露問題(這也是Java被普遍使用於服務器端編程的一個重要緣由);然而在實際開發中,可能會存在無用但可達的對象,這些對象不能被GC回收,所以也會致使內存泄露的發生。
GC是垃圾收集的意思,內存處理是編程人員容易出現問題的地方,忘記或者錯誤的內存回收會致使程序或系統的不穩定甚至崩潰,Java提供的GC功能能夠自動監測對象是否超過做用域從而達到自動回收內存的目的,Java語言沒有提供釋放已分配內存的顯示操做方法。Java程序員不用擔憂內存管理,由於垃圾收集器會自動進行管理。要請求垃圾收集,能夠調用下面的方法之一:System.gc()
或Runtime.getRuntime().gc()
,但JVM能夠屏蔽掉顯示的垃圾回收調用。
垃圾回收能夠有效的防止內存泄露,有效的使用可使用的內存。垃圾回收器一般是做爲一個單獨的低優先級的線程運行,不可預知的狀況下對內存堆中已經死亡的或者長時間沒有使用的對象進行清除和回收,程序員不能實時的調用垃圾回收器對某個對象或全部對象進行垃圾回收。在Java誕生初期,垃圾回收是Java最大的亮點之一,由於服務器端的編程須要有效的防止內存泄露問題,然而時過境遷,現在Java的垃圾回收機制已經成爲被詬病的東西。移動智能終端用戶一般以爲iOS的系統比Android系統有更好的用戶體驗,其中一個深層次的緣由就在於Android系統中垃圾回收的不可預知性。
不能,雖然你能夠調用System.gc()
或者Runtime.gc()
,可是沒有辦法保證 GC 的執行。
當經過 Java 命令啓動 Java 進程的時候,會爲它分配內存。內存的一部分用於建立堆空間,當程序中建立對象的時候,就從空間中分配內存。GC 是 JVM 內部的一個進程,回收無效對象的內存用於未來的分配。
JVM 中堆和棧屬於不一樣的內存區域,使用目的也不一樣。棧經常使用於保存方法幀和局部變量,而對象老是在堆上分配。棧一般都比堆小,也不會在多個線程之間共享,而堆被整個 JVM 的全部線程共享。
能夠經過 java.lang.Runtime 類中與內存相關方法來獲取剩餘的內存,總內存及最大堆內存。經過這些方法你也能夠獲取到堆使用的百分比及堆內存的剩餘空間。Runtime.freeMemory()
方法返回剩餘空間的字節數,Runtime.totalMemory()
方法總內存的字節數,Runtime.maxMemory()
返回最大內存的字節數。
1)方法區(method):被全部的線程共享。方法區包含全部的類信息和靜態變量。
2)堆(heap):被全部的線程共享,存放對象實例以及數組,Java堆是GC的主要區域。
3)棧(stack):每一個線程包含一個棧區,棧中保存一些局部變量等。
4)程序計數器:是當前線程執行的字節碼的行指示器。
持久代主要存放的是Java類的類信息,與垃圾收集要收集的Java對象關係不大。全部新生成的對象首先都是放在年輕代的,年老代中存放的都是一些生命週期較長的對象。
內存溢出:程序申請內存時,沒有足夠的內存,out of memory;
內存泄漏值垃圾對象沒法回收,可使用memory analyzer工具查看泄漏。