摘要:點擊進(jìn)入我的博客初始化和清理是編程安全的兩個問題。延續(xù)了中的構(gòu)造器確保初始化,并引入了垃圾回收器管理和釋放內(nèi)存。用構(gòu)造方法確保初始化構(gòu)造方法和類名相同。用于強(qiáng)制進(jìn)行終結(jié)動作。載入該文件,靜態(tài)數(shù)據(jù)進(jìn)行初始化,執(zhí)行靜態(tài)代碼塊。
點擊進(jìn)入我的博客
初始化和清理是編程安全的兩個問題。Java延續(xù)了C++中的構(gòu)造器確保初始化,并引入了“垃圾回收器”管理和釋放內(nèi)存。
5.1 用構(gòu)造方法確保初始化構(gòu)造方法和類名相同。原因是盡量減少和成員名稱沖突;并且調(diào)用構(gòu)造方法是編譯器的責(zé)任,名稱相同編譯器才會知道應(yīng)該調(diào)用哪個方法。
由于構(gòu)造方法和類名相同,所以構(gòu)造方法不適應(yīng)于首字母小寫的約定規(guī)范。
構(gòu)造方法有默認(rèn)的無參構(gòu)造方法,也可以帶參數(shù)。
構(gòu)造方法沒有返回值,請注意這跟void不同。
5.2 方法重載方法重載是指方法名相同,但是參數(shù)的個數(shù)、類型和順序不同。
由于構(gòu)造方法必須和類名相同,即方法名已經(jīng)確定,但想要用多種方式(參數(shù))創(chuàng)建一個對象,就必須引入方法重載。
方法重載不僅適用于構(gòu)造方法,還適用于其他方法。
warning:func(int i, String str)和func(String str, int i)參數(shù)順序不同構(gòu)成重載,但請盡量避免這種寫法。
構(gòu)成重載深層次的原因:只要這兩個方法編譯器能區(qū)分開來,在調(diào)用的時候知道要調(diào)用的是哪一個,不會產(chǎn)生混淆,這兩個方法就構(gòu)成重載。
對于byte、short、int、float、double如果找不到對應(yīng)基本類型方法,則會按照向上轉(zhuǎn)化的路線找匹配的方法
如果是char,如果找不到對應(yīng)的基本類型方法,直接從int向上找匹配的方法。
public static void print(char c) { System.out.println("char: " + c); } public static void print(byte b) { System.out.println("byte: " + b); } public static void print(short s) { System.out.println("short: " + s); } public static void print(int i) { System.out.println("int: " + i); } public static void print(long l) { System.out.println("long: " + l); } public static void print(float f) { System.out.println("float: " + f); } public static void print(double d) { System.out.println("double: " + d); }
void f() {}; boolean f() { return true; }; // 只調(diào)用f()無法區(qū)分是哪個方法5.3 默認(rèn)構(gòu)造器
如果你的類中沒有構(gòu)造器,則編譯器會幫你自動創(chuàng)建一個默認(rèn)構(gòu)造器??梢酝ㄟ^反編譯.class文件來驗證這一點。
如果你自己定義了一個構(gòu)造方法,則編譯器不會幫你創(chuàng)建默認(rèn)構(gòu)造器。
5.4 this關(guān)鍵字下述代碼中,有兩個對象a1、a2,按照面向過程的函數(shù)形式,在執(zhí)行func()函數(shù)的時候,怎么知道是被a1、a2調(diào)用呢?為了能用面向?qū)ο蟮恼Z法來編寫代碼,編譯器做了一些幕后工作。它暗自把“所操作的對象”作為第一個參數(shù)傳遞給func()函數(shù),即func(a1)。這是內(nèi)部的表示形式,我們并不能這樣寫代碼。
public class Test { public static void main(String[] args) { A a1 = new A(); A a2 = new A(); a1.func(); a2.func(); } } class A { void func() {} }
this關(guān)鍵字只能在方法內(nèi)部使用,表示對“調(diào)用方法的那個對象的引用?!?/strong>
有人喜歡將this放到每個方法調(diào)用和字段引用前,千萬不要這么做!
當(dāng)需要返回當(dāng)前對象的引用時,可以通過return this;
5.4.1 在構(gòu)造器中調(diào)用構(gòu)造器可以通過this(params);來調(diào)用其他的構(gòu)造方法
可以通過this調(diào)用一個其他的構(gòu)造方法,但不能調(diào)用兩個及以上
通過this調(diào)用其他的構(gòu)造方法必須放到該構(gòu)造方法的第一行
構(gòu)造方法不能通過this調(diào)用自己
public Test(int i) { System.out.println("Test " + i); } public Test(String str) { System.out.println("Test " + str); } // (1) public Test() { this(1); // this("imbug"); System.out.println("Test"); } public static void main(String[] args) { Test test = new Test(); }5.4.2 static方法
static方法中不能使用this關(guān)鍵字
static方法中不能調(diào)用非靜態(tài)方法,反之則可以
5.5 清理:終結(jié)處理和垃圾回收Java的垃圾回收器(GC)負(fù)責(zé)回收無用對象占據(jù)的內(nèi)存資源
假定你的對象(不是通過new)獲得了一塊“特殊”的內(nèi)存區(qū)域,由于GC只知道new分配的內(nèi)存,所以它不知道如何釋放該對象的“特殊”內(nèi)存區(qū)域。為了應(yīng)付這種情況,Java允許在類中定義一個名為finalize()的方法。
5.5.1 finalize()方法一旦GC準(zhǔn)備釋放對象的存儲空間,首先調(diào)用該方法;并且在下一次垃圾回收動作發(fā)生時,才會真正回收對象占用的內(nèi)存。即調(diào)用該方法但時候,對象還沒有被回收。
finalize()方法不是C++中的析夠方法,
在C++中對象一定會被銷毀(代碼無Bug),但是在Java里的對象并非總是被垃圾回收。
垃圾回收只與內(nèi)存相關(guān),也就是說使用GC的唯一原因是為了回收程序不再使用的內(nèi)存。
上述討論了,對象可能會獲得一塊“特殊”的內(nèi)存區(qū)域,這主要發(fā)生在JNI本地方法的情況下,本地方法是在Java中使用非Java代碼的方式。非Java代碼可能會調(diào)用C的malloc()來分配存儲空間,而且除了free()方法否則其存儲空間將得不到釋放,從而造成內(nèi)存泄漏。此時就可以在finalize()中調(diào)用free()方法,清理本地對象。
不建議用finalize方法完成“非內(nèi)存資源”的清理工作,但也可以作為確保某些非內(nèi)存資源(如Socket、文件等)釋放的一個補(bǔ)充。
System.gc()與System.runFinalization()方法增加了finalize方法執(zhí)行的機(jī)會,但不保證一定會執(zhí)行。
用戶可以手動調(diào)用對象的finalize方法,但并不影響GC對finalize的行為,即沒有卵用~
finalize()執(zhí)行流程
5.5.2 你必須實施清理Java不允許創(chuàng)建局部對象(即堆棧上的對象),必須使用new創(chuàng)建對象。
無論是“垃圾回收”還是“終結(jié)”,都不保證一定會發(fā)生。
5.5.3 終結(jié)條件如果某個對象的內(nèi)存可以被安全釋放了,例如對象代表了一個打開的文件,那么回收內(nèi)存前必須保證文件關(guān)閉。這個在finalize()中可以檢驗文件的狀態(tài)。
System.gc()用于強(qiáng)制進(jìn)行終結(jié)動作。
@Override protected void finalize() throws Throwable { super.finalize(); // if(文件未安全關(guān)閉) System.out.println("error"); } public static void main(String[] args) { func(); System.gc(); } public static void func() { Test t1 = new Test(); Test t2 = new Test(); }5.5.4 GC如何工作
更詳細(xì)內(nèi)容請看JVM工作原理?。?!
GC會整理堆內(nèi)存空間,因此導(dǎo)致new新建對象時的內(nèi)存分配速度
每個對象都含有一個計數(shù)器,當(dāng)引用連接至對象時+1,引用離開作用域或被置為null時-1。GC遍歷全部對象,發(fā)現(xiàn)計數(shù)器為0的時候就會釋放其內(nèi)存。
優(yōu)點:簡單
缺點:慢、循環(huán)引用問題、對象應(yīng)該被回收但引用計數(shù)不為零
引用計數(shù)只是為了說明GC的工作方式,但實際上似乎沒有任何Java虛擬機(jī)實現(xiàn)過。
原理:每個“活”的對象,一定能追溯到其存活在堆?;蜢o態(tài)存儲區(qū)之中的引用。
方法:從堆棧和靜態(tài)存儲區(qū)開始,遍歷所有引用;然后追蹤它所引用的對象,然后是這些對象包含的所有對象,反復(fù)進(jìn)行直至“根源于堆棧和靜態(tài)存儲區(qū)的引用”所形成的網(wǎng)絡(luò)被全部訪問完為止
先暫停程序的運(yùn)行,然后將全部活的對象從當(dāng)前堆復(fù)制到另一個堆,沒有復(fù)制的都是垃圾;新堆里的對象在內(nèi)存中時連續(xù)的
不屬于后臺回收模式,因為要暫停程序的運(yùn)行
把對象從一個堆復(fù)制到另一個堆時,所有指向它們的引用都必須要修正。
效率低的原因(1):需要兩個分離的堆,因此需要兩倍的內(nèi)存空間
效率低的原因(2):程序穩(wěn)定后垃圾很少,即需要存活的對象遠(yuǎn)大于垃圾數(shù)量,此時復(fù)制到另一個堆非常浪費(fèi)。
用根搜索算法找到所有存活的對象并標(biāo)記(此過程不回收),當(dāng)全部標(biāo)記工作完成的時候,清理所有沒有標(biāo)記的對象
缺點(1):導(dǎo)致內(nèi)存空間不連續(xù)
缺點(2):也會暫停程序
JVM中,內(nèi)存以較大的“塊”為單位;如果對象比較大,它會占據(jù)多帶帶的塊;有了塊之后,GC就可以在回收的時候往廢棄的塊中拷貝對象了
每個塊用相應(yīng)的代數(shù)(generation count)來記錄是否存活;如果塊在某處被引用,其代數(shù)會增加;GC會對上次回收動作之后新分配的塊進(jìn)行整理
GC會定期進(jìn)行完整的清理動作,大型對象不會被復(fù)制但是其代數(shù)會增加;小型對象的那些塊則被復(fù)制并整理
JVM會進(jìn)行監(jiān)視,如果所有對象都很穩(wěn)定,垃圾回收器的效率降低的話,就切換到標(biāo)記-清掃模式;同樣,JVM會跟蹤標(biāo)記-清掃的效果,要是堆空間出現(xiàn)很多碎片,就會切換回停止-復(fù)制模式。
即使編譯器(Just-In-Time JIT):可以把程序全部或部分翻譯成機(jī)器碼來提高運(yùn)行速度。當(dāng)需要裝載某個類時,編譯器會先找到其.class文件,然后將該類的字節(jié)碼裝入內(nèi)存。此時,有兩種方案可供選擇:
讓即時編譯器編譯所有代碼:這種操作散落在整個程序的聲明周期內(nèi),累加起來耗時更長;會增加可執(zhí)行代碼的長度,造成頁面調(diào)度
惰性評估:意思是即時編譯器只在必要的時候才編譯代碼,這樣,從不會被執(zhí)行的代碼也許就壓根不會被JIT所編譯。
5.6 成員初始化Java盡量保證:所有變量使用前一定會初始化
局部變量:不會自動初始化,而是編譯錯誤
類成員變量:類的每個基本類型數(shù)據(jù)成員都保證會有初始值;引用類型為null
定義類成員變量的時候給它賦值——(1)
通過調(diào)用某個方法來提供初值——(2)
注意:(2)、(3)不能顛倒順序,因為存在向前引用。
缺點:這種方式所有成員有相同的屬性
public class Test { // (1) int a = 10; // (2) int i = f(); // (3) int j = g(i); int g(int n) { return n; } int f() { return 1; } public static void main(String[] args) { Test t = new Test(); } }5.7 構(gòu)造器初始化
無法阻止自動初始化的進(jìn)行,它發(fā)生在構(gòu)造器被調(diào)用之前!
5.7.1 初始化順序遍歷定義的先后順序決定了初始化的順序。
5.7.2 靜態(tài)數(shù)據(jù)的初始化靜態(tài)數(shù)據(jù)跟非靜態(tài)數(shù)據(jù)的默認(rèn)初值是一致的。
先初始化靜態(tài)對象,然后初始化非靜態(tài)對象。
靜態(tài)初始化只有在必要的時候執(zhí)行,如創(chuàng)建第一個該類對象或調(diào)用靜態(tài)方法的時候執(zhí)行。
在調(diào)用該類的靜態(tài)方法或者首次new對象(構(gòu)造器其實也是靜態(tài)方法)的時候,Java解釋器查找類路徑定位到該類的.class文件。
載入該.class文件,靜態(tài)數(shù)據(jù)進(jìn)行初始化,執(zhí)行靜態(tài)代碼塊。
當(dāng)new對象創(chuàng)建對象的時候,首先在堆內(nèi)存中為此對象分配足夠的內(nèi)存空間。
把此存儲空間清零,即所有非靜態(tài)基本數(shù)據(jù)類型置為0,對象類型置為null
執(zhí)行非靜態(tài)數(shù)據(jù)初始化動作。
執(zhí)行構(gòu)造器。
5.7.3 顯式的靜態(tài)初始化即靜態(tài)代碼塊。
在調(diào)用該類的靜態(tài)方法或者首次new對象的時候執(zhí)行,即和靜態(tài)數(shù)據(jù)初始化相同的條件,但是發(fā)生在靜態(tài)數(shù)據(jù)初始化之后。
5.7.4 非靜態(tài)實例初始化Java中也有被稱為實例初始化的語法,用來初始化每一個對象的非靜態(tài)變量。
實例初始代碼塊和成員變量的初始化順序是按照遍歷的先后順序執(zhí)行的,但兩者執(zhí)行都在構(gòu)造方法之前。即如果(1)、(2)位置改變,輸出會變成213。
這種語法對于支持“匿名內(nèi)部類”的初始化是必須的
// (1) { System.out.println(1); } // (2) int i = func(); int func() { System.out.println(2); return 2; } // (3) Test() { System.out.println(3); } public static void main(String[] args) { new Test(); // output 123 }5.8 數(shù)組初始化
int[] arr、int arr[]這兩種寫法都可以,但更推薦前者。
為了給數(shù)組創(chuàng)建相應(yīng)的內(nèi)存空間,必須初始化數(shù)組的大?。换蛘叱跏蓟臅r候直接初始化數(shù)組的值(int[] arr = {1, 2, 3}),此時存儲空間的分配由編譯器負(fù)責(zé)。
所有數(shù)組都有一個固定成員length獲知成員數(shù)量,但不可以修改這個值。
數(shù)組坐標(biāo)從0開始。
數(shù)組中的元素會自動初始化為空值。
5.8.1 可變參數(shù)列表void func(String... args) {}
可變參數(shù)列表可以接受不傳任何參數(shù),即func()是可行的。
可變列表與自動包裝機(jī)制可以和諧相處
基本數(shù)據(jù)類型:class、空格、多個(值為數(shù)組維數(shù))[、對應(yīng)數(shù)據(jù)類型的標(biāo)識
對象類型:class、空格、多個(值為數(shù)組維數(shù))[、大寫L、對應(yīng)數(shù)據(jù)類型的全路徑、;
System.out.println(new int[0].getClass()); // class [I System.out.println(new Integer[0].getClass()); // class [Ljava.lang.Integer; System.out.println(new long[0].getClass()); // class [J System.out.println(new double[0].getClass()); // class [D System.out.println(new int[0][0].getClass()); // class [[I System.out.println(new int[0][0][0].getClass()); // class [[[I System.out.println(new String[0].getClass()); // class [Ljava.lang.String; System.out.println(new String[0][0].getClass()); // class [[Ljava.lang.String;
此段代碼編譯失敗,因為編譯器發(fā)現(xiàn)有多個方法可以調(diào)用。
public static void main(String[] args) { func(1, "a"); func("a", "b"); } static void func(int i, Character... args) { System.out.println("first"); } static void func(Character... args) { System.out.println("second"); }5.9 枚舉類型
枚舉常量命名規(guī)范:全部大寫字母用下劃線分割
枚舉會自動創(chuàng)建toString()方法
會自動創(chuàng)建ordinal()方法,用來表示枚舉常量的聲明順序
枚舉可以在switch中使用
public class Test { public static void main(String[] args) { Color green = Color.GREEN; Color red = Color.RED; System.out.println(green + " " + green.ordinal()); System.out.println(red + " " + red.ordinal()); } } enum Color { RED, GREEN; }
在代碼中Enum禁止繼承
// final class 禁止繼承 final class Color extends Enum { public static Color[] values() { return (Color[])$VALUES.clone(); } public static Color valueOf(String name) { return (Color)Enum.valueOf(s2/Color, name); } // 私有構(gòu)造方法,所以無法用new創(chuàng)建對象 private Color(String s, int i) { super(s, i); } public static final Color RED; public static final Color GREEN; private static final Color $VALUES[]; static { RED = new Color("RED", 0); GREEN = new Color("GREEN", 1); $VALUES = (new Color[] { RED, GREEN }); } }
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/72184.html
摘要:多態(tài)的作用是消除類型之間的耦合關(guān)系。編寫構(gòu)造器準(zhǔn)則用盡可能簡單的方法使對象進(jìn)入正常狀態(tài),如果可以的話,避免調(diào)用其他方法。 點擊進(jìn)入我的博客 在面向?qū)ο蟮某绦蛟O(shè)計語言中,多態(tài)是繼數(shù)據(jù)抽象(封裝)和繼承之后的第三種基本特征。多態(tài)通過分離做什么和怎么做,從另一角度將接口和實現(xiàn)分離開來。多態(tài)的作用是消除類型之間的耦合關(guān)系。 8.1 再論向上轉(zhuǎn)型 對象既可以作為它自己的本類使用,也可以作為它的...
摘要:在類的構(gòu)造方法中。對基類構(gòu)造器的調(diào)用必須放到子類構(gòu)造器的第一行。約定用大寫字母下劃線命名規(guī)范空白空白指被聲明為但又未給定初值的域,但可以在構(gòu)造方法必須在域的定義處代碼塊或構(gòu)造器中對進(jìn)行賦值。 點擊進(jìn)入我的博客 復(fù)用代碼是Java眾多引人注目的功能之一,但要成為極具革命性的語言,僅僅能夠復(fù)制代碼并對之加以改變是不夠的,它還必須能夠做更多的事情。 7.1 組合 組合語法 就是在當(dāng)前類中產(chǎn)...
摘要:一引用操縱對象在的世界里,一切都被視為對象。特點創(chuàng)建程序時,需要知道存儲在棧內(nèi)所有數(shù)據(jù)的確切生命周期,以便上下移動堆棧指針。因為,指向同一塊內(nèi)存空間除了通過對象引用靜態(tài)變量,我們還可以通過類直接引用靜態(tài)變量 一、引用操縱對象 在Java的世界里,一切都被視為對象。操縱的標(biāo)識符實際上是對象的引用, 例如:遙控器與電視的關(guān)系。 可以在沒有對象關(guān)聯(lián)的情況下,擁有一個引用。沒有電視機(jī),也可以擁...
摘要:一旦異常被拋出,就表明錯誤已無法挽回,也不能回來繼續(xù)執(zhí)行。這種在編譯時被強(qiáng)制檢查的異常稱為被檢查的異常。通過獲取原始異常。構(gòu)造器對于在構(gòu)造階段可能會拋出異常,并要求清理的類,最安全的做法是使用嵌套的子句。 點擊進(jìn)入我的博客 Java異常處理的目的在于通過使用少于目前數(shù)量的代碼來簡化大型、可靠的程序的生成,并且通過這種方式可以使你更自信:你的應(yīng)用中沒有未處理的錯誤。 12.1 概念 異...
摘要:注本文首發(fā)于公眾號,可長按或掃描下面的小心心來訂閱關(guān)于構(gòu)造器與初始化無參構(gòu)造器默認(rèn)構(gòu)造器自己未寫編譯器幫忙自動創(chuàng)建的若自行定義了構(gòu)造器無論參數(shù)有否,編譯器便停止默認(rèn)創(chuàng)建動作類里的對象引用默認(rèn)初始化為,基本類型初始化為構(gòu)造器也是類的靜態(tài)方法四 showImg(https://segmentfault.com/img/remote/1460000015723687); 注: 本文首發(fā)于 ...
閱讀 2361·2021-11-24 11:16
閱讀 2047·2021-09-30 09:47
閱讀 2019·2021-09-10 10:51
閱讀 1330·2019-08-30 14:08
閱讀 3150·2019-08-30 13:47
閱讀 1536·2019-08-30 13:02
閱讀 3241·2019-08-29 12:29
閱讀 3212·2019-08-26 17:05