天天看點

JAVA面向對象之代碼塊 繼承 方法的重寫 super關鍵字與重寫toString()方法介紹JAVA面向對象之代碼塊與繼承

JAVA面向對象之代碼塊與繼承

代碼塊分類

局部代碼塊

作用:限制變量生命周期
書寫位置:在方法中
           

構造代碼塊

開發中很少使用
書寫位置:類中  方法外
調用時機:如果你有構造代碼塊 系統會幫你調用 幫你在建立對象時調用
           

靜态代碼塊(一定是被static修飾)

依賴類 随着類的加載而加載
注意:隻加載一次(系統隻建立一次 不管你調用多少對象)
應用場景:U盤裝載驅動程式(第二次插入U盤,不會再加載驅動程式)
         加載驅動(資料庫驅動 JDBC)
           

同步代碼塊(多線程)

這裡暫時不做介紹 後續給予說明
           

代碼示例

public class Demo24 {
           //第三代碼塊
    {
         int a = ;
         System.out.println(a);
         System.out.println("我是Demo24構造代碼塊");
    }
    public static void main(String[] args){
        Person2 person = new Person2();
        person.name = "張三";
        person.age = ;
        person.sayHi();
        System.out.println("我是main函數中得普通方法");
        {
            System.out.println("我是局部代碼塊");
        }
        Person2 person1 = new Person2("李四",);
    }
}
class Person2{
    String name;
    int age;
    //第一個代碼塊
    static {
          System.out.println("我是person類的靜态代碼塊");
    }
    //第二個代碼塊
    {
        //每一個對象 都會調用同一個方法 這時可以使用
           sleep();
           System.out.println("我是構造代碼塊");
    }
    public Person2(){
           System.out.println("我是無參數的構造方法");
    }
    public Person2(String name, int age){
          this.name = name;
          this.age = age;
          System.out.println("我是有參數的構造方法");
    }

    public String getName(){
            return name;
    }
    public void setName(){
            this.name = name;
    }
    public int getAge(){
            return age;
    }
    public void setAge(int age){
             this.age = age;
    }

    public void sayHi(){
           System.out.println("姓名:" + name + "年齡:" + age);
    }

     public void sleep(){
          System.out.println("睡覺");
     }
}

           

運作結果

我是person類的靜态代碼塊
睡覺
我是構造代碼塊
我是無參數的構造方法
姓名:張三年齡:15
我是main函數中得普通方法
我是局部代碼塊
睡覺
我是構造代碼塊
我是有參數的構造方法
姓名:李四年齡:22
           

解釋

當程式運作時 會先加載Demo01.class檔案,進入.class檔案後,會尋找靜态代碼塊,如果沒有,會繼續尋找構造代碼塊,由于沒有
Demo24類的執行個體對象,所有構造代碼塊與構造方法均不會執行,如果另外建立一個類,Demo25,在Demo25類中建立Demo24類的執行個體
對象,Demo24類中的構造代碼塊與構造方法便會執行,之後會首先尋找main函數,找到main函數後,(局部代碼塊定義在方法中,在該
方法中并沒有優先級)會首先遇到Person2類,遇到Person2 person = new Person2();這時會加載Person2類,
加載Person2.class檔案,靜态代碼塊與靜态方法和靜态屬性一樣,會随着類的加載而加載,存放在方法區的靜态區,與對象無關,所
以在加載Person2.class檔案時,會将static代碼塊一同加載,所有會輸出'我是Person類的靜态代碼塊',之後需要new一個對象,
繼續執行Person2類中的代碼,執行時,會先尋找有沒有構造代碼塊(構造代碼塊存放在類中方法外,優先級高于構造方法);發現有構
造代碼塊,即先執行構造代碼塊,構造代碼塊中有sleep方法,故先執行sleep方法,列印'睡覺',之後執行下一句,列印'我是構造代碼
塊',構造代碼塊執行之後,會執行相應的構造方法(有參或者無參),構造代碼塊優先于構造方法執行,與構造代碼塊的位置無關,new
對象的時候沒有傳遞參數,所有這裡調用無參構造方法,列印'我是無參數的構造方法',之後回到Demo01函數,對對象中的變量進行賦
值,賦完值後,調用對象的sayHi方法,列印'姓名:張三','年齡:15',随後執行到列印語句,列印出"我是main函數中得普通方法",
之後遇到局部代碼塊,列印"我是局部代碼塊"(順序執行),當new第二個Person2對象時,靜态代碼塊不會再執行,即在函數運作過
程中,隻會加載一次,非靜态構造代碼塊将會繼續加載,第二次new對象時候,先執行構造代碼塊,構造代碼塊中有sleep方法,所有會
先執行sleep方法,列印"睡覺",随後列印"我是構造代碼塊",new對象時候,傳進來了參數,所有會調用Person2類中的有參數構造
方法,列印"我是有參數的構造方法",指派後,調用sayHi方法,列印"姓名:李四年齡22",函數運作結束.
           

繼承

繼承特點

1.減少你的代碼量
2.讓類與類之間産生關聯(産生 父子的)
           

繼承弊端

當父類中添加新德屬性時候,比如白血病,子類即使不想繼承這個屬性,卻還是由于
繼承的關系,自動得到了白血病這個屬性
           

注意

.繼承時  可把多個類中 相同的功能或方法 抽取出來 重新構造一個類出來  
把這些類建立 繼承關系
.建立繼承關系的同時 一定要符合邏輯(切記不要為了繼承而繼承)
.繼承使用關鍵字:extends
           

舉例

繼承:
手機類  <打電話 發短信 玩遊戲>
蘋果手機類 繼承 手機類 <打電話 發短信 玩遊戲 爆炸>
小米手機類 繼承 手機類 <打電話 發短信 玩遊戲 暖手功能>
           
項目經理:姓名 工資 工号 分紅
程式員:姓名 工資 工号

項目經理 繼承 程式員 就繼承了姓名 工資 和 工号 ,特有功能 分紅 這樣不行 
不符合邏輯
應該是相同的功能抽取出來
員工類 姓名 工資 工号
項目經理 和 程式員 繼承員工類  這樣才符合邏輯
           

注意

如果是繼承關系 一定符合什麼是什麼
項目經理是員工 子類是父類的
           
動物 貓 狗 馬
貓 是 動物 √
動物 是 貓 ×

水果(父類) 香蕉 蘋果 橘子
水果 是 香蕉 ×
香蕉 是 水果 √
           

繼承的寫法

class 子類 extends 父類{

}
           

代碼示例

/*
 * 貓類
 * 姓名 顔色  種類 會睡覺 會抓老鼠
 * 狗類 
 * 姓名 顔色  種類 會睡覺 會啃骨頭
 */

// 抽取出 相同部分 組成 動物類
public class Demo02{
    public static void main(String[] args){
        Cat cat = new Cat();
        cat.name = "湯姆";
        cat.color = "灰色";
        cat.kind = "灰貓";
        cat.sleep();
        cat.sayHi();
    }
}

class Animal{
    String name;
    String color;
    String kind;

    public void sleep(){
        System.out.println("睡覺");
    }
    public void sayHi(){
        System.out.println("姓名:" + name +"顔色:" + color + "種類:" + kind);
    }
}

class Cat extends Animal{
    public void hitMouse(){
        System.out.println("抓老鼠");
    }
}

class Dog extends Animal{
    public void eatBone(){
        System.out.println("啃骨頭");
    }
}
           

運作結果:

抓老鼠
睡覺
姓名:湯姆顔色:灰色種類:灰貓
           

JAVA中的繼承

注意

java 隻允許 單繼承(多繼承 可以 使用 接口 來間接實作)
java 中 允許 多層繼承(爺爺 父親 兒子 孫子 重孫子....)

java中 最頂層的父類(最基礎類) Object類
如果我這個類沒有寫 繼承哪個父親 預設就是繼承 Object類
           
1.如果我要使用 這些 類中 共有的方法(屬性) 使用 哪個類?
    建立目前繼承中最 頂端的 類 去使用
2.如果我要使用 這些 類中 特有的方法(屬性) 使用 哪個類?
    建立目前繼承中 最末端類 去使用
           

代碼示例

public class Demo03 extends Object{

}
//A類 爺爺類 B類 是父親 C類 是 孫子類
//A類中又name C類中 會叫爺爺

class A extends Object{
    String name;
}

class B extends A{

}
class C extends B{
    public void speak(){
        System.out.println("會叫爺爺");
    }
}
           

構造方法能不能被繼承?

爺爺 父親 兒子
爹 和 兒子 都用同一中 方法生出來 行嗎? 亂
奶奶不願意  媽媽不願意

構造方法是不能被繼承的

為了保證繼承的完整性 在你建立對象的時候
如果你不調用 父類的構造方法 
系統會幫你去調用 父類的無參構造方法
           

代碼舉例

public class Demo04{
    public static void main(String[] args){
        Son son = new Son();
        son.name = "張三";
        son.sayHi();

        //有參構造對象
        Son son2 = new Son("小明");
        son2.sayHi();
    }
}

class Father{
    String name;
    //有參 無參 構造方法
    public Father(){
        System.out.println("我是無參構造方法");
    }
    public Father(String name){
        this.name = name;
        System.out.println("我是有參構造方法");
    }
    public void sayHi(){
        System.out.println("姓名:" + name);
    }
}

class Son extends Father{
    //無參構造
    public Son(){
        //如果你沒有在子類的構造方法中 調用父類的構造方法
        //系統會預設給你的子類構造方法中 添加一行代碼
        super();//調用父類的 無參構造方法
        System.out.println("我是兒子類的無參構造方法");
    }
    //有參的
    public Son(String name){
        //如果你沒有在子類的構造方法中 調用父類的構造方法
        //系統 會預設 給你的子類 構造方法中 添加一行代碼
        super(); //系統幫你調用父類的構造方法
        this.name = name;
        System.out.println("我是兒子類的有參構造方法");
    }
}
           

輸出結果

我是爸爸類無參的構造方法
我是兒子類無參構造方法
張三

我是爸爸類無參的構造方法
我是兒子類有參的構造方法
小明
           

結果解釋

當new一個Son對象時,由于沒有傳進去參數,是以會先調用兒子類的無參構造方法,
由于Son類是Father的子類,所有在子類的構造方法中會自動調用父類的無參構造方
法,進而實作Son類的執行個體對象同時具有Son類與Father類的屬性與方法,是以先列印
父類無參構造方法中的"我是爸爸類的無參構造方法",之後傳回子類構造方法,列印
子類無參構造器中得"我是兒子類無參構造方法",之後對Son的nane屬性指派,調用s
ayHi方法将姓名打出

第二次new一個Son類對象的時候,傳進去姓名這個參數,所有會調用Son類中得有參
構造方法,該有參構造方法中第一句會先執行super(),即會先執行父類無參構造方
法,向上調用父類構造方法時,沒有傳進去參數,所有不會調用父類有參構造方法,打
印"我是爸爸類無參構造方法"後,傳回子類,即Son類,列印"我是兒子類有參構造方
法",然後将姓名指派,随後傳回main函數,調用sayHi方法将name列印出來.
           

super關鍵字

super 用于指向子類對象中的父類對象(構造方法)  相當于父類的對象
super 調用對象 super.對象
super 調用方法 super.方法()


super(); 調用父類的構造方法
this();  調用的是本類的構造方法
           

代碼示例

public class Demo05{
    public static void main(String[] args){
        TestB b = new TestB();
        b.fun();
    }
}

class TestA{
    int num1 = ;
    int num2 = ;

    public void sayHi(){
        System.out.println("我是父類的sayHi方法");
    }
}

class TestB extends TestA{
    int num1 = ;

    public void fun(){
        //使用this時 會先在本類中尋找該屬性
        //沒找到 就去父類中找  就近原則
        System.out.println(this.num1);  //30
        System.out.println(this.num2);  //20
        System.out.println(this.num3);  //10
    }
}
           

思考:如果父類中沒有無參構造方法 咋整?

建議:不管是父類 還是 子類 構造方法一定要寫全,避免出現問題
           

代碼舉例

public class Demo04{

}
class Phone{
    String name;

    public Phone(){

    }
    public Phone(String name){
        this.name = name;
    }
}
class MI extends Phone{
    public MI(){
        //子類的構造方法 如果你什麼都不寫 會預設調父類無參構造
        //如果父類中 沒有無參構造 就證明父類中一定有有參的構造方法
        //父類構造方法無論有參 還是 無參, 子類的構造方法都必須要調用一個


        //必須手動指定一個有參構造方法去調用
        super("Note2");
    }
    public MI(String name){
        super(name);
    }
}
           

方法的重寫

思考: 如果父類 和 子類的 方法 重名了,咋整?能不能重名?

答案是可以  方法的重寫
           

注意

1.方法的聲明完全一緻的 叫方法的重寫
2.方法的重寫建立在類與類之間有繼承關系(子類重寫父類的方法)
           

Override(重寫)和Overload(重載) 的差別

1.重寫前提:需要繼承關系
重載:在同一個類裡面實作

2.重寫:需要方法的聲明完全一緻
重載:相同的功能,不同的實作方式 隻跟參數有關
           

代碼示例

public class Demo07{
    public static void main(String[] args){
        IOS8 ios8 = new IOS8();
        ios8.siri();

        //如果直接列印對象 相當于 系統幫你列印時 調用 toString()方法
        System.out.println(ios8);   //輸出haha
        System.out.println(ios8.toString());   //輸出haha
    }
}

class IOS7{
    public void call(){
        System.out.println("打電話");
    }

    public void siri(){
        System.out.println("說英文");
    }
}

class IOS8 extends IOS7{
    //方法的重寫:對父類的方法 進行一個功能上的更新
    //調不調父類的方法 要根據實際情況
    public void siri(){
        //中 英文都會說
        super.siri();   //調用父類的方法
        System.out.println("會說中文");
    }
    //重寫toString()方法
    //利用toString方法 來寫介紹自己的方法
    public String toString(){
        return "haha";
    }
}
           

重寫toString()方法介紹

toString方法是Object類中的一個方法,故所有繼承Object類的類,這些類中都有toString方法;
假設有一個類為Animal,他的一個對象為animal,則System.out.println(animal),
列印出來的是一個全限定類名com.lanou3g.IOS7@33909752,而列印System.out.println(animal.toString),
.lanou3g.IOS7@33909752,即包名+類名+@33909752,說明列印animal與列印animal.toString()的結果是一樣的,toString()方法寫完整則是
public String toString(){},即說明toString方法是有返還值的,即列印animal.t
oString()的結果,列印的是toString()函數中return語句返還的值,而列印animal
的結果又與列印animal.toString()方法的值相同,說明列印對象名所得到的結果就
是toString中return語句返還的結果,所有重寫Object類中得toString方法,改變re
turn語句的返還值,然後直接列印對象名,便可以得到自己想要的結果.具體結果如上面代碼所示
           

執行個體練習繼承關系

需求

老師類 學生類  
* 無參 有參構造 set/get方法 成員變量私有化 介紹自己的方法
  屬性:姓名,年齡
* 行為:吃飯
* 老師有特有的方法:講課
* 學生有特有的方法:學習
           

代碼實作

package com.lanou3g;


public class Demo08 {
    public static void main(String[] args) {
        //建立一個學生
        Student student = new Student("王龍",,);
        System.out.println(student);

        Teacher teacher = new Teacher("劉",);
        System.out.println(teacher);
    }
}
class Person1{
    //屬性
    //繼承中private修飾的變量 是不能直接通路的 但可以間接通路的
    private String name;
    private int age;
    //構造方法
    public Person1() {

    }
    public Person1(String name, int age) {
        this.name = name;
        this.age = age;
    }
    //set/get方法
    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("吃飯");
    }
    //介紹自己的方法
    @Override
    public String toString() {
        // TODO Auto-generated method stub
        return "姓名:" + name + "年齡" + age;
    }

}
//學生類
class Student extends Person1{
    //學号
    private int num;

    public int getNum() {
        return num;
    }
    public void setNum(int num) {
        this.num = num;
    }
    //構造方法
    public Student(){
    }
    //有參
    public Student(String name, int age, int num) {
        //直接調用父類的構造方法 完成指派初始化
        //為了保證繼承的完整性 在子類構造方法中
        //第一行 必須調用父類的構造方法(無參 有參都行)
        super(name,age);
        //特有屬性 直接指派就行
        this.num = num;
    }
    public void study() {
        System.out.println("學生在學習");
    }
    //介紹自己的方法
    @Override
    public String toString() {
        //可以在父類的基礎上 添加自己特有的屬性 去列印
        return super.toString() + "學号:" + num;
    }
}
//教師類
class Teacher extends Person1 {
    public Teacher(){

    }
    public Teacher(String name, int num) {
        super(name,num);
    }
    public void teach() {       
        System.out.println("老師會講課");
    }
    @Override
    public String toString() {
        // TODO Auto-generated method stub
        return super.toString();
    }
}