文章目錄
繼承
在繼承的關係中,“子類就是一個父類”。也就是說,子類可以被當做父類看待。
例如父類是員工,子類是講師,那麼“講師就是一個員工”。這樣的關係:is-a。
定義父類的格式:(一個普通的定義)
public class 父類名稱{
// …
}
定義子類的格式:
public class 子類名稱 extends 父類名稱{
// …
}
//定義一個父類:員工
public class Employee {
public void method(){
System.out.println("方法執行!");
}
}
// 定義了一個員工的子類:講師
public class Teacher extends Employee{
}
// 定義了一個員工的子類:助教
public class Assistant extends Employee {
}
public class Demo01Extends {
public static void main(String[] args) {
// 創建了一個子類對象
Teacher teacher = new Teacher();
// Teacher類當中雖然什麼都沒寫,但是會繼承來自父類的method方法
teacher.method();
//創建另一個子類助教的對象
Assistant assistance = new Assistant();
assistance.method();
}
}
繼承中成員變量的訪問特點
public class Fu {
int numFu = 10;
int num = 100;//跟子類中變量重名
public void methodFu(){
// 使用的是本類當中的,不會向下找子類的
System.out.println(num);
}
}
public class Zi extends Fu{
int numZi = 20;
int num = 200;
public void methodZi(){
// 因爲本類當中有num,所以這裏用的是本類的num
System.out.println(num);
}
}
在父子類的繼承關係當中,如果成員變量重名,則創建子類對象時,訪問有兩種方式:
- 直接通過子類對象訪問成員變量:
等號左邊是誰,就優先用誰,沒有則向上找。 - 間接通過成員方法訪問成員變量:
該方法屬於誰,就優先用誰,沒有則向上找。
public class Demo01ExtendsField {
public static void main(String[] args) {
Fu fu = new Fu();//創建父類對象
System.out.println(fu.numFu);//只能使用父類的東西,沒有任何子類內容
Zi zi = new Zi();
System.out.println(zi.numFu);// 10
System.out.println(zi.numZi);// 20
System.out.println(zi.num); // 優先子類,200
// 這個方法是子類的,就優先用子類的,沒有再向上找
zi.methodZi(); // 200
// 這個方法是在父類當中定義的
zi.methodFu(); // 100
}
}
區分子類方法中重名的三種變量
局部變量: 直接寫
本類的成員變量: this.成員變量名
父類的成員變量: super.成員變量名
// 父類
public class Fu {
int num = 10;
}
// 子類
public class Zi extends Fu {
int num = 20;
public void method() {
int num = 30;//局部變量
System.out.println(num);//30,局部變量
System.out.println(this.num);//20,本類的成員變量
System.out.println(super.num);//10,父類的成員變量
}
}
繼承中成員方法的訪問特點
在父子類的繼承關係中,創建子類對象,訪問成員方法的規則
創建的對象是誰,就優先用誰,如果沒有則向上找
【注意事項】
無論是成員方法還是成員變量,如果沒有都是向上找父類,絕對不會向下找子類的。
public class Fu {
public void methodFu(){
System.out.println("父類方法執行");
}
public void method(){
System.out.println("父類重名方法執行");
}
}
public class Zi extends Fu {
public void methodZi(){
System.out.println("子類方法執行");
}
public void method(){
System.out.println("子類重名方法執行");
}
}
public class Demo01ExtendsMethod {
public static void main(String[] args) {
Zi zi = new Zi();
zi.methodFu(); //父類方法執行
zi.methodZi(); //子類方法執行
// 創建的是new了的子類對象,所以優先用子類方法
zi.method(); //子類重名方法執行
}
}
繼承中方法的覆蓋重寫
重寫(Override)概念
在繼承關係當中,方法的名稱一樣,參數列表也一樣
重寫(Override):方法的名稱一樣,參數列表【也一樣】。 覆蓋、覆寫。
重載(Overload):方法的名稱一樣,參數列表【不一樣】。
方法的覆蓋重寫特點
創建的是子類對象,則優先用子類方法。
方法覆蓋重寫的注意事項:
- 必須保證父子類方法的名稱相同,參數列表也相同
@override:寫在方法前面,用來檢測是不是有效的正確覆蓋重寫(起到安全檢測的作用)
這個註解就算不寫,只要滿足要求,也是正確的覆蓋重寫 - 子類方法的返回值必須【小於等於】父類方法的返回值範圍
java.lang.Object類是所有類的公共最高父類(祖宗類),java.lang.String就是Object的子類 - 子類方法的權限必須【大於等於】父類方法的權限修飾符。
public > protected > (default) > private
備註:(default)不是關鍵字default,而是什麼都不寫。留空。
public\protected\default\private的區別
private : 在同一類內可見。使用對象:變量、方法。 注意:不能修飾類(外部類)
default (即缺省,什麼也不寫,不使用任何關鍵字): 在同一包內可見,不使用任何修飾符。使用對象:類、接口、變量、方法。
protected : 對同一包內的類和所有子類可見。使用對象:變量、方法。 注意:不能修飾類(外部類)。
public : 對所有類可見。使用對象:類、接口、變量、方法
public class Fu {
private Object method(){
return null;
}
}
public class Zi extends Fu {
@Override
public String method(){
return null;
}
}
覆蓋重寫的應用
// 父類:老手機
public class Phone {
public void call(){
System.out.println("打電話");
}
public void send(){
System.out.println("發短信");
}
public void show(){
System.out.println("顯示號碼");
}
}
//子類:新手機
public class NewPhone extends Phone{
@Override //輸入show,回車
public void show() {
super.show(); //把父類的show方法拿過來重複利用
// 自己子類再來添加更多內容
System.out.println("顯示姓名");
System.out.println("顯示頭像");
}
}
public class Demo01Phone {
public static void main(String[] args) {
Phone phone = new Phone();
phone.call();
phone.send();
phone.show();
System.out.println("=============");
NewPhone newPhone = new NewPhone();
newPhone.call();
newPhone.send();
newPhone.show();
}
}
結果:
繼承中構造方法的訪問特點
public class Fu {
public Fu(){
System.out.println("父類無參構造");
}
public Fu(int num){
System.out.println("父類有參構造");
}
}
public class Zi extends Fu {
public Zi(){
//下面這兩個只能調用一個
// super();//隱含的,不用寫,自帶
super(20);//在調用父類重載的構造方法
System.out.println("子類構造方法");
}
public void method(){
// super(); //錯誤寫法!只有子類構造方法,才能調用父類構造方法
}
}
繼承關係中,父子類構造方法的訪問特點:
- 子類構造方法中有一個默認隱含的“super()”調用,所以一定是先調用父類構造,後執行子類構造
public class Demo01Constructor {
public static void main(String[] args) {
Zi zi = new Zi();//先出父,再出子
}
}
結果
2. 子類構造通過super關鍵字來調用父類重載構造
3. super的父類構造調用,必須是子類構造方法的第一個語句。不能一個子類構造調用多次super構造。
4. 子類必須調用父類構造方法,不寫則贈送super(),寫了則用寫的指定的super()調用,super只能有一個,還必須是第一個。
super關鍵字的三種用法
- 在子類的成員方法中,訪問父類的成員變量
- 在子類的成員方法中,訪問父類的成員方法
- 在子類的構造方法中,訪問父類的構造方法
this關鍵字的三種用法
super關鍵字用來訪問父類內容,this關鍵字用來訪問本類內容
- 在本類的成員方法中,訪問本類的成員變量
- 在本類的成員方法中,訪問本類的另一個成員方法
- 在本類的構造方法中,訪問本類的另一個構造方法
在第三種用法種要注意:
A. this(…)調用也必須是構造方法的第一個語句
B. super和this兩種構造調用,不能同時使用
public class Zi extends Fu {
int num = 20;
public Zi(){
// super();
// 1.在本類的構造方法中,訪問本類的另一個構造方法
this(123);//本類的無參構造,調用本類的有參構造
// this(1,2); // 寫兩個不行
}
public Zi(int n){
this(1,2);
}
public Zi(int m,int n){
// this(); //形成循環,不行!
super();
}
public void showNum(){
int num = 10;
System.out.println(num);//局部變量
//1.在本類的成員方法中,訪問本類的成員變量
System.out.println(this.num);//本類中的成員變量
System.out.println(super.num); //父類中的成員變量
}
public void methodA(){
System.out.println("AAA");
}
public void methodB(){
this.methodA();//2.在本類的成員方法中,訪問本類的另一個成員方法
System.out.println("BBB");
}
}
super與this關鍵字圖解
java繼承的三個特點
抽象
圖解
抽象類和抽查方法
抽象方法:加上abstract關鍵字,然後去掉大括號,直接分號結束
抽象類:抽象方法所在的類必須是抽象類,在class之前寫上abstract即可
如何使用抽象類和抽象方法
- 不能直接創建抽象類對象。
- 必須用一個子類來繼承抽象父類
- 子類必須覆蓋重寫抽象父類中所有抽象方法
覆蓋重寫(實現):去掉抽象方法的abstract關鍵字,然後不上方法體大括號 - 創建子類對象進行使用
public abstract class Animal {
// 這是一個抽象方法
public abstract void eat();
// 這是普通的成員方法
public void normalMethod(){
}
}
public class Cat extends Animal {
@Override
public void eat(){
System.out.println("貓喫魚");
}
}
public class DemoMain {
public static void main(String[] args) {
// Animal animal = new Animal();//錯誤寫法,不能直接創建抽象類對象
Cat cat = new Cat();
cat.eat();
}
}
抽象方法和抽象類的注意事項
- 抽象類中,可以有構造方法,是供子類創建對象時,初始化父類成員使用的
理解:子類的構造方法中,有默認的super(),需要訪問父類構造方法
public abstract class Fu {
public Fu(){
System.out.println("抽象父類的構造方法執行");
}
public abstract void eat();
}
public class Zi extends Fu {
public Zi(){
//super();
System.out.println("子類構造方法執行");
}
@Override
public void eat() {
System.out.println("喫飯飯");
}
}
public class DemoMain {
public static void main(String[] args) {
Zi zi = new Zi();
zi.eat();
}
}
結果
- 抽象類中,不一定有抽象方法,但抽象方法一定要在抽象類中。
這樣沒有抽象方法的抽象類,也不能直接創建對象,在一些特殊場合下有用途。 - 抽象類的子類,必須覆蓋重寫所有父類的抽象方法,否則,編譯無法通過而報錯,除非該子類也是抽象類
理解:假設不重寫所有抽象方法,則類中可能包含抽象方法,那麼創建對象後,調用抽象的方法,沒有意義。
// 最高抽象父類
public abstract class Animal {
public abstract void sleep();
public abstract void eat();
}
// 子類也是一個抽象類
public abstract class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗喫骨頭");
}
// public abstract void sleep();
}
// 孫子輩的
public class DogGolden extends Dog{
@Override
public void sleep() {
System.out.println("呼呼呼~");
}
}
// 孫子輩的
public class Dog2Ha extends Dog {
@Override
public void sleep() {
System.out.println("嘿嘿嘿~");
}
}
public class DemoMain {
public static void main(String[] args) {
// Animal animal = new Animal();//錯誤!
// Dog dog = new Dog();//錯誤,也是抽象類
Dog2Ha ha = new Dog2Ha();//這是普通類,可以直接new對象
ha.sleep();
ha.eat();
}
}
發紅包案例
羣主發普通紅包,某羣有多名成員,羣主給成員發普通紅包,普通紅包的規則:
- 羣主的一筆金額,從羣主餘額中扣除,平均分成n等份,讓成員領取
- 成員領取紅包後,保存到成員餘額中。
題目分析
public abstract class User {
private String name; //姓名
private int money; //餘額
public User() {
}
public User(String name, int money) {
this.name = name;
this.money = money;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
//展示一下當前用戶有多少錢
public void show(){
System.out.println("我叫:"+name+",我有"+money+"元錢");
}
}
//羣主
public class Manager extends User {
public Manager() {
}
public Manager(String name, int money) {
super(name, money);
}
public ArrayList<Integer> send(int totalMoney, int count) {
//用一個集合存儲若干紅包的金額
ArrayList<Integer> redList = new ArrayList<>();
// 首先看一下羣主自己有多少錢
int leftMoney = super.getMoney();//羣主當前餘額
if (totalMoney > leftMoney) {
System.out.println("餘額不足");
return redList; //返回空集合
}
// 扣錢,重新設置餘額
super.setMoney(leftMoney - totalMoney);
// 發紅包,需要平均拆分成爲count份
int avg = totalMoney / count;
int mod = totalMoney % count;//餘數
//剩下的零頭包在最後一個紅包當中
//下面把紅包一個一個放到集合當中
for (int i = 0; i < count-1; i++) {
redList.add(avg);
}
redList.add(avg+mod);
return redList;
}
}
public class Member extends User {
public Member(){
}
public Member(String name,int money){
super(name, money);
}
public void receive(ArrayList<Integer> list){
//從多個紅包中隨便抽取一個,給自己
//所及獲取一個集合當中的索引編號
int index = new Random().nextInt(list.size());
//根據索引,從集合當中刪除
int delta = list.remove(index);
// 當前成員本來由多少錢
int money = super.getMoney();
//成員錢變多
super.setMoney(money+delta);
}
}
public class MainRedPacket {
public static void main(String[] args) {
Manager manager = new Manager("羣主",100);
Member one = new Member("成員A",0);
Member two = new Member("成員B",0);
Member three = new Member("成員C",0);
manager.show();
one.show();
two.show();
three.show();
System.out.println("============");
// 羣主發紅包
ArrayList<Integer> redList = manager.send(20,3);
// 成員收紅包
one.receive(redList);
two.receive(redList);
three.receive(redList);
manager.show();
one.show();
two.show();
three.show();
}
}
太難了,要多看幾遍