哈喽,我是兔哥呀,今天就让我们继续这个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中对于重写的方法,有一个向上箭头的标志,点一下就能进入父类的同名方法。
5.方法重写的返回值问题
这边就是要记住一个规律啦,当父类方法的返回值是void或者基本数据类型时,子类的重写方法的返回值必须和父类方法完全一致。
当父类方法的返回值是引用类型时,子类的重写方法的返回值可以是父类的其他子类引用。
验证一下,父类Person有一个getAge方法,我们试着重写这个方法。
//这是父类Person的getAge方法 public int getAge(){ return age; }
//这是子类的getAge方法,返回值改了 public double getAge(){ return 1.0; }
报错了:
'getAge()' in 'com.company.dto.Student' clashes 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(); }
如下图,这是允许的。
你改成另一个子类引用也可以。
6.方法重写的方法重载的区别
方法重写要求子类方法的参数列表
必须与父类方法的参数列表完全相同,返回值类型
也必须完全相同(返回值如果是引用类型则向下兼容,参考上一节);
而方法重载仅要求子类方法的参数个数
或类型
不同,而返回值类型可以相同也可以不同。
7.属性可以重写吗
答案是可以的,子类可以重复定义父类的同名属性,到时候使用的就是子类的属性啦。
8. 访问修饰符
Java的权限访问修饰符是用来控制类、接口、变量、方法的访问权限的,它有四种:public、protected、default和private。
8.1 private
private代表私有的,只能在本类中访问,其他任何地方都不能访问,包括子类。
证明:
这个name是private修饰的:
然后在子类中:
连子类中都无法访问,其他地方就都不必说了吧。哪怕你在其他地方把Person类new一个对象除了,都无法直接访问里面的private属性,对象也不行哦。
所以,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; }
那么子类不管你有没有写无参构造都会报错:
错误信息: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类