第一部分 Java基礎
第二部分 Java進階

java基礎語法面試題

 

1、String類可以被繼承嗎?

 

String類在聲明時使用final關鍵字修飾,被final關鍵字修飾的類無法被繼承。

接下來我們可以看一下String類的源代碼片段:

 

public final class String
    implements java.io.Serializable, Comparable<String>,CharSequence {
    /** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0

    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    private static final long serialVersionUID = -6849794470754667710L;

 

為什么Java語言的開發者,把String類定義為final的呢?

  • 因為只有當字符串是不可變的,字符串池才有可能實現。字符串池的實現可以在運行時節約很多heap空間,因為不同的字符串變量都指向池中的同一個字符串。但如果字符串是可變的,那么String interning將不能實現,因為這樣的話,如果變量改變了它的值,那么其它指向這個值的變量的值也會一起改變。如果字符串是可變的,那么會引起很嚴重的安全問題。譬如,數據庫的用戶名、密碼都是以字符串的形式傳入來獲得數據庫的連接,或者在socket編程中,主機名和端口都是以字符串的形式傳入。因為字符串是不可變的,所以它的值是不可改變的,否則黑客們可以鉆到空子,改變字符串指向的對象的值,造成安全漏洞。
  • 因為字符串是不可變的,所以是多線程安全的,同一個字符串實例可以被多個線程共享。這樣便不用因為線程安全問題而使用同步。字符串自己便是線程安全的。
  • 因為字符串是不可變的,所以在它創建的時候HashCode就被緩存了,不需要重新計算。這就使得字符串很適合作為Map中的鍵,字符串的處理速度要快過其它的鍵對象。這就是HashMap中的鍵往往都使用字符串。

 

final關鍵字除了修飾類之外,還有哪些用法呢?

  • final修飾的變量,一旦賦值,不可重新賦值;
  • final修飾的方法無法被覆蓋;
  • final修飾的實例變量,必須手動賦值,不能采用系統默認值;
  • final修飾的實例變量,一般和static聯用,用來聲明常量;

注意:final不能和abstract關鍵字聯合使用。

總之,final表示最終的、不可變的。

 

 

2、& 和 && 的區別?

 

  • &運算符是:邏輯與;&&運算符是:短路與。
  • &和&&在程序中最終的運算結果是完全一致的,只不過&&存在短路現象,當&&運算符左邊的表達式結果為false的時候,右邊的表達式不執行,此時就發生了短路現象。如果是&運算符,那么不管左邊的表達式是true還是false,右邊表達式是一定會執行的。這就是他們倆的本質區別。
  • 當然,&運算符還可以使用在二進制位運算上,例如按位與操作。

 

 

 

3、兩個對象值相同equals結果為true,但卻可有不同的 hashCode,這句話對不對?

 

不對,如果兩個對象x和y滿足x.equals(y) == true,它們的哈希值(hashCode)應當相同。Java 對于equals方法和hashCode方法是這樣規定的:

 

(1)如果兩個對象相同(equals方法返回true),那么它們的hashCode值一定要相同;

 

(2)如果兩個對象的 hashCode相同,它們并不一定相同。當然,你未必按照要求去做,但是如果你違背了上述原則就會發現在使用集合時,相同的對象可以出現在Set 集合中,同時增加新元素的效率會大大降低(對于使用哈希存儲的系統,如果哈希碼頻繁的沖突將會造成存取性能急劇下降)。

 

關于equals和hashCode方法,很多Java程序員都知道,但很多人也就是僅僅了解而已,在Joshua Bloch的大作《Effective Java》(《Effective Java》在很多公司,是Java程序員必看書籍,如果你還沒看過,那就趕緊去買一本吧)中是這樣介紹 equals 方法的:

 

首先equals方法必須滿足自反性(x.equals(x)必須返回true)、對稱性(x.equals(y)返回true 時,y.equals(x)也必須返回true)、傳遞性(x.equals(y)和y.equals(z)都返回true時,x.equals(z)也必須返回true)和一致性(當x和y引用的對象信息沒有被修改時,多次調用x.equals(y)應該得到同樣的返回值),而且對于任何非null值的引用x,x.equals(null)必須返回false。實現高質量的equals方法的訣竅包括:

  • 使用==操作符檢查"參數是否為這個對象的引用";
  • 使用 instanceof操作符檢查"參數是否為正確的類型";
  • 對于類中的關鍵屬性,檢查參數傳入對象的屬性是否與之相匹配;
  • 編寫完equals方法后,問自己它是否滿足對稱性、傳遞性、一致性;
  • 重寫equals時總是要重寫hashCode;
  • 不要將equals方法參數中的Object對象替換為其他的類型,在重寫時不要忘掉@Override注解。

 

4、在 Java 中,如何跳出當前的多重嵌套循環?

在最外層循環前加一個標記如outfor,然后用break outfor;可以跳出多重循環。例如以下代碼:

 

public class TestBreak {
    public static void main(String[] args) {
        outfor: for (int i = 0; i < 10; i++){
            for (int j = 0; j < 10; j++){
                if (j == 5){
                    break outfor;
                }
                System.out.println("j = " + j);
            }
        }
    }
}

 

運行結果如下所示:

j = 0

j = 1

j = 2

j = 3

j = 4

 

5、重載(overload)和重寫(override)的區別?重載的方法能否根據返回類型進行區分?

方法的重載和重寫都是實現多態的方式,區別在于前者實現的是編譯時的多態性,而后者實現的是運行時的多態性。

 

重載發生在一個類中,同名的方法如果有不同的參數列表(類型不同、個數不同、順序不同)則視為重載。

 

重寫發生在子類與父類之間,重寫要求子類重寫之后的方法與父類被重寫方法有相同的返回類型,比父類被重寫方法更好訪問,不能比父類被重寫方法聲明更多的異常(里氏代換原則)。重載對返回類型沒有特殊的要求。

 

方法重載的規則:

  • 方法名一致,參數列表中參數的順序,類型,個數不同。
  • 重載與方法的返回值無關,存在于父類和子類,同類中。
  • 可以拋出不同的異常,可以有不同修飾符。

 

方法重寫的規則:

  • 參數列表、方法名、返回值類型必須完全一致;
  • 構造方法不能被重寫;
  • 聲明為 final 的方法不能被重寫;
  • 聲明為 static 的方法不存在重寫(重寫和多態聯合才有意義);
  • 訪問權限不能比父類更低;
  • 重寫之后的方法不能拋出更寬泛的異常;

 

6、當一個對象被當作參數傳遞到一個方法后,此方法可改變這個對象的屬性,并可返回變化后的結果,那么這里是值傳遞還是引用傳遞?

是值傳遞。Java 語言的方法調用只支持參數的值傳遞。當一個對象實例作為一個參數被傳遞到方法中時,參數的值就是對該對象的內存地址。這個值(內存地址)被傳遞后,同一個內存地址指向堆內存當中的同一個對象,所以通過哪個引用去操作這個對象,對象的屬性都是改變的。

 

7、為什么方法不能根據返回類型來區分重載?

我們來看以下的代碼:

 

public void testMethod(){
    doSome();
}
public void doSome(){
    
}
public int doSome(){
    return 1;
}

 

在Java語言中,調用一個方法,即使這個方法有返回值,我們也可以不接收這個返回值,例如以上兩個方法doSome(),在testMethod()中調用的時候,Java編譯器無法區分調用的具體是哪個方法。所以對于編譯器來說,doSome()方法不是重載而是重復了,編譯器報錯。所以區分這兩個方法不能依靠方法的返回值類型。

 

8、抽象類(abstract class)和接口(interface)有什么異同?

 

都不能。

  • 抽象方法需要子類重寫,而靜態的方法是無法被重寫的,因此二者是矛盾的。
  • 本地方法是由本地代碼(如 C++ 代碼)實現的方法,而抽象方法是沒有實現的,也是矛盾的。
  • synchronized 和方法的實現細節有關,抽象方法不涉及實現細節,因此也是相互矛盾的。

 

9、char 型變量中能不能存儲一個中文漢字,為什么?

char 類型可以存儲一個中文漢字,因為Java中使用的編碼是Unicode(不選擇任何特定的編碼,直接使用字符在字符集中的編號,這是統一的唯一方法),一個char 類型占2個字節(16 比特),所以放一個中文是沒問題的。

 

補充:使用Unicode 意味著字符在JVM內部和外部有不同的表現形式,在JVM內部都是 Unicode,當這個字符被從JVM內部轉移到外部時(例如存入文件系統中),需要進行編碼轉換。所以 Java 中有字節流和字符流,以及在字符流和字節流之間進行轉換的轉換流,如 InputStreamReader和OutputStreamReader,這兩個類是字節流和字符流之間的適配器類,承擔了編碼轉換的任務。

 

10、抽象的(abstract)方法是否可同時是靜態的(static), 是否可同時是本地方法(native),是否可同時被 synchronized?

 

都不能。

  • 抽象方法需要子類重寫,而靜態的方法是無法被重寫的,因此二者是矛盾的。
  • 本地方法是由本地代碼(如 C++ 代碼)實現的方法,而抽象方法是沒有實現的,也是矛盾的。
  • synchronized 和方法的實現細節有關,抽象方法不涉及實現細節,因此也是相互矛盾的。

 

11、==和equals的區別?

equals和==最大的區別是一個是方法一個是運算符。

  • ==:如果比較的對象是基本數據類型,則比較的是數值是否相等;如果比較的是引用數據類型,則比較的是對象的地址值是否相等。
  • equals():用來比較方法兩個對象的內容是否相等。equals方法不能用于基本數據類型的變量,如果沒有對equals方法進行重寫,則比較的是引用類型的變量所指向的對象的地址。

 

12、闡述靜態變量和實例變量的區別?

不管創建多少個對象,靜態變量在內存中有且僅有一個;實例變量必須依存于某一實例,需要先創建對象然后通過對象才能訪問到它。靜態變量可以實現讓多個對象共享內存。

 

13、break和continue的區別?

  • break和continue 都是用來控制循環的語句。
  • break 用于完全結束一個循環,跳出循環體執行循環后面的語句。

continue 用于跳過本次循環,繼續下次循環。

 

14、String s = "Hello";s = s + " world!";這兩行代碼執行后,原始的 String 對象中的內容變了沒有?

沒有。

因為 String被設計成不可變類,所以它的所有對象都是不可變對象。

 

在這段代碼中,s原先指向一個 String 對象,內容是 "Hello",然后我們對 s 進行了“+”操作,那么 s 所指向的那個對象是否發生了改變呢?

 

答案是沒有。這時s不指向原來那個對象了,而指向了另一個 String 對象,內容為"Hello world!",原來那個對象還存在于內存之中,只是 s 這個引用變量不再指向它了。

 

通過上面的說明,我們很容易導出另一個結論,如果經常對字符串進行各種各樣的修改,或者說,不可預見的修改,那么使用 String 來代表字符串的話會引起很大的內存開銷。因為 String 對象建立之后不能再改變,所以對于每一個不同的字符串,都需要一個 String 對象來表示。這時,應該考慮使用 StringBuffer/StringBuilder類,它允許修改,而不是每個不同的字符串都要生成一個新的對象。并且,這兩種類的對象轉換十分容易。同時,我們還可以知道,如果要使用內容相同的字符串,不必每次都 new 一個 String。例如我們要在構造器中對一個名叫 s 的 String 引用變量進行初始化,把它設置為初始值,應當這樣做:

s = new String("動力節點,口口相傳的Java黃埔軍校");

而不是這樣做:

  s = new String("動力節點,口口相傳的Java黃埔軍校");

后者每次都會調用構造器,生成新對象,性能低下且內存開銷大,并且沒有意義,因為 String 對象不可改變,所以對于內容相同的字符串,只要一個 String 對象來表示就可以了。也就說,多次調用上面的構造器創建多個對象,他們的 String 類型屬性 s 都指向同一個對象。

 

上面的結論還基于這樣一個事實:對于字符串常量,如果內容相同,Java 認為它們代表同一個 String 對象。而用關鍵字 new 調用構造器,總是會創建一個新的對象,無論內容是否相同。

 

至于為什么要把 String 類設計成不可變類,是它的用途決定的。其實不只String,很多 Java 標準類庫中的類都是不可變的。在開發一個系統的時候,我們有時候也需要設計不可變類,來傳遞一組相關的值,這也是面向對象思想的體現。不可變類有一些優點,比如因為它的對象是只讀的,所以多線程并發訪問也不會有任何問題。當然也有一些缺點,比如每個不同的狀態都要一個對象來代表,可能會造成性能上的問題。所以 Java 標準類庫還提供了一個可變版本,即 StringBuffer。

全部教程
广东36选7开奖公告