java 面试知识点总结
1.面向對象可以解釋下嗎?都有哪些特性?
答:面向對象是一種思想,可以將復雜問題簡單化,讓我們從執行者變為了指揮者。面向對象的三大特性為:封裝,繼承與多態。
- 封裝:將事物封裝成一個類,減少耦合,隱藏細節。保留特定的接口與外界聯系,當接口內部發生改變時,不會影響外部調用方。
- 繼承:從一個已知的類中派生出一個新的類,新類可以擁有已知類的行為和屬性,并且可以通過覆蓋/重寫來增強已知類的能力。
- 多態:多態的本質就是一個程序中存在多個同名的不同方法,主要通過三種方式來實現:
- 通過子類對父類的覆蓋來實現
- 通過在一個類中對方法的重載來實現
- 通過將子類對象作為父類對象使用來實現
關于繼承:
我們需要注意Java中不支持多繼承,即一個類只可以有一個直接父類存在。另外Java中的構造函數是不可以繼承的,如果構造函數被private修飾,那么就是不明確的構造函數,該類是不可以被其它類繼承的,具體原因我們可以先來看下Java中類的初始化順序:
- 初始化父類中的靜態成員變量和靜態代碼塊
- 初始化子類中的靜態成員變量和靜態代碼塊
- 初始化父類中的普通成員變量和代碼塊,再執行父類的構造方法
- 初始化子類中的普通成員變量和代碼塊,再執行子類的構造方法
如果父類構造函數是私有(private)的,則初始化子類的時候不可以被執行,所以解釋了為什么該類不可以被繼承,也就是說其不允許有子類存在。我們知道,子類是由其父類派生產生的,那么子類有哪些特點呢?
- 子類擁有父類非private的屬性和方法
- 子類可以添加自己的方法和屬性,即對父類進行擴展
- 子類可以重新定義父類的方法,即方法的覆蓋/重寫
既然子類可以通過方法的覆蓋/重寫以及方法的重載來重新定義父類的方法,那么我們來看下什么是方法的覆蓋/重寫吧。(其實這也是一個高頻的面試熱身題目)
?
覆蓋(@Override):
覆蓋也叫重寫,是指子類和父類之間方法的一種關系,比如說父類擁有方法A,子類擴展了方法A并且添加了豐富的功能。那么我們就說子類覆蓋或者重寫了方法A,也就是說子類中的方法與父類中繼承的方法有完全相同的返回值類型、方法名、參數個數以及參數類型。
重載:
重載是指在一個類中(包括父類)存在多個同名的不同方法,這些方法的參數個數,順序以及類型不同均可以構成方法的重載。如果僅僅是修飾符、返回值、拋出的異常不同,那么這是2個相同的方法。
如果只有方法返回值不同,可以構成重載嗎?
答:不可以。因為我們調用某個方法,有時候并不關心其返回值,這個時候編譯器根據方法名和參數無法確定我們調用的是哪個方法。
舉例:如果我們分別定義了如下的兩個方法:
| 1 2 | public String Test(String userName){ } public void Test(String userName){ } |
在調用的時候,直接 Test(“XiaoMing”); 那么就會存在歧義。
我們再來看看如何通過將子類對象作為父類對象使用來實現多態。
把不同的子類對象都當作父類對象來看,可以屏蔽不同子類對象之間的差異,寫出通用的代碼,做出通用的編程,以適應需求的不斷變化。這樣操作之后,父類的對象就可以根據當前賦值給它的子類對象的特性以不同的方式運作。
對象的引用型變量具有多態性,因為一個引用型變量可以指向不同形式的對象,即:子類的對象作為父類的對象來使用。在這里涉及到了向上轉型和向下轉型,我們分別介紹如下:
?
向上轉型:
子類對象轉為父類,父類可以是接口。
公式:Father f = new Son(); Father是父類或接口,Son是子類。
向下轉型:
父類對象轉為子類。公式:Son s = (Son) f;
在向上轉型的時候我們可以直接轉,但是在向下轉型的時候我們必須強制類型轉換。并且,如案例中所述,該父類必須實際指向了一個子類對象才可強制類型向下轉型,即其是以這種方式Father f = new Son()創建的父類對象。若以Father f = new Father()這種方式創建的父類對象,那么不可以轉換向下轉換為子類的Son對象,運行會報錯,因為其本質還是一個Father對象。
JDK,JRE和JVM的區別與聯系有哪些?
答:三者的基本概念可以概括如下:
- JDK(Java Development Kit)是一個開發工具包,是Java開發環境的核心組件,并且提供編譯、調試和運行一個Java程序所需要的所有工具,可執行文件和二進制文件,是一個平臺特定的軟件
- JRE(Java Runtime Environment)是指Java運行時環境,是JVM的實現,提供了運行Java程序的平臺。JRE包含了JVM,但是不包含Java編譯器/調試器之類的開發工具
- JVM(Java Virtual Machine)是指Java虛擬機,當我們運行一個程序時,JVM負責將字節碼轉換為特定機器代碼,JVM提供了內存管理/垃圾回收和安全機制等
如果你只是為了運行一下 Java 程序的話,那么你只需要安裝 JRE 就可以了。如果你需要進行一些 Java 編程方面的工作,那么你就需要安裝 JDK 了。但是,這不是絕對的。有時,即使您不打算在計算機上進行任何 Java 開發,仍然需要安裝 JDK。例如,如果要使用 JSP 部署 Web 應用程序,那么從技術上講,您只是在應用程序服務器中運行 Java 程序。那你為什么需要 JDK 呢?因為應用程序服務器會將 JSP 轉換為 Java servlet,并且需要使用 JDK 來編譯 servlet。
區別與聯系:
- JDK是開發工具包,用來開發Java程序,而JRE是Java的運行時環境
- JDK和JRE中都包含了JVM
- JVM是Java編程的核心,獨立于硬件和操作系統,具有平臺無關性,而這也是Java程序可以一次編寫,多處執行的原因
解析:
這也是一道Java面試中的基礎題,因為我們學習Java都是從安裝一個JDK開始的。上邊有說Java程序具有平臺無關性,可以做到一次編寫,多處執行。那么Java的跨平臺性是如何實現的呢?
我們知道,Java程序都是運行在Java虛擬機,即JVM之上。JVM屏蔽了底層操作系統和硬件的差異。我想大多數同學的Hello Word程序都是在文本文件中寫的,然后我們通過javac來編譯.java文件,生成了一個.class文件,最后再通過java命令來運行.class文件。其實這就是經歷了一個先編譯,再解釋執行的過程,即先將java文件編譯成了字節碼.class文件,然后交給Java虛擬機解釋成特定平臺上的機器碼。
另外一個與平臺無關性的原因是,Java的語言規范中規定了基本數據類型的取值范圍和行為在各個平臺上是保持一致的。
我們做一個簡單的總結:
Java語言的平臺無關性是如何實現的?
- JVM屏蔽了操作系統和底層硬件的差異
- Java面向JVM編程,先編譯生成字節碼文件,然后交給JVM解釋成機器碼執行
- 通過規定基本數據類型的取值范圍和行為
學習了上邊的內容,聰明的你肯定可以回答面試官的如下問題了。
面試官:Java語言是編譯型還是解釋型語言?
答:Java的執行經歷了編譯和解釋的過程,是一種先編譯,后解釋執行的語言,不可以單純歸到編譯性或者解釋性語言的類別中。高級編程語言按照程序的執行方式分為編譯型和解釋型兩種。簡單來說,編譯型語言是指編譯器針對特定的操作系統將源代碼一次性翻譯成可被該平臺執行的機器碼;解釋型語言是指解釋器對源程序逐行解釋成特定平臺的機器碼并立即執行。
Java 語言既具有編譯型語言的特征,也具有解釋型語言的特征,因為 Java 程序要經過先編譯,后解釋兩個步驟,由 Java 編寫的程序需要先經過編譯步驟,生成字節碼(*.class?文件),這種字節碼必須由 Java 解釋器來解釋執行。因此,我們可以認為 Java 語言編譯與解釋并存。
什么是字節碼?采用字節碼的好處是什么?
在 Java 中,JVM 可以理解的代碼就叫做字節碼(即擴展名為?.class?的文件),它不面向任何特定的處理器,只面向虛擬機。Java 語言通過字節碼的方式,在一定程度上解決了傳統解釋型語言執行效率低的問題,同時又保留了解釋型語言可移植的特點。所以 Java 程序運行時比較高效,而且,由于字節碼并不針對一種特定的機器,因此,Java 程序無須重新編譯便可在多種不同操作系統的計算機上運行。
Java 程序從源代碼到運行一般有下面 3 步:
我們需要格外注意的是 .class->機器碼 這一步。在這一步 JVM 類加載器首先加載字節碼文件,然后通過解釋器逐行解釋執行,這種方式的執行速度會相對比較慢。而且,有些方法和代碼塊是經常需要被調用的(也就是所謂的熱點代碼),所以后面引進了 JIT 編譯器,而 JIT 屬于運行時編譯。當 JIT 編譯器完成第一次編譯后,其會將字節碼對應的機器碼保存下來,下次可以直接使用。而我們知道,機器碼的運行效率肯定是高于 Java 解釋器的。這也解釋了我們為什么經常會說 Java 是編譯與解釋共存的語言。
HotSpot 采用了惰性評估(Lazy Evaluation)的做法,根據二八定律,消耗大部分系統資源的只有那一小部分的代碼(熱點代碼),而這也就是 JIT 所需要編譯的部分。JVM 會根據代碼每次被執行的情況收集信息并相應地做出一些優化,因此執行的次數越多,它的速度就越快。JDK 9 引入了一種新的編譯模式 AOT(Ahead of Time Compilation),它是直接將字節碼編譯成機器碼,這樣就避免了 JIT 預熱等各方面的開銷。JDK 支持分層編譯和 AOT 協作使用。但是 ,AOT 編譯器的編譯質量是肯定比不上 JIT 編譯器的。
Java 中的幾種基本數據類型是什么?對應的包裝類型是什么?各自占用多少字節呢?
Java 中有 8 種基本數據類型,分別為:
這 8 種基本數據類型的默認值以及所占空間的大小如下:
| int | 32 | 4 | 0 |
| short | 16 | 2 | 0 |
| long | 64 | 8 | 0L |
| byte | 8 | 1 | 0 |
| char | 16 | 2 | 'u0000' |
| float | 32 | 4 | 0f |
| double | 64 | 8 | 0d |
| boolean | 1 | false |
另外,對于?boolean,官方文檔未明確定義,它依賴于 JVM 廠商的具體實現。邏輯上理解是占用 1 位,但是實際中會考慮計算機高效存儲因素。
注意:
這八種基本類型都有對應的包裝類分別為:Byte、Short、Integer、Long、Float、Double、Character、Boolean?。
包裝類型不賦值就是?Null?,而基本類型有默認值且不是?Null。
另外,這個問題建議還可以先從 JVM 層面來分析。
基本數據類型直接存放在 Java 虛擬機棧中的局部變量表中,而包裝類型屬于對象類型,我們知道對象實例都存在于堆中。相比于對象類型, 基本數據類型占用的空間非常小。
Java 泛型了解么?什么是類型擦除?介紹一下常用的通配符?
Java 泛型(generics)是 JDK 5 中引入的一個新特性, 泛型提供了編譯時類型安全檢測機制,該機制允許程序員在編譯時檢測到非法的類型。泛型的本質是參數化類型,也就是說所操作的數據類型被指定為一個參數。
Java 的泛型是偽泛型,這是因為 Java 在運行期間,所有的泛型信息都會被擦掉,這也就是通常所說類型擦除 。
List<Integer> list = new ArrayList<>();list.add(12); //這里直接添加會報錯 list.add("a"); Class<? extends List> clazz = list.getClass(); Method add = clazz.getDeclaredMethod("add", Object.class); //但是通過反射添加,是可以的 add.invoke(list, "kl");System.out.println(list);hashCode()與 equals()
面試官可能會問你:“你重寫過?hashcode?和?equals么,為什么重寫?equals?時必須重寫?hashCode?方法?”
1)hashCode()介紹:
hashCode()?的作用是獲取哈希碼,也稱為散列碼;它實際上是返回一個 int 整數。這個哈希碼的作用是確定該對象在哈希表中的索引位置。hashCode()定義在 JDK 的?Object?類中,這就意味著 Java 中的任何類都包含有?hashCode()?函數。另外需要注意的是:?Object?的 hashcode 方法是本地方法,也就是用 c 語言或 c++ 實現的,該方法通常用來將對象的 內存地址 轉換為整數之后返回。
public native int hashCode();Copy to clipboardErrorCopied散列表存儲的是鍵值對(key-value),它的特點是:能根據“鍵”快速的檢索出對應的“值”。這其中就利用到了散列碼!(可以快速找到所需要的對象)
2)為什么要有 hashCode?
我們以“HashSet?如何檢查重復”為例子來說明為什么要有 hashCode?
當你把對象加入?HashSet?時,HashSet?會先計算對象的 hashcode 值來判斷對象加入的位置,同時也會與其他已經加入的對象的 hashcode 值作比較,如果沒有相符的 hashcode,HashSet?會假設對象沒有重復出現。但是如果發現有相同 hashcode 值的對象,這時會調用?equals()?方法來檢查 hashcode 相等的對象是否真的相同。如果兩者相同,HashSet?就不會讓其加入操作成功。如果不同的話,就會重新散列到其他位置。(摘自我的 Java 啟蒙書《Head First Java》第二版)。這樣我們就大大減少了 equals 的次數,相應就大大提高了執行速度。
3)為什么重寫?equals?時必須重寫?hashCode?方法?
如果兩個對象相等,則 hashcode 一定也是相同的。兩個對象相等,對兩個對象分別調用 equals 方法都返回 true。但是,兩個對象有相同的 hashcode 值,它們也不一定是相等的 。因此,equals 方法被覆蓋過,則?hashCode?方法也必須被覆蓋。
hashCode()的默認行為是對堆上的對象產生獨特值。如果沒有重寫?hashCode(),則該 class 的兩個對象無論如何都不會相等(即使這兩個對象指向相同的數據)
4)為什么兩個對象有相同的 hashcode 值,它們也不一定是相等的?
在這里解釋一位小伙伴的問題。以下內容摘自《Head Fisrt Java》。
因為?hashCode()?所使用的哈希算法也許剛好會讓多個對象傳回相同的哈希值。越糟糕的哈希算法越容易碰撞,但這也與數據值域分布的特性有關(所謂碰撞也就是指的是不同的對象得到相同的?hashCode?)。
我們剛剛也提到了?HashSet,如果?HashSet?在對比的時候,同樣的 hashcode 有多個對象,它會使用?equals()?來判斷是否真的相同。也就是說?hashcode?只是用來縮小查找成本。
更多關于?hashcode()?和?equals()?的內容可以查看:Java hashCode() 和 equals()的若干問題解答
抽象類和接口有什么區別?
答:抽象類和接口的主要區別可以總結如下。
- 抽象類中可以沒有抽象方法,也可以抽象方法和非抽象方法共存
- 接口中的方法在JDK8之前只能是抽象的,JDK8版本開始提供了接口中方法的default實現
- 抽象類和類一樣是單繼承的;接口可以實現多個父接口
- 抽象類中可以存在普通的成員變量;接口中的變量必須是static final類型的,必須被初始化,接口中只有常量,沒有變量
抽象類和接口應該如何選擇?分別在什么情況下使用呢?
答:根據抽象類和接口的不同之處,當我們僅僅需要定義一些抽象方法而不需要其余額外的具體方法或者變量的時候,我們可以使用接口。反之,則需要使用抽象類,因為抽象類中可以有非抽象方法和變量
JDK8中為什么會出現默認方法呢?
答:使用接口,使得我們可以面向抽象編程,但是其有一個缺點就是當接口中有改動的時候,需要修改所有的實現類。在JDK8中,為了給已經存在的接口增加新的方法并且不影響已有的實現,所以引入了接口中的默認方法實現。
默認方法允許在不打破現有繼承體系的基礎上改進接口,解決了接口的修改與現有的實現不兼容的問題。該特性在官方庫中的應用是:給java.util.Collection接口添加新方法,如stream()、parallelStream()、forEach()和removeIf()等等。在我們實際開發中,接口的默認方法應該謹慎使用,因為在復雜的繼承體系中,默認方法可能引起歧義和編譯錯誤。
Java中的元注解有哪些?
答:Java中提供了4個元注解,元注解的作用是負責注解其它注解。
- @Target:說明注解所修飾的對象范圍
- @Retention:(保留策略)保留策略定義了該注解被保留的時間長短SOURCE:表示在源文件中有效(即源文件保留);CLASS:表示在class文件中有效(即class保留)RUNTIME:表示在運行時有效(即運行時保留)。例如@Retention(RetentionPolicy.RUNTIME)標注表示該注解在運行時有效。
- @Documented:該注解用于描述其它類型的annotation應該被作為被標注的程序成員的公共API,因此可以被javadoc此類的工具文檔化。Documented是一個標記注解,沒有成員。關鍵源碼如下:
- @Inherited:該注解是一個標記注解,@Inherited闡述了某個被標注的類型是被繼承的。如果一個使用了@Inherited修飾的annotation類型被用于一個class,則這個annotation將被用于該class的子類
注解的作用:
代替繁雜的配置文件,簡化開發。
當定義一個注解之后,還需要一個注解處理器來執行注解的內部邏輯。注解處理器定義了注解的處理邏輯,涉及到反射機制和線程機制等
說說Java中反射機制?
答:?反射機制是指在運行中,對于任意一個類,都能夠知道這個類的所有屬性和方法。對于任意一個對象,都能夠調用它的任意一個方法和屬性。即動態獲取信息和動態調用對象方法的功能稱為反射機制。
與反射相關的類:
- Class:表示類,用于獲取類的相關信息
- Field:表示成員變量,用于獲取實例變量和靜態變量等
- Method:表示方法,用于獲取類中的方法參數和方法類型等
- Constructor:表示構造器,用于獲取構造器的相關參數和類型等
這里我們講述下如何獲取Class類吧,獲取Class類有三種基本方式:
(1)通過類名稱.class來獲取Class類對象:
| 1 2 3 | Class c =?int.class; Class c =?int[ ].class; Class c = String.class |
(2)通過對象.getClass( )方法來獲取Class類對象:
| 1 | Class c = obj.getClass( ); |
(3)通過類名稱加載類Class.forName( ),只要有類名稱就可以得到Class:
| 1 | Class c = Class.forName(“cn.ywq.Demo”); |
反射機制優缺點
- 優點?: 可以讓咱們的代碼更加靈活、為各種框架提供開箱即用的功能提供了便利
- 缺點?:讓我們在運行時有了分析操作類的能力,這同樣也增加了安全問題。比如可以無視泛型參數的安全檢查(泛型參數的安全檢查發生在編譯時)。另外,反射的性能也要稍差點,不過,對于框架來說實際是影響不大的。Java Reflection: Why is it so slow?
在一個靜態方法內調用一個非靜態成員為什么是非法的?
這個需要結合 JVM 的相關知識,靜態方法是屬于類的,在類加載的時候就會分配內存,可以通過類名直接訪問。而非靜態成員屬于實例對象,只有在對象實例化之后才存在,然后通過類的實例對象去訪問。在類的非靜態成員不存在的時候靜態成員就已經存在了,此時調用在內存中還不存在的非靜態成員,屬于非法操作。
深拷貝 vs 淺拷貝
String StringBuffer 和 StringBuilder 的區別是什么? String 為什么是不可變的?
可變性
簡單的來說:String?類中使用 final 關鍵字修飾字符數組來保存字符串,private final char value[],所以String?對象是不可變的。
在 Java 9 之后,String 、StringBuilder?與?StringBuffer?的實現改用 byte 數組存儲字符串?private final byte[] value
而?StringBuilder?與?StringBuffer?都繼承自?AbstractStringBuilder?類,在?AbstractStringBuilder?中也是使用字符數組保存字符串char[]value?但是沒有用?final?關鍵字修飾,所以這兩種對象都是可變的。
StringBuilder?與?StringBuffer?的構造方法都是調用父類構造方法也就是AbstractStringBuilder?實現的,大家可以自行查閱源碼。
AbstractStringBuilder.java
abstract class AbstractStringBuilder implements Appendable, CharSequence {/*** The value is used for character storage.*/char[] value;線程安全性
String?中的對象是不可變的,也就可以理解為常量,線程安全。AbstractStringBuilder?是?StringBuilder?與?StringBuffer?的公共父類,定義了一些字符串的基本操作,如?expandCapacity、append、insert、indexOf?等公共方法。StringBuffer?對方法加了同步鎖或者對調用的方法加了同步鎖,所以是線程安全的。StringBuilder?并沒有對方法進行加同步鎖,所以是非線程安全的。
性能
每次對?String?類型進行改變的時候,都會生成一個新的?String?對象,然后將指針指向新的?String?對象。StringBuffer?每次都會對?StringBuffer?對象本身進行操作,而不是生成新的對象并改變對象引用。相同情況下使用?StringBuilder?相比使用?StringBuffer?僅能獲得 10%~15% 左右的性能提升,但卻要冒多線程不安全的風險。
對于三者使用的總結:
try-catch-finally
- try塊:?用于捕獲異常。其后可接零個或多個?catch?塊,如果沒有?catch?塊,則必須跟一個?finally?塊。
- catch塊:?用于處理 try 捕獲到的異常。
- finally?塊:?無論是否捕獲或處理異常,finally?塊里的語句都會被執行。當在?try?塊或?catch?塊中遇到?return?語句時,finally?語句塊將在方法返回之前被執行。
在以下 3 種特殊情況下,finally?塊不會被執行:
注意:?當 try 語句和 finally 語句中都有 return 語句時,在方法返回之前,finally 語句的內容將被執行,并且 finally 語句的返回值將會覆蓋原始的返回值
使用?try-with-resources?來代替try-catch-finally
《Effecitve Java》中明確指出:
面對必須要關閉的資源,我們總是應該優先使用?try-with-resources?而不是try-finally。隨之產生的代碼更簡短,更清晰,產生的異常對我們也更有用。try-with-resources語句讓我們更容易編寫必須要關閉的資源的代碼,若采用try-finally則幾乎做不到這點。
Java 中類似于InputStream、OutputStream?、Scanner?、PrintWriter等的資源都需要我們調用close()方法來手動關閉,一般情況下我們都是通過try-catch-finally語句來實現這個需求,如下:
//讀取文本文件的內容Scanner scanner = null;try {scanner = new Scanner(new File("D://read.txt"));while (scanner.hasNext()) {System.out.println(scanner.nextLine());}} catch (FileNotFoundException e) {e.printStackTrace();} finally {if (scanner != null) {scanner.close();}}Copy to clipboardErrorCopied使用 Java 7 之后的?try-with-resources?語句改造上面的代碼:
try (Scanner scanner = new Scanner(new File("test.txt"))) {while (scanner.hasNext()) {System.out.println(scanner.nextLine());} } catch (FileNotFoundException fnfe) {fnfe.printStackTrace(); }Copy to clipboardErrorCopied當然多個資源需要關閉的時候,使用?try-with-resources?實現起來也非常簡單,如果你還是用try-catch-finally可能會帶來很多問題。
通過使用分號分隔,可以在try-with-resources塊中聲明多個資源。
try (BufferedInputStream bin = new BufferedInputStream(new FileInputStream(new File("test.txt")));BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(new File("out.txt")))) {int b;while ((b = bin.read()) != -1) {bout.write(b);}}catch (IOException e) {e.printStackTrace();}既然有了字節流,為什么還要有字符流?
問題本質想問:不管是文件讀寫還是網絡發送接收,信息的最小存儲單元都是字節,那為什么 I/O 流操作要分為字節流操作和字符流操作呢?
回答:字符流是由 Java 虛擬機將字節轉換得到的,問題就出在這個過程還算是非常耗時,并且,如果我們不知道編碼類型就很容易出現亂碼問題。所以, I/O 流就干脆提供了一個直接操作字符的接口,方便我們平時對字符進行流操作。如果音頻文件、圖片等媒體文件用字節流比較好,如果涉及到字符的話使用字符流比較好。
逃逸分析:
逃逸分析的基本行為就是分析對象動態作用域,當一個對象在方法中被定義后,它可能被外部方法所引用,例如作為調用參數傳遞到其他地方中,稱為方法逃逸。JIT編譯器的優化包括如下:
- 同布省略:也就是鎖消除,當JIT編譯器判斷不會產生并發問題,那么會將同步synchronized去掉
- 標量替換
標量替換
簡單地說,就是用標量替換聚合量。這樣做的好處是如果創建的對象并未用到其中的全部變量,則可以節省一定的內存。對于代碼執行而言,無需去找對象的引用,也會更快一些
標量是指不可分割的量,如java中基本數據類型和reference類型,相對的一個數據可以繼續分解,稱為聚合量;
如果把一個對象拆散,將其成員變量恢復到基本類型來訪問就叫做標量替換;
如果逃逸分析發現一個對象不會被外部訪問,并且該對象可以被拆散,那么經過優化之后,并不直接生成該對象,而是在棧上創建若干個成員變量;
通過-XX:+EliminateAllocations可以開啟標量替換, -XX:+PrintEliminateAllocations查看標量替換情況。
我們先來解釋下標量和聚合量的基本概念。
-
標量(Scalar)是指一個無法再分解成更小的數據的數據。Java中的原始數據類型就是標量。
-
聚合量(Aggregate)是還可以分解的數據。Java中的對象就是聚合量,因為他可以分解成其他聚合量和標量。
在JIT階段,如果經過逃逸分析,發現一個對象不會被外界訪問的話,那么經過JIT優化,就會把這個對象拆解成若干個其中包含的若干個成員變量來代替。這個過程就是標量替換。標量替換的好處就是對象可以不在堆內存進行分配,為棧上分配提供了良好的基礎。
?
那么逃逸分析技術存在哪些缺點呢?
????技術不是特別成熟,分析的過程也很耗時,如果沒有一個對象是不逃逸的,那么就得不償失了。
對象分配流程:
Java對象內存分配流程_夕灬顏的博客-CSDN博客_java 對象內存分配過程
Java對象分配簡要流程 - SegmentFault 思否https://segmentfault.com/a/1190000004606059
java中的偽共享?如何解決:
Java中的偽共享(false sharing) - 簡書 (jianshu.com)
三次握手四次揮手:
(11條消息) TCP 協議(包含三次握手,四次揮手)_一朵花花-CSDN博客
spring事務:
(11條消息) SpringBoot2異常處理回滾事務詳解(自動回滾/手動回滾/部分回滾)_zzhongcy的專欄-CSDN博客_部分回滾https://blog.csdn.net/zzhongcy/article/details/102893309
總結
以上是生活随笔為你收集整理的java 面试知识点总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 高等代数第3版下 [丘维声 著] 201
- 下一篇: 《算法导论》中文版