new對象實際是在幹嗎,懂了後String相關面試題隨便推導

目錄java

對象的建立,內存佈局以及訪問定位,推導String必問面試題答案程序員

JVM 遇到了 new 對象以後作了什麼?

package com.kuaizhan.web.utils;
 /**  * @author by zengzhiqin  * 2020-07-15  */ class Person {  String name;   public void say(String name) {  System.out.println("hello " + name);  } } public class TestPerson {  public static void main(String[] args){  Person person;  person = new Person();  person.say("特朗普");  } } 複製代碼

以此爲例,結合以前說到的類生命週期來講(不懂的朋友能夠看前面的文章Java類的生命週期,不懂這個都很差意思和別人說我是搞JAVA的 ),當執行到 new Person 的時候:web

  1. 首先去常量池中看可否根據這個類的全路徑找到這個類的信息,查看是否加載過,解析,初始化過。若是沒有,則先進行類加載過程;
  2. 接下來JVM爲新的 Person對象分配內存,對象所需的內存大小在類加載的時候就能夠徹底肯定,由於一個對象具備什麼屬性,屬性大小(int,byte,long等所佔字節大小)均可以進行肯定; 分配內存 = 從虛擬機設置的堆內存裏面劃分一塊新對象所需的肯定大小的內存

具體的劃分方式根據各家垃圾收集器不一樣也採用不一樣的劃分方式,主要兩種:面試

方式一:指針碰撞(假設JAVA堆內存是整整齊齊的,用過的站一塊兒,沒用過的站一塊兒,那麼在中間分界地方放個指針,每次新分配往沒用過的那邊移動一點就行了。這種方式須要垃圾收集器維護這個用過的和沒用過的內存,由於運行過程當中可能中間隨時有使用過的內存被回收了就出現了缺口,須要垃圾收集器進行整理內存)編輯器

方式二:空閒列表(JAVA堆空閒內存之間是這裏缺一塊那裏缺一塊的,虛擬機維護一個空閒內存列表,記錄下來哪些空閒哪些佔用了,分配的時候從空閒列表裏面找)佈局

  1. 分配完內存,JVM 將拿到內存的數據類型都初始化爲默認值;
  2. JVM 對對象頭進行設置,例如對象是哪一個類的實例,如何找到類的元數據信息,對象hash 碼,GC分代年齡信息等,都屬於對象頭信息;
  3. 」clinit「方法統一賦值對象的屬性,前面第三步初始化是默認值,例如 int i = 4 d在第三步是初始化爲int的默認值0,這裏是將其賦值爲你定義的4;
  4. 在棧中新建對象引用,並將其指向堆中新建的對象實例。

舉個栗子

仍是這段代碼:flex

package com.kuaizhan.web.utils;
 /**  * @author by zengzhiqin  * 2020-07-15  */ class Person {  String name;   public void say(String name) {  System.out.println("hello " + name);  } } public class TestPerson {  public static void main(String[] args){  Person person;  person = new Person();  person.say("特朗普");  } } 複製代碼
代碼對應對象調用的內存
代碼對應對象調用的內存

調用過程:優化

  1. JVM去方法區尋找Person類信息
  2. 若是找不到,Classloader加載Person類信息進入內存方法區
  3. 在堆內存中建立Person對象,並持有方法區中Person類的類型信息的引用
  4. 把person添加到執行main()方法的主線程java調用棧中,指向堆空間中的內存對象
  5. 執行person.sayHello()時,JVM根據person定位到堆空 間的Person實例
  6. 根據Person實例在方法區持有的引用,定位到方法區 Person類型信息,得到sayHello() 字節碼,執行此方法。執行,打印出結果。

注意:url

  • 當 new Person() 的時候,虛擬機作了兩件事情:
  1. 在堆上產生一個實例,假設實例地址是 0x22;
  2. 將變量地址 0x11 指向堆實例的地址 0x22;

new Person()

JVM堆、棧和方法區
JVM堆、棧和方法區

局部變量 person 其實就是一個reference引用,說白了就是一個相似 0x11 的地址,存在於局部變量表裏面(局部變量表在棧區,不記得的朋友能夠看看上一篇講JVM內存分佈的文章Java跨平臺根本緣由,面試必問JVM內存模型白話文詳解來了)。spa

引用指向關係: 0x11 => 0x22 => 0x33

reference1 就是局部變量person,指向堆區的實例;而後 new Person() 指向方法區裏面的 Person類元數據信息包括sayHello方法。

當調用sayHello的時候,步驟以下三步:

  1. 首先能拿到 person 也就是reference1,其地址是0x11;
  2. 0x11 地址指向堆裏面的 new Person(),new Person 擁有方法區類存放地址假設 0x22;
  3. 0x22 指向的是方法區的 Person類元數據信息,元數據信息裏面是包括 sayHello() 方法的地址,最後完成調用。

從虛擬機內存推導String面試高頻題答案

String 面試高頻題,其實都是靠推導出來的,記永遠是記不清的:

  1. 建立了幾個對象?
String str = "1" + "2" + "3";
 答案:一個對象,編譯時候會進行字符串摺疊,算是一個優化,之前確實是四個對象,「1」,「2」,「3」,「123」 字符串摺疊:若是是常量相加,通俗理解就是先加,而後去常量池找,有直接返回,沒有就建立 複製代碼
  1. 打印結果?
String s1 = "hello";
String s2 = "world"; String s3 = "hello world";  System.out.print(s3 == "hello" + " world") true,根據上面的分析,都是比較常量池的值,是同一個,true System.out.print(s3 == s1 + s2); false,變量相加,只要有一個變量,那麼都要先給變量開空間,就成了地址比較 複製代碼
  1. 打印結果?

String str2 = new String("Trump"); 建立了幾個對象?

String str1 = "Trump"; 
直接去常量池建立一個Trump,棧裏面保存引用直接指向常量池」Trump「  String str2 = new String("Trump"); 建立幾個對象分狀況(String 不可變): 1. 一個對象(若是常量池中已經存在」Trump」,堆裏面建立個對象就能夠,棧裏面來個引用指向堆) 2. 兩個對象(若是常量池中不存在」Trump「,先在常量池裏面建立」Trump「,而後堆裏面new一個對象,最後棧裏面來個引用指向堆)  System.out.print(str1 == str2); 比較的棧裏面的引用地址,指向的東西都不同怎麼多是同樣的,果斷 false System.out.print(str1.equals(str2)); 比較的常量池裏面的值,都是Trump, true 複製代碼

公衆號下篇內容預告: 垃圾回收,JAVA程序員的福音吶

往期推薦:

Java跨平臺根本緣由,面試必問JVM內存模型白話文詳解來了

從JVM設計者的角度來看.class文件結構,一文弄懂.class文件的身份地位

Java類的生命週期,不懂這個都很差意思和別人說我是搞JAVA的

歡迎批評指正,有收穫的朋友點個在看或者分享鼓勵一下吧,十分感謝~

關注我,一塊兒成長
關注我,一塊兒成長
相關文章
相關標籤/搜索