1.初始面向對象
1.1 什么是面向對象
Java是一門純面向對象的編程語言(Object Oriented Program,簡稱OOP),在面向對象的世界里,一切皆為對象。面向對象是解決問題的一種思想,主要依靠對象之間的交換來完成一件事情
1.2 面向過程&面向對象的區別
這里以洗衣服為例:
1.手洗衣服過程
按照以上方式洗衣服,我們必須要親自完成每一個階段,其中每個階段的處理方式也要我們自己來把握,比如:加水量,洗衣粉的量,搓衣服的力度等等。如果某個以后不洗衣服,改成洗鞋子,那么以上的流程就不一樣了。按照這種方式來寫代碼,將來對代碼進行擴展和維護就比較困難
2.現在洗衣服的過程
現在洗衣服會經常用到洗衣機來完成,那么使用洗衣機來洗衣服一般涉及四個對象:人,衣服,洗衣機,洗衣粉
洗衣服的過程如下:
人把衣服放進洗衣機,加入洗衣粉,啟動洗衣機
以上整個過程中,主要是人,衣服,洗衣機,洗衣粉這四個對象在交互。人不需要關注洗衣機是怎么洗衣服的,人只需要把衣服放進去,加洗衣粉,然后啟動開關就行了。
換言之,人只需要面向洗衣機這個對象,就能完成洗衣服,而不需要面向洗衣服的各個過程,這就是面向對象和面向過程的區別
2.類的定義和使用
面向對象編程關注的是對象,對象就相當于是生活中的實物,比如:洗衣機。但是計算機不知道什么是洗衣機,這需要我們開發人員來告訴計算機什么是洗衣機
上圖左邊的信息就是對洗衣機的描述,我們可以用這種描述信息來表示一個具體的實物,這個過程稱為抽象。所謂抽象,指的是從具體事物中抽取共同的、本質的特征,忽略次要的、非本質的特征。但是這些簡化的抽象結果(圖片左側的描述信息)也不能被計算機識別,開發人員就需要使用某種面向對象的編程語言來進行描述,比如:Java
2.1 簡單認識類
**類是對一個實體(對象)來進行描述的。**主要描述該實體(對象)有哪些屬性,有哪些功能,描述完成后計算機就可以識別了。以上述洗衣機為例,我們使用左邊的描述信息來抽象一臺洗衣機,但是計算機并不能識別這些描述信息,我們可以將這些信息放進一個類里面,然后計算機就可以識別了
2.2 類的定義格式
定義類的具體語法如下:
field; //屬性或者成員變量method; //行為或者成員方法 } ```
class
是定義類使用的關鍵字,ClassName是類的名字,{}中是類的主體,類的主體中定義了類有哪些屬性,有哪些方法
下面我們來定義一個類來描述一臺洗衣機:
public class WashingMachine {//品牌public String brand;//型號public String model;//大小public Double size;//重量public Double weight;//價格public Double price;//洗衣服public void wash() {System.out.println("Washing clothes...");}//脫水public void dehydrate() {System.out.println("Dehydrating clothes...");}
}
上述過程中,我們使用Java語言定義了一個洗衣機類,通過javac編譯后轉換為字節碼文件,再由JVM翻譯就可以被計算機識別了(Java文件如何被計算機識別,請看博文Java虛擬機——JVM(Java Virtual Machine)解析一)
注意1:
- 類名采用大駝峰形式。大駝峰:每個單詞的首字母大寫
- 類的成員變量/方法暫時使用public修飾。public是訪問限定修飾符之一,后面講到繼承時再介紹
- 類的成員變量/方法暫時不使用static修飾
下面再來定義兩個類來熟悉一下類的定義
(1)定義Dog類
class Dog{public String name;public int age;public String color;//public void eat(){System.out.println("eat food");}public void bark(){System.out.println("汪汪汪...");}
}
(2)定義學生類
class Student{public String name;public int age;public int id;public double score;public String height;public String weight;//public void exam(){System.out.println("參加考試");}public void doHomework(){System.out.println("寫作業");}
}
注意2:
- 一個Java文件建議定義一個類
- 使用public修飾的類在一個Java文件中只能存在一個
- 使用public修飾的類的類名和所在文件的文件名相同
3.類的實例化
3.1 什么是實例化?
在上述代碼中,我們定義了WashingMachine、Dog和Student類,就相當于在計算機中定義了這三個新的數據類型。和int、float等基本數據類型一樣,只不過int和float是Java內置的2類型,而WashingMachine這些是用戶自定義的類型,我們通過這些自定義類型就可以定義實例/對象。而通過類來定義實例/對象的過程,稱之為實例化,在Java中需要借助new關鍵字來完成實例化
3.2 如何訪問對象中的成員變量/方法?
public static void main(String[] args) {Dog dog = new Dog();dog.name = "Dog";System.out.println(dog.name);dog.age = 18;System.out.println(dog.age);dog.color = "blue";System.out.println(dog.color);dog.eat();dog.bark();} } ```
運行結果如下:
注意:
- Java中使用
.
來訪問對象中的成員變量/方法 - 同一個類可以實例化多個對象
3.3 類和對象的關系
類
是一種抽象的數據類型,它可以實例化一組具有相同屬性(成員變量)和行為(成員方法)的對象。類可以看作是一個藍圖或者模板,用于創建對象
對象
是類的具體實現,每個對象都可以給自己的屬性(成員變量)賦予獨特的值
房屋和具體的房子
- 類(Class):房屋(House)
屬性(Attributes):房間數量、面積、樓層數、地址
方法(Methods):建造、入住、出售- 對象(Object):具體的房子,如“張三的家”
實例化(Instantiation):張三的家是一個具體的房子,它有5個房間,面積是150平方米,3層樓,地址是“北京市朝陽區XX路”
行為(Behavior):張三的家可以被建造、入住或出售
4.this關鍵字
4.1 為什么要使用this?
這里舉個例子
預期結果:2025-5-1
實際結果:null-null-null
原因分析:
public void setDate(String year, String month, String day) {year = year;month = month;day = day;}
這個方法中,形參的參數名和MyDate類的成員變量嗎名一致,當給year賦值時,編譯器無法區分year是成員變量還是方法的局部變量,在這種情況下會優先調用局部變量,相當于是自己給賦值
public void printDate(){System.out.println(year + "-" + month + "-" + day);}
這個方法中沒有形參,調用的參數(year/month/day)只能是MyDate類的成員變量,由于setDate方法沒有給成員變量賦值,所以printDate打印的時候才會打印出null-null-null。分析到這里就明白了,問題在于當方法的形參名和類的成員變量名一致時,該如何進行區分呢?這就要借助this關鍵字
4.2 this是什么?
this引用指向當前對象(成員方法運行時調用該成員方法的對象),在成員方法中所有成員變量的操作,都是通過該引用去訪問。只不過所有的操作對用戶是透明的,即用戶不需要來傳遞,編譯器自動完 成
通過代碼來解釋一下:
當編譯器去調用成員變量的時候都會通過this關鍵字來調用。this引用指向當前對象
,這句話又該怎么理解呢?以上面的代碼為例
這里的當前引用指的就是myDate。換言之,this = myDate。
public void setDate(String year1, String month1, String day1) {//this.year的含義是:myDate指向的對象中的year//this.year = year1;這段代碼的意思是:把 方法形參year1 賦值給 myDate指向的對象中的yearthis.year = year1;this.month = month1;this.day = day1;}
修改之后的代碼如下:
public class MyDate {public String year;public String month;public String day;//public void setDate(String year1, String month1, String day1) {//使用this關鍵字來區分哪個是成員變量,哪個是方法形參this.year = year1;this.month = month1;this.day = day1;}//public void printDate(){System.out.println(this.year + "-" + this.month + "-" + this.day);}//public static void main(String[] args) {MyDate myDate = new MyDate();myDate.setDate("2025","5","1");myDate.printDate();}
}
注意:
在剛學習Java的使用就提醒過,使用一個變量之前需要給該變量賦初值,否則會報錯
但是:
打印結果是null-null-null,并沒有報錯。這是怎么回事呢?
原因是,對于成員變量來說,如果沒有進行初始化,會有?個對應的默認值
String類型是引用類型,會賦予默認值null,所以不會報錯
默認值遵循如下規則:
數據類型 | 默認值 |
---|---|
byte | 0 |
short | 0 |
int | 0 |
long | 0 |
boolean | FALSE |
float | 0.0f |
double | 0 |
引用類型(reference) | null |
5.對象的構造及初始化
上文已經介紹到,成員變量在沒有賦初值的時候會賦予默認值,這就是對象的默認初始化。說實話,我個人認為這是防止程序報錯而出現的一個措施,不算正常的初始化操作,那么對象的初始化有哪些方式呢?
5.1 就地初始化
在聲明成員變量時,就直接給出了初始值
public class MyDate {public String year = "2025";public String month = "5";public String day = "1";//public void setDate(String year1, String month1, String day1) {this.year = year1;this.month = month1;this.day = day1;}//public void printDate(){System.out.println(this.year + "-" + this.month + "-" + this.day);}//public static void main(String[] args) {MyDate myDate = new MyDate();myDate.printDate();}
}
5.2 自定義方法初始化
用戶自定義一個方法來進行成員變量的初始化操作
public class MyDate {public String year;public String month;public String day;//public void setDate(String year1, String month1, String day1) {this.year = year1;this.month = month1;this.day = day1;}//public void printDate(){System.out.println(this.year + "-" + this.month + "-" + this.day);}//public static void main(String[] args) {MyDate myDate = new MyDate();myDate.setDate("2025","5","1");myDate.printDate();}
}
5.3 構造方法初始化
上面介紹的兩種初始化方法,說實話都不是很常見。就地初始化靈活性太差;自定義方法初始化還需要創建并調用自定義的方法,不太方便
那么,有沒有既靈活又方便的初始化方式呢?
那就是使用構造方法來初始化
5.3.1 什么是構造方法?
構造方法(也稱為構造器)是一個特殊的成員方法,方法名必須和類型一致,在創建對象時由編譯器自動調用,并且在整個對象的生命周期內構造方法只會調用一次
public class MyDate {public String year;public String month;public String day;//public MyDate(String year, String month, String day) {this.year = year;this.month = month;this.day = day;}//public void printDate(){System.out.println(this.year + "-" + this.month + "-" + this.day);}//public static void main(String[] args) {MyDate myDate = new MyDate("2025","5","1");myDate.printDate();}
}
在使用new關鍵字實例化對象的時候,編譯器會根據括號內的參數數量和類型來判斷調用哪個構造方法。這段代碼中,new對象的時候一共傳遞了三個String類型的參數,編譯器就會查找哪個構造方法的參數列表能與之對應
5.3.2 構造方法注意事項&使用規范
注意:
- 1.構造方法名必須和類名一致
- 2.沒有返回值,設置void也不行
- 3.創建對象時由編譯器自動調用,并且在對象的生命周期內只調用一次
- 4.構造方法可以重載(用戶根據自己的需求創建不同參數列表的構造方法)
- 一般來說,構造方法使用public修飾
注意6:
當用戶沒有手動添加構造方法的時候,編譯器會默認生成一份無參的構造方法。當用戶顯式地添加構造方法(不論有參還是無參)的時候,編譯器將不再默認生成構造方法
左圖中,用戶沒有顯式地添加構造方法,編譯器會默認生成一個無參的構造方法,當用戶需要實例化一個不賦初值地對象時,就能匹配到該無參構造方法;右圖中,用戶顯式地添加一個構造方法,參數列表有三個形參,當用戶需要實例化一個不賦初值地對象時,由于編譯器沒有默認生成一個無參的構造方法,導致匹配失敗。所以,構造方法也有一個不成文的使用規范:當用戶顯式地添加構造方法后,不論無參的構造方法有沒有用,都建議添加上
5.3.2 構造方法中使用this來簡化代碼
public class MyDate {public String year;public String month;public String day;//public MyDate() {this("2025","5","1");}public MyDate(String year, String month, String day) {this.year = year;this.month = month;this.day = day;}//public void printDate(){System.out.println(this.year + "-" + this.month + "-" + this.day);}
}
class Test{public static void main(String[] args) {MyDate myDate = new MyDate();myDate.printDate();}
}
在上述代碼中,new對象的時候會默認調用無參的構造方法,無參的構造方法內使用了this()代碼,編譯器就會根據括號內的參數數量和類型去查找參數列表與之匹配的構造方法
注意:
- this()必須是構造方法中的第一條語句
- this()不能形成環的調用