JavaRush /Java Blog /Random-TW /前 50 個 Java 核心面試問題和答案。第1部分
Roman Beekeeper
等級 35

前 50 個 Java 核心面試問題和答案。第1部分

在 Random-TW 群組發布
各位軟體工程師,女士先生們大家好!我們來談談面試問題。關於您需要準備什麼以及您需要了解什麼。這是從頭開始重複或研究這些要點的絕佳理由。 前 50 個 Java 核心面試問題和答案。 第 1 - 1 部分我收集了相當廣泛的有關 OOP、Java 語法、Java 中的異常、集合和多線程的常見問題,為了方便起見,我將其分為幾個部分。 重要的:我們只會討論 Java 版本 8 之前的版本。這裡不會考慮 9、10、11、12、13 的所有創新。歡迎任何關於如何改進答案的想法/評論。祝閱讀愉快,走吧!

Java面試:OOP問題

1.Java有什麼特點?

答:
  1. 物件導向程式設計的概念:

    1. 面向對象;
    2. 遺產;
    3. 封裝;
    4. 多態性;
    5. 抽象。
  2. 跨平台: Java程式無需任何修改就可以在任何平台上運行。您唯一需要的是已安裝的 JVM(java 虛擬機器)。

  3. 高效能: JIT(Just In Time編譯器)允許高效能。JIT 將字節碼轉換為機器碼,然後 JVM 開始執行。

  4. 多執行緒:稱為 的執行緒Thread。JVM 建立一個名為 的執行緒main thread。程式設計師可以透過繼承 Thread 類別或實作介面來建立多個執行緒Runnable

2.什麼是繼承?

繼承意味著一個類別可以繼承(“擴展”)另一個類別。這樣您就可以重複使用您繼承的類別中的程式碼。現有類別稱為superclass,正在建立的類別稱為subclass。他們還說parentchild
public class Animal {
   private int age;
}

public class Dog extends Animal {

}
其中Animalparent,並且Dog- child

3.什麼是封裝?

這個問題在 Java 開發人員面試中經常出現。封裝是使用存取修飾符、getter 和 setter 來隱藏實作。這樣做是為了在開發人員認為有必要的地方關閉外部使用的存取。生活中一個容易理解的例子是汽車。我們無法直接了解引擎的運作情況。對我們來說,工作就是將鑰匙插入點火開關並啟動引擎。幕後將發生什麼流程與我們無關。此外,我們對這項活動的干擾可能會導致不可預測的情況,因此我們可能會損壞汽車並傷害自己。程式設計中也發生同樣的事情。維基百科上有很好的描述。JavaRush上也有一篇關於封裝的文章。

4.什麼是多態性?

多態性是程式能夠以相同的介面相同地使用對象,而無需了解該對象的特定類型。正如他們所說,一個介面 - 多種實現。透過多態性,您可以根據不同類型的物件的共同行為來組合和使用它們。例如,我們有一個 Animal 類,它有兩個後代 - Dog 和 Cat。通用 Animal 類別有一個共同的行為 - 發出聲音。當我們需要將 Animal 類別的所有後代放在一起並執行「發出聲音」方法時,我們可以使用多態性的可能性。它將如下所示:
List<Animal> animals = Arrays.asList(new Cat(), new Dog(), new Cat());
animals.forEach(animal -> animal.makeSound());
所以多態性對我們有幫助。此外,這也適用於多態(重載)方法。 使用多態性的實踐

面試問題 - Java 文法

5. Java中的建構函數是什麼?

以下特徵有效:
  1. 建立新物件時,程式會使用適當的建構函數來執行此操作。
  2. 構造函數就像一個方法。它的奇特之處在於沒有返回元素(包括void),並且它的名稱與類別的名稱相同。
  3. 如果沒有明確編寫建構函數,則會自動建立一個空構造函數。
  4. 構造函數可以被重寫。
  5. 如果建立了帶有參數的建構函數,但也需要不帶參數的建構函數,則必須單獨編寫它,因為它不會自動建立。

6. 哪兩個類別不是繼承自Object?

不要被挑釁所迷惑,不存在這樣的類別:所有直接或透過祖先的類別都是從 Object 類別繼承的!

7.什麼是局部變數?

Java 開發人員面試中的另一個常見問題。局部變數是在方法內部定義的變量,並且在方法執行之前一直存在。一旦執行結束,局部變數將不復存在。下面是一個在 main() 方法中使用 helloMessage 局部變數的程式:
public static void main(String[] args) {
   String helloMessage;
   helloMessage = "Hello, World!";
   System.out.println(helloMessage);
}

8.什麼是實例變數?

實例變數是在類別內部定義的變量,它一直存在到物件存在的那一刻。一個例子是 Bee 類,它有兩個變數 nectarCapacity 和 maxNectarCapacity:
public class Bee {

   /**
    * Current nectar capacity
    */
   private double nectarCapacity;

   /**
    * Maximal nectar that can take bee.
    */
   private double maxNectarCapacity = 20.0;

  ...
}

9. 什麼是存取修飾符?

存取修飾符是一種允許您自訂對類別、方法和變數的存取的工具。有以下修飾符,依存取權限增加的順序排列:
  1. private- 用於方法、欄位和建構函式。訪問級別只是聲明它的類別。
  2. package-private(default)- 可用於課程。僅在聲明了類別、方法、變數、建構函式的特定套件中存取。
  3. protectedpackage-private—對於那些從帶有修飾符的類別繼承的類,具有與 + 相同的存取權protected
  4. public- 也用於課程。整個應用程式的完全存取權。
  5. 前 50 個 Java 核心面試問題和答案。 第 1 - 2 部分

10.什麼是重寫方法?

當子類別想要更改父類別的行為時,就會發生方法重寫。如果你想要執行父方法中的內容,你可以在子方法中使用像 super.methodName() 這樣的構造,它將完成父方法的工作,然後才加入邏輯。需滿足的要求:
  • 方法簽名必須相同;
  • 傳回值應該是相同的。

11.什麼是方法簽名?

前 50 個 Java 核心面試問題和答案。 第 1 - 3 部分方法簽名是方法名稱和方法接受的參數的集合。方法簽名是重載方法時方法的唯一識別碼。

12.什麼是方法重載?

方法重載是多態性的屬性,透過更改方法簽名,您可以為相同的操作建立不同的方法:
  • 相同的方法名稱;
  • 不同的論點;
  • 可能有不同的返回類型。
例如,可以如下重載相同的add()內容ArrayList,並將根據傳入參數以不同的方式執行加法:
  • add(Object o)- 簡單地新增一個物件;
  • add(int index, Object o)— 將物件加入特定索引;
  • add(Collection<Object> c)— 新增物件清單;
  • add(int index, Collection<Object> c)— 從某個索引開始新增物件清單。

13.什麼是介面?

Java 中沒有實作多重繼承,因此為了解決這個問題,增加了我們所知道的介面;) 很長一段時間,介面只有方法而沒有實作它們。作為這個答案的一部分,我們將討論它們。例如:

public interface Animal {
   void makeSound();
   void eat();
   void sleep();
}
由此得出一些細微差別:
  • 介面中的所有方法都是公共的、抽象的;
  • 所有變數都是public static final;
  • 類別不是繼承它們(擴展),而是實現它們(實作)。此外,您可以實現任意數量的介面。
  • 實作介面的類別必須提供該介面所具有的所有方法的實作。
像這樣:
public class Cat implements Animal {
   public void makeSound() {
       // method implementation
   }

   public void eat() {
       // implementation
   }

   public void sleep() {
       // implementation
   }
}

14. 介面中的預設方法是什麼?

現在我們來談談預設方法。為了什麼,為了誰?添加這些方法是為了讓一切「既是你的,也是我們的」。我在說什麼?是的,一方面,有必要添加新功能:lambdas、Stream API,另一方面,有必要保留 Java 聞名的東西——向後相容性。為此,有必要在介面中引入現成的解決方案。這就是我們如何使用預設方法的。也就是說,預設方法是介面中具有關鍵字 的實作方法default。例如,眾所周知的stream()方法Collection。看看吧,這個介面並不像看起來那麼簡單;)。forEach()或也是來自 的同樣眾所周知的方法Iterable。在添加預設方法之前它也不存在。順便說一句,您也可以在JavaRush上閱讀有關此內容的內容。

15. 那麼如何繼承兩個相同的預設方法呢?

根據之前關於預設方法是什麼的答案,您可以問另一個問題。如果你可以在接口中實現方法,那麼理論上你可以用相同的方法實現兩個接口,如何做到這一點?有兩個不同的介面具有相同的方法:
interface A {
   default void foo() {
       System.out.println("Foo A");
   }
}

interface B {
   default void foo() {
       System.out.println("Foo B");
   }
}
並且有一個類別實現了這兩個介面。foo()為了避免不確定性並編譯程式碼,我們需要重寫類別中的方法,並且我們可以簡單地呼叫其中任何介面的C方法-或。但究竟如何選擇具體的介面方法呢?有一個這樣的結構: foo()ABАВA.super.foo()
public class C implements A, B {
   @Override
   public void foo() {
       A.super.foo();
   }
}
或者:
public class C implements A, B {
   @Override
   public void foo() {
       B.super.foo();
   }
}
因此,foo()類別方法將使用介面中的C預設方法或介面中的方法。 foo()Afoo()B

16.什麼是抽象方法和類別?

Java 有一個保留字abstract,用來表示抽象類別和方法。首先,一些定義。abstract抽象方法是在抽象類別中使用關鍵字創建的沒有實現的方法。也就是說,這是一個像是介面中的方法,只是增加了一個關鍵字,例如:
public abstract void foo();
抽象類別是一個也有abstract這個字的類別:
public abstract class A {

}
抽象類別有幾個特點:
  • 不能在其基礎上創建物件;
  • 它可以有抽象方法;
  • 它可能沒有抽象方法。
需要抽象類別來概括某種抽象(抱歉是同義反覆),這種抽像在現實生活中並不存在,但它包含許多常見的行為和狀態(即方法和變數)。生活中的例子已經夠多了。一切都在我們周圍。它可以是「動物」、「汽車」、「幾何圖形」等等。

17. String、String Builder 和 String Buffer 有什麼不同?

這些值String儲存在常數字串池中。一旦創建了一行,它將出現在該池中。並且將無法刪除它。例如:
String name = "book";
...變數將引用字串池常數字串池 前 50 個 Java 核心面試問題和答案。 第 1 - 4 部分 如果將變數名稱設為不同的值,您將得到以下結果:
name = "pen";
常數字串池 前 50 個 Java 核心面試問題和答案。 第 1 - 5 部分所以這兩個值會保留在那裡。 字串緩衝區:
  • String儲存在堆疊中。如果該值發生變化,則新值將被舊值取代;
  • String Buffer同步,因此線程安全;
  • 由於線程安全的原因,運行速度還有很多不盡人意的地方。
例子:
StringBuffer name = "book";
前 50 個 Java 核心面試問題和答案。 第 1 - 6 部分一旦 name 的值發生變化,堆疊上的值就會改變: 前 50 個 Java 核心面試問題和答案。 第 1 - 7 部分StringBuilder 與 完全相同StringBuffer,只是它不是執行緒安全的。因此,它的速度明顯高於StringBuffer

18. 抽象類別和介面有什麼差別?

抽象類別:
  • 抽象類別有一個預設建構函數;每次創建該抽象類別的子類別時都會呼叫它;
  • 包含抽象方法和非抽象方法。總的來說,它可能不包含抽象方法,但仍然是一個抽象類別;
  • 從抽象類別繼承的類別必須只實作抽象方法;
  • 抽象類別可以包含實例變數(參見問題#5)。
介面:
  • 沒有構造函數,無法初始化;
  • 只應該加入抽象方法(不包括預設方法);
  • 實作介面的類別必須實作所有方法(不包括預設方法);
  • 介面只能包含常數。

19. 為什麼存取陣列中的元素需要 O(1)?

這個問題其實來自上次採訪。後來我才知道,問這個問題是為了看看一個人是怎麼想的。顯然,這些知識沒有什麼實際意義:只要知道這個事實就夠了。首先,我們需要澄清 O(1) 是當操作在恆定時間內發生時演算法的時間複雜度的指定。也就是說,這個指定是最快執行的。要回答這個問題,我們需要了解我們對陣列了解多少?要建立數組int,我們必須編寫以下內容:
int[] intArray = new int[100];
從這段錄音可以得到幾個結論:
  1. 創建數組時,它的類型是已知的。如果類型已知,那麼數組的每個單元的大小就很清楚了。
  2. 數組的大小是已知的。
由此可見:要了解要寫入哪個儲存格,您只需計算要寫入哪個記憶體區域即可。對汽車來說,這再簡單不過了。本機器具有已分配記憶體的起始位置、多個元素和單一單元大小。由此可以清楚看出,記錄空間將等於數組的起始位置+單元格的大小乘以其大小。

如何以 O(1) 的速度存取 ArrayList 中的物件?

這個問題緊接著上一個問題。確實,當我們使用數組並且其中有基元時,我們在創建該類型時提前知道該類型的大小是多少。但是,如果有一個如圖所示的方案: 前 50 個 Java 核心面試問題和答案。 第 1 - 8 部分我們想要建立一個包含 A 類型元素的集合,並添加不同的實作 - B、C、D:
List<A> list = new ArrayList();
list.add(new B());
list.add(new C());
list.add(new D());
list.add(new B());
在這種情況下,您如何了解每個單元格的大小,因為每個物件都會不同,並且可能具有不同的附加欄位(或完全不同)。怎麼辦?這裡提出這個問題,是為了混淆視聽。我們知道,其實集合並不會儲存對象,而只是儲存這些對象的連結。所有連結都具有相同的大小,這是已知的。因此,這裡計算空間的方式與上一個問題相同。

21. 自動裝箱和拆箱

歷史背景:自動裝箱和自動拆箱是 JDK 5 的主要創新之一。 自動裝箱是從原始類型自動轉換為適當的包裝類別的過程。 自動拆箱- 與自動裝箱完全相反 - 將包裝類別轉換為基元。但如果存在包裝器值null,則在解包期間將引發異常NullPointerException

匹配原語 - 包裝器

原始 類包裝器
布林值 布林值
整數 整數
位元組 位元組
字元 特點
漂浮 漂浮
長的 長的
短的 短的
雙倍的 雙倍的

自動打包發生:

  • 當包裝類別的引用為原語分配時:

    Java 5 之前:

    //manual packaging or how it was BEFORE Java 5.
    public void boxingBeforeJava5() {
       Boolean booleanBox = new Boolean(true);
       Integer intBox = new Integer(3);
       // and so on to other types
    }
    
    после Java 5:
    //automatic packaging or how it became in Java 5.
    public void boxingJava5() {
       Boolean booleanBox = true;
       Integer intBox = 3;
       // and so on to other types
    }
  • 當將原語作為參數傳遞給需要包裝器的方法時:

    public void exampleOfAutoboxing() {
       long age = 3;
       setAge(age);
    }
    
    public void setAge(Long age) {
       this.age = age;
    }

自動解包發生:

  • 當我們將一個原始變數分配給包裝類別時:

    //before Java 5:
    int intValue = new Integer(4).intValue();
    double doubleValue = new Double(2.3).doubleValue();
    char c = new Character((char) 3).charValue();
    boolean b = Boolean.TRUE.booleanValue();
    
    //and after JDK 5:
    int intValue = new Integer(4);
    double doubleValue = new Double(2.3);
    char c = new Character((char) 3);
    boolean b = Boolean.TRUE;
  • 在進行算術運算的情況下。它們僅適用於原始類型;為此您需要對原始類型進行解包。

    // Before Java 5
    Integer integerBox1 = new Integer(1);
    Integer integerBox2 = new Integer(2);
    
    // for comparison it was necessary to do this:
    integerBox1.intValue() > integerBox2.intValue()
    
    //в Java 5
    integerBox1 > integerBox2
  • 當傳遞給接受相應原語的方法中的包裝器:

    public void exampleOfAutoboxing() {
       Long age = new Long(3);
       setAge(age);
    }
    
    public void setAge(long age) {
       this.age = age;
    }

22.final關鍵字是什麼以及在哪裡使用它?

此關鍵字final可用於變數、方法和類別。
  1. 最終變數不能重新分配給另一個物件。
  2. 最終類別是不孕的))它不能有繼承人。
  3. 最終方法不能在祖先上被重寫。
我們已經討論了頂部內容,現在讓我們更詳細地討論它。

最終變數

;Java 為我們提供了兩種建立變數並為其賦值的方法:
  1. 您可以聲明一個變數並稍後對其進行初始化。
  2. 您可以聲明一個變數並立即分配它。
在這些情況下使用最終變數的範例:
public class FinalExample {

   //final static variable, which is immediately initialized:
   final static String FINAL_EXAMPLE_NAME = "I'm likely final one";

   //final is a variable that is not initialized, but will only work if
   //initialize this in the constructor:
   final long creationTime;

   public FinalExample() {
       this.creationTime = System.currentTimeMillis();
   }

   public static void main(String[] args) {
       FinalExample finalExample = new FinalExample();
       System.out.println(finalExample.creationTime);

       // final field FinalExample.FINAL_EXAMPLE_NAME cannot be assigned
//    FinalExample.FINAL_EXAMPLE_NAME = "Not you're not!";

       // final field Config.creationTime cannot be assigned
//    finalExample.creationTime = 1L;
   }
}

Final 變數可以被視為常數嗎?

由於我們無法為最終變數分配新值,因此這些似乎是常數變數。但這只是乍看之下。如果變數引用的資料型別是 immutable,那麼是的,它是一個常數。但是,如果資料類型mutable是可變的,則使用方法和變數就可以更改變數所引用的物件的值final,在這種情況下,它不能稱為常數。因此,此範例表明某些最終變數確實是常數,但有些則不是,並且它們是可以更改的。
public class FinalExample {

   //immutable final variables:
   final static String FINAL_EXAMPLE_NAME = "I'm likely final one";
   final static Integer FINAL_EXAMPLE_COUNT  = 10;

   // mutable filter variables
   final List<String> addresses = new ArrayList();
   final StringBuilder finalStringBuilder = new StringBuilder("constant?");
}

局部最終變數

final一個變數在方法內部創建時,它被稱為local final變數:
public class FinalExample {

   public static void main(String[] args) {
       // This is how you can
       final int minAgeForDriveCar = 18;

       // or you can do it this way, in the foreach loop:
       for (final String arg : args) {
           System.out.println(arg);
       }
   }

}
我們可以final在擴充循環中使用該關鍵字for,因為循環迭代完成後,for每次都會建立一個新變數。但這些都不適用於普通的 for 循環,因此下面的程式碼將引發編譯時錯誤。
// final local changed j cannot be assigned
for (final int i = 0; i < args.length; i ++) {
   System.out.println(args[i]);
}

最後一堂課

您不能擴展聲明為 的類別final。簡而言之,沒有任何類別可以繼承這個類別。finalJDK 中的類別的一個很好的例子是String. 創建不可變類別的第一步是將其標記為final,以便它無法擴展:
public final class FinalExample {
}

// Compilation error here
class WantsToInheritFinalClass extends FinalExample {
}

最終方法

當一個方法被標記為final時,它被稱為final方法(邏輯上的,對吧?)。Final 方法不能在後代類別中被重寫。順便說一句,Object 類別中的方法 - wait() 和 notification() - 是最終方法,因此我們沒有機會重寫它們。
public class FinalExample {
   public final String generateAddress() {
       return "Some address";
   }
}

class ChildOfFinalExample extends FinalExample {

   // compile error here
   @Override
   public String generateAddress() {
       return "My OWN Address";
   }
}

在 Java 中如何以及在何處使用 Final

  • 使用final關鍵字定義一些類別層級的常數;
  • 當您不希望物件被修改時,為物件建立最終變數。例如,我們可以用於記錄目的的特定於物件的屬性;
  • 如果您不想延長該課程,請將其標記為最終課程;
  • 如果你需要創建一個不可變的<類,你需要將其設為final;
  • 如果您希望方法的實作在其後代中不會發生更改,請將該方法指定為final。這對於確保實施不會改變非常重要。

23.什麼是可變不可變?

可變的

可變物件是其狀態和變數在創建後可以更改的物件。例如StringBuilder、StringBuffer等類別。例子:
public class MutableExample {

   private String address;

   public MutableExample(String address) {
       this.address = address;
   }

   public String getAddress() {
       return address;
   }

   // this setter can change the name field
   public void setAddress(String address) {
       this.address = address;
   }

   public static void main(String[] args) {

       MutableExample obj = new MutableExample("first address");
       System.out.println(obj.getAddress());

       // update the name field, so this is a mutable object
       obj.setAddress("Updated address");
       System.out.println(obj.getAddress());
   }
}

不可變的

不可變物件是指在創建物件後其狀態和變數不能更改的物件。為什麼 HashMap 沒有一個很好的按鍵,對吧?)例如 String、Integer、Double 等等。例子:
// make this class final so no one can change it
public final class ImmutableExample {

   private String address;

   ImmutableExample (String address) {
       this.address = address;
   }

   public String getAddress() {
       return address;
   }

   //remove the setter

   public static void main(String[] args) {

       ImmutableExample obj = new ImmutableExample("old address");
       System.out.println(obj.getAddress());

       // Therefore, do not change this field in any way, so this is an immutable object
       // obj.setName("new address");
       // System.out.println(obj.getName());

   }
}

24.如何寫不可變類別?

弄清楚什麼是可變物件和不可變物件後,下一個問題就很自然了──怎麼寫?要編寫一個不可變的不可變類,您需要遵循簡單的步驟:
  • 使課程成為最終的。
  • 將所有欄位設為私有並只為它們建立 getter。當然,不需要設定器。
  • 將所有可變欄位設為最終值,以便該值只能設定一次。
  • 透過建構子初始化所有字段,執行深複製(即複製物件本身、其變數、變數的變數等)
  • 在 getter 中克隆可變變數物件以僅傳回值的副本,而不是對實際物件的參考。
例子:
/**
* An example of creating an immutable object.
*/
public final class FinalClassExample {

   private final int age;

   private final String name;

   private final HashMap<String, String> addresses;

   public int getAge() {
       return age;
   }


   public String getName() {
       return name;
   }

   /**
    * Clone the object before returning it.
    */
   public HashMap<String, String> getAddresses() {
       return (HashMap<String, String>) addresses.clone();
   }

   /**
    * In the constructor, deep copy the mutable objects.
    */
   public FinalClassExample(int age, String name, HashMap<String, String> addresses) {
       System.out.println("Performing a deep copy in the constructor");
       this.age = age;
       this.name = name;
       HashMap<String, String> temporaryMap = new HashMap<>();
       String key;
       Iterator<String> iterator = addresses.keySet().iterator();
       while (iterator.hasNext()) {
           key = iterator.next();
           temporaryMap.put(key, addresses.get(key));
       }
       this.addresses = temporaryMap;
   }
}
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION