Java筆記4

第一章 static關鍵字

2.1 概述

以前我們定義過如下類:

public class Student {// 成員變量public String name;public char sex; // '男'  '女'public int age;// 無參數構造方法public Student() {}// 有參數構造方法public Student(String  a) {}
}

我們已經知道面向對象中,存在類和對象的概念,我們在類中定義了一些成員變量,例如name,age,sex ,結果發現這些成員變量,每個對象都存在(因為每個對象都可以訪問)。

而像name ,age , sex確實是每個學生對象都應該有的屬性,應該屬于每個對象。

所以Java中成員(變量和方法)等是存在所屬性的,Java是通過static關鍵字來區分的。static關鍵字在Java開發非常的重要,對于理解面向對象非常關鍵。

關于 static 關鍵字的使用,它可以用來修飾的成員變量和成員方法,被static修飾的成員是屬于類的是放在靜態區中,沒有static修飾的成員變量和方法則是屬于對象的。我們上面案例中的成員變量都是沒有static修飾的,所以屬于每個對象。

2.2 定義格式和使用

static是靜態的意思。 static可以修飾成員變量或者修飾方法。

2.2.1 靜態變量及其訪問

有static修飾成員變量,說明這個成員變量是屬于類的,這個成員變量稱為類變量或者靜態成員變量。 直接用 類名訪問即可。因為類只有一個,所以靜態成員變量在內存區域中也只存在一份。所有的對象都可以共享這個變量。
如何使用呢
例如現在我們需要定義傳智全部的學生類,那么這些學生類的對象的學校屬性應該都是“傳智”,這個時候我們可以把這個屬性定義成static修飾的靜態成員變量。
定義格式

修飾符 static 數據類型 變量名 = 初始值;

public class Student {public static String schoolName = "傳智播客"// 屬于類,只有一份。// .....
}

靜態成員變量的訪問:

格式:類名.靜態變量

public static void  main(String[] args){System.out.println(Student.schoolName); // 傳智播客Student.schoolName = "黑馬程序員";System.out.println(Student.schoolName); // 黑馬程序員
}

2.2.2 實例變量及其訪問

無static修飾的成員變量屬于每個對象的, 這個成員變量叫實例變量,之前我們寫成員變量就是實例成員變量。
需要注意的是:實例成員變量屬于每個對象,必須創建類的對象才可以訪問。
格式:對象.實例成員變量

2.2.3 靜態方法及其訪問

有static修飾成員方法,說明這個成員方法是屬于類的,這個成員方法稱為類方法或者靜態方法**。 直接用 類名訪問即可。因為類只有一個,所以靜態方法在內存區域中也只存在一份。所有的對象都可以共享這個方法。
與靜態成員變量一樣,靜態方法也是直接通過類名.方法名稱即可訪問。

舉例

public class Student{public static String schoolName = "傳智播客"// 屬于類,只有一份。// .....public static void study(){System.out.println("我們都在黑馬程序員學習");   }
}

靜態成員變量的訪問:

格式:類名.靜態方法

public static void  main(String[] args){Student.study();
}

2.2.4 實例方法及其訪問

無static修飾的成員方法屬于每個對象的,這個成員方法也叫做實例方法

需要注意的是:實例方法是屬于每個對象,必須創建類的對象才可以訪問。

格式:對象.實例方法

示例

public class Student {// 實例變量private String name ;// 2.方法:行為// 無 static修飾,實例方法。屬于每個對象,必須創建對象調用public void run(){System.out.println("學生可以跑步");}// 無 static修飾,實例方法public  void sleep(){System.out.println("學生睡覺");}public static void study(){}
}
public static void main(String[] args){// 創建對象 Student stu = new Student ;stu.name = "徐干";// Student.sleep();// 報錯,必須用對象訪問。stu.sleep();stu.run();
}

2.3 小結

1.當 static 修飾成員變量或者成員方法時,該變量稱為靜態變量,該方法稱為靜態方法。該類的每個對象都共享同一個類的靜態變量和靜態方法。任何對象都可以更改該靜態變量的值或者訪問靜態方法。但是不推薦這種方式去訪問。因為靜態變量或者靜態方法直接通過類名訪問即可,完全沒有必要用對象去訪問。

2.無static修飾的成員變量或者成員方法,稱為實例變量,實例方法,實例變量和實例方法必須創建類的對象,然后通過對象來訪問。

3.static修飾的成員屬于類,會存儲在靜態區,是隨著類的加載而加載的,且只加載一次,所以只有一份,節省內存。存儲于一塊固定的內存區域(靜態區),所以,可以直接被類名調用。它優先于對象存在,所以,可以被所有對象共享。

4.無static修飾的成員,是屬于對象,對象有多少個,他們就會出現多少份。所以必須由對象調用。

第三章 繼承

3.1 概述

3.1.1 引入

假如我們要定義如下類:
學生類,老師類和工人類,分析如下。

  1. 學生類
    屬性:姓名,年齡
    行為:吃飯,睡覺
  2. 老師類
    屬性:姓名,年齡,薪水
    行為:吃飯,睡覺,教書
  3. 班主任
    屬性:姓名,年齡,薪水
    行為:吃飯,睡覺,管理

如果我們定義了這三個類去開發一個系統,那么這三個類中就存在大量重復的信息(屬性:姓名,年齡。行為:吃飯,睡覺)。這樣就導致了相同代碼大量重復,代碼顯得很臃腫和冗余,那么如何解決呢?

假如多個類中存在相同屬性和行為時,我們可以將這些內容抽取到單獨一個類中,那么多個類無需再定義這些屬性和行為,只要繼承那一個類即可。如圖所示:
在這里插入圖片描述
其中,多個類可以稱為子類,單獨被繼承的那一個類稱為父類超類(superclass)或者基類

3.1.2 繼承的含義

繼承描述的是事物之間的所屬關系,這種關系是:is-a 的關系。例如,兔子屬于食草動物,食草動物屬于動物。可見,父類更通用,子類更具體。我們通過繼承,可以使多種事物之間形成一種關系體系。
繼承:就是子類繼承父類的屬性行為,使得子類對象可以直接具有與父類相同的屬性、相同的行為。子類可以直接訪問父類中的非私有的屬性和行為。

3.1.3 繼承的好處

  1. 提高代碼的復用性(減少代碼冗余,相同代碼重復利用)。
  2. 使類與類之間產生了關系。

3.2 繼承的格式

通過 extends 關鍵字,可以聲明一個子類繼承另外一個父類,定義格式如下:

class 父類 {...
}class 子類 extends 父類 {...
}

需要注意:Java是單繼承的,一個類只能繼承一個直接父類,跟現實世界很像,但是Java中的子類是更加強大的。

3.3 繼承案例

3.3.1 案例

請使用繼承定義以下類:

  1. 學生類
    屬性:姓名,年齡
    行為:吃飯,睡覺
  2. 老師類
    屬性:姓名,年齡,薪水
    行為:吃飯,睡覺,教書
  3. 班主任
    屬性:姓名,年齡,薪水
    行為:吃飯,睡覺,管理

3.3.2案例圖解分析

老師類,學生類,還有班主任類,實際上都是屬于人類的,我們可以定義一個人類,把他們相同的屬性和行為都定義在人類中,然后繼承人類即可,子類特有的屬性和行為就定義在子類中了。

如下圖所示。
請添加圖片描述

3.3.3 案例代碼實現

1.父類Human類

 public class Human {// 合理隱藏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;}}

** 2.子類Teacher類**

public class Teacher extends Human {  // 工資   private double salary ;     // 特有方法  public void teach(){        System.out.println("老師在認真教技術!")}?    public double getSalary() {   return salary; }?   public void setSalary(double salary) {        this.salary = salary;   }}

3.子類Student類

public class Student extends Human{}

4.子類BanZhuren類

public class Teacher extends Human {// 工資private double salary ;// 特有方法public void admin(){System.out.println("班主任強調紀律問題!")}public double getSalary() {return salary;}public void setSalary(double salary) {this.salary = salary;}
}

5.測試類

  public class Test {public static void main(String[] args) {Teacher dlei = new Teacher();dlei.setName("播仔");dlei.setAge("31");dlei.setSalary(1000.99);System.out.println(dlei.getName());System.out.println(dlei.getAge());System.out.println(dlei.getSalary());dlei.teach();BanZhuRen linTao = new BanZhuRen();linTao.setName("靈濤");linTao.setAge("28");linTao.setSalary(1000.99);System.out.println(linTao.getName());System.out.println(linTao.getAge());System.out.println(linTao.getSalary());linTao.admin();Student xugan = new Student();xugan.setName("播仔");xugan.setAge("31");//xugan.setSalary(1000.99); // xugan沒有薪水屬性,報錯!System.out.println(xugan.getName());System.out.println(xugan.getAge());}}

3.4 子類不能繼承的內容

3.4.1 引入

并不是父類的所有內容都可以給子類繼承的:

子類不能繼承父類的構造方法。

值得注意的是子類可以繼承父類的私有成員(成員變量,方法),只是子類無法直接訪問而已,可以通過getter/setter方法訪問父類的private成員變量。

3.4.1 演示代碼

public class Demo03 {public static void main(String[] args) {Zi z = new Zi();System.out.println(z.num1);
//		System.out.println(z.num2); // 私有的子類無法使用// 通過getter/setter方法訪問父類的private成員變量System.out.println(z.getNum2());z.show1();// z.show2(); // 私有的子類無法使用}
}class Fu {public int num1 = 10;private int num2 = 20;public void show1() {System.out.println("show1");}private void show2() {System.out.println("show2");}public int getNum2() {return num2;}public void setNum2(int num2) {this.num2 = num2;}
}class Zi extends Fu {
}

3.5 繼承后的特點—成員變量

當類之間產生了繼承關系后,其中各類中的成員變量,又產生了哪些影響呢?

3.5.1 成員變量不重名

如果子類父類中出現不重名的成員變量,這時的訪問是沒有影響的。代碼如下:

class Fu {// Fu中的成員變量int num = 5;
}
class Zi extends Fu {// Zi中的成員變量int num2 = 6;// Zi中的成員方法public void show() {// 訪問父類中的numSystem.out.println("Fu num="+num); // 繼承而來,所以直接訪問。// 訪問子類中的num2System.out.println("Zi num2="+num2);}
}
class Demo04 {public static void main(String[] args) {// 創建子類對象Zi z = new Zi(); // 調用子類中的show方法z.show();  }
}演示結果:
Fu num = 5
Zi num2 = 6

3.5.2 成員變量重名

如果子類父類中出現重名的成員變量,這時的訪問是有影響的。代碼如下:

class Fu1 {// Fu中的成員變量。int num = 5;
}
class Zi1 extends Fu1 {// Zi中的成員變量int num = 6;public void show() {// 訪問父類中的numSystem.out.println("Fu num=" + num);// 訪問子類中的numSystem.out.println("Zi num=" + num);}
}
class Demo04 {public static void main(String[] args) {// 創建子類對象Zi1 z = new Zi1(); // 調用子類中的show方法z1.show(); }
}
演示結果:
Fu num = 6
Zi num = 6

子父類中出現了同名的成員變量時,子類會優先訪問自己對象中的成員變量。如果此時想訪問父類成員變量如何解決呢?我們可以使用super關鍵字。

3.5.3 super訪問父類成員變量

子父類中出現了同名的成員變量時,在子類中需要訪問父類中非私有成員變量時,需要使用super 關鍵字,修飾父類成員變量,類似于之前學過的 this
需要注意的是:super代表的是父類對象的引用,this代表的是當前對象的引用。

使用格式:

super.父類成員變量名

子類方法需要修改,代碼如下:

class Fu {// Fu中的成員變量。int num = 5;
}class Zi extends Fu {// Zi中的成員變量int num = 6;public void show() {int num = 1;// 訪問方法中的numSystem.out.println("method num=" + num);// 訪問子類中的numSystem.out.println("Zi num=" + this.num);// 訪問父類中的numSystem.out.println("Fu num=" + super.num);}
}class Demo04 {public static void main(String[] args) {// 創建子類對象Zi1 z = new Zi1(); // 調用子類中的show方法z1.show(); }
}演示結果:
method num=1
Zi num=6
Fu num=5

3.6 繼承后的特點—成員方法

3.6.1 成員方法不重名

如果子類父類中出現不重名的成員方法,這時的調用是沒有影響的。對象調用方法時,會先在子類中查找有沒有對應的方法,若子類中存在就會執行子類中的方法,若子類中不存在就會執行父類中相應的方法。代碼如下:

class Fu {public void show() {System.out.println("Fu類中的show方法執行");}
}
class Zi extends Fu {public void show2() {System.out.println("Zi類中的show2方法執行");}
}
public  class Demo05 {public static void main(String[] args) {Zi z = new Zi();//子類中沒有show方法,但是可以找到父類方法去執行z.show(); z.show2();}
}

3.6.2 成員方法重名

如果子類父類中出現重名的成員方法,則創建子類對象調用該方法的時候,子類對象會優先調用自己的方法。

代碼如下:

class Fu {public void show() {System.out.println("Fu show");}
}
class Zi extends Fu {//子類重寫了父類的show方法public void show() {System.out.println("Zi show");}
}
public class ExtendsDemo05{public static void main(String[] args) {Zi z = new Zi();// 子類中有show方法,只執行重寫后的show方法z.show();  // Zi show}
}

3.7 方法重寫

3.7.1 概念

方法重寫 :子類中出現與父類一模一樣的方法時(返回值類型,方法名和參數列表都相同),會出現覆蓋效果,也稱為重寫或者復寫。聲明不變,重新實現

3.7.2 使用場景與案例

發生在子父類之間的關系。
子類繼承了父類的方法,但是子類覺得父類的這方法不足以滿足自己的需求,子類重新寫了一個與父類同名的方法,以便覆蓋父類的該方 法。

例如:我們定義了一個動物類代碼如下:

public class Animal  {public void run(){System.out.println("動物跑的很快!");}public void cry(){System.out.println("動物都可以叫~~~");}
}

然后定義一個貓類,貓可能認為父類cry()方法不能滿足自己的需求

代碼如下:

public class Cat extends Animal {public void cry(){System.out.println("我們一起學貓叫,喵喵喵!喵的非常好聽!");}
}public class Test {public static void main(String[] args) {// 創建子類對象Cat ddm = new Cat()// 調用父類繼承而來的方法ddm.run();// 調用子類重寫的方法ddm.cry();}
}

3.7.2 @Override重寫注解

  • @Override:注解,重寫注解校驗!
  • 這個注解標記的方法,就說明這個方法必須是重寫父類的方法,否則編譯階段報錯。
  • 建議重寫都加上這個注解,一方面可以提高代碼的可讀性,一方面可以防止重寫出錯!
    加上后的子類代碼形式如下:
public class Cat extends Animal {// 聲明不變,重新實現// 方法名稱與父類全部一樣,只是方法體中的功能重寫寫了!@Overridepublic void cry(){System.out.println("我們一起學貓叫,喵喵喵!喵的非常好聽!");}
}

3.7.3 注意事項

  1. 方法重寫是發生在子父類之間的關系。
  2. 子類方法覆蓋父類方法,必須要保證權限大于等于父類權限。
  3. 子類方法覆蓋父類方法,返回值類型、函數名和參數列表都要一模一樣。

3.8 繼承后的特點—構造方法

3.8.1 引入

當類之間產生了關系,其中各類中的構造方法,又產生了哪些影響呢?
首先我們要回憶兩個事情,構造方法的定義格式和作用。

  1. 構造方法的名字是與類名一致的。所以子類是無法繼承父類構造方法的。
  2. 構造方法的作用是初始化對象成員變量數據的。所以子類的初始化過程中,必須先執行父類的初始化動作。子類的構造方法中默認有一個super() ,表示調用父類的構造方法,父類成員變量初始化后,才可以給子類使用。(先有爸爸,才能有兒子

繼承后子類構方法器特點:子類所有構造方法的第一行都會默認先調用父類的無參構造方法

3.8.2 案例演示

按如下需求定義類:

  1. 人類
    成員變量: 姓名,年齡
    成員方法: 吃飯
  2. 學生類
    成員變量: 姓名,年齡,成績
    成員方法: 吃飯

代碼如下:

class Person {private String name;private int age;public Person() {System.out.println("父類無參");}// getter/setter省略
}class Student extends Person {private double score;public Student() {//super(); // 調用父類無參,默認就存在,可以不寫,必須再第一行System.out.println("子類無參");}public Student(double score) {//super();  // 調用父類無參,默認就存在,可以不寫,必須再第一行this.score = score;    System.out.println("子類有參");}}public class Demo07 {public static void main(String[] args) {Student s1 = new Student();System.out.println("----------");Student s2 = new Student(99.9);}
}輸出結果:
父類無參
子類無參
----------
父類無參
子類有參

3.8.3 小結

  • 子類構造方法執行的時候,都會在第一行默認先調用父類無參數構造方法一次。
  • 子類構造方法的第一行都隱含了一個**super()**去調用父類無參數構造方法,**super()**可以省略不寫。

3.9 super(…)和this(…)

class Person {private String name;private int age;public Person() {System.out.println("父類無參");}// getter/setter省略
}class Student extends Person {private double score;public Student() {//super(); // 調用父類無參構造方法,默認就存在,可以不寫,必須再第一行System.out.println("子類無參");}public Student(double score) {//super();  // 調用父類無參構造方法,默認就存在,可以不寫,必須再第一行this.score = score;    System.out.println("子類有參");}// getter/setter省略
}public class Demo07 {public static void main(String[] args) {// 調用子類有參數構造方法Student s2 = new Student(99.9);System.out.println(s2.getScore()); // 99.9System.out.println(s2.getName()); // 輸出 nullSystem.out.println(s2.getAge()); // 輸出 0}
}

我們發現,子類有參數構造方法只是初始化了自己對象中的成員變量score,而父類中的成員變量name和age依然是沒有數據的,怎么解決這個問題呢,我們可以借助與super(…)去調用父類構造方法,以便初始化繼承自父類對象的name和age.

3.9.2 super和this的用法格式+

super和this完整的用法如下,其中this,super訪問成員我們已經接觸過了。

this.成員變量 – 本類的
super.成員變量 – 父類的
this.成員方法名() – 本類的
super.成員方法名() – 父類的

接下來我們使用調用構造方法格式:

super(…) – 調用父類的構造方法,根據參數匹配確認
this(…) – 調用本類的其他構造方法,根據參數匹配確認

3.9.3 super(…)用法演示

class Person {private String name ="鳳姐";private int age = 20;public Person() {System.out.println("父類無參");}public Person(String name , int age){this.name = name ;this.age = age ;}// getter/setter省略
}class Student extends Person {private double score = 100;public Student() {//super(); // 調用父類無參構造方法,默認就存在,可以不寫,必須再第一行System.out.println("子類無參");}public Student(String name , int age,double score) {super(name ,age);// 調用父類有參構造方法Person(String name , int age)初始化name和agethis.score = score;    System.out.println("子類有參");}// getter/setter省略
}public class Demo07 {public static void main(String[] args) {// 調用子類有參數構造方法Student s2 = new Student("張三"2099);System.out.println(s2.getScore()); // 99System.out.println(s2.getName()); // 輸出 張三System.out.println(s2.getAge()); // 輸出 20}
}

注意:
子類的每個構造方法中均有默認的super(),調用父類的空參構造。手動調用父類構造會覆蓋默認的super()。
super() 和 this() 都必須是在構造方法的第一行,所以不能同時出現。
super(…)是根據參數去確定調用父類哪個構造方法的。

3.9.5 this(…)用法演示

this(…)

  • 默認是去找本類中的其他構造方法,根據參數來確定具體調用哪一個構造方法。
  • 為了借用其他構造方法的功能。
package com.itheima._08this和super調用構造方法;
/*** this(...):*    默認是去找本類中的其他構造方法,根據參數來確定具體調用哪一個構造方法。*    為了借用其他構造方法的功能。**/
public class ThisDemo01 {public static void main(String[] args) {Student xuGan = new Student();System.out.println(xuGan.getName()); // 輸出:徐干System.out.println(xuGan.getAge());// 輸出:21System.out.println(xuGan.getSex());// 輸出: 男}
}class Student{private String name ;private int age ;private char sex ;public Student() {// 很弱,我的兄弟很牛逼啊,我可以調用其他構造方法:Student(String name, int age, char sex)this("徐干",21,'男');}public Student(String name, int age, char sex) {this.name = name ;this.age = age   ;this.sex = sex   ;}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 char getSex() {return sex;}public void setSex(char sex) {this.sex = sex;}
}

3.9.6 小結

  • 子類的每個構造方法中均有默認的super(),調用父類的空參構造。手動調用父類構造會覆蓋默認的super()。
  • super() 和 this() 都必須是在構造方法的第一行,所以不能同時出現。
  • super(…)和this(…)是根據參數去確定調用父類哪個構造方法的。
  • super(…)可以調用父類構造方法初始化繼承自父類的成員變量的數據。
  • this(…)可以調用本類中的其他構造方法。

3.10 繼承的特點

  1. Java只支持單繼承,不支持多繼承。
// 一個類只能有一個父類,不可以有多個父類。
class A {}
class B {}
class C1 extends A {} // ok
// class C2 extends A, B {} // error
  1. 一個類可以有多個子類
// A可以有多個子類
class A {}
class C1 extends A {}
class C2 extends  A {}
  1. 可以多層繼承
class A {}
class C1 extends A {}
class D extends C1 {}

頂層父類是Object類。所有的類默認繼承Object,作為父類。

第一章 多態

1.1 多態的形式

多態是繼封裝、繼承之后,面向對象的第三大特性。

多態是出現在繼承或者實現關系中的

多態體現的格式

父類類型 變量名 = new 子類/實現類構造器;
變量名.方法名();

多態的前提:有繼承關系,子類對象是可以賦值給父類類型的變量。例如Animal是一個動物類型,而Cat是一個貓類型。Cat繼承了Animal,Cat對象也是Animal類型,自然可以賦值給父類類型的變量。

1.2 多態的使用場景

如果沒有多態,在下圖中register方法只能傳遞學生對象,其他的Teacher和administrator對象是無法傳遞給register方法方法的,在這種情況下,只能定義三個不同的register方法分別接收學生,老師和管理員。請添加圖片描述
有了多態之后,方法的形參就可以定義為共同的父類Person。
要注意的是:

  • 當一個方法的形參是一個類,我們可以傳遞這個類所有的子類對象。
  • 當一個方法的形參是一個接口,我們可以傳遞這個接口所有的實現類對象(后面會學)。
  • 而且多態還可以根據傳遞的不同對象來調用不同類中的方法。

請添加圖片描述

父類:
public class Person {private String name;private int age;// 空參構造//帶全部參數的構造//get和set方法//此處省略public void show(){System.out.println(name + ", " + age);}
}子類1public class Administrator extends Person {@Overridepublic void show() {System.out.println("管理員的信息為:" + getName() + ", " + getAge());}
}子類2public class Student extends Person{@Overridepublic void show() {System.out.println("學生的信息為:" + getName() + ", " + getAge());}
}子類3public class Teacher extends Person{@Overridepublic void show() {System.out.println("老師的信息為:" + getName() + ", " + getAge());}
}測試類:
public class Test {public static void main(String[] args) {//創建三個對象,并調用register方法Student s = new Student();s.setName("張三");s.setAge(18);Teacher t = new Teacher();t.setName("王建國");t.setAge(30);Administrator admin = new Administrator();admin.setName("管理員");admin.setAge(35);register(s);register(t);register(admin);}//這個方法既能接收老師,又能接收學生,還能接收管理員//只能把參數寫成這三個類型的父類public static void register(Person p){p.show();}
}

1.3 多態的定義和前提

多態: 是指同一行為,具有多個不同表現形式。

從上面案例可以看出,Cat和Dog都是動物,都是吃這一行為,但是出現的效果(表現形式)是不一樣的。

前提【重點】

  1. 有繼承或者實現關系

  2. 方法的重寫【意義體現:不重寫,無意義】

  3. 父類引用指向子類對象【格式體現】

1.4 多態的運行特點

調用成員變量時:編譯看左邊,運行看左邊
調用成員方法時:編譯看左邊,運行看右邊
代碼示例:

Fu f = new Zi()//編譯看左邊的父類中有沒有name這個屬性,沒有就報錯
//在實際運行的時候,把父類name屬性的值打印出來
System.out.println(f.name);
//編譯看左邊的父類中有沒有show這個方法,沒有就報錯
//在實際運行的時候,運行的是子類中的show方法
f.show();

1.5 多態的弊端

我們已經知道多態編譯階段是看左邊父類類型的,如果子類有些獨有的功能,此時多態的寫法就無法訪問子類獨有功能了

class Animal{public  void eat()System.out.println("動物吃東西!")}
class Cat extends Animal {  public void eat() {  System.out.println("吃魚");  }  public void catchMouse() {  System.out.println("抓老鼠");  }  
}  class Dog extends Animal {  public void eat() {  System.out.println("吃骨頭");  }  
}class Test{public static void main(String[] args){Animal a = new Cat();a.eat();a.catchMouse();//編譯報錯,編譯看左邊,Animal沒有這個方法}
}

1.6 引用類型轉換

1.6.1 為什么要轉型

多態的寫法就無法訪問子類獨有功能了。

當使用多態方式調用方法時,首先檢查父類中是否有該方法,如果沒有,則編譯錯誤。也就是說,不能調用子類擁有,而父類沒有的方法。編譯都錯誤,更別說運行了。這也是多態給我們帶來的一點"小麻煩"。所以,想要調用子類特有的方法,必須做向下轉型。

回顧基本數據類型轉換

  • 自動轉換: 范圍小的賦值給范圍大的.自動完成:double d = 5;
  • 強制轉換: 范圍大的賦值給范圍小的,強制轉換:int i = (int)3.14

? 多態的轉型分為向上轉型(自動轉換)與向下轉型(強制轉換)兩種。

1.6.2 向上轉型(自動轉換)

  • 向上轉型:多態本身是子類類型向父類類型向上轉換(自動轉換)的過程,這個過程是默認的。
    當父類引用指向一個子類對象時,便是向上轉型。
    使用格式:
父類類型  變量名 = new 子類類型();
如:Animal a = new Cat();

原因是:父類類型相對與子類來說是大范圍的類型,Animal是動物類,是父類類型。Cat是貓類,是子類類型。Animal類型的范圍當然很大,包含一切動物。 所以子類范圍小可以直接自動轉型給父類類型的變量。

1.6.3 向下轉型(強制轉換)

  • 向下轉型:父類類型向子類類型向下轉換的過程,這個過程是強制的。
    一個已經向上轉型的子類對象,將父類引用轉為子類引用,可以使用強制類型轉換的格式,便是向下轉型。

使用格式:

子類類型 變量名 = (子類類型) 父類變量名;:Aniaml a = new Cat();Cat c =(Cat) a;  

1.6.4 案例演示

想要調用子類特有的方法,必須做向下轉型。
轉型演示,代碼如下:
定義類:

abstract class Animal {  abstract void eat();  
}  class Cat extends Animal {  public void eat() {  System.out.println("吃魚");  }  public void catchMouse() {  System.out.println("抓老鼠");  }  
}  class Dog extends Animal {  public void eat() {  System.out.println("吃骨頭");  }  public void watchHouse() {  System.out.println("看家");  }  
}

定義測試類:

public class Test {public static void main(String[] args) {// 向上轉型  Animal a = new Cat();  a.eat(); 				// 調用的是 Cat 的 eat// 向下轉型  Cat c = (Cat)a;       c.catchMouse(); 		// 調用的是 Cat 的 catchMouse}  
}

1.6.5 轉型的異常

轉型的過程中,一不小心就會遇到這樣的問題,請看如下代碼:

public class Test {public static void main(String[] args) {// 向上轉型  Animal a = new Cat();  a.eat();               // 調用的是 Cat 的 eat// 向下轉型  Dog d = (Dog)a;       d.watchHouse();        // 調用的是 Dog 的 watchHouse 【運行報錯】}  
}

這段代碼可以通過編譯,但是運行時,卻報出了 ClassCastException ,類型轉換異常!這是因為,明明創建了Cat類型對象,運行時,當然不能轉換成Dog對象的。

1.6.6 instanceof關鍵字

為了避免ClassCastException的發生,Java提供了 instanceof 關鍵字,給引用變量做類型的校驗,格式如下:

變量名 instanceof 數據類型
如果變量屬于該數據類型或者其子類類型,返回true。
如果變量不屬于該數據類型或者其子類類型,返回false。

所有在轉換前,我們最好先做個判斷,代碼如下:

public class Test {public static void main(String[] args) {// 向上轉型  Animal a = new Cat();  a.eat();               // 調用的是 Cat 的 eat// 向下轉型  if (a instanceof Cat){Cat c = (Cat)a;       c.catchMouse();        // 調用的是 Cat 的 catchMouse} else if (a instanceof Dog){Dog d = (Dog)a;       d.watchHouse();       // 調用的是 Dog 的 watchHouse}}  
}

1.6.7 instanceof新特性

JDK14的時候提出了新特性,把判斷和強轉合并成了一行

//新特性
//先判斷a是否為Dog類型,如果是,則強轉成Dog類型,轉換之后變量名為d
//如果不是,則不強轉,結果直接是false
if(a instanceof Dog d){d.lookHome();
}else if(a instanceof Cat c){c.catchMouse();
}else{System.out.println("沒有這個類型,無法轉換");
}

1.7綜合練習

需求:根據需求完成代碼:
1.定義狗類
屬性:
年齡,顏色
行為:
eat(String something)(something表示吃的東西)
看家lookHome方法(無參數)
2.定義貓類
屬性:
年齡,顏色
行為:
eat(String something)方法(something表示吃的東西)
逮老鼠catchMouse方法(無參數)
3.定義Person類//飼養員
屬性:
姓名,年齡
行為:
keepPet(Dog dog,String something)方法
功能:喂養寵物狗,something表示喂養的東西
行為:
keepPet(Cat cat,String something)方法
功能:喂養寵物貓,something表示喂養的東西
生成空參有參構造,set和get方法
4.定義測試類(完成以下打印效果):
keepPet(Dog dog,String somethind)方法打印內容如下:
年齡為30歲的老王養了一只黑顏色的2歲的狗
2歲的黑顏色的狗兩只前腿死死的抱住骨頭猛吃
keepPet(Cat cat,String somethind)方法打印內容如下:
年齡為25歲的老李養了一只灰顏色的3歲的貓
3歲的灰顏色的貓瞇著眼睛側著頭吃魚
5.思考:
1.Dog和Cat都是Animal的子類,以上案例中針對不同的動物,定義了不同的keepPet方法,過于繁瑣,能否簡化,并體會簡化后的好處?
2.Dog和Cat雖然都是Animal的子類,但是都有其特有方法,能否想辦法在keepPet中調用特有方法?

畫圖分析:
請添加圖片描述
代碼示例

//動物類(父類)
public class Animal {private int age;private String color;public Animal() {}public Animal(int age, String color) {this.age = age;this.color = color;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}public void eat(String something){System.out.println("動物在吃" + something);}
}//貓類(子類)
public class Cat extends Animal {public Cat() {}public Cat(int age, String color) {super(age, color);}@Overridepublic void eat(String something) {System.out.println(getAge() + "歲的" + getColor() + "顏色的貓瞇著眼睛側著頭吃" + something);}public void catchMouse(){System.out.println("貓抓老鼠");}}//狗類(子類)
public class Dog extends Animal {public Dog() {}public Dog(int age, String color) {super(age, color);}//行為//eat(String something)(something表示吃的東西)//看家lookHome方法(無參數)@Overridepublic void eat(String something) {System.out.println(getAge() + "歲的" + getColor() + "顏色的狗兩只前腿死死的抱住" + something + "猛吃");}public void lookHome(){System.out.println("狗在看家");}
}//飼養員類
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;}//飼養狗/* public void keepPet(Dog dog, String something) {System.out.println("年齡為" + age + "歲的" + name + "養了一只" + dog.getColor() + "顏色的" + dog.getAge() + "歲的狗");dog.eat(something);}//飼養貓public void keepPet(Cat cat, String something) {System.out.println("年齡為" + age + "歲的" + name + "養了一只" + cat.getColor() + "顏色的" + cat.getAge() + "歲的貓");cat.eat(something);}*///想要一個方法,能接收所有的動物,包括貓,包括狗//方法的形參:可以寫這些類的父類 Animalpublic void keepPet(Animal a, String something) {if(a instanceof Dog d){System.out.println("年齡為" + age + "歲的" + name + "養了一只" + a.getColor() + "顏色的" + a.getAge() + "歲的狗");d.eat(something);}else if(a instanceof Cat c){System.out.println("年齡為" + age + "歲的" + name + "養了一只" + c.getColor() + "顏色的" + c.getAge() + "歲的貓");c.eat(something);}else{System.out.println("沒有這種動物");}}
}//測試類
public class Test {public static void main(String[] args) {//創建對象并調用方法/* Person p1 = new Person("老王",30);Dog d = new Dog(2,"黑");p1.keepPet(d,"骨頭");Person p2 = new Person("老李",25);Cat c = new Cat(3,"灰");p2.keepPet(c,"魚");*///創建飼養員的對象Person p = new Person("老王",30);Dog d = new Dog(2,"黑");Cat c = new Cat(3,"灰");p.keepPet(d,"骨頭");p.keepPet(c,"魚");}
}

第二章 包

2.1 包

? 包在操作系統中其實就是一個文件夾。包是用來分門別類的管理技術,不同的技術類放在不同的包下,方便管理和維護。
在IDEA項目中,建包的操作如下:

請添加圖片描述

包名的命名規范

路徑名.路徑名.xxx.xxx
// 例如:com.itheima.oa

  • 包名一般是公司域名的倒寫。例如:黑馬是www.itheima.com,包名就可以定義成com.itheima.技術名稱。
  • 包名必須用”.“連接。
  • 包名的每個路徑名必須是一個合法的標識符,而且不能是Java的關鍵字。

2.2 導包

什么時候需要導包?
? 情況一:在使用Java中提供的非核心包中的類時
? 情況二:使用自己寫的其他包中的類時
什么時候不需要導包?
? 情況一:在使用Java核心包(java.lang)中的類時
? 情況二:在使用自己寫的同一個包中的類時

2.3 使用不同包下的相同類怎么辦?

假設demo1和demo2中都有一個Student該如何使用?
代碼示例:

//使用全類名的形式即可。
//全類名:包名 + 類名
//拷貝全類名的快捷鍵:選中類名crtl + shift + alt + c 或者用鼠標點copy,再點擊copy Reference
com.itheima.homework.demo1.Student s1 = new com.itheima.homework.demo1.Student();
com.itheima.homework.demo2.Student s2 = new com.itheima.homework.demo2.Student();

第三章 權限修飾符

3.1 權限修飾符

? 在Java中提供了四種訪問權限,使用不同的訪問權限修飾符修飾時,被修飾的內容會有不同的訪問權限,我們之前已經學習過了public 和 private,接下來我們研究一下protected和默認修飾符的作用。

  • public:公共的,所有地方都可以訪問。

  • protected:本類 ,本包,其他包中的子類都可以訪問。

  • 默認(沒有修飾符):本類 ,本包可以訪問。

    注意:默認是空著不寫,不是default

  • private:私有的,當前類可以訪問。
    public > protected > 默認 > private

3.2 不同權限的訪問能力

publicprotected默認private
同一類中
同一包中的類
不同包的子類
不同包中的無關類

可見,public具有最大權限。private則是最小權限。
編寫代碼時,如果沒有特殊的考慮,建議這樣使用權限:

  • 成員變量使用private ,隱藏細節。
  • 構造方法使用 public ,方便創建對象。
  • 成員方法使用public ,方便調用方法。

小貼士:不加權限修飾符,就是默認權限

第四章 final關鍵字

4.1 概述

? 學習了繼承后,我們知道,子類可以在父類的基礎上改寫父類內容,比如,方法重寫。

如果有一個方法我不想別人去改寫里面內容,該怎么辦呢?

Java提供了final 關鍵字,表示修飾的內容不可變。

  • final: 不可改變,最終的含義。可以用于修飾類、方法和變量。
    • 類:被修飾的類,不能被繼承。
    • 方法:被修飾的方法,不能被重寫。
    • 變量:被修飾的變量,有且僅能被賦值一次。

4.2 使用方式

4.2.1 修飾類

final修飾的類,不能被繼承。

格式如下:

final class 類名 {
}

代碼:

final class Fu {
}
// class Zi extends Fu {} // 報錯,不能繼承final的類

查詢API發現像 public final class Stringpublic final class Mathpublic final class Scanner 等,很多我們學習過的類,都是被final修飾的,目的就是供我們使用,而不讓我們所以改變其內容。

4.2.2 修飾方法

final修飾的方法,不能被重寫。
格式如下:

修飾符 final 返回值類型 方法名(參數列表){//方法體
}
class Fu2 {final public void show1() {System.out.println("Fu2 show1");}public void show2() {System.out.println("Fu2 show2");}
}class Zi2 extends Fu2 {
//	@Override
//	public void show1() {
//		System.out.println("Zi2 show1");
//	}@Overridepublic void show2() {System.out.println("Zi2 show2");}
}

4.2.3 修飾變量-局部變量

  1. 局部變量——基本類型
    基本類型的局部變量,被final修飾后,只能賦值一次,不能再更改。代碼如下:
public class FinalDemo1 {public static void main(String[] args) {// 聲明變量,使用final修飾final int a;// 第一次賦值 a = 10;// 第二次賦值a = 20; // 報錯,不可重新賦值// 聲明變量,直接賦值,使用final修飾final int b = 10;// 第二次賦值b = 20; // 報錯,不可重新賦值}
}

4.2.4 修飾變量-成員變量

成員變量涉及到初始化的問題,初始化方式有顯示初始化和構造方法初始化,只能選擇其中一個:

  • 顯示初始化(在定義成員變量的時候立馬賦值)(常用);
public class Student {final int num = 10;
}

構造方法初始化(在構造方法中賦值一次)(不常用,了解即可)。

注意:每個構造方法中都要賦值一次!

public class Student {final int num = 10;final int num2 = 20;public Student() {this.num2 = 20;
//     this.num2 = 20;}public Student(String name) {this.num2 = 20;
//     this.num2 = 20;}
}

被final修飾的常量名稱,一般都有書寫規范,所有字母都大寫

第一章、抽象類

1.1 概述


1.1.1 抽象類引入

? 父類中的方法,被它的子類們重寫,子類各自的實現都不盡相同。那么父類的方法聲明和方法主體,只有聲明還有意義,而方法主體則沒有存在的意義了(因為子類對象會調用自己重寫的方法)。換句話說,父類可能知道子類應該有哪個功能,但是功能具體怎么實現父類是不清楚的(由子類自己決定),父類只需要提供一個沒有方法體的定義即可,具體實現交給子類自己去實現。我們把沒有方法體的方法稱為抽象方法。Java語法規定,包含抽象方法的類就是抽象類

  • 抽象方法 : 沒有方法體的方法。
  • 抽象類:包含抽象方法的類。

1.2 abstract使用格式

abstract是抽象的意思,用于修飾方法方法和類,修飾的方法是抽象方法,修飾的類是抽象類。

1.2.1 抽象方法

使用abstract 關鍵字修飾方法,該方法就成了抽象方法,抽象方法只包含一個方法名,而沒有方法體。

定義格式:

修飾符 abstract 返回值類型 方法名(參數列表);

代碼舉例:

public abstract void run();

1.2.2 抽象類

如果一個類包含抽象方法,那么該類必須是抽象類。注意:抽象類不一定有抽象方法,但是有抽象方法的類必須定義成抽象類。

定義格式:

abstract class 名字{}
public abstract class Animal{public abstract void run();
}

1.2.3 抽象類的使用

要求:繼承抽象類的子類必須重寫父類所有的抽象方法。否則,該子類也必須聲明為抽象類。

代碼舉例:

// 父類,抽象類
abstract class Employee {private String id;private String name;private double salary;public Employee() {}public Employee(String id, String name, double salary) {this.id = id;this.name = name;this.salary = salary;}// 抽象方法// 抽象方法必須要放在抽象類中abstract public void work();
}// 定義一個子類繼承抽象類
class Manager extends Employee {public Manager() {}public Manager(String id, String name, double salary) {super(id, name, salary);}// 2.重寫父類的抽象方法@Overridepublic void work() {System.out.println("管理其他人");}
}// 定義一個子類繼承抽象類
class Cook extends Employee {public Cook() {}public Cook(String id, String name, double salary) {super(id, name, salary);}@Overridepublic void work() {System.out.println("廚師炒菜多加點鹽...");}
}// 測試類
public class Demo10 {public static void main(String[] args) {// 創建抽象類,抽象類不能創建對象// 假設抽象類讓我們創建對象,里面的抽象方法沒有方法體,無法執行.所以不讓我們創建對象
//		Employee e = new Employee();
//		e.work();// 3.創建子類Manager m = new Manager();m.work();Cook c = new Cook("ap002", "庫克", 1);c.work();}
}

此時的方法重寫,是子類對父類抽象方法的完成實現,我們將這種方法重寫的操作,也叫做實現方法

1.3 抽象類的特征

抽象類的特征總結起來可以說是 有得有失

有得:抽象類得到了擁有抽象方法的能力。

有失:抽象類失去了創建對象的能力。

其他成員(構造方法,實例方法,靜態方法等)抽象類都是具備的。

1.4 抽象類的細節

不需要背,只要當idea報錯之后,知道如何修改即可。

關于抽象類的使用,以下為語法上要注意的細節,雖然條目較多,但若理解了抽象的本質,無需死記硬背。

  1. 抽象類不能創建對象,如果創建,編譯無法通過而報錯。只能創建其非抽象子類的對象。

    理解:假設創建了抽象類的對象,調用抽象的方法,而抽象方法沒有具體的方法體,沒有意義。

  2. 抽象類中,可以有構造方法,是供子類創建對象時,初始化父類成員使用的。

    理解:子類的構造方法中,有默認的super(),需要訪問父類構造方法。

  3. 抽象類中,不一定包含抽象方法,但是有抽象方法的類必定是抽象類。

    理解:未包含抽象方法的抽象類,目的就是不想讓調用者創建該類對象,通常用于某些特殊的類結構設計。

  4. 抽象類的子類,必須重寫抽象父類中所有的抽象方法,否則子類也必須定義成抽象類,編譯無法通過而報錯。

    理解:假設不重寫所有抽象方法,則類中可能包含抽象方法。那么創建對象后,調用抽象的方法,沒有意義。

  5. 抽象類存在的意義是為了被子類繼承。

    理解:抽象類中已經實現的是模板中確定的成員,抽象類不確定如何實現的定義成抽象方法,交給具體的子類去實現。

1.5 抽象類存在的意義

? 抽象類存在的意義是為了被子類繼承,否則抽象類將毫無意義。抽象類可以強制讓子類,一定要按照規定的格式進行重寫。

第二章 接口

2.1 概述

我們已經學完了抽象類,抽象類中可以用抽象方法,也可以有普通方法,構造方法,成員變量等。那么什么是接口呢?接口是更加徹底的抽象,JDK7之前,包括JDK7,接口中全部是抽象方法。接口同樣是不能創建對象的

2.2定義格式

//接口的定義格式:
interface 接口名稱{// 抽象方法
}// 接口的聲明:interface
// 接口名稱:首字母大寫,滿足“駝峰模式”

2.3 接口成分的特點

在JDK7,包括JDK7之前,接口中的只有包含:抽象方法和常量

2.3.1.抽象方法

? 注意:接口中的抽象方法默認會自動加上public abstract修飾程序員無需自己手寫!!
? 按照規范:以后接口中的抽象方法建議不要寫上public abstract。因為沒有必要啊,默認會加上。

2.3.2 常量

在接口中定義的成員變量默認會加上: public static final修飾。也就是說在接口中定義的成員變量實際上是一個常量。這里是使用public static final修飾后,變量值就不可被修改,并且是靜態化的變量可以直接用接口名訪問,所以也叫常量。常量必須要給初始值。常量命名規范建議字母全部大寫,多個單詞用下劃線連接。

2.3.3 案例演示

public interface InterF {// 抽象方法!//    public abstract void run();void run();//    public abstract String getName();String getName();//    public abstract int add(int a , int b);int add(int a , int b);// 它的最終寫法是:// public static final int AGE = 12 ;int AGE  = 12; //常量String SCHOOL_NAME = "黑馬程序員";}

2.4 基本的實現

2.4.1 實現接口的概述

類與接口的關系為實現關系,即類實現接口,該類可以稱為接口的實現類,也可以稱為接口的子類。實現的動作類似繼承,格式相仿,只是關鍵字不同,實現使用 implements關鍵字。

2.4.2 實現接口的格式

/**接口的實現:在Java中接口是被實現的,實現接口的類稱為實現類。實現類的格式:*/
class 類名 implements 接口1,接口2,接口3...{}

從上面格式可以看出,接口是可以被多實現的。

2.4.3 類實現接口的要求和意義

  1. 必須重寫實現的全部接口中所有抽象方法。
  2. 如果一個類實現了接口,但是沒有重寫完全部接口的全部抽象方法,這個類也必須定義成抽象類。
  3. 意義:接口體現的是一種規范,接口對實現類是一種強制性的約束,要么全部完成接口申明的功能,要么自己也定義成抽象類。這正是一種強制性的規范。

2.4.4 類與接口基本實現案例

假如我們定義一個運動員的接口(規范),代碼如下:

/**接口:接口體現的是規范。* */
public interface SportMan {void run(); // 抽象方法,跑步。void law(); // 抽象方法,遵守法律。String compittion(String project);  // 抽象方法,比賽。
}

接下來定義一個乒乓球運動員類,實現接口,實現接口的實現類代碼如下:

```java
package com.itheima._03接口的實現;
/*** 接口的實現:*    在Java中接口是被實現的,實現接口的類稱為實現類。*    實現類的格式:*      class 類名 implements 接口1,接口2,接口3...{***      }* */
public class PingPongMan  implements SportMan {@Overridepublic void run() {System.out.println("乒乓球運動員稍微跑一下!!");}@Overridepublic void law() {System.out.println("乒乓球運動員守法!");}@Overridepublic String compittion(String project) {return "參加"+project+"得金牌!";}
}```java
package com.itheima._03接口的實現;
/*** 接口的實現:*    在Java中接口是被實現的,實現接口的類稱為實現類。*    實現類的格式:*      class 類名 implements 接口1,接口2,接口3...{***      }* */
public class PingPongMan  implements SportMan {@Overridepublic void run() {System.out.println("乒乓球運動員稍微跑一下!!");}@Overridepublic void law() {System.out.println("乒乓球運動員守法!");}@Overridepublic String compittion(String project) {return "參加"+project+"得金牌!";}
}

測試代碼

public class TestMain {public static void main(String[] args) {// 創建實現類對象。PingPongMan zjk = new PingPongMan();zjk.run();zjk.law();System.out.println(zjk.compittion("全球乒乓球比賽"));}
}

2.4.5 類與接口的多實現案例

類與接口之間的關系是多實現的,一個類可以同時實現多個接口。

首先我們先定義兩個接口,代碼如下:

/** 法律規范:接口*/
public interface Law {void rule();
}/** 這一個運動員的規范:接口*/
public interface SportMan {void run();
}

然后定義一個實現類:

/*** Java中接口是可以被多實現的:*    一個類可以實現多個接口: Law, SportMan** */
public class JumpMan implements Law ,SportMan {@Overridepublic void rule() {System.out.println("尊長守法");}@Overridepublic void run() {System.out.println("訓練跑步!");}
}

從上面可以看出類與接口之間是可以多實現的,我們可以理解成實現多個規范,這是合理的。

2.5 接口與接口的多繼承

Java中,接口與接口之間是可以多繼承的:也就是一個接口可以同時繼承多個接口。大家一定要注意:

類與接口是實現關系

接口與接口是繼承關系

接口繼承接口就是把其他接口的抽象方法與本接口進行了合并。

案例演示:

public interface Abc {void go();void test();
}/** 法律規范:接口*/
public interface Law {void rule();void test();
}**  總結:*     接口與類之間是多實現的。*     接口與接口之間是多繼承的。* */
public interface SportMan extends Law , Abc {void run();
}

2.6擴展:接口的細節

不需要背,只要當idea報錯之后,知道如何修改即可。

關于接口的使用,以下為語法上要注意的細節,雖然條目較多,但若理解了抽象的本質,無需死記硬背。

  1. 當兩個接口中存在相同抽象方法的時候,該怎么辦?

只要重寫一次即可。此時重寫的方法,既表示重寫1接口的,也表示重寫2接口的。

  1. 實現類能不能繼承A類的時候,同時實現其他接口呢?

繼承的父類,就好比是親爸爸一樣
實現的接口,就好比是干爹一樣
可以繼承一個類的同時,再實現多個接口,只不過,要把接口里面所有的抽象方法,全部實現。

  1. 實現類能不能繼承一個抽象類的時候,同時實現其他接口呢?

實現類可以繼承一個抽象類的同時,再實現其他多個接口,只不過要把里面所有的抽象方法全部重寫。

  1. 實現類Zi,實現了一個接口,還繼承了一個Fu類。假設在接口中有一個方法,父類中也有一個相同的方法。子類如何操作呢?

處理辦法一:如果父類中的方法體,能滿足當前業務的需求,在子類中可以不用重寫。
處理辦法二:如果父類中的方法體,不能滿足當前業務的需求,需要在子類中重寫。

  1. 如果一個接口中,有10個抽象方法,但是我在實現類中,只需要用其中一個,該怎么辦?

可以在接口跟實現類中間,新建一個中間類(適配器類)
讓這個適配器類去實現接口,對接口里面的所有的方法做空重寫。
讓子類繼承這個適配器類,想要用到哪個方法,就重寫哪個方法。
因為中間類沒有什么實際的意義,所以一般會把中間類定義為抽象的,不讓外界創建對象

第三章 內部類

3.1 概述

3.1.1 什么是內部類

將一個類A定義在另一個類B里面,里面的那個類A就稱為內部類,B則稱為外部類。可以把內部類理解成寄生,外部類理解成宿主。

3.1.2 什么時候使用內部類

一個事物內部還有一個獨立的事物,內部的事物脫離外部的事物無法獨立使用

  1. 人里面有一顆心臟。
  2. 汽車內部有一個發動機。
  3. 為了實現更好的封裝性。

3.2 內部類的分類

按定義的位置來分

  1. 成員內部類,類定義在了成員位置 (類中方法外稱為成員位置,無static修飾的內部類)
  2. 靜態內部類,類定義在了成員位置 (類中方法外稱為成員位置,有static修飾的內部類)
  3. 局部內部類,類定義在方法內
  4. 匿名內部類,沒有名字的內部類,可以在方法中,也可以在類中方法外。

3.3 成員內部類

成員內部類特點

  • 無static修飾的內部類,屬于外部類對象的。
  • 宿主:外部類對象。

內部類的使用格式

 外部類.內部類。 // 訪問內部類的類型都是用 外部類.內部類

獲取成員內部類對象的兩種方式

方式一:外部直接創建成員內部類的對象

外部類.內部類 變量 = new 外部類().new 內部類();

方式二:在外部類中定義一個方法提供內部類的對象

案例演示

3.4 成員內部類的細節

編寫成員內部類的注意點:

  1. 成員內部類可以被一些修飾符所修飾,比如: private,默認,protected,public,static等
  2. 在成員內部類里面,JDK16之前不能定義靜態變量,JDK16開始才可以定義靜態變量。
  3. 創建內部類對象時,對象中有一個隱含的Outer.this記錄外部類對象的地址值。(請參見3.6節的內存圖)

詳解:

? 內部類被private修飾,外界無法直接獲取內部類的對象,只能通過3.3節中的方式二獲取內部類的對象

? 被其他權限修飾符修飾的內部類一般用3.3節中的方式一直接獲取內部類的對象

? 內部類被static修飾是成員內部類中的特殊情況,叫做靜態內部類下面單獨學習。

? 內部類如果想要訪問外部類的成員變量,外部類的變量必須用final修飾,JDK8以前必須手動寫final,JDK8之后不需要手動寫,JDK默認加上。

3.5例題:

public class Test {public static void main(String[] args) {Outer.inner oi = new Outer().new inner();oi.method();}
}class Outer {	// 外部類private int a = 30;// 在成員位置定義一個類class inner {private int a = 20;public void method() {int a = 10;System.out.println(???);	// 10   答案:aSystem.out.println(???);	// 20	答案:this.aSystem.out.println(???);	// 30	答案:Outer.this.a}}
}

3.6 成員內部類內存圖

請添加圖片描述

3.7 靜態內部類

靜態內部類特點

  • 靜態內部類是一種特殊的成員內部類。
  • 有static修飾,屬于外部類本身的。
  • 總結:靜態內部類與其他類的用法完全一樣。只是訪問的時候需要加上外部類.內部類。
  • 拓展1:靜態內部類可以直接訪問外部類的靜態成員。
  • 拓展2:靜態內部類不可以直接訪問外部類的非靜態成員,如果要訪問需要創建外部類的對象。
  • 拓展3:靜態內部類中沒有銀行的Outer.this。

內部類的使用格式

外部類.內部類。

靜態內部類對象的創建格式

外部類.內部類  變量 = new  外部類.內部類構造器;

調用方法的格式:

  • 調用非靜態方法的格式:先創建對象,用對象調用
  • 調用靜態方法的格式:外部類名.內部類名.方法名();

案例演示

// 外部類:Outer01
class Outer01{private static  String sc_name = "黑馬程序";// 內部類: Inner01public static class Inner01{// 這里面的東西與類是完全一樣的。private String name;public Inner01(String name) {this.name = name;}public void showName(){System.out.println(this.name);// 拓展:靜態內部類可以直接訪問外部類的靜態成員。System.out.println(sc_name);}}
}public class InnerClassDemo01 {public static void main(String[] args) {// 創建靜態內部類對象。// 外部類.內部類  變量 = new  外部類.內部類構造器;Outer01.Inner01 in  = new Outer01.Inner01("張三");in.showName();}
}

3.8 局部內部類

  • 局部內部類 :定義在方法中的類。

定義格式:

class 外部類名 {  數據類型 變量名;   修飾符 返回值類型 方法名(參數列表) {    // …      class 內部類 {      // 成員變量  // 成員方法       } }}

3.9 匿名內部類【重點】

3.9.1 概述

匿名內部類 :是內部類的簡化寫法。他是一個隱含了名字的內部類。開發中,最常用到的內部類就是匿名內部類了。

3.9.2 格式

new 類名或者接口名() {重寫方法;
};

包含了:

  • 繼承或者實現關系

  • 方法重寫

  • 創建對象

所以從語法上來講,這個整體其實是匿名內部類對象

3.9.2 什么時候用到匿名內部類

實際上,如果我們希望定義一個只要使用一次的類,就可考慮使用匿名內部類。匿名內部類的本質作用

是為了簡化代碼

之前我們使用接口時,似乎得做如下幾步操作:

  1. 定義子類
  2. 重寫接口中的方法
  3. 創建子類對象
  4. 調用重寫后的方法
interface Swim {public abstract void swimming();
}// 1. 定義接口的實現類
class Student implements Swim {// 2. 重寫抽象方法@Overridepublic void swimming() {System.out.println("狗刨式...");}
}public class Test {public static void main(String[] args) {// 3. 創建實現類對象Student s = new Student();// 4. 調用方法s.swimming();}
}

我們的目的,最終只是為了調用方法,那么能不能簡化一下,把以上四步合成一步呢?匿名內部類就是做這樣的快捷方式。

3.9.3 匿名內部類前提和格式

匿名內部類必須繼承一個父類或者實現一個父接口

匿名內部類格式

x new 父類名或者接口名(){// 方法重寫 @Override    public void method() {     // 執行語句  }};

3.9.4 使用方式

以接口為例,匿名內部類的使用,代碼如下:

interface Swim{public abstract void swimming();
}
public class Demo7{public static void main(String[] args) {// 使用匿名內部類new Swim() {@Overridepublic void swimming() {System.out.println("自由泳...");}}.swimming();// 接口 變量 = new 實現類(); // 多態,走子類的重寫方法Swim s2 = new Swim() {@Overridepublic void swimming() {System.out.println("蛙泳...");}};s2.swimming();s2.swimming();}
}

3.9.5 匿名內部類的特點

  1. 定義一個沒有名字的內部類
  2. 這個類實現了父類,或者父類接口
  3. 匿名內部類會創建這個沒有名字的類的對象

3.9.6 匿名內部類的使用場景

通常在方法的形式參數是接口或者抽象類時,也可以將匿名內部類作為參數傳遞。代碼如下:

interface Swim {public abstract void swimming();
}public class Demo07 {public static void main(String[] args) {// 普通方式傳入對象// 創建實現類對象Student s = new Student();goSwimming(s);// 匿名內部類使用場景:作為方法參數傳遞Swim s3 = new Swim() {@Overridepublic void swimming() {System.out.println("蝶泳...");}};// 傳入匿名內部類goSwimming(s3);// 完美方案: 一步到位goSwimming(new Swim() {public void swimming() {System.out.println("大學生, 蛙泳...");}});goSwimming(new Swim() {public void swimming() {System.out.println("小學生, 自由泳...");}});}// 定義一個方法,模擬請一些人去游泳public static void goSwimming(Swim s) {s.swimming();}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/82750.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/82750.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/82750.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

記一次redis未授權被種挖礦

#挖礦程序 /etc/httpgd /etc/nnt.sh #大小問 #定時任務名為root /var/spool/cron/root 內容:*/50 * * * * sh /etc/nnt.sh >/dev/null 2>&1 定時任務只有所有者可以寫,且chmod修改權限失敗。 #先查看定時任務的拓展屬性,不可變(i…

Excel分組計算求和的兩種實現方案

文章目錄 背景樣例數據方案一、函數求和實現步驟缺點 方案二、數據透視表實現步驟優點 背景 在Excel文檔中,經常會進行數據的求和計算,可使用不同的方式實現,記錄下來,方便備查。 樣例數據 已有商品銷量信息,包含銷…

如何應對網站被爬蟲和采集?綜合防護策略與實用方案

在互聯網時代,網站內容被惡意爬蟲或采集工具竊取已成為常見問題。這不僅侵犯原創權益,還可能影響網站性能和SEO排名。以下是結合技術、策略與法律的綜合解決方案,幫助網站構建有效防護體系。 一、技術防護:阻斷爬蟲的“技術防線”…

網卡網孔速率的協商是如何進行的?

網卡與交換機等網絡設備之間的速率協商主要通過**自動協商(Auto-Negotiation)**機制實現,其核心是物理層(PHY)芯片之間的信息交互。以下是協商過程的詳細解析: 一、自動協商的核心流程 1. 發送配置幀&am…

FastExcel 本地開發和Linux上上傳Resource文件的差異性

不能直接通過路徑來獲取 這個是一個下載導出文件的操作 GetMapping(value "/export/all") public void exportAll(HttpServletResponse response, LaylineListReq req) throws IOException {// 從類路徑下獲取 Excel 文件資源ClassPathResource classPathResource…

【RAG】Milvus、Pinecone、PgVector向量數據庫索引參數優化

Milvus 、PgVector 索引參數優化 IVF類索引關鍵參數(基于聚類算法) nlist (倒排列表數量): 決定將向量空間劃分為多少個聚類中心值越大搜索越精確但耗時越長推薦值: 通常設置為數據量的4√n到n/1000之間例如: 1百萬數據量可設nlist1000到4000 nprobe (搜…

5月12日信息差

一、國際政治與安全:俄烏沖突與中美博弈 1. 烏克蘭戰場信息分化 俄方戰報: 俄羅斯國防部宣稱在頓巴斯地區摧毀烏軍12輛坦克及3套美制“海馬斯”火箭系統,稱烏軍反攻受阻。 信息特點:強調裝備摧毀數量,淡化前線實際控制變化。 烏方通報: 烏克蘭總參謀部表示已奪回巴赫穆特…

Python如何使用進行風險管理和投資組合優化

文章目錄 前言python3.13 環境配置風險管理投資組合優化 前言 在 Python 中,可以使用多個庫來進行風險管理和投資組合優化,以下是一些常見的方法和庫。 python3.13 環境配置 python3.13安裝教程:https://blog.csdn.net/2501_91538706/artic…

C++ 狀態模式詳解

狀態模式(State Pattern)是一種行為設計模式,它允許一個對象在內部狀態改變時改變其行為,使對象看起來像是改變了其類。 核心概念 設計原則 狀態模式遵循以下設計原則: 單一職責原則:將狀態相關行為分離…

Html5新特性_js 給元素自定義屬性_json 詳解_淺克隆與深克隆

文章目錄 1. html5新特性2.用 js 給元素自定義屬性3.json3.1 json與普通對象的區別3.2 json對象與 js對象的轉化 4.淺克隆和深克隆 1. html5新特性 html5中引入了新的特性(新的標簽),下面的新標簽是新的結構標簽,不過不太常用 h…

std::move 和 std::forward

關聯點 都是執行轉換(cast)的函數(函數模板),不產生任何可執行代碼。且都可以把實參轉換成右值。 std::move無條件將實參(const除外 )轉換成右值引用,std::forward 條件返回右值引用 _EXPORT_STD template…

Uniapp編寫微信小程序,使用canvas進行繪圖

一、canvas文檔: https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API/Tutorial 二、數據繪制(單位是像素): 1、繪制文本: 文字的長度超過設置的最大寬度,文字會縮在一起 ① 填充文本&#xf…

FLASH閃存(擦除、編譯)

FLASH閃存 文章目錄 FLASH閃存1.存儲器映像位置2.FLASH簡介3.閃存模塊組織3.2閃存的共性: 4.FLASH基本結構4.1FLASH解鎖4.2使用指針訪問寄存器 5.選項字節5.1選項字節編程5.2選項字節擦除 6.相關函數介紹7.讀取內部FLASH(實操)7.1接線圖7.2工…

PostgreSQL 序列(Sequence) 與 Oracle 序列對比

PostgreSQL 序列(Sequence) 與 Oracle 序列對比 PostgreSQL 和 Oracle 都提供了序列(Sequence)功能,但在實現細節和使用方式上存在一些重要差異。以下是兩者的詳細對比: 一 基本語法對比 1.1 創建序列 PostgreSQL: CREATE [ { TEMPORARY | TEMP } |…

12.2.2 allocator類

allocator類將分配內存空間、調用構造函數、調用析構函數、釋放內存空間這4部分操作分開&#xff0c;全部交給程序員來執行&#xff0c;不像new和delete #include <iostream> #include <string>int main() {const int n 10;std::allocator<std::string> al…

Android 中 Handler (創建時)內存泄漏問題及解決方案

一、Handler 內存泄漏核心原理 真題 1&#xff1a;分析 Handler 內存泄漏場景 題目描述&#xff1a; 在 Activity 中使用非靜態內部類 Handler 發送延遲消息&#xff0c;旋轉屏幕后 Activity 無法釋放&#xff0c;分析原因并給出解決方案。 內存泄漏鏈路分析&#xff1a; 引…

SSTI記錄

SSTI(Server-Side Template Injection&#xff0c;服務器段模板注入) 當前使用的一些框架&#xff0c;如python的flask、php的tp、java的spring&#xff0c;都采用成熟的MVC模式&#xff0c;用戶的輸入會先進入到Controller控制器&#xff0c;然后根據請求的類型和請求的指令發…

探索邊緣計算:賦能物聯網的未來

摘要 隨著物聯網&#xff08;IoT&#xff09;技術的飛速發展&#xff0c;越來越多的設備接入網絡&#xff0c;產生了海量的數據。傳統的云計算模式在處理這些數據時面臨著延遲高、帶寬不足等問題&#xff0c;而邊緣計算的出現為解決這些問題提供了新的思路。本文將深入探討邊緣…

tabs切換#

1、html <el-tabs v-model"tabValue" tab-change"handleTabClick"><el-tab-pane label"集群" name"1"></el-tab-pane><el-tab-pane label"節點" name"2"></el-tab-pane></el-ta…

JSON 實體屬性映射的最佳實踐

一、結構與命名規范 ?保持字段命名一致性? JSON 字段名與實體屬性名應遵循統一的命名規則&#xff08;如駝峰命名或下劃線分隔&#xff09;&#xff0c;避免因大小寫差異導致映射失敗。 // 使用 JsonProperty 顯式指定映射關系&#xff08;Jackson&#xff09; public class …