封装 encapsulation
将数据(属性)保存在内部,写一个方法 授权 去操作它
好处
1 隐藏实现细节
2 可以对数据进行验证,保证安全合理 Person的年龄
实现
1 将属性进行私有化 (需要私有化的属性,不必是全部私有化,但是最好全部私有化,即使是名字)
2 提供一个set方法对属性进行 验证后赋值 数据验证部分在这里写
3 提供一个get方法根据权限 获取属性值 权限判断在这里写
alt+insert生成setter getter方法
setter方法与构造器的冲突与结合
冲突 使用构造器会绕过setter方法的数据验证
解决 在构造器中调用setter方法
继承 extends
解决代码复用问题
子类自动拥有父类的属性与方法
共性提取 共有的属性与方法提取出来形成一个新类
注意:
1 子类继承了所有的属性与方法,但是【私有属性与方法】不能直接访问,需要通过公共方法(父类提供)访问
全部继承了,但是有的可以用,有的用不了
注意:父类有但是私有,就不会在往上找了【找到了,就不会在往上找了,即使访问不了】,会直接报错
2 子类必须调用父类的构造器,完成父类的初始化
3 创建子类对象时,无论用哪个构造器都会【默认调用】 父类无参构造
如果父类没有无参构造,则每个子类构造方法必须用super指定用哪个父类构造器,否则编译不通过
【创建子类对象会调用父类构造器,这是必须的】
4 如果需要指定父类构造器,可以显式调用
5 super在使用时需要放在第一行
6 super与this都需要在第一行,不能同时使用
7 是先调用的父类构造,再执行的子类下面下面,因为super在第一行
8 所有类都是object子类
9 子类调用父类构造,父类构造还是会调用其父类的构造,直到最顶object
10Java是单继承与多重继承
那么a如何继承b与c呢? 如何实现多继承?
让b继承c,a继承b,就直接满足需求了,但是注意:b类有冗余需要解决【多重继承解决多继承问题】
11 Java中的继承必须满足is-a逻辑才能继承,不能滥用
Person is a music?不满足,不能用继承
12 子类的属性名与父类可以相同,直接调用与super调用
本质:【重要】
子类继承父类,创建子类对象时,到底发生了什么?
子类对象创建时,会建立了查找关系
先加载类,在方法区建立关系
创建子类对象会自动初始化所有父类属性(执行构造方法)
子类创建的内存布局
资料
继承设计基本思想
子类构造器中写
父类构造器super调用完成父类属性初始化
子类构造器完成子类属性初始化
方法的重写/覆盖 里氏替换原则
不一定是直接父类的覆盖 ctrl+b定位位置
子类方法参数与方法名要完全一样
规定:没有原理
子类方法的返回值类型要与父类一样,或者是父类返回值类型的子类(引用类型才有父子类关系,基础类型没有,所以有包装类)
子类方法不能缩小父类方法的访问权限
而且子类抛出的异常要比父类小
【理解】:(Java中子类可以替换父类,所以权限要大,否则有问题)--》返回值要比父类小;权限要比父类大
super关键字
super用于访问父类
但是不能访问父类私有属性与方法
访问父类构造器只能【在构造器中】,【首行】,【只能有一个·语句】
super访问不限于直接父类,会根据就近原则向上找
多态
解决代码维护问题,提高代码复用性
多态的体现:
方法的体现:
重载 传入不同参数就会调用不同的方法
重写 子父类方法名相同,对象不同调用的方法不同
对象的体现:(核心)
1 一个对象的编译类型与运行类型可以不一样
2 编译类型在定义对象时就确定了,不能改变
3 运行类型可以变化
4 编译类型看左,运行类型看右(=)
5 对象引用与真正对象的区别,多态是父类引用指向子类对象
前提:类存在继承关系
核心/本质:父类引用指向子类对象--》对象的向上转型
(理解:是子类对象,但其向上转型就相对于为其【本身的特有东西】与【属性】加了锁)
特点:可以调用父类所有属性(要遵循访问规则),不能调用子类特有属性(编译阶段编译器看编译类型决定能调用什么属性/方法)
属性与方法运行结果看子类具体实现(运行先找子类,没有向上找)
多态的向下转型 instanceOf
1 只能强转父类引用,不能强转父类对象
2 要求父类的引用必须是当前目标类型的对象
3 向下转型后可以调用子类类型所有成员,就与直接创建子类对象没有区别了
4 instanceOf判断是否是后面的类型/后面类型的子类型(is-a)
多态终极理解:
属性没有重写,看编译类型(属性没有动态绑定,编译期就确定了),编译看左的(无论有没有多态)
方法有重写,看运行类型(方法是动态绑定的,运行期才有结果),运行看右的(无论有没有多态)
多态应用:
多态数组
数组定义类型为父类类型,存储子类对象(多种)(要调用子类特有方法,用instanceOf向下转型)
多态参数
方法定义形参为父类,可以传参子类
动态绑定机制
当调用对象方法时,该方法会与该对象内存地址/运行类型绑定
对象调用的方法子类没有,父类有,执行父类,但是如果这个方法中还调用方法,并且这个方法父子类都有,是执行子类的
当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
总结:属性就看直观执行就行,也就是编译类型,方法要看子类执行开始找(无论是第几层的调用)
测试结果:this关键字无法影响动态绑定的结果
绑定
把一个【方法】与其所在的类/对象 关联起来叫做方法的绑定。绑定分为静态绑定(前期绑定)和动态绑定(后期绑定)
静态绑定:程序运行前就知道是属于哪个类了(编译时)
final,static,private修饰的方法以及构造方法都是静态绑定
动态绑定 多态为例
编译期,
类加载,类的 方法表(每个类一张) 存入方法区,
子类会完全拷贝父类方法表,如果有子类重写父类方法,改变父类的方法表指向,指向子类(也就是从父类继承来的方法位于子类定义的特有方法的前面)
写对象调用方法语句时,编译器会自动检查引用类型(父类)的方法表,看有没有这个方法,所以不能调用子类特有方法
(这只是为了进行编译期安全检查,确保这个方法确实存在)
运行期:
运行时,会根据真正的对象,将其压入操作数栈,根据其方法表调用方法
所以子类调用方法时,即使没有这个方法(没有重写,看上去没有),本质上也是执行的自己子类的方法(父类拷贝的)
这时,如果这个方法调用其他方法,当然也是优先执行子类的
(如果子类有【也就是重写了】,根本执行不到父类,方法表都不一样)
(如果没有【没有重写】,执行的也是自己的【拷贝了】,只是看上去是父类的)
【注意这里自己的表示方法表中引用地址是自己的,真正的方法地址只有一个,是父类的】
【有点绕,但好理解】