new的過程是怎樣的?看完這一篇就懂了


在現實世界中,找對象是一門學問,找對象不在於多而在於精

在計算機世界中,面向對象編程的關鍵在於能否靈活地運用類,如何設計出一個符合需求的對象也是也是值得學習和思考的。

那么,面向對象編程到底是什么?

面向對象編程中,肯定會涉及對象兩個概念。是什么?對象是什么,兩者有什么關系?

接下來就一 一地來解答這些疑惑吧

類和對象

  • 類,是指將相同屬性的東西放在放在一起,類是一個模板,能夠描述一類對象的狀態和行為
  • 而對象,就是實際存在某個類中的一個個的個體,所以也被稱為實例(instance)。
  • 對象的抽象是類,類的具體化就是對象,也就是類的實例就是對象。

在C語言中,結構體是數據的集合,它將數據捆綁在一起,使得我們可以將這些數據看作是一個整體。而對結構體中的數據

進行操作的函數就寫在結構體的外部。

而在面向對象編程中,將表示事物行為的函數也放入了這個整體,就形成了對象的概念。使得這個整體既描述屬性,又能描述行為。

所以,面向對象編程是一種將關注點置於對象Object本身的程序設計方法,對象的構成要素包含對象的行為及操作,以此基礎進行編程。

這種方法使得程序易於復用。OOP主要使用的編程技巧有:繼承封裝多態三種。

說了這么多,是不是看暈了,沒關系,繼續往下看。

現實世界中的抽象

在現實生活中,可以將人看成一個類,這類稱為人類(抽象類)

如果某個男孩想找一個對象(女朋友),那么所有的女孩都是這個男孩選女朋友的范圍,所有的女孩就是一【類】

  • 對象

如果這個時候男孩已經找到喜歡的對象了,他的女朋友名字叫【林允兒】。那么假設這個名字是唯一的,此時名字叫【林允兒】的這個女孩就是一個對象(小聲bb,其實{她是我老婆|hē hē hē hē}狗頭)

接下來通過具體的代碼來講解一下

// FileName: Person.java
public abstract class Person {

	protected int age;
	protected String name;
	
	public Person(int age,String name) {
		
		this.age = age;
		this.name = name;
		
	}

	public abstract void speak();

	public abstract void sayInfo(Person person);

}

在這里,定義了一個抽象類-人,在人的這個抽象類里面,包含了人的一些屬性行為,代表了人具有的共同屬性

然后,定義了一個子類Man子類Woman繼承父類Person,里面包含了共同擁有的屬性,並增加了性別sex,然后對方法進行了重寫

// FileName: Man.java
public class Man extends Person {
	private String sex = "man";
	public Man(int age,String name) {
		super(age,name);
	}
  
	@Override
  
	public void speak() {
		System.out.println("我的名字是: " + super.name + "\n" + "我今年" + super.age + "歲了.");
	};

	@Override	
	
	public void sayInfo(Person person) {
		System.out.println("我的女朋友是:" + person.name + "\n" + "她今年" + person.age + "歲了.");
	}
}

// FileName: WoMan.java
public class WoMan extends Person {
	private String sex = "woman";
	public WoMan(int age,String name) {
		super(age,name);
	}
  
	@Override
  
	public void speak() {
		System.out.println("我的名字是: " + super.name + "\n" + "我今年" + super.age + "歲了.");
	};

	@Override	
	
	public void sayInfo(Person person) {
		System.out.println("我的男朋友是:" + person.name + "\n" + "她今年" + person.age + "歲了.");
	}
}

那么,他們的關系就是如下圖:

所以,一個類的基本組成為下圖:

那么,我們再來編寫下測試代碼:

// FileName: TestPerson.java
public class TestPerson {
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Person codevald = new Man(21,"codevald");
		Person lye = new Woman(20,"linyuer");
    
		codevald.speak();
		codevald.sayInfo(lye);
    
		lye.speak();
		lye.sayInfo(codevald);
    
	  }
}

運行結果:

接下來,是最有意思的部分,我們來分析下代碼中new的時候發生了什么。

new操作的過程

當我們new一個對象的時候JVM首先會去找到對應的類元信息,如果找不到意味着類信息還沒有被加載,所以在對象創建的時候也可能會觸發類的加載操作。當類元信息被加載之后,我們就可以通過類元信息來確定對象信息和需要申請的內存大小。

對象創建的流程

1.構建對象

首先main線程會在棧中申請一個屬於自己的棧空間,然后我們調用main方法的時候,會生成一個main方法的棧幀,然后執行new Man(),這里會根據Man類的元信息先確定對象的大小,然后在JVM堆里申請一塊內存區域並構建對象,同時對Man對象成員變量信息

並賦予默認值(在這里會涉及多態,在內存中的分配情況有機會再解釋解釋)。

2.初始化對象

這一步會執行對象內部的init方法,初始化成員變量值,即執行對象的構造方法(這里調用父類的構造方法進行賦值),構造方法執行完,此時的age = 21,name = "codevald"。

3.引用對象

對象實例化完成之后,再把棧中的Person對象引用地址指向Man對象在堆內存中的地址

4.繼續構造、初始化,引用對象對象

這一步和上面三個步驟一樣,就不詳細說了

附上圖

5.調用方法

調用speak()方法的時候,會先找到Person對象(codevald)中的引用地址,找到真正的在堆內存中的Man對象,執行speak()方法,執行的時候,會調用父類中的成員變量,所以會找到堆內存中的父對象的成員變量(name和age),加載進來,進行輸出

`我的名字是: codevald

我今年21歲了.`

調用sayInfo()方法的時候,一樣先找到對象(codevald)中的引用地址,找到真正的在堆內存中的Man對象,執行sayInfo()方法,執行的時候,會找到Person對象(linyuner)的地址,即指向堆內存中的Woman對象,找到父類里面的成員變量(name和age),進行輸出

`我的女朋友是:linyuer

她今年20歲了.`

下面的代碼的執行過程同理,就留給小伙伴們自行去分析啦~

附上TestPerson的字節碼文件,感興趣的小伙伴可以自行查看進行分析

Compiled from "TestPerson.java"
public class person.TestPerson {
  public person.TestPerson();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #7                  // class person/Man
       3: dup
       4: bipush        21
       6: ldc           #9                  // String codevald
       8: invokespecial #11                 // Method person/Man."<init>":(ILjava/lang/String;)V
      11: astore_1
      12: new           #14                 // class person/Woman
      15: dup
      16: bipush        20
      18: ldc           #16                 // String linyuer
      20: invokespecial #18                 // Method person/Woman."<init>":(ILjava/lang/String;)V
      23: astore_2
      24: aload_1
      25: invokevirtual #19                 // Method person/Person.speak:()V
      28: aload_1
      29: aload_2
      30: invokevirtual #24                 // Method person/Person.sayInfo:(Lperson/Person;)V
      33: aload_2
      34: invokevirtual #19                 // Method person/Person.speak:()V
      37: aload_2
      38: aload_1
      39: invokevirtual #24                 // Method person/Person.sayInfo:(Lperson/Person;)V
      42: return
}

學無止境,我們曾經擅長的正在被淘汰,不擅長的卻是仍然存在。最基礎的往往是最難的,而往往也是最重要的,平時注重基礎的積累,學會去分析底層的執行過程,才是學習中最應該掌握的技能,希望這篇回答能幫到正在尋找這個問題的答案的你~

如果覺得這篇文章不錯的話,記得幫我@codevald點個贊喲,感謝您的支持!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM