JAVA基础再回首(六)——父与子的继承、super关键字、方法重写、方法重载
不墨迹,才是时间管理的最大利器,学无止境。
继承
首先我们来学习继承的概述
- 多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。
- 通过extends关键字可以实现类与类的继承
- 格式:class 子类名 extends 父类名 {}
- 单独的这个类称为父类,基类或者超类;这多个类可以称为子类或者派生类。
- 有了继承以后,我们定义一个类的时候,可以在一个已经存在的类的基础上,还可以定义自己的新成员。
以前我们没有学习继承之前,是这样定义一个类和类中的方法的。
//使用继承前
class Student {
public void eat() {
System.out.println("吃饭");
}
public void sleep() {
System.out.println("睡觉");
}
}
class Teacher {
public void eat() {
System.out.println("吃饭");
}
public void sleep() {
System.out.println("睡觉");
}
}
通过观察我们可以学生类和老师类他们都有共同的方法就是吃和睡,既然老司机已经带我们上路了,那么我们就可以改进上面的代码,将相同的成员抽取到一个类中,争取早日成为一名老司机。
//使用继承后
//首先我们定义一个人类,因为老师、学生都属于人,都要吃饭和睡觉
class Person {
public void eat() {
System.out.println("吃饭");
}
public void sleep() {
System.out.println("睡觉");
}
}
class Student extends Person {}
class Teacher extends Person {}
public class ExtendsDemo {
public static void main(String[] args) {
Student s = new Student();
s.eat();
s.sleep();
System.out.println("-------------");
Teacher t = new Teacher();
t.eat();
t.sleep();
}
}
作为小司机可以看到使用继承前后的输出结果相同
运用了继承后,我们可以看到继承的好处也就是优点。
- 提高了代码的复用性
- 多个类相同的成员可以放到同一个类中
- 提高了代码的维护性
- 如果功能的代码需要修改,修改一处即可
- 让类与类之间产生了关系,是多态的前提
当然运用了继承后,他也是存在弊端的
- 类的耦合性很强
设计原则: 高内聚低耦合。
简单的理解:
内聚就是自己完成某件事情的能力。
耦合就是类与类之间的关系。
我们在设计的时候原则是:自己能完成的就不麻烦别人,这样将来别人产生了修改,就对我的影响较小。
由此可见:在开发中使用继承其实是在使用一把双刃剑。今天我们还是以继承的好处来使用,因为继承还有很多其他的特性。
我们了解了继承的概述,继续了解Java中继承的特点
- Java只支持单继承,不支持多继承。
- 一个类只能有一个父类,不可以有多个父类。
- class SubDemo extends Demo{} //ok
- class SubDemo extends Demo1,Demo2…//error
- Java支持多层继承(继承体系)
- class A{}
- class B extends A{}
- class C extends B{}
我们一起写个小Demo验证继承的特点!
//Java只支持单继承,不支持多继承。
class Father {}
class Mother {}
class Son exnteds Father {} //正确的
class Son extends Father,Mother {} // 错误的
//Java支持多层继承
class GrandFather {
public void show() {
System.out.println("我是爷爷");
}
}
class Father extends GrandFather {
public void method(){
System.out.println("我是老子");
}
}
class Son extends Father {}
class ExtendsDemo2 {
public static void main(String[] args) {
Son s = new Son();
s.method(); //使用父亲的
s.show(); //使用爷爷的
}
}
那么,在Java中继承的注意事项有哪些呢?
- 子类只能继承父类所有非私有的成员(成员方法和成员变量)
- 其实这也体现了继承的另一个弊端:打破了封装性
- 子类不能继承父类的构造方法,但是可以通过super关键字去访问父类构造方法。
- 不要为了部分功能而去继承
- 我们到底在什么时候使用继承呢?
- 继承中类之间体现的是:”is a”的关系。
- 举例:水果和苹果,苹果是一种水果。
- 学生和人,学生是人的一种。
- 所以我们总结采用假设法。如果有两个类A,B。只有他们符合A是B的一种,或者B是A的一种,就可以考虑使用继承。
class Father {
private int num = 10;
public int num2 = 20;
//私有方法,子类不能继承
private void method() {
System.out.println(num);
System.out.println(num2);
}
public void show() {
System.out.println(num);
System.out.println(num2);
}
}
class Son extends Father {
public void function() {
//num可以在Father中访问private
//System.out.println(num); //子类不能继承父类的私有成员变量
System.out.println(num2);
}
}
public class ExtendsDemo {
public static void main(String[] args) {
// 创建对象
Son s = new Son();
//s.method(); //子类不能继承父类的私有成员方法
s.show();
s.function();
}
}
输出结果:
10
20
20
继承中成员变量的关系
- A:子类中的成员变量和父类中的成员变量名称不一样,这个太简单。
- B:子类中的成员变量和父类中的成员变量名称一样,这个怎么玩呢?
- 结论在子类方法中访问一个变量的查找顺序:
- a:在子类方法的局部范围找,有就使用
- b:在子类的成员范围找,有就使用
- c:在父类的成员范围找,有就使用
- d:如果还找不到,就报错。
- 结论在子类方法中访问一个变量的查找顺序:
class Father {
public int num = 10;
public void method() {
int num = 50;
}
}
class Son extends Father {
public int num2 = 20;
public int num = 30;
public void show() {
int num = 40;
System.out.println(num);
System.out.println(num2);
// 找不到符号,报错
//System.out.println(num3);
}
}
public class ExtendsDemo {
public static void main(String[] args) {
// 创建对象
Son s = new Son();
s.show();
}
}
输出结果:
![]()
记住上面的结论,到哪里都不会出错的
下面来学习super关键字
super关键字
- super的用法和this很像
- this代表本类对应的引用。
- super代表父类存储空间的标识(可以理解为父类引用,可以操作父类的成员)
- 用法(this和super均可如下使用)
- 访问成员变量
- this.成员变量 调用本类的成员变量
- super.成员变量 调用父类的成员变量
- 访问成员变量
- 访问构造方法
- this(…) 调用本类的构造方法
- super(…) 调用父类的构造方法
- 访问成员方法
- this.成员方法() 调用本类的成员方法
- super.成员方法() 调用父类的成员方法
class Father {
public int num = 10;
}
class Son extends Father {
public int num = 20;
public void show() {
int num = 30;
System.out.println(num);
System.out.println(this.num);
System.out.println(super.num);
}
}
public class ExtendsDemo{
public static void main(String[] args) {
Son s = new Son();
s.show();
}
}
输出结果:
继承中构造方法的关系
- 子类中所有的构造方法默认都会访问父类中空参数的构造方法
- 因为子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化。
- 注意:子类每一个构造方法的第一条语句默认都是:super();
class Father {
int age;
public Father() {
System.out.println("Father的无参构造方法");
}
public Father(String name) {
System.out.println("Father的带参构造方法");
}
}
class Son extends Father {
public Son() {
//super();
System.out.println("Son的无参构造方法");
}
public Son(String name) {
//super();
System.out.println("Son的带参构造方法");
}
}
public class ExtendsDemo {
public static void main(String[] args) {
//创建对象
Son s = new Son();
System.out.println("------------");
Son s2 = new Son("宝宝");
}
}
输出结果:
如果父类没有无参构造方法,那么子类的构造方法会出现什么现象呢?我注释了上面例子中父类的无参构造,然后项目报错了,那么我们怎么解决呢?
- 在父类中加一个无参构造方法
- 通过使用super关键字去显示的调用父类的带参构造方法
- 子类通过this去调用本类的其他构造方法
- (子类中一定要有一个去访问了父类的构造方法,否则父类数据就没有初始化。)
- 注意事项:
- this(…)或者super(…)必须出现在第一条语句上。
- 如果不是放在第一条语句上,就可能对父类的数据进行了多次初始化,所以必须放在第一条语句上。
上面说了这么多,不知道你是否理解了,我们通过Demo再来学习
class Father {
int age;
/*public Father() {
System.out.println("Father的无参构造方法");
}*/
public Father(String name) {
System.out.println("Father的带参构造方法");
}
}
class Son extends Father {
public Son() {
super("111");
System.out.println("Son的无参构造方法");
// super("111");
}
public Son(String name) {
// super("222");
this();
System.out.println("Son的带参构造方法");
}
}
public class ExtendsDemo {
public static void main(String[] args) {
//创建对象
Son s = new Son();
System.out.println("------------");
Son s2 = new Son("宝宝");
}
}
输出结果:不知道你通过上面的概念是否能观察出结果呢,不能的话就要去多想多练多理解
继承中成员方法的关系:
- A:子类中的方法和父类中的方法声明不一样,这个太简单。
- B:子类中的方法和父类中的方法声明一样,这个该怎么玩呢?
- 通过子类对象调用方法:
- a:先找子类中,看有没有这个方法,有就使用,没有就在父类中找
- b:再看父类中,有没有这个方法,有就使用
- c:如果没有就报错。
- 通过子类对象调用方法:
方法重写、方法重载
- 方法重写概述
- 子类中出现了和父类中一模一样的方法声明,也被称为方法覆盖,方法复写。
- 方法重载概述
- 本类中出现的方法名一样,参数列表不同的方法。与返回值无关。
- 使用特点:
- 如果方法名不同,就调用对应的方法
- 如果方法名相同,最终使用的是子类自己的
- 方法重写的应用:
- 当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容。
class Phone {
public void call(String name) {
System.out.println("给"+name+"打电话");
}
}
class NewPhone extends Phone {
public void call(String name) {
super.call(name);
System.out.println("打完电话了,我要听首歌");
}
}
public class ExtendsDemo {
public static void main(String[] args) {
NewPhone np = new NewPhone();
np.call("老板");
}
}
输出结果:
![]()
上面的Demo中,手机打电话功能,是手机本身就具备的最基本的功能。所以,新手机是不用在提供这个功能的。由于它继承了手机类,所以,我们就直接使用父类的功能即可。通过super关键字调用
- 方法重写的注意事项
- A:父类中私有方法不能被重写(因为父类私有方法子类根本就无法继承)
- B:子类重写父类方法时(访问权限不能更低,最好就一致)
- C:父类静态方法,子类也必须通过静态方法进行重写
- 子类重写父类方法的时候,最好声明一模一样。
我们一起来做两个经典例题
方法重写(Override)和方法重载(Overload)的区别?方法重载能改变返回值类型吗?
方法重写:在子类中,出现和父类中一模一样的方法声明的现象。
方法重载:同一个类中,出现的方法名相同,参数列表不同的现象。方法重载能改变返回值类型,因为它和返回值类型无关。
this关键字和super关键字分别代表什么?以及他们各自的使用场景和作用。
this:代表当前类的对象引用
super:代表父类存储空间的标识。(可以理解为父类的引用,通过这个东西可以访问父类的成员)
场景:
成员变量:this.成员变量——super.成员变量
构造方法:this(…)——super(…)
成员方法:this.成员方法——super.成员方法
好了,今天我们学了这么多,要好好消化一下,做到学以致用。最后送大家一句话。
不管有多苦,千万要记住:苦难是我们人生路上不可缺少的经历,只有活着,才有幸福的可能!