1 概述
1.1 引入
如果咱们要定义如下类:
学生类, 老师类和工人类,剖析如下。
- 学生类
属性: 姓名, 年龄
行为: 吃饭, 睡觉 - 老师类
属性: 姓名, 年龄,薪水
行为: 吃饭, 睡觉,教书 - 班主任
属性: 姓名, 年龄,薪水
行为: 吃饭, 睡觉,治理
如果咱们定义了这三个类去开发一个零碎,那么这三个类中就存在大量反复的信息(属性: 姓名,年龄。行为:吃饭,睡觉)。这样就导致了雷同代码大量反复,代码显得很臃肿和冗余,那么如何解决呢?
如果多个类中存在雷同属性和行为时,咱们能够将这些内容抽取到独自一个类中,那么多个类无需再定义这些属性和行为,只有 继承 那一个类即可。如图所示:
其中,多个类能够称为 子类 ,独自被继承的那一个类称为 父类 、 超类(superclass)或者 基类。
1.2 继承的含意
继承形容的是事物之间的所属关系,这种关系是:is-a
的关系。例如,兔子属于食草动物,食草动物属于动物。可见,父类更通用,子类更具体。咱们通过继承,能够使多种事物之间造成一种关系体系。
继承 :就是子类继承父类的 属性 和行为 ,使得子类对象能够间接具备与父类雷同的属性、雷同的行为。子类能够间接拜访父类中的 非公有 的属性和行为。
1.3 继承的益处
- 进步 代码的复用性(缩小代码冗余,雷同代码反复利用)。
- 使类与类之间产生了关系。
2 继承的格局
通过 extends
关键字,能够申明一个子类继承另外一个父类,定义格局如下:
class 父类 {...}
class 子类 extends 父类 {...}
须要留神:Java 是单继承的,一个类只能继承一个间接父类,跟事实世界很像,然而 Java 中的子类是更加弱小的。
3 继承案例
3.1 案例
请应用继承定义以下类:
- 学生类
属性: 姓名, 年龄
行为: 吃饭, 睡觉 - 老师类
属性: 姓名, 年龄,薪水
行为: 吃饭, 睡觉,教书 - 班主任
属性: 姓名, 年龄,薪水
行为: 吃饭, 睡觉,治理
3.2 案例图解剖析
老师类,学生类,还有班主任类,实际上都是属于人类的,咱们能够定义一个人类,把他们雷同的属性和行为都定义在人类中,而后继承人类即可,子类特有的属性和行为就定义在子类中了。
如下图所示。
3.3 案例代码实现
1. 父类 Human 类
public class Human {
// 正当暗藏
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;}
}
2. 子类 Teacher 类
public class Teacher extends Human {
// 工资
private double salary ;
// 特有办法
public void teach(){System.out.println("老师在认真教技术!");}
public double getSalary() {return salary;}
public void setSalary(double salary) {this.salary = salary;}
}
3. 子类 Student 类
public class Student extends Human{}
4. 子类 BanZhuren 类
public class Teacher extends Human {
// 工资
private double salary ;
// 特有办法
public void admin(){System.out.println("班主任强调纪律问题!");}
public double getSalary() {return salary;}
public void setSalary(double salary) {this.salary = salary;}
}
5. 测试类
public class Test {public static void main(String[] args) {Teacher dlei = new Teacher();
dlei.setName("播仔");
dlei.setAge("31");
dlei.setSalary(1000.99);
System.out.println(dlei.getName());
System.out.println(dlei.getAge());
System.out.println(dlei.getSalary());
dlei.teach();
BanZhuRen linTao = new BanZhuRen();
linTao.setName("灵涛");
linTao.setAge("28");
linTao.setSalary(1000.99);
System.out.println(linTao.getName());
System.out.println(linTao.getAge());
System.out.println(linTao.getSalary());
linTao.admin();
Student xugan = new Student();
xugan.setName("播仔");
xugan.setAge("31");
//xugan.setSalary(1000.99); // xugan 没有薪水属性,报错!System.out.println(xugan.getName());
System.out.println(xugan.getAge());
}
}
3.4 小结
1. 继承实际上是子类雷同的属性和行为能够定义在父类中,子类特有的属性和行为由本人定义,这样就实现了雷同属性和行为的反复利用,从而进步了代码复用。
2. 子类继承父类,就能够间接失去父类的成员变量和办法。是否能够继承所有成分呢?请看下节!
4 子类不能继承的内容
4.1 引入
并不是父类的所有内容都能够给子类继承的:
子类不能继承父类的构造方法。
值得注意的是子类能够继承父类的公有成员(成员变量,办法),只是子类无奈间接拜访而已,能够通过 getter/setter 办法拜访父类的 private 成员变量。
4.1 演示代码
public class Demo03 {public static void main(String[] args) {Zi z = new Zi();
System.out.println(z.num1);
// System.out.println(z.num2); // 公有的子类无奈应用
// 通过 getter/setter 办法拜访父类的 private 成员变量
System.out.println(z.getNum2());
z.show1();
// z.show2(); // 公有的子类无奈应用}
}
class Fu {
public int num1 = 10;
private int num2 = 20;
public void show1() {System.out.println("show1");
}
private void show2() {System.out.println("show2");
}
public int getNum2() {return num2;}
public void setNum2(int num2) {this.num2 = num2;}
}
class Zi extends Fu {}
5 继承后的特点—成员变量
当类之间产生了继承关系后,其中各类中的成员变量,又产生了哪些影响呢?
5.1 成员变量不重名
如果子类父类中呈现 不重名 的成员变量,这时的拜访是 没有影响的。代码如下:
class Fu {
// Fu 中的成员变量
int num = 5;
}
class Zi extends Fu {
// Zi 中的成员变量
int num2 = 6;
// Zi 中的成员办法
public void show() {
// 拜访父类中的 num
System.out.println("Fu num="+num); // 继承而来,所以间接拜访。// 拜访子类中的 num2
System.out.println("Zi num2="+num2);
}
}
class Demo04 {public static void main(String[] args) {
// 创立子类对象
Zi z = new Zi();
// 调用子类中的 show 办法
z.show();}
}
演示后果:Fu num = 5
Zi num2 = 6
5.2 成员变量重名
如果子类父类中呈现 重名 的成员变量,这时的拜访是 有影响的。代码如下:
class Fu1 {
// Fu 中的成员变量。int num = 5;
}
class Zi1 extends Fu1 {
// Zi 中的成员变量
int num = 6;
public void show() {
// 拜访父类中的 num
System.out.println("Fu num=" + num);
// 拜访子类中的 num
System.out.println("Zi num=" + num);
}
}
class Demo04 {public static void main(String[] args) {
// 创立子类对象
Zi1 z = new Zi1();
// 调用子类中的 show 办法
z1.show();}
}
演示后果:Fu num = 6
Zi num = 6
子父类中呈现了同名的成员变量时,子类会优先拜访本人对象中的成员变量。如果此时想拜访父类成员变量如何解决呢?咱们能够应用 super 关键字。
5.3 super 拜访父类成员变量
子父类中呈现了同名的成员变量时,在子类中须要拜访父类中非公有成员变量时,须要应用super
关键字,润饰父类成员变量,相似于之前学过的 this
。
须要留神的是:super 代表的是父类对象的援用,this 代表的是以后对象的援用。
应用格局:
super. 父类成员变量名
子类办法须要批改,代码如下:
class Fu {
// Fu 中的成员变量。int num = 5;
}
class Zi extends Fu {
// Zi 中的成员变量
int num = 6;
public void show() {
int num = 1;
// 拜访办法中的 num
System.out.println("method num=" + num);
// 拜访子类中的 num
System.out.println("Zi num=" + this.num);
// 拜访父类中的 num
System.out.println("Fu num=" + super.num);
}
}
class Demo04 {public static void main(String[] args) {
// 创立子类对象
Zi1 z = new Zi1();
// 调用子类中的 show 办法
z1.show();}
}
演示后果:method num=1
Zi num=6
Fu num=5
小贴士:Fu 类中的成员变量是非公有的,子类中能够间接拜访。若 Fu 类中的成员变量公有了,子类是不能间接拜访的。通常编码时,咱们遵循封装的准则,应用 private 润饰成员变量,那么如何拜访父类的公有成员变量呢?对!能够在父类中提供公共的 getXxx 办法和 setXxx 办法。
6 继承后的特点—成员办法
当类之间产生了关系,其中各类中的成员办法,又产生了哪些影响呢?
6.1 成员办法不重名
如果子类父类中呈现 不重名 的成员办法,这时的调用是 没有影响的。对象调用办法时,会先在子类中查找有没有对应的办法,若子类中存在就会执行子类中的办法,若子类中不存在就会执行父类中相应的办法。代码如下:
class Fu {public void show() {System.out.println("Fu 类中的 show 办法执行");
}
}
class Zi extends Fu {public void show2() {System.out.println("Zi 类中的 show2 办法执行");
}
}
public class Demo05 {public static void main(String[] args) {Zi z = new Zi();
// 子类中没有 show 办法,然而能够找到父类办法去执行
z.show();
z.show2();}
}
6.2 成员办法重名
如果子类父类中呈现 重名 的成员办法,则创立子类对象调用该办法的时候,子类对象会优先调用本人的办法。
代码如下:
class Fu {public void show() {System.out.println("Fu show");
}
}
class Zi extends Fu {
// 子类重写了父类的 show 办法
public void show() {System.out.println("Zi show");
}
}
public class ExtendsDemo05{public static void main(String[] args) {Zi z = new Zi();
// 子类中有 show 办法,只执行重写后的 show 办法
z.show(); // Zi show}
}
7 办法重写
7.1 概念
办法重写 :子类中呈现与父类截然不同的办法时(返回值类型,办法名和参数列表都雷同),会呈现笼罩成果,也称为重写或者复写。 申明不变,从新实现。
7.2 应用场景与案例
产生在子父类之间的关系。
子类继承了父类的办法,然而子类感觉父类的这办法不足以满足本人的需要,子类从新写了一个与父类同名的办法,以便笼罩父类的该方 法。
例如:咱们定义了一个动物类代码如下:
public class Animal {public void run(){System.out.println("动物跑的很快!");
}
public void cry(){System.out.println("动物都能够叫~~~");
}
}
而后定义一个猫类,猫可能认为父类 cry()办法不能满足本人的需要
代码如下:
public class Cat extends Animal {public void cry(){System.out.println("咱们一起学猫叫,喵喵喵!喵的十分好听!");
}
}
public class Test {public static void main(String[] args) {
// 创立子类对象
Cat ddm = new Cat();// 调用父类继承而来的办法
ddm.run();
// 调用子类重写的办法
ddm.cry();}
}
7.2 @Override 重写注解
- @Override: 注解,重写注解校验!
- 这个注解标记的办法,就阐明这个办法必须是重写父类的办法,否则编译阶段报错。
-
倡议重写都加上这个注解,一方面能够进步代码的可读性,一方面能够避免重写出错!
加上后的子类代码模式如下:
public class Cat extends Animal { // 申明不变,从新实现 // 办法名称与父类全副一样,只是办法体中的性能重写写了!@Override public void cry(){System.out.println("咱们一起学猫叫,喵喵喵!喵的十分好听!"); } }
7.3 注意事项
- 办法重写是产生在子父类之间的关系。
- 子类办法笼罩父类办法,必须要保障权限大于等于父类权限。
- 子类办法笼罩父类办法,返回值类型、函数名和参数列表都要截然不同。
8 继承后的特点—构造方法
8.1 引入
当类之间产生了关系,其中各类中的构造方法,又产生了哪些影响呢?
首先咱们要回顾两个事件,构造方法的定义格局和作用。
- 构造方法的名字是与类名统一的。所以子类是无奈继承父类构造方法的。
- 构造方法的作用是初始化对象成员变量数据的。所以子类的初始化过程中,必须先执行父类的初始化动作。子类的构造方法中默认有一个
super()
,示意调用父类的构造方法,父类成员变量初始化后,才能够给子类应用。( 先有爸爸,能力有儿子)
继承后子类构办法器特点: 子类所有构造方法的第一行都会默认先调用父类的无参构造方法
8.2 案例演示
按如下需要定义类:
- 人类
成员变量: 姓名, 年龄
成员办法: 吃饭 - 学生类
成员变量: 姓名, 年龄, 问题
成员办法: 吃饭
代码如下:
class Person {
private String name;
private int age;
public Person() {System.out.println("父类无参");
}
// getter/setter 省略
}
class Student extends Person {
private double score;
public Student() {//super(); // 调用父类无参, 默认就存在,能够不写,必须再第一行
System.out.println("子类无参");
}
public Student(double score) {//super(); // 调用父类无参, 默认就存在,能够不写,必须再第一行
this.score = score;
System.out.println("子类有参");
}
}
public class Demo07 {public static void main(String[] args) {Student s1 = new Student();
System.out.println("----------");
Student s2 = new Student(99.9);
}
}
输入后果:父类无参
子类无参
----------
父类无参
子类有参
8.3 小结
- 子类构造方法执行的时候,都会在第一行默认先调用父类无参数构造方法一次。
- 子类构造方法的第一行都隐含了一个 super() 去调用父类无参数构造方法,super()能够省略不写。
9 super(…)和 this(…)
9.1 引入
请看上节中的如下案例:
class Person {
private String name;
private int age;
public Person() {System.out.println("父类无参");
}
// getter/setter 省略
}
class Student extends Person {
private double score;
public Student() {//super(); // 调用父类无参构造方法, 默认就存在,能够不写,必须再第一行
System.out.println("子类无参");
}
public Student(double score) {//super(); // 调用父类无参构造方法, 默认就存在,能够不写,必须再第一行
this.score = score;
System.out.println("子类有参");
}
// getter/setter 省略
}
public class Demo07 {public static void main(String[] args) {
// 调用子类有参数构造方法
Student s2 = new Student(99.9);
System.out.println(s2.getScore()); // 99.9
System.out.println(s2.getName()); // 输入 null
System.out.println(s2.getAge()); // 输入 0
}
}
咱们发现,子类有参数构造方法只是初始化了本人对象中的成员变量 score,而父类中的成员变量 name 和 age 仍然是没有数据的,怎么解决这个问题呢,咱们能够借助与 super(…)去调用父类构造方法,以便初始化继承自父类对象的 name 和 age.
9.2 super 和 this 的用法格局
super 和 this 残缺的用法如下,其中 this,super 拜访成员咱们曾经接触过了。
this. 成员变量 -- 本类的
super. 成员变量 -- 父类的
this. 成员办法名() -- 本类的
super. 成员办法名() -- 父类的
接下来咱们应用调用构造方法格局:
super(...) -- 调用父类的构造方法,依据参数匹配确认
this(...) -- 调用本类的其余构造方法,依据参数匹配确认
9.3 super(….)用法演示
代码如下:
class Person {
private String name ="凤姐";
private int age = 20;
public Person() {System.out.println("父类无参");
}
public Person(String name , int age){
this.name = name ;
this.age = age ;
}
// getter/setter 省略
}
class Student extends Person {
private double score = 100;
public Student() {//super(); // 调用父类无参构造方法, 默认就存在,能够不写,必须再第一行
System.out.println("子类无参");
}
public Student(String name,int age,double score) {super(name ,age);// 调用父类有参构造方法 Person(String name , int age)初始化 name 和 age
this.score = score;
System.out.println("子类有参");
}
// getter/setter 省略
}
public class Demo07 {public static void main(String[] args) {
// 调用子类有参数构造方法
Student s2 = new Student("张三",20,99);
System.out.println(s2.getScore()); // 99
System.out.println(s2.getName()); // 输入 张三
System.out.println(s2.getAge()); // 输入 20
}
}
留神:
子类的每个构造方法中均有默认的 super(),调用父类的空参结构。手动调用父类结构会笼罩默认的 super()。
super() 和 this() 都必须是在构造方法的第一行,所以不能同时呈现。
super(..)是依据参数去确定调用父类哪个构造方法的。
9.4 super(…)案例图解
父类空间优先于子类对象产生
在每次创立子类对象时,先初始化父类空间,再创立其子类对象自身。目标在于子类对象中蕴含了其对应的父类空间,便能够蕴含其父类的成员,如果父类成员非 private 润饰,则子类能够随便应用父类成员。代码体现在子类的结构七调用时,肯定先调用父类的构造方法。了解图解如下:
9.5 this(…)用法演示
this(…)
- 默认是去找本类中的其余构造方法,依据参数来确定具体调用哪一个构造方法。
- 为了借用其余构造方法的性能。
package com.itheima._08this 和 super 调用构造方法;
/**
* this(...):
* 默认是去找本类中的其余构造方法,依据参数来确定具体调用哪一个构造方法。* 为了借用其余构造方法的性能。*
*/
public class ThisDemo01 {public static void main(String[] args) {Student xuGan = new Student();
System.out.println(xuGan.getName()); // 输入: 徐干
System.out.println(xuGan.getAge());// 输入:21
System.out.println(xuGan.getSex());// 输入:男
}
}
class Student{
private String name ;
private int age ;
private char sex ;
public Student() {// 很弱,我的兄弟很牛逼啊,我能够调用其余构造方法:Student(String name, int age, char sex)
this("徐干",21,'男');
}
public Student(String name, int age, char sex) {
this.name = name ;
this.age = age ;
this.sex = sex ;
}
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 char getSex() {return sex;}
public void setSex(char sex) {this.sex = sex;}
}
9.6 小结
- 子类的每个构造方法中均有默认的 super(),调用父类的空参结构。手动调用父类结构会笼罩默认的 super()。
- super() 和 this() 都必须是在构造方法的第一行,所以不能同时呈现。
- super(..)和 this(…)是依据参数去确定调用父类哪个构造方法的。
- super(..)能够调用父类构造方法初始化继承自父类的成员变量的数据。
- this(..)能够调用本类中的其余构造方法。
10 继承的特点
-
Java 只反对单继承,不反对多继承。
// 一个类只能有一个父类,不能够有多个父类。class A {} class B {} class C1 extends A {} // ok // class C2 extends A, B {} // error
-
一个类能够有多个子类。
// A 能够有多个子类 class A {} class C1 extends A {} class C2 extends A {}
-
能够多层继承。
class A {} class C1 extends A {} class D extends C1 {}
顶层父类是 Object 类。所有的类默认继承 Object,作为父类。