继承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. 不要随意更改父类结构,如果要重写方法可以用抽象方法/接口替换