類與物件、封裝、構造方法
1. 面向物件思想
1.1 面向物件思想概述
概述
Java語言是一種面向物件的程式設計語言,而面向物件思想是一種程式設計思想,我們在面向物件思想的指引下,使用Java語言去設計、開發計算機程式。 這裡的物件泛指現實中一切事物,每種事物都具備自己的屬性和行為。面向物件思想就是在計算機程式設計過程中,參照現實中事物,將事物的屬性特徵、行為特徵抽象出來,描述成計算
機事件的設計思想。 它區別於面向過程思想,強調的是通過呼叫物件的行為來實現功能,而不是自己一步一步的去操作實現。
特點
面向物件思想是一種更符合我們思考習慣的思想,它可以將複雜的事情簡單化,並將我們從執行者變成了指揮者。
面向物件的語言中,包含了三大基本特徵,即封裝、繼承和多型。
1.2 類和物件
什麼是類
- 類 :是一組相關屬性和行為的集合。可以看成是一類事物的模板,使用事物的屬性特徵和行為特徵來描述該類事物。
現實中,描述一類事物: - 屬性 :就是該事物的狀態資訊。
- 行為 :就是該事物能夠做什麼。
什麼是物件
-
物件:是一類事物的具體體現。物件是類的一個例項(物件並不是找個女朋友),必然具備該類事物的屬性和行為。
現實中,一類事物的一個例項:一隻小貓。
類與物件的關係
- 類是對一類事物的描述,是 抽象的。
- 物件是一類事物的例項,是 具體的。
- 類是物件的模板,物件是類的實體 。
1.3 類的定義
事物與類的對比
現實世界的一類事物:
屬性:事物的狀態資訊。 行為:事物能夠做什麼。
Java中用class描述事物也是如此:
成員變數:對應事物的屬性 成員方法:對應事物的行為
類的定義格式
public class ClassName {
//成員變數
//成員方法
}
- 定義類 :就是定義類的成員,包括成員變數和成員方法。
- 成員變數 :和以前定義變數幾乎是一樣的。只不過位置發生了改變。在類中,方法外。
- 成員方法 :和以前定義方法幾乎是一樣的。只不過把static去掉。
類的定義格式舉例:
public class Student { //成員變數 String name;//姓名 int age;//年齡 //成員方法 //學習的方法 publicvoid study() { System.out.println("好好學習,天天向上"); } //吃飯的方法 publicvoid eat() { System.out.println("學習餓了要吃飯"); } }
1.4 物件的使用
物件的使用格式
建立物件:
類名 物件名 = new 類名();
使用物件訪問類中的成員:
物件名.成員變數;
物件名.成員方法();
物件的使用格式舉例:
public class Test01_Student { public static void main(String[] args) { //建立物件格式:類名 物件名 = new 類名(); Student s = new Student(); System.out.println("s:"+s); //cn.itcast.Student@100363 //直接輸出成員變數值 System.out.println("姓名:"+s.name); //null System.out.println("年齡:"+s.age); //0 System.out.println("‐‐‐‐‐‐‐‐‐‐"); //給成員變數賦值 s.name = "趙麗穎"; s.age = 18; //再次輸出成員變數的值 System.out.println("姓名:"+s.name); //趙麗穎 System.out.println("年齡:"+s.age); //18 System.out.println("‐‐‐‐‐‐‐‐‐‐"); //呼叫成員方法 s.study(); // "好好學習,天天向上" s.eat(); // "學習餓了要吃飯" } }
成員變數的預設值

1.5 類與物件的練習
定義手機類:
public class Phone { // 成員變數 String brand; //品牌 int price; //價格 String color; //顏色 // 成員方法 //打電話 public void call(String name) { System.out.println("給"+name+"打電話"); } //發簡訊 public void sendMessage() { System.out.println("群發簡訊"); } }
定義測試類:
public class Test02Phone { public static void main(String[] args) { //建立物件 Phone p = new Phone(); //輸出成員變數值 System.out.println("品牌:"+p.brand);//null System.out.println("價格:"+p.price);//0 System.out.println("顏色:"+p.color);//null System.out.println("‐‐‐‐‐‐‐‐‐‐‐‐"); //給成員變數賦值 p.brand = "錘子"; p.price = 2999; p.color = "棕色"; //再次輸出成員變數值 System.out.println("品牌:"+p.brand);//錘子 System.out.println("價格:"+p.price);//2999 System.out.println("顏色:"+p.color);//棕色 System.out.println("‐‐‐‐‐‐‐‐‐‐‐‐"); //呼叫成員方法 p.call("紫霞"); p.sendMessage(); } }
1.6 物件記憶體圖
一個物件,呼叫一個方法記憶體圖

兩個物件,呼叫同一方法記憶體圖

一個引用,作為引數傳遞到方法中記憶體圖

1.7 成員變數和區域性變數區別
變數根據定義位置的不同,我們給變數起了不同的名字。如下圖所示:

- 在類中的位置不同
- 成員變數:類中,方法外
- 區域性變數:方法中或者方法宣告上 (形式引數)
- 作用範圍不一樣
- 成員變數:類中
- 區域性變數:方法中
- 初始化值的不同
- 成員變數:有預設值
- 區域性變數:沒有預設值。必須先定義,賦值,最後使用
- 在記憶體中的位置不同
- 成員變數:堆記憶體
- 區域性變數:棧記憶體
- 生命週期不同
- 成員變數:隨著物件的建立而存在,隨著物件的消失而消失
- 區域性變數:隨著方法的呼叫而存在,隨著方法的呼叫完畢而消失
2. 封裝
2.1 封裝概述
概述
面向物件程式語言是對客觀世界的模擬,客觀世界裡成員變數都是隱藏在物件內部的,外界無法直接操作和修改。封裝可以被認為是一個保護屏障,防止該類的程式碼和資料被其他類隨意訪問。要訪問該類的資料,必須通過指定的方式。適當的封裝可以讓程式碼更容易理解與維護,也加強了程式碼的安全性。
原則
將屬性隱藏起來,若需要訪問某個屬性,提供公共方法對其訪問。
2.2 封裝的步驟
- 使用 private 關鍵字來修飾成員變數。
- 對需要訪問的成員變數,提供對應的一對 getXxx 方法 、 setXxx 方法。
2.3 封裝的操作——private關鍵字
private的含義
- private是一個許可權修飾符,代表最小許可權。
- 可以修飾成員變數和成員方法。
- 被private修飾後的成員變數和成員方法,只在本類中才能訪問。
private的使用格式
private 資料型別 變數名 ;
- 使用 private 修飾成員變數,程式碼如下:
public class Student { private String name; private int age; }
- 提供 getXxx 方法 / setXxx 方法,可以訪問成員變數,程式碼如下:
public class Student { private String name; private int age; public void setName(String n) { name = n; } public String getName() { return name; } public void setAge(int a) { age = a; } public int getAge() { return age; } }
2.4 封裝優化1——this關鍵字
我們發現 setXxx 方法中的形參名字並不符合見名知意的規定,那麼如果修改與成員變數名一致,是否就見名知意了呢?程式碼如下:
public class Student { private String name; private int age; public void setName(String name) { name = name; } public void setAge(int age) { age = age; } }
經過修改和測試,我們發現新的問題,成員變數賦值失敗了。也就是說,在修改了 setXxx() 的形參變數名後,方法並沒有給成員變數賦值!這是由於形參變數名與成員變數名重名,導致成員變數名被隱藏,方法中的變數名,無法訪問到成員變數,從而賦值失敗。所以,我們只能使用this關鍵字,來解決這個重名問題。
this的含義
this代表所在類的當前物件的引用(地址值),即物件自己的引用。
記住 :方法被哪個物件呼叫,方法中的this就代表那個物件。即誰在呼叫,this就代表誰。
this使用格式
this.成員變數名;
使用 this 修飾方法中的變數,解決成員變數被隱藏的問題,程式碼如下:
public class Student { private String name; private int age; public void setName(String name) { //name = name; this.name = name; } public String getName() { return name; } public void setAge(int age) { //age = age; this.age = age; } public int getAge() { return age; } }
小貼士:方法中只有一個變數名時,預設也是使用 this 修飾,可以省略不寫。
2.5 封裝優化2——構造方法
當一個物件被建立時候,構造方法用來初始化該物件,給物件的成員變數賦初始值。
小貼士:無論你與否自定義構造方法,所有的類都有構造方法,因為Java自動提供了一個無引數構造方法,一旦自己定義了構造方法,Java自動提供的預設無引數構造方法就會失效。
構造方法的定義格式
修飾符 構造方法名(引數列表){ // 方法體 }
構造方法的寫法上,方法名與它所在的類名相同。它沒有返回值,所以不需要返回值型別,甚至不需要void。使用構造方法後,程式碼如下:
public class Student { private String name; private int age; // 無引數構造方法 public Student() {} // 有引數構造方法 public Student(String name,int age) { this.name = name; this.age = age; } }
注意事項
- 如果你不提供構造方法,系統會給出無引數構造方法。
- 如果你提供了構造方法,系統將不再提供無引數構造方法。
- 構造方法是可以過載的,既可以定義引數,也可以不定義引數。
2.6 標準程式碼——JavaBean
JavaBean 是 Java語言編寫類的一種標準規範。符合 JavaBean 的類,要求類必須是具體的和公共的,並且具有無引數的構造方法,提供用來操作成員變數的 set 和 get 方法。
public class ClassName{ //成員變數 //構造方法 //無參構造方法【必須】 //有參構造方法【建議】 //成員方法 //getXxx() //setXxx() }
編寫符合 JavaBean 規範的類,以學生類為例,標準程式碼如下:
public class Student { //成員變數 private String name; private int age; //構造方法 public Student() {} public Student(String name,int age) { this.name = name; this.age = age; } //成員方法 publicvoid setName(String name) { this.name = name; } public String getName() { return name; } publicvoid setAge(int age) { this.age = age; } publicint getAge() { return age; } }
測試類,程式碼如下:
public class TestStudent { public static void main(String[] args) { //無參構造使用 Student s= new Student(); s.setName("柳巖"); s.setAge(18); System.out.println(s.getName()+"‐‐‐"+s.getAge()); //帶參構造使用 Student s2= new Student("趙麗穎",18); System.out.println(s2.getName()+"‐‐‐"+s2.getAge()); } }
3.程式碼
3.1
import java.util.Arrays; /* 面向過程:當需要實現一個功能的時候,每一個具體的步驟都要親力親為,詳細處理每一個細節。 面向物件:當需要實現一個功能的時候,不關心具體的步驟,而是找一個已經具有該功能的人,來幫我做事兒。 */ public class Demo01PrintArray { public static void main(String[] args) { int[] array = { 10, 20, 30, 40, 50, 60 }; // 要求列印格式為:[10, 20, 30, 40, 50] // 使用面向過程,每一個步驟細節都要親力親為。 System.out.print("["); for (int i = 0; i < array.length; i++) { if (i == array.length - 1) { // 如果是最後一個元素 System.out.println(array[i] + "]"); } else { // 如果不是最後一個元素 System.out.print(array[i] + ", "); } } System.out.println("=============="); // 使用面向物件 // 找一個JDK給我們提供好的Arrays類, // 其中有一個toString方法,直接就能把陣列變成想要的格式的字串 System.out.println(Arrays.toString(array)); } }
/* 通常情況下,一個類並不能直接使用,需要根據類建立一個物件,才能使用。 1. 導包:也就是指出需要使用的類,在什麼位置。 import 包名稱.類名稱; import cn.itcast.day06.demo01.Student; 對於和當前類屬於同一個包的情況,可以省略導包語句不寫。 2. 建立,格式: 類名稱 物件名 = new 類名稱(); Student stu = new Student(); 3. 使用,分為兩種情況: 使用成員變數:物件名.成員變數名 使用成員方法:物件名.成員方法名(引數) (也就是,想用誰,就用物件名點兒誰。) 注意事項: 如果成員變數沒有進行賦值,那麼將會有一個預設值,規則和陣列一樣。 */ public class Demo02Student { public static void main(String[] args) { // 1. 導包。 // 我需要使用的Student類,和我自己Demo02Student位於同一個包下,所以省略導包語句不寫 // 2. 建立,格式: // 類名稱 物件名 = new 類名稱(); // 根據Student類,建立了一個名為stu的物件 Student stu = new Student(); // 3. 使用其中的成員變數,格式: // 物件名.成員變數名 System.out.println(stu.name); // null System.out.println(stu.age); // 0 System.out.println("============="); // 改變物件當中的成員變數數值內容 // 將右側的字串,賦值交給stu物件當中的name成員變數 stu.name = "趙麗穎"; stu.age = 18; System.out.println(stu.name); // 趙麗穎 System.out.println(stu.age); // 18 System.out.println("============="); // 4. 使用物件的成員方法,格式: // 物件名.成員方法名() stu.eat(); stu.sleep(); stu.study(); } }
/* 定義一個類,用來模擬“學生”事物。其中就有兩個組成部分: 屬性(是什麼): 姓名 年齡 行為(能做什麼): 吃飯 睡覺 學習 對應到Java的類當中: 成員變數(屬性): String name; // 姓名 int age; // 年齡 成員方法(行為): public void eat() {} // 吃飯 public void sleep() {} // 睡覺 public void study() {} // 學習 注意事項: 1. 成員變數是直接定義在類當中的,在方法外邊。 2. 成員方法不要寫static關鍵字。 */ public class Student { // 成員變數 String name; // 姓名 int age; // 姓名 // 成員方法 public void eat() { System.out.println("吃飯飯!"); } public void sleep() { System.out.println("睡覺覺!"); } public void study() { System.out.println("學習!"); } }
3.2
/* 定義一個類,用來模擬“手機”事物。 屬性:品牌、價格、顏色 行為:打電話、發簡訊 對應到類當中: 成員變數(屬性): String brand; // 品牌 double price; // 價格 String color; // 顏色 成員方法(行為): public void call(String who) {} // 打電話 public void sendMessage() {} // 群發簡訊 */ public class Phone { // 成員變數 String brand; // 品牌 double price; // 價格 String color; // 顏色 // 成員方法 public void call(String who) { System.out.println("給" + who + "打電話"); } public void sendMessage() { System.out.println("群發簡訊"); } }
public class Demo01PhoneOne { public static void main(String[] args) { // 根據Phone類,建立一個名為one的物件 // 格式:類名稱 物件名 = new 類名稱(); Phone one = new Phone(); System.out.println(one.brand); // null System.out.println(one.price); // 0.0 System.out.println(one.color); // null System.out.println("========="); one.brand = "蘋果"; one.price = 8388.0; one.color = "黑色"; System.out.println(one.brand); // 蘋果 System.out.println(one.price); // 8388.0 System.out.println(one.color); // 黑色 System.out.println("========="); one.call("喬布斯"); // 給喬布斯打電話 one.sendMessage(); // 群發簡訊 } }
public class Demo02PhoneTwo { public static void main(String[] args) { Phone one = new Phone(); System.out.println(one.brand); // null System.out.println(one.price); // 0.0 System.out.println(one.color); // null System.out.println("========="); one.brand = "蘋果"; one.price = 8388.0; one.color = "黑色"; System.out.println(one.brand); // 蘋果 System.out.println(one.price); // 8388.0 System.out.println(one.color); // 黑色 System.out.println("========="); one.call("喬布斯"); // 給喬布斯打電話 one.sendMessage(); // 群發簡訊 System.out.println("========="); Phone two = new Phone(); System.out.println(two.brand); // null System.out.println(two.price); // 0.0 System.out.println(two.color); // null System.out.println("========="); two.brand = "三星"; two.price = 5999.0; two.color = "藍色"; System.out.println(two.brand); // 三星 System.out.println(two.price); // 5999.0 System.out.println(two.color); // 藍色 System.out.println("========="); two.call("歐巴"); // 給歐巴打電話 two.sendMessage(); // 群發簡訊 } }
public class Demo03PhoneSame { public static void main(String[] args) { Phone one = new Phone(); System.out.println(one.brand); // null System.out.println(one.price); // 0.0 System.out.println(one.color); // null System.out.println("========="); one.brand = "蘋果"; one.price = 8388.0; one.color = "黑色"; System.out.println(one.brand); // 蘋果 System.out.println(one.price); // 8388.0 System.out.println(one.color); // 黑色 System.out.println("========="); one.call("喬布斯"); // 給喬布斯打電話 one.sendMessage(); // 群發簡訊 System.out.println("========="); // 將one當中儲存的物件地址值賦值給two Phone two = one; System.out.println(two.brand); // 蘋果 System.out.println(two.price); // 8388.0 System.out.println(two.color); // 黑色 System.out.println("========="); two.brand = "三星"; two.price = 5999.0; two.color = "藍色"; System.out.println(two.brand); // 三星 System.out.println(two.price); // 5999.0 System.out.println(two.color); // 藍色 System.out.println("========="); two.call("歐巴"); // 給歐巴打電話 two.sendMessage(); // 群發簡訊 } }
public class Demo04PhoneParam { public static void main(String[] args) { Phone one = new Phone(); one.brand = "蘋果"; one.price = 8388.0; one.color = "土豪金"; method(one); // 傳遞進去的引數其實就是地址值 } public static void method(Phone param) { System.out.println(param.brand); // 蘋果 System.out.println(param.price); // 8388.0 System.out.println(param.color); // 土豪金 } }
public class Demo05PhoneReturn { public static void main(String[] args) { Phone two = getPhone(); System.out.println(two.brand); // 蘋果 System.out.println(two.price); // 8388.0 System.out.println(two.color); // 玫瑰金 } public static Phone getPhone() { Phone one = new Phone(); one.brand = "蘋果"; one.price = 8388.0; one.color = "玫瑰金"; return one; } }
3.3
/* 問題描述:定義Person的年齡時,無法阻止不合理的數值被設定進來。 解決方案:用private關鍵字將需要保護的成員變數進行修飾。 一旦使用了private進行修飾,那麼本類當中仍然可以隨意訪問。 但是!超出了本類範圍之外就不能再直接訪問了。 間接訪問private成員變數,就是定義一對兒Getter/Setter方法 必須叫setXxx或者是getXxx命名規則。 對於Getter來說,不能有引數,返回值型別和成員變數對應; 對於Setter來說,不能有返回值,引數型別和成員變數對應。 */ public class Person { String name; // 姓名 private int age; // 年齡 public void show() { System.out.println("我叫:" + name + ",年齡:" + age); } // 這個成員方法,專門用於向age設定資料 public void setAge(int num) { if (num < 100 && num >= 9) { // 如果是合理情況 age = num; } else { System.out.println("資料不合理!"); } } // 這個成員方法,專門私語獲取age的資料 public int getAge() { return age; } }
/* 對於基本型別當中的boolean值,Getter方法一定要寫成isXxx的形式,而setXxx規則不變。 */ public class Student { private String name; // 姓名 private int age; // 年齡 private boolean male; // 是不是爺們兒 public void setMale(boolean b) { male = b; } public boolean isMale() { return male; } public void setName(String str) { name = str; } public String getName() { return name; } public void setAge(int num) { age = num; } public int getAge() { return age; } }
/* 區域性變數和成員變數 1. 定義的位置不一樣【重點】 區域性變數:在方法的內部 成員變數:在方法的外部,直接寫在類當中 2. 作用範圍不一樣【重點】 區域性變數:只有方法當中才可以使用,出了方法就不能再用 成員變數:整個類全都可以通用。 3. 預設值不一樣【重點】 區域性變數:沒有預設值,如果要想使用,必須手動進行賦值 成員變數:如果沒有賦值,會有預設值,規則和陣列一樣 4. 記憶體的位置不一樣(瞭解) 區域性變數:位於棧記憶體 成員變數:位於堆記憶體 5. 生命週期不一樣(瞭解) 區域性變數:隨著方法進棧而誕生,隨著方法出棧而消失 成員變數:隨著物件建立而誕生,隨著物件被垃圾回收而消失 */ public class Demo01VariableDifference { String name; // 成員變數 public void methodA() { int num = 20; // 區域性變數 System.out.println(num); System.out.println(name); } public void methodB(int param) { // 方法的引數就是區域性變數 // 引數在方法呼叫的時候,必然會被賦值的。 System.out.println(param); int age; // 區域性變數 //System.out.println(age); // 沒賦值不能用 //System.out.println(num); // 錯誤寫法! System.out.println(name); } }
/* 面向物件三大特徵:封裝、繼承、多型。 封裝性在Java當中的體現: 1. 方法就是一種封裝 2. 關鍵字private也是一種封裝 封裝就是將一些細節資訊隱藏起來,對於外界不可見。 */ public class Demo02Method { public static void main(String[] args) { int[] array = {5, 15, 25, 20, 100}; int max = getMax(array); System.out.println("最大值:" + max); } // 給我一個數組,我還給你一個最大值 public static int getMax(int[] array) { int max = array[0]; for (int i = 1; i < array.length; i++) { if (array[i] > max) { max = array[i]; } } return max; } }
public class Demo03Person { public static void main(String[] args) { Person person = new Person(); person.show(); person.name = "趙麗穎"; //person.age = -20; // 直接訪問private內容,錯誤寫法! person.setAge(20); person.show(); } }
public class Demo04Student { public static void main(String[] args) { Student stu = new Student(); stu.setName("鹿晗"); stu.setAge(20); stu.setMale(true); System.out.println("姓名:" + stu.getName()); System.out.println("年齡:" + stu.getAge()); System.out.println("是不是爺們兒:" + stu.isMale()); } }
3.4
/* 當方法的區域性變數和類的成員變數重名的時候,根據“就近原則”,優先使用區域性變數。 如果需要訪問本類當中的成員變數,需要使用格式: this.成員變數名 “通過誰呼叫的方法,誰就是this。” */ public class Person { String name; // 我自己的名字 // 引數name是對方的名字 // 成員變數name是自己的名字 public void sayHello(String name) { System.out.println(name + ",你好。我是" + this.name); System.out.println(this); } }
/* 構造方法是專門用來建立物件的方法,當我們通過關鍵字new來建立物件時,其實就是在呼叫構造方法。 格式: public 類名稱(引數型別 引數名稱) { 方法體 } 注意事項: 1. 構造方法的名稱必須和所在的類名稱完全一樣,就連大小寫也要一樣 2. 構造方法不要寫返回值型別,連void都不寫 3. 構造方法不能return一個具體的返回值 4. 如果沒有編寫任何構造方法,那麼編譯器將會預設贈送一個構造方法,沒有引數、方法體什麼事情都不做。 public Student() {} 5. 一旦編寫了至少一個構造方法,那麼編譯器將不再贈送。 6. 構造方法也是可以進行過載的。 過載:方法名稱相同,引數列表不同。 */ public class Student { // 成員變數 private String name; private int age; // 無引數的構造方法 public Student() { System.out.println("無參構造方法執行啦!"); } // 全引數的構造方法 public Student(String name, int age) { System.out.println("全參構造方法執行啦!"); this.name = name; this.age = age; } // Getter Setter public void setName(String name) { this.name = name; } public String getName() { return name; } public void setAge(int age) { this.age = age; } public int getAge() { return age; } }
public class Demo01Person { public static void main(String[] args) { Person person = new Person(); // 設定我自己的名字 person.name = "王健林"; person.sayHello("王思聰"); System.out.println(person); // 地址值 } }
public class Demo02Student { public static void main(String[] args) { Student stu1 = new Student(); // 無參構造 System.out.println("============"); Student stu2 = new Student("趙麗穎", 20); // 全參構造 System.out.println("姓名:" + stu2.getName() + ",年齡:" + stu2.getAge()); // 如果需要改變物件當中的成員變數資料內容,仍然還需要使用setXxx方法 stu2.setAge(21); // 改變年齡 System.out.println("姓名:" + stu2.getName() + ",年齡:" + stu2.getAge()); } }
3.5
/* 一個標準的類通常要擁有下面四個組成部分: 1. 所有的成員變數都要使用private關鍵字修飾 2. 為每一個成員變數編寫一對兒Getter/Setter方法 3. 編寫一個無引數的構造方法 4. 編寫一個全引數的構造方法 這樣標準的類也叫做Java Bean */ public class Student { private String name; // 姓名 private int age; // 年齡 public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
public class Demo01Student { public static void main(String[] args) { Student stu1 = new Student(); stu1.setName("迪麗熱巴"); stu1.setAge(20); System.out.println("姓名:" + stu1.getName() + ",年齡:" + stu1.getAge()); System.out.println("================="); Student stu2 = new Student("古力娜扎", 21); System.out.println("姓名:" + stu2.getName() + ",年齡:" + stu2.getAge()); stu2.setAge(22); System.out.println("姓名:" + stu2.getName() + ",年齡:" + stu2.getAge()); } }