Java最經典知識點總結,看完你都記住的了嗎?

1實現多線程的方式有幾種?

其實這個問題並不難,只是在這裏做一個總結。一共有三種。

實現Runnable接口,並實現該接口的run()方法

繼承Thread類,重寫run()方法

實現Callable接口,實現call()方法。

大家可能對前兩種已經很清楚了,重點說下第三種。

Callable接口是屬於Executor框架中的類,Callable接口與Runnable接口類似,但比後者功能更加強大,主要有三點:

Callable可以在任務結束後提供一個返回值,Runnable無法提供這個功能;

Callable的call()方法可以拋出異常,Runnable接口的run()方法不能拋出異常;

運行Callable可以得到一個Future對象,Future對象表示異步計算的結果。它提供了檢查計算是否完成的方法。由於線程屬於異步計算模型,所以無法從其他線程中得到方法的返回值。在這種情況下,就可以使用Future來監視目標線程調用call()方法的情況。當調用Futrue的get()方法以獲取結果時,當前線程就會阻塞,直到call()方法結束返回結果。

小編相信這裏有很多學習java的朋友,小編整理了一份java方面的學習資料,想要獲取的可以加我的java學習羣的喲,928204055。歡迎愛學習Java的你們。

舉個例子,此代碼在JDK 8 下運行,因爲使用了lambda表達式:

package exam;

import java.util.concurrent.ExecutionException;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.Future;

public class CallableAndFuture {

public static void main(String[] args) {

ExecutorService threadPool = Executors.newSingleThreadExecutor();

// 啓動線程

Future<String> future = threadPool.submit(() -> "Hello, world");

try {

System.out.println("waiting thread to finish.");

System.out.println(future.get()); // 等待線程結束,並獲取返回結果

threadPool.shutdown();

} catch (InterruptedException | ExecutionException e) {

e.printStackTrace();

}

}

}

2 volatile關鍵字的作用

在Java語言中,有時候爲了提高程序的運行效率,編譯器會做一些優化操作,把經常被訪問的變量緩存起來,程序在讀取這個變量的時候又有可能直接從寄存器中讀取這個值,而不會去內存中讀取。這樣的好處提高了程序的運行效率,但當遇到多線程編程時,變量的值可能被其他線程改變了,而該緩存的值不會做相應的改變,從而導致應用程序讀取的值可能與實際的變量值不一致。關鍵字volatile正好解決這個問題,被volatile修飾的變量編譯器不會做優化,每次都會從內存讀取。

3代碼中不同屬性和方法的執行順序

經常會遇到一個這樣的代碼,new一個子類,其子類以及父類每個屬性和方法的執行順序,具體可以看以下例子:

**

* Java程序初始化工作可以在許多不同的代碼中來完成,它們執行的順序如下:

* 父類靜態變量

* 父類靜態代碼塊

* 子類靜態變量

* 子類靜態代碼塊

* 父類非靜態變量

* 父類非靜態代碼塊

* 父類構造函數

* 子類非靜態變量

* 子類非靜態代碼塊

* 子類構造函數

*

*

* 注意,只有方法具有多態性,屬性則沒有。

* @author TurtusLi

*

*/

class BaseI {

int num = 1;

public BaseI() {

this.print();

num = 2;

}

public void print() {

System.out.println("Base.num = " + num);

}

}

public class Example1423 extends BaseI {

int num = 3;

public Example1423() {

this.print();

num = 4;

}

// 去掉這個複寫方法,運行看效果

@Override

public void print() {

System.out.println("Sub.num = " + num);

}

public static void main(String[] args) {

BaseI b = new Example1423();

System.out.println(b.num);

}

}

4 switch語句支持String類型的實現原理

在Java 7 以後,switch語句可以用作String類型上。

從本質來講,switch對字符串的支持,其實也是int類型值的匹配。它的實現原理如下:

通過對case後面的String對象調用hashCode()方法,得到一個int類型的Hash值,然後用這個Hash值來唯一標識着這個case。

那麼當匹配的時候,首先調用這個字符串的hashCode()方法,獲取一個Hash值(int類型),用這個Hash值來匹配所有的case,如果沒有匹配成功,說明不存在;如果匹配成功了,接着會調用字符串的equals()方法進行匹配。

由此看出,String變量不能是null;同時,switch的case子句中使用的字符串也不能爲null。

5多線程同步有幾種實現方法

Java主要提供了三種實現同步機制的方法。

synchronized關鍵字。有兩種用法,可以是synchronized方法和synchronized代碼塊。

wait和notify方法。

Lock。Lock接口有一個實現類ReentrantLock,也可以實現多線程的同步。

6在多線程編程的時候有哪些注意事項

如果能用volatile代替synchronized,近可能使用volatile。因爲被synchronized修飾的方法或代碼塊在同一時間只能允許一個線程訪問,而volatile沒有這個限制,因此使用synchronized會降低併發量。由於volatile無法保證原子性操作,因此在多線程的情況下,只有對變量的操作爲原子操作的情況下纔可以使用volatile。

儘可能減少synchronized塊內的代碼。

給每一個線程定義一個名字,這樣有利於調試。

儘量使用concurrent容器(ConcurrentHashMap)來代替synchronized容器(Hashtable)。

使用線程池來控制多線程的執行。

7 fail-fast和fail-safe迭代器的區別是什麼?

他們的主要區別是fail-safe允許在遍歷的過程中對容器的數據進行修改,而fail-fast則不允許。下面分別介紹這兩種迭代器的工作原理。

fail-fast:直接在容器上進行遍歷,在遍歷的過程中,一旦發現容器中的數據被修改了(添加元素、刪除或修改元素),就會拋出ConcurrentModificationException異常導致遍歷失敗。常見的使用fail-fast的容器有HashMap和ArrayList等。

fail-safe:這種遍歷是基於容器的克隆。因此,對容器中內容的修改不影響遍歷。常見使用fail-safe方式的容器有ConcurrentHashMap和CopyOnWriteArrayList。

8如何能夠使JVM中的虛擬機棧、堆內存和方法區發生內存溢出?

關於JVM的知識,有一本非常好的書籍——周志明《深入理解Java虛擬機:JVM高級特性與最佳實踐(第2版)》,裏面有非常好的介紹。幾乎可以說是Java程序員必讀書籍。

虛擬機棧是線程私有的,當創建一個線程時,同時會新建一個虛擬機棧,它描述的是Java方法執行的內存模型。 棧中有一個非常重要的概念——棧幀。棧幀用於保存局部變量表,操作數棧,方法出口等。

其實棧溢出最簡單的方式是無限遞歸。

堆內存是線程共享的,是JVM中內存管理的最大一塊內存,它保存所有實例化的對象。

堆內存溢出最簡單的方式是不停的new對象,GC來不及回收,直到內存全部耗盡。

方法區也是內存共享的。它用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。

方法區溢出簡單的方式是,調用String類的intern()方法,此方法如果在堆區找不到已經存在的String對象的話,就會往方法區中的常量池放一份,然後返回其引用放在堆區。還有一種辦法是不停地加載類。

9在int i =0; i=i++;語句中,i=i++是線程安全的嗎?如果不安全,請說明上面操作在JVM中的執行過程,爲什麼不安全?說出JDK中哪個類能達到以上程序的效果,並且是線程安全且高效的,簡述其原理。

語句i=i++的執行過程:先把i的值取出來放到棧頂,可以理解爲引入了第三方變量k,此時,k的值爲i,然後執行自增操作,於是i的值變爲1,最後執行賦值操作i=k(自增前的值),因此,執行結束後,i的值還是0。從上面的分析得知,i=i++語句的執行過程是由多個操作組成,它不是原子操作,因此,不是線程安全的。

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