1、Java 有沒(méi)有 goto 語(yǔ)句?
goto 是 Java 中的保留字,在目前版本的 Java 中沒(méi)有使用。根據(jù) James Gosling(Java 之父)編寫(xiě)的《The Java ?Programming Language》一書(shū)的附錄中給出了一個(gè) Java 關(guān)鍵字列表,其中有 goto 和 const,但是這兩個(gè)是目前 無(wú)法使用的關(guān)鍵字,因此有些地方將其稱之為保留字,其實(shí)保留字這個(gè)詞應(yīng)該有更廣泛的意義,因?yàn)槭煜?C 語(yǔ)言的程 序員都知道,在系統(tǒng)類庫(kù)中使用過(guò)的有特殊意義的單詞或單詞的組合都被視為保留字。
2、& 和 && 的區(qū)別
&運(yùn)算符有兩種用法:(1)按位與;(2)邏輯與。?
&&運(yùn)算符是短路與運(yùn)算。邏輯與跟短路與的差別是非常巨大的,雖然二者都要求運(yùn)算符左右兩端的布爾值都是 true 整個(gè)表達(dá)式的值才是 true。 &&之所以稱為短路運(yùn)算是因?yàn)?,如?amp;&左邊的表達(dá)式的值是 false,右邊的表達(dá)式會(huì)被直接短路掉,不會(huì)進(jìn)行 運(yùn)算。很多時(shí)候我們可能都需要用&&而不是&,例如在驗(yàn)證用戶登錄時(shí)判定用戶名不是 null 而且不是空字符串,應(yīng)當(dāng)寫(xiě)為username != null &&!username.equals(""),二者的順序不能交換,更不能用&運(yùn)算符,因?yàn)榈谝粋€(gè)條件如果不成立,根本不能進(jìn)行字符串的eqals比較,否則會(huì)產(chǎn)生NullPointerException異常。
注意:邏輯或運(yùn)算符(|) 和短路或運(yùn)算符(||)的差別也是如此。
3、在 Java 中,如何跳出當(dāng)前的多重嵌套循環(huán)
在最外層循環(huán)前加一個(gè)標(biāo)記如 A,然后用 break A;可以跳出多重循環(huán)。(Java 中支持帶標(biāo)簽的 break 和 continue ?語(yǔ)句,作用有點(diǎn)類似于 C 和 C++中的 goto 語(yǔ)句,但是就像要避免使用 goto 一樣,應(yīng)該避免使用帶標(biāo)簽的 break ?和 continue,因?yàn)樗粫?huì)讓你的程序變得更優(yōu)雅,很多時(shí)候甚至有相反的作用)。
4、兩個(gè)對(duì)象值相同 (x.equals(y) == true) ,但卻可有不同的 hashCode,這句 話對(duì)不對(duì)?
不對(duì),如果兩個(gè)對(duì)象 x 和 y 滿足 x.equals(y) == true,它們的哈希碼(hashCode)應(yīng)當(dāng)相同。
Java 對(duì)于 eqauls 方法和 hashCode 方法是這樣規(guī)定的:(1)如果兩個(gè)對(duì)象相同(equals 方法返回 true),那 么它們的 hashCode 值一定要相同;
(2)如果兩個(gè)對(duì)象的 hashCode 相同,它們并不一定相同。當(dāng)然,你未必要按照 要求去做,但是如果你違背了上述原則就會(huì)發(fā)現(xiàn)在使用容器時(shí),相同的對(duì)象可以出現(xiàn)在 Set 集合中,同時(shí)增加新元素 的效率會(huì)大大下降(對(duì)于使用哈希存儲(chǔ)的系統(tǒng),如果哈希碼頻繁的沖突將會(huì)造成存取性能急劇下降)。?
關(guān)于 equals 和 hashCode 方法,很多 Java 程序員都知道,但很多人也就是僅僅知道而已,在 Joshua Bloch 的大作《Effective Java》(很多軟件公司),《Effective Java》、《Java 編程思想》以及《重構(gòu):改善既有代碼質(zhì)量》 是 Java 程序員必看書(shū)籍,如果你還沒(méi)看過(guò),那就趕緊去買(mǎi)一本吧)中是這樣介紹 equals 方法的。
首先 equals 方法必須滿足自反性(x.equals(x)必須返回 true)、對(duì)稱性(x.equals(y)返回 true 時(shí),y.equals(x) 也必須返回 true)、傳遞性(x.equals(y)和 y.equals(z)都返回 true 時(shí),x.equals(z)也必須返回 true)和一致性(當(dāng) x 和 y 引用的對(duì)象信息沒(méi)有被修改時(shí),多次調(diào)用 x.equals(y)應(yīng)該得到同樣的返回值),而且對(duì)于任何非 null 值的引 用 x,x.equals(null)必須返回 false。
實(shí)現(xiàn)高質(zhì)量的equals方法的訣竅包括:
1. 使用==操作符檢查"參數(shù)是否為這個(gè)對(duì)象的引用";
2. 使用 instanceof 操作符檢查"參數(shù)是否為正確的類型";3. 對(duì)于類中的關(guān)鍵屬性,檢查參數(shù)傳入對(duì)象的屬性是否與之相匹配;
4. 編寫(xiě)完equals方法后,問(wèn)自己它是否滿足對(duì)稱性、傳遞性、一致性;
5. 重寫(xiě)equals時(shí)總是要重寫(xiě)hashCode;
6. 不要將equals方法參數(shù)中的Object對(duì)象替換為其他的類型,在重寫(xiě)時(shí)不要忘掉 @Override 注解。
5、是否可以繼承 String?
String 類是 final 類,不可以被繼承。繼承 String 本身就是一個(gè)錯(cuò)誤的行為,對(duì) String 類型最好的重用方式是關(guān)聯(lián)關(guān)系(Has-A)和依賴關(guān)系(Use- A)而不是繼承關(guān)系(Is-A)。
6、 當(dāng)一個(gè)對(duì)象被當(dāng)作參數(shù)傳遞到一個(gè)方法后,此方法可改變這個(gè)對(duì)象的屬性,并 可返回變化后的結(jié)果,那么這里到底是值傳遞還是引用傳遞?
是值傳遞。Java 語(yǔ)言的方法調(diào)用只支持參數(shù)的值傳遞。當(dāng)一個(gè)對(duì)象實(shí)例作為一個(gè)參數(shù)被傳遞到方法中時(shí),參數(shù)的 值就是對(duì)該對(duì)象的引用。對(duì)象的屬性可以在被調(diào)用過(guò)程中被改變,但對(duì)對(duì)象引用的改變是不會(huì)影響到調(diào)用者的。C++ 和 C#中可以通過(guò)傳引用或傳輸出參數(shù)來(lái)改變傳入的參數(shù)的值。說(shuō)明:Java 中沒(méi)有傳引用實(shí)在是非常的不方便,這一 點(diǎn)在 Java 8 中仍然沒(méi)有得到改進(jìn),正是如此在 Java 編寫(xiě)的代碼中才會(huì)出現(xiàn)大量的 Wrapper 類(將需要通過(guò)方法 調(diào)用修改的引用置于一個(gè) Wrapper 類中,再將 Wrapper 對(duì)象傳入方法),這樣的做法只會(huì)讓代碼變得臃腫,尤其 是讓從 C 和 C++轉(zhuǎn)型為 Java 程序員的開(kāi)發(fā)者無(wú)法容忍。
7、 重載(overload)和重寫(xiě)(override)的區(qū)別?重載的方法能否根據(jù)返回類型 進(jìn)行區(qū)分?
方法的重載和重寫(xiě)都是實(shí)現(xiàn)多態(tài)的方式,區(qū)別在于前者實(shí)現(xiàn)的是編譯時(shí)的多態(tài)性,而后者實(shí)現(xiàn)的是運(yùn)行時(shí)的多態(tài) 性。重載發(fā)生在一個(gè)類中,同名的方法如果有不同的參數(shù)列表(參數(shù)類型不同、參數(shù)個(gè)數(shù)不同或者二者都不同)則視為 重載;重寫(xiě)發(fā)生在子類與父類之間,重寫(xiě)要求子類被重寫(xiě)方法與父類被重寫(xiě)方法有相同的返回類型,比父類被重寫(xiě)方 法更好訪問(wèn),不能比父類被重寫(xiě)方法聲明更多的異常(里氏代換原則)。重載對(duì)返回類型沒(méi)有特殊的要求。 方法重載的規(guī)則: 1.方法名一致,參數(shù)列表中參數(shù)的順序,類型,個(gè)數(shù)不同
2.重載與方法的返回值無(wú)關(guān),存在于父類和子類,同類中
? 3.可以拋出不同的異常,可以有不同修飾符。
方法重寫(xiě)的規(guī)則:
?1.參數(shù)列表必須完全與被重寫(xiě)方法的一致,返回類型必須完全與被重寫(xiě)方法的返回類型一致。
?2.構(gòu)造方法不能被重寫(xiě),聲明為 final 的方法不能被重寫(xiě),聲明為 static 的方法不能被重寫(xiě),但是能夠被再次 聲明。 3.訪問(wèn)權(quán)限不能比父類中被重寫(xiě)的方法的訪問(wèn)權(quán)限更低。
4.重寫(xiě)的方法能夠拋出任何非強(qiáng)制異常(UncheckedException,也叫非運(yùn)行時(shí)異常),無(wú)論被重寫(xiě)的方法是 否拋出異常。但是,重寫(xiě)的方法不能拋出新的強(qiáng)制性異常,或者比被重寫(xiě)方法聲明的更廣泛的強(qiáng)制性異常,反之則 可以。
8、為什么函數(shù)不能根據(jù)返回類型來(lái)區(qū)分重載?
該道題來(lái)自華為面試題。 因?yàn)檎{(diào)用時(shí)不能指定類型信息,編譯器不知道你要調(diào)用哪個(gè)函數(shù)。?
例如:
1.float max(int a, int b);?
2.int max(int a, int b);
當(dāng)調(diào)用 max(1, 2);時(shí)無(wú)法確定調(diào)用的是哪個(gè),單從這一點(diǎn)上來(lái)說(shuō),僅返回值類型不同的重載是不應(yīng)該允許的。 再比如對(duì)下面這兩個(gè)方法來(lái)說(shuō),雖然它們有同樣的名字和自變量,但其實(shí)是很容易區(qū)分的:
1.void f() {} ?
2.int f() {}
若編譯器可根據(jù)上下文(語(yǔ)境)明確判斷出含義,比如在 int x=f()中,那么這樣做完全沒(méi)有問(wèn)題。然而, 我們也可能調(diào)用一個(gè)方法,同時(shí)忽略返回值;我們通常把這稱為“為它的副作用去調(diào)用一個(gè)方法”,因?yàn)槲?們關(guān)心的不是返回值,而是方法調(diào)用的其他效果。所以假如我們像下面這樣調(diào)用方法: f(); Java 怎樣判斷 f()的具體調(diào) 用方式呢?而且別人如何識(shí)別并理解代碼呢?由于存在這一類的問(wèn)題,所以不能。函數(shù)的返回值只是作為函數(shù)運(yùn)行之后的一個(gè)“狀態(tài)”,他是保持方法的調(diào)用者與被調(diào)用者進(jìn)行通信的關(guān)鍵。并不能 作為某個(gè)方法的“標(biāo)識(shí)”。
9、 char 型變量中能不能存儲(chǔ)一個(gè)中文漢字,為什么?
char 類型可以存儲(chǔ)一個(gè)中文漢字,因?yàn)?Java 中使用的編碼是 Unicode(不選擇任何特定的編碼,直接 使用字符在字符集中的編號(hào),這是統(tǒng)一的唯一方法),一個(gè) char 類型占 2 個(gè)字節(jié)(16 比特),所以放一個(gè)中 文是沒(méi)問(wèn)題的。?
補(bǔ)充:使用 Unicode 意味著字符在 JVM 內(nèi)部和外部有不同的表現(xiàn)形式,在 JVM 內(nèi)部都是 Unicode,當(dāng)這個(gè)字符被 從 JVM 內(nèi)部轉(zhuǎn)移到外部時(shí)(例如存入文件系統(tǒng)中),需要進(jìn)行編碼轉(zhuǎn)換。所以 Java 中有字節(jié)流和字符流,以及在字 符流和字節(jié)流之間進(jìn)行轉(zhuǎn)換的轉(zhuǎn)換流,如 InputStreamReader 和 OutputStreamReader,這兩個(gè)類是字節(jié)流和字符 流之間的適配器類,承擔(dān)了編碼轉(zhuǎn)換的任務(wù);對(duì)于 C 程序員來(lái)說(shuō),要完成這樣的編碼轉(zhuǎn)換恐怕要依賴于 union(聯(lián)合 體/共用體)共享內(nèi)存的特征來(lái)實(shí)現(xiàn)了。
10、抽象類(abstract class)和接口(interface)有什么異同?(2017-11-16-wl) 不同:
抽象類:
1、抽象類中可以定義構(gòu)造器?
2、可以有抽象方法和具體方法?
3、接口中的成員全都是 public 的?
4、抽象類中可以定義成員變量?
5、有抽象方法的類必須被聲明為抽象類,而抽象類未必要有抽象方法?
6、抽象類中可以包含靜態(tài)方法
7、一個(gè)類只能繼承一個(gè)抽象類?
接口:
1、接口中不能定義構(gòu)造器
2、方法全部都是抽象方法?
3、抽象類中的成員可以是 private、默認(rèn)、protected、public?
4、接口中定義的成員變量實(shí)際上都是常量?
5、接口中不能有靜態(tài)方法
6、一個(gè)類可以實(shí)現(xiàn)多個(gè)接口
相同:?
1.不能夠?qū)嵗?/p>
2.可以將抽象類和接口類型作為引用類型?
3.一個(gè)類如果繼承了某個(gè)抽象類或者實(shí)現(xiàn)了某個(gè)接口都需要對(duì)其中的抽象方法全部進(jìn)行實(shí)現(xiàn),否則該類仍然需要 被聲明為抽象類
11. 抽象的(abstract)方法是否可同時(shí)是靜態(tài)的(static), 是否可同時(shí)是本地方法 (native),是否可同時(shí)被 synchronized
都不能。
抽象方法需要子類重寫(xiě),而靜態(tài)的方法是無(wú)法被重寫(xiě)的,因此二者是矛盾的。本地方法是由 本地代碼(如 C 代碼)實(shí)現(xiàn)的方法,而抽象方法是沒(méi)有實(shí)現(xiàn)的,也是矛盾的。synchronized 和方法的實(shí)現(xiàn)細(xì)節(jié)有關(guān), 抽象方法不涉及實(shí)現(xiàn)細(xì)節(jié),因此也是相互矛盾的。
12、闡述靜態(tài)變量和實(shí)例變量的區(qū)別?
靜態(tài)變量: 是被 static 修飾符修飾的變量,也稱為類變量,它屬于類,不屬于類的任何一個(gè)對(duì)象,一個(gè)類不管創(chuàng)建多少個(gè)對(duì)象,靜態(tài)變量在內(nèi)存中有且僅有一個(gè)拷貝;?
實(shí)例變量: 必須依存于某一實(shí)例,需要先創(chuàng)建對(duì)象然后通過(guò)對(duì)象才能訪問(wèn)到它。靜態(tài)變量可以實(shí)現(xiàn)讓多個(gè)對(duì) 象共享內(nèi)存。
13、 ==和 equals 的區(qū)別?
equals 和== 最大的區(qū)別是一個(gè)是方法一個(gè)是運(yùn)算符。
?==:如果比較的對(duì)象是基本數(shù)據(jù)類型,則比較的是數(shù)值是否相等;如果比較的是引用數(shù)據(jù)類型,則比較的是對(duì)象 的地址值是否相等。?
equals():用來(lái)比較方法兩個(gè)對(duì)象的內(nèi)容是否相等。 注意:equals 方法不能用于基本數(shù)據(jù)類型的變量,如果沒(méi)有對(duì) equals 方法進(jìn)行重寫(xiě),則比較的是引用類型的變 量所指向的對(duì)象的地址。
14、 break 和 continue 的區(qū)別?
break 和 continue 都是用來(lái)控制循環(huán)的語(yǔ)句。?
break 用于完全結(jié)束一個(gè)循環(huán),跳出循環(huán)體執(zhí)行循環(huán)后面的語(yǔ)句。
continue 用于跳過(guò)本次循環(huán),執(zhí)行下次循環(huán)。
15、 String s = "Hello";s = s + " world!";這兩行代碼執(zhí)行后,原始的 String 對(duì)象 中的內(nèi)容到底變了沒(méi)有?
沒(méi)有。
因?yàn)?String 被設(shè)計(jì)成不可變(immutable)類,所以它的所有對(duì)象都是不可變對(duì)象。在這段代碼中,s 原先指 向一個(gè) String 對(duì)象,內(nèi)容是 "Hello",然后我們對(duì) s 進(jìn)行了“+”操作,那么 s 所指向的那個(gè)對(duì)象是否發(fā)生了改變呢? 答案是沒(méi)有。這時(shí),s 不指向原來(lái)那個(gè)對(duì)象了,而指向了另一個(gè) String 對(duì)象,內(nèi)容為"Hello world!",原來(lái)那個(gè)對(duì)象還存在于內(nèi)存之中,只是 s 這個(gè)引用變量不再指向它了。 通過(guò)上面的說(shuō)明,我們很容易導(dǎo)出另一個(gè)結(jié)論,如果經(jīng)常對(duì)字符串進(jìn)行各種各樣的修改,或者說(shuō),不可預(yù)見(jiàn)的修 改,那么使用 String 來(lái)代表字符串的話會(huì)引起很大的內(nèi)存開(kāi)銷。因?yàn)?String 對(duì)象建立之后不能再改變,所以對(duì)于每 一個(gè)不同的字符串,都需要一個(gè) String 對(duì)象來(lái)表示。這時(shí),應(yīng)該考慮使用 StringBuffer 類,它允許修改,而不是每個(gè) 不同的字符串都要生成一個(gè)新的對(duì)象。并且,這兩種類的對(duì)象轉(zhuǎn)換十分容易。同時(shí),我們還可以知道,如果要使用內(nèi)容 相同的字符串,不必每次都 new 一個(gè) String。例如我們要在構(gòu)造器中對(duì)一個(gè)名叫 s 的 String 引用變量進(jìn)行初始化, 把它設(shè)置為初始值,應(yīng)當(dāng)這樣做:
1.public class Demo {
2. private String s;?
3. ...?
4. s = "Initial Value";?
5. ...
6.}
而非
1.s = new String("Initial Value");
后者每次都會(huì)調(diào)用構(gòu)造器,生成新對(duì)象,性能低下且內(nèi)存開(kāi)銷大,并且沒(méi)有意義,因?yàn)?String 對(duì)象不可改變,所 以對(duì)于內(nèi)容相同的字符串,只要一個(gè) String 對(duì)象來(lái)表示就可以了。也就說(shuō),多次調(diào)用上面的構(gòu)造器創(chuàng)建多個(gè)對(duì)象,他 們的 String 類型屬性 s 都指向同一個(gè)對(duì)象。 上面的結(jié)論還基于這樣一個(gè)事實(shí):對(duì)于字符串常量,如果內(nèi)容相同,Java 認(rèn)為它們代表同一個(gè) String 對(duì)象。而 用關(guān)鍵字 new 調(diào)用構(gòu)造器,總是會(huì)創(chuàng)建一個(gè)新的對(duì)象,無(wú)論內(nèi)容是否相同。 至于為什么要把 String 類設(shè)計(jì)成不可變 類,是它的用途決定的。其實(shí)不只 String,很多 Java 標(biāo)準(zhǔn)類庫(kù)中的類都是不可變的。在開(kāi)發(fā)一個(gè)系統(tǒng)的時(shí)候,我們 有時(shí)候也需要設(shè)計(jì)不可變類,來(lái)傳遞一組相關(guān)的值,這也是面向?qū)ο笏枷氲捏w現(xiàn)。不可變類有一些優(yōu)點(diǎn),比如因?yàn)樗?的對(duì)象是只讀的,所以多線程并發(fā)訪問(wèn)也不會(huì)有任何問(wèn)題。當(dāng)然也有一些缺點(diǎn),比如每個(gè)不同的狀態(tài)都要一個(gè)對(duì)象來(lái) 代表,可能會(huì)造成性能上的問(wèn)題。所以 Java 標(biāo)準(zhǔn)類庫(kù)還提供了一個(gè)可變版本,即 StringBuffer。