Java:关于继承

1.为什么需要继承

Java中使用类对现实世界中实体进行描述,类经过实例化之后的产物对象,则可以用来表示现实中的实体,但是现实世界错综复杂,事物之间可能存在一些关联,那在设计程序时就需要考虑。

例如:鸟和猫都是动物:

//Bird.java
public class Bird {
    public String name;
    public int age;

    public void eat(){
        System.out.println(this.name + "正在吃饭...");
    }

    public void fly(){
        System.out.println(this.name + "正在飞...");
    }
}
//Cat.java
public class Cat {
    public String name;
    public int age;

    public void eat(){
        System.out.println(this.name + "正在吃饭...");
    }

    public void mimi(){
        System.out.println(this.name + "正在咪咪叫...");
    }
}

从鸟类和猫类中,不难看出其中有重复的部分,为

 public String name;
    public int age;

    public void eat(){
        System.out.println(this.name + "正在吃饭...");
    }

而在面对对象思想中提出了继承的概念,专门进行重复内容的抽取,实现代码的复用。


2.继承的语法

2.1 表示继承

Java中借助 extends关键字 来表示类之间的继承关系,形式如下:

修饰符 class 子类 extends 父类{

  // ....

}

2.2 对1中事例使用继承方式重新设计

//Bird.java
public class Bird extends Animal{

    public void fly(){
        System.out.println(this.name + "正在飞...");
    }
}
//Cat.java
public class Cat extends Animal{

    public void mimi(){
        System.out.println(this.name + "正在咪咪叫...");
    }
}
//Animal.java
public class Animal {
    public String name;
    public int age;

    public void eat(){
        System.out.println(this.name + "正在吃饭...");
    }

}

2.3 注意

  • 子类会将父类中的成员变量或者成员方法继承到子类中。
  • 子类继承父类后,必须要添加自己特有的成员,体现出不同性,否则没必要继承了。

3.父类成员访问

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

①子类和父类不存在同名的成员变量

//Advenced.java
public class Advenced {
     int a = 10;
     int b = 20;
}

//Base.java
public class Base extends Advenced{
    int c = 30;
    public void func(){
        System.out.println("a= " + a);
        System.out.println("b= " + b);
        System.out.println("c= " + c);
    }
}

//Test.java
public class Test {
    public static void main(String[] args) {
        Base base = new Base();
        base.func();
    }
}


输出:
a= 10
b= 20
c= 30

看看能不能得出这种情况下的访问情况,后面有结论,可以对照思考一下。

②子类和父类存在同名的成员变量

//Advenced.java
public class Advenced {
     int a = 10;
     int b = 20;
}

//Base.java
public class Base extends Advenced{
    int a = 5;
    int c = 30;
    public void func(){
        System.out.println("a= " + a);
        System.out.println("b= " + b);
        System.out.println("c= " + c);
    }
}

//Test.java
public class Test {
    public static void main(String[] args) {
        Base base = new Base();
        base.func();
    }
}


输出:
a= 5
b= 20
c= 30

③总结

在子类方法中 或 通过子类对象访问成员时:

  • 如果访问的成员变量子类中有,优先访问子类自己的成员变量。
  • 如果访问的成员变量子类中无,则访问父类继承下来的,如果父类也没有定义,则编译报错。
  • 如果访问成员变量与父类的成员变量同名,则优先访问子类自己的。

成员变量访问遵循就近原则,自己有优先自己的,没有就向父类中去找。

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

①成员方法名字不同

//Advenced.java
public class Advenced {
    public void MethodA(){
        System.out.println("Advenced.MethodA");
    }
}

//Base.java
public class Base extends Advenced {
    public void MethodB(){
        System.out.println("Base.MethodB");
    }
    public void MethodC(){
        MethodA();
        MethodB();
    }
}

public class Test {
    public static void main(String[] args) {
        Base base = new Base();
        base.MethodC();
    }
}


输出:
Advenced.MethodA
Base.MethodB

②成员方法名字相同

//Advenced.java
public class Advenced {
    public void MethodA(){
        System.out.println("Advenced.MethodA");
    }

    public void MethodB(){
        System.out.println("Advenced.MethodB");
    }
}


//Base.java
public class Base extends Advenced {
    public void MethodA(int a){
        System.out.println(a);
    }
    public void MethodB(){
        System.out.println("Base.MethodB");
    }

    public void MethodC(){
        MethodA();
        MethodA(10);
        MethodB();
    }
}

//Test.java
public class Test {
    public static void main(String[] args) {
        Base base = new Base();
        base.MethodC();
    }
}


输出:
Advenced.MethodA
10
Base.MethodB

③总结

  • 通过子类对象访问父类与子类不同名方法时,优先在子类中,找到则访问,否则在父类中找,找到则访问,否则编译报错。
  • 通过子类对象访问父类与子类相同名方法时,如果子类和父类同名方法的参数列表不同(重载),根据调用方法适配传递的参数选择合适的方法访问,如果没有则会报错。

4.super关键字

如果子类中存在和父类相同成员的情况下,想调用父类里的成员该怎么办?super关键字来了。

以3.2中②为例:

//Advenced.java
public class Advenced {

    public void MethodB(){
        System.out.println("Advenced.MethodB");
    }
}

public class Base extends Advenced {

    public void MethodB(){
        System.out.println("Base.MethodB");
    }

    public void MethodC(){
        super.MethodB();
    }
}

//Test.java
public class Test {
    public static void main(String[] args) {
        Base base = new Base();
        base.MethodC();
    }
}


输出:
Advenced.MethodB

在子类方法中,想要访问父类中成员方法时,借助super关键字即可。详细可见第6部分

注意事项:

  1. 只能在非静态方法中使用
  2. 在子类方法中,访问父类的成员变量和方法

5.子类构造方法

父子父子,先有父再有子,即:子类对象构造时,需要先调用子类构造方法,然后执行子类的构造方法

//Advenced.java
public class Advenced {
    public Advenced(){
        System.out.println("Advenced....");
    }
}

//Base.java
public class Base extends Advenced{
    public Base(){
        
        System.out.println("Base...");
    }
}

//Test.java
public class Test {
    public static void main(String[] args) {
        Base base = new Base();
    }
}


输出:
Advenced....
Base...

从输出结果可以看出,父类和子类的构造方法都被执行,但在子类中并没有提及到父类的成员方法。但子类构造方法中实际情况是这样:

//Base.java
public class Base extends Advenced{
    public Base(){
        //super();  //注意子类构造方法中默认调用父类的无参构造方法:super()
        //如果用户没有写,编译器会自动添加,而且super()必须是子类构造方法中的一句
        //并且只能调用一次
        System.out.println("Base...");
    }
}

 注意:

  1. 父类显式定义无参或者默认的构造方法中,在子类构造方法第一行默认有super(),调用父类构造方法
  2. 父类显式定义有参,此时用户需要为子类显式定义构造方法,在子类构造方法中选择合适的父类构造方法调用,否则会调用失败。
  3. 在子类构造方法中,super()调用父类构造方法时,必须要放在子类构造函数中的第一条语句。
  4. super()只能在子类构造方法中出现一次,并且不能和this同时出现

补充:若父类方法中含参,代码如下

//Advenced.java
public class Advenced {
    public Advenced(int a){
        System.out.println(a);
    }
}

//Base.java
public class Base extends Advenced{
    public Base(int a){
        super(a);
        System.out.println("Base...");
    }
}

//Test.java
public class Test {
    public static void main(String[] args) {
        Base base = new Base(10);
    }
}


输出:
10
Base...

6.super和this

super和this都可以在成员方法中访问:成员变量和调用其他的成员函数,都可以作为构造方法的第一条语句,那么他们之间有什么区别?

6.1 相同点

  • 都是Java关键字
  • 只能在类的非静态方法中调用,用来访问非静态成员方法和变量
  • 在构造方法调用时,必须时第一条语句,并且两者不可同时调用

6.2 不同点

  • this是当前对象的引用,当前对象即调用构造方法的对象。super相当于是对子类从父类继承下来部分成员的调用
  • 在非静态成员方法中,this用来访问本类的方法和属性,super用来访问父类继承下来的方法和属性
  • 在构造方法中:this用来调用本类构造方法,super用来调用父类构造方法,两种调用不能同时出现在构造方法中
  • 构造方法中一定会存在super的调用,用户没有写编译器也会增加,但是this不写就没有

7. 再谈初始化

先谈谈几个重要的代码块:静态代码块,实例代码块。还记得它们和构造方法的执行顺序吗?

7.1 没有继承关系时的执行顺序

class Internal{
    public String name;
    public int age;

    public Internal(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("构造方法执行");
    }
    {
        System.out.println("实例代码块执行");
    }
    static {
        System.out.println("静态代码块执行");
    }
}

public class Test {
    public static void main(String[] args) {
        Internal internal = new Internal("zhangsan",16);
        System.out.println("============");
        Internal internal1 = new Internal("lisi",18);

    }
}


输出:
静态代码块执行
实例代码块执行
构造方法执行
============
实例代码块执行
构造方法执行
  1. 静态代码块先执行,并且只执行一次,在类加载阶段执行
  2. 当对象创建时,才会执行实例代码块,当实例代码块执行完成后,最后执行方法执行

7.2 有继承关系时的执行顺序

class External{
    public String name;
    public int age;

    public External(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("父类构造方法执行");
    }
    {
        System.out.println("父类实例代码块执行");
    }
    static {
        System.out.println("父类静态代码块执行");
    }
}

class Internal extends External{
    public String name;
    public int age;

    public Internal(String name, int age) {
        super(name,age);
        this.name = name;
        this.age = age;
        System.out.println("子类构造方法执行");
    }
    {
        System.out.println("子类实例代码块执行");
    }
    static {
        System.out.println("子类静态代码块执行");
    }
}

public class Test {
    public static void main(String[] args) {
        Internal internal = new Internal("zhangsan",16);
        System.out.println("============");
        Internal internal1 = new Internal("lisi",18);

    }
}



输出:
父类静态代码块执行
子类静态代码块执行
父类实例代码块执行
父类构造方法执行
子类实例代码块执行
子类构造方法执行
============
父类实例代码块执行
父类构造方法执行
子类实例代码块执行
子类构造方法执行
  1. 父类静态代码块优先执行,之后是子类静态代码块
  2. 父类实例代码块和构造方法紧跟其后
  3. 然后是子类实例代码块和构造方法
  4. 父类和子类的静态代码块只会执行一次

8.protectd关键字

8.1  四大访问修饰限定符​​​​

  1. private 只能在当前类使用

  2. default/空白 同一个包底下可以访问

  3. protected 同一个包底下都可以使用。但是,不同包下的只有子类才能使用

  4. public 那里都可以使用

8.2 protected关键字

package demo2;
public class Enternal {
    private int a;
    int b;
    protected int c;
    public int d;

package demo1;
import demo2.Enternal;


public class Internal extends Enternal {
    public void func(){
        super.a = 10;
        super.b = 20;
        super.c = 30;
        super.d = 40;
    }
}

图中的super.a和super.b会出现编译报错。原因如下:

  • super.a :private 只能在当前类中使用,即只可在Enternal类中使用
  • super.b:空白/default 须在一个包里才可访问,即在demo2包里使用

9.继承方式

           单继承                         多层继承                                      不同类继承同一个类  

Java支持这三种继承方式,不支持多继承

10.final关键字

final可以用来修饰变量,成员方法以及类

1.修饰变量或者字段,表示常量,即不可修改

public class Test {
    public static void main(String[] args) {
        final int a = 10;
        a = 10;
        System.out.println(a);
    }
}


输出:
java: 无法为最终变量a分配值

2.修饰类:表示此类不能被继承

//Animal.java
final public class  Animal {
    public String name;
    public int age;
}

//Dog.java
public class Dog extends Animal{
    public void bark(){
        System.out.println(this.name + "正在旺旺叫...");
    }
}

//Test.java
public class Test {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.bark();
    }
}


输出:
java: 无法从最终Animal进行继承

这次就先到这了,我们下次见。

目录

1.为什么需要继承

2.继承的语法

2.1 表示继承

2.2 对1中事例使用继承方式重新设计

2.3 注意

3.父类成员访问

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

①子类和父类不存在同名的成员变量

②子类和父类存在同名的成员变量

③总结

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

①成员方法名字不同

②成员方法名字相同

③总结

4.super关键字

5.子类构造方法

6.super和this

6.1 相同点

6.2 不同点

7. 再谈初始化

7.1 没有继承关系时的执行顺序

7.2 有继承关系时的执行顺序

8.protectd关键字

8.1  四大访问修饰限定符​​​​

8.2 protected关键字

9.继承方式

10.final关键字


评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值