欧美一区二区三区老妇人-欧美做爰猛烈大尺度电-99久久夜色精品国产亚洲a-亚洲福利视频一区二区

面試guan:在 Java 中 new 一個對象的流程是怎樣的?徹底被問懵了。。

對象怎么創(chuàng)建,這個太熟悉了,new一下(其實還有很多途徑,比如反射、反序列化、clone等,這里拿最簡單的new來講):

從策劃到設計制作,每一步都追求做到細膩,制作可持續(xù)發(fā)展的企業(yè)網站。為客戶提供成都網站制作、做網站、網站策劃、網頁設計、域名注冊、虛擬主機、網絡營銷、VI設計、 網站改版、漏洞修補等服務。為客戶提供更好的一站式互聯網解決方案,以客戶的口碑塑造優(yōu)易品牌,攜手廣大客戶,共同發(fā)展進步。

Dog dog = new Dog();

我們總是習慣于固定語句的執(zhí)行,卻對于背后的實現過程缺乏認知,而理解這個過程對后面晦澀難懂的反射和代理其實會有很大幫助,所以請務必學好這塊內容。

在看這篇文章之前,啰嗦一句:如果你死記硬背下面所說的流程等于白看,就算現在記住了,一個禮拜后呢,一個月后你又能記得多少,因為對象創(chuàng)建過程這個知識點平常的工作中基本不會涉及到,太底層了,背熟的知識點不經常加以運用容易遺忘,所以我的建議是什么呢,流程做到心里大概有個數,其中涉及到關鍵的知識點記牢就可以了。

JVM內存

先簡單說下java虛擬機內存模型和存放內容的區(qū)別,兩部分:

  • 棧內存 存放基本類型數據和對象的引用變量,數據可以直接拿來訪問,速度比堆快
  • 堆內存 存放創(chuàng)建的對象和數組,會由java虛擬機的自動垃圾回收來管理(GC),創(chuàng)建一個對象放入堆內的同時也會在棧中創(chuàng)建一個指向該對象堆內存中的地址引用變量,下面說的對象就是存在該內存中

下面我們就按照對象生成的過程來一一講解參與其中過程的各個概念

首先有這么一個類,后面的初始化基于這個講解:

/**
 * @author 煒哥
 * @since 2021-04-18 11:01:41
 *
 * 執(zhí)行順序:(優(yōu)先級從高到低。)靜態(tài)代碼塊>構造代碼塊>構造方法>普通方法。
 * 其中靜態(tài)代碼塊只執(zhí)行一次。構造代碼塊在每次創(chuàng)建對象是都會執(zhí)行。
 */
public class Dog {

    //默認狗狗的最大年齡是16歲
    private static int dog_max_age = 16;

    //狗狗的名字
    private String dog_name;

    {
        System.out.println("狗狗的構造代碼塊");
    }

    static {
        System.out.println("狗狗的靜態(tài)代碼塊");
    }

    //無參構造器故意沒設
    //有參構造器
    public Dog(String dog_name) {
        this.dog_name = dog_name;
    }

    public void getDogInfo(){
        System.out.println("名字是:"+dog_name + "  年齡:" + dog_max_age);
    }

    //狗叫
    public static void barking(){
        System.out.println("汪汪汪~~~");
    }
}

JVM生成.class文件

一個java文件會在編譯期間被初始化生成.class字節(jié)碼文件,字節(jié)碼文件是專門給JVM閱讀的,我們平時吭哧吭哧寫的一行行代碼最終都會被編譯成機器能看懂的語句,這個文件后面會被類加載器加載到內存。

類加載器加載.class文件

《深入理解Java的虛擬機》中大概有這么一句話:在虛擬機遇到一條new的指令時,會去檢查一遍在靜態(tài)常量池中能否定位到一個類的符號引用 (就這個類的路徑+名字),并且檢查這個符號引用代表的類是否已被加載、解析和初始化過。如果不是第一次使用,那必須先執(zhí)行相應的類加載過程,這個過程由類加載器來完成。

類加載字面意思就可以理解成加載class文件,更準確點的說法就是會把class文件變成一個二進制流加載到內存中,即把類的描述信息加載到Metaspace,至于類加載器如何找到并把一個class文件轉成IO流加載到內存中,我后面會專門寫一篇關于類加載器的文章,這里就只要理解創(chuàng)建對象中有這么一步就行了。不過這里面有很重要的概念不得不講:Class對象

知識擴展:Class對象

劃重點,這是個非常重要的概念,理解它對于理解后面的反射和代理會有很大的幫助

類加載器 ClassLoader 加載class文件時,會把類里的一些數值常量、方法、類信息等加載到內存中,稱之為類的元數據,最終目的是為了生成一個Class對象用來描述類,這個對象會被保存在.class文件里,可能有新手看到這里會比較懵逼,class也有對象?

當然了,Class是個實實在在的類(用來描述類的類,比較拗口),有構造方法( private ,意味著可以生成對象,但不能手動生成,由JVM自動創(chuàng)建Class對象),類加載器會給每個java文件都創(chuàng)建一個Class對象,用來描述類,我畫個圖:

//以下操作只能由jvm完成,我們手動做不了
Class cls1 = new Class(Dog.class.getClassLoader());
Class cls2 = new Class(Cat.class.getClassLoader());
Class cls3 = new Class(People.class.getClassLoader());

這個Class對象除了描述對應的類之外還有什么作用呢?也可以生成對象,就是java的反射概念(通過Class實例獲取類信息) 上面說了,Class類是用來描述像People.Class類的類,那么它里面肯定包含了所有能夠描述該class的所有屬性,比如類名、方法、接口等,我們先到Class類源碼中瞄一眼:

這里面有個方法 newInstance(),即創(chuàng)建對象, 我把源代碼貼出來并簡單解析下:

@CallerSensitive
public T newInstance()
    throws InstantiationException, IllegalAccessException
    {

        if (System.getSecurityManager() != null) {
            checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), false);
        }

        if (cachedConstructor == null) {
            if (this == Class.class) {
                throw new IllegalAccessException(
                    "Can not call newInstance() on the Class for java.lang.Class"
                );
            }
            try {
                Class<?>[] empty = {};
                //聲明無參構造對象
                final Constructor<T> c = getConstructor0(empty, Member.DECLARED);
                // Disable accessibility checks on the constructor
                // since we have to do the security check here anyway
                // (the stack depth is wrong for the Constructor's
                // security check to work)
                java.security.AccessController.doPrivileged(
                    new java.security.PrivilegedAction<Void>() {
                        public Void run() {
                                c.setAccessible(true);
                                return null;
                            }
                        });
                cachedConstructor = c;
            } catch (NoSuchMethodException e) {
             //如果class中沒有無參構造方法,那么拋InstantiationException錯誤
                throw (InstantiationException)
                    new InstantiationException(getName()).initCause(e);
            }
        }
        Constructor<T> tmpConstructor = cachedConstructor;
        // Security check (same as in java.lang.reflect.Constructor)
        int modifiers = tmpConstructor.getModifiers();
        if (!Reflection.quickCheckMemberAccess(this, modifiers)) {
            Class<?> caller = Reflection.getCallerClass();
            if (newInstanceCallerCache != caller) {
                Reflection.ensureMemberAccess(caller, this, null, modifiers);
                newInstanceCallerCache = caller;
            }
        }
        // Run constructor
        try {
         //最終還是調用了無參構造器對象的newInstance方法
            return tmpConstructor.newInstance((Object[])null);
        } catch (InvocationTargetException e) {
            Unsafe.getUnsafe().throwException(e.getTargetException());
            // Not reached
            return null;
        }
    }

首先搞清楚 newInstance 兩種方法區(qū)別:

Class.newInstance()只能夠調用無參的構造函數,即默認的構造函數,我們在Class源碼里也看到了其實最終還是調用了無參構造器對象 ConstructornewInstance 方法,舉個栗子:Dog.class 中是沒有無參構造方法,那么會直接拋出 InstantiationException 異常:

//Dog類中只有一個dog_name的有參構造方法
Class c = Class.forName("com.service.ClassAnalysis.Dog");
Dog dog = (Dog) c.newInstance();//直接拋InstantiationException異常

Constructor.newInstance()可以根據傳入的參數,調用任意構造構造函數,也可以反射私有構造器(了解就行)

//Dog類中只有一個dog_name的有參構造方法
Constructor cs = Dog.class.getConstructor(String.class);
Dog dog = (Dog) cs.newInstance("小黑");//執(zhí)行沒有問題

但是這里面的 newInstance跟我們這次要說的 new 方法存在區(qū)別,兩者創(chuàng)建對象的方式不同,創(chuàng)建條件也不同:

  • 使用 newInstance 時必須要保證這類已經加載并且已經建立連接,就是已經被類記載器加載完畢,而 new 不需要
  • class對象的 newInstance 方法只能用無參構造,上面已經提到了,而 new 不需要
  • 前者使用的是類加載機制,是一種方法,后者是創(chuàng)建一個新類,一種關鍵字

這個不能說newInstance 不方便,相反它在反射、工廠設計模式、代理中發(fā)揮了重要作用,后續(xù)我也會寫下代理和反射,因為理解起來確實有點繞。還有一點需要注意,不管以哪種方式創(chuàng)建對象,對應的Class對象都是同一個

Dog dog1 = new Dog("旺財");
Dog dog2 = new Dog("小黑");
Class c = Class.forName("com.service.classload.Dog");//為了測試,加了無參構造
Dog dog3 = (Dog) c.newInstance();
System.out.println(dog1.getClass() == dog2.getClass());
System.out.println(dog1.getClass() == dog3.getClass());

連接和初始化

在此階段首先為靜態(tài)static變量內存中分配存儲空間,設立初始值(還未被初始化)比如:

public static int i = 666;//被類加載器加載到內存時會執(zhí)行,賦予一個初始值
public static Integer ii = new Integer(666);//也被賦值一個初始值

但請注意,實際上i 的初始值是0,不是666,其他基本數據類型比如boolean的初始值就是false,以此類推。如果是引用類型的成員變量 ii 那么初始值就是null。

Dog dog = new Dog("旺財");//在這里打個斷點

執(zhí)行,首先會執(zhí)行靜態(tài)成員變量初始化,默認值是0:

但有例外,如果加上了 final 修飾詞那么初始值就是設定的值。

接著對已經分配存儲空間的靜態(tài)變量真正賦值,比如為上面的dog_max_age 賦值16,還有執(zhí)行靜態(tài)代碼塊,也就是類似下面的代碼:

static {
    System.out.println("狗狗的靜態(tài)代碼塊");
}

到這為止,類的加載過程才算完成。

創(chuàng)建實例

在加載類完畢后,對象的所需大小根據類信息就可以確認了,具體創(chuàng)建的步驟如下:

  • 先給對象分配內存(包括本類和父類的所有實例變量,不包括上面的靜態(tài)變量),并設置默認值,如果有引用對象那么會在棧內存中申請一個空間用來指向的實際對象。
  • 執(zhí)行初始化代碼實例化,先初始化父類再初始化子類,賦予給定值(尊重長輩是java的傳統(tǒng)美德)
  • 對象實例化完畢后如果存在引用對象的話還需要把第一步的棧對象指向到堆內存中的實際對象,這樣一個真正可用的對象才被創(chuàng)建出來。

說了這么多估計很多人都沒概念,懵逼狀態(tài)中,其實很簡單,我們只要記住new的創(chuàng)建對象就兩步:初始化和實例化,再給你們搞一張圖:可以簡單理解②③④為初始化⑤實例化(可惡,我這該死的責任感?。?/p>

本文不指望你能使勁弄懂java虛擬機底層加載創(chuàng)建對象的過程(其實有些步驟我都懶得講了,因為說出來也都非常理論化,沒多大意思),是想讓你知道對象的誕生過程有哪幾個重要概念參與了,弄懂這些概念比起單單知道對象創(chuàng)建的過程有意義的多:

  • 類加載器,可以找找網上的資料,蠻多的,這塊內容做個了解就行
  • Class類和Class對象的概念,請重點掌握,不然理解反射和動態(tài)代理很費勁,spring的源碼也會難以理解
  • 棧內存和堆內存以及對應的基本類型和引用類型,也很重要,爭取能基本理解

來源:blog.csdn.net/qq_16887951/article/details/115872678

近期熱文推薦:

1.1,000+ 道 Java面試題及答案整理(2022最新版)

2.勁爆!Java 協程要來了。。。

3.Spring Boot 2.x 教程,太全了!

4.別再寫滿屏的爆爆爆炸類了,試試裝飾器模式,這才是優(yōu)雅的方式??!

5.《Java開發(fā)手冊(嵩山版)》最新發(fā)布,速速下載!

覺得不錯,別忘了隨手點贊+轉發(fā)哦!

當前名稱:面試guan:在 Java 中 new 一個對象的流程是怎樣的?徹底被問懵了。。
文章鏈接:http://chinadenli.net/article14/dsoioge.html

成都網站建設公司_創(chuàng)新互聯,為您提供網站排名、微信公眾號、網站內鏈、自適應網站、網站建設、網站策劃

廣告

聲明:本網站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯

h5響應式網站建設