1. JAVA中的幾種基本資料類型是什麼,各自占用多少位元組。
類型 | 占位 | 取值範圍 | 位元組大小 |
byte | 8 | -128 ~ 127 | 1 |
short | 16 | | 2 |
char | 16 | | 2 |
int | 32 | | 4 |
long | 64 | | 8 |
double | 64 | | 8 |
float | 32 | | 4 |
boolean | 1 | | 1 |
==================================================================
2. String類能被繼承嗎,為什麼。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence
String 被final修飾了,所有不能被繼承。
1.final修飾的對象不能被修改;
2.final修飾的類不能被繼承;
3.final修飾的方法不能被重寫;
==================================================================
3. String,Stringbuffer,StringBuilder的差別。
1.是否多線程安全
String對象因為是不可變的,是以是線程安全的;
StringBuffer對方法加了同步鎖或者對調用的方法加了同步鎖,是以是線程安全的;
StringBuilder非線程安全的。
2.可變與不可變
String類中使用字元數組儲存字元串,如下就是,因為有“final”修飾符,是以可以知道string對象是不可變的。
private final char value[];
StringBuffer和StringBuilder類中使用字元數組儲存字元串,沒有使用final修飾,是以是可變的。
是以String對象進行改變時,是重新生成一個對象,并把引用指向新生成的對象,每次生成對象的時候都會浪費性能,而且會産生很多的垃圾的中間對象,導緻GC更加頻繁,StringBuffer和StringBuilder對象都是對對象本身進行操作,不會改變對象的引用。是以普遍認為當字元串需要經常進行修改的情況下,String的性能比StringBuffer和StringBuilder低,但是也是有特例的:
public class Test{
public static void main(String[] args){
String string ="A"+"B"+"C";
StringBuffer stringBuffer = new StringBuffer("A").append("B").append("C");
StringBuilder stringBuilder = new StringBuilder("A").append("B").append("C");
}
}
javac Test.java
javap -verbose Test
0: ldc #2 // String ABC
2: astore_1
3: new #3 // classjava/lang/StringBuffer
6: dup
7: ldc #4 // String A
9: invokespecial #5 // Method java/lang/StringBuffer.
>":(Ljava/lang/String;)V
12: ldc #6 // String B
14: invokevirtual #7 // Method java/lang/StringBuffer.
:(Ljava/lang/String;)Ljava/lang/StringBuffer;
17: ldc #8 // String C
19: invokevirtual #7 // Method java/lang/StringBuffer.
:(Ljava/lang/String;)Ljava/lang/StringBuffer;
22: astore_2
23: new #9 // classjava/lang/StringBuilder
26: dup
27: ldc #4 // String A
29: invokespecial #10 // Method java/lang/StringBuilder
t>":(Ljava/lang/String;)V
32: ldc #6 // String B
34: invokevirtual #11 // Method java/lang/StringBuilder
d:(Ljava/lang/String;)Ljava/lang/StringBuilder;
37: ldc #8 // String C
39: invokevirtual #11 // Method java/lang/StringBuilder
d:(Ljava/lang/String;)Ljava/lang/StringBuilder;
42: astore_3
43: return
1.這裡面建立一個String 對象,JVM在編譯期進行了常量折疊,直接拼接了ABC,是以在運作的時候隻進行了兩步:從字元串常量池中擷取ABC、将string指向ABC
2.這裡面建立一個StringBuffer和StringBuilder對象時,都經曆了七步:
a.從字元串常量池裡面取出A,
b.調用StringBuilder和StringBuilder的append方法,添加A;
c.從字元串常量池裡面取出B,
d.調用StringBuilder和StringBuilder的append方法,添加B;
e.從字元串常量池裡面取出C,
f.調用StringBuilder和StringBuilder的append方法,添加C;
g.将 StringBuilder和StringBuilder指向 ABC
總結:
運作速度:StringBuilder > StringBuffer > String
線上程安全上,StringBuilder是線程不安全的,而StringBuffer是線程安全的
String:适用于少量的字元串操作的情況
StringBuilder:适用于單線程下在字元緩沖區進行大量操作的情況
StringBuffer:适用多線程下在字元緩沖區進行大量操作的情況
==================================================================
4. ArrayList和LinkedList有什麼差別。
1.對ArrayList和LinkedList而言,在清單末尾增加一個元素所花的開銷都是固定的。對ArrayList而言,主要是在内部數組中增加一項,指向所添加的元素,偶爾可能會導緻對數組重新進行配置設定;而對LinkedList而言,這個開銷是統一的,配置設定一個内部Entry對象。
2.在ArrayList的中間插入或删除一個元素意味着這個清單中剩餘的元素都會被移動;而在LinkedList的中間插入或删除一個元素的開銷是固定的。
3.LinkedList不支援高效的随機元素通路。
4.ArrayList的空間浪費主要展現在在list清單的結尾預留一定的容量空間,而LinkedList的空間花費則展現在它的每一個元素都需要消耗相當的空間
可以這樣說:當操作是在一列資料的後面添加資料而不是在前面或中間,并且需要随機地通路其中的元素時,使用ArrayList會提供比較好的性能;當你的操作是在一列資料的前面或中間添加或删除資料,并且按照順序通路其中的元素時,就應該使用LinkedList了。
==================================================================
5. 講講類的執行個體化順序,比如父類靜态資料,父類構造函數,父類字段,子類靜态資料,子類構造函數,子類字段,當new的時候,他們的執行順序。
類的執行個體化順序:先靜态再父子
父類靜态代碼->子類靜态代碼->父類非靜态代碼->子類非靜态代碼->父類構造函數->子類構造函數
==================================================================
6. 用過哪些Map類,都有什麼差別,HashMap是線程安全的嗎,并發下使用的Map是什麼,他們内部原理分别是什麼,比如存儲方式,hashcode,擴容,預設容量等。
最常用的Map實作類有:HashMap,ConcurrentHashMap(jdk1.8),LinkedHashMap,TreeMap,HashTable;
其中最頻繁的是HashMap和ConcurrentHashMap,他們的主要差別是HashMap是非線程安全的。ConcurrentHashMap是線程安全的。
并發下可以使用ConcurrentHashMap和HashTable,他們的主要差別是:
1.ConcurrentHashMap的hash計算公式:(key.hascode()^ (key.hascode()>>> 16)) & 0x7FFFFFFF
HashTable的hash計算公式:key.hascode()& 0x7FFFFFFF
2.HashTable存儲方式都是連結清單+數組,數組裡面放的是目前hash的第一個資料,連結清單裡面放的是hash沖突的資料
ConcurrentHashMap是數組+連結清單+紅黑樹
3.預設容量都是16,負載因子是0.75。就是當hashmap填充了75%的busket是就會擴容,最小的可能性是(16*0.75),一般為原記憶體的2倍
4.線程安全的保證:HashTable是在每個操作方法上面加了synchronized來達到線程安全,ConcurrentHashMap線程是使用CAS(compore and swap)來保證線程安全的
==================================================================
7. JAVA8的ConcurrentHashMap為什麼放棄了分段鎖,有什麼問題嗎,如果你來設計,你如何設計。
jdk8 放棄了分段鎖而是用了Node鎖,減低鎖的粒度,提高性能,并使用CAS操作來確定Node的一些操作的原子性,取代了鎖。
但是ConcurrentHashMap的一些操作使用了synchronized鎖,而不是ReentrantLock,雖然說jdk8的synchronized的性能進行了優化,但是我覺得還是使用ReentrantLock鎖能更多的提高性能
==================================================================
8. 有沒順序的 Map 實作類,如果有,他們是怎麼保證有序的 。
順序的 Map 實作類:LinkedHashMap,TreeMap
LinkedHashMap 是基于元素進入集合的順序或者被通路的先後順序排序,TreeMap 則是基于元素的固有順序 (由 Comparator 或者 Comparable 确定)。
==================================================================
9. 抽象類和接口的差別,類可以繼承多個類麼,接口可以繼承多個接口麼,類可以實作多個接口麼。
抽象類和接口的差別:
1.抽象類可以有自己的實作方法,接口在jdk8以後也可以有自己的實作方法(default)
2.抽象類的抽象方法是由非抽象類的子類實作,接口的抽象方法有接口的實作類實作
3.接口不能有私有的方法跟對象,抽象類可以有自己的私有的方法跟對象
類不可以繼承多個類,接口可以繼承多個接口,類可以實作多個接口
==================================================================
10. 繼承和聚合的差別在哪。
繼承:指的是一個類(稱為子類、子接口)繼承另外的一個類(稱為父類、父接口)的功能,并可以增加它自己的新功能的能力,繼承是類與類或者接口與接口之間最常見的關系;在Java中此類關系通過關鍵字extends明确辨別,在設計時一般沒有争議性;
聚合:聚合是關聯關系的一種特例,他展現的是整體與部分、擁有的關系,即has-a的關系,此時整體與部分之間是可分離的,他們可以具有各自的生命周期,部分可以屬于多個整體對象,也可以為多個整體對象共享;比如計算機與CPU、公司與員工的關系等;表現在代碼層面,和關聯關系是一緻的,隻能從語義級别來區分;
==================================================================
11. 講講你了解的nio。他和bio的差別是啥,談談reactor模型。
BIO:同步阻塞式IO,伺服器實作模式為一個連接配接一個線程,即用戶端有連接配接請求時伺服器端就需要啟動一個線程進行處理,如果這個連接配接不做任何事情會造成不必要的線程開銷,當然可以通過線程池機制改善。
NIO:同步非阻塞式IO,伺服器實作模式為一個請求一個線程,即用戶端發送的連接配接請求都會注冊到多路複用器上,多路複用器輪詢到連接配接有I/O請求時才啟動一個線程進行處理。
reactor模型:反應器模式(事件驅動模式):當一個主體發生改變時,所有的屬體都得到通知,類似于觀察者模式。
==================================================================
12. 反射的原理,反射建立類執行個體的三種方式是什麼。
反射的原理:如果知道一個類的名稱/或者它的一個執行個體對象, 就能把這個類的所有方法和變量的資訊(方法名,變量名,方法,修飾符,類型,方法參數等等所有資訊)找出來。
反射建立類執行個體的三種方式:
1.Class.forName("com.A");
2.new A().getClass();
3.A.class;
==================================================================
13. 反射中,Class.forName和ClassLoader差別。
class.forName()除了将類的.class檔案加載到jvm中之外,還會對類進行解釋,執行類中的static塊。
classLoader隻幹一件事情,就是将.class檔案加載到jvm中,不會執行static中的内容,隻有在newInstance才會去執行static塊。
==================================================================
14. 描述動态代理的幾種實作方式,分别說出相應的優缺點。
動态代理有兩種實作方式,分别是:jdk動态代理和cglib動态代理
jdk動态代理的前提是目标類必須實作一個接口,代理對象跟目标類實作一個接口,進而避過虛拟機的校驗。
cglib動态代理是繼承并重寫目标類,是以目标類和方法不能被聲明成final。
==================================================================
15. 動态代理與cglib實作的差別。
動态代理有兩種實作方式,分别是:jdk動态代理和cglib動态代理
jdk動态代理的前提是目标類必須實作一個接口,代理對象跟目标類實作一個接口,進而避過虛拟機的校驗。
cglib動态代理是繼承并重寫目标類,是以目标類和方法不能被聲明成final。
==================================================================
16. 為什麼cglib方式可以對接口實作代理。
cglib動态代理是繼承并重寫目标類,是以目标類和方法不能被聲明成final。而接口是可以被繼承的。
==================================================================
17. final的用途。
1.final修飾的對象不能被修改;
2.final修飾的類不能被繼承;
3.final修飾的方法不能被重寫;
==================================================================
18. 寫出三種單例模式實作。
/**
* 單例模式
*/
public class Singleton {
/**
* 餓漢式 單例模式
* 類在加載時就執行個體化,
* 提供一個公共的方法擷取執行個體化的類
* <p>
* 優點:線程安全
* <p>
* 缺點:類被加載時就執行個體化,
* 有可能在整個代碼周期都沒有使用
* 且不會被回收,會一直存在
*/
private static class 餓漢式 {
private static 餓漢式 餓漢式 = new 餓漢式();
private 餓漢式() {
}
static 餓漢式 get餓漢式() {
return 餓漢式;
}
}
/**
* 懶漢式 單例模式
* 隻有在真正使用的時候,才執行個體化
* <p>
* 優點:隻有在真正使用的時候才執行個體化
* <p>
* 缺點:線程不安全
*/
private static class 懶漢式 {
private static 懶漢式 懶漢式;
private 懶漢式() {
}
static synchronized 懶漢式 get懶漢式() {
if (null == 懶漢式) {
懶漢式 = new 懶漢式();
}
return 懶漢式;
}
}
/**
* 枚舉式 單例模式
* 跟 餓漢式 一樣
*/
private enum 枚舉式 {
枚舉式;
public 枚舉式 get枚舉式() {
return 枚舉式;
}
}
public static void main(String[] args) {
Singleton.懶漢式 懶漢式 = Singleton.懶漢式.get懶漢式();
if (懶漢式 == Singleton.懶漢式.get懶漢式()) {
System.out.println("懶漢式--單例生效");
}
Singleton.餓漢式 餓漢式 = Singleton.餓漢式.get餓漢式();
if (餓漢式 == Singleton.餓漢式.get餓漢式()) {
System.out.println("餓漢式--單例生效");
}
if (枚舉式.枚舉式.get枚舉式() == 枚舉式.枚舉式.get枚舉式()) {
System.out.println("枚舉式--單例生效");
}
}
}
==================================================================
19. 如何在父類中為子類自動完成所有的hashcode和equals實作?這麼做有何優劣。
父類的equals不一定滿足子類的equals需求。比如所有的對象都繼承Object,預設使用的是Object的equals方法,在比較兩個對象的時候,是看他們是否指向同一個位址。
但是我們的需求是對象的某個屬性相同,就相等了,而預設的equals方法滿足不了目前的需求,是以我們要重寫equals方法。
如果重寫了equals 方法就必須重寫hashcode方法,否則就會降低map等集合的索引速度。
==================================================================
20. 請結合OO設計理念,談談通路修飾符public、private、protected、default在應用設計中的作用。
OO設計理念:封裝、繼承、多态
封裝,也就是把客觀事物封裝成抽象的類,并且類可以把自己的資料和方法隻讓可信的類或者對象操作,對不可信的進行資訊隐藏。是以我們可以通過public、private、protected、default 來進行通路控制
關鍵字 | 類内部 | 本包 | 子類 | 外部包 |
public | √ | √ | √ | √ |
protected | √ | √ | √ | × |
default | √ | √ | × | × |
private | √ | × | × | × |
java通路控制符的含義和使用情況
==================================================================
21. 深拷貝和淺拷貝差別。
淺拷貝隻拷貝指針,深拷貝就是拷貝他的值,重新生成的對像。
==================================================================
22. 數組和連結清單資料結構描述,各自的時間複雜度。
數組是将元素在記憶體中連續存放,由于每個元素占用記憶體相同,可以通過下标迅速通路數組中任何元素。
連結清單恰好相反,連結清單中的元素在記憶體中不是順序存儲的,而是通過存在元素中的指針聯系到一起。
通路數組中第 n 個資料的時間花費是 O(1) 但是要在數組中查找一個指定的資料則是 O(N) 。當向數組中插入或者删除資料的時候,最好的情況是在數組的末尾進行操作,時間複雜度是 O(1) ,但是最壞情況是插入或者删除第一個資料,時間複雜度是 O(N) 。在數組的任意位置插入或者删除資料的時候,後面的資料全部需要移動,移動的資料還是和資料個數有關是以總體的時間複雜度仍然是 O(N) 。
在連結清單中查找第 n 個資料以及查找指定的資料的時間複雜度是 O(N) ,但是插入和删除資料的時間複雜度是 O(1)
==================================================================
23. error和exception的差別,CheckedException,RuntimeException的差別。
Error(錯誤)表示系統級的錯誤和程式不必處理的異常,是java運作環境中的内部錯誤或者硬體問題。比如:記憶體資源不足等。對于這種錯誤,程式基本無能為力,除了退出運作外别無選擇,它是由Java虛拟機抛出的。
Exception(違例)表示需要捕捉或者需要程式進行處理的異常,它處理的是因為程式設計的瑕疵而引起的問題或者在外的輸入等引起的一般性問題,是程式必須處理的。
Exception又分為運作時異常,受檢查異常。
RuntimeException(運作時異常),表示無法讓程式恢複的異常,導緻的原因通常是因為執行了錯誤的操作,建議終止程式,是以,編譯器不檢查這些異常。
CheckedException(受檢查異常),是表示程式可以處理的異常,也即表示程式可以修複(由程式自己接受異常并且做出處理), 是以稱之為受檢查異常。
==================================================================
24. 請列出5個運作時異常。
NullPointerException
IndexOutOfBoundsException
ClassCastException
ArrayStoreException
BufferOverflowException
==================================================================
25. 在自己的代碼中,如果建立一個java.lang.String對象,這個對象是否可以被類加載器加載?為什麼。
不可以,雙親委派模式會保證父類加載器先加載類,就是BootStrap(啟動類)加載器加載jdk裡面的java.lang.String類,而自定義的java.lang.String類永遠不會被加載到
==================================================================
26. 說一說你對java.lang.Object對象中hashCode和equals方法的了解。在什麼場景下需要重新實作這兩個方法。
父類的equals不一定滿足子類的equals需求。比如所有的對象都繼承Object,預設使用的是Object的equals方法,在比較兩個對象的時候,是看他們是否指向同一個位址。
但是我們的需求是對象的某個屬性相同,就相等了,而預設的equals方法滿足不了目前的需求,是以我們要重寫equals方法。
如果重寫了equals 方法就必須重寫hashcode方法,否則就會降低map等集合的索引速度。
==================================================================
27. 在jdk1.5中,引入了泛型,泛型的存在是用來解決什麼問題。
泛型的好處是在編譯的時候檢查類型安全,并且所有的強制轉換都是自動和隐式的,提高代碼的重用率。
==================================================================
28. 有沒可能 2個不相等的對象有同hashcode。
有可能,最簡單的方法,百分百實作的方式就是重寫hascode();
==================================================================
29. Java中的HashSet内部是如何工作的。
public HashSet() {
map = new HashMap<>();
}
預設使用的是HaseMap;
==================================================================
30. 什麼是序列化,怎麼序列化,為什麼序列化,反序列化會遇到什麼問題,如何解決。
序列化是一種用來處理對象流的機制 ,所謂對象流就是将對象的内容進行流化。
序列化是為了解決在對對象流進行讀寫操作時所引發的問題。
序列化的實作:将需要被序列化的類實作Serializable接口,該接口沒有需要實作的方法,implements Serializable隻是為了标注該對象是可被序列化的,然後使用一個輸出流(如:FileOutputStream)來構造一個ObjectOutputStream(對象流)對象,接着,使用ObjectOutputStream對象的writeObject(Object obj)方法就可以将參數為obj的對象寫出(即儲存其狀态),要恢複的話則用輸入流;
==================================================================
31. java8 的新特性。
參見:https://blog.csdn.net/54powerman/article/details/73188951
轉自: https://blog.csdn.net/u012257955/article/details/70847206