继承extends

继承extends

关键字extends,语法:

public class 子类名 extends 父类{
    
}

子类继承了父类的成员(排除private修饰成员,以及父类的构造方法
子类也叫派生类,父类也叫基类。java里面的继承属于单根继承,一个类只能继承一个类,所有的类都默认继承超级父类Object。子类可以有独有的成员变量和方法。

创建子类对象
1. 默认执行父类的无参构造
2. 执行子类的无参构造

创建子类对象的时候,先输出父类的无参构造,再输出子类的构造,如果在父类有static代码块时候,先走静态代码块,因为static是class加载的时候就被加载,所以在子类对象被创建的时候,先走父类的静态代码块,再走子类的静态代码块,再回到栈内存创建子类的对象,再走父类的无参构造,再走子类的无参构造

子类 对象名=new 子类();
//执行流程:父类class被加载-->父类静态代码块被加载-->子类class被加载-->子类static被加载-->父类构造-->子类构造

且子类需要通过set/get来访问父类继承到的私有属性

demo1.setId(3);//给继承父类的id属性赋值
demo1.getId();//得到id

创建子类对象默认调用了父类的无参构造方法,但没有创建父类对象,因为没有new在堆内存开辟空间

public class Demo {
    private int id;
    public Demo() {
        System.out.println("父类的无参构造this"+this);
    }
    static {
        System.out.println("父类的staic代码块");
    }
}
public class Demo1 extends Demo {
    public Demo1() {
        //默认调用了父类的无参构造,也就是默认存在super();
        System.out.println("子类的无参构造this"+this);
    }
    static {
        System.out.println("子类的staic代码块");
    }
}

子类的所有构造方法里面默认调用了父类的无参构造,也就是默认存在super();,所以会先走super();执行父类的无参构造,再走子类构造的内容。

public static void main(String[] args) {
    Demo1 demo1 = new Demo1();
        demo1.setId(3);
        System.out.println(demo1.getId());
        System.out.println("创建的对象demo1"+demo1);
}

输出结果是

父类的staic代码块
子类的staic代码块
父类的无参构造thiscom.extendsdemo.Demo1@1540e19d
子类的无参构造thiscom.extendsdemo.Demo1@1540e19d
3
创建的对象demo1com.extendsdemo.Demo1@1540e19d

表示在继承关系下创建的子类对象,在父类中的this还是正在执行的对象,也就是子类对象。

子类代码:

public class Demo1 extends Demo {
    private String name;
    public Demo1() {
        System.out.println("子类的无参构造this"+this);
    }
    public Demo1(String name,int id) {
     //默认存在super();执行调用父类的无参构造
        this.name=name;
        this.setId(id);
    }

}

父类代码:

public class Demo {
    private int id;
    public Demo(int id) {
        this.id = id;
    }
    public Demo() {
        System.out.println("父类的无参构造this"+this);
    }
}

执行程序类:

public class test1 {
    public static void main(String[] args) {
        Demo1 demo1 = new Demo1("张三",36);
        System.out.println(demo1.getId());
        System.out.println(demo1.getName());
    }
}

输出结果:

父类的无参构造thiscom.extendsdemo.Demo1@1540e19d
36
张三

这时候假如我把父类的无参构造删掉留下父类的有参构造,会出现什么现象?

Error:(16, 20) java: 无法将类 com.extendsdemo.Demo中的构造器 Demo应用到给定类型;
  需要: int
  找到: 没有参数
  原因: 实际参数列表和形式参数列表长度不同

在子类的无参构造中调用父类的有参构造是没有意义的,所以正常开发中,几乎不存在子类的无参或少参数的构造调用父类的有参,子类的有参构造也不会调用父类的无参构造,因为子类的有参构造是用来赋值的,如果调用父类的无参,子类的有参构造的参数就没有赋到值,不会混合使用,也就是有参调有参,无参调无参。

当父类中没有无参构造方法时,子类需要加上super(参数);

public class Demo1 extends Demo {
    private String name;
    public Demo1() {
        super(28);//需要赋值,因为子类没有参数给传值
        System.out.println("子类的无参构造this"+this);
    }
    public Demo1(String name){
        super(36);;//需要赋值,因为子类没有参数给传值
    }
    public Demo1(String name,int id1) {
        super(id1);//不需要赋值,这里有参数id1来传递参数给父类的有参构造
        this.name=name;
//        this.setId(id);父类无参构造删掉时候需要用super(id)代替这行操作
    }

所以说,如果父类改变的时候子类也需要改变,是比较麻烦的,这时候可以利用工具来完成alt+ctrl,选中所有的构造方法,就可以完成子类的构造创建。

super

this和super的区别:

this是指当前执行的对象,可以调用属性和方法。

super只是一个父类引用的关键字,不是对象,假如把父类的无参构造删掉,需要加上super(子类继承的属性赋值)。

1. 在子类中,super可以访问父类成员变量和方法
子类属性跟父类属性相同时候,利用super来区分子类和父类的属性
父类代码:
public class Demo {
    private int id=100;
    public Demo() {
    }
}
子类代码:
public class Demo1 extends Demo {
    private int id=34;
    public Demo1() {
      super();//调用父类的无参构造,因为父类在这时没写有参,所以我这里子类也不写有参构造
    }
    public void fun(){
        System.out.println("子类的id"+this.id);//34
        System.out.println("父类的id"+super.getId());//100
    }
   /* public static void main(String[] args) {//执行程序
        Demo1 demo1=new Demo1();
        demo1.fun();
    }*/
}
所以在子类和父类的属性定义名称时最好不要定义相同的,如果相同需要用super区分
2. 在子类构造中,利用super调用父类构造方法
方法重写

方法重载:方法名相同,形参不同,与修饰符和返回值无关。一般体现在工具类

方法重写:前提:有层级关系(继承,实现)

在子类中重写父类的方法,覆盖掉父类的方法

方法名和形参必须与父类的方法相同

父类代码:

@Setter
@Getter
public class Demo {
    private int id=100;
    public Demo() {
    }
    public void fun(){
        System.out.println("父类的方法");
    }
}

子类方法重写:alt+ctrl选择override

public class Demo1 extends Demo {
    private int id=34;
    public Demo1() {
      super();
    }
    @Override//jdk提供的内置注解
    public void fun(){//对父类的方法重写
        super.fun();//加上这一行父类的方法也会执行,如果不加这一行父类的fun不会执行
        System.out.println(getId()+"对父类的方法重写");
    }
}
举例:重写超级父类object类的tostring方法

需要在父类代码中重写tostring方法:

public class Demo {
    private int id=100;
    public Demo() {
    }
    @Override
    public String toString() {
        return "Demo{" +
                "id=" + id +
                '}';
    }
}

子类代码中的tostring会返回super.toString();因为他的父类是Demo,而Demo的父类才是object类,所以子类继承之前需要在父类中先重写超级父类中的方法:

public class Demo1 extends Demo {
    private int id=34;
    public Demo1() {
      super();
    }
    @Override
    public String toString() {
        return super.toString();
    }
}

在main中就可以直接将子类对象转换成字符串打印出来:

public static void main(String[] args) {
    Demo1 demo1=new Demo1();
    System.out.println(demo1.toString());
}

输出结果:

Demo{id=100}
子返回值类型要小于等于父类方法的返回值,访问权限修饰符要大于等于父类的访问权限修饰符

如果一个数据类型底层代码是final修饰,说明它是没有子类的。public>protected>默认>private 所以一般父类可以写成protected,可以只对子类友好。

父类方法用object类

private Object fun(){
    return null;
}

那子类重写方法中返回值得小于父类方法的返回值

public String str(){
    return null;
}

但是不推荐在子类中重写父类的方法:

在面向对象编程的设计中:
SOLID:
     S:单一功能原则
     O:开放闭合原则
     L:里氏替换原则
     I:接口隔离原则
     D:依赖注入原则
里氏替换原则:
1. 父类出现的地方都可以使用子类替换
2. 不要随意更改父类结构,如果要重写方法可以用抽象方法/接口替换
posted @ 2022-10-19 07:13  Liku007  阅读(111)  评论(0编辑  收藏  举报