【Java学习笔记】继承

继承(extends

为什么需要继承?

当不同的类中有相同的属性或者方法的时候可以避免重复定义,提高代码的阅读性和复用性

关系

  • 父类(又名基类超类

  • 子类(又名派生类

  • 子类继承父类的属性和方法,子类又可以当作父类继续继承子类


案例:快速入门

// 父类 A
package extend;

public class A {
    String name;
    int age;
    double height;

    // 声明无参构造器
    public A() {

    }

    // 构造器的声明
    public A(String name, int age, double height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }

    // 设置属性值
    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setHeight(double height) {
        this.height = height;
    }

    public void printinfo() {
        System.out.println("A类的print函数被调用");
        System.out.print("姓名:" + name + "\n年龄:" + age + "\n身高:" + height);
    }
}

// 子类 B 继承 父类 A
package extend;

public class B extends A {

    // B 继承 A类, 此时 B 拥有 A 的所有属性和方法,不同的地方重新声明即可,
    // 共有的部分:直接调用即可

    public void printinfo() {
        System.out.println("B类的print函数被调用");
        System.out.print("姓名:" + name + "\n年龄:" + age + "\n身高:" + height);
    }
}


// 主函数中调用
package extend;

public class test {
    public static void main(String[] args) {
        A a = new A();
        a.setName("jackson");
        a.setAge(18);
        a.setHeight(1.68);
        a.printinfo();

        System.out.println("\n");

        B b = new B();
        b.setName("bob");
        b.setAge(22);
        b.setHeight(1.73);
        b.printinfo();
    }
}


// 输出结果
A类的print函数被调用
姓名:jackson
年龄:18
身高:1.68

B类的print函数被调用
姓名:bob
年龄:22
身高:1.73

代码解析

  • 使用了继承避免了在B类中重复定义相同的属性和方法

  • 使用继承后B类拥有A类的所有属性和方法,是共用的

  • B类中修改了print函数的提示信息,实则上如果没有提示信息,B类中可以不写任何内容

得出结论:子类继承父类后,只需要修改子类中和父类不同的地方即可,其余的利用继承直接调用即可


继承的使用细节

  • (1) 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性和方法不能在子类直接访问要通过父类提供的公共方法去访问

  • (2) 子类必须调用父类的构造器,完成父类的初始化,之后才会调用子类的构造器完成子类的初始化。

  • (3) 当创建子对象时,不管使用子类的哪个构造器,默认情况下总会调用父类的无参构造器。如果父类没有提供无参构造器,必须显式在子类的构造器中用 super 去指定使用父类的哪个构造器来完成父类的初始化工作,否则编译报错。

  • (4) 如果希望指定去调用父类的某个构造器,则显式的调用:super(参数列表)

  • (5) super 在使用时,必须放在构造器第一行(super 只能在构造器中使用)。

  • (6) super()this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器。

  • (7) java 所有类都是 Object 类的子类,Object 是所有类的基类。

  • (8)父类构造器的调用不信啊与父类,将一直往上追溯到Object类(顶级父类),子类最多只能继承一个父类(指直接继承),即Java 中是单继承机制

    • 注意:不能滥用继承,子类和父类之间必须满足is - a的逻辑关系

  • (9)可以通过间接的方法实现多继承。例如:A继承了B,B继承了C,就可以间接的实现 A继承B和C

第一点的代码示例说明

子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性和方法不能在子类直接访问要通过父类提供的公共方法去访问

// 父类 A
package extend1;

public class A {

    String name;
    private int age;

    public A(){

    }

    public A(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public double getage(){
        return this.age;
    }
    public void printinfo() {
        System.out.println("A类的print函数被调用");
        System.out.print("姓名:" + name + "\n年龄:" + age);
    }
}

// 子类 B 继承 父类 A
package extend1;

public class B extends A{
    // age 是私有的,这个时候使用 getage方法访问 age
    public void printinfo() {
        System.out.println("B类的print函数被调用");
        System.out.print("姓名:" + name + "\n年龄:" + getage());
    }

}

// 主函数
package extend1;

public class test {
    public static void main(String[] args) {
        A a = new A();
        a.setName("jackson");
        a.setAge(18);
        a.printinfo();

        System.out.println("\n");

        B b = new B();
        b.setName("bob");
        b.setAge(22);
        b.printinfo();
    }
}

// 输出结果
A类的print函数被调用
姓名:jackson
年龄:18

B类的print函数被调用
姓名:bob
年龄:22.0

代码解析

ageprivate的,在子类B中无法访问,通过在父类中写入getage方法,通过继承来调用getage方法实现对private age属性的访问

代码说明:关于super的使用

一、创建子类时,子类的构造器默认会先调用父类无参构造器,之后调用自身的构造器

// 父类A
package extend1;

public class A {

    public A(){
        System.out.print("A类的无参构造器被调用\n");
    }
}

// 子类B 继承 父类A
package extend1;

public class B extends A{
    public B(){
        // super默认存在,优先调用父类的构造器,之后才执行子类的构造器
        super(); // 默认调用父类的无参构造器,如果没有,需要指定使用父类的哪个构造器
        System.out.print("子类B的无参构造器被调用\n");
    }

}

// 主类
package extend1;

public class test {
    public static void main(String[] args) {
        B b = new B();
    }
}

// 输出结果
A类的构造器被调用
子类B的无参构造器被调用

代码解析:创建子类 B 对象时,优先调用父类的无参构造器,输出后,再调用子类的构造器,如果父类的无参构造器被覆盖或者没有显示声明需要用 super 指定调用父类的构造器

二、super调用父类指定的构造器

// 父类A
package extend1;

public class A {
    String name;
    int age;

    public A(){
        System.out.println("A类的无参构造器被调用");
    }

    public A(String name) {
        this.name = name;
        System.out.println("A类中public A(String name)构造器被调用");
    }

    public A(int age) {
        this.age = age;
        System.out.println("A类中public A(int age)构造器被调用");
    }

    public A(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("A类中带有全部属性初始化的构造器被调用");
    }

    public void printinfo() {
        System.out.println("A类的print函数被调用");
        System.out.print("姓名:" + name + "\n年龄:" + age);
        System.out.println("\n");
    }

}

// 子类B
package extend1;

public class B extends A{
    public B(){

    }
    public B(int age){
        super(age);
        System.out.println("子类B的无参构造器被调用");
    }
    public void printinfo() {
        System.out.println("B类的print函数被调用");
        System.out.print("姓名:" + name + "\n年龄:" + age);
        System.out.println("\n");
    }

}

// 主类
package extend1;

public class test {
    public static void main(String[] args) {
        /*
        共有的属性
        int age;
        String name

        对于 父类A 可以初始化 age/name/age,name
        对于 子类B 可以初始化 age (使用super指定了)
         */

        A a = new A("jackson",18);
        a.printinfo();

        // 测试 子类B 中的无参构造器
        B b = new B();
        b.printinfo();


        // 测试 子类B 中用super调用主类中指定的构造器
        B b1 = new B(18);
        b1.printinfo();
    }
}

// 输出结果
A类中带有全部属性初始化的构造器被调用
A类的print函数被调用
姓名:jackson
年龄:18

A类的无参构造器被调用
B类的print函数被调用
姓名:null
年龄:0

A类中public A(int age)构造器被调用
子类B的无参构造器被调用
B类的print函数被调用
姓名:null
年龄:18

代码解析

通过输出结果可以发现两点

  • (1)子类调用构造器时优先调用父类的无参构造器

  • (2)在子类的构造器中可以用super指定调用父类中的哪一个构造器


继承的内存分析(本质)

对象创建好之后会建立查找关系

  • 首先看子类是否有该属性

  • 如果子类有这个属性,并且可以访问,则返回信息

  • 如果子类没有这个属性,就看父类有没有这个属性(如父类有该属性,并且可以访问,就返回信息)

  • 如果父类没有就按(3)的规则,继续找上级父类,直到 Object…

public class pra {
    public static void main(String[] args) {
        Son son = new Son(); // 创建 Son 类的对象
        System.out.println("son.name:" + son.name);
        System.out.println("son.age:" + son.age);
        System.out.println("son.hobby:" + son.hobby);
    }
}

class GrandPa {  // 爷爷类
    String name = "大头爷爷";
    String hobby = "钓鱼";
}

class Father extends GrandPa {  // 父亲类继承爷爷类
    String name = "大头爸爸";
    int age = 39;
}

class Son extends Father {  // 儿子类继承父亲类
    String name = "大头儿子";
}

// 输出结果
son.name:大头儿子
son.age:39
son.hobby:钓鱼

分析:son没有agehobby这两个属性,但是访问结果输出了,验证了继承中会寻找父类的属性,如果没有就找到上级父类,直到object

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jackson凌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值