Java零基础笔记08(Java编程核心:面向对象编程高级 {继承、多态})
1.继承
Java中提供了一个关键字extends,用这个关键字,可以让一个类和另一个类建立起父子关系。
子类能继承啥?
● 子类能继承父类的非私有成员(成员变量、成员方法) 继承后对象的创建
● 子类的对象是由子类、父类共同完成的。
注意:
子类对象其实是由子类和父类多张设计图共同创建出来的对象,所以子类对象是完整的
1.1 权限修饰符
就是用来限制类中的成员 ( 成员变量, 成员方法, 构造器 ) 能够被访问的范围
修饰符 同类 同包 不同包的子类 不同包的非子类 通俗解释
public ✅ ✅ ✅ ✅ 完全开放,像公共广场
protected ✅ ✅ ✅ ❌ 家族共享,像家族遗产
默认 ✅ ✅ ❌ ❌ 小区内部,像小区公共设施
private ✅ ❌ ❌ ❌ 完全私有,像自家卧室
1.2 继承的特点
1.2.1 单继承和多层继承
public class Test {public static void main(String[] args) {// 目标:继承的注意事项:继承的特点。}
}// 1. java的类只能是单继承的,不支持多继承,支持多层继承
class A {
}class B extends A {
}class C extends B {
}
为什么Java不支持多继承
1.2.2 Java中的祖宗类:Object
● Java中所有类,要么直接继承了Object,要么默认继承了Object,要么间接继承Object。
● 因此,Object是所有类的祖宗类。
1.2.3 继承后子类访问成员的特点: 就近原则
在子类方法中访问其他成员(成员变量、成员方法),是依照就近原则的。先子类局部范围找,然后子类成员范围找,然后父类成员范围找,如果父类范围还没有找到则报错。
如果子父类中,出现了重名的成员,会优先使用子类的,如果此时一定要在子类中使用父类的怎么办?可以通过super关键字,指定访问父类的成员:
// 定义父类 Fu
class Fu {// 定义一个字符串属性 name,并初始化为 "fu的name"String name = "fu的name";// 定义一个方法 run,并打印 "fu类的run方法"public void run() {System.out.println("fu类的run方法");}
}// 定义子类 Zi,继承自父类 Fu
class Zi extends Fu {// 定义一个字符串属性 name,并初始化为 "zi的name"String name = "zi的name";// 定义一个方法 show,展示 name 属性的访问特点public void show() {// 定义一个局部变量 name,并初始化为 "show的name"String name = "show的name";// 打印局部变量 name 的值System.out.println(name); // 输出: show的name// 打印当前对象的 name 属性的值,由于就近原则,输出 Zi 类的 nameSystem.out.println(this.name); // 输出: zi的name// 打印父类的 name 属性的值System.out.println(super.name); // 输出: fu的name}// 重写父类的 run 方法,并打印 "zi类的run方法"public void run() {System.out.println("zi类的run方法");}
}// 定义主类 Test2,包含 main 方法
public class Test2 {public static void main(String[] args) {// 创建 Zi 类的对象 ziZi zi = new Zi();// 调用 Zi 类的 show 方法zi.show();// 调用 Zi 类的 run 方法zi.run(); // 输出: zi类的run方法// 调用父类的 run 方法zi.super.run(); // 输出: fu类的run方法}
}
1.2.4 方法重写
当子类觉得父类中的某个方法不好用,或者无法满足自己的需求时,子类可以重写一个方法名称、参数列表一样的方法,去覆盖父类的这个方法,这就是方法重写。
重写规范: 声明不变, 重新实现
public class Test {public static void main(String[] args) {// 目标:认识方法重写,再搞清楚场景。Cat cat = new Cat();cat.cry();}
}class Cat extends Animal {// 方法重写:方法名称、形参列表必须一样,这个方法就是方法重写。@Override // 方法重写的校验注解(标志),要求方法名称和形参列表必须与被重写方法一致,否则报错!更安全,可读性好,更优雅!public void cry() {System.out.println("🐱 喵喵喵的叫~~~");}
}class Animal {public void cry() {System.out.println("动物会叫~~~");}
}
方法重写在开发中常见的应用场景:
子类重写Object类的toString()方法, 以便返回对象的内容
public class Test2 {public static void main(String[] args) {// 目标:方法重写的常见应用场景,子类重写Object的toString方法,以便返回对象的内容。Student s = new Student("赵敏", '女', 25);// System.out.println(s.toString()); // com.itheima.extends5override.Student@b4c966a 所谓的地址System.out.println(s); // com.itheima.extends5override.Student@b4c966a 所谓的地址// 注意1:直接输出对象,默认会调用Object的toString方法(可以省略不写调用toString的代码),返回对象的地址信息// 注意2:输出对象的地址实际上总是没有什么意义的,开发中更希望输出对象的内容信息,所以子类需要重写Object的toString方法。// 以便以后输出对象时,默认调用的是子类重写的toString方法返回对象的内容。}
}class Student {private String name;private char sex;private int age;@Overridepublic String toString() {return "Student{name='" + name + "',sex='" + sex + "',age=" + age + "}";}
}
1.2.5 子类构造器
子类的全部构造器, 都会先调用父类的构造器, 再执行自己
public static void main(String[] args) {// 目标:认识子类构造器的特点,再看应用场景。// 子类构造器都会先调用父类的无参数构造器,再执行自己的构造器。Zi z = new Zi();
}class Zi extends Fu {public Zi() {System.out.println("子类无参构造器执行了");}
}class Fu {public Fu() {System.out.println("父类无参构造器执行了");}
}
// 父类
// 继承的好处:1.代码复用 2.减少了重复代码。public class People {private String name;private char sex;public People() {}public People(String name, char sex) {this.name = name;this.sex = sex;}public String getName() { return name; }public void setName(String name) { this.name = name; }public char getSex() { return sex; }public void setSex(char sex) { this.sex = sex; }
}// 子类
public class Teacher extends People {private String skill; // 技术。public Teacher(String name, String skill, char sex) {// 子类构造器调用父类构造器的应用场景。// 可以把子类继承自父类这部分的数据也完成初始化赋值。super(name, sex);this.skill = skill;}public String getSkill() {return skill;}
}public class Test2 {public static void main(String[] args) {// 目标:子类构造器调用父类构造器的应用场景。Teacher t = new Teacher( name: "dei", skill: "java、大数据、微服务", sex: 'M');System.out.println(t.getName());System.out.println(t.getSkill());System.out.println(t.getSex());}
}
1.2.6 this (...) 调用兄弟构造器
public class Test3 {public static void main(String[] args) {// 目标:理解this(...)调用兄弟构造器// 创建对象,存储一个学生的数据。Student s1 = new Student(name: "张无忌", sex: '男', age: 23);System.out.println(s1);Student s2 = new Student(name: "赵敏", sex: '女', age: 19);System.out.println(s2);}
}public class Student {private String name;private char sex;private int age;private String schoolName;public Student() {}public Student(String name, char sex, int age) {// this.name = name;// this.sex = sex;// this.age = age;// this.schoolName = "黑马程序员";// this调用兄弟构造器this(name, sex, age, schoolName: "黑马程序员");}public Student(String name, char sex, int age, String schoolName) {this.name = name;this.sex = sex;this.age = age;this.schoolName = schoolName;}public String getName() {return name;}
}
2.多态
多态是在继承/实现情况下的一种现象, 表现为: 对象多态 和 行为多态
// Test.java
package com.itheima.polymorphism1;public class Test {public static void main(String[] args) {// 目标:认识多态的代码。// 1. 对象多态,行为多态。Animal a1 = new Wolf();a1.run(); // 方法:编译看左边,运行看右边System.out.println(a1.name); // 成员变量:编译看左边,运行看左边Animal a2 = new Tortoise();a2.run(); // 方法:编译看左边,运行看右边System.out.println(a2.name); // 成员变量:编译看左边,运行也看左边}
}// Animal.java
package com.itheima.polymorphism1;public class Animal {String name = "动物";public void run() {System.out.println("动物会跑~~~");}
}// Wolf.java
package com.itheima.polymorphism1;public class Wolf extends Animal {String name = "狼";@Overridepublic void run() {System.out.println("奔跑的狼~~~");}
}// Tortoise.java
public class Tortoise extends Animal {String name = "乌龟";@Overridepublic void run() {System.out.println("乌龟跑的贼慢~~~");}
}
2.1 多态的好处
在多态形态下, 右边对象是解耦合的, 更便于扩展和维护
// Test.java
public class Test {public static void main(String[] args) {Animal a1 = new Tortoise();a1.run();Wolf w = new Wolf();go(w);Tortoise t = new Tortoise();go(t);}// 宠物游戏:所有动物都可以送给这个方法开始跑步。// 多态的好处2:父类类型的变量作为参数,可以接收一个子类对象public static void go(Animal a) {System.out.println("开始。。。");a.run();}
}// Wolf.java
package com.itheima.polymorphism2;public class Wolf extends Animal {String name = "狼";@Overridepublic void run() {System.out.println("奔跑的狼~~~");}
}// Tortoise.java
// 假设Tortoise类和Wolf类在同一个包中,或者已经正确导入
public class Tortoise extends Animal {String name = "乌龟";@Overridepublic void run() {System.out.println("乌龟跑的贼慢~~~");}
}// Animal.java
// 假设Animal类和Wolf类在同一个包中,或者已经正确导入
public class Animal {String name = "动物";public void run() {System.out.println("动物会跑~~~");}
}
多态下会产生的一个问题, 怎么解决?
多态性下不能使用子类的独有功能 ( 可以在多态下的类型转换解决 )
2.2 多态下的类型转换
public static void go(Animal a){System.out.println("开始。。。");a.run();// a.shrinkHead(); // 报错,多态下不能调用子类独有功能。// Java建议强制转换前,应该判断对象的真实类型,再进行强制类型转换。if(a instanceof Wolf){Wolf w1 = (Wolf) a;w1.eatSheep();}else if(a instanceof Tortoise){Tortoise t1 = (Tortoise) a;t1.run();}
}