欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java經(jīng)典面試題最全匯總208道(一)

 更新時間:2023年01月17日 15:55:23   作者:哪?吒  
這篇文章主要介紹了Java經(jīng)典面試題最全匯總208道(一),本文章內(nèi)容詳細,該模塊分為了六個部分,本次為第一部分,需要的朋友可以參考下

前言 

短時間提升自己最快的手段就是背面試題,最近總結(jié)了Java常用的面試題,分享給大家,希望大家都能圓夢大廠,加油,我命由我不由天。

1、JDK 和 JRE 有什么區(qū)別?

JDK(Java Development Kit),Java開發(fā)工具包

JRE(Java Runtime Environment),Java運行環(huán)境

JDK中包含JRE,JDK中有一個名為jre的目錄,里面包含兩個文件夾bin和lib,bin就是JVM,lib就是JVM工作所需要的類庫。

2、== 和 equals 的區(qū)別是什么?

  1. 對于基本類型,==比較的是值;
  2. 對于引用類型,==比較的是地址;
  3. equals不能用于基本類型的比較;
  4. 如果沒有重寫equals,equals就相當于==;
  5. 如果重寫了equals方法,equals比較的是對象的內(nèi)容;

3、final 在 java 中有什么作用?

(1)用來修飾一個引用

  1.  如果引用為基本數(shù)據(jù)類型,則該引用為常量,該值無法修改;
  2.  如果引用為引用數(shù)據(jù)類型,比如對象、數(shù)組,則該對象、數(shù)組本身可以修改,但指向該對象或數(shù)組的地址的引用不能修改。
  3.  如果引用時類的成員變量,則必須當場賦值,否則編譯會報錯。

(2)用來修飾一個方法

當使用final修飾方法時,這個方法將成為最終方法,無法被子類重寫。

但是,該方法仍然可以被繼承。

(3)用來修飾類

當用final修改類時,該類成為最終類,無法被繼承。

 比如常用的String類就是最終類。

4、java 中的 Math.round(-1.5) 等于多少?

Math提供了三個與取整有關(guān)的方法:ceil、floor、round

(1)ceil:向上取整;

Math.ceil(11.3) = 12;

Math.ceil(-11.3) = 11;

(2)floor:向下取整;

Math.floor(11.3) = 11;

Math.floor(-11.3) = -12;

(3)round:四舍五入;

加0.5然后向下取整。

Math.round(11.3) = 11;

Math.round(11.8) = 12;

Math.round(-11.3) = -11;

Math.round(-11.8) = -12;

5、String 屬于基礎(chǔ)的數(shù)據(jù)類型嗎?

不屬于。

八種基本數(shù)據(jù)類型:byte、short、char、int、long、double、float、boolean。

6、String str="i"與 String str=new String(“i”)一樣嗎?

String str="i"會將起分配到常量池中,常量池中沒有重復的元素,如果常量池中存中i,就將i的地址賦給變量,如果沒有就創(chuàng)建一個再賦給變量。

String str=new String(“i”)會將對象分配到堆中,即使內(nèi)存一樣,還是會重新創(chuàng)建一個新的對象。

7、如何將字符串反轉(zhuǎn)?

將對象封裝到stringBuilder中,調(diào)用reverse方法反轉(zhuǎn)。

8、String 類的常用方法都有那些?

(1)常見String類的獲取功能

length:獲取字符串長度;

charAt(int index):獲取指定索引位置的字符;

indexOf(int ch):返回指定字符在此字符串中第一次出現(xiàn)處的索引;

substring(int start):從指定位置開始截取字符串,默認到末尾;

substring(int start,int end):從指定位置開始到指定位置結(jié)束截取字符串;

(2)常見String類的判斷功能

equals(Object obj): 比較字符串的內(nèi)容是否相同,區(qū)分大小寫;

contains(String str): 判斷字符串中是否包含傳遞進來的字符串;

startsWith(String str): 判斷字符串是否以傳遞進來的字符串開頭;

endsWith(String str): 判斷字符串是否以傳遞進來的字符串結(jié)尾;

isEmpty(): 判斷字符串的內(nèi)容是否為空串"";

(3)常見String類的轉(zhuǎn)換功能

byte[] getBytes(): 把字符串轉(zhuǎn)換為字節(jié)數(shù)組;

char[] toCharArray(): 把字符串轉(zhuǎn)換為字符數(shù)組;

String valueOf(char[] chs): 把字符數(shù)組轉(zhuǎn)成字符串。

valueOf可以將任意類型轉(zhuǎn)為字符串;

toLowerCase(): 把字符串轉(zhuǎn)成小寫;

toUpperCase(): 把字符串轉(zhuǎn)成大寫;

concat(String str): 把字符串拼接;

(4)常見String類的其他常用功能

replace(char old,char new) 將指定字符進行互換

replace(String old,String new) 將指定字符串進行互換

trim() 去除兩端空格

int compareTo(String str) 會對照ASCII 碼表 從第一個字母進行減法運算 返回的就是這個減法的結(jié)果。

如果前面幾個字母一樣會根據(jù)兩個字符串的長度進行減法運算返回的就是這個減法的結(jié)果,如果連個字符串一摸一樣 返回的就是0。

9、new String("a") + new String("b") 會創(chuàng)建幾個對象?

對象1:new StringBuilder()

對象2:new String("a")

對象3:常量池中的"a"

對象4:new String("b")

對象5:常量池中的"b"

深入剖析:StringBuilder中的toString():

對象6:new String("ab")

強調(diào)一下,toString()的調(diào)用,在字符串常量池中,沒有生成"ab"

附加題

String s1 = new String("1") + new String("1");//s1變量記錄的地址為:new String
s1.intern();//在字符串常量池中生成"11"。如何理解:jdk6:創(chuàng)建了一個新的對象"11",也就有新的地址;jdk7:此時常量池中并沒有創(chuàng)建"11",而是創(chuàng)建了一個指向堆空間中new String("11")的地址;
String s2 = "11";
System.out.println(s1 == s2);//jdk6:false;jdk7:true

10、如何將字符串反轉(zhuǎn)?

添加到StringBuilder中,然后調(diào)用reverse()。

11、String 類的常用方法都有那些?

equals、length、contains、replace、split、hashcode、indexof、substring、trim、toUpperCase、toLowerCase、isEmpty等等。

12、普通類和抽象類有哪些區(qū)別?

抽象類不能被實例化;

抽象類可以有抽象方法,只需申明,無須實現(xiàn);

有抽象方法的類一定是抽象類;

抽象類的子類必須實現(xiàn)抽象類中的所有抽象方法,否則子類仍然是抽象類;

抽象方法不能聲明為靜態(tài)、不能被static、final修飾。

13、接口和抽象類有什么區(qū)別?

(1)接口

接口使用interface修飾;

接口不能實例化;

類可以實現(xiàn)多個接口;

①java8之前,接口中的方法都是抽象方法,省略了public abstract。

②java8之后;接口中可以定義靜態(tài)方法,靜態(tài)方法必須有方法體,普通方法沒有方法體,需要被實現(xiàn);

(2)抽象類

抽象類使用abstract修飾;

抽象類不能被實例化;

抽象類只能單繼承;

抽象類中可以包含抽象方法和非抽象方法,非抽象方法需要有方法體;

如果一個類繼承了抽象類,

①如果實現(xiàn)了所有的抽象方法,子類可以不是抽象類;

②如果沒有實現(xiàn)所有的抽象方法,子類仍然是抽象類。

14、java 中 IO 流分為幾種?

(1)按流劃分,可以分為輸入流和輸出流;

(2)按單位劃分,可以分為字節(jié)流和字符流;

字節(jié)流:inputStream、outputStream;

字符流:reader、writer;

15、BIO、NIO、AIO 有什么區(qū)別?

(1)同步阻塞BIO

一個連接一個線程。

JDK1.4之前,建立網(wǎng)絡(luò)連接的時候采用BIO模式,先在啟動服務(wù)端socket,然后啟動客戶端socket,對服務(wù)端通信,客戶端發(fā)送請求后,先判斷服務(wù)端是否有線程響應。

如果沒有則會一直等待或者遭到拒絕請求,如果有的話會等待請求結(jié)束后才繼續(xù)執(zhí)行。

(2)同步非阻塞NIO

NIO主要是想解決BIO的大并發(fā)問題,BIO是每一個請求分配一個線程,當請求過多時,每個線程占用一定的內(nèi)存空間,服務(wù)器癱瘓了。

JDK1.4開始支持NIO,適用于連接數(shù)目多且連接比較短的架構(gòu),比如聊天 服務(wù)器,并發(fā)局限于應用中。

一個請求一個線程。

(3)異步非阻塞AIO

一個有效請求一個線程。

JDK1.7開始支持AIO,適用于連接數(shù)目多且連接比較長的結(jié)構(gòu),比如相冊服務(wù)器,充分調(diào)用OS參與并發(fā)操作。

16、Files的常用方法都有哪些?

exist

createFile

createDirectory

write

read

copy

size

delete

move

17、什么是反射?

所謂反射,是java在運行時進行自我觀察的能力,通過class、constructor、field、method四個方法獲取一個類的各個組成部分。

在Java運行時環(huán)境中,對任意一個類,可以知道類有哪些屬性和方法。

這種動態(tài)獲取類的信息以及動態(tài)調(diào)用對象的方法的功能來自于反射機制。

18、什么是 java 序列化?什么情況下需要序列化?

序列化就是一種用來處理對象流的機制。

將對象的內(nèi)容流化,將流化后的對象傳輸于網(wǎng)絡(luò)之間。

序列化是通過實現(xiàn)serializable接口,該接口沒有需要實現(xiàn)的方法,implement Serializable只是為了標注該對象是可被序列化的,使用一個輸出流(FileOutputStream)來構(gòu)造一個ObjectOutputStream對象,接著使用ObjectOutputStream對象的writeObejct(Object object)方法就可以將參數(shù)的obj對象到磁盤,需要恢復的時候使用輸入流。

序列化是將對象轉(zhuǎn)換為容易傳輸?shù)母袷降倪^程。

例如,可以序列化一個對象,然后通過HTTP通過Internet在客戶端和服務(wù)器之間傳輸該對象。

在另一端,反序列化將從流中心構(gòu)造成對象。

一般程序在運行時,產(chǎn)生對象,這些對象隨著程序的停止而消失,但我們想將某些對象保存下來。

這時,我們就可以通過序列化將對象保存在磁盤,需要使用的時候通過反序列化獲取到。

對象序列化的最主要目的就是傳遞和保存對象,保存對象的完整性和可傳遞性。

譬如通過網(wǎng)絡(luò)傳輸或者把一個對象保存成本地一個文件的時候,需要使用序列化。

19、為什么要使用克?。咳绾螌崿F(xiàn)對象克???深拷貝和淺拷貝區(qū)別是什么?

(1)什么要使用克?。?/p>

想對一個對象進行復制,又想保留原有的對象進行接下來的操作,這個時候就需要克隆了。

(2)如何實現(xiàn)對象克隆?

實現(xiàn)Cloneable接口,重寫clone方法;

實現(xiàn)Serializable接口,通過對象的序列化和反序列化實現(xiàn)克隆,可以實現(xiàn)真正的深克隆。

BeanUtils,apache和Spring都提供了bean工具,只是這都是淺克隆。

(3)深拷貝和淺拷貝區(qū)別是什么?

淺拷貝:僅僅克隆基本類型變量,不克隆引用類型變量;

深克?。杭瓤寺』绢愋妥兞浚挚寺∫妙愋妥兞?;

(4)代碼實例

20、throw 和 throws 的區(qū)別?

(1)throw

作用在方法內(nèi),表示拋出具體異常,由方法體內(nèi)的語句處理;

一定拋出了異常;

(2)throws

作用在方法的聲明上,表示拋出異常,由調(diào)用者來進行異常處理;

可能出現(xiàn)異常,不一定會發(fā)生異常;

21、final、finally、finalize 有什么區(qū)別?

final可以修飾類,變量,方法,修飾的類不能被繼承,修飾的變量不能重新賦值,修飾的方法不能被重寫

finally用于拋異常,finally代碼塊內(nèi)語句無論是否發(fā)生異常,都會在執(zhí)行finally,常用于一些流的關(guān)閉。

finalize方法用于垃圾回收。

一般情況下不需要我們實現(xiàn)finalize,當對象被回收的時候需要釋放一些資源,比如socket鏈接,在對象初始化時創(chuàng)建,整個生命周期內(nèi)有效,那么需要實現(xiàn)finalize方法,關(guān)閉這個鏈接。

但是當調(diào)用finalize方法后,并不意味著gc會立即回收該對象,所以有可能真正調(diào)用的時候,對象又不需要回收了,然后到了真正要回收的時候。

因為之前調(diào)用過一次,這次又不會調(diào)用了,產(chǎn)生問題。

所以,不推薦使用finalize方法。

22、try-catch-finally 中,如果 catch 中 return 了,finally 還會執(zhí)行嗎?

23、常見的異常類有哪些?

  1. NullPointerException:空指針異常;
  2. SQLException:數(shù)據(jù)庫相關(guān)的異常;
  3. IndexOutOfBoundsException:數(shù)組下角標越界異常;
  4. FileNotFoundException:打開文件失敗時拋出;
  5. IOException:當發(fā)生某種IO異常時拋出;
  6. ClassCastException:當試圖將對象強制轉(zhuǎn)換為不是實例的子類時,拋出此異常;
  7. NoSuchMethodException:無法找到某一方法時,拋出;
  8. ArrayStoreException:試圖將錯誤類型的對象存儲到一個對象數(shù)組時拋出的異常;
  9. NumberFormatException:當試圖將字符串轉(zhuǎn)換成數(shù)字時,失敗了,拋出;
  10. IllegalArgumentException 拋出的異常表明向方法傳遞了一個不合法或不正確的參數(shù)。
  11. ArithmeticException當出現(xiàn)異常的運算條件時,拋出此異常。例如,一個整數(shù)“除以零”時,拋出此類的一個實例。 

24、hashcode是什么?有什么作用?

Java中Object有一個方法:

public native int hashcode();

(1)hashcode()方法的作用

hashcode()方法主要配合基于散列的集合一起使用,比如HashSet、HashMap、HashTable。

當集合需要添加新的對象時,先調(diào)用這個對象的hashcode()方法,得到對應的hashcode值,實際上hashmap中會有一個table保存已經(jīng)存進去的對象的hashcode值

如果table中沒有改hashcode值,則直接存入,如果有,就調(diào)用equals方法與新元素進行比較,相同就不存了,不同就存入。

(2)equals和hashcode的關(guān)系

如果equals為true,hashcode一定相等; 

如果equals為false,hashcode不一定不相等;

如果hashcode值相等,equals不一定相等;

如果hashcode值不等,equals一定不等;

(3)重寫equals方法時,一定要重寫hashcode方法

(4)百度百科

hashcode方法返回該對象的哈希碼值。支持該方法是為哈希表提供一些優(yōu)點,例如,java.util.Hashtable 提供的哈希表。 

hashCode 的常規(guī)協(xié)定是: 

在 Java 應用程序執(zhí)行期間,在同一對象上多次調(diào)用 hashCode 方法時,必須一致地返回相同的整數(shù),前提是對象上 equals 比較中所用的信息沒有被修改。從某一應用程序的一次執(zhí)行到同一應用程序的另

一次執(zhí)行,該整數(shù)無需保持一致。 

如果根據(jù) equals(Object) 方法,兩個對象是相等的,那么在兩個對象中的每個對象上調(diào)用 hashCode 方法都必須生成相同的整數(shù)結(jié)果。 

以下情況不 是必需的:如果根據(jù) equals(java.lang.Object) 方法,兩個對象不相等,那么在兩個對象中的任一對象上調(diào)用 hashCode 方法必定會生成不同的整數(shù)結(jié)果。

但是,程序員應該知道,為不相等的對象生成不同整數(shù)結(jié)果可以提高哈希表的性能。 

實際上,由 Object 類定義的 hashCode 方法確實會針對不同的對象返回不同的整數(shù)。(這一般是通過將該對象的內(nèi)部地址轉(zhuǎn)換成一個整數(shù)來實現(xiàn)的,但是 JavaTM 編程語言不需要這種實現(xiàn)技巧。) 

當equals方法被重寫時,通常有必要重寫 hashCode 方法,以維護 hashCode 方法的常規(guī)協(xié)定,該協(xié)定聲明相等對象必須具有相等的哈希碼。

(5)小白解釋

1.hashcode是用來查找的,如果你學過數(shù)據(jù)結(jié)構(gòu)就應該知道,在查找和排序這一章有

例如內(nèi)存中有這樣的位置

0  1  2  3  4  5  6  7  

而我有個類,這個類有個字段叫ID,我要把這個類存放在以上8個位置之一,如果不用hashcode而任意存放,那么當查找時就需要到這八個位置里挨個去找,或者用二分法一類的算法。

但如果用hashcode那就會使效率提高很多。

我們這個類中有個字段叫ID,那么我們就定義我們的hashcode為ID%8,然后把我們的類存放在取得得余數(shù)那個位置。比如我們的ID為9,9除8的余數(shù)為1,那么我們就把該類存在1這個位置

如果ID是13,求得的余數(shù)是5,那么我們就把該類放在5這個位置。這樣,以后在查找該類時就可以通過ID除 8求余數(shù)直接找到存放的位置了。

2.但是如果兩個類有相同的hashcode怎么辦那(我們假設(shè)上面的類的ID不是唯一的),例如9除以8和17除以8的余數(shù)都是1,那么這是不是合法的,回答是:可以這樣。那么如何判斷呢?在這個時候就需

要定義 equals了。

也就是說,我們先通過 hashcode來判斷兩個類是否存放某個桶里,但這個桶里可能有很多類,那么我們就需要再通過 equals 來在這個桶里找到我們要的類。

那么。重寫了equals(),為什么還要重寫hashCode()呢?

想想,你要在一個桶里找東西,你必須先要找到這個桶啊,你不通過重寫hashcode()來找到桶,光重寫equals()有什么用啊。

25、java 中操作字符串都有哪些類?它們之間有什么區(qū)別?

(1)String

String是不可變對象,每次對String類型的改變時都會生成一個新的對象。

(2)StringBuilder

線程不安全,效率高,多用于單線程。

(3)StringBuffer

線程安全,由于加鎖的原因,效率不如StringBuilder,多用于多線程。

不頻繁的字符串操作使用String,操作頻繁的情況不建議使用String。

StringBuilder > StringBuffer > String。

26、java 中都有哪些引用類型?

(1)強引用

Java中默認聲明的就是強引用,比如:

Object obj = new Object();
obj = null;

只要強引用存在,垃圾回收器將永遠不會回收被引用的對象。如果想被回收,可以將對象置為null; 

(2)軟引用(SoftReference)

在內(nèi)存足夠的時候,軟引用不會被回收,只有在內(nèi)存不足時,系統(tǒng)才會回收軟引用對象,如果回收了軟引用對象之后仍然沒有足夠的內(nèi)存,才會跑出內(nèi)存溢出異常。

byte[] buff = new byte[1024 * 1024];

SoftReference<byte[]> sr = new SoftReference<>(buff);

(3)弱引用(WeakReference)

進行垃圾回收時,弱引用就會被回收。

(4)虛引用(PhantomReference)

(5)引用隊列(ReferenceQueue)

引用隊列可以與軟引用、弱引用、虛引用一起配合使用。

當垃圾回收器準備回收一個對象時,如果發(fā)現(xiàn)它還有引用,就會在回收對象之前,把這個引用加入到引用隊列中。

程序可以通過判斷引用隊列中是否加入了引用,來判斷被引用的對象是否將要被垃圾回收,這樣可以在對象被回收之前采取一些必要的措施。

27、在 Java 中,為什么不允許從靜態(tài)方法中訪問非靜態(tài)變量?

  1. 靜態(tài)變量屬于類本身,在類加載的時候就會分配內(nèi)存,可以通過類名直接訪問;
  2. 非靜態(tài)變量屬于類的對象,只有在類的對象產(chǎn)生時,才會分配內(nèi)存,通過類的實例去訪問;
  3. 靜態(tài)方法也屬于類本身,但是此時沒有類的實例,內(nèi)存中沒有非靜態(tài)變量,所以無法調(diào)用。

28、說說Java Bean的命名規(guī)范

1、JavaBean 類必須是一個公共類,并將其訪問屬性設(shè)置為 public

2、JavaBean 類必須有一個空的構(gòu)造函數(shù):類中必須有一個不帶參數(shù)的公用構(gòu)造器,此構(gòu)造器也應該通過調(diào)用各個特性的設(shè)置方法來設(shè)置特性的缺省值。

3、一個javaBean類不應有公共實例變量,類變量都為private

4、持有值應該通過一組存取方法(getXxx 和 setXxx)來訪問:對于每個特性,應該有一個帶匹配公用 getter 和 setter 方法的專用實例變量。

屬性為布爾類型,可以使用 isXxx() 方法代替 getXxx() 方法。

通常屬性名是要和 包名、類名、方法名、字段名、常量名作出區(qū)別的:

首先:必須用英文,不要用漢語拼音

(1)包(package)

用于將完成不同功能的類分門別類,放在不同的目錄(包)下,包的命名規(guī)則:將公司域名反轉(zhuǎn)作為包名。

比如www.sohu.com 對于包名:每個字母都需要小寫。比如:com.sohu.test;該包下的Test類的全名是:com.sohu.Test.Java 。

如果定義類的時候沒有使用package,那么java就認為我們所定義的類位于默認包里面(default package)。

(2)類

首字母大寫,如果一個類由多個單詞構(gòu)成,那么每個單詞的首字母都大寫,而且中間不使用任何的連接符。盡量使用英文。如ConnectionFactory

(3)方法

首單詞全部小寫,如果一個方法由多個單詞構(gòu)成,那么從第二個單詞開始首字母大寫,不使用連接符。addPerson

(4)字段

與方法相同。如ageOfPerson

(5)常量

所有單詞的字母都是大寫,如果有多個單詞,那么使用下劃線鏈接即可。

如:public static final int AGE_OF_PERSON = 20; //通常加上static

29、Java Bean 屬性命名規(guī)范問題分析

public class User {
	private String busName;
	private String pCount;
	private Boolean isRunning;
	//正確的命名方式,駝峰式的
	public String getBusName() {
		return busName;
	}
	public void setBusName(String busName) {
		this.busName = busName;
	}
    //這是什么?
	public String getpCount() {
		return pCount;
	}
	public void setpCount(String pCount) {
		this.pCount = pCount;
	}
    //這個也是不允許的
	public Boolean getIsRunning() {
		return isRunning;
	}
	public void setIsRunning(Boolean isRunning) {
		this.isRunning = isRunning;
	}
}

1. javabean屬性命名盡量使用常規(guī)的駝峰式命名規(guī)則

2. 屬性名第一個單詞盡量避免使用一個字母:如eBook, eMail。

3. boolean屬性名避免使用 “is” 開頭的名稱

4. 隨著jdk, eclipse, spring 等軟件版本的不斷提高, 底版本的出現(xiàn)的問題可能在高版本中解決了, 低版本原來正常的代碼可能在高版本環(huán)境下不再支持。

30、什么是 Java 的內(nèi)存模型?

在了解什么是 Java 內(nèi)存模型之前,先了解一下為什么要提出 Java 內(nèi)存模型。

之前提到過并發(fā)編程有三大問題

CPU 緩存,在多核 CPU 的情況下,帶來了可見性問題

操作系統(tǒng)對當前執(zhí)行線程的切換,帶來了原子性問題

譯器指令重排優(yōu)化,帶來了有序性問題

為了解決并發(fā)編程的三大問題,提出了 JSR-133,新的 Java 內(nèi)存模型,JDK 5 開始使用。

簡單總結(jié)下

  • Java 內(nèi)存模型是 JVM 的一種規(guī)范
  • 定義了共享內(nèi)存在多線程程序中讀寫操作行為的規(guī)范
  • 屏蔽了各種硬件和操作系統(tǒng)的訪問差異,保證了 Java 程序在各種平臺下對內(nèi)存的訪問效果一致
  • 解決并發(fā)問題采用的方式:限制處理器優(yōu)化和使用內(nèi)存屏障
  • 增強了三個同步原語(synchronized、volatile、final)的內(nèi)存語義
  • 定義了 happens-before 規(guī)則

31、在 Java 中,什么時候用重載,什么時候用重寫?

(1)重載是多態(tài)的集中體現(xiàn),在類中,要以統(tǒng)一的方式處理不同類型數(shù)據(jù)的時候,可以用重載。

(2)重寫的使用是建立在繼承關(guān)系上的,子類在繼承父類的基礎(chǔ)上,增加新的功能,可以用重寫。

(3)簡單總結(jié):

  • 重載是多樣性,重寫是增強劑;
  • 目的是提高程序的多樣性和健壯性,以適配不同場景使用時,使用重載進行擴展;
  • 目的是在不修改原方法及源代碼的基礎(chǔ)上對方法進行擴展或增強時,使用重寫;

生活例子:

你想吃一碗面,我給你提供了拉面,炒面,刀削面,擔擔面供你選擇,這是重載;

你想吃一碗面,我不但給你端來了面,還給你加了青菜,加了雞蛋,這個是重寫;

設(shè)計模式:

cglib實現(xiàn)動態(tài)代理,核心原理用的就是方法的重寫;

詳細解答:

Java的重載(overload) 最重要的應用場景就是構(gòu)造器的重載,構(gòu)造器重載后,提供多種形參形式的構(gòu)造器,可以應對不同的業(yè)務(wù)需求,加強程序的健壯性和可擴展性

比如我們最近學習的Spring源碼中的ClassPathXmlApplicationContext,它的構(gòu)造函數(shù)使用重載一共提供了10個構(gòu)造函數(shù),這樣就為業(yè)務(wù)的選擇提供了多選擇性。

在應用到方法中時,主要是為了增強方法的健壯性和可擴展性,比如我們在開發(fā)中常用的各種工具類

比如我目前工作中的短信工具類SMSUtil, 發(fā)短信的方法就會使用重載,針對不同業(yè)務(wù)場景下的不同形參,提供短信發(fā)送方法,這樣提高了工具類的擴展性和健壯性。

總結(jié):重載必須要修改方法(構(gòu)造器)的形參列表,可以修改方法的返回值類型,也可以修改方法的異常信息即訪問權(quán)限;

使用范圍是在同一個類中,目的是對方法(構(gòu)造器)進行功能擴展,以應對多業(yè)務(wù)場景的不同使用需求。提高程序的健壯性和擴展性。

java的重寫(override) 只要用于子類對父類方法的擴展或修改,但是在我們開發(fā)中,為了避免程序混亂,重寫一般都是為了方法的擴展,比如在cglib方式實現(xiàn)的動態(tài)代理中,代理類就是繼承了目標類,對目標類的方法進行重寫,同時在方法前后進行切面織入。

  • 總結(jié):方法重寫時,參數(shù)列表,返回值得類型是一定不能修改的,異??梢詼p少或者刪除,但是不能拋出新的異?;蛘吒鼜V的異常,方法的訪問權(quán)限可以降低限制,但是不能做更嚴格的限制。

(4)在里氏替換原則中,子類對父類的方法盡量不要重寫和重載。(我們可以采用final的手段強制來遵循)

32、舉例說明什么情況下會更傾向于使用抽象類而不是接口?

接口和抽象類都遵循”面向接口而不是實現(xiàn)編碼”設(shè)計原則,它可以增加代碼的靈活性,可以適應不斷變化的需求。

下面有幾個點可以幫助你回答這個問題:在 Java 中,你只能繼承一個類,但可以實現(xiàn)多個接口。

所以一旦你繼承了一個類,你就失去了繼承其他類的機會了。

接口通常被用來表示附屬描述或行為如: Runnable 、 Clonable 、 Serializable 等等,因此當你使用抽象類來表示行為時,你的類就不能同時是 Runnable 和 Clonable

( 注:這里的意思是指如果把 Runnable 等實現(xiàn)為抽象類的情況 )

因為在 Java 中你不能繼承兩個類,但當你使用接口時,你的類就可以同時擁有多個不同的行為。

在一些對時間要求比較高的應用中,傾向于使用抽象類,它會比接口稍快一點。

如果希望把一系列行為都規(guī)范在類繼承層次內(nèi),并且可以更好地在同一個地方進行編碼,那么抽象類是一個更好的選擇。

有時,接口和抽象類可以一起使用,接口中定義函數(shù),而在抽象類中定義默認的實現(xiàn)。

33、實例化對象有哪幾種方式

  • new
  • clone()
  • 通過反射機制創(chuàng)建
//用 Class.forName方法獲取類,在調(diào)用類的newinstance()方法
Class<?> cls = Class.forName("com.dao.User");
User u = (User)cls.newInstance();
  • 序列化反序列化
//將一個對象實例化后,進行序列化,再反序列化,也可以獲得一個對象(遠程通信的場景下使用)
ObjectOutputStream out = new ObjectOutputStream (new FileOutputStream("D:/data.txt"));
//序列化對象
out.writeObject(user1); 
out.close();
//反序列化對象
ObjectInputStream in = new ObjectInputStream(new FileInputStream("D:/data.txt"));
User user2 = (User) in.readObject();
System.out.println("反序列化user:" + user2);
in.close();

34、byte類型127+1等于多少

byte的范圍是-128~127。

字節(jié)長度為8位,最左邊的是符號位,而127的二進制為01111111,所以執(zhí)行+1操作時,01111111變?yōu)?0000000。

大家知道,計算機中存儲負數(shù),存的是補碼的興衰。左邊第一位為符號位。

那么負數(shù)的補碼轉(zhuǎn)換成十進制如下:

一個數(shù)如果為正,則它的原碼、反碼、補碼相同;一個正數(shù)的補碼,將其轉(zhuǎn)化為十進制,可以直接轉(zhuǎn)換。

已知一個負數(shù)的補碼,將其轉(zhuǎn)換為十進制數(shù),步驟如下:

  1. 先對各位取反;
  2. 將其轉(zhuǎn)換為十進制數(shù);
  3. 加上負號,再減去1;

例如10000000,最高位是1,是負數(shù),①對各位取反得01111111,轉(zhuǎn)換為十進制就是127,加上負號得-127,再減去1得-128;

35、Java 容器都有哪些?

(1)Collection

① set

HashSet、TreeSet

② list

ArrayList、LinkedList、Vector

(2)Map

HashMap、HashTable、TreeMap

36、Collection 和 Collections 有什么區(qū)別?

(1)Collection是最基本的集合接口,Collection派生了兩個子接口list和set,分別定義了兩種不同的存儲方式。

(2)Collections是一個包裝類,它包含各種有關(guān)集合操作的靜態(tài)方法(對集合的搜索、排序、線程安全化等)。

此類不能實例化,就像一個工具類,服務(wù)于Collection框架。

37、list與Set區(qū)別

(1)List簡介

實際上有兩種List:

一種是基本的ArrayList,其優(yōu)點在于隨機訪問元素,另一種是LinkedList,它并不是為快速隨機訪問設(shè)計的,而是快速的插入或刪除。

ArrayList:由數(shù)組實現(xiàn)的List。允許對元素進行快速隨機訪問,但是向List中間插入與移除元素的速度很慢。

LinkedList :對順序訪問進行了優(yōu)化,向List中間插入與刪除的開銷并不大。隨機訪問則相對較慢。

還具有下列方 法:addFirst(), addLast(), getFirst(), getLast(), removeFirst() 和 removeLast(), 這些方法 (沒有在任何接口或基類中定義過)

使得LinkedList可以當作堆棧、隊列和雙向隊列使用。

(2)Set簡介

Set具有與Collection完全一樣的接口,因此沒有任何額外的功能。

實際上Set就是Collection,只是行為不同。

這是繼承與多態(tài)思想的典型應用:表現(xiàn)不同的行為。

Set不保存重復的元素(至于如何判斷元素相同則較為負責) 

Set : 存入Set的每個元素都必須是唯一的,因為Set不保存重復元素。

加入Set的元素必須定義equals()方法以確保對象的唯一性。

Set與Collection有完全一樣的接口。Set接口不保證維護元素的次序。 

HashSet:為快速查找設(shè)計的Set。存入HashSet的對象必須定義hashCode()。 

TreeSet: 保存次序的Set, 底層為樹結(jié)構(gòu)。使用它可以從Set中提取有序的序列。 

(3)list與Set區(qū)別

① List,Set都是繼承自Collection接口

② List特點:元素有放入順序,元素可重復 

Set特點:元素無放入順序,元素不可重復,重復元素會覆蓋掉,(元素雖然無放入順序,但是元素在set中的位置是有該元素的HashCode決定的,其位置其實是固定的,加入Set 的Object必須定義equals()方法 ,另外list支持for循環(huán),也就是通過下標來遍歷,也可以用迭代器,但是set只能用迭代,因為他無序,無法用下標來取得想要的值。) 

③ Set和List對比: 

Set:檢索元素效率低下,刪除和插入效率高,插入和刪除不會引起元素位置改變。 

List:和數(shù)組類似,List可以動態(tài)增長,查找元素效率高,插入刪除元素效率低,因為會引起其他元素位置改變。

38、HashMap 和 Hashtable 有什么區(qū)別?

  1. HashMap是線程不安全的,HashTable是線程安全的;
  2. HashMap中允許鍵和值為null,HashTable不允許;
  3. HashMap的默認容器是16,為2倍擴容,HashTable默認是11,為2倍+1擴容;

39、說一下 HashMap 的實現(xiàn)原理?

(1)簡介

HashMap基于map接口,元素以鍵值對方式存儲,允許有null值,HashMap是線程不安全的。

(2)基本屬性

初始化大小,默認16,2倍擴容;

負載因子0.75;

初始化的默認數(shù)組;

size

threshold。判斷是否需要調(diào)整hashmap容量

(3)HashMap的存儲結(jié)構(gòu)

JDK1.7中采用數(shù)組+鏈表的存儲形式。

HashMap采取Entry數(shù)組來存儲key-value,每一個鍵值對組成了一個Entry實體,Entry類時機上是一個單向的鏈表結(jié)構(gòu)

它具有next指針,指向下一個Entry實體,以此來解決Hash沖突的問題。

HashMap實現(xiàn)一個內(nèi)部類Entry,重要的屬性有hash、key、value、next。

JDK1.8中采用數(shù)據(jù)+鏈表+紅黑樹的存儲形式。當鏈表長度超過閾值(8)時,將鏈表轉(zhuǎn)換為紅黑樹。在性能上進一步得到提升。

40、set有哪些實現(xiàn)類?

(1)HashSet

HashSet是set接口的實現(xiàn)類,set下面最主要的實現(xiàn)類就是HashSet(也就是用的最多的),此外還有LinkedHashSet和TreeSet。

HashSet是無序的、不可重復的。通過對象的hashCode和equals方法保證對象的唯一性。

HashSet內(nèi)部的存儲結(jié)構(gòu)是哈希表,是線程不安全的。

(2)TreeSet

TreeSet對元素進行排序的方式:

元素自身具備比較功能,需要實現(xiàn)Comparable接口,并覆蓋compareTo方法。

元素自身不具備比較功能,需要實現(xiàn)Comparator接口,并覆蓋compare方法。

(3)LinkedHashSet

LinkedHashSet是一種有序的Set集合,即其元素的存入和輸出的順序是相同的。

41、說一下 HashSet 的實現(xiàn)原理?

HashSet實際上是一個HashMap實例,數(shù)據(jù)存儲結(jié)構(gòu)都是數(shù)組+鏈表。

HashSet是基于HashMap實現(xiàn)的,HashSet中的元素都存放在HashMap的key上面,而value都是一個統(tǒng)一的對象PRESENT。

private static final Object PRESENT = new Object();

HashSet中add方法調(diào)用的是底層HashMap中的put方法,put方法要判斷插入值是否存在

而HashSet的add方法,首先判斷元素是否存在,如果存在則插入,如果不存在則不插入,這樣就保證了HashSet中不存在重復值。

 通過對象的hashCode和equals方法保證對象的唯一性。

42、ArrayList 和 LinkedList 的區(qū)別是什么?

ArrayList是動態(tài)數(shù)組的數(shù)據(jù)結(jié)構(gòu)實現(xiàn),查找和遍歷的效率較高;

LinkedList 是雙向鏈表的數(shù)據(jù)結(jié)構(gòu),增加和刪除的效率較高;

43、如何實現(xiàn)數(shù)組和 List 之間的轉(zhuǎn)換?

String[] arr = {"zs","ls","ww"};
List<String> list = Arrays.asList(arr);
System.out.println(list);
 
ArrayList<String> list1 = new ArrayList<String>();
list1.add("張三");
list1.add("李四");
list1.add("王五");
String[] arr1 = list1.toArray(new String[list1.size()]);
System.out.println(arr1);
for(int i = 0; i < arr1.length; i++){
    System.out.println(arr1[i]);
}

44、在 Queue 中 poll()和 remove()有什么區(qū)別?

(1)offer()和add()區(qū)別:

增加新項時,如果隊列滿了,add會拋出異常,offer返回false。

(2)poll()和remove()區(qū)別:

poll()和remove()都是從隊列中刪除第一個元素,remove拋出異常,poll返回null。

(3)peek()和element()區(qū)別:

peek()和element()用于查詢隊列頭部元素,為空時element拋出異常,peek返回null。

45、哪些集合類是線程安全的

Vector:就比Arraylist多了個同步化機制(線程安全)。

Stack:棧,也是線程安全的,繼承于Vector。

Hashtable:就比Hashmap多了個線程安全。

ConcurrentHashMap:是一種高效但是線程安全的集合。

46、迭代器 Iterator 是什么?

為了方便的處理集合中的元素,Java中出現(xiàn)了一個對象,該對象提供了一些方法專門處理集合中的元素.例如刪除和獲取集合中的元素.該對象就叫做迭代器(Iterator)。

47、Iterator 怎么使用?有什么特點?

Iterator 接口源碼中的方法:

  1. java.lang.Iterable 接口被 java.util.Collection 接口繼承,java.util.Collection 接口的 iterator() 方法返回一個 Iterator 對象
  2. next() 方法獲得集合中的下一個元素
  3. hasNext() 檢查集合中是否還有元素
  4. remove() 方法將迭代器新返回的元素刪除

48、Iterator 和 ListIterator 有什么區(qū)別?

(1)ListIterator 繼承 Iterator

(2)ListIterator 比 Iterator多方法

  1. add(E e)  將指定的元素插入列表,插入位置為迭代器當前位置之前
  2. set(E e)  迭代器返回的最后一個元素替換參數(shù)
  3. ehasPrevious()  迭代器當前位置,反向遍歷集合是否含有元素
  4. previous()  迭代器當前位置,反向遍歷集合,下一個元素
  5. previousIndex()  迭代器當前位置,反向遍歷集合,返回下一個元素的下標
  6. nextIndex()  迭代器當前位置,返回下一個元素的下標

(3)使用范圍不同,Iterator可以迭代所有集合;ListIterator 只能用于List及其子類

  1. ListIterator 有 add 方法,可以向 List 中添加對象;
  2. Iterator 不能ListIterator 有 hasPrevious() 和 previous() 方法,可以實現(xiàn)逆向遍歷;
  3. Iterator不可以ListIterator 有 nextIndex() 和previousIndex() 方法,可定位當前索引的位置;
  4. Iterator不可以ListIterator 有 set()方法,可以實現(xiàn)對 List 的修改;Iterator 僅能遍歷,不能修改。

49、怎么確保一個集合不能被修改?

我們很容易想到用final關(guān)鍵字進行修飾,我們都知道

final關(guān)鍵字可以修飾類,方法,成員變量,final修飾的類不能被繼承,final修飾的方法不能被重寫,final修飾的成員變量必須初始化值

如果這個成員變量是基本數(shù)據(jù)類型,表示這個變量的值是不可改變的

如果說這個成員變量是引用類型,則表示這個引用的地址值是不能改變的,但是這個引用所指向的對象里面的內(nèi)容還是可以改變的。

那么,我們怎么確保一個集合不能被修改?首先我們要清楚,集合(map,set,list…)都是引用類型,所以我們?nèi)绻胒inal修飾的話,集合里面的內(nèi)容還是可以修改的。

我們可以做一個實驗:

可以看到:我們用final關(guān)鍵字定義了一個map集合,這時候我們往集合里面?zhèn)髦?,第一個鍵值對1,1;我們再修改后,可以把鍵為1的值改為100,說明我們是可以修改map集合的值的。

那我們應該怎么做才能確保集合不被修改呢?
我們可以采用Collections包下的unmodifiableMap方法,通過這個方法返回的map,是不可以修改的。

他會報 java.lang.UnsupportedOperationException錯。

同理:Collections包也提供了對list和set集合的方法。

Collections.unmodifiableList(List)

Collections.unmodifiableSet(Set)

50、隊列和棧是什么?有什么區(qū)別?

(1)隊列先進先出,棧先進后出。

(2)遍歷數(shù)據(jù)速度不同。

棧只能從頭部取數(shù)據(jù) 也就最先放入的需要遍歷整個棧最后才能取出來,而且在遍歷數(shù)據(jù)的時候還得為數(shù)據(jù)開辟臨時空間,保持數(shù)據(jù)在遍歷前的一致性;

隊列則不同,他基于地址指針進行遍歷,而且可以從頭或尾部開始遍歷,但不能同時遍歷,無需開辟臨時空間,因為在遍歷的過程中不影像數(shù)據(jù)結(jié)構(gòu),速度要快的多。

51、Java8開始ConcurrentHashMap,為什么舍棄分段鎖?

ConcurrentHashMap的原理是引用了內(nèi)部的 Segment ( ReentrantLock )  分段鎖,保證在操作不同段 map 的時候, 可以并發(fā)執(zhí)行, 操作同段 map 的時候,進行鎖的競爭和等待。

從而達到線程安全, 且效率大于 synchronized。

但是在 Java 8 之后, JDK 卻棄用了這個策略,重新使用了 synchronized+CAS。

棄用原因

通過  JDK 的源碼和官方文檔看來, 他們認為的棄用分段鎖的原因由以下幾點:

加入多個分段鎖浪費內(nèi)存空間。

生產(chǎn)環(huán)境中, map 在放入時競爭同一個鎖的概率非常小,分段鎖反而會造成更新等操作的長時間等待。

為了提高 GC 的效率

新的同步方案

既然棄用了分段鎖, 那么一定由新的線程安全方案, 我們來看看源碼是怎么解決線程安全的呢?(源碼保留了segment 代碼, 但并沒有使用)。

52、ConcurrentHashMap(JDK1.8)為什么要使用synchronized而不是如ReentranLock這樣的可重入鎖?

我想從下面幾個角度討論這個問題:

(1)鎖的粒度

首先鎖的粒度并沒有變粗,甚至變得更細了。每當擴容一次,ConcurrentHashMap的并發(fā)度就擴大一倍。

(2)Hash沖突

JDK1.7中,ConcurrentHashMap從過二次hash的方式(Segment -> HashEntry)能夠快速的找到查找的元素。

在1.8中通過鏈表加紅黑樹的形式彌補了put、get時的性能差距。

JDK1.8中,在ConcurrentHashmap進行擴容時,其他線程可以通過檢測數(shù)組中的節(jié)點決定是否對這條鏈表(紅黑樹)進行擴容,減小了擴容的粒度,提高了擴容的效率。

下面是我對面試中的那個問題的一下看法。

為什么是synchronized,而不是ReentranLock

(1)減少內(nèi)存開銷

假設(shè)使用可重入鎖來獲得同步支持,那么每個節(jié)點都需要通過繼承AQS來獲得同步支持。

但并不是每個節(jié)點都需要獲得同步支持的,只有鏈表的頭節(jié)點(紅黑樹的根節(jié)點)需要同步,這無疑帶來了巨大內(nèi)存浪費。

(2)獲得JVM的支持

可重入鎖畢竟是API這個級別的,后續(xù)的性能優(yōu)化空間很小。

synchronized則是JVM直接支持的,JVM能夠在運行時作出相應的優(yōu)化措施:鎖粗化、鎖消除、鎖自旋等等。

這就使得synchronized能夠隨著JDK版本的升級而不改動代碼的前提下獲得性能上的提升。

到此這篇關(guān)于Java經(jīng)典面試題最全匯總208道(一)的文章就介紹到這了,更多相關(guān)Java面試題內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 解決OkHttp接收gzip壓縮數(shù)據(jù)返回亂碼問題

    解決OkHttp接收gzip壓縮數(shù)據(jù)返回亂碼問題

    這篇文章主要介紹了解決OkHttp接收gzip壓縮數(shù)據(jù)返回亂碼問題,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-06-06
  • 教你怎么用Java獲取國家法定節(jié)假日

    教你怎么用Java獲取國家法定節(jié)假日

    這篇文章主要介紹了教你怎么用Java獲取國家法定節(jié)假日,文中有非常詳細的代碼示例,對正在學習java的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-04-04
  • SpringBoot實現(xiàn)熱部署Community的示例代碼

    SpringBoot實現(xiàn)熱部署Community的示例代碼

    本文主要介紹了SpringBoot實現(xiàn)熱部署Community的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-06-06
  • 使用MUI框架構(gòu)建App請求http接口實例代碼

    使用MUI框架構(gòu)建App請求http接口實例代碼

    下面小編就為大家分享一篇使用MUI框架構(gòu)建App請求http接口實例代碼,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-01-01
  • java如何消除太多的if else判斷示例代碼

    java如何消除太多的if else判斷示例代碼

    這篇文章主要介紹了java如何消除太多的if else判斷,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-05-05
  • 詳解Java如何跨平臺獲取MAC地址

    詳解Java如何跨平臺獲取MAC地址

    有時我們因為軟件授權(quán)或者其它需要獲取主機唯一標識而需要獲取用戶主機的MAC地址,而本文則將介紹如何通過Java來實現(xiàn)跨平臺獲取MAC地址的兩種方法,需要的朋友可以參考下
    2021-06-06
  • CentOS 7快速安裝jdk

    CentOS 7快速安裝jdk

    這篇文章主要為大家詳細介紹了CentOS 7快速安裝jdk的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-07-07
  • 詳解SpringCloud是如何動態(tài)更新配置的

    詳解SpringCloud是如何動態(tài)更新配置的

    spring cloud在config配置管理的基礎(chǔ)上,提供了consul config的配置管理和動態(tài)監(jiān)聽,那么這里面到底是怎樣實現(xiàn)的,本文將為你揭秘,感興趣的小伙伴可以跟著小伙伴一起來學習
    2023-06-06
  • 如何使用SpringBootCondition更自由地定義條件化配置

    如何使用SpringBootCondition更自由地定義條件化配置

    這篇文章主要介紹了如何使用SpringBootCondition更自由地定義條件化配置,幫助大家更好的理解和學習使用springboot框架,感興趣的朋友可以了解下
    2021-04-04
  • spring-boot報錯java: 程序包javax.servlet.http不存在

    spring-boot報錯java: 程序包javax.servlet.http不存在

    當springboot項目從2.7.x的升級到3.0.x的時候,會遇到一個問題java: 程序包javax.servlet.http不存在,下面就來具體介紹一下,感興趣的可以了解一下
    2024-08-08

最新評論