Java 从入门到放弃09 - 《封装、继承、多态》
封装
- 该露的露,该藏得藏
- 高内聚,低耦合
- 一句话概括封装: 属性私有,get/set
也就是把属性变成private,然后通过使用用public的方法来允许其他人访问类的属性
- 快捷键 alt+insert 可以快速地插入方法
- 封装还可以通过 set/get 来进行提前的安全性检查,避免不合理数据的注入或不合理成员的获取
方法的重载
- 在同一个类名字里加入许多同名函数,让他们传入的参数不同,叫做方法的重载
继承
-
继承的本质是对某一种类的抽象,从而实现更好的建模
-
extends 的意思是拓展,子类是对父类的拓展
-
Java中只有单继承,没有多继承!
- 继承是类与类之间的一种关系。除此之外,类和类之间的关系还有依赖、组合、聚合等。
- 继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用extends表示
- 子类与父类之间,存在“is a”的关系
- 继承之间注意修饰符,比如 public protected default private 之间的继承是由顺序要求的
-
子类无法继承父类的 private 字段或者 private 方法。可以通过protected修饰符来修饰,使得字段和方法的访问权限被控制在继承树内部
-
子类不会继承父类的任何构造方法,子类的构造方法是编译器自动生成的,不是继承的
组织继承
- 正常情况下,如果一个类没有 final 修饰符,就是可以被继承的,但是还有permits关键字,可以指定可以继承该类的类的名称。前提是用 sealed 修饰class
public sealed class Shape permits Rect, Circle, Triangle {
...
}
object类
- object类是所有类的父类,所有的类都是从object拓展来的,所以才会有“toString”等方法
super 与 this
- this 指向自身,super指向父类
方法的重写
- 重写都是方法的重写,与属性无关
- 如图,我们建立了两个类,A类是B类的拓展,并且各赋予一个方法
-
接下来在main函数里创建一个A类的对象a,调用a的方法test()
-
程序也是很自然的跑出了结果
A test
- 我们此时用B类创建一个A类的对象b,调用方法test(),查看结果
A test
B test
- 如何理解这里呢?定义是什么类,就调用什么类的静态方法
- 父类的引用指向了子类,调用了子类的方法
- 用B类创建了A类的对象,把A的值给了B,这时候B是A,而A又继承了B类,向上转型,所以调用的是B类的方法,也就是 B test
这就是多态
- 多态的关键:父类的引用指向了子类的对象
- 如果此时我们把static去掉呢?
可以看到这里两边都多出来了两个圈圈
6.使用快捷键 alt+insert 之后
- 这里的override是 注解 注解是有功能的注释,
- 这时候再执行程序,可以看到结果
A test
A test
- 重写只和非静态方法有关,只和非static方法有关
- 静态方法只能被继承,不能被重写
- 这是因为静态方法,是和类同时被加载的,所以只和类有关
- 重写的范围可以扩大不能缩小
- 抛出异常的范围可以缩小不能扩大
向上转型
- 上述的过程涉及到向上转型。
- 上述过程中,A是从B继承下来的,那么一个引用类型为 B 的变量,能够指向子类 A 的实例
- 向上转型实际上是把一个子类型安全地变为更加抽象的父类型
向下转型
Person p1 = new Student(); // upcasting, ok
Person p2 = new Person();
Student s1 = (Student) p1; // ok
Student s2 = (Student) p2; // runtime error! ClassCastException!
- Person 类型 p1 作为父类,指向了子类 Student 的实例,但 Person类型 p2 指向 Person 的实例。在向下转型的过程中,p1 的转型会成功,因为 p1 确实指向 Student 的实例,但把 p2 转型成为 Student 会失败,因为 p2 的实例,实际上就是 Person ,不能把父类变成子类,因为子类的功能比父类更多,多的功能无法凭空编出来。
- 转型失败报错会报错 ClassCastException
- 可以使用 instanceof 操作符 来判断一个实例究竟是不是某种类型
- 所以可以使用 instanceof 进行安全地向下转型
Object obj = "hello";
if (obj instanceof String) {
String s = (String) obj;
System.out.println(s.toUpperCase());
}
从java 14 开始,可以更简洁的使用下面这种写法
public class Main {
public static void main(String[] args) {
Object obj = "hello";
if (obj instanceof String s) {
// 可以直接使用变量s:
System.out.println(s.toUpperCase());
}
}
}
区分继承和组合
假设Person类拥有属性name ,Student类继承自Person类,现在有一个新类 Book,Book类也拥有属性name。那么我们能不能让Student继承自Book呢?
显然从逻辑上讲,这是不合理的,因为Student 与 Book 之间是 has 的关系 而不是 is a 的关系
所以这里我们使用组合,而不是继承,即让Student可以持有一个Book的实例
class Student extends Person {
protected Book book;
protected int score;
}
多态
- 多态是指,针对某个类型的方法调用,其真正的执行方法取决于运行时期实际类型的方法
Person p = new Student();
p.run();
上面的代码,可以看出来调用的是 Student 的 run() 方法
但是假如有如下的方法
public void runTwice(Person p) {
p.run();
p.run();
}
传入的参数类型是 Person 但是我们并不知道传入参数的实际类型到底是 Person 还是 Student 抑或是 Person 的其他子类,因此也就无法确定调用的到底是不是 Person 类的 run() 方法
所以,多态的特性就是,运行期才能动态决定调用的子类方法。对某个类型调用某个方法,执行的实际方法可能是某个子类override之后的方法。
public class Main {
public static void main(String[] args) {
// 给一个有普通收入、工资收入和享受国务院特殊津贴的小伙伴算税:
Income[] incomes = new Income[] {
new Income(3000),
new Salary(7500),
new StateCouncilSpecialAllowance(15000)
};
System.out.println(totalTax(incomes));
}
public static double totalTax(Income... incomes) {
double total = 0;
for (Income income: incomes) {
total = total + income.getTax();
}
return total;
}
}
class Income {
protected double income;
public Income(double income) {
this.income = income;
}
public double getTax() {
return income * 0.1; // 税率10%
}
}
class Salary extends Income {
public Salary(double income) {
super(income);
}
@Override
public double getTax() {
if (income <= 5000) {
return 0;
}
return (income - 5000) * 0.2;
}
}
class StateCouncilSpecialAllowance extends Income {
public StateCouncilSpecialAllowance(double income) {
super(income);
}
@Override
public double getTax() {
return 0;
}
}
观察totalTax()方法,利用多态,totalTax() 方法只需要和 Income 打交道,它完全不需要知道Slery等类的存在,就可以计算出总的税,如果要新增一种稿费收入,只需要从Income中派生,然后正确的覆写(override) getTax()方法就可以。把新的类型传入 totalTax(),剩下的部分都不需要修改
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了