> 文档中心 > Java语言面向对象三大核心特性之【继承】

Java语言面向对象三大核心特性之【继承】

             ​​​​​​​

目录

🐲 1.理解继承的概念

🐲 2.继承的语法格式(关键字extends)

🐲 3.子类对父类成员的访问

🦄 3.1 子类中访问父类的成员变量 

🦖 3.1.1 子类和父类没有同名的成员变量(直接访问)

🦖 3.1.2 子类和父类有同名的成员变量(关键字super)

🦄 3.2 子类中访问父类的成员方法

🦖 3.2.1子类和父类成员方法名字不同(直接调用)

🦖 3.2.2 子类和父类成员方法名字相同(关键字 super)

🐲 4.super关键字

🐲 5.super和this的异同点

🐲 6.子类对象的构造方法

🐲 7.在继承关系中代码块的执行顺序

🐲 8.访问限定符protected(受保护的)

🐲 9.继承类型

🐲 10.关键字final

🐲 11.继承和组合的选择


🐲 1.理解继承的概念

⚜️我们先思考一下,为什么会有继承这个概念,继承的作用是什么?

我们先看一张图片(图片取自网络,侵权删除)

这个图片反映了这些动物的一些共性,比如

兔子和羊都是食草动物,狮子和豹子又是食肉动物

食草动物和食肉动物,又都是动物。

每一层次都有一些共性,这样就可以比如说

各种动物是父类,下面的的这些兔子,羊等是子类

🤠父类更通用,子类更具体,子类会具有父类的一般特性也会具有自身的特性

这样的例子放在我们java中,就比如这样

🟩所以根据这种共性,在java面向对象思想中提出了继承的概念,专门用来进行共性抽取,实现代码的复用,

🟪并且在java中,它还允许程序员在保持原有类特性的基础上进行扩展,增加新功能,这样产生新的类,叫派生类。

⚜️              再次强调一句话:共性抽取,实现代码复用 这是继承存在的目的                    ⚜️


🐲 2.继承的语法格式(关键字extends)

既然已经知道了继承的存在意义和概念,下面就说明一下语法格式,

🟥语法格式很简单,但必须记住继承的关键字是extends

修饰符  class  子类(派生类)  extends  父类(基类/超类)  {

      //

}

⚜️下面把前面写过的猫和狗的代码,进行共性抽取,写出派生类来

class Animal{    public String name;    public int age;    public void eat() { System.out.println(name+"正在吃饭!");    }}class Dog extends Animal {    public float weight; public void bark() { System.out.println(name+"正在狗叫");    }}class cat extends Animal{      public void bark() { System.out.println(name+"正在猫叫");    }}

⚜️下面将现在写的继承代码,和之前写的代码进行比较

⚜️ 我们这里再强调两个必须要注意的点:

🟧(1)子类继承父类,会将父类中的成员变量或者成员方法都继承到它自己身上,

🟫(2)子类继承父类,子类必须新添加自己特有的成员,体现出和父类的相同,不然子类和父类相同了,那子类干嘛要继承呢

比如说这个


 

🐲 3.子类对父类成员的访问

⚜️下面思考这样一个问题,子类继承父类,会将父类中的成员变量和成员方法都继承下来,

     那么在子类中如何访问从父类中继承下来的成员,能直接访问吗?

要回答这个问题就要把这个,分成两部分回答

🤠(1)在子类中,如何访问父类的成员变量

🤠(2)在子类中,如何访问父类的成员方法

🦄 3.1 子类中访问父类的成员变量 

🦖 3.1.1 子类和父类没有同名的成员变量(直接访问)

这种很简单直接就可以访问到,看代码

//子类和父类不存在同名成员变量class Base{    public int a;    public int b;}class Derived extends Base {    public int c;    public int d;    public void test(){ System.out.println(this.a); System.out.println(this.b); System.out.println(this.c); System.out.println(this.d);    }}public class Test02 {    public static void main(String[] args) { Derived derived = new Derived(); derived.test();    }}

🦖 3.1.2 子类和父类有同名的成员变量(关键字super)

🟪当子类和父类有同名的成员变量时,优先访问自己的

class Base{    public int a=1;    public int b=2;}class Derived extends Base {    public int a=3;    public int d=4;    public void test(){ System.out.println(this.a);//3 System.out.println(this.b);//2 System.out.println(this.d);//4    }}public class Test02 {    public static void main(String[] args) { Derived derived = new Derived(); derived.test();    }}

⚜️成员同名直接访问的话,访问的是子类自己的成员,那怎么才能访问到父类的成员呢? 

🟦如果子类非要访问父类的成员,那就要使用 super 这个关键字了

🟧super 表示子类从父类中继承的这一部分成员的地址

 

子类访问父类成员变量 

⚜️ 总结一下:

🟥(1)如果子类中有,优先访问自己的成员变量

🟩(2)如果子类中没有,就从父类中继承下来,如果父类中也没有,那就报错了

🟧(3)如果有成员变量同名,那就优先访问子类自己的

一句话,那就是:

🟪成员变量访问遵循就近原则,先访问自己,如果没有,找父类


🦄 3.2 子类中访问父类的成员方法

🦖 3.2.1子类和父类成员方法名字不同(直接调用)

这种很简单直接就可以调用到,看代码

class Base{    public int a=1;    public int b=2;    public void methodA() { System.out.println("Base::methodA()");    }}class Derived extends Base {    public int a=3;    public int d=4;    public void methodB() { System.out.println("Derived::methodB()");    }    public void test(){ methodA();//访问父类继承的methodA() methodB();//访问字类自己的methodB()    }}public class Test02 {    public static void main(String[] args) { Derived derived = new Derived(); derived.test();    }}

🦖 3.2.2 子类和父类成员方法名字相同(关键字 super)

我们先看这样一段代码

class Base{    public int a=1;    public int b=2;    public void methodA() { System.out.println("Base::methodA()");    }    public void methodB() { System.out.println("Base::methodB()");    }}class Derived extends Base {    public int a=3;    public int d=4;    public void methodA(int val) { System.out.println("Derived::methodA(int)"+val);    }    public void methodB() { System.out.println("Derived::methodB()");    }    public void test(){ methodA();//Base::methodA() methodA(100);//Derived::methodA(int) methodB();//Derived::methodB()    }}public class Test02 {    public static void main(String[] args) { Derived derived = new Derived(); derived.test();    }}

通过观察:

⚜️子类和父类同名时,子类要访问父类的话,

🟥如果子类和父类同名方法中,构成了重载,也就是同名但参数列表不同时,就可以根据自己参数,选择合适的的方法进行访问

🟦如果子类和父类同名方法中,没有构成重载,那就是重写了,后面会提到重写

就还是会优先访问子类自己的方法。

⚜️再思考一下

🟪如果还是非要访问父类的同名成员方法,那该如何访问?

    和前面那个一样还是用super.同名方法就可以访问同名的父类成员方法了

class Base{    public int a=1;    public int b=2;    public void methodA() { System.out.println("Base::methodA()");    }    public void methodB() { System.out.println("Base::methodB()");    }}class Derived extends Base {    public int a=3;    public int d=4;    public void methodA(int val) { System.out.println("Derived::methodA(int)"+val);    }    public void methodB() { System.out.println("Derived::methodB()");    }    public void test(){ methodA();//Base::methodA() methodA(100);//Derived::methodA(int) super.methodB();//Base::methodB()    }}public class Test02 {    public static void main(String[] args) { Derived derived = new Derived(); derived.test();    }}

子类访问父类成员方法

⚜️ 总结一下:

🟥(1)如果子类中有,优先访问自己的成员方法

🟩(2)如果子类中没有,就从父类中继承下来,如果父类中也没有,那就报错了

🟧(3)如果有成员方法同名,并且构成了重载,就可以根据自己参数,选择合适的的方法进行访问。

🟦(4)如果有成员方法同名,但没有构成重载,如果直接访问成员方法就会访问子类自己的

🟫(5)如果有成员方法同名,但没有构成重载,那就要用super . 同名方法,才可以访问父类成员方法。

一句话,那就是:

🟪成员变量访问遵循就近原则,先访问自己,如果没有,找父类


🐲 4.super关键字

前面已经用过了super关键字,这里再总结一下

🟥super关键字作用:在子类方法中访问父类的成员

🟦super 只能在非静态的方法中使用:这是因为

super还有一种用法就是

🟪super()    调用父类的构造方法


🐲 5.super和this的异同点

⚜️我们在前面都提过了super和this,它们两个都可以用来访问:

成员变量和成员方法,并且都可以作为构造方法中的第一条语句,那么它们两个还有什么异同点?

相同点:

(1)在构造方法中调用,必须是构造方法中第一句,并且super和this不能同时存在

(2)都是只能在非静态方法中使用,因为这两个都需要对象

(3)都是关键字

不同点:

(1)this 是当前对象的引用,

         super是子类对象从父类继承下来部分成员的引用 

(2)在构造方法中一定会存在super()的调用,不管写不写都有

         this()是如果不写就不调用

(3)this()用于调用自己类的构造方法

        super()用于调用父类的构造方法,两个不能同时使用

(4)在非静态的成员方法中,this是用来访问本类的方法和属性

        super用来访问从父类继承下来的方法和属性


🐲 6.子类对象的构造方法

先看这段代码

 ⚜️注意看子类继承是代码报错了,这是因为什么?

🟩前面在父类中已经写了构造方法,而在子类中又没有重新写,编译器会自动, 提供一个无参的子类构造方法进行初始化,

一个父类构造方法有参,一个子类构造方法无参,所以才会报错

⚜️下面写一个子类对象的构造方法, 

class Animal{    public String name;    public int age;    public Animal(String name, int age) { this.name = name; this.age = age;    }    public void eat() { System.out.println(name+"正在吃饭!");    }}class Dog extends Animal {    public float weight;    public Dog(String name,int age,float weight) { super(name, age); //调用父类的构造函数,来初始化此时子类继承过来父类的属性 this.weight = weight;    }    public void bark() { System.out.println(name+"正在狗叫");    }    @Override    public String toString() { return "Dog{" +  "name='" + name + '\'' +  ", age=" + age +  ", weight=" + weight +  '}';    }}}

⚜️总结一下子类对象构造方法:

子类对象成员是由两部分组成:

 🟧子类对象构造时,需要先调用父类构造方法,将从父类继承下来的成员构造完整,

      然后再调用子类自己的构造方法,将子类自己新增加的成员初始化完整。

⚜️提几点注意:

🤠(1)如果父类执行默认的构造方法,那么在子类构造方法的第一行默认含有super()的调用

🤠(2)如果父类构造方法是带有参数的,此时编译器给子类不会执行默认的构造方法,

这就要程序员自己给子类定义构造方法了

🤠(3)在子类构造方法中,super()调用父类构造时,必须是子类构造方法中第一条语句。

🤠(4)super()只能在子类构造方法中出现一次,并且不能和this同时出现

这里再说一次,用idea如何快速写构造方法


🐲 7.在继承关系中代码块的执行顺序

⚜️直接先看这段代码,思考一下打印顺序

class Animal{    public String name;    public int age;    static { System.out.println("Animal的静态代码块!");    }    { System.out.println("Animal的实例代码块!");    }    public Animal() { System.out.println("Animal不带参数的构造方法!");    }    public Animal(String name, int age) { this.name = name; this.age = age;    }    public void eat() { System.out.println(name+"正在吃饭!");    }}class Dog extends Animal {    public float weight;    static { System.out.println("Dog的静态代码块!");    }    { System.out.println("Dog的实例代码块!");    }    public Dog() { System.out.println("Dog的不带参数的构造方法!");    }    public Dog(String name, int age, float weight) { super(name, age); this.weight = weight;    }    public void bark() { System.out.println(name+"正在狗叫");    }    @Override    public String toString() { return "Dog{" +  "name='" + name + '\'' +  ", age=" + age +  ", weight=" + weight +  '}';    }}}public class Test01 {    public static void main(String[] args) { Dog dog = new Dog();    }}

我们看一下运行结果:

 

⚜️下来我们再思考一下,如果把代码在运行一次,执行结果会不会变化

🟪 这个说明了,静态的代码块只会执行一次 

⚜️总结一下,在继承关系中代码块的执行顺序 

🟫(1)父类静态代码块先执行 ---》下来执行子类静态代码块。(静态代码块最先执行)

🟦(2)父类实例代码块先执行 ---》下来执行父类构造方法。    (父类要比子类执行早)

🟧(3)子类实例代码块先执行 ---》下来执行子类构造方法

                                                                                            (实例代码块比构造方法执行早)

🟥(4)第二次实例化对象时,不论子类父类,静态代码块都不执行


🐲 8.访问限定符protected(受保护的)

将我们上一篇的表格拿过来

 下面我们通过代码来看一看限定范围

🤠同一个包中的同一类

class Demo {    protected int a;    public void func() { System.out.println(a);    }}

🤠同一包中不同的类

🤠不同包中同的子类

 


🐲 9.继承类型

先看一张图片(图片取自网络,侵权删除)

这张图片很清楚的画出了,java中的几种继承方式

 下面来简单分析一下

⚜️(1)单继承

 ⚜️(2)多级继承

⚜️ (3)分层继承(也就是不同的类继承同一个类)


🐲 10.关键字final

🟥在前面很多地方都用到了 final 主要是用来修饰变量、成员方法以及类。

这里就简单提一下

⚜️(1)final修饰变量成员,就表示不能被修改的常量了

 这里修改就会报错

⚜️2)final 修饰类,就表示这个类不能被继承

被final修饰了,强行继承就会报错

⚜️(3)修饰方法:表示该方法不能被重写


🐲 11.继承和组合的选择

⚜️本篇前面一直在写继承,那么看到这里,你理解继承了吗,继承到底是什么?

🟥继承指的是一个类(称为子类、子接口)继承另外的一个类(称为父类、父接口)的功

能,而且可以增加它自己的新功能的能力,直接点讲就是

🟪共性抽取,实现代码复用

🟦它的关系就是 is-a​​​​​​​

⚜️那么我们这里为什么又会提到组合,组合是什么你了解吗?

🟥复用性是面向对象的语言好处之一,

🟧而这个复用性在java代码中,有三个表现形式:继承,组合,代理,

🟫所以我们下面将要介绍一下 复用性表现形式之  组合

⚜️对于组合的理解:

🟩组合是通过对现有对象进行拼装即组合产生新的具有更复杂的功能。 

🟦组合体现的是整体与部分,所拥有的关系,也就是has-a的关系

🟪也把这种方式的代码复用叫黑盒式代码复用

⚜️那么最重要的一个问题就是,继承和组合该如何选择

这里提个建议,

🟧在两种都可行的情况下,优先使用组合

🟦原因是组合比继承更加灵活,也更有助于代码维护


 

588库千库资源网