JAVA程序設計<5>
1.Java程序設計基本概念
1.1 JVM
ClassLoader(類加載器):每一個java文件都對應一個java類,根據程序需要逐漸載入內存。(一般ExtClassLoader加載java的擴展API即/lib/ext中的類,APPClassLoader用來加載用戶機器上的CLASSPATH設置的目錄中的class)。
ClassLoader加載流程:當程序運行時,JVM啟動,運行 bootstrap classloader(啟動類加載器),該ClassLoader加載Java核心API(Extclassloader和AppClassLoader也在此時被加載),調用Extclassloader加載擴展API,最後Appclassloader加載CLASSPATH目錄下定義的class。----一個程序最基本加載流程
JVM內部,統一使用編碼是Unicode表示,編碼轉換只能發生在邊界地方,JVM和OS的交界處,也就各種輸入輸出(或者Reader,Writer類)起作用的地方。(所以面向字節和面向字符有差別)
1.2 i++
i++是程序完畢後自增,++i是程序開始前自增
i=0;i=i++ + ++i=0+2=2
1.3 類型轉換
布爾型 數值型 字符型
低級轉高級自動轉化(如果是char類型轉高級類型,自動轉對應ASCII碼)
強制類型轉化:(類型)變量名
包裝類過度類型轉化
字符串與其他類型轉化(toString()轉字符串)
1.4 程序結構
assert(表達式) 斷言
1.5 運算符
& &&(和前面一個&區別是只要前面false後面無需判斷)
1.6 異常
java程序運行時出現的非正常現象,這種情況稱之為運行錯誤。 Error(由java虛擬機生成並拋出5)和Exception
final(變量值不變或者對象引用不變)、finall、finalize(最多運行一次)
1.7 反射----是指程序可以訪問、檢測和修改它本身的狀態或行為的一種能力。
反射允許在編寫與執行時,使程序代碼能夠接入裝載到JVM中的類的內部信息,而不是源代碼中選中的的類協作代碼。需要註意的是如果使用不當,反射的成本會很高。
java中的類反射Relectiion是java程序語言開發的特點之一,它允許運行中的java程序對自身進行檢查,並能直接操作程序的內部屬性。
2 Java內存管理
2.1垃圾收集
顯示的分配內存和釋放內存常常引起“內存泄漏”,因此,java在創建對象時會自動分配內存,並在該對象引用不存在自動釋放內存。
垃圾收集器技術監視java程序運行。java使用一系列軟指針來跟蹤對象的各個引用,並用一個對象表將這些軟指針映射為對象的引用。使用軟指針,Java的垃圾收集器能夠以單獨的線程在後臺運行,並依次檢查每個對象。(標記對象、移除對象、移動對象或檢查對象)
java語言中,判斷一塊內存空間是否符合垃圾收集器手機標準只有兩個條件:(1)給對象賦予了null空值以後再也沒有調用過;(2)給對象賦了新值,即重新分配了內存空間。
(提醒:一塊內存空間符合垃圾收集器的收集標準,並不意味著這塊內存空間就一定被垃圾回收器回收。)
垃圾回收註意幾點:
1)不要試圖假定垃圾回收時間,未知的。
2)Java中提供一些和垃圾回收打交道的方法,而且還提供強制執行垃圾回收的方法--調用System.gc(),但是該芳芳同樣是不確定的方法。(並不能保證調用就一定能啟動來及回收,只是向JVM發出請求,一切未知)
3)挑選合適的垃圾收集器。(一般使用JVM默認選項,但,,)
4)內存泄漏問題(內存對象明明已經不需要的時候,還仍然保留著這塊內存和它的訪問方式(引用),對象都是有生命周期的,有的長,有的短,如果長生命周期的對象持有短生命周期的引用,就很可能會出現內存泄露)。良好的編程習慣和嚴謹的變成態度。
在Java程序中,我們通常使用new為對象分配內存,而這些內存空間都在堆(Heap)上。
此處借用別人的例子:
public class Simple { Object object; public void method1(){ object = new Object(); //...其他代碼 } }
method1方法調用結束後object對象沒有被釋放,一直到Simple結束才被釋放。
解決方案:
public class Simple { Object object; public void method1(){ object = new Object(); //...其他代碼 object=null //增加一步 } }
5)盡早釋放無用對象的引用。對於頻繁申請內存和釋放內存的操作,最好使用finalize強制執行或者自己寫finalize方法,System.gc()方法不一定適用。在jvm垃圾收集器收集一個對象之前,一般要求程序員調用適當的方法釋放資源,但在沒有明確釋放資源的情況下,java提供了默認機制終止化該對象來釋放資源,即finalize()方法。
2.2 內存管理
java的內存管理就是對象的分配和釋放問題。java中,程序員通過關鍵字new為每個對象申請內存(基本類型除外),所有對象都在堆中分配空間。對象釋放由gc決定和執行。gc監控每一個對象的運行狀態(申請、引用、被引用、賦值等)
內存泄露兩個特點:1)對象可達,即存在通路與其相連2)對象是無用的。(內存泄露原因:1.全局集合。2.緩存3.ClassLoader)
2.3 clone
3.傳遞與引用
3.1傳值和傳引用
在java中,變量分為兩類。(java傳值傳引用的討論)
1)對於基本數據類型(int float long double boolean char byte),傳值的副本。
2)對於一切對象型變量,java都是傳引用的副本。傳引用副本的實質就是復制指向地址的指針。(這點與C++不同,在c++中當參數是引用類型時,傳遞的是真實的引用而不是副本)
String類型也是對象類型,所以它必然是傳引用副本。它是非可變類,使用傳值或者傳引用 顯得 沒有區別。
對於基本類型而言,傳值就是把自己復制一份傳遞,即使自己的副本變了自己也不會變。
對於對象類型,它傳的是副本引用,指向自己的地址而不是實際值的副本。因為對象類型放在堆裏,一方面速度相對於基本類型較慢,另一方面對象類型本身比較大,如果選擇重新復制值浪費內存且速度慢。(有趣比喻:復制鑰匙而不是復制倉庫) ---說明,一些書籍如thinking in java提到“不管是基本類型還是對象類型,都是傳值”他們是把引用副本也當做一種值。
引用傳遞例子1:
1 public class Test { 2 public static void main(String[] args) { 3 StringBuffer str= new StringBuffer("Hello"); 4 test(str); 5 System.out.println(str); 6 } 7 public static void test(StringBuffer s){ 8 s=s.append(",World!"); 9 } 10 }
輸出:Hello,World!
又一個例子2:
1 public class Test { 2 public static void main(String[] args) { 3 String str= "Hello"; 4 test(str); 5 System.out.println(str); 6 } 7 public static void test(String s){ 8 s="World!"; //系統自動生成一個新的String對象設為“World”,然後把這個對象的引用賦給str. 9 } 10 }
輸出:Hello
Stringl類是final類型的,因此不可以修改和繼承這個類,當例子2中函數結束,s作用消失。
例子1中StringBuffer是產生一塊內存空間,相關的增刪改操作都在其中,仍然是在同一段內存地址上進行。
String StringBuffer Stringbuilder
1)可變、不可變
String類中使用字符數組保存字符串,因為有“final”修飾符,所以可以知道string對象是不可變的。StringBuffer和Stringbuilder是可變的)
----String -----private final char value[];
-----StringBuffer和Stringbuilder -----char[] value;
2)是否線程安全
String中的對象是不可變的,也就可以理解為常量,顯然線程安全。
AbstractStringBuilder是StringBuilder與StringBuffer的公共父類,定義了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。
StringBuffer對方法加了同步鎖或者對調用的方法加了同步鎖,所以是線程安全的。
StringBuilder並沒有對方法進行加同步鎖,所以是非線程安全的。
如果程序不是多線程的,那麽使用StringBuilder效率高於StringBuffer。
同理,數組傳值的本質也是傳地址值的副本。
3.2. 靜態變量與私有變量
定義在類裏的變量會默認一個初始值。布爾類型默認初始值是false
main函數不能訪問一個非靜態變量或方法
3.3.輸入/輸出流
大文件讀取:new BufferedReader(new InputStreamReader(new FileInputStream("filename")))
寫入文件:FileOutputStream =new FileOutStream("filename") ;out.write("string to write".getBytes());out.close();
java IO操作有面向字節Byte和面向字符Character兩種方式.
面向字節的操作以8位二進制為單位對數據操作,不需要對數據轉化,這些操作的類都是InputStream和ouputStream的子類。
面向字符的操作以字符為單位讀取時需要將二進制轉化為字符,寫的時候需要將字符轉化為二進制,這些類都是Reader和Writer的子類。
3.4 序列化
JAVA程序設計<5>