一、封裝
- 該顯示的顯示,該隱藏的隱藏
- 程式設計追求“高內聚,低耦合”
- 高內聚:類的內部資料操作細節自己完成,不允許外部干涉
- 低耦合:僅暴露少量的方法給外部使用
- 封裝即資料的隱藏,通常應禁止直接訪問一個物件中資料的實際表示,而透過操作介面來訪問,這稱為資訊隱藏
- 屬性私有,透過get/set方法運算元據
1、封裝的特點
- 提高程式的安全性,保護資料
- 隱藏程式碼的實現細節
- 統一介面
- 增加系統的可維護性
2、舉例
- 建立一個Student類
//private : 私有
public class Student02 {
//屬性私有
private String name;//姓名
private int age;//年齡
private char sex;//性別
//提供一些可以操作這些屬性的方法
//get/set方法
//get方法:獲取這個資料
public String getName(){
return this.name;
}
//set方法:給這個資料設定值
public void setName(String name){
this.name = name;
}
}
- 透過呼叫get,set方法賦值獲取
public class Appliance {
public static void main(String[] args) {
Student02 s1 = new Student02();
s1.setName("張三");
System.out.println(s1.getName());//輸出張三
}
}
注意:當屬性前新增了private時,在main方法中直接使用s1.name,如圖:
注意:快捷鍵Alt+Insert(Fn+Alt+Insert),可以自動生成get set方法,步驟如下:
- 按下Alt+Insert-->選擇Getter and Setter
- 選擇需要的屬性
3、封裝的好處--判斷輸入合法性
//private : 私有
public class Student02 {
//屬性私有
private int age;//年齡
//get方法:獲取這個資料
public int getAge() {
return age;
}
//set方法:設定這個資料的值
public void setAge(int age) {
//進行合法性判斷
if(age<120 && age>0){
this.age = age;
}else {
this.age = 3;
}
}
}
public class Appliance {
public static void main(String[] args) {
Student02 s1 = new Student02();
//s1.setAge(130);//錯誤資料
//System.out.println(s1.getAge());//輸出3
s1.setAge(18);//有效資料
System.out.println(s1.getAge());//輸出18
}
}
二、繼承
1、什麼是繼承?
-
繼承的本質是對某一批類的抽象,從而實現對現實世界更好的建模
-
繼承關係的兩個類,一個為子類(派生類),一個為父類(基類)。
- 子類繼承父類,使用關鍵字extends來表示
- extends的意思是“擴充套件”,子類是父類的擴充套件
- 子類和父類之間,從意義上來講具有”is a“的關係
-
繼承是類和類之間的一種關係。除此之外,類和類之間的關係還有依賴、組合、聚合等
-
Java中類只有單繼承,沒有多繼承(即子類只能有一個父類,一個父類可以有多個子類)
例項如下:
- 先建立一個Person類作為父類
//父類
public class Person {
//私有屬性
private String name;
//方法
public void say(){
System.out.println("在說話!");
}
//get set方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
(注意:get/set方法請檢視封裝介紹)
- 建立一個Student類和Teacher類作為子類
//Person的子類:子類繼承父類的屬性和方法
public class Student extends Person {
}
//Person的子類:子類繼承父類的屬性和方法
public class Teacher extends Person {
}
- 再建立一個帶有main方法的Application類
Student類中什麼也沒有,但是繼承了父類中的name屬性和方法,可以使用
public class Application {
public static void main(String[] args) {
Student student = new Student();
student.setName("張三");//獲取父類中的屬性name
System.out.print(student.getName());//輸出張三
student.say();//呼叫父類中的say()
}
}
2、Object類
-
快捷鍵Ctrl+H:檢視類之間的父子類關係
-
注意:需把滑鼠停留在某個類中,再按快捷鍵
-
圖例如下:
注意:我們發現最上層有一個Object類,是Person類的父類
- 每一個類預設直接或間接的以Object類作為父類!!!!
例如:
public class Person /*extends Object*/ {
//什麼也沒寫
}
public class Application {
public static void main(String[] args) {
Person person = new Person();
//person物件還是可以點出很多方法,這些方法都是Object類的方法
person.toString();
person.equals();
person.getClass();
}
}
以下均為Object類中的方法:
3、super
用於呼叫父類中的方法和屬性
例項:
- Person類(父類)
public class Person /*extends Object*/ {
//protected:受保護的
protected String name = "張三";
//print方法
public void print(){
System.out.println("Person");
}
}
- Student類(子類)
//Person的子類
public class Student extends Person {
private String name = "李四";
//test方法
public void test(String name){
//this.name中的name表示Student類中的name屬性
//等號右邊的name表示test方法的引數name
this.name = name;
//super.name中的name表示父類Person類中的name屬性
super.name = name;
System.out.println(this.name);
System.out.println(super.name);
}
//print方法
public void print(){
System.out.println("Student");
}
//方法呼叫也是一樣
public void test_2(){
print();//呼叫本類的print方法,輸出Student
this.print();//呼叫本類的print方法,輸出Student
super.print();//呼叫父類的print方法,輸出Person
}
}
- Application類,包含main方法
public class Application {
public static void main(String[] args) {
Student student = new Student();
student.test("王五");
System.out.println("================");
student.test_2();
}
}
執行結果如下圖:
注意點:
- super呼叫父類的構造方法,必須在構造方法的第一個
- super必須且只能出現在子類的方法或構造方法中
public class Person {
public Person() {
System.out.println("Person類的無參執行了!");
}
}
public class Student extends Person {
//無參構造
public Student() {
System.out.println("Student類的無參執行了!");
}
}
public class Application {
public static void main(String[] args) {
Student student = new Student();//只new了一個物件,其他操作不做
}
}
執行結果如下:Person類(父類)的構造方法先執行了,之後才執行了Student類(子類)的構造方法!!!!
原因如下:Student類中的無參構造有一句隱藏程式碼super(),呼叫了父類的無參構造
注意:super();必須放在構造器中的第一行位置,其他位置會報錯無法呼叫
public class Student extends Person {
//無參構造
public Student() {
//隱藏程式碼:呼叫了父類的無參構造(程式碼如下:)
super();//呼叫父類的構造器必須放在子類構造器的第一行
System.out.println("Student類的無參執行了!");
}
}
- super和this不能同時呼叫構造方法
Student類修改如下:
public class Student extends Person {
//無參構造
public Student() {
//隱藏程式碼:呼叫了父類的無參構造(程式碼如下:)
super();//呼叫父類的構造器必須放在子類構造器的第一行
this();//呼叫本類的有參構造 //報錯!!!
System.out.println("Student類的無參執行了!");
}
//有參構造
public Student(String name) {
}
}
報錯:報錯內容即‘this()'必須放在構造器的第一行,即super和this無法同時呼叫
- 當父類只有有參構造器,沒有無參構造器時,子類的無參構造中必須呼叫父類的有參構造,否則子類無法定義無參構造
注意:當父類的無參構造器顯示定義時即有無參,super()不會報錯!!
public class Person {
//有參構造
public Person(String name) {
System.out.println("Person類的無參執行了!");
}
}
public class Student extends Person {
//無參構造
public Student() {
//隱藏程式碼:呼叫了父類的無參構造(程式碼如下:)
super();//報錯!!!!!
//原因:父類沒有無參構造
System.out.println("Student類的無參執行了!");
}
}
-
報錯原因:父類沒有無參構造
-
**解決辦法:利用super("張三");呼叫父類的有參構造器如下:
super("張三");
super 與 this
- 代表的物件不同:
- this:本身呼叫者這個物件
- super:代表父類物件的應用
- 使用前提:
- this:沒有繼承也可以使用
- super:只有在繼承條件下才可以使用
- 構造方法:
- this():本類的構造
- super():父類的構造
三、重寫
重寫都是對方法的重寫,與屬性無關
靜態方法和非靜態方法的差別很大!!!
注意:父子類需是同一種方法,即均為靜態或均為非靜態
1、靜態方法:
- 方法的呼叫只和左邊定義的資料型別有關
//父類
public class B {
//靜態方法
public static void test(){
System.out.println("B=>test()");
}
}
//子類
public class A extends B {
//靜態方法
public static void test(){
System.out.println("A=>test()");
}
}
public class Application {
public static void main(String[] args) {
A a = new A();
a.test();//輸出A=>test();
B b = new A();//父類的引用(b)指向了A類
b.test();//輸出B=>test();!!!!
}
}
2、非靜態方法:
- 重寫
注意:重寫的方法不能是私有的方法(即private的方法)
//父類
public class B {
//非靜態方法
public void test(){
System.out.println("B=>test()");
}
}
//子類
public class A extends B {
//非靜態方法
//子類重寫了B(父類)的方法,那麼就是執行子類的方法
public void test(){
System.out.println("A=>test()");
}
}
public class Application {
public static void main(String[] args) {
A a = new A();
a.test();//輸出A=>test();
B b = new A();
b.test();//輸出A=>test();!!!!!
}
}
快速重寫父類方法:
- 在子類中按快捷鍵Alt+Insert(Fn+Alt+Insert),選擇Override Methods...
- 點選後會自動生成以下程式碼
//@Override:重寫
@Override //註解:有功能的註釋
//子類重寫了B(父類)的方法,那麼就是執行子類的方法
//沒有重寫則呼叫父類的方法
public void test() {
//內容可自行修改
System.out.println("A=>test()");
//super.test();//自動生成,super用於呼叫父類的方法
}
3、小結:
重寫需要有繼承關係,子類重寫父類的方法!
- 方法名必須相同
- 引數列表必須相同
- 修飾符:範圍可以擴大但不能縮小
- public>Protected>Default>private
- 丟擲的異常:範圍可以被縮小但不能擴大
- 例如:ClassNotFoundException(小) --->Exception(大)
- 子類的方法和父類的方法必須一致,但方法體不同
- 方法重寫快捷鍵:Alt+Insert
為什麼要重寫?
- 父類的功能,子類不一定需要或者不一定滿足
四、多型
多型:即同一方法可以根據傳送物件的不同而採用多種不同的行為方式
- 一個物件的實際型別是確定的,但可以指向物件的引用型別有很多(父類,有關係的類)
1、情況一:無重寫
//父類
public class Person {
public void run(){
System.out.println("run");
}
}
//父類
public class Person {
public void run(){
System.out.println("run");
}
}
//子類
public class Student extends Person {
public void say(){
System.out.println("say");
}
}
//測試類
public class Applicaton {
public static void main(String[] args) {
//一個物件的實際型別是確定的
//new Student(); 或者 new Person();
//但一個物件可以指向的引用型別是不確定的:父類的引用可以指向子類
//Studnet子類:可以呼叫自己的方法或者繼承父類的方法
Student s1 = new Student();
//Person父類:引用物件可以指向子類,但無法呼叫子類獨有的方法
Person s2 = new Student();
Object s3 = new Student();
//物件可以執行哪些方法,主要是看物件左邊的型別,和右邊關係不大!!
s1.run();//子類呼叫父類的方法,輸出run
s2.run();//輸出run
//s2.say();//錯誤!!!父類引用物件無法呼叫子類獨有的方法
//((Student)s2).say();//強制型別轉換即可呼叫
}
}
2、情況二:重寫
Student類重寫了父類的say()方法:
//子類
public class Student extends Person {
//重寫父類方法:會執行子類的方法
@Override
public void run() {
System.out.println("run_02");
}
public void say(){
System.out.println("say");
}
}
測試類和Person類不變:
//父類
public class Person {
public void run(){
System.out.println("run");
}
}
//測試類
public class Applicaton {
public static void main(String[] args) {
//Studnet子類:可以呼叫自己的方法或者繼承父類的方法
Student s1 = new Student();
//Person父類:引用物件可以指向子類,但無法呼叫子類獨有的方法
Person s2 = new Student();
Object s3 = new Student();
s1.run();//輸出run_02
s2.run();//重寫父類方法:會執行子類的方法 輸出run_02!!!!
}
}
3、注意事項:
- 多型是方法的多型,屬性沒有多型
- 多型的使用需要類之間有父子類關係
- 型別轉換異常會報錯:ClasCastException
- 多型存在條件:
- 需要有父子類關係(繼承關係)
- 方法需要重寫
- 父類的引用指向子類物件
- Father f1 = new Son();
- 無法被重寫的方法有:
- static方法:靜態方法屬於類和類一起載入,不屬於例項,無法被重寫
- final 常量
- private 方法
五、instanceof
判斷兩個類是否相似,是否可以比較轉換(型別轉換),相似返回true,不相似返回false,語法如下:
注意:有關係的兩個類才能進行比較
System.out.println(X instanceof Y);
public class Application {
public static void main(String[] args) {
//instanceof 二者有關係才能進行判斷
//Object > String
//Object > Person > Student
//Object > Person > Teacher
Object obj = new Student();
System.out.println(obj instanceof Person);//True
System.out.println(obj instanceof Student);//True
System.out.println(obj instanceof Object);//True
System.out.println(obj instanceof Teacher);//False; //Student類和Teacher類是同級
System.out.println(obj instanceof String);//False; //String是Object的子類
System.out.println("==============================");
Person person = new Student();
System.out.println(person instanceof Person);//True
System.out.println(person instanceof Student);//True
System.out.println(person instanceof Object);//True
System.out.println(person instanceof Teacher);//False;
//System.out.println(person instanceof String);//編譯報錯 //Person與String沒有聯絡無法比較
System.out.println("==============================");
Student student = new Student();
System.out.println(student instanceof Person);//True
System.out.println(student instanceof Student);//True
System.out.println(student instanceof Object);//True
//System.out.println(student instanceof Teacher);//編譯報錯 //Student類和Teacher類沒有聯絡無法比較
//System.out.println(person instanceof String);//編譯報錯
}
}
1、強制型別轉換
//父類
public class Person {
public void to(){
System.out.println("to");
}
}
//子類
public class Student extends Person {
public void go(){
System.out.println("go");
}
}
//測試類
public class Application {
public static void main(String[] args) {
//型別轉換:父類 子類
Student s1 = new Student();
s1.go();//子類方法
//子類轉為父類,可能會丟失自己本來的一些方法
//子類可以直接轉為父類
Person s2 = s1;
s2.to();//父類方法
//父類需強制轉換為子類
Person p1 = new Person();
//p1.go();//報錯,父類無法使用子類的方法,需要強制轉換
//強制轉換
Student p2 = (Student) p1;
p2.go();//子類方法
//以上兩句合為一句如下:
((Student)p1).go();
}
}
2、小結:
- 父類引用指向子類的物件‘
- 向上轉型:把子類轉為父類
- 向下轉型:把父類轉為子類(強制轉換)
- 強制轉換的作用:
- 方便方法的呼叫,減少重複程式碼