1.封裝
封裝的目的是簡化編程和增強安全性,
簡化編程是指,封裝可以讓使用者不必了解具體類的內部實作細節,而只是要通過提供給外部訪問的方法來訪問類中的屬性和方法
增強安全性是指,封裝可以使某個屬性只能被當前類使用,從而避免被其他類或物件進行誤操作,
如何增強安全性:使用訪問修飾符private修飾屬性
2.建構式
構造方法不同于普通方法,普通方法代表物件的行為,而構造方法是提供給系統用于創建物件的方法,
構造方法(也稱為建構式)是一種特殊的方法,它具有以下特點,
- 構造方法的方法名必須與類名相同,
- 構造方法沒有回傳型別,在方法名前不宣告回傳型別(包括 void),
- 但其實構造方法是有回傳值的,回傳的是剛剛被初始化完成的當前物件的參考
建構式的語法形式:[訪問修飾符] 類名([引數串列]) ;
類中沒有構造方法時JVM會默認產生一個無參構造方法,但是如果類中已經有任意的構造方法,JVM不會再創建,
當使用反射創建物件時,必須要使用無參構造方法,
3.this關鍵字:
this指向當前物件的參考
this經常用于:
- 區分成員變數和區域變數,避免命名的煩惱,
- 呼叫其他構造方法, this呼叫構造方法,只能是方法內的第一條陳述句,
private int age ; ... public void setAge(int age) { ... this.age = age ; }
public Student() { //呼叫有一個String引數的構造方法 this(" WangYun" ); } public Student(String name) { //呼叫有兩個引數的構造方法 this(name,23); } public Student(String name, int age) { ... }
4.練習

//以下代碼 Student s1 = new Student("zs",18); Student s2 = new Student("zs",18); System.out.print(s1 == s2); System.out.print(s1.getName() == s2.getName()); //執行后,控制臺輸出為? false true
因為setName()都是雙引號字串物件,是對常量池中同一個變數的參考,
5.Java包
- 為了更好地組織類,Java 提供了包機制,包是類的容器,用于分隔類名空間,
- 如果沒有指定包名,所有的類都屬于一個默認的無名包,
- Java 中將實作相關功能的類組織到一個包中,
- 提供了類似于作業系統樹形檔案夾的組織形式,能分門別類地存盤、管理類,易于查找并使用類,
- 解決了同名類的命名沖突問題,例如,學生王云定義了一個類,類名叫
TestStudent,學生劉靜濤也定義了一個叫TestStudent的類,如果在同一個檔案夾下,就會產生命名沖突的問題,而使用了包的機制,就可以把王云定義的類存放在wangyun包下,把劉靜濤定義的類存放在liujingtao包下,之后就可以先通過wangyun和liujingtao這樣的包名,區分不同的目錄,然后再使用TestStudent訪問兩個包中各自的類,從而解決了命名沖突的問題, - 包允許在更廣的范圍內保護類、屬性和方法,關于這方面的作用,在本章后面介紹訪問權限的時候,大家就能體會到,
包的宣告語法格式:package pkg1[.pkg2[.pkg3…]];
命名規則:通常包名全部用小寫字母,現在使用最多的規則是使用翻轉的 internet 域名(不含 www、ftp 等訪問協議),
java常用包:
java.lang:lang 是 language 的簡寫,這個包提供 Java 語言的基礎類,例如 String、Math、Integer、System 和 Thread 等,java.util:util 是 utility 的簡寫,組織了 Java 的工具類,包含集合、事件模型、日期和時間設定、國際化和各種實用工具類,java.io:io 是 input 和 output 的合并簡寫,指輸入和輸出,組織了資料流、序列化和檔案系統相關的類,java.net:net 即網路,這個包組織了為實作網路應用程式而提供的類,java.awt:抽象視窗工具集(Abstract Window Toolkit),包含用于創建用戶界面和繪制圖形影像的類,
參考包的兩種方法:import 包名.類名;和import 包名.* ; 第二種形式的包只能參考當前包下的類,不能匯入其子包中的類;同時只會匯入用到的類,
import陳述句要寫在package之后,在類定義之前,
java.lang中的類不需要import
6.Java的權限修飾符:
Java 語言中的訪問權限修飾符有 4 種,但卻只有 3 個關鍵字,因為不寫訪問權限修飾符時,在 Java 中被稱為默認權限(包權限),本課程中以 default 代替,其他 3 個訪問權限修飾符分別為 private、protected 和 public,
修飾類:public和default,public代表類能在各種地方使用,default代表類只能在本包內使用,
修飾屬性、構造方法、普通方法:private default protected public,private修飾的成員只能在本類中使用,default修飾的成員只能在本類和本包內使用,protected修飾的成員只能在本類、本包內和子類中使用,public可以在所有類中使用,
可以看到,只有成員變數/方法和類/介面才可以使用訪問權限修飾符,區域變數不可以使用,
7.成員變數:
物件成員變數和類成員變數,物件成員變數可以在每一個物件中創建,不同物件的物件成員變數不能共享;類成員變數只創建一次,可以被所有物件共享,通過在變數前面加上static使成員變數成為靜態的(類成員變數),
可以直接通過類名參考靜態變數,也可以通過實體名來參考靜態變數,但推薦采用前者,因為采用后者容易混淆靜態變數和實體變數,
被static修飾的方法稱為靜態方法/類方法,可以通過類名.方法名直接呼叫,
靜態方法中無法使用實體變數,
Java 類首次裝入 JVM 時,會對靜態成員或靜態塊進行一次初始化,注意此時還沒有產生物件,
因此,靜態成員和靜態塊都是和類系結的,會在類加載時就進行初始化操作,
在使用 new 關鍵字創建并初始化物件的程序中,具體的初始化分為以下 4 步,
- 給物件的實體變數分配空間,默認初始化成員變數,
- 成員變數宣告時的初始化
- 初始化塊初始化,
- 構造方法初始化,
8.單例模式:
單例模式指的是無論創建了多少個參考,在堆中僅僅只有一個實體物件,
單例模式通過private修飾構造方法實作,也稱為構造方法私有化,
public class Singleton { private static Singleton instance; private Singleton() { } }
同時創建一個堆記憶體物件供使用,這個方法在沒有物件時創建,存在物件時直接回傳這個物件的參考,
public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }
9.繼承
繼承可以使得子類沿用父類的成員(屬性和方法),當多個子類具有相同的成員時,就可以考慮將這些成員提取出來,放到父類中,然后再用子類去繼承這個父類,也就是將一些相同的成員提取到了更高的層次中,
繼承可以大大減少冗余代碼,提高代碼的復用性,
繼承的關鍵字是extends,繼承的語法形式是class A extends B{},類A稱為父類、超類、基類,類B稱為子類、衍生類和匯出類,
-
子類無法繼承父類的構造方法,
構造方法是一種特殊的方法,子類無法繼承父類的構造方法,
-
子類不能繼承父類中不符合訪問權限的成員,
子類不能繼承private修飾或者不在同包內的default修飾的父類變數,
10.final關鍵字
final 關鍵字用法如下:
若用于修飾屬性,則該屬性不允許被修改,
若用于修飾父類中的方法,則該方法不允許被子類重寫,
若用于修飾類,則該類不允許被其他類繼承,
final修飾的方法可以被多載,但不能被重寫,
11.重寫
- 重寫方法與被重寫方法同名,引數串列也必須相同,
- 重寫方法的回傳值型別必須和被重寫方法的回傳值型別相同或是其子類,
- 重寫方法不能縮小被重寫方法的訪問權限,
- 子類不能重寫父類的構造方法
12.super關鍵字
super關鍵字用于參考父類的物件,
super()用于呼叫父類的構造方法來創建子類物件,和this()一樣,必須寫在構造方法中的第一行,
需要注意的是,子類的構造方法中如果不寫 super(),編譯器會幫助你在子類構造方法的第一行加上super(),因為在子類中呼叫父類構造器是“必須的”,但如果父類中只存在有參構造方法,并沒有提供無參構造方法,則需要在子類構造方法中顯式地呼叫父類存在的構造器,否則可能因為父類中沒有無參構造器而得到一個編譯錯誤,
使用super呼叫重寫方法在父類的方法:super.重寫方法(),其作用是對父類方法進行一些補充,
13.多型
多型可以優雅的解決程式中的擴展性問題,
在形式上,父類參考可以指向子類物件,在 Vehicle vehicle = new Car(); 中,子類的 Car 物件賦值給了父類 Vehicle 參考,這稱為向上轉型;
在參考 vehicle 上呼叫方法,在運行時刻究竟呼叫的是父類 Vehicle 中的方法還是子類 Car 中的方法呢?實際需要通過運行時的物件型別來判斷,這稱為動態系結,
向上轉型和動態系結就是多型的具體實作機制,
向上轉型的程序中涉及了繼承、重寫和父類參考子類物件的概念,
向上轉型的物件呼叫重寫方法,執行的是子類的重寫方法,
向上轉型的好處,是不需要針對父類的多個子類再設計不同的方法,減少了代碼量并且增加了可拓展性,
動態系結是指在編譯期間方法并不會和“參考”的型別系結在一起,而是在程式運行的程序中,JVM 需要根據具體的實體物件才能確定此時要呼叫的是哪個方法,重寫方法遵循的就是動態系結,
例如,子類和父類中都有info()方法,動態系結就是指,編譯程序中info()不會和具體的類系結到一起,而是在運行期間列舉出子類和父類的info()方法,根據當前的實體物件,呼叫該實體物件的info()方法,
靜態系結是指程式編譯期的系結,以下的類資訊,使用的就是靜態系結機制:
final、static或private修飾的方法,以及多載方法和構造方法,- 成員變數,Java不支持(實際上是不鼓勵)成員變數的多型行為,
一種推薦的編程思想是“面向基類”編程,也就是建議將面向的“物件”抽象為更高層次的基類,
向上轉型的缺點:轉型后的物件無法呼叫匯出類中的方法,
向下轉型:強制從基類轉向衍生類,
確保向下轉型的正確性:使用instance of運算子,
14.練習


無論子類建構式中有沒有super();實際運行時都會呼叫父類的構造器,
當子類重寫了父類的方法時,呼叫主體(即物件)和方法是運行時系結的;
當子類和父類的屬性重名時,呼叫主體(即物件)和屬性是編譯時系結的,
這兩種說法,前一種是方法的動態系結,后一種是成員變數的靜態系結,

java在編譯時確定物件型別,同時對這個物件的成員變數進行靜態編譯,也就是說,sup物件的i屬性是在Super類中加載的,

此時sub物件的i屬性是在sub類中加載的,

首先,定義了A型別的變數ab,在編譯程序中會加載A類,此時A中的靜態塊會被執行,列印“1”,然后ab物件被賦值為實體化物件的參考,此時需要執行B類的建構式,首先加載B類到記憶體中,此時B類中的靜態塊會被執行,列印"a",然后,執行B類的建構式,因為B類是A類的子類,所以一定會首先執行A的建構式,因此列印“2”,然后再執行B中的建構式,列印“b”,第二句陳述句,因為A類和B類都不是首次加載,所以只會呼叫B類的建構式,列印“2”再列印"b",因此列印出來結果是"1a2b2b",
//以下代碼 class Student{ int age = 1; public Student(int age) { this.age = age; } } public class Test extends Student{ public static void main(String[] args) { Test t = new Test(); System.out.println(t.age); } } //執行后,控制臺輸出為? 編譯時錯誤(可見錯誤)
使用Test類的建構式,會首先呼叫父類的建構式,由于當前僅定義了父類的有參構造方法,所以JVM不會再創建父類的無參建構式,因此程式出現編譯時錯誤,

//以下代碼 class Student{ public static void eat() { System.out.print(1); } } public class Test extends Student{ public static void eat() { System.out.print(2); } public static void main(String[] args) { eat(); } } //執行后,控制臺輸出為?2
此時,沒有指明呼叫的是哪個類中的重寫方法,因為入口函式是子類中,所以“就近”呼叫子類的重寫方法,
//以下代碼 class Student{ public void eat() { System.out.print(1); } } public class Test extends Student{ private void eat() { System.out.print(2); } public static void main(String[] args) { Test t = new Test(); t.eat(); } } //執行后,控制臺輸出為?編譯錯誤
重寫方法中,權限修飾符不能比父類方法的范圍小

如果范圍相等,或擴大則不會報錯,

//以下代碼 class Student{ int i; public Student(int i) { this.i = i; } } public class Test extends Student{ int i = 1; public Test(int i) { super(10); this.i = i; } public static void main(String[] args) { Student t = new Test(100); System.out.print(t.i); } } //執行后,控制臺輸出為?10
編譯時,首先根據靜態系結將父類t.i初始化為0;然后呼叫Test(100)建構式時,首先顯式呼叫父類有參建構式,此時父類t.i=10,繼續執行剩下的Test類建構式,子類i=100,最后列印的是父類的t.i=10,
由于靜態系結,盡管它是子類物件,但是它在編譯時系結了父類成員變數,也會一直用父類成員變數,
//以下代碼 class Student{ int i; public Student(int i) { this.i = i; } } public class Test extends Student{ int i = 1; public Test(int i) { super(10); this.i = i; } public static void main(String[] args) { Test t = new Test(100); System.out.print(t.i); } } //執行后,控制臺輸出為?100
同理,這個物件的型別是Test型別,所以由于靜態系結,t物件的成員變數是Test類中的成員變數,
首先子類初始化t.i=1,然后呼叫Test類的有參建構式,其中先顯式呼叫父類的有參建構式 ,父類i成員變數初始化為0,又賦值為10,然后繼續執行子類建構式,t.i=100,最后顯示的結果是子類t.i為100,
所以這種題型只需要確定物件靜態系結的型別是什么型別(即定義為什么型別),跟蹤這個變數的變化就可以了,
//以下代碼 class Student{ public Student() { System.out.print(1); } } public class Test extends Student{ String name; public Test(String name) { this.name = name; System.out.print(2); } public static void main(String[] args) { Test t = new Test("zs"); } } //執行后,控制臺輸出為?12
首先,t物件靜態系結Test類,所以t.name默認值為null,實體化呼叫Test類的有參建構式時,首先呼叫父類的無參建構式,列印1,然后繼續執行子類的建構式,t.name="zs",然后列印2,
//以下代碼 class Student{ int age; public Student(int age) { this.age = age; } } public class Test extends Student{ public Test(int age) { super(age); } public static void main(String[] args) { Test t = new Test(10); System.out.println(t.age); } } //執行后,控制臺輸出為?10
子類繼承了父類的age屬性,
class Student1{ int age; public Student1(int age) { this.age = age; } } public class Test extends Student1{ int age; public Test(int age) { super(age); } public static void main(String[] args) { Test t = new Test(10); System.out.println(t.age); } }
如果沒有繼承,那么子類建構式中僅呼叫父類建構式,修改的是父類物件的屬性,并沒有修改子類age屬性,因此列印默認值0,
15.為什么區域變數不能被賦初值?
原因是區域變數一旦定義就肯定會被用到,需要使用者賦初值0.0否則會引起編譯錯誤,
16.為什么只能在第一行呼叫this()和super()構造方法?
super()第一行的原因:如果super()不在第一行,那么編譯器會自動在第一行補充上super();
this()第一行的原因:如果this()不在第一行,那么編譯器會自動在第一行補充上super();
那么就會出現一個構造方法內,構造出多個物件的情況,JVM不允許這樣的情況出現,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/296184.html
標籤:其他
上一篇:5Java基礎整理
下一篇:7Java基礎補充
