<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          盤點(diǎn) Java 創(chuàng)建對(duì)象的 x 操作

          共 3908字,需瀏覽 8分鐘

           ·

          2021-03-17 07:50

          我們?nèi)粘I钪袝?huì)創(chuàng)建很多對(duì)象,但是這個(gè)對(duì)象和你理解的那么對(duì)象不一樣,因?yàn)樽髡卟皇桥畫z,不能造人。作者只是程序員,他只能在 Java 中創(chuàng)建對(duì)象。

          那么我問你一個(gè)問題,你知道 Java 中如何創(chuàng)建對(duì)象嗎?

          這個(gè)問題仿佛是給 Java 新手來寫的,好像有點(diǎn)瞧不起在座各位的樣子,嗯。。。那么我換種問法好了,畢竟看我公眾號(hào)的人都是將來月入 10w 的大佬。

          你知道 Java 中有哪幾種創(chuàng)建對(duì)象的方式嗎?

          誒?這個(gè)問題有點(diǎn)意思,平常我們用的最多的就是使用 new 來創(chuàng)建對(duì)象了,這是第一種方式;如果我們使用框架的話就直接交給 Spring 去管理就好了,Spring 底層是使用反射來創(chuàng)建對(duì)象的,這是第二種方式;然后。。。。。。。有點(diǎn)想不起來了,不要急,這篇文章就幫你回顧下。

          使用 new 來創(chuàng)建對(duì)象

          使用 new 來創(chuàng)建對(duì)象是最簡(jiǎn)單的一種方式了,new 是 Java 中的關(guān)鍵字,new 通過為新對(duì)象分配內(nèi)存并返回對(duì)該內(nèi)存的引用來實(shí)例化一個(gè)類,這個(gè)實(shí)例化一個(gè)類其實(shí)就相當(dāng)于創(chuàng)建了一個(gè)對(duì)象,因?yàn)轭愐彩且环N對(duì)象;new 也負(fù)責(zé)調(diào)用對(duì)象的構(gòu)造函數(shù),下面是使用 new 來創(chuàng)建對(duì)象的代碼

          Object obj = new Object();

          這段代碼中,我們?cè)诙褏^(qū)域中分配了一塊內(nèi)存,然后把 obj 對(duì)象指向了這塊內(nèi)存區(qū)域。

          不知道你有沒有看過 new 的字節(jié)碼呢?下面是這段代碼的字節(jié)碼

          在 Java 中,我們認(rèn)為創(chuàng)建一個(gè)對(duì)象就是調(diào)用其構(gòu)造方法,所以我們使用 new Object() 構(gòu)造的對(duì)象,其實(shí)是調(diào)用了 Object 類的無參數(shù) 的構(gòu)造方法。但是通過字節(jié)碼我們發(fā)現(xiàn),對(duì)象的創(chuàng)建和調(diào)用其構(gòu)造方法是分開的。

          字節(jié)碼的 new 表示在堆中創(chuàng)建一個(gè)對(duì)象,并把對(duì)象的引用推入棧中。invokespecial 表示調(diào)用對(duì)象無參數(shù)的構(gòu)造方法。其實(shí),JVM 提供了五種方法調(diào)用指令,分別是

          • invokestatic:該指令用于調(diào)用靜態(tài)方法,即使用 static 關(guān)鍵字修飾的方法;
          • invokespecial:該指令用于三種場(chǎng)景:調(diào)用實(shí)例構(gòu)造方法,調(diào)用私有方法(即 private 關(guān)鍵字修飾的方法)和父類方法(即 super 關(guān)鍵字調(diào)用的方法);
          • invokeinterface:該指令用于調(diào)用接口方法,在運(yùn)行時(shí)再確定一個(gè)實(shí)現(xiàn)此接口的對(duì)象;
          • invokevirtual:該指令用于調(diào)用虛方法(就是除了上述三種情況之外的方法);
          • invokedynamic:在運(yùn)行時(shí)動(dòng)態(tài)解析出調(diào)用點(diǎn)限定符所引用的方法之后,調(diào)用該方法;在 JDK 1.7 中提出,主要用于支持 JVM 上的動(dòng)態(tài)腳本語言(如 Groovy,Jython 等)

          好了,現(xiàn)在你知道了 new 和 invokespecial 是干啥用的,那么 dup 指令呢?

          dup 會(huì)復(fù)制棧上的最后一個(gè)元素,然后再次將其推入棧;因此,如果在棧上有一個(gè)對(duì)象引用,并且調(diào)用了 dup,則現(xiàn)在在棧上有對(duì)該對(duì)象的兩個(gè)引用??雌饋碛悬c(diǎn)不知其所以然,所以在求助網(wǎng)上的時(shí)候,又發(fā)現(xiàn)了 R 大的解釋

          來源:https://www.zhihu.com/question/52749416

          后面的 astore 就會(huì)把操作數(shù)棧頂?shù)哪莻€(gè)引用消耗掉,保存到指定的局部變量去。

          如果直接使用 new Object() 沒有創(chuàng)建局部變量的話,請(qǐng)注意一下它的字節(jié)碼。

          看出來細(xì)微的差別了嗎?上圖中的 astore_1 竟然變成了 pop,這也就是說,new Object() 沒有保存對(duì)象的局部變量,而是直接把它給消耗掉了。嗯,符合預(yù)期。

          所以這是第一種創(chuàng)建的方式,也就是使用 new 來創(chuàng)建。

          使用 newInstance 方法來創(chuàng)建

          這個(gè)newInstance 方法指的是 class 類中的方法,newInstance 方法會(huì)調(diào)用無參的構(gòu)造方法創(chuàng)建對(duì)象。

          我們可以使用 newInstance 方法創(chuàng)建對(duì)象,下面是使用示例代碼

          User user = (User)Class.forName("com.cxuan.test.User").newInstance();

          // 或者使用

          User user = User.class.newInstance();

          下面我們分析一下這個(gè)字節(jié)碼,其實(shí)使用第一種方式和第二種方式就差了一個(gè) Class.forName 的字節(jié)碼,這是一個(gè)靜態(tài)方法,應(yīng)該用的是 invokestatic,下面我們驗(yàn)證一下。

          第一種方式的字節(jié)碼

          第二種方式的字節(jié)碼

          可以看到,我們驗(yàn)證的是正確的。

          那么這段字節(jié)碼是什么意思呢?

          ldc 的意思是把常量池中的引用推入到當(dāng)前堆棧中,invokestatic 和 invokevirtual 我們上面解釋過了,然后就是 checkcast, 這個(gè)字節(jié)碼的含義就是進(jìn)行類型轉(zhuǎn)換,因?yàn)?newInstance 生成的是一個(gè) Object 的對(duì)象,所以我們需要把它轉(zhuǎn)換為我們需要的 User 類型,這個(gè)字節(jié)碼就是干這個(gè)活的。

          使用反射來創(chuàng)建對(duì)象

          使用反射來創(chuàng)建對(duì)象其實(shí)也是使用了 newInstance 方法,只不過這個(gè)方法是 Constructor ,Java 反射中構(gòu)造器的方法,我們可以通過這種方式來創(chuàng)建一個(gè)新的對(duì)象。如下代碼所示

          Constructor<User> constructor = User.class.getConstructor();
          User user = constructor.newInstance();

          下面是它的字節(jié)碼

          這里解釋下 iconst_0 ,它的意思就是將 int 值 0 加載到堆棧上,這個(gè)相當(dāng)于是為 getConstructor 方法準(zhǔn)備參數(shù)分配的字節(jié)碼。

          為了驗(yàn)證這個(gè)結(jié)論,我們從簡(jiǎn)優(yōu)化,看一下其他方法的字節(jié)碼

          User.class.getDeclaredField("id");

          它的字節(jié)碼如下:

          可以看到,第二個(gè) ldc 其實(shí)就是 getDeclaredField 中的參數(shù),為 String 類型,所以是用的 ldc,它是將引用推入堆棧。

          使用對(duì)象克隆來創(chuàng)建對(duì)象

          這是第四種創(chuàng)建方式,使用 Cloneable 類中的 clone() 方法來創(chuàng)建,它的前提是你需要實(shí)現(xiàn) Cloneable 接口并實(shí)現(xiàn)其定義的 clone 方法。用 clone 方法創(chuàng)建對(duì)象并不會(huì)調(diào)用任何構(gòu)造函數(shù)。

          如下代碼所示

          Constructor<User> constructor = User.class.getConstructor();
          User user = constructor.newInstance();
          user.setName("cxuan");

          User user2 = (User)user.clone();
          System.out.println(user2.getName());

          輸出 cxuan

          它的字節(jié)碼如下

          這個(gè)字節(jié)碼有些長(zhǎng),但是字節(jié)碼的概念和含義我們上面已經(jīng)介紹過了,最主要的就是推入堆棧,調(diào)用對(duì)應(yīng)的實(shí)例方法。

          對(duì)象克隆這塊是面試官非常喜歡考的一個(gè)點(diǎn),我后面會(huì)解析一下淺拷貝和深拷貝的區(qū)別。

          使用反序列化創(chuàng)建對(duì)象

          當(dāng)我們使用序列化和反序列化時(shí),JVM 也會(huì)幫我們創(chuàng)建一個(gè)單獨(dú)的對(duì)象。在反序列化時(shí),JVM 創(chuàng)建對(duì)象不會(huì)調(diào)用任何構(gòu)造函數(shù),如下代碼所示

          ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("xxx"));
          out.writeObject(user2);
          out.close();
          //Deserialization
          ObjectInputStream in = new ObjectInputStream(new FileInputStream("xxx"));
          User user3 = (User) in.readObject();
          in.close();
          user3.setName("cxuan003");
          System.out.println(user3 + ", hashcode : " + user3.hashCode());

          這段反編譯過后的字節(jié)碼文件比較長(zhǎng),我這里就先不貼出來了,讀者們可以自己編譯看一下,其實(shí)并沒有特別的字節(jié)碼指令,大部分我們上面已經(jīng)提到過了。

          最后

          這篇文章我大致通過字節(jié)碼和你聊了一下關(guān)于創(chuàng)建對(duì)象的幾種方式,如果這篇文章對(duì)你有幫助的話,還請(qǐng)各位觀眾姥爺們點(diǎn)贊、在看、轉(zhuǎn)發(fā)!


          往期精選

          騰訊九年,再見嘍!

          面試,真的刺激!

          炸裂!MySQL 82 張圖帶你飛!

          就這樣,我被禁足了?。?/a>

          千萬別再瞎招人了 (干貨)

          為什么要有 AtomicReference ?

          動(dòng)態(tài)代理竟然如此簡(jiǎn)單!

          另外,cxuan 肝了六本 PDF,公號(hào)回復(fù) cxuan ,領(lǐng)取作者全部 PDF 。


          瀏覽 55
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  色婷婷亚洲精品天天 | AV无码免费电影 | 91色区| 婷婷五月综合在线 | 成人开心五月天 |