繼承
繼承概述
下面有一個學生類
public class Student{private String name;private int age;public void study(){System.out.println("努力學習了");}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 Teacher {private String name;private int age;public void teach(){System.out.println("教書育人");}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 Person{private String name;private int 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 Teacher extends Person{public void teach(){System.out.println("教書育人");}
}
【繼承的好處和弊端】
繼承好處:
- 提高了代碼的復用性(多個類相同的成員可以放到同一個類中)
- 提高了代碼的維護性(如果方法的代碼需要修改,修改一處即可)
繼承弊端:
- 繼承讓類與類之間產生了關系,類的耦合性增強了,當父類發生變化時子類實現也不得不跟著變化,削弱了子類的獨立性
繼承中變量的訪問特點
在子類方法中訪問一個變量
- 子類局部范圍找
- 子類成員范圍找
- 父類成員范圍找
- 如果都沒有就報錯(不考慮父親的父親…)
Super
public class Zi extends Fu {public int age = 20;public void show() {int age = 30;System.out.println(age + ",局部變量");// 我要訪問本壘的成員變量age,怎么辦呢?System.out.println(this.age + ",子類的成員變量");// 我要訪問父類的成員變量age, 怎么辦呢?System.out.println(super.age + ",父類的成員變量");}
}
super關鍵字的用法和this關鍵字的用法相似
- this: 代表本類對象的引用
- super: 代表父類存儲空間的標識(可以理解為父類對象引用)
關鍵字 | 訪問成員變量 | 訪問構造方法 | 訪問成員方法 |
---|---|---|---|
this | this.成員變量,訪問本類成員變量 | this(…)訪問本類構造方法 | this.成員方法()訪問本類成員方法 |
super | super.成員變量: 訪問父類成員變量 | super(…)訪問父類構造方法 | super.成員方法(…): 訪問父類成員方法 |
繼承中構造方法的訪問特點
子類中所有的構造方法默認都會訪問父類中無參的構造方法.
-
因為子類會繼承父類中的數據,可能還會使用父類的數據。所以,子類初始化之前,一定要先完成父類數據的初始化。
-
每一個子類的構造方法的第一條語句,默認都是: super()
如果父類中沒有無參構造方法,只有帶參構造方法,該怎么辦呢?
- 通過使用super關鍵字去顯示的調用父類的帶參構造方法
- 在父類中自己提供一個無參構造方法(推薦)
繼承中成員方法的訪問特點
通過子類對象訪問一個方法
- 子類成員范圍找
- 父類成員范圍找
- 如果沒有就報錯(不考慮父親的父親)
方法重寫
方法重寫: 子類中出現了和父類中一模一樣的方法聲明的現象
方法重寫的應用: 當子類需要父類的功能,而功能主體子類有自己的特有內容時,可以重寫父類中的方法,這樣,即沿襲了父類的功能,又定義了子類特有的內容
public class Phone {public void call(String name) {System.out.println("給" + name + "打電話");}
}
public class NewPhone extends Phone {// 在子類中重寫call功能@Overridepublic void call(String name) {System.out.println("開啟視頻功能");super.call(name);}
}
方法重寫注意事項
-
父類中的私有內容,子類中是繼承不到的
-
子類方法訪問權限不能更低 (public > 默認 > 私有)
Java中繼承的注意事項
- Java中類只支持單繼承,不支持多繼承
- Java中類支持多層繼承
public class GrandDad{public void drink(){System.out.println("爺爺愛喝酒");}
}
public class Father extends GrandDad{public void smoke(){System.out.println("爸爸愛抽煙");}
}
public class Son extends Father{}
栗子 - 包含關系
需求: 定義老師類和學生類,然后些代碼測試;最后找到老師類和學生類當中的共性內容,抽取出一個父類,用繼承的方式改寫代碼,并進行測試.
// Person.java
public class Person {private String name;private int age;public Person() {}public Person(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;}
}
// Teacher.java
public class Teacher extends Person {public Teacher() {}public Teacher(String name, int age) {super(name, age);}public void teach() {System.out.println("老師愛教書喲~");}
}
// Demo.java
public class Demo {public static void main(String[] args) {Teacher t1 = new Teacher();t1.setAge(18);t1.setName("Marron");System.out.println(t1.getName() + "," + t1.getAge());t1.teach();Teacher t2 = new Teacher("Mar", 17);System.out.println(t2.getName() + "," + t2.getAge());t2.teach();}
}
栗子 - 并列關系
需求: 請采用繼承的思想實現貓和狗的案例,并在測試類中進行測試
// Animal.java
public class Animal {private String name;private int age;public Animal() {}public Animal(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;}
}
// Dog.java
public class Dog extends Animal {public Dog() {}public Dog(String name, int age) {super(name, age);}public void lookDoor() {System.out.println("狗看門");}
}
// Cat.java
public class Cat extends Animal {public Cat() {}public Cat(String name, int age) {super(name, age);}public void catchMouse() {System.out.println("貓抓老鼠");}
}
修飾符
包
包: 其實就是文件夾,對類進行分類管理
包的定義格式:
- 格式: package 包名; (多級包名.分開)
- 范例: package com.marron;
帶包的Java類編譯和執行:
-
手動建包:
- 按照以前的格式編譯java文件:
javac HelloWorld.java
- 手動創建包:
在磁盤中建立文件夾com, 然后在com下建立文件家 marron
- 把class文件放到包的最里面:
把HelloWorld.class文件放到com下的marron這個文件夾下
- 按照以前的格式編譯java文件:
-
自動建包:
- 在使用javac命令編譯的時候:
javac -d . HelloWorld.java
- 執行的時候:
java com.marron.HelloWorld
- 在使用javac命令編譯的時候:
-
附上HelloWorld.java的代碼
// HelloWorld.java
package com.marron;public class HelloWorld{public static void main(String[] args){System.out.println("HelloWorld");}
}
導包
使用不同包下的類時,使用的時候要寫類的全路徑,寫起來太麻煩了
為了簡化帶包的操作,Java提供了導包的功能
導包的格式:
- 格式: import 包名;
- 范例: import cn.marz.Teacher
修飾符
權限修飾符
修飾符 | 同一個類中 | 同一個包中子類無關類 | 不同包的子類 | 不同包的無關類 |
---|---|---|---|---|
private | √ | |||
默認 | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
狀態修飾符 - final
final關鍵字是最終的意思,可以修飾成員方法,成員變量、類
final修飾的特點
- 修飾方法: 表示該方法是最終方法,不能被重寫
- 修飾變量: 表示該變量是常量,不能被再次賦值
- 修飾類: 表示該類是最終類,不能被繼承
final修飾局部變量
- 變量是基本類型: final修飾指的是基本類型的數據值不能發生改變
- 變量是引用類型: final修飾指的是引用類型的地址值不能發生改變,但是地址值里面的內容是可以發生改變的
狀態修飾符 - static
static關鍵字是靜態的意思,可以修飾成員方法、成員變量
public class Student {public String name;public int age;public static String university; // 表示學校類是共享屬性,只能被賦值一次public void show() {System.out.println(name + "," + age + "," + university);}
}/* 測試類 */
public class StaticDemo{public static void main(String[] args){Student.university = "華中科技大學";Student s1 = new Student();s1.name = "Marron";s1.age = 18;s1.show();Student s2 = new Student();s2.name = "Mar";s2.age = 17;s2.show();}
}
static修飾的特點
- 被類的所有對象共享
- 這也是我們判斷是否使用靜態關鍵字的條件
- 可以通過類名調用
- 當然,也可以通過對象名調用
static訪問特點
- 非靜態成員方法可以訪問所有的靜態成員(方法)、非靜態成員(方法)
- 靜態成員方法中,只能訪問靜態成員(方法)
public class Student {private String name = "Marron";private static String university = "華中科技大學";// 非靜態成員方法public void show1() {}// 非青苔成員方法public void show2() {System.out.println(name);System.out.println(university);show1();show3();}// 靜態成員方法public static void show3() {}// 靜態成員方法public static void show4() {
// System.out.println(name); // 報錯System.out.println(university);
// show1(); // 報錯show3();}
}
小結: 靜態成員方法只能訪問靜態成員
多態
同一個對象,在不同時刻表現出來的不同形態
// Animal.java
public class Animal {public void eat() {System.out.println("動物吃東西");}
}// Cat.java
public class Cat extends Animal {@Overridepublic void eat() {System.out.println("貓吃魚");}
}// AnimalDemo.java
public class AnimalDemo {public static void main(String[] args) {// 有父類引用指向子類實例Animal a = new Cat();}
}
多態的前提:
- 有繼承/實現關系
- 有方法重寫
- 有父類引用指向子類對象
多態中成員訪問的特點
// Animal.java
public class Animal {public int age = 40;public void eat() {System.out.println("動物吃東西");}
}// Cat.java
public class Cat extends Animal {public int age = 20;public int weight = 10;@Overridepublic void eat() {System.out.println("貓吃魚");}public void playGame() {System.out.println("貓捉迷藏");}
}// AnimalDemo.java
public class AnimalDemo {public static void main(String[] args) {// 有父類引用指向子類對象Animal a = new Cat();System.out.println(a.age);
// System.out.println(a.weight); // 報錯a.eat();
// a.playGame(); // 報錯}
}
- 成員變量: 編譯看左邊, 執行看左邊
a.age
全是Animal中的數據 - 成員方法: 編譯看左邊,執行看右邊
eat()方法可以使用,但是playGame()不能
為什么成員變量和成員方法的訪問不一樣呢?
- 因為成員方法有重寫,而成員變量沒有
多態的好處和弊端
// Animal.java
public class Animal {public void eat() {System.out.println("動物吃東西");}
}// Dog.java
public class Dog extends Animal {@Overridepublic void eat() {System.out.println("狗吃骨頭");}
}// Cat.java
public class Cat extends Animal {@Overridepublic void eat() {System.out.println("貓吃魚");}
}// AnimalOperator.java
public class AnimalOperator {/* 這里就用到了多態Animal a = new Cat();Animal a = new Dog();// 編譯看Animal中有無eat方法, 執行分別看Cat和Dog中eat方法是否重寫*/public void useAnimal(Animal a) {a.eat();}
}// 測試類: AnimalDemo.java
public class AnimalDemo {public static void main(String[] args) {// 創建動物操作類的對象,調用方法AnimalOperator ao = new AnimalOperator();Cat c = new Cat();ao.useAnimal(c);Dog d = new Dog();ao.useAnimal(d);}
}
- 多態的好處: 提高了程序的擴展性
- 具體體現: 定義方法的時候,使用父類型作為參數,將來在使用的時候,使用具體的子類型參與操作
- 多態的弊端: 不能使用子類的特有功能
多態中的轉型
public class Animal {public void eat() {System.out.println("動物吃東西");}
}public class Cat extends Animal {@Overridepublic void eat() {System.out.println("貓吃魚");}public void playGame() {System.out.println("貓捉迷藏");}
}public class AnimalDemo {public static void main(String[] args) {// 多態Animal a = new Cat(); // 向上轉型:子類對象轉為父類a.eat();// 向下轉型Cat c = (Cat) a;c.eat();c.playGame();}
}
栗子
需求: 用多態的思想實現貓和狗的案例,并在測試類中進行測試
// Animal.java
public class Animal {private String name;private int age;public Animal() {}public Animal(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 void eat() {System.out.println("動物吃東西");}
}// Cat.java
public class Cat extends Animal {public Cat() {}public Cat(String name, int age) {super(name, age);}@Overridepublic void eat() {System.out.println("貓吃魚");}
}// AnimalDemo.java
public class AnimalDemo {public static void main(String[] args) {Animal a = new Cat();a.setName("加菲");a.setAge(5);System.out.println(a.getName() + "," + a.getAge());a.eat();a = new Cat("加菲", 5);System.out.println(a.getName() + "," + a.getAge());a.eat();}
}
抽象類
在Java中,一個沒有方法體得方法應該定義為抽象方法,而類如果有抽象方法,改類必須定義為抽象類
抽象類的特點
-
抽象類和抽象方法必須使用abstract關鍵字修飾
public abstract class 類名 {}
public abstract void eat()
-
抽象類中不一定有抽象方法,有抽象方法的類一定是抽象類
-
抽象類不能實例化
- 抽象類如何實例化? 參照多態的方式,通過子類對象實例化,這叫抽象類多態
-
抽象類的子類
- 要么重寫抽象類中的所有抽象方法
- 要么是抽象類
抽象類的成員特點
-
成員變量:
- 可以是變量:
private int age = 20
- 也可以是常量:
private final String city = "武漢"
- 可以是變量:
-
構造方法:
- 有構造方法,但是不能實例化
- 構造方法用于子類訪問父類數據的初始化
-
成員方法:
- 可以是抽象方法: 限定子類必須完成某些動作
- 也可以是非抽象方法: 提高代碼的復用性
接口
接口: 就是一種公共的規范標準,只要符合規范,大家都可以通用
Java中的接口更多的體現在對行為的抽象
接口的特點
-
接口用關鍵字
interface
修飾-
public interface Jumpping {public abstract void jump(); }
-
-
類實現接口用implements表示
-
public class Cat implements Jumpping{@overridepublic void jump(){System.out.print("貓可以跳高了");} }
-
-
接口不能實例化
- 如何實現接口實例化? 參照多態的實現方式,通過實現類對象實例化,這叫接口多態
多態的形式: 具體類多態、抽象類多態、接口多態
多態的前提: 有繼承或者實現關系; 有方法重寫; 有父(類/接口)引用指向(子/實現)類對象
- 接口的實現類:
- 要么重寫接口中的所有抽象方法
- 要么是抽象類
接口的成員特點
-
成員變量:
- 只能是常量
- 默認修飾符:
public static final
-
構造方法
- 接口沒有構造方法,因為接口主要是對行為進行抽象的,是沒有具體存在
- 一個類如果沒有父類,默認繼承自Object類
-
成員方法
- 只能是抽象方法
- 默認修飾符:
public abstract
類和接口的關系
-
類和類的關系
- 繼承關系, 只能單繼承,但是可以多層繼承
-
類和接口的關系
- 實現關系,可以單實現,也可以多實現,還可以在繼承一個類的同時實現多個接口
public class InterImpl extends Object implements Inter1, Inter2, Inter3
-
接口和接口的關系
- 繼承關系, 可以單繼承,也可以多繼承
public interface Inter2 extends Inter1, Inter3
抽象類和接口的區別
- | 抽象類 | 接口 |
---|---|---|
成員區別 | 既有變量也有常量; 既有抽象方法,也有非抽象方法 | 只要常量(final)和抽象方法 |
設計理念區別 | 對類抽象,包括屬性和行為 | 對行為抽象,主要是行為 |
【小栗子】: 門和警報
需求: 門: 有open()和close()兩個動作,下面使用抽象類和接口來定義這個抽象概念.有的門具備報警功能,有的門不具備.因此可如下設計.
public interface Alarm{void alarm():
}public abstract class Door{public abstract void open();public abstract void close();
}
public class AlaramDoor extends Door implements Alarm {@overridepublic void open(){// ...}@overridepublic void close(){// ...}@overridepublic void alarm(){// ...}
}
在這里,我們再次強調抽象類是對事物的抽象,而接口是對行為的抽象.
形參和返回值
抽象類名作為形參和返回值
- 方法的形參是抽象類型,其實需要的是該抽象類的子類對象
- 方法的返回值是抽象類名,其實返回的是該抽象類的子類對象
接口名作為形參和返回值
-
方法的形參是接口名,其實需要的是該接口的實現類對象
-
方法的返回值是接口名,其實返回的是該接口的實現類對象
// Jummping.java
public interface Jumpping {void jump(); // public abstract void jump();
}// Cat.java
public class Cat implements Jumpping {@Overridepublic void jump() {System.out.println("貓跳高高");}
}// JumppingOperator.java
public class JummpingOperator{public void sueJumpping(Jumpping j){j.jump();}public Jummping getJumpping(){Jumpping j = new Cat();return j;}
}// JumppingDemo.java
public class JumppingDemo{public static void main(String[] args){// 創建操作對象,并調用方法JumppingOperator jo = new JumppingOperator();Jumpping j = new Cat();jo.useJumpping(j);System.out.println("--------");Jumpping j2 = jo.getJumpping();j2.jump();}
}
內部類
內部類: 就是在一個類中定義一個類
public class Outer{public class Inner{}
}
內部類與外部類的交互
public class Outer {private int num = 10;public class Inner {public void show() {// 內部類可以直接訪問外部類的私有變量System.out.println(num);}}// 外部類使用內部類時,需要先new一個內部類實例public void method(){Inner i = new Inner();i.show();}
}
成員內部類
成員內部類: 在類中定義的類
public class Outer {private int num = 10;public class Inner {public void show() {System.out.println(num);}}
}
- 訪問如下:
public class InnerDemo {public static void main(String[] args) {// 創建內部類對象,并調用方法Outer.Inner i = new Outer().new Inner();i.show();}
}/*格式: 外部類名.內部類名 對象名 = 外部類對象.內部類對象;
*/
一般來說,我們創建內部類,就是不希望該類直接被引用
可以參照類的形式,為內部類添加一個訪問方法
// Outer.java
public class Outer{private int num = 10;private class Inner{public void show(){System.out.println(num);}}public void innerShow() {Inner i = new Inner();i.show();}
}// InnerDemo.java
public class InnerDemo{public static void main(String[] args){Outer o = new Outer();o.innerShow();}
}
局部內部類
寫在外部類的方法里面
// Outer.java
public class Outer{private int num = 10;public void method(){class Inner{public void show(){System.out.println(num);}}}
}
局部內部類的調用,需要在外部類的方法中創建這個局部內部類的實例,然后間接調用局部內部類的方法
// Outer.java
public class Outer{private int num = 10;public void method(){Class Inner{public void show(){System.out.println(num);}}Inner i = new Inner();i.show();}
}// InnerDemo.java
public class InnerDemo{public static void main(String[] args){Outer o = new Outer();o.method();}
}
匿名內部類
new Inter(){public void show(){// ...}
}
本質: 是一個繼承了該類或者實現了該接口的子類匿名對象
上面提及繼承類或者接口,那么首先得有一個接口
// Inter.java
public interface Inter{void show();
}// Outer.java
// 在Outer類中實現匿名內部類
public class Outer{public void method(){Inter i = new Inter(){@Overridepublic void show(){System.out.println("匿名內部類");}};i.show(); // 調用重寫后的show方法i.show();}
}// 測試類: OuterDemo.java
public class OuterDemo{public static void main(String[] args){Outer o = new Outer();o.method();}
}
【小栗子】
// Jumpping.java
public interface Jumpping{void jump();
}// JunmppingOperator.java
public class JumppingOperator{public void method(Jumping j){j.jump();}
}
以上有一個接口Jumping
以及一個接口操作類JumppingOperator
, 接口中定義的方法是抽象的.因此調用需要使用匿名內部類來實現
// JumppingDemo.java
public class JumppingDemo{public static void main(String[] args){JumppingOperator jo = new JumppingOperator();jo.method(new Jumpping(){@Overridepublic void jump(){System.out.println("貓可以跳高高了");}})}
}