一些Java面試經常提及的知識點

一、static關鍵字的作用 

1、static修飾成員變量

static修飾的變量屬於類,在類初始化時通過類加載器加載到JVM來分配內存空間

2、static修飾成員方法

static修飾的方法屬於類方法,不需要創建實例就可以直接調用。在static修飾的成員方法中不能使用this和super等關鍵字,不能調用非static方法,只能訪問所屬類的靜態成員變量和靜態方法

3、static修飾代碼塊

JVM在加載類時會執行static代碼塊,static代碼塊常用於初始化靜態變量,static代碼塊只會在類加載時執行且只執行一次

4、static修飾內部類

static內部類可以不依賴外部類實例對象而被實例化,而內部類需要在外部類實例化後才能實例化

5、static靜態導包

用import static 替代import

一般我們導入一個類用

import java.io.File;

而靜態導包是這樣的

import static java.lang.Integer.*;

 

二、final關鍵字

1.修飾一個引用

如果引用是一個基本數據類型,則該引用爲一個常量,無法修改

如果引用爲引用數據類型,比如對象、數組,則該對象、數組本身可以修改,但指向該對象的引用不可以修改

如果引用是類的成員變量,則必須當場賦值,否則編譯報錯

2、修飾一個方法

當final修飾一個方法時,該方法爲最終方法,無法被子類重寫。但可以被繼承

3.修飾一個類

final修飾類,這個類就是“斷子絕孫類”,最終類,無法被繼承

 

三、transient關鍵字

1、如果變量被transient修飾,變量將不會是對象持久化的一部分,該變量內容在序列化後無法獲得訪問

2、transient關鍵字只能修飾變量,而不能修飾方法和類。本地變量是不能被transient修飾的

3、被transient修飾的變量不能再被序列化,一個靜態變量不管是否被transient修飾均不能被序列化

爲什麼要使用transient關鍵字?

當持久化對象時,可能會有一些特殊的對象成員數據,比如銀行卡密碼等,我們不想序列化來持久化保存它,就在這個域前加上關鍵字transient即可

四、volatile關鍵字

一旦一個類的成員變量或者類的靜態成員變量被volatile修飾,就具備了兩層意思:

1.保證了不同線程對這個變量的操作是可見性的,即一個線程修改了值,那麼這個新值對於其他線程是立即可見的

2.禁止進行指令重排序

PS:volatile禁止指令重排序有兩層含義:1.當程序執行到volatile變量的讀寫操作時,該變量前面的操作肯定已經完成,且其結果對後面的操作可見;在該變量後面的操作肯定還沒有開始進行。2.在進行指令優化時,不能把volatile變量後面的語句放在其前面執行

volatile關鍵字不能保證對變量操作的原子性(單線程可以)!!!

volatile的底層實現:

jvm底層採用‘內存屏障’來實現volatile語義。在JMM中,線程之間的通信通過共享內存來實現。

volatile內存語義:

  • 當寫一個volatile變量時,JMM會把該線程所對應的本地內存中的共享變量值立即刷新到主內存上

  • 當讀一個volatile變量時,JMM會把該線程所對應的本地內存中的共享變量值設爲無效,直接從主內存上讀

volatile的底層是通過插入內存屏障來實現的,但是對於編譯器來說,發現一個最優佈置來最小化插入內存屏障的總數幾乎是不可能的,所以,JMM採用保守策略。如下:

  • 在每一個volatile寫操作前面插入一個StoreStore屏障

  • 在每一個volatile寫操作後面插入一個StoreLoad屏障

  • 在每一個volatile讀操作後面插入一個LoadLoad屏障

  • 在每一個volatile讀操作後面插入一個LoadStore屏障

StoreStore屏障可以保證在volatile寫之前,其前面的所有普通寫操作都已經刷新到主內存中;
StoreLoad屏障的作用是避免volatile寫與後面可能有的volatile讀/寫操作重排序
LoadLoad屏障用來禁止處理器把上面的volatile讀與下面的普通讀重排序
LoadStore屏障用來禁止處理器把上面的volatile讀與下面的普通寫重排序

java中volatile關鍵字提供了一個功能,那就是被其修飾的變量在被修改後可以立即同步到主內存,被其修飾的變量在每次使用之前都從主內存刷新。因此,可以使用volatile來保證多線程操作時變量的可見性。

五、單例模式

單例模式有差不多6、7種寫法吧,這是我感覺比較好用的幾種:

public class SingleTon {
	
	private static class SingTonHodler{
		private static SingleTon INSTANCE = new SingleTon();
	}
	
	private SingleTon(){};
	
	public static final SingleTon getInstance(){
		return SingTonHodler.INSTANCE;
	}
}

還有effectiveJava中提倡的一種寫法:

public enum SingleTon {
	
	INSTANCE;
	
	public void doSomething(){
		System.out.println("singleTON 創建完成");
	}

}

六、多線程

6.1 線程多次調用start方法,會發生什麼?

調用Thread類的start()方法時,此時該線程就處於就緒狀態,並沒有運行。通過調用run()方法來完成運行操作,run()稱爲線程體,它包含了要執行的這個線程的內容,run()運行完成,此線程終止。如果CPU再運行其他線程,直接調用run()方法,這只是調用一個方法而已,程序中依然只有一個主線程,是沒有達到多線程的目的的。

一個線程對象只能調用一次start()方法,如果調用多次就會拋出java.lang.IllegalThreadStateException。

可以被重複調用的是Run()方法。

6.2 常用的幾種線程池

打開java.util.concurrent.Executors類,可以看到的線程池列表如下:

但常用的也就幾種。

6.2.1 newCachedThreadPool 

這是一個可緩存的線程池,如果線程池個數超過需要的個數,可靈活回收空閒線程;如若不夠,則創建新線程。

這種線程池的特點是:

1.創建的線程最大個數爲Interger. MAX_VALUE,相當於沒有限制

2.如果長時間沒有向線程池中提交任務——工作線程空閒了指定時間(默認1分鐘),則工作線程將自動終止。這時再提交一個新任務,線程池會重新創建一個新的線程。

3.使用該線程時,一定要注意控制線程池線程的數量,否則大量線程同時運行會造成系統癱瘓。

6.2.2 newFixedThreadPool

這是一個創建固定工作線程數量的線程池。每提交一個任務就會創建一個工作線程,如果任務超過了線程池的線程數量,則將超過數量的任務存入池隊列中。

FixedThreadPool是一個典型且優秀的線程池。它具有線程池提高程序效率和節省創建線程所耗開銷的優點。但是在線程池空閒時,即線程池中沒有可運行的任務時,它也不會釋放工作線程,還會佔用一定的系統資源。

該線程池的大小最好根據系統資源進行設置:Runtime.getRuntime().availableProcessors();

6.2.3 newSingleThreadExecutor

這是一個創建單線程的Executor,即只創建唯一的工作者線程來執行任務,它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行。如果這個線程異常結束,會有另一個取代它,保證順序執行。單工作線程最大的特點是可保證順序地執行各個任務,並且在任意給定的時間不會有多個線程是活動的。

6.2.4 newScheduleThreadPool

這是一個創建定時工作線程的線程池。支持定時執行和週期性執行。

 

 

 

 

 

 

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