自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

盤點 Java 創(chuàng)建對象的 x 操作

開發(fā) 后端
我們日常生活中會創(chuàng)建很多對象,但是這個對象和你理解的那么對象不一樣,因為作者不是女媧,不能造人。作者只是程序員,他只能在 Java 中創(chuàng)建對象。

[[388017]]

我們日常生活中會創(chuàng)建很多對象,但是這個對象和你理解的那么對象不一樣,因為作者不是女媧,不能造人。作者只是程序員,他只能在 Java 中創(chuàng)建對象。

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

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

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

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

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

使用 new 來創(chuàng)建對象是最簡單的一種方式了,new 是 Java 中的關鍵字,new 通過為新對象分配內存并返回對該內存的引用來實例化一個類,這個實例化一個類其實就相當于創(chuàng)建了一個對象,因為類也是一種對象;new 也負責調用對象的構造函數(shù),下面是使用 new 來創(chuàng)建對象的代碼

  1. Object obj = new Object(); 

這段代碼中,我們在堆區(qū)域中分配了一塊內存,然后把 obj 對象指向了這塊內存區(qū)域。

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

 

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

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

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

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

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

 

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

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

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

 

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

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

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

這個newInstance 方法指的是 class 類中的方法,newInstance 方法會調用無參的構造方法創(chuàng)建對象。

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

  1. User user = (User)Class.forName("com.cxuan.test.User").newInstance(); 
  2.  
  3. // 或者使用 
  4.  
  5. User user = User.class.newInstance(); 

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

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

 

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

 

可以看到,我們驗證的是正確的。

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

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

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

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

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

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

 

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

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

  1. User.class.getDeclaredField("id"); 

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

 

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

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

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

如下代碼所示

  1. Constructor<User> constructor = User.class.getConstructor(); 
  2. User user = constructor.newInstance(); 
  3. user.setName("cxuan"); 
  4.  
  5. User user2 = (User)user.clone(); 
  6. System.out.println(user2.getName()); 

輸出 cxuan

它的字節(jié)碼如下

 

這個字節(jié)碼有些長,但是字節(jié)碼的概念和含義我們上面已經介紹過了,最主要的就是推入堆棧,調用對應的實例方法。

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

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

當我們使用序列化和反序列化時,JVM 也會幫我們創(chuàng)建一個單獨的對象。在反序列化時,JVM 創(chuàng)建對象不會調用任何構造函數(shù),如下代碼所示

  1. ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("xxx")); 
  2. out.writeObject(user2); 
  3. out.close(); 
  4. //Deserialization 
  5. ObjectInputStream in = new ObjectInputStream(new FileInputStream("xxx")); 
  6. User user3 = (Userin.readObject(); 
  7. in.close(); 
  8. user3.setName("cxuan003"); 
  9. System.out.println(user3 + ", hashcode : " + user3.hashCode()); 

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

本文轉載自微信公眾號「Java建設者」,可以通過以下二維碼關注。轉載本文請聯(lián)系Java建設者公眾號。

 

責任編輯:武曉燕 來源: Java建設者
相關推薦

2009-12-21 17:35:24

ADO.NET對象

2011-04-15 17:07:13

Java

2021-08-19 10:30:13

Java集合排序程序開發(fā)

2012-01-13 12:57:48

Java

2021-10-09 07:10:31

JavaScript對象Python

2017-02-27 11:48:58

JVM源碼分析Java

2021-06-29 07:04:16

Sed常用操作

2013-05-27 15:38:37

Java對象C++

2022-01-15 10:01:15

Javascript 高階函數(shù)前端

2012-12-26 09:41:13

2021-09-14 07:26:25

JavaScript迭代對象

2021-09-03 10:00:00

JavaScript迭代對象

2025-03-26 10:56:54

2024-11-14 13:15:22

JavaScript可迭代數(shù)組

2010-04-19 17:39:50

Unix操作系統(tǒng)

2013-12-27 09:51:02

Android移動操作系統(tǒng)

2021-03-18 09:06:17

JavaScriptPythonPyExecJS

2024-04-30 10:08:00

Lombok開發(fā)字段

2019-07-24 08:34:35

Java對象數(shù)據(jù)結構

2025-03-25 10:49:13

點贊
收藏

51CTO技術棧公眾號