继承
目标
-
能够理解继承的作用及优点
-
能够理解this和super的作用
-
能够理解权限修饰符的作用 public protected 默认 private
-
能够理解继承下构造方法的执行过程
-
能够理解子类重写父类的方法
两个类有了父和子的关系,就有了继承,子类会继承父类的成员变量和成员方法
基本语法:
public class 子类名字 extends 父类名字{
}
学生老师类抽取Person练习
老师代码
-
抽取了共同的属性name和age
-
共同的方法 show,show里面可以暂时不写内容
public class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person() {
}
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 void show() {
}
}
学生类和老师类代码
//extends 继承
public class Student extends Person {
String id;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student() {
}
}
public class Teacher extends Person {
double salary;
public Teacher(String name, int age) {
this.name = name;
this.age = age;
}
public Teacher() {
}
}
测试
public static void main(String[] args) {
Student stu = new Student();
Teacher t1 = new Teacher();
stu.name = "小明";
System.out.println(stu.getName());
stu.setAge(10);
System.out.println(stu.age);
stu.number = "001";
System.out.println(stu.number);
t1.setName("halon");
t1.setAge(18);
System.out.println(t1.getName() + "--" + t1.getAge());
t1.salary = 9999.99;
System.out.println(t1.salary);
}
继承的好处和坏处
-
好处:代码的复用性和维护性
-
坏处:增加了两个类的耦合性(关联性)
super关键字(父类 超类 基类 )
作用:
-
访问父类构造函数
-
访问父类成员变量
-
访问父类成员方法
访问父类构造的学习
构造函数无法继承,子类需要添加自己的空参和有参构造
在子类的有参构造里,使用super调用父类的有参构造,可以更方便的初始化成员变量
public class Sales extends Worker {
public Sales() {
System.out.println("执行了空参构造");
}
public Sales(String id, String name) {
//在子类的有参构造里,使用super调用父类的有参构造,可以更方便的初始化成员变量
super(id, name);
System.out.println("执行了有参构造");
}
子类的无参和有参构造都会默认调用父类的无参构造 也就是super()
-
Sales是Worker子类,测试类中空参和有参方式创建了对象
-
发现Sales里的空参有参构造都默认调用了父类Worker的里无参构造
this可以调用本类的构造
-
实际工作用,当需要使用空参构造初始化,却希望给成员变量默认值时,可以使用
-
如下代码,调用空参构造后,用this调用了自己的有参数构造,传入了默认值-1和未知
class Sales extends Worker {
public Sales(String id, String name) {
//调用父类的构造
super(id, name);
System.out.println("子类 有参");
}
public Sales() {
this("-1", "未知");
System.out.println("子类 无参");
}
public void work() {
System.out.println("销售产品");
System.out.println(this);
}
}
测试代码
Sales s1=new Sales();
Sales s2=new Sales();
Sales s3=new Sales();
System.out.println(s1.name);
System.out.println(s2.name);
System.out.println(s3.name); // 会打印 未知未知未知
如果父类没有空参构造
-
1 最简单的方法就是补上父类的空参构造
-
2 子类的有参构造调用父类的有参构造,子类的无参构造调用自己的有参构造
-
注意传入的值 是每种数据类型的初始化默认值。
-
public class Student extends Person {
public Student() {
this(null,0,null);
}
public Student(String name, int age, String number) {
super(name, age);
this.number = number;
}
String number;
}
构造方法总结
-
一般我们会在父类 和子类都生成自己需要的空参和有参构造
-
有参构造里通过super调用父类的有参构造来初始化成员变量,子类特有的成员变量自己调用赋值
-
如果希望空参构造创建对象有默认值,那么就在空参构造里通过this调用有参构造就可以,传入需要的默认值
-
父类没有空参构造:补上父类的空参构造或者子类的有参构造调用父类的有参构造,子类的无参构造调用自己的有参构造
-
-
注意:super 和this调用构造方法只能2选1,因为语法要求,都要在第一行
访问父类成员变量的学习,super可以调用父类的成员变量
show方法中 分别打印了局部变量age ,当前类的成员变量age,父类的成员变量age
public class Demo01 {
public static void main(String[] args) {
Zi zi = new Zi();
System.out.println(zi.age);//5
zi.show();
}
}
//父类
class Fu {
int age = 100;
}
//子类
class Zi extends Fu {
int age = 5;
public void show() {
int age = 200;
System.out.println(age); //200 调用局部变量
System.out.println(this.age); //5 调用本类 成员变量
System.out.println(super.age);//100 调用父类 成员变量
}
}
继承关系下代码的执行顺序
-
父类的成员变量初始化默认值 > 父类构造方法 > 子类成员变量初始化默认值 > 子类构造方法
public class Demo02 {
public static void main(String[] args) {
//父类的成员变量初始化默认值 --> 父类构造方法 --> 子类成员变量初始化默认值 --> 子类构造方法
Zi1 z = new Zi1();
}
}
//父类
class Fu1 {
int age = 100;
public Fu1() {
System.out.println("Fu");
System.out.println(this.age);
}
}
//子类
class Zi1 extends Fu1 {
double height = 200;
public Zi1() {
System.out.println("Zi");
System.out.println(this.height);
System.out.println(super.age);//100
}
}
super和this调用成员方法
-
eat();调用本类成员方法
-
super.eat(); 调用父类 成员方法
public class Demo01 {
public static void main(String[] args) {
Zi zi = new Zi();
zi.show();
}
}
//父类
class Fu {
int age = 100;
public void eat() {
System.out.println("吃火锅");
}
}
//子类
class Zi extends Fu {
int age = 5;
public void show() {
eat();
super.eat();
}
public void eat() {
System.out.println("吃烤鸭");
}
}
父类中不能被继承的成员:
-
1、构造方法
-
2、private私有成员
-
3、父子类不在同一个包,父类使用默认访问权限的成员不能被继承
权限修饰符 重点掌握private和public就可以
Java中类只支持单继承,不支持多继承,但支持多层继承,类如果没有语法上继承另一个类,那么默认继承Objec
方法重写
子类重写父类的方法
-
子类出现了和父类中一模一样的方法声明(方法名一样,参数列表也必须一样)
应用场景
-
当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了
父类的功能,又定义了子类特有的内容
代码练习
-
Tudi类继承了Shifu类,重写了skill方法
-
在子类方法中,可以使用super调用父类的skill
-
ctrl+o 重写的快捷键
-
Override注解 可以检查是不是重写,如果不是,就会报错误
class Shifu {
public void skill() {
System.out.println("一阳指");
}
}
class Tudi extends Shifu {
//ctrl+o 重写的快捷键
//注解 Override 检查这个代码是不是重写
@Override
public void skill() {
super.skill();//使用父类的skill
System.out.println("六脉神剑");
}
}
方法重写的语法规则:
-
方法名相同,方法参数列表相同
-
返回值类型相同(或其子类也可以)
-
访问修饰符权限不能低于父类
-
构造方法和私有方法本身就无法继承,也就不存在重写
抽象类
-
一个有抽象方法的类 必须定义为抽象类 在class 的左侧添加关键字abstract
-
抽象方法定义时,在返回值类型的左侧 添加关键字abstract
-
抽象方法主要是为了制定规则,让所有继承的子类都必须实现这个抽象方法
抽象类体验练习
-
父类Animal 有抽象方法 eat ,注意类也 要写成抽象类
-
子类Dog,Cat,Tiger继承了Animal ,必须重写父类的抽象方法,否则报错
抽象方法练习
-
1定义一个抽象类Animal,成员变量name和age
-
2 里面有2个抽象方法 eat和drink
-
3 定义类Dog继承Animal 实现方法
-
4 定义类Cat继承Animal 实现方法
public abstract class Animal{
String name;
int age;
public void eat();
public void drink();
}
public class Dog extends Animal{
@Override
public void eat(){
System.out.println("狗吃骨头");
}
@Override
public void drink(){
System.out.println("狗喝可乐");
}
}
public class Cat extends Animal{
@Override
public void eat(){
System.out.println("猫吃鱼");
}
@Override
public void drink(){
System.out.println("猫喝雪碧");
}
}
抽象类 抽象方法特点小结
-
抽象类中可以有普通方法,可以有抽象方法,也可以没有;
-
但有抽象方法的类一定是抽象类
-
-
抽象类的子类要么重写抽象类中的所有抽象方法,要么子类自己也是抽象类
-
可以和普通类一样定义成员变量
-
可以和普通类一样定义构造方法,但是不能实例化
-
构造方法的作用是用于子类访问父类构造方法时,初始化成员变量
-