極力推薦文章:歡迎收藏 Android 乾貨分享 html
利用可達性分析算法,虛擬機會將一些對象定義爲GC Roots
,從GC Roots
出發沿着引用鏈向下尋找,若是某個對象不能經過GC Roots
尋找到,虛擬機就認爲該對象能夠被回收掉。java
JNI(Native方法)
引用的對象;即便不可達,對象也不必定會被垃圾收集器回收 1)先判斷對象是否有必要執行finalize()
方法,對象必須重寫finalize()
方法且沒有被運行過。 2)如有必要執行,會把對象放到一個隊列中,JVM
會開一個線程去回收它們,這是對象最後一次能夠逃逸清理的機會。程序員
計算機中存儲的最小單元是一個字節即8bit
,所能表示的字符範圍是25
5個,而人類要表示的符號太多,沒法用一個字節來徹底表示,固須要將符號編碼,將各類語言翻譯成計算機能懂的語言。算法
總共128
個,用一個字節的低7
位表示,0〜31
控制字符如換回車刪除等;32~126
是打印字符,可經過鍵盤輸入並顯示出來;設計模式
用來擴展ASCII
編碼,256
個字符,涵蓋了大多數西歐語言字符。api
雙字節編碼,總編碼範圍是A1-A7,A1-A9
是符號區,包含682
個字符,B0-B7
是漢字區,包含6763
個漢字;數組
爲了擴展GB2312
,加入了更多的漢字,編碼範圍是8140~FEFE
,有2394
0個碼位,能表示21003
個漢字。緩存
ISO
試圖想建立一個全新的超語言字典,世界上全部語言均可經過這本字典Unicode
來相互翻譯,而UTF-16
定義了Unicode
字符在計算機中存取方法,用兩個字節來表示Unicode
轉化格式。不論什麼字符均可用兩字節表示,即16bit
,固叫UTF-16
。安全
UTF-16
統一採用兩字節表示一個字符,但有些字符只用一個字節就可表示,浪費存儲空間,而UTF-8
採用一種變長技術,每一個編碼區域有不一樣的字碼長度。 不一樣類型的字符能夠由1~6個字節組成。bash
utf-8
是一種變長編碼技術,utf-8
編碼中的中文佔用的字節不肯定,可能2個、3個、4個,int
型佔4
個字節。
代理是一種經常使用的設計模式,
爲其餘對象提供一個代理以控制對某個對象的訪問,將兩個類的關係解耦。代理類和委託類都要實現相同的接口,由於代理真正調用的是委託類的方法。
由程序員建立或是由特定工具生成,在代碼編譯時就肯定了被代理的類是哪個是靜態代理。靜態代理一般只代理一個類;
在代碼運行期間,運用反射機制動態建立生成。動態代理代理的是一個接口下的多個實現類;
InvocationHandler
接口建立本身的調用處理器;Proxy
類提供ClassLoader
和代理接口類型數組建立動態代理類;Retrofit
中直接調用接口的方法;Spring
的AOP
機制;
Java
中Throwable
是全部異常和錯誤的超類,兩個直接子類是Error(錯誤)
和Exception(異常)
如OOM、ThreadDeath
等。這些異常發生時,JVM
通常會選擇終止程序。
又分爲運行時異常(RuntimeException)(也叫Checked Eception)
和非運行時異常(不檢查異常Unchecked Exception)
。
運行時異常
有NullPointerException\IndexOutOfBoundsException
等,這些異常通常是由程序邏輯錯誤引發的,應儘量避免。
非運行時異常 有IOException\SQLException\FileNotFoundException
以及由用戶自定義的Exception
異常等。
指方法在運行前,即編譯期間就可知的,有一個肯定的版本,運行期間也不會改變。解析是靜態的,在類加載的解析階段就可將符號引用轉變成直接引用。
可分爲靜態分派和動態分派,重載屬於靜態分派,覆蓋屬於動態分派。 靜態分派是指在重載時經過參數的靜態類型而非實際類型做爲判斷依據,在編譯階段,編譯器可根據參數的靜態類型決定使用哪個重載版本。 動態分派則須要根據實際類型來調用相應的方法。
會調用對象的equals
方法,若是對象的equals
方法沒有被重寫,equals
方法和==
都是比較棧內局部變量表中指向堆內存地址值是否相等。
多態是指程序中定義的引用變量所指向的具體類型和經過該引用變量發出的方法調用在編譯時不肯定,在運行期間才肯定,一個引用變量到底會指向哪一個類的實例。這樣就能夠不用修改源程序,就可讓引用變量綁定到各類不一樣的類實現上。
繼承、重定、向上轉型,在多態中須要將子類的引用賦值給父類對象,只有這樣該引用纔可以具有調用父類方法和子類的方法。
ObjectOutputStream.writeObject()
負責將指定的流寫入 ObjectInputStream.readObject()
從指定流讀取序列化數據。
//寫入
try {
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("D:/student.txt"));
os.writeObject(studentList);
os.close();
} catch(FileNotFoundException e) {
e.printStackTrace();
} catch(IOException e) {
e.printStackTrace();
}
複製代碼
在運行狀態中,對任意一個類,都能知道這個類的全部屬性和方法,對任意一個對象,都能調用它的任意一個方法和屬性。這種能動態獲取信息及動態調用對象方法的功能稱爲java語言的反射機制。
開發過程當中,常常會遇到某個類的某個成員變量、方法或屬性是私有的,或只對系統應用開放,這裏就能夠利用java
的反射機制經過反射來獲取所需的私有成員或是方法。
Class
對象實例Class clz = Class.forName("com.zhenai.api.Apple");
Class
對象實例獲取Constructor
對象Constructor appConstructor = clz.getConstructor();
Constructor
對象的newInstance
方法獲取反射類對象Object appleObj = appConstructor.newInstance();
Method
對象Method setPriceMethod = clz.getMethod("setPrice", int.class);
invoke
方法調用方法setPriceMethod.invoke(appleObj, 14);
getFields()
能夠獲取Class
類的屬性,但沒法獲取私有屬性而getDeclaredFields()
能夠獲取到包括私有屬性在內的全部屬性。帶有Declared
修飾的方法能夠反射到私有的方法,沒有Declared
修飾的只能用來反射公有的方法,其餘如Annotation\Field\Constructor
也是如此。
註解是經過@interface
關鍵字來進行定義的,形式和接口差很少,只是前面多了一個@
舉例以下:
public @interface TestAnnotation {
}
複製代碼
使用時@TestAnnotation
來引用,要使註解能正常工做,還須要使用元註解,它是能夠註解到註解上的註解。
元標籤有@Retention @Documented @Target @Inherited @Repeatable
五種
說明註解的存活時間,取值有RetentionPolicy.SOURCE
註解只在源碼階段保留,在編譯器進行編譯時被丟棄;RetentionPolicy.CLASS 註解只保留到編譯進行的時候,並不會被加載到
JVM中。
RetentionPolicy.RUNTIME能夠留到程序運行的時候,它會被加載進入到
JVM`中,因此在程序運行時能夠獲取到它們。
註解中的元素包含到javadoc
中去
限定註解的應用場景,ElementType.FIELD
給屬性進行註解;ElementType.LOCAL_VARIABLE
能夠給局部變量進行註解;ElementType.METHOD
能夠給方法進行註解;ElementType.PACKAGE
能夠給一個包進行註解 ElementType.TYPE能夠給一個類型進行註解,如類、接口、枚舉
若一個超類被@Inherited
註解過的註解進行註解,它的子類沒有被任何註解應用的話,該子類就可繼承超類的註解;
註解是經過反射獲取的,能夠經過Class
對象的isAnnotationPresent()
方法判斷它是否應用了某個註解,再經過getAnnotation()
方法獲取Annotation
對象
泛型就是將類型變成參數傳入,使得可使用的類型多樣化,從而實現解耦。Java
泛型是在Java1.5
之後出現的,爲保持對之前版本的兼容,使用了擦除的方法實現泛型。擦除是指在必定程度無視類型參數T
,直接從T
所在的類開始向上T
的父類去擦除,如調用泛型方法,傳入類型參數T進入方法內部,若沒在聲明時作相似
public T methodName(T extends Father t){}
複製代碼
Java
就進行了向上類型的擦除,直接把參數t
當作Object
類來處理,而不是傳進去的T
。即在有泛型的任何類和方法內部,它都沒法知道本身的泛型參數,擦除和轉型都是在邊界上發生,即傳進去的參在進入類或方法時被擦除掉,但傳出來的時候又被轉成了咱們設置的T
。在泛型類或方法內,任何涉及到具體類型(即擦除後的類型的子類)操做都不能進行,如new T()
,或者T.play()(play爲某子類的方法而不是擦除後的類的方法)
String
類是final
型,固String
類不能被繼承,它的成員方法也都默認爲final
方法。String
對象一旦建立就固定不變了,對String
對象的任何改變都不影響到原對象,相關的任何改變操做都會生成新的String
對象。String
類是經過char
數組來保存字符串的,String
對equals
方法進行了重定,比較的是值相等。String a = "test";
String b = "test";
String c = new String("test");
複製代碼
a、b
和字面上的test
都是指向JVM
字符串常量池中的"test"
對象,他們指向同一個對象。 而new
關鍵字必定會產生一個對象test
,該對象存儲在堆中。因此new String("test")
產生了兩個對象,保存在棧中的c
和保存在堆中的test
。而在java中根本就不存在兩個徹底如出一轍的字符串對象,故在堆中的test
應該是引用字符串常量池中的test
。
String str1 = "abc"; //棧中開闢一塊空間存放引用str1,str1指向池中String常量"abc"
String str2 = "def"; //棧中開闢一塊空間存放引用str2,str2指向池中String常量"def"
String str3 = str1 + str2;//棧中開闢一塊空間存放引用str3
//str1+str2經過StringBuilder的最後一步toString()方法返回一個新的String對象"abcdef"
//會在堆中開闢一塊空間存放此對象,引用str3指向堆中的(str1+str2)所返回的新String對象。
System.out.println(str3 == "abcdef");//返回false
複製代碼
由於str3
指向堆中的"abcdef"
對象,而"abcdef"
是字符池中的對象,因此結果爲false
。JVM
對String str="abc"
對象放在常量池是在編譯時作的,而String str3=str1+str2
是在運行時才知道的,new
對象也是在運行時才作的。
由於String
設計成不可變,當建立一個String
對象時,若此字符串值已經存在於常量池中,則不會建立一個新的對象,而是引用已經存在的對象。若是字符串變量容許必變,會致使各類邏輯錯誤,如改變一個對象會影響到另外一個獨立對象。
字符串的不可變性保證了hash
碼的惟一性,所以能夠緩存String
的hashCode
,這樣不用每次去從新計算哈希碼。在進行字符串比較時,能夠直接比較hashCode
,提升了比較性能;
String
被許多java
類用來看成參數,如url
地址,文件path
路徑,反射機制所需String
參數等,若String
可變,將會引發各類安全隱患。
至此,本篇已結束,若有不對的地方,歡迎您的建議與指正。同時期待您的關注,感謝您的閱讀,謝謝!