1、什么是继承?
继承就是让类与类之间产生关系(子父类关系),子类可以直接使用父类中的非私有成员。
注意:子类不能使用父类中的私有成员。父类中的私有成员如果提供了共有的set和get方法,我们也可以在子类中通过get和set方法访问这些成员变量。
格式:public class 子类名 extends 父类名{}
父类也称基类和超类
子类也叫派生类
2、继承的好处和弊端
好处:
- 提高了代码的复用性
有相同需求的类向上抽取一个父类,相同需求的类就可以直接复用这个父类中的方法或者使用它的成员方法,这大大提高了代码的复用性。 - 提高了代码的维护性
比如我需要给子类增加一个成员变量,使用继承之后只需要在父类中增加该变量即可,没有使用继承时需要在每个类里面进行添加。 - 让类与类之间产生了联系,是多态的前提
这个就先不说了,为什么说继承是多态的前提,后面会专门写一篇关于多态的文章。
弊端:
- 继承是具有侵入性的
- 降低了代码的灵活性
继承关系要求子类必须拥有父类中的非私有成员和方法(一定要有),让得子类自由的世界多了许多约束。 - 增强了代码的耦合性
代码与代码之间有了关联都会增加代码一定的耦合度。比如我在父类中删除了一个成员变量,那它的所有子类中使用了这个成员变量的地方都会报错。
什么时候使用继承?
当类与类之间存在相同的内容,并且产生 is a的关系,就可以考虑使用继承关系。
is a关系就是比如学生和老师,学生是人,老师也是人,就可以抽取一个父类“人”,再比如程序员和技术经理,程序员是员工,技术经理也是员工,也可以抽取一个父类“员工”。
3、继承的特点
- Java只支持单继承,不支持多继承,但是支持多层继承。
一个子类只能继承一个父类,一个父类只能有一个子类,但是可以A继承B,B继承C。
不支持多继承的原因是防止不同父类中的方法冲突。
4、继承中成员变量的访问特点
子类在访问父类成员变量时采取就近原则,这里通过一个小demo演示一下,先定义一个子类和一个父类,然后定义一个测试类去进行测试
Fu.Java
public class Fu {
int a=10;
}
Zi.java
public class Zi extends Fu{
int a=20;
public void method(){
System.out.println(a);
}
}
Test.java
public class Test {
public static void main(String[] args) {
Zi zi = new Zi();
zi.method();
}
}
结果:
image.png
在上面例子中父类中有一个非私有成员变量a=10,子类中也有一个非私有成员变量a=20,在子类访问a的时候就会优先访问自己的成员变量a,这就是访问成员变量的就近原则。
子类父类有变量重名,就会优先打印子类成员变量,但是如果我们非要打印父类的成员变量呢?
只需要将子类代码改成:
public class Zi extends Fu{
int a=20;
public void method(){
System.out.println(super.a);
}
}
需要使用父类同名成员变量可以使用super关键字。
image.png
注意访问构造方法的格式,直接是this或者super+(...)
值得注意的是,在继承中,成员方法的访问特点和成员变量的访问特点是一样的,也是才去就近原则,如要调用父类的成员方法请使用super关键字。
4、方法的重写
概念:在继承体系中,子类出现和父类中一模一样的方法声明,这就是子类对父类中的方法进行了重写。
方法重写的应用场景:
当子类需要父类的功能 ,而功能主体子类有自2特有内容,可以重写父类中的方法,这样,即沿袭了父类的
功能,又定义了子类特有的内容。
其实很简单了,就是在父类的基础上加一些新的功能,就是一个super关键字的使用,一个demo就能简单说明了。
父类代码:
public class Fu {
int a=10;
public void method( ){
System.out.println("这是父类的方法");
}
}
子类方法:
public class Zi extends Fu {
public void method() {
super.method();
System.out.println("在子类中增加了这句输出");
}
}
Test.java
public class Test {
public static void main(String[] args) {
Zi zi = new Zi();
zi.method();
}
}
测试结果:
image.png
可以看到我们调用子类的method()时,既实现了父类的输出也实现了子类的输出,这就是方法重写,原理主要是借助了super关键字在子类同名方法中调用了父类的同名方法。
下面是一些方法重写需要注意的事项:
- 父类中的私有成员方法不能被重写
- 父类中的静态方法只能通过静态方法重写,非静态方法只能通过非静态方法进行重写。
- 子类重写父类方法时,重写方法的访问权限必须大于等于父类成员方法的访问权限。
权限大小:private<默认<protected<public.
扩展知识点:权限修饰符的访问范围(如下图)
image.png
5、继承中构造方法的访问特点
子类中所有的构造方法默认都会访问父类中无参的构造方法.
验证:我们来用代码演示一下,我在父类中重写它的无参构造方法,子类中定义一个无参构造方法和有参构造方法,在test类中分别调用子类的无参和有参构造方法,看看是不是都会调用父类的无参构造。
父类:
public class Fu {
public Fu() {
System.out.println("这是父类的无参构造");
}
public Fu(int a){
System.out.println("这是父类的有参构造方法");
}
}
子类:
public class Zi extends Fu {
public Zi(){
System.out.println("这是子类的无参构造方法");
}
public Zi(int a){
System.out.println("这是子类的有参构造方法");
}
}
Test.java
public class Test {
public static void main(String[] args) {
Zi zi = new Zi();
Zi zi1 = new Zi(1);
}
}
输出结果:
image.png
为什么创建子类对象一定会先调用父类的初始化方法呢?
这是因为子 类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据。所以子类初始化之前,一定要先完成父类初始化。
那它是怎么初始化父类的?
其实在子类的构造方法的第一 条语句默认都是 : super(),前面也说过这是调用父类的构造方法,只是这句代码被默认隐藏掉了。所以如果父类没有空参构造方法,子类实现构造方法时会出错,需要在构造方法第一行加上super(参数)的方式来调用父类的有参构造方法。
注意:如果我们编写的类,没有手动指定父类,系统也会自动继承Object ( Java继承体系中的最顶层父类)
继承需要注意的大概就是这么多,其实很简单,大概明白个意思就可以了。
网友评论