阿里 Java 面試題——Java基礎篇

1.JAVA中的幾種基本數據類型是什麼,各自佔用多少字節。

答案:

java分為兩種數據類型:

1.基本數據類型

2.引用數據類型:如,類,接口,數組

基本數據類型:8種

1.byte 1個字節,-128~127

2.short 2個字節,-32768 ~ 32767

3.int 4個字節,-231-1~231 (21 億)

4.long 8個字節,

5.float 4個字節,

6.double 8個字節,

7.char 2個字節,

8.boolean 1或4個字節,一個字節表示,至少也得用一個字節來表示,4個字節,32位的cpu每次處理4個字節,效率問題

2.String類能被繼承嗎,為什麼。

答案:

不能。應該改類final類,不能被繼承

3.String,Stringbuffer,StringBuilder的區別。

答案

String對象一旦被創建,裡面的值便不可改變。需要改變就需要創建新的對象

而Stringbuffer和StringBuilder是可變的。裡面的值改變而不需要創建新的對象。

Stringbuffer是線程安全的,性能低

StringBuilder是線程不安全的,性能高

4.ArrayList和LinkedList有什麼區別。

答案

都實現了List接口。ArrayList的底層實現原理是數組類型。數組類型代表著,查詢快,但是插入和刪除慢,需要查詢某個值時,直接通過索引,時間複雜度O(1)。當需要插入某個值時,在這個位置後面的值都得向後移動一位,物理上是一段連續內存。

LinkedList低層實現是鏈表,鏈表則表示查詢慢,新增刪除快。刪除,只需要改變,指針的指向,當然在java中沒有指針這一概念。

5.講講類的實例化順序,比如父類靜態數據,構造函數,字段,子類靜態數據,構造函數,字段,當new的時候,他們的執行順序。

答案

類的實例化順序。最先執行的是父類的靜態數據->子類的靜態數據->父類的構造方法->子類的構造方法。

記住兩個順序,先靜態,先父類。

6.用過哪些Map類,都有什麼區別,HashMap是線程安全的嗎,併發下使用的Map是什麼,他們 內部原理分別是什麼,比如存儲方式,hashcode,擴容,默認容量等。

答案

阿里 Java 面試題——Java基礎篇

直接實現了map接口的主要有HashMap和AbstractMap以及Hashtable。而TreeMap和ConcurrentHashMap都繼承與AbstractMap。、

區別:

1.HashMap的底層數據結構是數組加鏈表。每一個entry都有一個key,value,當不同的key經過hash函數運算之後得到了一個相同的值,這時候便發生了hash衝突。便會把value值存在一個數組裡面。而多個entry便構成一個數組。允許key,value位null。採用鏈表可以有效的解決hash衝突。

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //這是hashMap的默認容量。2^4為16.默認的加載因子為0.75,當數組元素的實際個數超過16 * 0.75時,便會進行擴容操作。大小為2*16,擴大一倍。

2.Hashtable方法是同步的,也就是線程安全的,key和value都不能為空。

3.CurrentHashMap是線程安全的,採用了分段鎖,在java1.8之後放棄的分段鎖的使用。

4.TreeMap底層的數據結構是有順序訪問的紅黑樹,

7.JAVA8的ConcurrentHashMap為什麼放棄了分段鎖,有什麼問題嗎,如果你來設計,你如何設計。

答案

jdk1.8之後ConcurrentHashMap取消了segment分段鎖,而採用CAS和synchronized來保證併發安全。數據結構跟HashMap1.8的結構一樣,數組+鏈表/紅黑二叉樹。

synchronized只鎖定當前鏈表或紅黑二叉樹的首節點,這樣只要hash不衝突,就不會產生併發,效率又提升N倍。

8.有沒有有順序的Map實現類,如果有,他們是怎麼保證有序的。

答案

LinkedHashMap,TreeMap便是有順序的map實現類。LinkedHashMap繼承於HashMap。

LinkedHashMap保證有序的結構是雙向鏈表,TreeMap保證有序的結構是紅黑樹。

9.抽象類和接口的區別,類可以繼承多個類麼,接口可以繼承多個接口麼,類可以實現多個接口麼。

答案

抽象類,可以有默認的 方法實現。可以有構造器,不可以實例化,可以有public,protected,default。可以有main方法,可以運行main方法。

接口,沒有方法的實現,沒有構造器,不可以實例化,只有public。不可以有main方法。

10.繼承和聚合的區別在哪。

答案

繼承:子類繼承於父類

實現:類實現接口

依賴:類A在method中使用到了類B

關聯:被關聯類B作為一個全局屬性出現在關聯類A中

聚合:關聯的一種特殊情況,類A Has-a 類B,一個類B的集合作為全局屬性出現在類A

組合:a擁有b,a沒了b也就沒了

11.IO模型有哪些,講講你理解的nio ,他和bio,aio的區別是啥,談談reactor模型。

答案

bio:同步阻塞io。意思是當進行io操作時,我們的一個線程對應一個io操作。並且時阻塞的。假如socket通信,當客服端沒有發送信息到服務器端時,服務器上這個線程便一直阻塞在這裡,不會向下執行。

nio:同步非阻塞,採用的原理主要是輪詢。當多個io操作同時存在時,一個線程便可以解決,線程先走到一個io操作的代碼處,如果此時通道並沒有數據,也不需要寫入,線程會繼續走,訪問下一個io操作,就這樣不停的輪詢訪問,當通道有數據了,並且需要讀取的時候,線程便執行讀操作。

aio:異步非阻塞,也成為nio2.是nio的改進版。每一個io操作都會有一個回調函數。線程不會去輪詢。當某個io需要讀取或寫入數據時,便會執行回調函數,線程便在此處。

reactor:基於事件處理

12.反射的原理,反射創建類實例的三種方式是什麼。

答案

原理:在jvm運行中,java文件被編譯為.class文件。通過字節碼找到類,以及類中的方法和屬性。

1.通過已知對象s1.getClass();

2.通過類名Student.class();

3.通過Class.forName(“包名.類名”);

通過以上三種方法便可得到一個Class類,一個Class類對應著該類的.class字節碼,但此時不能直接通過new來創建對象。可以通過getConstructors() 獲取該類的構造器。在使用構造器類的newInstance()方法便可以得到該類的對象。

13.反射中,Class.forName和ClassLoader區別 。

答案

Java類裝載過程

加載–>驗證–>準備–>解析–>初始化–>使用–>卸載

1.Class.forName(className)方法,內部實際調用的方法是Class.forName(className,true,classloader);

第二個參數表示釋放需要初始化,默認為true,則需要初始化,初始化便會激活靜態變量和靜態代碼塊,並賦值。

2.ClassLoader.loadClass(className)方法,內部實際調用的方法是 ClassLoader.loadClass(className,false);第二個參數表示是否進行鏈接。鏈接包含以上的三個步驟,驗證–>準備–>解析。在準備的時候便會給類的靜態變量分配並初始化內存空間。false表示不進行鏈接,則無法執行靜態代碼塊或靜態對象。

14.描述動態代理的幾種實現方式,分別說出相應的優缺點。

答案

1.jdk動態代理;必須繼承接口。

2.cglib動態代理不能使用final類。

15.動態代理與cglib實現的區別。

答案

1.jdk動態代理;反射原理實現

2.cglib動態代理是利用asm開源包,對代理對象類的class文件加載進來,通過修改其字節碼生成子類來處理。

16.為什麼CGlib方式可以對接口實現代理。

答案

cglib動態代理是利用asm開源包,對代理對象類的class文件加載進來,通過修改其字節碼生成子類來處理。意思便是這個實現類只要不是final類,便可以創建其子類。便可以實現動態代理。

17.final的用途。

答案

表示最終類,不能被繼承。

1.final關鍵字提高了性能。JVM和Java應用都會緩存final變量。

2.final變量可以安全的在多線程環境下進行共享,而不需要額外的同步開銷。

3.使用final關鍵字,JVM會對方法、變量及類進行優化。

用途:

1.常量,

2.多線程共享

18.寫出三種單例模式實現 。

答案

1.餓漢模式

<code>package com.singlo;

public class Student {

\tprivate static final Student student=new Student();
\t
\tprivate Student(){
\t\t
\t}
\t
\tpublic static Student getInstans(){
\t\treturn student;
\t}
}
/<code>

2.懶漢模式

<code>package com.singlo;

public class Student {

\tprivate static Student student=new Student();
\t
\tprivate Student(){
\t\t
\t}
\t
\tpublic static Student getInstans(){
\t\tsynchronized (Student.class) {
\t\t\tif(student==null){
\t\t\t\treturn new Student();
\t\t\t}
\t\t\treturn student;
\t\t}
\t}
}/<code>

3.靜態內部類實現(登記者模式)

<code>package com.singlo;

public class Student {

\tprivate Student(){
\t\t
\t}
\t
\tprivate static Student getInstance(){

\t\treturn Holder.student;
\t}
\t
\tprivate static class Holder{
\t\tprivate static Student student=new Student();
\t}
}
/<code>

19.如何在父類中為子類自動完成所有的hashcode和equals實現?這麼做有何優劣。

答案

Obejct是所有類都父類,裡面封裝了hashcode本地方法和equals方法。

如果我們自定義父類。來重寫hashcode方法和equals方法。

優勢:這個不用說了,所有的類都享有父類的方法。

劣勢:當面我們的子類擁有不同的屬性時,而這個屬性又需要作為判斷是否相等時,這時候我們還是需要自己重寫父類的方法,而在java中繼承是不推薦使用的。

20.請結合OO設計理念,談談訪問修飾符public、private、protected、default在應用設計中的作用。

答案

public:

具有最大的訪問權限,可以訪問任何一個在classpath下的類、接口、異常等。它往往>>用於對外的情況,也就是對象或類對外的一種接口的形式。

protected:

主要的作用就是用來保護子類的。它的含義在於子類可以用它修飾的成員,其他的不可以,它相當於傳遞給子類的一種繼承的東西

default:

有時候也稱為friendly,它是針對本包訪問而設計的,任何處於本包下的類、接口、異常等,都可以相互訪問,即使是父類沒有用protected修飾的成員也可以。

private:

訪問權限僅限於類的內部,是一種封裝的體現,例如,大多數成員變量都是修飾符為private的,它們不希望被其他任何外部的類訪問。

個人理解:其實這四種修飾符表示的就是權限問題。即要遵循最小權限原則,則只要需要protected權限,絕不能使用public。

21.深拷貝和淺拷貝區別。

答案

句柄指向了改對象的物理內存地址,

淺拷貝,句柄不一樣,但是指向的物理內存地址是同一個地址。即A是B的淺拷貝對象,修改了B的屬性,A的屬性也跟著變了。

深拷貝,句柄不一樣,指向的物理內存也不一樣。

22.數組和鏈表數據結構描述,各自的時間複雜度。

答案

數組:一段連續的物理內存。可通過數組下標直接獲取值。通過下標查詢值的時間複雜度,O(1),插入,刪除操作,時間複雜度O(n)

鏈表:邏輯上是連續的,物理上不一定是連續的。查詢的時間複雜度O(n),刪除,插入的時間複雜度O(1)。

23.error和exception的區別,CheckedException,RuntimeException的區別。

答案

阿里 Java 面試題——Java基礎篇

簡單來說,Error是系統級錯誤,是程序員不可控的。

Exception是應用程序的錯誤,分為檢查異常和非檢查異常。

CheckedException,檢查異常也叫編譯異常,必須處理,在代碼中捕獲或者拋出。

RuntimeException,非檢查異常也叫運行時異常,不必須要求處理。

24.請列出5個運行時異常。

答案

IndexOutOfBoundsException(下標越界異常)

NullPointerException(空指針異常)

ArithmeticException -(算術運算異常 如除數為0)

ArrayStoreException - (向數組中存放與聲明類型不兼容對象異常)

SecurityException -(安全異常)

25.在自己的代碼中,如果創建一個java.lang.String類,這個類是否可以被類加載器加載?為什麼。

答案

不能

java中類加載器 自定義類加載器 >> 應用程序類加載器 >> 擴展類加載器 >> 啟動類加載器

應用程序類加載器 ,擴展類加載器繼承自抽象類java.lang.ClassLoader。這4個加載器之間並不是繼承關係,而是組合關係。

首先要知道自定義String能不能被加載,我們先來了解下類加載器採用的模式,雙親委託模式。

1.什麼是雙親委託模式。

即一個類在加載過程中,會向上傳遞,最終到達啟動類加載,啟動類加載器,查看該類是否是核心庫裡面的類同名,如果是,只會加載核心庫的類,不會加載該類。如果改類與核心庫不同名,啟動類加載器也不會加載該類,而會交給下一級加載器進行處理。

雙親委託模式便是至下向上傳遞。至上往下加載。

所以我們自定義的String對象不會被加載。這種模式也更好的保護了我們的核心類不會被自定義類所替換。

26.說一說你對java.lang.Object對象中hashCode和equals方法的理解。在什麼場景下需要重新實現這兩個方法。

答案

在比較兩個對象的值是否相同的情況下,需要重寫equals方法,而重寫了equals方法就需要重寫hashcode方法。

目的:先說我們重寫equals之後就要重寫hashcode的目的,為了我們能夠在set集合或者map集合中能夠正常的使用改類。

原因:在object中equals方法內容是this==obj。而hashcode也就是通過一個值,可能是一個對象或者一個內存地址。經過hash運算過後得到一個值。這個值叫hash碼值。

hash碼值有一個特徵。同一個對象經過無論多少次運算得到的結果都是一樣的。而不同的對象經過運算,有可能一樣,有可能不一樣。

而在map集合中,不能存在相同的key值。

我們重寫了equals方法。而不重寫hashcode方法後果是怎樣。我們創建該類的兩個對象,兩個對象賦予相同的值。然後依次put進hashmap中。hashmap中會通過對象得到hashcode。由於是兩個不同的對象,hashcode並沒有重寫,此時有可能得到的是不同的hash值。然後存粗在了hashmap集合中不同的下標上。而在我們看來,他們兩個對象的值相同,就是同一個對象,為什麼還能在集合中存在兩個呢。所以錯誤

在重寫了equals方法後,並重寫hashcode方法。還是上面的兩個對象。由於重寫了hashcode。兩個對象的值相同,所以得到了同一個hash值。這時候通過equlas判斷是否是同一個對象。由於重寫了equals方法,所以得到的還是true。hashmap便會認為這兩個對象是同一個對象。

其實就是一點。當我們重寫了equals方法,即在我們眼中只要這個對象的值相同,即我們把他看作了同一個對象。而你要把兩個不同的對象看成是同一個對象,就必須重寫他的hashcode方法。

所以在我們沒有重寫equals方法時,哪怕兩個對象的值一樣,我們也看作是兩個對象

在java中。String,Integer等類都重寫了equals方法和hashcode方法。

27.在jdk1.5中,引入了泛型,泛型的存在是用來解決什麼問題。

答案

首先為什麼要引入泛型。我們假定一種場景。有兩個不同的數據類型,它們都需要做相同的業務邏輯。這時候由於數據類型不同,我們是不是得寫兩個方法來分別解決。

但是有了泛型,我們不需要管傳入的到底是哪種數據類型,

這時候可能會問,所以的類都繼承於Object,使用Object可以完成同樣的工作啊,為什麼不使用Object呢。

1.使用Object。普通數據類型,拆箱和裝箱都是需要性能的,如果是引用數據類型。Object需要與與之交互的實際數據類型進行強制轉換。這個也是需要性能的

2.因為在java中,允許Object強制轉換成其他任何對象,這將意味著你將失去編譯期的類型安全。

28.這樣的a.hashcode() 有什麼用,與a.equals(b)有什麼關係。

答案

在沒有重寫這兩個方法的情況下。

同一個對象的hashcode一定相同,不同的hashcode有可能不同,也有可能相同。

equals方法即在比較兩個對象是同一個對象。

兩點:同一個對象,equals一定為true,hashcode一定相同

不同對象,equals一定為false,hashcode有可能相同。

當然這都是建立在不重寫這個兩個方法的基礎上

29.有沒有可能2個不相等的對象有相同的hashcode。

答案

有可能。這也是為什麼,會產生hash衝突。當兩個不同對象,得到了同一個hashcode。在hashmap中,即表示這兩個對象的下標是相同的。解決hash衝突的方法有幾種,在hash化,尋地址法。鏈地址法。hashmap中便採用了鏈地址法

30.Java中的HashSet內部是如何工作的。

答案

HashSet繼承於AbstractSet,實現了set接口。

內部實現是一個私有化的hashmap。當我們add(E)時。實際調用的是map.put(e, PRESENT)

hashmap的鍵值對,鍵由於不能重複,所以我們傳入的值作了map的鍵,而map的值則是一個常量。

31.什麼是序列化,怎麼序列化,為什麼序列化,反序列化會遇到什麼問題,如何解決。

答案

什麼是序列化:在java中,把對象轉換成字節序列的過程叫做序列化。

把字節序列轉換成對象的過程叫做反序列化。

怎麼序列化:在實現了Serializable接口的情況下,便可以序列化。

為什麼要序列化:

1.為了我們在內存中的對象能夠存儲在硬盤文件或數據庫需要序列化

2.為了對象在網絡中通過套接字進行傳輸。

3.通過rmi傳輸對象時需要用到序列化

反序列化會遇到什麼問題。

在我們實現了Serializable接口後,沒有給serialVersionUID一個固定值。這時候會發生這種情況,當我們序列化之後,然後改變了類中的屬性。然後再進行反序列化。這時候程序便會報錯,原因在於我們序列化時候的serialVersionUID與我們反序列化時候的serialVersionUID不一致所導致的,這也是為什麼我們在實現了Serializable接口後,不給serialVersionUID一個固定值,ide會給我們一個警告。因為我們沒有給一個固定的值。系統會根據類的屬性自動生成一個值,在我們改變了類中的屬性後,這個值自然也會發生變化。

32.java8的新特性。

答案

1.接口可以通過default關鍵字修飾方法,實現方法的具體內容,子類實現後,可直接調用該方法。

2.Lambda表達式。new Thread( () -> System.out.println(“In Java8, Lambda expression rocks !!”) ).start();

features.forEach(System.out::println);

阿里 Java 面試題——Java基礎篇


分享到:


相關文章: