你見過老外的 Java 面試題嗎 (上)?

前言

最近無聊的在逛某 tube 網站,原本想看看你們是怎麼吐槽川普的,結果無心間點進了一個老外面試 Java 的視頻,對於常年面試被吊打的我瑟瑟發抖,因而決定進去一探究竟。java

畢竟不是專業的後臺開發,因此我在面試到後臺知識的時候果斷的退了出來,才讓本身免受了侮辱面試

不過鑑於我手速出衆,飛速的記錄下了 Java 的基礎題,因此準備貢獻出來,供你們享樂。算法

鑑於題目比較多,會分紅上下 2 篇 來整理,主要是面對 Java 的基礎,看看老外的面試題和咱們有什麼區別。編程

固然問題是老外問的,答案是我編的。數組

正文

Java 中有哪些可用的分配內存數據結構

Java 虛擬機在執行程序時候會將內存劃分爲不一樣的數據區域oracle

  • 方法區 - Method Area
    • 方法區(Method Area)與 Java 堆同樣,是全部線程共享的內存區域
    • 雖然 Java 虛擬機規範把方法區描述爲堆的一個邏輯部分,可是它卻有一個別名叫 Non-Heap(非堆),目的應該是與 Java 堆區分開。
    • 運行時常量池(Runtime Constant Pool)是方法區的一部分。Class 文件中除了有類的版本/字段/方法/接口等描述信息外,還有一項信息是常量池(Constant Pool Table),用於存放編譯期生成的各類字面量和符號引用,這部份內容將類在加載後進入方法區的運行時常量池中存放。運行期間也可能將新的常量放入池中,這種特性被開發人員利用得比較多的是 String.intern() 方法。受方法區內存的限制,當常量池沒法再申請到內存時會拋出 OutOfMemoryError 異常。
    • 方法區的大小和堆空間同樣,能夠選擇固定大小也可選擇可擴展,方法區的大小決定了系統能夠放多少個類,若是系統類太多,會致使方法區溢出虛擬機一樣會拋出內存溢出錯誤
    • JVM 關閉後方法區即被釋放
  • 堆 - Heap Area
    • 對於大多數應用,Java 堆是 Java 虛擬機管理的內存中最大的一塊,被全部線程共享。此內存區域的惟一目的就是存放對象實例,幾乎全部的對象實例以及數據都在這裏分配內存。
    • Java 虛擬機規範規定,Java 堆能夠是處於物理上不連續的內存空間中,只要邏輯上是連續的便可,像磁盤空間同樣。實現時,既能夠是固定大小,也能夠是可擴展的,主流虛擬機都是可擴展的(經過 -Xmx-Xms 控制),若是堆中沒有完成實例分配,而且堆沒法再擴展時,就會拋出 OutOfMemoryError 異常。
  • 棧 - Stack Area
    • 棧是一種快速有效的分配存儲方式,訪問速度僅次於程序計數器
    • JVM 直接對虛擬機棧的操做只有兩個:每一個方法執行,伴隨着入棧(進棧/壓棧),方法執行結束出棧
    • 棧不存在垃圾回收問題
  • 程序計數器 - Program Counter Register
    • 它是一塊很小的內存空間,幾乎能夠忽略不計。也是運行速度最快的存儲區域
    • 在 JVM 規範中,每一個線程都有它本身的程序計數器,是線程私有的,生命週期與線程的生命週期一致
    • 任什麼時候間一個線程都只有一個方法在執行,也就是所謂的當前方法。若是當前線程正在執行的是 Java 方法,程序計數器記錄的是 JVM 字節碼指令地址,若是是執行 native 方法,則是未指定值(undefined)
    • 它是程序控制流的指示器,分支、循環、跳轉、異常處理、線程恢復等基礎功能都須要依賴這個計數器來完成
    • 字節碼解釋器工做時就是經過改變這個計數器的值來選取下一條須要執行的字節碼指令
    • 它是惟一一個在 JVM 規範中沒有規定任何 OutOfMemoryError 狀況的區域
  • 本地方法棧 - Native Method Stack Area
    • Java 虛擬機棧用於管理 Java 方法的調用,而本地方法棧用於管理本地方法的調用
    • 本地方法棧也是線程私有
    • 容許線程固定或者可動態擴展的內存大小
      • 若是線程請求分配的棧容量超過本地方法棧容許的最大容量,Java 虛擬機將會拋出一個 StackOverflowError 異常
      • 若是本地方法棧能夠動態擴展,而且在嘗試擴展的時候沒法申請到足夠的內存,或者在建立新的線程時沒有足夠的內存去建立對應的本地方法棧,那麼 Java虛擬機將會拋出一個 OutofMemoryError 異常
    • 它的具體作法是 Mative Method Stack 中登記 native 方法,在 Execution Engine 執行時加載本地方法庫當某個線程調用一個本地方法時,它就進入了一個全新的而且再也不受虛擬機限制的世界。它和虛擬機擁有一樣的權限。
    • 並非全部 JVM 都支持本地方法。由於 Java 虛擬機規範並無明確要求本地方法棧的使用語言、具體實現方式、數據結構等。若是 JVM 產品不打算支持 native 方法,也能夠無需實現本地方法棧
    • 在 Hotspot JVM 中,直接將本地方法棧和虛擬機棧合二爲一

若是我寫 static public void main,程序還會運行嗎?dom

剛看到這個題目的時候懵逼了,在國內面試真沒見過這麼問的,因此仍是有些不肯定,因此趕忙搜索。編程語言

在 Java 中的修飾符出如今字段聲明中時的順序與 oracle 規定的 FieldModifier 順序一致,這是習慣順序,造成規範。不遵照這個約定沒有技術影響,可是會下降代碼的可讀性,由於大多數開發人員都習慣於標準順序。函數

因此程序變成 static public void main() 也能夠運行。

下面僅列舉方法修飾符:

局部變量中的默認值是什麼?

在 Java 中的局部變量不會初始化。

public class staticFactory {
    public static void main(String[] args) {
        int temp;
        System.out.println(temp);
    }
}
// 打印信息:Error:(10, 28) java: 可能還沒有初始化變量 temp

若是是靜態變量不賦值,會直接默認值爲 0

public class staticFactory {
    static int temp;
    public static void main(String[] args) {
        System.out.println(temp);
    }
}
// 打印值爲 0

若是是成員變量

public class staticFactory {
    public static void main(String[] args) {
        LocalVariables obj = new LocalVariables();
        System.out.println("a="+obj.a);
        System.out.println("b="+obj.b);
        System.out.println("c="+obj.c);
        System.out.println("d="+obj.d);
    }
}
class LocalVariables {
    int a;
    char b;
    float c;
    String d;
}
/*
	打印結果:
	a=0     int 默認值爲 0
	b=""    char 默認值爲 ""
	c=0.0   float 默認值爲 0.0
	d=null  String 默認值爲 null
*/

在 Java 中如何拷貝構造函數

Java 中的拷貝構造方法是一種使用該類的一個對象構造另一個對象的構造方法。

如何創造拷貝構造方法

// 要建立拷貝構造方法,首先須要聲明帶有和本類相同類型的參數構造函數:
public class Employee {
    private int id;
    private String name; 

    public Employee(Employee employee) {

    }
}
// 而後,將參數對象的每一個屬性都複製給新的實例。
public class Employee {
    private int id;
    private String name;
    
    public Employee(Employee employee) {
        this.id = employee.id;
        this.name = employee.name;
    }
}

上面的作法屬於淺拷貝。

上面定義的屬性是基本類型和不可變類型 (int 和 String),所以使用前拷貝就沒問題。

可是若是類中包含可變類型就要經過該構造函數實現深拷貝

爲了實現深拷貝,咱們須要根據原始可變對象類型構造新的實例。

public class Employee {
    private int id;
    private String name;
    private Date startDate;
 
    public Employee(Employee employee) {
        this.id = employee.id;
        this.name = employee.name;
        this.startDate = new Date(employee.startDate.getTime());
    }
}

什麼是標記接口

標記接口是沒有任何方法和屬性的接口,它僅僅代表它的類屬於一個特定類型,供其餘代碼測試容許作一些事情。而且標記接口不是 Java 這門語言特有的,而是計算機科學中的一種通用化的設計理念。

使用標記接口的惟一目的是使得能夠用 instanceof 進行類型查詢,例如:

if (obj instanceof Cloneable) {………}

在 Java 中,有一個經常使用的標記接口類

  • java.io.Serializable:未實現此接口的類將沒法使其任何狀態序列化或反序列化。爲保證 serialVersionUID 值跨不一樣 java 編譯器實現的一致性,序列化類必須聲明一個明確的 serialVersionUID 值。
  • java.lang.Cloneable:代表 Object.clone() 方法能夠合法地對該類實例進行按字段複製。實現此接口的類應該使用公共方法重寫 Object.clone(它是受保護的)。若是在沒有實現 Cloneable 接口的實例上調用 Object 的 clone 方法,則會致使拋出 CloneNotSupportedException 異常。
  • java.util.RandomAccess:用來代表其支持快速(一般是固定時間)隨機訪問。此接口的主要目的是容許通常的算法更改其行爲,從而在將其應用到隨機或連續訪問列表時能提供良好的性能。
  • java.rmi.Remote:Remote 接口用於標識其方法能夠從非本地虛擬機上調用的接口。任何遠程對象都必須直接或間接實現此接口。只有在「遠程接口」(擴展 java.rmi.Remote 的接口)中指定的這些方法纔可遠程使用。

爲何 Java 不徹底面向對象

首先什麼纔是純面嚮對象語言?

純面嚮對象語言或徹底面向對象語言是指徹底面向對象的語言,它支持或具備將程序內的全部內容視爲對象的功能。它不支持原始數據類型(如 int,char,float,bool 等)。編程語言知足七種標準能夠就能夠稱爲純粹的面嚮對象語言,他們是:

  1. 封裝 / 數據隱藏
  2. 繼承
  3. 多態性
  4. 抽象化
  5. 全部預約義類型都是對象
  6. 全部用戶定義的類型都是對象
  7. 對對象執行的全部操做必須僅經過對象公開的方法

爲何 Java 不是純面嚮對象語言?

Java 支持屬性 一、二、三、4 和 6 但不支持上面給出的 5 和 7。 Java 語言不是純面嚮對象語言,由於它包含如下屬性:

原始數據類型例如對象:

Smalltalk 是一種 「純粹的」 面向對象的編程語言,與 Java 和 C++ 不一樣,由於做爲對象的值和做爲基本類型的值之間沒有區別。在 Smalltalk 中,原始值,例如整數,布爾值和字符也是對象。在 Java 中,咱們將預約義類型做爲非對象 (基本類型)。

int a = 5;  
System.out.print(a);

static 關鍵字:

當咱們將一個類聲明爲 static 時,能夠在不使用 Java 中的對象的狀況下使用它。

以上兩點能夠解釋爲何 Java 不是徹底面向對象。

你能在 Java 程序中實現指針嗎

Java 虛擬機隱式地負責內存管理。Java 的主要格言是保持編程簡單。所以,不建議直接經過指針訪問內存。因此,指針在 Java 中被消除了,Java 中並無指針

解釋一下 Java String 池

在 Java 的堆內存空間中,有一塊區域用來存放 Java 字符串池。當須要建立一個新的字符串的時候,JVM 首先檢查對象是否存在於字符串池中,若是存在,這個引用的對象將指向該變量,若是不存在則建立一個新對象。

因爲String實例對象不可修改,所以,「多個 String 實例對象引用變量引用同一個 String 實例對象」在節省 Java 堆內存的狀況下,並不會帶來任何問題。

直接賦值的方式

使用 new 的方式

  • 此時,若是判斷 S1 == S2 是否相等是返回 true 的,由於他們指向同一個值
  • 若是判斷 S3 == S4 是否相等返回 false,由於他們比較的是 S3 和 S4 的地址

當 main() 方法沒有生命成靜態會發生什麼?

main() 方法是 Java 程序的入口,也能夠理解爲一個接口,在 java 編程中,JVM 會查找類中的 public static void main ( String [] args ),若是找不到該方法就拋出錯誤 NoSuchMethodError:main 程序終止。main() 方法必須嚴格遵循它的語法規則,方法簽名必須是 public static void,參數是字符串數組類型。

固然還有其餘一些意義:

  • 正由於 main() 方法是靜態的,JVM 調用這個方法就不須要建立任何包含這個 main() 方法的實例。
  • 由於 C 和 C++ 一樣有相似的 main() 方法做爲程序執行的入口。
  • 若是 main() 方法不聲明爲靜態的,JVM 就必須建立 main() 類的實例,由於構造器能夠被重載,JVM 就無法肯定調用哪一個 main() 方法。
  • 靜態方法和靜態數據加載到內存就能夠直接調用而不須要像實例方法同樣建立實例後才能調用,若是 main() 方法是靜態的,那麼它就會被加載到 JVM 上下文中成爲可執行的方法

總結

以上能夠看的出來,老外的面試的偏向性和咱們仍是有些區別的,這些題有一半在咱們的面試中是不常問到的,因此取長補短。

相關文章
相關標籤/搜索