第六章_Java中面向对象的三大特征之继承
1.概念
继承是面向对象三大特征之一,可以使得子类具有父类的属性和方法,还可以在子类中重新定义,以及追加属性和方法
在java中一个类可以通过extends关键字继承另一个类
通过继承可以得到被继承类的所有非私有的成员
2.继承的格式
class 子类 extends 父类 { }
举例:class Dog extends Animal { }
3.继承带来的好处
继承可以让类与类之间产生关系,子父类关系,产生子父类后,子类则可以使用父类中非私有的成员。
代码演示:
1.先定义一个people类
2.再定义一个student类继承people类
3.测试
public class People {
private String name;
private String id;
public People() {
}
public People(String name, String id) {
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
public class Student extends People {
}
public class Test {
public static void main(String[] args) {
Student student = new Student();
student.setName("张三");
student.setId("A001");
System.out.println(student.getName()+"..."+student.getId());
}
}
执行效果
4.继承的应用场景
1.当很多类都有共性的内容,我们可以将共性的内容抽取出来
单独形成一个类(父类),其他的类直接继承这个父类,就可以拥有父类中的成员(非私有)
2.使用继承,需要考虑类与类之间是否存在is…a的关系,不能盲目使用继承
is…a的关系:谁是谁的一种
例如:老师和学生是人的一种,那人就是父类,学生和老师就是子类
5.继承的优点和缺点
优点
1.通过继承可以把多个类共有的代码提取到父类
2.提高了代码的复用性
修改的时候只修改父类就可以了
3.提高了代码的可维护性
为多态提供了前提条件
缺点
类与类之间的关系变得紧密了,代码的耦合度变高了
6.继承的特点
1.继承不能多继承,支持单继承(不能有两个亲爹)
2.继承可以多层继承(一脉单传)
class Son extends Father{}
Father extends GrandFather{}
3.一个类可以有多个子类
4.如果一个类没有明确的写出 extends 父类,那么这个类也是有父类的(Object)
7.super关键字
子类在继承父类之后可能还会定义与父类名字相同的变量或方法,此时就需要使用super和this关键字来区分变量或方法到底是属于子类,还是属于父类
- this&super关键字:
- this:代表本类对象的引用
- super:代表父类存储空间的标识(可以理解为父类对象引用)
- this和super的使用分别
- 成员变量:
- this.成员变量 - 访问本类成员变量
- super.成员变量 - 访问父类成员变量
- 成员方法:
- this.成员方法 - 访问本类成员方法
- super.成员方法 - 访问父类成员方法
- 成员变量:
- 构造方法:
- this(…) - 访问本类构造方法
- super(…) - 访问父类构造方法
super内存图
对象在堆内存中,会单独存在一块super区域,用来存放父类的数据
类中的属性,成员变量,跟随对象进入到堆内存
父类中的属性,跟随子类对象进入到堆内存
8.继承中成员变量的访问特点
1.不重名
如果new的父类对象,只能调用父类的
如果new的是子类对象,既能调用子类自己的,还能调用从父类那边继承过来的(非私有)
代码演示:
public class Test {
public static void main(String[] args) {
//创建父类对象只能调用父类中的成员变量
Fu fu = new Fu();
System.out.println("父类调用本类的成员变量a:"+fu.a);//10
System.out.println("------------------------");
//创建子类对象,既能调自己的成员变量,也可以调父类的
Zi zi = new Zi();
System.out.println("子类调用父类的成员变量a:"+zi.a);//10
System.out.println("子类调用本类的成员变量b:"+zi.b);//20
}
}
class Fu{
int a = 10;
}
class Zi extends Fu{
int b = 20;
}
执行效果:
2.重名
看等号左边是谁,先调用谁的成员,子类没有,找父类
代码演示:
public class Test {
public static void main(String[] args) {
//创建父类对象只能调用父类中的成员变量
Fu fu = new Fu();
System.out.println("父类调用本类的成员变量num:"+fu.num);
System.out.println("------------------------");
//创建子类对象,既能调自己的成员变量,也可以调父类的
Zi zi = new Zi();
System.out.println("子类调用本类的成员变量num:"+zi.num);
}
}
class Fu{
int num = 100;
}
class Zi extends Fu{
int num = 200;
}
执行效果:
3.区分子类和父类重名变量
this.成员变量:代表的是自己的成员变量
super.成员变量:代表的是父类中的成员变量
代码演示:
public class Test {
public static void main(String[] args) {
//创建子类对象,既能调自己的成员变量,也可以调父类的
Zi zi = new Zi();
zi.show();
}
}
class Fu{
int num = 100;
}
class Zi extends Fu{
int num = 200;
public void show() {
System.out.println("父类中的成员变量num:"+super.num);
System.out.println("子类中的成员变量num:"+this.num);
}
}
执行效果:
总结
在子类方法中访问一个变量,采用的是就近原则。
- 子类局部范围找
- 子类成员范围找
- 父类成员范围找
- 如果都没有就报错(不考虑父亲的父亲…)
9.继承中成员方法的访问特点
1.不重名
如果new的父类对象,只能调用父类的
如果new的是子类对象,既能调用子类自己的,还能调用从父类那边继承过来的(非私有)
2.重名
看new的是谁,先调用谁中的方法,子类没有找父类–>子债父偿
代码演示:
public class Test {
public static void main(String[] args) {
//创建子类对象,既能调自己的成员变量,也可以调父类的
Zi zi = new Zi();
//子类调用自己的show方法
zi.method();
//子类调用父类的show方法
zi.show();
}
}
class Fu{
public void show(){
System.out.println("父类中的show方法");
}
}
class Zi extends Fu{
public void method(){
System.out.println("子类中的method方法");
}
}
执行效果
总结
通过子类对象访问一个方法
- 子类成员范围找
- 父类成员范围找
- 如果都没有就报错(不考虑父亲的父亲…)
10.方法重写
1.方法的重载(overload)
-->方法名相同,参数列表不同
2.方法的重写(override)
-->子类中有一个和父类一样的方法
子类出现了和父类中一模一样的方法声明(方法名一样,参数列表也必须一样)
3.方法重写的应用场景
子类想要对父类中的方法进行功能的增强
当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容
4.Override注解
用来检测当前的方法,是否是重写的方法,起到【校验】的作用
前提:必须有子父类继承关系
代码演示:
public class Test {
public static void main(String[] args) {
//创建子类对象,既能调自己的成员变量,也可以调父类的
Zi zi = new Zi();
//子类调用自己的show方法
zi.method();
//子类调用增强后的show方法
zi.show();
}
}
class Fu{
public void show(){
System.out.println("父类中的show方法");
}
//默认权限defult
private void eat(){
System.out.println("吃饭");
}
}
class Zi extends Fu{
/*@Override 子类权限需要大于或等于父类权限
void show() {
super.show();
System.out.println("子类对show方法进行增强");
}*/
@Override
public void show() {
super.show();
System.out.println("子类对show方法进行增强");
}
//私有方法不能被重写
/* @Override
void eat() {
super.eat();
}*/
public void method(){
System.out.println("子类中的method方法");
}
}
执行效果
注意事项
a. 子类方法覆盖父类方法,必须要保证权限大于等于父类权限。(public->protected->默认->private)
b. 子类方法覆盖父类方法,返回值类型、函数名和参数列表都要一模一样。
c. 私有方法不能被重写(父类私有成员子类是不能继承的)
总结
- 私有方法不能被重写(父类私有成员子类是不能继承的)
- 子类方法访问权限不能更低(public > protected > 默认 > 私有)
- 静态方法不能被重写,如果子类也有相同的方法,并不是重写的父类的方法
11.权限修饰符
不同的权限修饰符修饰的成员,访问的权限
修饰符 | 同一个类中 | 同一个包中,子类无关类 | 不同包的子类 | 不同包的无关类 |
---|---|---|---|---|
private | √ | |||
默认 | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
12.抽象类
1.概述
当我们在做子类共性功能抽取时,有些方法在父类中并没有具体的体现,这个时候就需要抽象类了!
在Java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类!
例如:动物都要吃饭,但是有的吃肉,有的吃草,此时在父类中就不能明确的体现出来,我们就需要把动物吃饭的方法定义为抽象,而具体的方法体则需要让子类来实现
2.格式
2.1抽象类的定义
抽象类中的方法不一定是抽象方法,但是有抽象方法的类一定是一个抽象类。
public abstract class 类名 {}
2.2抽象方法的定义
抽象方法没有方法体,需要由子类实现。抽象方法所在的类一定是抽象类
public abstract void eat();
3.使用
a.定义抽象类:
修饰符 abstract class 类名{}
b.定义抽象方法
修饰符 abstract 返回值类型 方法名(参数);
c.创建子类对象 extends 抽象父类,重写抽象方法
d.创建子类对象(抽象父类不能new对象),调用子类重写的方法
代码演示
1.定义动物抽象类
2.定义猫类和狗类继承动物类
3.测试猫类的吃饭方法
//定义一个动物抽象类,里面有一个吃饭的抽象方法
public abstract class Animal {
public abstract void eat();
}
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
public class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃肉");
}
}
public class Test {
public static void main(String[] args) {
Cat cat = new Cat();
cat.eat();
System.out.println("-------------");
Dog dog = new Dog();
dog.eat();
}
}
执行效果
总结
1.抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。
2.抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的(成员变量)。
3.抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
4.抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象类。