Java继承全攻略

时间:2023-02-13 00:58:12


哈喽,我是兔哥呀,今天就让我们继续这个JavaSE成神之路!

这一节啊,咱们要学习的内容是Java的继承。

1.什么是继承

还是从现实中的例子出发,比如人和学生,学生属于人,每个学生都有姓名和年龄,人也拥有这些共通的属性,那么我们就可以说学生继承自人。

2.继承的实现

java继承是面向对象编程中一种基本概念,可以让子类继承父类的属性和方法,提高代码的复用性。

例如:

public class Person {
private String name;
private int age;
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public int getAge(){
return age;
}
public void setAge(int age){
this.age = age;
}
}

public class Student extends Person {
private String school;
public String getSchool(){
return school;
}
public void setSchool(String school){
this.school = school;
}
}

继承需要使用​​extends​​关键字。

上面的代码中,Student类继承了Person类,Student类继承了Person类的name和age属性,以及getName()和setName()方法,同时还定义了school属性和getSchool()方法。

在这个例子中,Person是被继承的类,叫做​​父类​​​或​​超类​​​。而Student是继承后的类,叫做​​子类​​​或者​​派生类​​。

3.继承的关系

在开发中,比如有学生类和老师类,他们有一些公共的属性,比如名字、年龄等,那么我们就可以提取一个公共的逻辑意义上的父类-人类。

所以,继承是 is a 的关系,学生是人,老师也是人,这样的情况下就可以生成一个逻辑父类 - Person,然后把学生类和老师类都去继承Person,实现属性和方法的复用。

4.方法的重写

Java方法重写是指在子类中重新定义父类中的方法,覆盖父类中的方法,使之成为子类的方法。

简单来说,就是子类的方法覆盖了父类的相同方法。

在开发中,使用方法重写可以让子类具有父类的特性,同时又能够根据自身的需要进行扩展,从而更加灵活地处理业务逻辑。

方法重写也是多态的一种表现形式,它可以让我们调用同一接口,却能够根据不同的实现类,调用不同的实现方法,实现不同的业务逻辑。

比如上面的例子,Person类中有一个sayHello方法:

public void sayHello(){
System.out.println("Hello, I am a person!");
}

子类又把sayHello重新定义了一遍,就是方法重写。

public void sayHello(){
System.out.println("Hello, I am a student!");
}

如下图,idea中对于重写的方法,有一个向上箭头的标志,点一下就能进入父类的同名方法。

Java继承全攻略

5.方法重写的返回值问题

这边就是要记住一个规律啦,当父类方法的返回值是void或者基本数据类型时,子类的重写方法的返回值必须和父类方法完全一致。

当父类方法的返回值是引用类型时,子类的重写方法的返回值可以是父类的其他子类引用。

验证一下,父类Person有一个getAge方法,我们试着重写这个方法。

//这是父类Person的getAge方法
public int getAge(){
return age;
}
//这是子类的getAge方法,返回值改了
public double getAge(){
return 1.0;
}

报错了:

Java继承全攻略

'getAge()' in 'com.company.dto.Student' *es with 'getAge()' in 'com.company.dto.Person'; attempting to use incompatible return type

所以,如果是基本数据类型,方法重写我们不能修改返回值。

父类增加一个返回值是Person的方法:

public Person getInstance(){
return new Person();
}

我们再创建一个老师类,重写父类的getInstance,同时把返回值改成自己。

public Teacher getInstance() {
return new Teacher();
}

如下图,这是允许的。

Java继承全攻略

你改成另一个子类引用也可以。

Java继承全攻略

6.方法重写的方法重载的区别

方法重写要求子类方法的​​参数列表​​​必须与父类方法的参数列表完全相同,​​返回值类型​​也必须完全相同(返回值如果是引用类型则向下兼容,参考上一节);

而方法重载仅要求子类方法的​​参数个数​​​或​​类型​​不同,而返回值类型可以相同也可以不同。

7.属性可以重写吗

答案是可以的,子类可以重复定义父类的同名属性,到时候使用的就是子类的属性啦。

8. 访问修饰符

Java的权限访问修饰符是用来控制类、接口、变量、方法的访问权限的,它有四种:public、protected、default和private。

8.1 private

private代表私有的,只能在本类中访问,其他任何地方都不能访问,包括子类。

证明:

这个name是private修饰的:

Java继承全攻略

然后在子类中:

Java继承全攻略

连子类中都无法访问,其他地方就都不必说了吧。哪怕你在其他地方把Person类new一个对象除了,都无法直接访问里面的private属性,对象也不行哦。

Java继承全攻略

所以,private的访问权限是最严格的。

8.2 public

public代表公有的,在任何地方都能访问,不管跨不跨包,是不是子类,public和private就是两个极端。

8.3 protected

protected代表受保护的,允许​​当前类​​​,​​同包下的任意类​​​,以及​​跨包子类​​调用。就这3个,没多的了,记好就行。

8.4 default(就是啥也不写)

啥权限修饰符都不写,代表默认访问权限,允许​​当前类​​​,​​同包下的任意类​​​调用。和protected的区别就是少一个​​跨包子类​​调用。

访问权限从小到大是:

private < default < protected < public

记住就行了,非常重要哈。

9.访问修饰符对方法重写的影响(大于等于父类的权限)

方法重写要求子类方法的访问权限​​不能低于​​父类方法的访问权限,即子类方法的访问权限可以和父类方法的访问权限相同,也可以更高,但不能更低。

为什么会这样呢,其实也是来源于生活,父母都希望自己的孩子超越自己,所谓望子成龙,望女成凤啊。

10. super关键字

super关键字是Java语言中的一个关键字,它的作用是调用超类中的成员变量和方法,也就是父类的成员变量和方法。

public class Person {

protected double money = 10000; //留给孩子1w买糖吃

... ...
}

给父类添加一个protected的money属性,子类重写这个属性。

public class Student extends Person {

protected double money;

public double getMoney() {
return money;
}

... ...
}

测试:

public static void main(String[] args) {
Student s = new Student();
System.out.println(s.getMoney());
}

结果是0.0,这是因为Student重写了money属性,所以访问的就是自己的money,要访问到父类的money,我们可以用super:

public double getMoney() {
return super.money;
}

这样拿到的就是1w啦。

super关键字也可以用来调用超类的构造方法,这样可以在子类的构造方法中调用超类的构造方法,从而实现继承。

我们给父类Person添加一个空构造:

public Person(){
System.out.println("Person类的构造方法被调用");
this.money = 20000;
}

子类调用方式如下:

public Student() {
super(); //调用父类的构造方法
}

再测试:

public static void main(String[] args) {
Student s = new Student();
System.out.println(s.getMoney());
}

打印:

Person类的构造方法被调用

20000.0

注意:构造方法是没办法被子类重写的。每一个子类对象被创建,都会先去调用父类的构造方法,如果没有显式地用super去指定调用父类的哪个构造方法,就会默认调用父类的无参构造方法。如果你在父类写了一个有参构造方法,但没有写无参构造方法,那么子类的构造方法就会报错。

比如,你在Person类中写了一个有参构造,却没有定义无参构造。

public Person(double money){
System.out.println("Person类的构造方法被调用");
this.money = money;
}

那么子类不管你有没有写无参构造都会报错:

Java继承全攻略

错误信息:There is no default constructor available in 'com.company.dto.Person'

解决办法,要么你显示地给Person添加无参构造,要么每个子类都要显示地用super去调用一下那个有参构造。

就算是工作好几年的老码农,也常常会在这个地方犯迷糊,这一点也要尤其注意哦!

总之,super关键字是Java语言中的一个关键字,它的作用是调用超类中的成员变量和方法,也可以用来调用超类的构造方法,从而实现继承。

10. super关键字 和 this关键字

1、super引用的是超类对象,this引用的是本类对象。

super可以在子类中调用超类中的成员,而this只能在本类中调用本类中的成员。

2、对于那些从父类继承的成员属性和成员方法,this和super访问的是同一个。

如:

public class Person {

protected double money = 10000; //留给孩子1w买糖吃

... ...
}

子类中:

public Student() {
System.out.println(this.money == super.money);
}

因为money已经被继承下来了,所以用this访问也是可以的。

4. 不管是this还是super,都不能出现在static方法中。

11.课后练习

Q:已知有Object类、A类和B类,其中A类继承Object类,B类继承A类,以下哪个语句是正确的?

A. B类的父类是Object

B. A类的父类是B类

C. B类的父类是A类

D. Object类的父类是A类