Java基礎知識面試題(2021年最新版,持續更新...)整理

Java面試總結(2021優化版)已發佈在個人微信公衆號【Java精選】,根據讀者的反饋優化了部分答案存在的錯誤,同時根據最新面試總結和讀者的建議,刪除了低頻問題,並添加了一些常見面試題,對文章進行了精簡優化,目前約2000+道面試題,歡迎大家關注,關注後回覆Java面試,即可獲取最新面試資料!😊😊
在這裏插入圖片描述

【Java精選】專注程序員推送一些Java開發知識,包括基礎知識、各大流行框架、大數據技術、數據庫、面試題、面試經驗、職業規劃以及優質開源項目等。其中一部分由小編總結整理,另一部分來源於網絡上優質資源,希望對大家的學習和工作有所幫助。

本篇面試題資料只是部分面試資料,由於內容龐大、比較多,採用持續更新方式,這樣界面也比較美觀、方便閱讀,歡迎大家收藏,或者公衆號【Java精選】查看。

Java基礎

題1:面向對象編程有哪些特徵?

一、抽象和封裝

類和對象體現了抽象和封裝

抽象就是解釋類與對象之間關係的詞。類與對象之間的關係就是抽象的關係。一句話來說明:類是對象的抽象,而對象則是類得特例,即類的具體表現形式。

封裝兩個方面的含義:一是將有關數據和操作代碼封裝在對象當中,形成一個基本單位,各個對象之間相對獨立互不干擾。二是將對象中某些屬性和操作私有化,已達到數據和操作信息隱蔽,有利於數據安全,防止無關人員修改。把一部分或全部屬性和部分功能(函數)對外界屏蔽,就是從外界(類的大括號之外)看不到,不可知,這就是封裝的意義。

二、繼承

面向對象的繼承是爲了軟件重用,簡單理解就是代碼複用,把重複使用的代碼精簡掉的一種手段。如何精簡,當一個類中已經有了相應的屬性和操作的代碼,而另一個類當中也需要寫重複的代碼,那麼就用繼承方法,把前面的類當成父類,後面的類當成子類,子類繼承父類,理所當然。就用一個關鍵字extends就完成了代碼的複用。

三、多態

沒有繼承就沒有多態,繼承是多態的前提。雖然繼承自同一父類,但是相應的操作卻各不相同,這叫多態。由繼承而產生的不同的派生類,其對象對同一消息會做出不同的響應。

題2:JDK、JRE、JVM 之間有什麼關係?

1、JDK

JDK(Java development Toolkit),JDK是整個Java的核心,包括了Java的運行環境(Java Runtime Environment),一堆的Java工具(Javac,java,jdb等)和Java基礎的類庫(即Java API 包括rt.jar).

Java API 是Java的應用程序的接口,裏面有很多寫好的Java class,包括一些重要的結構語言以及基本圖形,網絡和文件I/O等等。

2、JRE

JRE(Java Runtime Environment),Java運行環境。在Java平臺下,所有的Java程序都需要在JRE下才能運行。只有JVM還不能進行class的執行,因爲解釋class的時候,JVM需調用解釋所需要的類庫lib。JRE裏面有兩個文件夾bin和lib,這裏可以認爲bin就是JVM,lib就是JVM所需要的類庫,而JVM和lib合起來就稱JRE。

JRE包括JVM和JAVA核心類庫與支持文件。與JDK不同,它不包含開發工具-----編譯器,調試器,和其他工具。

3、JVM

JVM:Java Virtual Machine(Java 虛擬機)JVM是JRE的一部分,它是虛擬出來的計算機,是通過在實際的計算機上仿真模擬各種計算機功能來實現的。JVM有自己完善的硬件構架,入處理器,堆棧,寄存器等,還有相應的指令系統。

JVM是Java實現跨平臺最核心的部分,所有的Java程序會首先被編譯爲class的類文件,JVM的主要工作是解釋自己的指令集(即字節碼)並映射到本地的CPU的指令集或OS的系統調用。Java面對不同操作系統使用不同的虛擬機,一次實現了跨平臺。JVM對上層的Java源文件是不關心的,它關心的只是由源文件生成的類文件

編譯和運行Java文件,需瞭解兩個命令:

1)javac命令:編譯java文件;使用方法: javac Hello.java ,如果不出錯的話,在與Hello.java 同一目錄下會生成一個Hello.class文件,這個class文件是操作系統能夠使用和運行的文件。

2)java命令: 作用:運行.class文件;使用方法:java Hello,如果不出錯的話,會執行Hello.class文件。注意:這裏的Hello後面不需要擴展名。

題3:如何使用命令行編譯和運行 Java 文件?

新建文件,編寫代碼如下:

public class Hello{
	public static void main(String[] args){
		System.out.println("Hello world,歡迎關注微信公衆號“Java精選”!");
	}
}

文件命名爲Hello.java,注意後綴爲“java”。

打開cmd,切換至當前文件所在位置,執行javac Hello.java,該文件夾下面生成了一個Hello.class文件

輸入java Hello命令,cmd控制檯打印出代碼的內容Hello world,歡迎關注微信公衆號“Java精選”!

題4:Java 中的關鍵字都有哪些?

1)48個關鍵字:abstract、assert、boolean、break、byte、case、catch、char、class、continue、default、do、double、else、enum、extends、final、finally、float、for、if、implements、import、int、interface、instanceof、long、native、new、package、private、protected、public、return、short、static、strictfp、super、switch、synchronized、this、throw、throws、transient、try、void、volatile、while。

2)2個保留字(目前未使用,以後可能用作爲關鍵字):goto、const。

3)3個特殊直接量(直接量是指在程序中通過源代碼直接給出的值):true、false、null。

題5:Java 中基本類型都有哪些?

Java的類型分成兩種,一種是基本類型,一種是引用類型。其中Java基本類型共有八種。

基本類型可以分爲三大類:字符類型char,布爾類型boolean以及數值類型byte、short、int、long、float、double。

數值類型可以分爲整數類型byte、short、int、long和浮點數類型float、double。

JAVA中的數值類型不存在無符號的,它們的取值範圍是固定的,不會隨着機器硬件環境或操作系統的改變而改變。實際上《Thinking in Java》一書作者,提到Java中還存在另外一種基本類型void,它也有對應的包裝類 java.lang.Void,因爲Void是不能new,也就是不能在堆裏面分配空間存對應的值,所以將Void歸成基本類型,也有一定的道理。

8種基本類型表示範圍如下:

byte:8位,最大存儲數據量是255,存放的數據範圍是-128~127之間。

short:16位,最大數據存儲量是65536,數據範圍是-32768~32767之間。

int:32位,最大數據存儲容量是2的32次方減1,數據範圍是負的2的31次方到正的2的31次方減1。

long:64位,最大數據存儲容量是2的64次方減1,數據範圍爲負的2的63次方到正的2的63次方減1。

float:32位,數據範圍在3.4e-45~1.4e38,直接賦值時必須在數字後加上f或F。

double:64位,數據範圍在4.9e-324~1.8e308,賦值時可以加d或D也可以不加。

boolean:只有true和false兩個取值。

char:16位,存儲Unicode碼,用單引號賦值。

題6:main 方法中 args 參數是什麼含義?

java中args即爲arguments的縮寫,是指字符串變量名,屬於引用變量,屬於命名,可以自定義名稱也可以採用默認值,一般習慣性照寫。

String[] args是main函數的形式參數,可以用來獲取命令行用戶輸入進去的參數。

1)字符串變量名(args)屬於引用變量,屬於命名,可以自定義名稱。

2)可以理解成用於存放字符串數組,若去掉無法知曉"args"聲明的變量是什麼類型。

3)假設public static void main方法,代表當啓動程序時會啓動這部分;

4)String[] args是main函數的形式參數,可以用來獲取命令行用戶輸入進去的參數。

5)java本身不存在不帶String args[]的main函數,java程序中去掉String args[]會出現錯誤。

​題7:final 關鍵字的基本用法?

在Java中final關鍵字可以用來修飾類、方法和變量(包括成員變量和局部變量)。下面從這三個方面來了解一下final關鍵字的基本用法。

1、修飾類

當用final修飾一個類時,表明這個類不能被繼承。也就是說,如果一個類你永遠不會讓他被繼承,就可以用final進行修飾。final類中的成員變量可以根據需要設爲final,但是要注意final類中的所有成員方法都會被隱式地指定爲final方法。

在使用final修飾類的時候,要注意謹慎選擇,除非這個類真的在以後不會用來繼承或者出於安全的考慮,儘量不要將類設計爲final類。

2、修飾方法

下面這段話摘自《Java編程思想》第四版第143頁:

“使用final方法的原因有兩個。第一個原因是把方法鎖定,以防任何繼承類修改它的含義;第二個原因是效率。在早期的Java實現版本中,會將final方法轉爲內嵌調用。但是如果方法過於龐大,可能看不到內嵌調用帶來的任何性能提升。在最近的Java版本中,不需要使用final方法進行這些優化了。“

因此,如果只有在想明確禁止該方法在子類中被覆蓋的情況下才將方法設置爲final的。即父類的final方法是不能被子類所覆蓋的,也就是說子類是不能夠存在和父類一模一樣的方法的。

final修飾的方法表示此方法已經是“最後的、最終的”含義,亦即此方法不能被重寫(可以重載多個final修飾的方法)。此處需要注意的一點是:因爲重寫的前提是子類可以從父類中繼承此方法,如果父類中final修飾的方法同時訪問控制權限爲private,將會導致子類中不能直接繼承到此方法,因此,此時可以在子類中定義相同的方法名和參數,此時不再產生重寫與final的矛盾,而是在子類中重新定義了新的方法。(注:類的private方法會隱式地被指定爲final方法。)

3、修飾變量

final成員變量表示常量,只能被賦值一次,賦值後值不再改變。
當final修飾一個基本數據類型時,表示該基本數據類型的值一旦在初始化後便不能發生變化;如果final修飾一個引用類型時,則在對其初始化之後便不能再讓其指向其他對象了,但該引用所指向的對象的內容是可以發生變化的。本質上是一回事,因爲引用的值是一個地址,final要求值,即地址的值不發生變化。

final修飾一個成員變量(屬性),必須要顯示初始化。這裏有兩種初始化方式,一種是在變量聲明的時候初始化;第二種方法是在聲明變量的時候不賦初值,但是要在這個變量所在的類的所有的構造函數中對這個變量賦初值。

當函數的參數類型聲明爲final時,說明該參數是隻讀型的。即你可以讀取使用該參數,但是無法改變該參數的值。

題8:如何理解 final 關鍵字?

1)類的final變量和普通變量有什麼區別?

當用final作用於類的成員變量時,成員變量(注意是類的成員變量,局部變量只需要保證在使用之前被初始化賦值即可)必須在定義時或者構造器中進行初始化賦值,而且final變量一旦被初始化賦值之後,就不能再被賦值了。

2)被final修飾的引用變量指向的對象內容可變嗎?

引用變量被final修飾之後,雖然不能再指向其他對象,但是它指向的對象的內容是可變的

3)final參數的問題

在實際應用中,我們除了可以用final修飾成員變量、成員方法、類,還可以修飾參數、若某個參數被final修飾了,則代表了該參數是不可改變的。如果在方法中我們修改了該參數,則編譯器會提示你:

The final local variable i cannot be assigned. It must be blank and not using a compound assignment。

java採用的是值傳遞,對於引用變量,傳遞的是引用的值,也就是說讓實參和形參同時指向了同一個對象,因此讓形參重新指向另一個對象對實參並沒有任何影響。

題9:爲什麼 String 類型是被 final 修飾的?

1、爲了實現字符串池

final修飾符的作用:final可以修飾類,方法和變量,並且被修飾的類或方法,被final修飾的類不能被繼承,即它不能擁有自己的子類,被final修飾的方法不能被重寫, final修飾的變量,無論是類屬性、對象屬性、形參還是局部變量,都需要進行初始化操作。

String爲什麼要被final修飾主要是爲了”安全性“和”效率“的原因。

final修飾的String類型,代表了String不可被繼承,final修飾的char[]代表了被存儲的數據不可更改性。雖然final修飾的不可變,但僅僅是引用地址不可變,並不代表了數組本身不會改變。

爲什麼保證String不可變呢?

因爲只有字符串是不可變,字符串池纔有可能實現。字符串池的實現可以在運行時節約很多heap空間,不同的字符串變量都指向池中的同一個字符串。但如果字符串是可變的,那麼String interning將不能實現,反之變量改變它的值,那麼其它指向這個值的變量值也會隨之改變。

如果字符串是可變,會引起很嚴重的安全問題。如數據庫的用戶名、密碼都是以字符串的形式傳入來獲得數據庫的連接或在socket編程中,主機名和端口都是以字符串的形式傳入。因爲字符串是不可變的,所以它的值是不可改變的,否則改變字符串指向的對象值,將造成安全漏洞。

2、爲了線程安全

因爲字符串是不可變的,所以是多線程安全的,同一個字符串實例可以被多個線程共享。這樣便不用因爲線程安全問題而使用同步。字符串自己便是線程安全的。

3、爲了實現String可創建HashCode不可變性

因爲字符串是不可變的,所以在它創建的時候HashCode就被緩存了,不需要重新計算。使得字符串很適合作爲Map鍵值對中的鍵,字符串的處理速度要快過其它的鍵對象。這就是HashMap中的鍵往往都使用字符串。

題10:接口(interface)和抽象類(abstract class)有什麼區別?

默認方法

抽象類可以有默認的方法實現;而接口類在JDK1.8之前版本,不存在方法的實現。

實現方式

抽象類子類使用extends關鍵字來繼承抽象類,如果子類不是抽象類,子類需要提供抽象類中所聲明方法的實現;而接口類子類使用implements來實現接口,需要提供接口中所有聲明的實現。

構造器

抽象類中可以有構造器;而接口中不能有構造器。

和正常類區別

抽象類不能被實例化;而接口是完全不同的類型。

訪問修飾符

抽象類中抽象方法可以有public、protected、default等修飾;而接口類默認是public,不能使用其他修飾符。

多繼承

抽象類一個子類只能存在一個父類;而接口類一個子類可以存在多個接口。

添加新方法

抽象類中添加新方法,可以提供默認的實現,因此可以不修改子類現有的代碼;而接口類中添加新方法,則子類中需要實現該方法。

題11:面向過程與面向對象有什麼區別?

面向過程

性能相比面向對象高,因其類調用時需要實例化,開銷比較大,消耗資源,比如單片機、嵌入式開發、Linux/Unix等一般採用面向過程開發,性能是最重要的因素。

面向對象

易維護、複用以及擴展,由於面向對象有封裝、繼承、多態性等特徵,可以設計出低耦合、高內聚的系統,使得更加靈活,易於維護。

題12:Java 編程語言有哪些特點?

1)簡單易學;

2)面向對象(封裝,繼承,多態);

3)平臺無關性(Java虛擬機實現平臺無關性);

4)可靠性;

5)安全性;

6)支持多線程;

7)支持網絡編程並方便易用;

8)編譯與解釋並存。

題13:重載和重寫有什麼區別?

重載(Overload) 是指讓類以統一的方式處理不同類型數據的一種手段,實質表現就是多個具有不同的參數個數或者不同類型的同名函數,存在於同一個類中,返回值類型不同,是一個類中多態性的一種表現。

調用方法時通過傳遞不同參數個數和參數類型來決定具體使用哪個方法的多態性。

重寫(Override) 是指父類與子類之間的多態性,實質就是對父類的函數進行重新定義。

如果子類中定義某方法與其父類有相同的名稱和參數則該方法被重寫,需注意的是子類函數的訪問修飾權限不能低於父類的。

如果子類中的方法與父類中的某一方法具有相同的方法名、返回類型和參數表,則新方法將覆蓋原有的方法,如需父類中原有的方法則可使用super關鍵字。

題14:靜態方法和實例方法有什麼不同?

靜態方法和實例方法的區別主要體現在兩個方面:

其一在外部調用靜態方法時,可以使用"類名.方法名"的方式,也可以使用"對象名.方法名"的方式而實例方法只能試用後面這種方式。也就是說,調用靜態方法可以無需創建對象進行實例化。

其二靜態方法在訪問本類的成員時,只允許訪問靜態成員也就是靜態成員變量和靜態方法,而不允許訪問實例成員變量和實例方法,實例方法是沒有這個限制的。

題15:== 和 equals 兩者有什麼區別?

使用==比較

用於對比基本數據類型的變量,是直接比較存儲的 “值”是否相等;

用於對比引用類型的變量,是比較的所指向的對象地址。

使用equals比較

equals方法不能用於對比基本數據類型的變量;

如果沒對Object中equals方法進行重寫,則是比較的引用類型變量所指向的對象地址,反之則比較的是內容。

題16:Integer 和 int 兩者有什麼區別?

Integer是int的包裝類,默認值是null;int是基本數據類型,默認值是0;

Integer變量必須實例化後才能使用;int變量不需要;

Integer實際是對象的引用,指向此new的Integer對象;int是直接存儲數據值。

分析總結

1)Integer與new Integer不相等。new出來的對象被存放在堆,而非new的Integer常量則在常量池,兩者內存地址不同,因此判斷是false。

2)兩個值都是非new Integer,如果值在-128,127區間,則是true,反之爲false。

這是因爲java在編譯Integer i2 = 128時,被翻譯成:

Integer i2 = Integer.valueOf(128);

而valueOf()函數會對-128到127之間的數進行緩存。

3)兩個都是new Integer,兩者判斷爲false,內存地址不同。

4)int和Integer對比不管是否new對象,兩者判斷都是true,因爲會把Integer自動拆箱爲int再去比。

題17:什麼是 Java 內部類?

內部類是指把A類定義在另一個B類的內部。

例如:把類User定義在類Role中,類User就被稱爲內部類。

class Role {
    class User {
    }
}

1、內部類的訪問規則

1)可以直接訪問外部類的成員,包括私有

​2)外部類要想訪問內部類成員,必須創建對象

2、內部類的分類

​1)成員內部類

​2)局部內部類

​3)靜態內部類

​4)匿名內部類

題18:什麼是自動裝箱?什麼是自動拆箱?

自動裝箱是指將基本數據類型重新轉化爲對象。

public class Test {  
	public static void main(String[] args) {  
		Integer num = 9;
	}  
}  

num = 9的值是屬於基本數據類型,原則上不能直接賦值給對象Integer。但是在JDK1.5版本後就可以進行這樣的聲明自動將基本數據類型轉化爲對應的封裝類型,成爲對象後可以調用對象所聲明的方法。

自動拆箱是指將對象重新轉化爲基本數據類型。

public class Test {  
	public static void main(String[] args) {  
		// 聲明Integer對象
		Integer num = 9;
		// 隱含自動拆箱
		System.out.print(num--);
	}  
}

由於對象不能直接進行運算,而是需要轉化爲基本數據類型後才能進行加減乘除。

// 裝箱
Integer num = 10;
// 拆箱
int num1 = num;

題19:JDK1.8 中 ConcurrentHashMap 不支持空鍵值嗎?

首先明確一點HashMap是支持空鍵值對的,也就是null鍵和null值,而ConcurrentHashMap是不支持空鍵值對的。

查看一下JDK1.8源碼,HashMap類部分源碼,代碼如下:

public V get(Object key) {
        Node<K,V> e;
        return (e = getNode(hash(key), key)) == null ? null : e.value;
}
static final int hash(Object key) {
	int h;
	return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

HashMap在調用put()方法存儲數據時會調用hash()方法來計算key的hashcode值,可以從hash()方法上得出當key==null時返回值是0,這意思就是key值是null時,hash()方法返回值是0,不會再調用key.hashcode()方法。

ConcurrentHashMap類部分源碼,代碼如下:

public V put(K key, V value) {
	return putVal(key, value, false);
}
/** Implementation for put and putIfAbsent */
final V putVal(K key, V value, boolean onlyIfAbsent) {
	if (key == null || value == null) throw new NullPointerException();
	int hash = spread(key.hashCode());
	int binCount = 0;
	for (Node<K,V>[] tab = table;;) {
		Node<K,V> f; int n, i, fh;
		if (tab == null || (n = tab.length) == 0)
			tab = initTable();
		else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
			if (casTabAt(tab, i, null,
						 new Node<K,V>(hash, key, value, null)))
				break;                   // no lock when adding to empty bin
		}
		else if ((fh = f.hash) == MOVED)
			tab = helpTransfer(tab, f);
		else {
			V oldVal = null;
			synchronized (f) {
				if (tabAt(tab, i) == f) {
					if (fh >= 0) {
						binCount = 1;
						for (Node<K,V> e = f;; ++binCount) {
							K ek;
							if (e.hash == hash &&
								((ek = e.key) == key ||
								 (ek != null && key.equals(ek)))) {
								oldVal = e.val;
								if (!onlyIfAbsent)
									e.val = value;
								break;
							}
							Node<K,V> pred = e;
							if ((e = e.next) == null) {
								pred.next = new Node<K,V>(hash, key,
														  value, null);
								break;
							}
						}
					}
					else if (f instanceof TreeBin) {
						Node<K,V> p;
						binCount = 2;
						if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
													   value)) != null) {
							oldVal = p.val;
							if (!onlyIfAbsent)
								p.val = value;
						}
					}
				}
			}
			if (binCount != 0) {
				if (binCount >= TREEIFY_THRESHOLD)
					treeifyBin(tab, i);
				if (oldVal != null)
					return oldVal;
				break;
			}
		}
	}
	addCount(1L, binCount);
	return null;
}

ConcurrentHashmap在調用put()方法時調用了putVal()方法,而在該方法中判斷key爲null或value爲null時拋出空指針異常NullPointerException。

ConcurrentHashmap是支持併發的,當通過get()方法獲取對應的value值時,如果指定的鍵爲null,則爲NullPointerException,這主要是因爲獲取到的是null值,無法分辨是key沒找到null還是有key值爲null。

題20:父類中靜態方法能否被子類重寫?

父類中靜態方法不能被子類重寫。

重寫只適用於實例方法,不能用於靜態方法,而且子類當中含有和父類相同簽名的靜態方法,一般稱之爲隱藏。

public class A {
	
	public static String a = "這是父類靜態屬性";
	
	public static String getA() {
		return "這是父類靜態方法";
	}
	
}
public class B extends A{
	
	public static String a = "這是子類靜態屬性";
	
	public static String getA() {
		return "這是子類靜態方法";
	}
	
	public static void main(String[] args) {
		A a =  new B();
		System.out.println(a.getA());
	}
}

如上述代碼所示,如果能夠被重寫,則輸出的應該是“這是子類靜態方法”。與此類似的是,靜態變量也不能被重寫。如果想要調用父類的靜態方法,應該使用類來直接調用。

集合

題1:Java 中常用的集合有哪些?

Map接口和Collection接口是所有集合框架的父接口

Collection接口的子接口包括:Set接口和List接口。

Set中不能包含重複的元素。List是一個有序的集合,可以包含重複的元素,提供了按索引訪問的方式。

Map接口的實現類主要有:HashMap、Hashtable、ConcurrentHashMap以及TreeMap等。Map不能包含重複的key,但是可以包含相同的value。根據鍵得到值,對map集合遍歷時先得到鍵的set集合,對set集合進行遍歷,得到相應的值。

Set接口的實現類主要有:HashSet、TreeSet、LinkedHashSet等

List接口的實現類主要有:ArrayList、LinkedList、Stack以及Vector等

Iterator所有的集合類,都實現了Iterator接口,這是一個用於遍歷集合中元素的接口,主要包含以下三種方法:

hasNext()是否還有下一個元素

next()返回下一個元素

remove()刪除當前元素

併發

JVM

Spring

Spring MVC

Spring Boot

Spring Cloud

Dubbo

MySQL

Redis

MyBaits

Zookeeper

Linux

數據結構與算法

項目管理工具

消息隊列

設計模式

Nginx

常見 BUG 問題

網絡編程

WEB

Docker

Netty

Elasticsearch

Spark

非技術類面試題

內容持續更新中.......歡迎大家持續關注!!!!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章