類與物件的初步認知
C語言是面向程序的,關注的是程序,分析出求解問題的步驟,通過函式呼叫逐步解決問題,
JAVA是基于面向物件的,關注的是物件,將一件事情拆分成不同的物件,靠物件之間的互動完成,
面向程序注重的是程序,在整個程序中所涉及的行為,就是功能,
面向物件注重的是物件,也就是參與程序所涉及到的主體,是通過邏輯將一個個功能實作連接起來
?
面向物件概念
1.面向物件是思考問題的一種思考方式,是一種思想,
2.類就是一類物件的統稱,物件就是這一類具體化的一個實體,
3.面向物件的好處:將復雜的事情變簡單了,只要面對一個物件就行,
?
面向物件設計】
面向物件設計把握一個重要的經驗:誰擁有資料,誰對外提供操作這些資料(私有)的方法!
(被動的一方是資料的擁有者,主動的一方是執行者)
開發時:找物件,建物件,用物件,并維護物件之間的關系,
簡而言之
面向物件就是用代碼(類)來描述客觀世界的事物的一種方式. 一個類主要包含一個事物的屬性和行為
圖 1

圖 2

通過 圖1 和 圖2 ,我們更加清楚 面向物件 與 面向程序的區別,
?
那么我們問題是︰物件從何而來,從類來,你可以想象成一個型別,假設我們都是人,額,就這樣吧,那么我們要定義一個類,這個類我們要定義出來,該怎么定義呢?
?
類和類的實體化
類就是一類物件的統稱,物件就是這一類具體化的一個實體
創建類 ( 基本語法 )
class <class_name>{
field;//成員屬性
method;//成員方法
}
實體化物件
<class_name> <物件名> = new <class_name>();
class為定義類的關鍵字,ClassName為類的名字,{}中為類的主體(類體),
類中的元素稱為:成員屬性(成員變數),類中的函式稱為:成員方法,
代碼如下
注意類的定義是寫在我們檔案類的外面(初識Java中,我們說了類名要和檔案相同,所以我寫成檔案類,方便你們區別)
class Person{
// 現在我們定義了 一個類 Person (注意類名要大駝峰結構,這個初識Java的時候就講過了)
// 類里面包含欄位(又稱 屬性 和 成員變數) 和 方法
// 欄位又稱屬性,屬性什么意思,比如人的屬性
public String name;// 每個人都有自己的名字
public int age; // 年齡
// 這兩個成員變數雖然可以賦值,但不推薦,跟前面 陣列文章中的不規則二維陣列是一樣的
// 可以 根據后期的需要, 進行賦值的,而且你總不能每個人的名字和年齡都一樣把,
// 注意 創建在 在類的內部,方法的外部的變數,被稱為 成員變數(屬性/欄位)
// 定義寫到方法的內部 是 區域變數
另外 成員變數 又可分為 普通成員變數(該程式的成員變數就是普通的) 和 靜態成員變數(static)
// 而方法(又稱 成員方法) 可以決議為 人的行為
// 比如說吃
public void eat(){
System.out.println(name + "正在吃飯,");
}
public void sleep(){
System.out.println(name + "睡著了,");
}
}
public class ClassAndObject {
public static void main(String[] args) {
// 我們經過圖1,得出的結論是 有了類才有物件
// 現在我們有了類,那么怎么產生物件的呢?
// 通過 new 去實體化一個物件,也就是說去產生一個物件
Person person = new Person();
// 注意此時的變數是由類定義的,由類定義的變數,都是參考變數,意味著初值可以賦 null
// Person person = null;
// 而且 前面也說,同一個類,我們可以產生很多物件
// 意味著 我們生成 很多人 person
Person person2 = new Person();
Person person3 = new Person();
Person person4 = new Person();
Person person5 = new Person();
// .
// .
// .
}
}
?
訪問 類的成員變數(通過參考來訪問)
基本語法
參考(由類定義的定義的類名).普通成員變數名
代碼實體1
class Person{
public String name;// 每個人都有自己的名字
public int age; // 年齡
public void eat(){
System.out.println(name + "正在吃飯,");
}
public void sleep(){
System.out.println(name + "睡著了,");
}
}
public class ClassAndObject {
public static void main(String[] args) {
Person person = new Person();
// 普通成員變數的訪問需要 通過物件 的參考(person)來訪問的
System.out.println(person.name);// name 是參考型別,存的是首字符地址
System.out.println(person.age);// int 整形
}// 圖 3
}// 圖4 , 圖中的默認值,是針對于 成員變數,不包括區域變數,而且區域變數不初始化是無法使用的
圖 3

圖4(成員變數和參考的默認值)

?
代碼實體2
class Person{
public String name;// 每個人都有自己的名字
public int age; // 年齡
public void eat(){
System.out.println(name + "正在吃飯,");
}
public void sleep(){
System.out.println(name + "睡著了,");
}
}
public class ClassAndObject {
public static void main(String[] args) {
Person person = new Person();
person.name = "author";
person.age =10;
System.out.println(person.name);
System.out.println(person.age);
Person person2 = new Person();
System.out.println(person2.name);// name 是參考型別,存的是首字符地址
System.out.println(person2.age);// int 整形
}// 圖 5
}// 圖解6
圖5

圖6

?
代碼實體3 (呼叫成員方法)
成員方法也和成員變數一樣,可分為 普通成員方法 和 靜態成員方法(static)
普通成員方法跟普通成員變數是一樣的,通過物件的參考來呼叫
class Person{
public String name;// 每個人都有自己的名字
public int age; // 年齡
public void eat(){
// eat 和 sleep 可以稱其為 方法片段,方法片段 是存盤在方法區中
// 如果方法里 創建了 int a 或者 其他型別的變數,在該方法被呼叫時,
// 方法會在堆疊上開辟記憶體(沒被呼叫,就放在方法區里)
// 那么方法的空間就會分出4byte來存盤它
System.out.println(name + "正在吃飯,");
}
public void sleep(){
System.out.println(name + "睡著了,");
}
}
public class ClassAndObject {
public static void main(String[] args) {
Person person = new Person();
person.name = "author";
person.age =10;
person.eat();
person.sleep();
}// 圖 7
}
圖 7

?
類的成員
類的成員可以包含以下:欄位、方法、代碼塊、內部類和介面等
本文主要講欄位、方法,代碼塊,
欄位 等價于 (屬性/成員變數)
成員變數又可分為兩類: 普通成員變數,靜態成員變數
代碼樣本1
class Person{
// 普通成員變數 都是屬于物件的
public String name;// 參考型別(存的字串首字符地址),默認值為null
public int age;// 默認值為 0
// 靜態成員變數
public static int count;// 默認值為 0
}
public class ClassAndObject {
public static void main(String[] args) {
Person person = new Person();
person.age = 10;// 訪問普通成員
person.count = 1;
System.out.println(person.age);
System.out.println(person.count);
}
}// 圖 8
圖8

?
代碼樣本2(訪問普通成員變數)
class Person{
// 普通成員變數 都是屬于物件的
public String name;// 參考型別(存的字串首字符地址),默認值為null
public int age;// 默認值為 0
}
public class ClassAndObject {
public static void main(String[] args) {
Person person = new Person();
person.age++;
System.out.println(person.age);
System.out.println("======================");
Person person2 = new Person();
person2.age++;
System.out.println(person2.age);
}
}// 圖 9,10
圖9

圖10

?
代碼樣本3(訪問靜態成員變數)
class Person{
// 普通成員變數 都是屬于物件的
public String name;// 參考型別(存的字串首字符地址),默認值為null
public int age;// 默認值為 0
public static int count;
}
public class ClassAndObject {
public static void main(String[] args) {
Person person = new Person();
person.age++;
person.count++;
System.out.println(person.age);
System.out.println(person.count);
System.out.println("======================");
Person person2 = new Person();
person2.age++;
person2.count++;
System.out.println(person2.age);
System.out.println(person2.count);
}
}// 圖 11,12
圖11

圖12

?
代碼樣本4(由圖12得出的結論,修改樣本3)
class Person{
// 普通成員變數 都是屬于物件的
public String name;// 參考型別(存的字串首字符地址),默認值為null
public int age;// 默認值為 0
public static int count;
}
public class ClassAndObject {
public static void main(String[] args) {
Person person = new Person();// 沒意義,靜態成員變數屬于類,所以實不實體化一個物件,沒有影響
Person.count++;
System.out.println(Person.count);
System.out.println("======================");
//Person person2 = new Person(); 沒意義
Person.count++;
System.out.println(Person.count);
}
}// 圖 13
圖13

?
代碼樣本5 (呼叫【靜態成員方法/類方法】)
class Person{
// 靜態成員方法/類方法
public static void staticFunc(){
System.out.println("static::func()");
}
}
public class ClassAndObject {
public static void main(String[] args) {
Person.staticFunc();
}
}// 圖 14
圖14

?
注意事項 1(能不能在方法中 創建一個 被 static修飾的變數)
class Person{
// 普通成員辦法
public void eat(){
static int size = 0;// 該寫法是錯的
}
// 靜態成員方法/類方法
public static void staticFunc(){
static int size2 = 0;// 該寫法是錯了
}
}// 圖 15
圖15

總結:
無論是 普通成員方法還是靜態成員方法,都不能在其內部創建一個被static修飾的變數
因為 被static修飾了的變數,該變數就屬于類了(類變數/靜態變數),
而你把一個(類變數/靜態成員變數)寫在方法里,就意味著屬于方法(是一個區域變數,不再是 類變數了),而不屬于類
所以沖突了,不合適,導致編譯器報錯,
還有一個原因
拿eat來說, eat是普通成員方法,是需要對應的參考來呼叫的,但是如果可以定義static的變數, 通過 類名Person 就能呼叫,
一個需要物件,不需要類;另一個需要類,不要有物件,兩者就像在這里卡bug一樣存在沖突,所以這種寫法是錯的
而靜態成員方法中,之所以不能創建一個被 static修飾的變數,是因為,你是在內部定義的,在內部定義,就意味著屬于方法,而不屬于類,所以錯的
總得來說: 只要是被 static 修飾的變數/方法,都是屬于類的,
?
注意事項 2(能不能在方法中呼叫方法)
普通成員方法 呼叫動態成員方法
class Person{
public String name = "author";
public void eat(){
staticFunc();// 可以這樣寫,沒有警告
System.out.println(name+"正在吃飯.");
}
public static void staticFunc(){
System.out.println("static::func()");
}
}
public class ClassAndObject {
public static void main(String[] args) {
Person p = new Person();
p.eat();// 圖 16
}
}由圖得知,普通成員方法 可以呼叫 動態成員方法 ,因為普通成員方法是依賴物件的,靜態成員方法 不依賴物件
所以 呼叫普通成員方法,new一個物件,對于靜態成員方法來說沒有任何影響,你用你的參考,我用我的類
圖16

&****
?
靜態成員方法/類方法 呼叫 普通成員方法
class Person{
public String name = "author";
public void eat(){
System.out.println(name+"正在吃飯.");
}
public static void staticFunc(){
eat();// error 圖17
System.out.println("static::func()");
}
}
圖17

由圖17得知,靜態成員方法 是無法呼叫 普通成員方法的
原因也很簡單,靜態成員方法不需要物件,而普通成員方法需要物件
靜態成員通過類名來呼叫,因此不需要new物件,那么 eat() 誰調?
沒有物件,不能調,所以編譯器會報錯,
當然硬要呼叫也行, 在 eat()前面 new一個物件,用該物件的參考 來呼叫 普通成員方法 eat()
總結
普通成員方法(有物件) 能呼叫 靜態成員方法(單身貴族)
而 靜態成員方法(單身貴族) 不能呼叫 普通成員方法(沒物件)
現在知道前面博客的一些例題,使用方法的時候,為什么都用static, 因為被static修飾的方法是可以直接呼叫的,不需要new物件
不是new物件麻煩,而是當時知識儲備有限,明白了吧,
?
這里講一個dan疼的面試題
main 函式為什么是靜態的?
首先 main 函式 是不是靜態都可以!!!
這個東西取決于 JVM 的規則,
main 之所以 是 靜態的,是因為 在設計 JVM的時候,就這么設計的
所以 main 只有被static 修飾的時候,JVM才認為它是main函式
舉個例子:
你去超市買東西,要付錢吧,你總不能拿東西不給錢,給警察抓吧?
買東西要給錢 和 在Java中 main方法需要 static 修飾 是一樣的
是默認的規則,是你必須要遵循的, 當然 你非要 不使用用 static修飾main函式
沒關系,問題就在于怎么new一個物件 輔助 編譯器 在編譯器 進入main函式,
而且,還沒考慮 main函式 是程式運行一開始就要進入,因為main函式是程式的入口
想想就麻煩,我是做不出來,
總之做一個安分守己的好公民,買東西要給錢;寫main方法 使用 static來修飾
?
在這里們再穩固一下知識
class Person{
public String name = "author";
public void eat(){
staticFunc();
System.out.println(name+"正在吃飯.");
}
public static void staticFunc(){
System.out.println("static::func()");
}
}
public class ClassAndObject {
public static void main(String[] args) {
Person p = null;// 這個參考 不指向 任何的物件
// 兩個參考指向同一個物件
Person p2 = new Person();
Person p3 = p2;// p3 這個參考 指向 p2 指向的物件
// 一個參考能不能指向多個物件?
Person person = new Person();
person = new Person();
person = new Person();// 這個是person真正指向的物件
// 答案是不能,person 此時指向的物件是最后一次 new的物件
}
}
?
參考 一定是在堆疊上嗎?
答案是 不是
代碼實體
class Person{
public String name = "author";
public void eat(){
staticFunc();
System.out.println(name+"正在吃飯.");
}
public static void staticFunc(){
System.out.println("static::func()");
}
}
public class ClassAndObject {
Person person = new Person();
public static void main(String[] args) {
ClassAndObject classAndObject = new ClassAndObject();
}
}// 圖 18
圖18

?
總結 static 關鍵字
1、修飾屬性
修飾屬性,Java靜態屬性和類相關, 和具體的實體無關. 換句話說, 同一個類的不同實體共用同一個靜態屬性
2、修飾方法
如果在任何方法上應用 static 關鍵字,此方法稱為靜態方法,
靜態方法屬于類,而不屬于類的物件,
可以直接呼叫靜態方法,而無需創建類的實體,
靜態方法可以訪問靜態資料成員,并可以更改靜態資料成員的值,
注意事項1: 靜態方法和實體無關, 而是和類相關. 因此這導致了兩個情況:
1. 靜態方法不能直接使用非靜態資料成員或呼叫非靜態方法(非靜態資料成員和方法都是和實體相關的).
2. this和super兩個關鍵字不能在靜態背景關系中使用(this 是當前實體的參考, super是當前實體父類實體的參考, 也
是和當前實體相關).
注意事項2
1. 我們曾經寫的方法為了簡單(不用new物件), 都統一加上了 static. 但實際上一個方法具體要不要帶 static, 都需要是情形而定.
2. main 方法為 static 方法.
注意事項3
1, 被final(使其具有常量屬性)修飾的成員變數,與成員變數是不是存盤在方法區或者堆上是無關的
簡單來說: 一個物件存盤到哪里 和 你是否被final修飾無關
還是那句話,凡是被 static 修飾的成員變數或者成員方法(又稱靜態成員變數和靜態成員方法),都是存盤在方法區中
沒有被static修飾的成員變數 或者 方法(又稱:普通成員變數 和 普通成員方法)
都需要通過new物件,來物體化物件,通過指向物件的參考來呼叫我們普通成員變數和方法,
注意事項4
以后在作業中,遇到被static修飾的成員變數和方法,意味著只有成員方法和變數一份,不能輕易動,因為你不知誰使用了它,不知道它涉及范圍有多廣
能在不使用 static 的情況,完成程式的編程,就盡量不要使用,(就像C語言的全域變數一樣,多了不安全,牽扯太多,又不能隨便動)
反正以后只要遇到static修飾的,你就當祖宗供著就行了,
?
在這里大家想想如果我們的類體中,欄位/屬性/成員變數,越來越多, 如果我們想列印一下內容,要寫很多方法,很麻煩
先來看看下面的代碼,我們一步步來看
代碼樣本1
class Person{
public String name;
public int age;
public static int count;
public void eat(){
System.out.println(name+"正在吃放");
}
public void print(){
System.out.println("姓名"+name+"年齡"+age);
}
}
public class ClassAndObject {
public static void main(String[] args) {
Person person = new Person();
System.out.println(person);// 圖 19
}
}
圖19

?
程式結果圖解流程圖
點擊 println 》 Ctrl+點擊,進入println函式,得圖20
圖20

點擊 valueOf 》 Ctrl+點擊,進入valueOf函式,得圖21
圖21

點擊 toString 》 Ctrl+點擊,進入toString函式,得圖22
圖22

?
通過我們層層決議,我發現它最后是通過 toString 來轉換
那么我們可不可以 自己寫一個 toString 方法
答案是可以的
代碼樣本2
class Person{
public String name;
public int age;
public static int count;
public void eat(){
System.out.println(name+"正在吃放");
}
public void print(){
System.out.println("姓名"+name+"年齡"+age);
}
public String toString(){// 這里我們寫的toString 方法 ,回傳一個字串
return "author";
}
}
public class ClassAndObject {
public static void main(String[] args) {
Person person = new Person();
System.out.println(person);// 圖 23
}
}
圖23

由圖23得知,結果很有意思 輸出的是我們自己寫 toString 回傳值
這里涉及到 動態系結,這個后面講到相應的內容,我們再來講這個
你只需要知道,我們在列印資料時,如果我們寫了一個toString的方法
編譯器,就會執行我們所寫的 toString 方法,而不是系統默認的toString
執行我們所寫的 toString方法 的 前提是 toString 方法 的名字不能變,是定死了的,缺胳膊少腿 或者 畫蛇添足都是不行的
例如
public String toString1(){
return "author";
}
public String toStrin(){
return "author";
}
你可以仔細去看看圖23 , 我們的寫法是與編譯器默認的toString 寫法是相同的
?
也就是說我們可以不用呼叫方法,就能輸出一樣的效果
代碼樣本(列印成員變數的值)
class Person{
public String name;
public int age;
public static int count;
public void eat(){
System.out.println(name+"正在吃放");
}
public void print(){
System.out.println("姓名:"+name+" 年齡:"+age);
}
public String toString(){// 這里我們寫的toString 方法 ,回傳一個字串
return"姓名:"+name+" 年齡:"+age;
}
}
public class ClassAndObject {
public static void main(String[] args) {
Person person = new Person();
System.out.println(person);// 圖 24
}
}
圖24

?
代碼樣本1~3,只是來說明 println 函式 是通過 呼叫 toString 方法來實作的
?
有的人可能會說,這跟我們 通過物件去呼叫成員方法,有什么區別?
都是自己手敲的,
區別就在于 toString函式 可以 通過快捷鍵和滑鼠來讓編譯器自動生成
操作流程圖: 圖25 》 圖26 》 圖27 》 圖 28
圖25

圖26

圖27

圖28

代碼如下
class Person{
public String name;
public int age;
public static int count;
public void eat(){
System.out.println(name+"正在吃放");
}
public void print(){
System.out.println("姓名:"+name+" 年齡:"+age);
}
@Override// 這個類似一個檢查功能,檢查我們要重寫的東西,與原來的一不一樣
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}// 這個功能 涉及 我們后面的內容 "重寫", 這里暫時不講,有個概念就行
}
public class ClassAndObject {
public static void main(String[] args) {
Person person = new Person();
System.out.println(person);// 圖 29
}
}
圖29

?
總結:
toString 方法會在 println 的時候被自動呼叫.
將物件轉成字串這樣的操作我們稱為序列化(把一個物件轉換成字串). 反序列化(把字串轉換成物件)
toString 是 Object 類提供的方法, 我們自己創建的 Person 類默認繼承自 Object 類, 可以重寫 toString 方法
我們自己寫的轉換字串方法. (關于繼承和重寫這樣的概念, 我們后面會重點介紹).
@Override 在 Java 中稱為 “注解”, 此處的 @Override 表示下面實作的 toString 方法是重寫了父類的方法. 關于注解后面的課程會詳細介紹…
IDEA快速生成Object的toString方法快捷鍵:alt + f12 / 0 (insert)
?
但是現在有一個問題
上代碼
class Person{
// 如果有人 在某天,改變了 Person 類中 成員變數名
// name 》 myName
public String myName;
public int age;
public static int count;
// 然后就 只把 Person 類 中的 name 都改 myName
// 雖然這樣做 在自己的類中,不影響 person類中的程式運行
public void eat(){
System.out.println( myName+"正在吃放");
}
public void print(){
System.out.println("姓名:"+ myName+" 年齡:"+age);
}
@Override
public String toString() {
return "Person{" +
"name='" + myName + '\'' +
", age=" + age +
'}';
}
}
public class ClassAndObject {
public static void main(String[] args) {
Person person = new Person();
person.name = "bit";//圖30
// 但是 如果有其他類,在類體中呼叫 或者 訪問 該成員變數,就存在著問題
// 因為 別人呼叫語法: 參考.成員變數名, 而你改名了,別人不知道啊
// 所以 別人也要改,但是如果呼叫它的類有很多,那么一個個改名,就不現實
}
}
圖30

?
在這里引入一個概念 封裝
什么叫封裝?
<<代碼大全>> 開篇就在討論一個問題: 軟體開發的本質就是對程式復雜程度的管理. 如果一個軟體代碼復雜程
度太高, 那么就無法繼續維護. 如何管理復雜程度? 封裝就是最基本的方法.
在我們寫代碼的時候經常會涉及兩種角色: 類的實作者和類的呼叫者.
封裝的本質就是讓類的呼叫者不必太多的了解類的實作者是如何實作類的, 只要知道如何使用類就行了.
這樣就降低了類使用者的學習和使用成本, 從而降低了復雜程度
private實作封裝
private/ public 這兩個關鍵字表示 "訪問權限控制" .
被 public 修飾的成員變數或者成員方法, 可以直接被其他類的呼叫者使用.
被 private 修飾的成員變數或者成員方法, 不能被其他類的呼叫者使用.
換句話說, 類的使用者根本不需要知道, 也不需要關注一個類都有哪些 private 的成員. 從而讓類呼叫者以更低的成本來使用類.
?
代碼實體
class Person{
private String name;// 此時 name 被 private 所修飾(被private包裝了)
// 普通成員變數 name,非本類內部,不可呼叫
// 圖31,由圖得知 name已經不能 其他類的呼叫者所呼叫
public int age;
public static int count;
private void eat(){
System.out.println( name+"正在吃放");
} // 普通成員方法 eat,非本類內部,不可呼叫
// 圖31,由圖得知 eat已經不能 其他類的呼叫者所呼叫
public void print(){
System.out.println("姓名:"+ name+" 年齡:"+age);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class ClassAndObject {
public static void main(String[] args) {
Person person = new Person();
person.name = "bit";
person.age = 10;
person.eat();
}
}
圖31

總結
private 不光能修飾欄位, 也能修飾方法
通常情況下我們會把欄位設為 private 屬性, 但是方法是否需要設為 public,
就需要視具體情形而定. 一般我們希望一個類只提供 "必要的" public 方法, 而不應該是把所有的方法都無腦設為 public.
?
那么這樣寫的好出在哪?雖然說安全性很強,但是我要用里面的東西,怎么辦?
這時就需要 getter和setter方法 來創建公開的2個介面,用來輸出和輸入
代碼實體
class Person{
private String name;// null
public int age;// 0
public static int count; // 0
public String getName(){
// 讀取 Person 類中的 普通成員變數存盤的資料,將其回傳(輸出)
return name;
}
public void setName(String myName){
// 輸入 一個 字串 來修改 Person 類中 name 的 值
this.name = myName;
}
public void eat(){
System.out.println( name+"正在吃放");
}
public void print(){
System.out.println("姓名:"+ name+" 年齡:"+age);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class ClassAndObject {
public static void main(String[] args) {
Person person = new Person();
person.setName("author");
System.out.println(person.getName());
// 圖 32,由圖可知 賦值 和 讀值 成功了,沒問題
}
}
圖32

由 圖 32 ,得出結論 private 和 (setter,getter)結合使用 是沒問題的,成功解決了 類的實作者,被修改成員變數的名稱,而無法進行訪問的問題,
然而還是存在某種問題,如果你修改 setter 和 getter的方法名,一樣會出現問題,
但是一般人不會去改,人家要改你也防不住,
所以做人要厚道 不要去改, 這是默認的道德規則,
setter 和 getter 方法 和 前面 toString 一樣,有快捷的方法,讓編譯器自動為我們添加
跟 toString方法一樣的流程,仔細 觀察 圖 26 的選項中 有 Getter and Setter 選項
選擇它,之后就圖 27 一樣,自己去選擇成員變數 去生成對應 Getter and Setter 的方法
這里我就不再說明
?
當set方法的形參名字和類中的成員屬性的名字一樣的時候,如果不使用this, 相當于自賦值. this 表示當前實體的參考.
class Person{
private String name;// null
private int age;// 0
public static int count; // 0
public String getName(){
return name;
}
public void setName(String name){
name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void eat(){
System.out.println( name+"正在吃放");
}
public void print(){
System.out.println("姓名:"+ name+" 年齡:"+age);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class ClassAndObject {
public static void main(String[] args) {
Person person = new Person();
person.setName("author");
System.out.println(person.getName());
person.setAge(10);
System.out.println(person.getAge());
}
}// 圖 33
圖33

總結
getName 即為 getter 方法, 表示獲取這個成員的值.
setName 即為 setter 方法, 表示設定這個成員的值
當set方法的形參名字和類中的成員屬性的名字一樣的時候,如果不使用this, 相當于自賦值. this 表示當前實體的參考.
不是所有的欄位都一定要提供 setter / getter 方法, 而是要根據實際情況決定提供哪種方法.
?
構造方法
構造方法是一種特殊方法, 使用關鍵字new實體化新物件時會被自動呼叫, 用于完成初始化操作
?
語法規則
1.方法名稱必須與類名稱相同
2.構造方法沒有回傳值
3.每一個類中一定至少存在一個構造方法(沒有明確定義,則系統自動生成一個無參構造)
那么構造方法是干嘛的?在了解構造方法是干嘛的之前,需要了解一個物件的產生(也就是說物件的實體化/new的執行程序)
new 執行程序
1.為物件分配記憶體空間
2.呼叫合適的構造方法
當上面這兩步完成之后,我們的物件才真正產生了,(意味著呼叫完構造方法之后,我們的物件才真正產生了)
注意 合適 這兩個字,意味著構造方法不止一個,就好比鞋子很多,但要挑合適的
?
現在我們來寫一個構造方法,代碼如下
class Person{
private String name;
private int age;
public Person(){// 構造方法
System.out.println("Person()::不帶引數的構造方法");
}
}
public class ClassAndObject {
public static void main(String[] args) {
// 前面我們也說了,構造方法 是在實體化物件(new物件)的程序中,會呼叫合適的構造方法
// 所以 我們想要呼叫 構造方法時,只需要new物件就行了,
Person person = new Person();// 圖34
}
}
圖34

很多人可能會問,你有輸出,怎么說是不帶引數的構造方法?這只是讓你理解 在new物件的程序中,會呼叫構造方法,
?
再來看看這個程式
class Person{
private String name;
private int age;
// public Person(){// 構造方法
// System.out.println("Person()::不帶引數的構造方法");
// }
}
public class ClassAndObject {
public static void main(String[] args) {
Person person = new Person();// 圖35
// 由圖得知,當我們將自己寫構造方法屏蔽時,照理說 是不能 new 物件的
//因為 new 需要呼叫構造方法,才能實作的
// 而現在代碼執行完成,那么就意味著編譯器在編譯 執行new物件陳述句時,發現我們沒有寫構造方法
// 會自動幫我們生成一個無引數引數的構造方法,如下所示,所以才沒有任何的輸出結果
例如
public Person(){
}
}
}
圖35

得出結論:
如果類中沒有提供任何的建構式,那么編譯器會默認生成一個不帶有引數的建構式
也就是說一個類至少有一個構造方法,就算你沒有寫,
?
另外注意一點 如果我們寫的 類中構造方法被private所修飾,那么其他類中 是不可使用 該類 去new 物件
當然 構造方法 所處類的內部是可以被呼叫的,因為 private 就是 將 所修飾部分 私有化,只有內部才能使用,外者不能使用
既然 構造方法 可以私有化,肯定由它的作用: 實作 單例模式 的 大前提,現在不講,還不會
代碼如下
class Person{
private String name;
private Person(){
System.out.println("不帶引數的構造方法");
}
Person p = new Person();
}
public class ClassAndObject {
public static void main(String[] args) {
Person person = new Person();
}// 圖 36
}
圖36

?
現在我們來寫一寫 多個構造方法,加深對 “合適” 的理解
class Person{
private String name;
public Person(){
System.out.println("不帶引數的構造方法");
}
public Person(String name){
this.name = name;// 給Person類中 成員變數傳參,類似 Setter 方法
System.out.println("Person(String)::帶一個String型別引數的構造方法");
}
注意 this 代表當前的物件 這種說法是錯誤的
因為 如果你要呼叫當前物件的前提是產生一個物件(呼叫完合適的構造方法才能實體化物件)
而我們現在這個程式,構造方法里就用this了,說明此時的 this 就不能代表當前的物件了
只能說完成 new的執行程序的第一步: 為物件分配記憶體,有了記憶體就有了地址,this此時代表當前物件的參考
}
public class ClassAndObject {
public static void main(String[] args) {
// 呼叫不帶引數構造方法
Person person = new Person();
System.out.println("=================");
// 呼叫 帶一個 引數的構造方法
Person person1 = new Person("author");
}// 圖 37
}
圖37

你們有沒有發現 構造方法支持多載. 規則和普通方法的多載一致
構造方法多載規則:類名相同,引數的型別和個數,兩者中,至少有一個不同項,
呼叫構造方法時,編譯器會自動篩選呼叫合適的構造方法
?
注意: 若類中定義了構造方法,則默認的無參構造將不再生成.
class Person{
private String name;
// public Person(){
// System.out.println("不帶引數的構造方法");
// }
public Person(String name){
this.name = name;// 給Person類中 成員變數傳參,類似 Setter 方法
System.out.println("Person(String)::帶一個String型別引數的構造方法");
}
}
public class ClassAndObject {
public static void main(String[] args) {
// 呼叫不帶引數構造方法
Person person = new Person();
System.out.println("=================");
// 呼叫 帶一個 引數的構造方法
Person person1 = new Person("author");
}// 圖 38
}
圖38

總得來說 構造方法的意義是:用來構造物件的,
之前我們寫的程式都沒有寫構造方法,但是現在我剛才講了編譯器會自動幫我們生成一個無引數的構造方法
你只需要要明白,沒有構造方法 是無法 實體化一個物件的,
?
在我們這篇博客代碼中 this 頻繁出現,現在我們就來講解一下this的用法
1.this.data 呼叫當前的物件的 屬性/欄位/成員變數
2.this.func() 呼叫當前的物件的方法
3.this() 呼叫當前物件的其他構造方法
?
第一種我就不說了,前面已經見過它的應用了,我來講第二種
class Person{
private String name;
public void eat(){
System.out.println(name + "正在吃飯");
}
public void print(){
this.eat();// 呼叫當前物件的eat方法,這里重復強調一個問題 靜態成員方法中不能使用this
// 寫錯也沒關系,編譯器會提示你寫錯了(紅色警告波浪線)
System.out.println("姓名:" + name);
}
}
&ensp;
第三種 this() 呼叫當前物件的構造方法
class Person{
private String name;
public Person(){
this();
// 注意不能這么去寫,因為 this() 表示呼叫當前物件的構造方法
// 而現在Person 就是當前的構造方法,兩者嵌套使用,會造成死回圈
// Person(){} 呼叫 this(), this() 呼叫 Person(){}
// 而且編譯器也出現 紅色波浪線警告 圖39
System.out.println("不帶引數的構造方法");
}
}
圖39

?
那么 this() 在上述情況下 該怎么使用?
呼叫 其他 有引數的構造方法就可以了
代碼如下:
class Person{
private String name;
public Person(){
this("author");
System.out.println("不帶引數的構造方法");
}
public Person(String name){
System.out.println("帶有一個引數的構造方法");
}
}
public class ClassAndObject {
public static void main(String[] args) {
// 現在我們來通過new物件,來呼叫無引數的構造方法
// 在進入無引數的方法后,執行第一條陳述句,就是this("author")
// 意思是 呼叫帶有一個引數的構造方法
// 呼叫完成之后,再執行 無引數的構造方法 的 輸出陳述句
// 也就是說 先列印 "帶有一個引數的構造方法" , 后列印 "不帶引數的構造方法"
Person person = new Person();// 圖40
}
}
圖40

?
但是 注意一點 this()在構造方法中 去呼叫 其他構造方法時,只能放在該構造方法的第一句的位置,this()才能使用( 圖41 ),而且 this() 這種方法,只能用于構造方法中,
圖41

?
代碼塊
本地代碼塊
實體代碼塊
靜態代碼塊
同步代碼塊
我們主要講解 實體 和 靜態代碼塊,
?
不過本地代碼塊 其實我們遇到過,在方法中,書寫兩個花括號,就是我們所說的 本地代碼塊
案例
public class ClassAndObject {
public static void main(String[] args) {
{
}
}
}
?
什么是實體代碼塊? 什么是靜態代碼塊?
在類中,方法外,書寫 兩個花括號, 就是我說的 實體代碼塊
而靜態代碼塊就是 在實體代碼塊的基礎上,加上static 修飾
也就是說 用 static 去修飾這兩個花括號
代碼如下:
class Person{
private String name;
{
System.out.println("實體代碼塊");
}
static{
System.out.println("靜態代碼塊");
}
public Person(){
System.out.println("不帶引數的構造方法");
}
}
此時 我們就應該去想一下,這些代碼塊怎么被呼叫的?
public class ClassAndObject {
public static void main(String[] args) {
當我們main函式里什么都沒有寫的時候,程式能運行,但沒有結果顯示,
圖42
現在 我們來 物體化一個物件,而且我們知道 它一定會呼叫 不帶引數的構造方法
但是當我們運行程式
圖 43
而且請注意 我 靜態 和 實體 代碼塊的位置(實體在前,靜態在后)
但是最后輸出結果 確實 靜態代碼塊,先執行,
也就意味著 靜態 和 實體 的運行順序,不受 代碼塊的位置 影響,(默認靜態代碼塊先執行,實體代碼塊后執行)
至于構造方法,我們得知它并不是第一個被執行的,
Person person = new Person();
System.out.println("==================");
// 此時,我們再來 new 一個物件
Person person1 = new Person();// 圖44
// 有輸出結果得知,雖然我們new了兩次物件,但靜態代碼塊只會被執行一次
// 也就說 靜態代碼塊的執行 跟 我們 new幾次物件沒關系
}
}
圖42

圖43

圖44

?
再來看一個點
如果我們不去new 物件,而是 去訪問 靜態成員變數會如何?
代碼如下
class Person{
private String name;
public static int count;
{
System.out.println("實體代碼塊");
}
static{
System.out.println("靜態代碼塊");
}
public Person(){
System.out.println("不帶引數的構造方法");
}
}
public class ClassAndObject {
public static void main(String[] args) {
System.out.println(Person.count);// 圖45
// 由圖得知,靜態代碼是不需要借助 new 物件,就可以被執行,(且只被執行一次)
// 也就是說 靜態代碼塊 在加載類的時候(你要訪問類體,肯定是要加載類的),被執行,
// 類的加載 屬于JVM當中知識,留著將JVM的時候再講,
}
}
圖 45

?
那么 實體 和 靜態代碼塊的作用是什么?
實體 和 靜態 代碼塊 用來實體化(初始化) 資料成員
代碼如下:
class Person{
private String name;
public static int count=5;
{
this.name = "author";
System.out.println("實體代碼塊");
}
static{// 注意 static 可不能 使用this
// 因為 被 static 修飾的函式,都是 屬于類的,不屬于物件
count =10;// 對靜態成員變數 初始化沒問題
System.out.println("靜態代碼塊");
}
}
那么當中就存在這一個疑問,靜態代碼中的count==10,和 成員變數的count == 5,那個先初始化?
最終的結果是 默認值 5 , 還是 10?
public class ClassAndObject {
public static void main(String[] args) {
System.out.println(Person.count);// 圖46
}
}
圖46

由圖46得知, 靜態代碼塊 是后初始化 count 的值,
難道 這就說明, 靜態代碼塊初始化資料的優先級 低于 靜態成員變數初始化 嗎?
不一定!
?
來看看這個程式(此時 靜態成員變數初始化 放在 靜態代碼塊下方)
class Person{
private String name;
static{
count =10;
System.out.println("靜態代碼塊");
}
public static int count = 20 ;// 你可以發現靜態變數的定義寫在 使用者塊下面,也能被使用
切記 一定要對count進行賦值,只有賦值才是初始化,
// 如果 你直接 像 圖 48 這樣用,那么 count 處于一種未初始化的狀態,此時再被 靜態方法塊所初始化,其值就是10了
}
public class ClassAndObject {
public static void main(String[] args) {
System.out.println(Person.count);// 圖47
}
}
圖47

圖48

?
實體代碼塊 跟 靜態代碼塊 的 初始化 規則是一模一樣的,這里就不再去寫,
?
總結
靜態代碼塊不管生成多少個物件,其只會執行一次,且是最先執行的,
靜態代碼塊執行完畢后, 實體代碼塊(構造塊)執行,再然后是建構式執行,
?
匿名物件
1. 表示沒有名字的物件.
2. 沒有參考的物件稱為匿名物件.
3. 匿名物件只能在創建物件時使用.
4. 如果一個物件只是用一次, 后面不需要用了, 可以考慮使用匿名物件
代碼實體
class Person{
public String name;
public void eat(){
System.out.println(name + "正在吃飯");
}
}
public class ClassAndObject {
public static void main(String[] args) {
System.out.println(new Person().name);
new Person().eat();
System.out.println(new Person());
System.out.println("================");
// 正確寫法
Person person = new Person();
System.out.println(person.name);
person.eat();
System.out.println(person);
}
}//圖 49
圖49

本文結束
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/344222.html
標籤:java
