在現實世界中,找對象是一門學問,找對象不在於多而在於精
在計算機世界中,面向對象編程的關鍵在於能否靈活地運用類,如何設計出一個符合需求的對象也是也是值得學習和思考的。
那么,面向對象編程到底是什么?
在面向對象編程中,肯定會涉及類和對象兩個概念。類是什么?對象是什么,兩者有什么關系?
接下來就一 一地來解答這些疑惑吧
類和對象
- 類,是指將相同屬性的東西放在放在一起,類是一個模板,能夠描述一類對象的狀態和行為
- 而對象,就是實際存在某個類中的一個個的個體,所以也被稱為實例(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點個贊喲,感謝您的支持!