小Sun學Java之Java面向物件(上)
一、Java基礎程式設計
Sun筆記作為記錄,方便以後檢視。核心源自:尚矽谷:http://www.atguigu.com/
1.4 面向物件——上
1.4.1 類與物件
1. 面向物件學習的三條主線:
- Java類及類的成員:屬性、方法、構造器;程式碼塊、內部類
- 面向物件的大特徵:封裝性、繼承性、多型性、(抽象性)
- 其它關鍵字:this、super、static、final、abstract、interface、package、import等
- “大處著眼,小處著手”
2. 面向物件與面向過程(理解)
1.面向過程:強調的是功能行為,以函式為最小單位,考慮怎麼做。
2.面向物件:強調具備了功能的物件,以類/物件為最小單位,考慮誰來做。
舉例對比:人把大象裝進冰箱。
3. 完成一個專案(或功能)的思路:
4. 面向物件中兩個重要的概念:
-
類:對一類事物的描述,是抽象的、概念上的定義
-
物件:是實際存在的該類事物的每個個體,因而也稱為例項(instance)
面向物件程式設計的重點是類的設計
設計類,就是設計類的成員。 -
二者的關係:
物件,是由類new出來的,派生出來的。
5. 面向物件思想落地實現的規則
- 建立類,設計類的成員
- 建立類的物件
- 通過“物件.屬性”或“物件.方法”呼叫物件的結構
補充:幾個概念的使用說明
- 屬性 = 成員變數 = field = 域、欄位
- 方法 = 成員方法 = 函式 = method
- 建立類的物件 = 類的例項化 = 例項化類
6. 物件的建立與物件的記憶體解析
典型程式碼:
Person p1 = new Person();
Person p2 = new Person();
Person p3 = p1;//沒有新建立一個物件,共用一個堆空間中的物件實體。
說明:
如果建立了一個類的多個物件,則每個物件都獨立的擁有一套類的屬性。(非static的)
意味著:如果我們修改一個物件的屬性a,則不影響另外一個物件屬性a的值。
記憶體解析:
7. 匿名物件:我們建立的物件,沒顯式的賦給一個變數名。即為匿名物件
特點:匿名物件只能呼叫一次。
舉例:
new Phone().sendEmail(); new Phone().playGame(); new Phone().price = 1999; new Phone().showPrice();//0.0 應用場景: PhoneMall mall = new PhoneMall(); //匿名物件的使用 mall.show(new Phone()); 其中, class PhoneMall{ public void show(Phone phone){ phone.sendEmail(); phone.playGame(); } }
8. 理解"萬事萬物皆物件"
- 在Java語言範疇中,我們都將功能、結構等封裝到類中,通過類的例項化,來呼叫具體的功能結構
- Scanner,String等
- 檔案:File
- 網路資源:URL
- 涉及到Java語言與前端Html、後端的資料庫互動時,前後端的結構在Java層面互動時,都體現為類、物件。
1.4.1.1 JVM記憶體結構(這裡只是簡單瞭解,詳細:JVM章節進行講解)
編譯完源程式以後,生成一個或多個位元組碼檔案。
我們使用JVM中的類的載入器和直譯器對生成的位元組碼檔案進行解釋執行。意味著,需要將位元組碼檔案對應的類載入到記憶體中,涉及到記憶體解析。
《JVM規範》
虛擬機器棧,即為平時提到的棧結構。我們將區域性變數儲存在棧結構中
堆,我們將new出來的結構(比如:陣列、物件)載入在對空間中。補充:物件的屬性(非static的)載入在堆空間中。
方法區:類的載入資訊、常量池、靜態域
1.4.2 類的結構之一:屬性
類的設計中,兩個重要結構之一:屬性
對比:屬性(成員變數) vs 區域性變數
1. 相同點:
-
1.1 定義變數的格式:資料型別 變數名 = 變數值
-
1.2 先宣告,後使用
-
1.3 變數都其對應的作用域
2. 不同點:
2.1 在類中宣告的位置的不同
-
屬性:直接定義在類的一對{}內
-
區域性變數:宣告在方法內、方法形參、程式碼塊內、構造器形參、構造器內部的變數
2.2 關於許可權修飾符的不同
-
屬性:可以在宣告屬性時,指明其許可權,使用許可權修飾符。
-
常用的許可權修飾符:private、public、預設、protected --->封裝性
-
目前,大家宣告屬性時,都使用預設就可以了。
-
區域性變數:不可以使用許可權修飾符。
2.3 預設初始化值的情況:
-
==屬性==:類的屬性,根據其型別,都預設初始化值。
-
整型(byte、short、int、long:0)
-
浮點型(float、double:0.0)
-
字元型(char:0 (或'\u0000'))
-
布林型(boolean:false)
-
引用資料型別(類、陣列、介面:null)
-
==區域性變數==:沒預設初始化值。
-
意味著,我們在呼叫區域性變數之前,一定要顯式賦值。
-
特別地:形參在呼叫時,我們賦值即可。
2.4 在記憶體中載入的位置:
-
==屬性==:載入到堆空間中 (非static)
-
==區域性變數==:載入到棧空間
補充:回顧變數的分類:
- 方式一:按照資料型別:
- 方式二:按照在類中宣告的位置:
1.4.3 類的結構之二:方法
1. 關鍵字:return
return關鍵字:
-
使用範圍:使用在方法體中
-
作用:
- 結束方法
- 針對於返回值型別的方法,使用"return 資料"方法返回所要的資料
注意點:return關鍵字後面不可以宣告執行語句。
2.方法的過載
- 方法的過載的概念
定義:在同一個類中,允許存在一個以上的同名方法,只要它們的引數個數或者引數型別不同即可。
總結:"兩同一不同":同一個類、相同方法名
引數列表不同:引數個數不同,引數型別不同
-
構成過載的舉例:
舉例一:Arrays類中過載的sort() / binarySearch();PrintStream中的println()
舉例二://如下的4個方法構成了過載
public void getSum(int i,int j){
System.out.println("1");
}public void getSum(double d1,double d2){
System.out.println("2");
}public void getSum(String s ,int i){
System.out.println("3");
}public void getSum(int i,String s){
System.out.println("4");
}
不構成過載的舉例:
//如下的3個方法不能與上述4個方法構成過載
// public int getSum(int i,int j){
// return 0;
// }
// public void getSum(int m,int n){
//
// }
// private void getSum(int i,int j){
//
// }
- 如何判斷是否構成方法的過載?
嚴格按照定義判斷:兩同一不同。
跟方法的許可權修飾符、返回值型別、形參變數名、方法體都沒關係! - 如何確定類中某一個方法的呼叫:
方法名 ---> 引數列表
面試題:各種區別大集合:
參考:https://blog.csdn.net/weixin_43495390/article/details/86533482
方法的過載與重寫的區別?
throws\throw
String\StringBuffer\StringBuilder
Collection\Collections
final\finally\finalize
...
抽象類、介面
sleep() / wait()
3.可變個數形參的方法
- 使用說明:
- 1.jdk 5.0新增的內容
- 2.具體使用:
- 2.1 可變個數形參的格式:資料型別 ... 變數名
- 2.2 當呼叫可變個數形參的方法時,傳入的引數個數可以是:0個,1個,2個,。。。
- 2.3 可變個數形參的方法與本類中方法名相同,形參不同的方法之間構成過載
- 2.4 可變個數形參的方法與本類中方法名相同,形參型別也相同的陣列之間不構成過載。換句話說,二者不能共存。
- 2.5 可變個數形參在方法的形參中,必須宣告在末尾
- 2.6 可變個數形參在方法的形參中,最多隻能宣告一個可變形參。
-
舉例說明:
public void show(int i){
}public void show(String s){
System.out.println("show(String)");
}public void show(String ... strs){
System.out.println("show(String ... strs)");for(int i = 0;i < strs.length;i++){ System.out.println(strs[i]); }
}
//不能與上一個方法同時存在
// public void show(String[] strs){
//
// }
呼叫時:
test.show("hello");
test.show("hello","world");
test.show();
test.show(new String[]{"AA","BB","CC"});
4.Java的值傳遞機制
-
針對於方法內變數的賦值舉例:
System.out.println("***********基本資料型別:****************"); int m = 10; int n = m; System.out.println("m = " + m + ", n = " + n); n = 20; System.out.println("m = " + m + ", n = " + n); System.out.println("***********引用資料型別:****************"); Order o1 = new Order(); o1.orderId = 1001; Order o2 = o1;//賦值以後,o1和o2的地址值相同,都指向了堆空間中同一個物件實體。 System.out.println("o1.orderId = " + o1.orderId + ",o2.orderId = " +o2.orderId); o2.orderId = 1002; System.out.println("o1.orderId = " + o1.orderId + ",o2.orderId = " +o2.orderId);
規則:
如果變數是基本資料型別,此時賦值的是變數所儲存的資料值。
如果變數是引用資料型別,此時賦值的是變數所儲存的資料的地址值。
-
針對於方法的引數概念
形參:方法定義時,宣告的小括號內的引數
實參:方法呼叫時,實際傳遞給形參的資料 -
java中引數傳遞機制:值傳遞
規則:
- 如果引數是基本資料型別,此時實參賦給形參的是實參真實儲存的資料值。
- 如果引數是引用資料型別,此時實參賦給形參的是實參儲存資料的地址值。
推廣:
如果變數是基本資料型別,此時賦值的是變數所儲存的資料值。
如果變數是引用資料型別,此時賦值的是變數所儲存的資料的地址值。
- 典型例題與記憶體解析:
【例題1】
【例題2】
5.遞迴方法
- 定義:
遞迴方法:一個方法體內呼叫它自身。 - 如何理解遞迴方法?
方法遞迴包含了一種隱式的迴圈,它會重複執行某段程式碼,但這種重複執行無須迴圈控制。
遞迴一定要向已知方向遞迴,否則這種遞迴就變成了無窮遞迴,類似於死迴圈。
-
舉例:
// 例1:計算1-n之間所自然數的和
public int getSum(int n) {// 3
if (n == 1) {
return 1;
} else {
return n + getSum(n - 1);
}}
// 例2:計算1-n之間所自然數的乘積:n!
public int getSum1(int n) {if (n == 1) { return 1; } else { return n * getSum1(n - 1); }
}
//例3:已知一個數列:f(0) = 1,f(1) = 4,f(n+2)=2f(n+1) + f(n),
//其中n是大於0的整數,求f(10)的值。
public int f(int n){
if(n == 0){
return 1;
}else if(n == 1){
return 4;
}else{
// return f(n + 2) - 2 * f(n + 1);
return 2f(n - 1) + f(n - 2);
}
}//例4:斐波那契數列
//例5:漢諾塔問題
//例6:快排
1.4.4 面向物件的特徵一:封裝性
面向物件的特徵一:封裝與隱藏
- 為什麼要引入封裝性?
- 我們程式設計追求“高內聚,低耦合”。
高內聚 :類的內部資料操作細節自己完成,不允許外部干涉;
低耦合 :僅對外暴露少量的方法用於使用。 - 隱藏物件內部的複雜性,只對外公開簡單的介面。便於外界呼叫,從而提高系統的可擴充套件性、可維護性。通俗的說,把該隱藏的隱藏起來,該暴露的暴露出來。這就是封裝性的設計思想。
- 問題引入:
當我們建立一個類的物件以後,我們可以通過"物件.屬性"的方式,對物件的屬性進行賦值。這裡,賦值操作要受到屬性的資料型別和儲存範圍的制約。除此之外,沒其他制約條件。但是,在實際問題中,我們往往需要給屬性賦值加入額外的限制條件。這個條件就不能在屬性宣告時體現,我們只能通過方法進行限制條件的新增。(比如:setLegs()同時,我們需要避免使用者再使用"物件.屬性"的方式對屬性進行賦值。則需要將屬性宣告為私有的(private).
-->此時,針對於屬性就體現了封裝性。 - 封裝性思想具體的程式碼體現:
體現一:將類的屬性xxx私化(private),同時,提供公共的(public)方法來獲取(getXxx)和設定(setXxx)此屬性的值
private double radius;
public void setRadius(double radius){
this.radius = radius;
}
public double getRadius(){
return radius;
}
體現二:不對外暴露的私有的方法
體現三:單例模式(將構造器私有化)
體現四:如果不希望類在包外被呼叫,可以將類設定為預設的。
-
Java規定的四種許可權修飾符
4.1 許可權從小到大順序為:private < 預設 < protected < public
4.2 具體的修飾範圍:4.3 許可權修飾符可用來修飾的結構說明:
4種許可權都可以用來修飾類的內部結構:屬性、方法、構造器、內部類
修飾類的話,只能使用:預設、public
1.4.5 類的結構之三:構造器
1.4.5.1 屬性賦值順序
-
總結:屬性賦值的先後順序
① 預設初始化
② 顯式初始化
③ 構造器中初始化
④ 通過"物件.方法" 或 "物件.屬性"的方式,賦值
-
以上操作的先後順序:① - ② - ③ - ④
1.4.5.2 JavaBean的概念
所謂JavaBean,是指符合如下標準的Java類:
-
類是公共的
-
一個無參的公共的構造器
-
屬性,且對應的get、set方法
1.4.6 關鍵字:this
- 可以呼叫的結構:屬性、方法;構造器
- this呼叫屬性、方法:
this理解為:當前物件 或 當前正在建立的物件
- 在類的方法中,我們可以使用"this.屬性"或"this.方法"的方式,呼叫當前物件屬性或方法。但是,通常情況下,我們都擇省略"this."。特殊情況下,如果方法的形參和類的屬性同名時,我們必須顯式的使用"this.變數"的方式,表明此變數是屬性,而非形參。
- 在類的構造器中,我們可以使用"this.屬性"或"this.方法"的方式,呼叫當前正在建立的物件屬性或方法。但是,通常情況下,我們都擇省略"this."。特殊情況下,如果構造器的形參和類的屬性同名時,我們必須顯式的使用"this.變數"的方式,表明此變數是屬性,而非形參。
- this呼叫構造器:
① 我們在類的構造器中,可以顯式的使用"this(形參列表)"方式,呼叫本類中指定的其他構造器
② 構造器中不能通過"this(形參列表)"方式呼叫自己
③ 如果一個類中有n個構造器,則最多有 n - 1構造器中使用了"this(形參列表)"
④ 規定:"this(形參列表)"必須宣告在當前構造器的首行
⑤ 構造器內部,最多隻能宣告一個"this(形參列表)",用來呼叫其他的構造器
1.4.7 關鍵字:package/import
- package的使用
1.1 使用說明:
- 為了更好的實現專案中類的管理,提供包的概念
- 使用package宣告類或介面所屬的包,宣告在原始檔的首行
- 包,屬於識別符號,遵循識別符號的命名規則、規範(xxxyyyzzz)、“見名知意”
- 每"."一次,就代表一層檔案目錄。
1.2 舉例:
舉例一:
某航運軟體系統包括:一組域物件、GUI和reports子系統
舉例二:MVC設計模式
1.3 JDK中的主要包介紹:
- import的使用:
import:匯入
-
- 在原始檔中顯式的使用import結構匯入指定包下的類、介面
-
- 宣告在包的宣告和類的宣告之間
-
- 如果需要匯入多個結構,則並列寫出即可
-
- 可以使用"xxx.*"的方式,表示可以匯入xxx包下的所結構
-
- 如果使用的類或介面是java.lang包下定義的,則可以省略import結構
-
- 如果使用的類或介面是本包下定義的,則可以省略import結構
-
- 如果在原始檔中,使用了不同包下的同名的類,則必須至少一個類需要以全類名的方式顯示。
-
- 使用"xxx.*"方式表明可以呼叫xxx包下的所結構。但是如果使用的是xxx子包下的結構,則仍需要顯式匯入
-
- import static:匯入指定類或介面中的靜態結構:屬性或方法。