此文已經(jīng)同步至個人站點博客,點擊下方鏈接可以體驗更加閱讀模式:
《java題庫》
final,finalize,finally關鍵字
1.finalize和final關鍵字
什么是finalize()方法?finalize()方法什么時候被調(diào)用?
答: Java允許在類中定義一個名為finalize()的方法,一旦垃圾回收器準備好釋放對象占用的存儲空間,將首先調(diào)用finalize()方法,并且在下一次垃圾回收動作發(fā)生時,才會真正回收對象占用的內(nèi)存。
析構函數(shù)(finalization)的目的是什么
析構函數(shù)目的是撤銷對象前、完成一些清理工作,比如釋放資源。釋放了之后這些資源可以被回收,重新利用。
final關鍵字有哪些用法
final關鍵字主要用于修飾類、類成員、方法、以及方法的形參。
- final修飾成員屬性:說明該成員屬性是常量,不能被修改;
- final修飾類,該類是最終類,不能被繼承。
- final修飾方法:該方法是最終方法,不能被重寫。
- final關鍵字修飾形參:1:當形參被修飾為final,那么該形參所屬的方法中不能被篡改。
2. final 與 static 關鍵字可以用于哪里?它們的作用是什么?
用于修飾成員變量和成員方法,可以理解為“全局常量”,對于變量表示一旦給定值就不可以修改,并且通過類名可以訪問;對于方法表示不可覆蓋,并且可以通過類名直接訪問
3. final, finally, finalize的區(qū)別(或者說final、finalize 和 finally 的不同之處?)
final關鍵字可以用于類,方法,變量前,用來表示該關鍵字修飾的類,方法,變量具有不可變的特性。
- final關鍵字用于基本數(shù)據(jù)類型前:這時表明該關鍵字修飾的變量是一個常量,在定義后該變量的值就不能被修改。
- final關鍵字用于方法聲明前:這時意味著該方法時最終方法,只能被調(diào)用,不能被覆蓋,但是可以被重載。
- final關鍵字用于類名前:此時該類被稱為最終類,該類不能被其他類繼承。
finalize()方法來自于java.lang.Object,用于回收資源??梢詾槿魏我粋€類添加finalize方法。finalize方法將在垃圾回收器清除對象之前調(diào)用。在實際應用中,不要依賴使用該方法回收任何短缺的資源,這是因為很難知道這個方法什么時候被調(diào)用。
finally,當代碼拋出一個異常時,就會終止方法中剩余代碼的處理,并退出這個方法的執(zhí)行。finally塊是程序在正常情況下或異常情況下都會運行的。比較適合用于既要處理異常又有資源釋放的代碼,保證了資源的合理回收。
補充: try / catch / finally 中return的關系?
1、不管有木有出現(xiàn)異常,finally塊中代碼都會執(zhí)行;
2、當try和catch中有return時,finally仍然會執(zhí)行;
3、finally是在return后面的表達式運算后執(zhí)行的(此時并沒有返回運算后的值,而是先把要返回的值保存起來,管finally中的代碼怎么樣,返回的值都不會改變,仍然是之前保存的值),所以函數(shù)返回值是在finally執(zhí)行前確定的;
4、finally中最好不要包含return,否則程序會提前退出,返回值不是try或catch中保存的返回值。
4. 能否在運行時向 static final 類型的賦值
不可以,被static final修飾的變量只能在被定義的時候或者類的靜態(tài)代碼塊中初始化,一旦賦值后就不能在改變了。static final相當于類常量,就是在類被加載進內(nèi)存的時候就要為屬性分配內(nèi)存,static塊就是類被加載的時候執(zhí)行且被執(zhí)行一次,所以可以在其中進行初始化。
5. 使用final關鍵字修飾一個變量時,是引用不能變,還是引用的對象不能變?
是引用不能變(final引用恒定不變),引用的對象內(nèi)容還是可以變的
8. throws, throw分別代表什么意義?
throw是指的語句拋出一個異常,throws指的是聲明方法可能拋出的異常類型
9、Java 有幾種修飾符?分別用來修飾什么
類的修飾符:
-
public可以在其他任何類中使用,默認為統(tǒng)一包下的任意類。 -
abstract抽象類,不能被實例化,可以包含抽象方法,抽象方法沒有被實現(xiàn),無具體功能,只能衍生子類。 -
final不能被繼承。
成員變量
-
訪問修飾符:
訪問修飾符 static類變量:一個類所擁有的變量,不是類的每個實例有的變量。類變量是指不管類創(chuàng)建了多少對象,系統(tǒng)僅在第一次調(diào)用類的時候為類變量分配內(nèi)存,所有對象共享該類的類變量,因此可以通過類本身或者某個對象來訪問類變量。final:常量。volatile:聲明一個可能同時被并存運行的幾個線程所控制和修改的變量。abstract:只有聲明部分,方法體為空,具體在子類中完成。transient:(過度修飾符)指定該變量是系統(tǒng)保留,暫無特別作用的臨時性變量。
方法修飾符:
- 訪問修飾符
public(公共控制符)
private(私有控制符)指定此方法只能有自己類等方法訪問,其他的類不能訪問(包括子類)
protected(保護訪問控制符)指定該方法可以被它的類和子類進行訪問。 -
final,指定該方法不能被重載。 -
static,指定不需要實例化就可以激活的一個方法。 -
synchronize,同步修飾符,在多個線程中,該修飾符用于在運行前,對他所屬的方法加鎖,以防止其他線程的訪問,運行結束后解鎖。 -
native,本地修飾符。指定此方法的方法體是用其他語言在程序外部編寫的。
volatile關鍵字
volatile是一個特殊的修飾符,只有成員變量才能使用它。在Java并發(fā)程序缺少同步類的情況下,多線程對成員變量的操作對其它線程是透明的。volatile變量可以保證下一個讀取操作會在前一個寫操作之后發(fā)生,就是上一題的volatile變量規(guī)則。
原理以及底層實現(xiàn)可參考:
1、volatile 修飾符的有過什么實踐
一種實踐是用 volatile 修飾 long 和 double 變量,使其能按原子類型來讀寫。double 和 long 都是64位寬,因此對這兩種類型的讀是分為兩部分的,第一次讀取第一個 32 位,然后再讀剩下的 32 位,這個過程不是原子的,但 Java 中 volatile 型的 long 或double 變量的讀寫是原子的。volatile 修復符的另一個作用是提供內(nèi)存屏障(memory barrier),例如在分布式框架中的應用。簡單的說,就是當你寫一個 volatile 變量之前,Java 內(nèi)存模型會插入一個寫屏障(write barrier),讀一個 volatile 變量之前,會插入一個讀屏障(read barrier)。意思就是說,在你寫一個volatile域時,能保證任何線程都能看到你寫的值,同時,在寫之前,也能保證任何數(shù)值的更新對所有線程是可見的,因為內(nèi)存屏障會將其他所有寫的值更新到緩存。
2、volatile 變量是什么?volatile 變量和 atomic 變量有什么不同
Java語言提供了一種稍弱的同步機制,即volatile變量,用來確保將變量的更新操作通知到其他線程。當把變量聲明為volatile類型后,編譯器與運行時都會注意到這個變量是共享的,因此不會將該變量上的操作與其他內(nèi)存操作一起重排序。volatile變量不會被緩存在寄存器或者對其他處理器不可見的地方,因此在讀取volatile類型的變量時總會返回最新寫入的值。
在訪問volatile變量時不會執(zhí)行加鎖操作,因此也就不會使執(zhí)行線程阻塞,因此volatile變量是一種比sychronized關鍵字更輕量級的同步機制。
3、volatile 類型變量提供什么保證?能使得一個非原子操作變成原子操作嗎?
volatile只提供了保證訪問該變量時,每次都是從內(nèi)存中讀取最新值,并不會使用寄存器緩存該值——每次都會從內(nèi)存中讀取。而對該變量的修改,volatile并不提供原子性的保證。那么編譯器究竟是直接修改內(nèi)存的值,還是使用寄存器修改都符合volatile的定義。所以,一句話,volatile并不提供原子性的保證。
一個典型的例子是在類中有一個 long 類型的成員變量。如果你知道該成員變量會被多個線程訪問,如計數(shù)器、價格等,你最好是將其設置為 volatile。為什么?因為 Java 中讀取 long 類型變量不是原子的,需要分成兩步,如果一個線程正在修改該 long 變量的值,另一個線程可能只能看到該值的一半(前 32 位)。但是對一個 volatile 型的 long 或 double 變量的讀寫是原子。
補充另外一種思路:volatile 類型變量提供什么保證?
volatile 變量提供順序和可見性保證,例如,JVM 或者 JIT為了獲得更好的性能會對語句重排序,但是 volatile 類型變量即使在沒有同步塊的情況下賦值也不會與其他語句重排序。 volatile 提供happens-before 的保證,確保一個線程的修改能對其他線程是可見的。某些情況下,volatile 還能提供原子性,如讀 64 位數(shù)據(jù)類型,像 long 和 double 都不是原子的,但 volatile 類型的 double 和long 就是原子的。
4、能創(chuàng)建 volatile 數(shù)組嗎?
可以,volatile修飾的變量如果是對象或數(shù)組之類的,其含義是對象獲數(shù)組的地址具有可見性,但是數(shù)組或對象內(nèi)部的成員改變不具備可見性。
換句話說就是:Java 中可以創(chuàng)建 volatile 類型數(shù)組,不過只是一個指向數(shù)組的引用,而不是整個數(shù)組。如果改變引用指向的數(shù)組,將會受到volatile的保護,但是如果多個線程同時改變數(shù)組的元素,volatile 標示符就不能起到之前的保護作用了。
5、transient變量有什么特點?
- 一旦變量被
transient修飾,變量將不再是對象持久化的一部分,該變量內(nèi)容在序列化后無法獲得訪問。 -
transient關鍵字只能修飾變量,而不能修飾方法和類。注意,本地變量是不能被transient關鍵字修飾的。變量如果是用戶自定義類變量,則該類需要實現(xiàn)Serializable接口。 - 被
transient關鍵字修飾的變量不能被序列化,一個靜態(tài)變量不管是否被transient修飾,均不能被序列化。
6、super什么時候使用?
super主要存在于子類方法中,用于指向子類對象中父類對象。
1:訪問父類的屬性
2:訪問父類的函數(shù)
3:訪問父類的構造函數(shù)
7、public static void 寫成 static public void會怎樣?
一樣的
8、說明一下public static void main(String args[])這段聲明里每個關鍵字的作用?
主函數(shù)是什么:主函數(shù)是一個特殊的函數(shù),作為程序的入口,可以被jvm識別。
主函數(shù)的定義:
-
public:代表該函數(shù)的訪問權限是最大的。 -
static:代表主函數(shù)隨著類的加載,就已經(jīng)存在了。 -
void: 主函數(shù)沒有具體的返回值 -
main: 不是關鍵字,是一個特殊的單詞可以被jvm識別。 -
(String[] args)函數(shù)的參數(shù),參數(shù)類型是一個數(shù)組,該數(shù)組中的元素是字符串。字符串類型的數(shù)組。
主函數(shù)的格式是固定的:jvm能夠識別
9、sizeof 是Java 的關鍵字嗎?
不是,C和C++用sizeof()解決移植問題,java不需要。
static關鍵字
1、static class 與 non static class的區(qū)別
靜態(tài)變量跟實例變量的區(qū)別:
- 靜態(tài)變量:由static修飾,在JVM中,靜態(tài)變量的加載順序在對象之前,因此靜態(tài)變量不依附于對象存在,可以在不實例化類的情況下直接使用靜態(tài)變量。靜態(tài)變量屬于類,不屬于類中任何一個對象,因此靜態(tài)變量又叫做類變量,一個類不管創(chuàng)建多少個對象(對象是類的一個實例),靜態(tài)變量在內(nèi)存中有且僅有一個。
- 實例變量:必須依附于對象存在,只有實例化類后才可以使用此類中的實例變量。
靜態(tài)方法跟實例方法的區(qū)別:
- 靜態(tài)方法:方法用static關鍵字修飾,靜態(tài)方法與靜態(tài)成員變量一樣,屬于類本身,在類裝載的時候被裝載到內(nèi)存,不自動進行銷毀,會一直存在于內(nèi)存中,直到JVM關閉。使用時也是不需要實例化類,能夠直接使用。靜態(tài)方法無法被重寫。需要注意的是:在靜態(tài)方法中只能訪問類中的靜態(tài)成員跟靜態(tài)方法,不能直接訪問類中的實例變量跟實例方法
- 實例化方法:屬于實例對象,實例化后才會分配內(nèi)存,必須通過類的實例來引用。不會常駐內(nèi)存,當實例對象被JVM 回收之后,也跟著消失。
線程安全和效率說明:
- 線程安全:靜態(tài)方法是共享代碼段,靜態(tài)變量是共享數(shù)據(jù)段。既然是“共享”就有并發(fā)的問題。非靜態(tài)方法是針對確定的一個對象的,所以不會存在線程安全的問題。
- 如果靜態(tài)方法在系統(tǒng)中定義太多,會占用大量的資源,最后造成內(nèi)存溢出,所以靜態(tài)方法不能濫用。
2、static 關鍵字是什么意思?Java中是否可以覆蓋(override)一個private或者是static的方法,靜態(tài)類型有什么特點?
static表示靜態(tài)的意思,可用于修飾成員變量和成員函數(shù),被靜態(tài)修飾的成員函數(shù)只能訪問靜態(tài)成員,不可以訪問非靜態(tài)成員。靜態(tài)是隨著類的加載而加載的,因此可以直接用類進行訪問。 重寫是子類中的方法和子類繼承的父類中的方法一樣(函數(shù)名,參數(shù),參數(shù)類型,反回值類型),但是子類中的訪問權限要不低于父類中的訪問權限。重寫的前提是必須要繼承,private修飾不支持繼承,因此被私有的方法不可以被重寫。在Java中,如果父類中含有一個靜態(tài)方法,且在子類中也含有一個返回類型、方法名、參數(shù)列表均與之相同的靜態(tài)方法,那么該子類實際上只是將父類中的該同名方法進行了隱藏,而非重寫。換句話說,父類和子類中含有的其實是兩個沒有關系的方法,它們的行為也并不具有多態(tài)性。
針對繼承中private的問題:
子類繼承了父類的所有屬性和方法或子類擁有父類的所有屬性和方法是對的,只不過父類的私有屬性和方法,子類是無法直接訪問到的。即只是擁有,但是無法使用。當然私有屬性可以通過public修飾的getter和setter方法訪問到的,但是私有方法不行。
3、main() 方法為什么必須是靜態(tài)的?能不能聲明 main() 方法為非靜態(tài)?
用static修飾的就是靜態(tài)方法。靜態(tài)方法不依靠對象而存在。其直接與類有關,只要包含在類中,就可以得到執(zhí)行,而不一定依附于對象的存在而執(zhí)行。因此,main方法作為程序的入口方法,在這之前是不可能有任何對象被建立的,也就在main之前包括main自身不可能是非靜態(tài)方法。所以main方法一定是靜態(tài)的,有類就行——從而得到執(zhí)行,進而有更多靜態(tài)或非靜態(tài)方法得到執(zhí)行。
4、是否可以從一個靜態(tài)(static)方法內(nèi)部發(fā)出對非靜態(tài)(non-static)方法的調(diào)用
不可以,靜態(tài)函數(shù)中不能訪問非靜態(tài)成員變量,只能訪問靜態(tài)變量。因為靜態(tài)優(yōu)先于對象存在.靜態(tài)方法中更不可以出現(xiàn)this
5、靜態(tài)變量在什么時候加載?編譯期還是運行期?靜態(tài)代碼塊加載的時機呢?
靜態(tài)是隨著類的加載而加載的,JVM的代碼編譯運行順序是編譯、類的加載到執(zhí)行,屬于二者的過渡期。靜態(tài)代碼塊也是如此。
6、成員方法是否可以訪問靜態(tài)變量?為什么靜態(tài)方法不能訪問成員變量?
成員方法中可以訪問靜態(tài)成員變量。
請看下面代碼來確定程序的打印先后順序:
public class test {
public static void main(String[] args) {
new test();
}
static int num = 4;
{
num += 3;
System.out.println(b);
}
int a = 5;
{
System.out.println(c);
}
test() {System.out.println(d);}
static {System.out.println(a);}
static void run() {System.out.println(e);}
}
執(zhí)行順序如下:
public class test { 1.第一步,準備加載類
public static void main(String[] args) {
new test(); 4.第四步,new一個類,但在new之前要處理匿名代碼塊
}
static int num = 4; 2.第二步,靜態(tài)變量和靜態(tài)代碼塊的加載順序由編寫先后決定
{
num += 3;
System.out.println(b);5.第五步,按照順序加載匿名代碼塊,代碼塊中有打印
}
int a = 5; 6.第六步,按照順序加載變量
{ 成員變量第三個
System.out.println(c); 7.第七步,按照順序打印c
}
如果將構造函數(shù)和構造代碼塊呼喚,依舊還是先執(zhí)行構造代碼塊。
test() { 類的構造函數(shù),第四個加載
System.out.println(d); 8.第八步,最后加載構造函數(shù),完成對象的建立
}
static { 3.第三步,靜態(tài)塊,然后執(zhí)行靜態(tài)代碼塊,因為有輸出,故打印a
System.out.println(a);
}
static void run() 靜態(tài)方法隨類加載,調(diào)用的時候才執(zhí)行 注意看,e沒有加載
{
System.out.println(e);
}
}
靜態(tài)塊(靜態(tài)變量)——成員變量——構造方法——靜態(tài)方法
1、靜態(tài)代碼塊(只加載一次) 2、構造方法(創(chuàng)建一個實例就加載一次)3、靜態(tài)方法需要調(diào)用才會執(zhí)行.
如果類還沒有被加載:
- 1、先執(zhí)行父類的靜態(tài)代碼塊和靜態(tài)變量初始化,并且靜態(tài)代碼塊和靜態(tài)變量的執(zhí)行順序只跟代碼中出現(xiàn)的順序有關。
- 2、執(zhí)行子類的靜態(tài)代碼塊和靜態(tài)變量初始化。
- 3、執(zhí)行父類的實例變量初始化
- 4、執(zhí)行父類的構造函數(shù)(有構造代碼塊則先執(zhí)行構造代碼塊)
- 5、執(zhí)行子類的實例變量初始化
- 6、執(zhí)行子類的構造函數(shù)
如果類已經(jīng)被加載:
則靜態(tài)代碼塊和靜態(tài)變量就不用重復執(zhí)行,再創(chuàng)建類對象時,只執(zhí)行與實例相關的變量初始化和構造方法。
補充構造代碼塊:給對象進行初始化。對象一建立就運行并且優(yōu)先于構造函數(shù)。
構造代碼塊和構造函數(shù)的區(qū)別,構造代碼塊是給所有對象進行統(tǒng)一初始化, 構造函數(shù)給對應的對象初始化。
關于類的加載可以重點參考:
switch關鍵字
1、switch 語句中的表達式可以是什么類型數(shù)據(jù)?
switch(A),括號中A的取值可以是byte、short、int、char、String,還有枚舉類型。
2、switch 是否能作用在byte 上,是否能作用在long 上,是否能作用在String上?
Java 7之前,switch后面的括號里面只能放int類型的值,注意是只能放int類型,但是放byte,short,char類型的也可以,是因為byte,short,shar可以自動提升(自動類型轉換)為int,不是說就可以放它們,說白了,你放的byte,short,shar類型,然后他們會自動轉換為int類型(寬化,自動轉換并且安全),其實最后放的還是int類型。String可以了,但是long仍然不行。
1.小的往大的轉換(寬化),自動轉換,有些時候就會自動提升為大的類型,比如switch中
2.大的往小的轉換(窄化)必須強制類型轉換所以long不行,要想行就得強轉如(int)long。同理,float、double也是不行的,要想行就強轉。
3、while 循環(huán)和 do 循環(huán)有什么不同?
while語法格式:
while(布爾表達式){
//語句
}
先判斷布爾表達式,如果為true就會執(zhí)行循環(huán)體中的語句,然后再判斷布爾表達式,如果為true就執(zhí)行循環(huán)體中的語句,一直到布爾表達式為false,然后循環(huán)結束。通常用算術運算符(++ -- 累減)
do/while語法格式:
do{
//語句
}while(布爾表達式);
先執(zhí)行一次循環(huán)體,然后在判斷布爾表達式是不是true,如果是就繼續(xù)執(zhí)行循環(huán)體,在判斷布爾表達式,直到為false就結束循環(huán)。
兩者的區(qū)別:while是先判斷在執(zhí)行如果判斷不成立,就不會執(zhí)行;do/while是先執(zhí)行在判斷,不管判斷是否成立都會執(zhí)行一次
操作符
1、&操作符和&&操作符有什么區(qū)別
&&運算符是短路與運算。邏輯與跟短路與的差別是非常巨大的,雖然二者都要求運算符左右兩端的布爾值都是true整個表達式的值才是true。&&之所以稱為短路運算是因為,如果&&左邊的表達式的值是false,右邊的表達式會被直接短路掉,不會進行運算。很多時候我們可能都需要用&&而不是&。
2、a = a + b 與 a += b 的區(qū)別?
★ =:賦值運算符,在編譯器將右邊的表達式結果計算出來后,和左邊的變量類型比較精度,如果左邊的變量精度低于右邊的結果的精度,編譯器會顯式的報錯,告訴程序員去強制轉型。(若a精度類型弱于b,a = a + b出錯,編譯檢查報錯)最后將表達式的結果復制到變量所在的內(nèi)存區(qū)。
★ +=:暫且稱之為運算符,編譯器自動隱式直接將+=運算符后面的操作數(shù)強制裝換為前面變量的類型,然后在變量所在的內(nèi)存區(qū)上直接根據(jù)右邊的操作數(shù)修改左邊變量內(nèi)存存儲的二進制數(shù)值最后達到和賦值運算符相同的目的。與前者相比,由于后者是位操作,效率也較前者高。
3、30.1 == 0.3 將會返回什么?true 還是 false?
False,類型不一致。
4、float f=3.4; 是否正確?
不正確。3.4是雙精度數(shù),將雙精度型(double)賦值給浮點型(float)屬于下轉型(down-casting,也稱為窄化)會造成精度損失,因此需要強制類型轉換float f =(float)3.4; 或者寫成float f =3.4F;。
5、short s1 = 1; s1 = s1 + 1;有錯嗎?short s1 = 1; s1 += 1;有錯嗎?
對于short s1 = 1; s1 = s1 + 1;由于1是int類型,因此s1+1運算結果也是int 型,需要強制轉換類型才能賦值給short型。而short s1 = 1; s1 += 1;可以正確編譯,因為s1+= 1;相當于s1 = (short)(s1 + 1);其中有隱含的強制類型轉換。
