12、面对对象
基本定义
-
1 基础
-
类
- 对事物、逻辑、算法或概念的抽象。描述一类对象的行为和特征。
类是对象的模板。
- 对事物、逻辑、算法或概念的抽象。描述一类对象的行为和特征。
-
对象(实例)
-
对象是类的一个实例,拥有多个特征和行为的实体。
-
创建对象
-
声明
- 声明一个对象,包括对象名称和对象类型
-
实例化
- 使用关键字new来创建一个对象
-
初始化
- 使用new创建对象时,会调用构造方法初始化对象
-
-
创建对象的四种方式:
-
- new 关键字创建
-
- 反射
-
- 反序列化
-
- 克隆
-
-
-
成员变量
-
实例变量(非静态变量)
- 默认值: 字面值
-
类变量(静态变量)
-
-
引用
- java中,一切都“看作”对象,但操纵的标识符实际是指向一个对象的“引用”(reference)。”
-
构造方法
- 新建对象时,执行的一个特殊方法
- 在创建一个对象的时候,至少要调用一个构造方法
- 构造方法的名称必须与类同名,一个类可以有多个构造方法
-
this
- 引用当前对象,调用实例属性,实例方法
- 构造方法之间调用
-
方法重载overload
- 定义:一个类中定义多个相同名称的方法
- 同名不同参: 方法名相同,参数列表不同(类型,个数,顺序)
与权限修饰符,返回值类型无关 - 调用带有重载的方法时,需要根据传入的实参找到与之匹配的方法
-
-
2 三个修饰符
-
abstract(抽象类)
-
作用
- 可被子类继承,提供共性属性和方法。
- 可声明为引用,更自然的使用多态。
-
注意点
- 不能new对象,但可以声明引用。
- abstract 类中可以没有abstract 方法
-
特点
- 抽象类不能实例化
- 抽象类可以包含抽象方法,也可以包含非抽象方法
- 抽象方法只有方法声明,没有方法实现
- 包含抽象方法的类,一定是抽象类
- 子类必须实现抽象类的抽象方法,除非子类也是抽象类
-
经验
- 抽象父类,可作为子类的组成部分, 依附于子类对象存在, 由父类共性+子类独有组成完整的子类对象。
-
-
final
-
特征
- 最终不可变的
- 修饰变量、方法、类
- 修饰类不能被继承
- 修饰方法不能被覆盖
-
final修饰实例变量(常量)
- 实例常量不再提供默认值,必须手动赋予初始值。
- 赋值时机:显示初始化(声明并赋值)、动态代码块、构造方法。
动态代码块本质就是把代码块内容放到构造代码块里面(反编译) - 注意:如果在构造方法中为实例常量赋值, 必须保证所有的构造方法都能对其正确赋值。
-
final修饰类变量(常量)
- 静态常量不再提供默认值,必须手动赋予初始值。
- 赋值时机:显示初始化(声明并赋值)、静态代码块。
-
final修饰基本类型: 值不可变
-
final修饰引用数据类型: 地址不可变
-
final修饰对象常量
- 不能修改地址,但是属性可以更改
-
-
static(静态)
-
特点:
- 静态方法中可以直接访问静态属性,不能访问非静态属性和方法
- 非静态方法中可以访问静态属性和方法
- 非静态成员属于实例
- 静态变量属于类,可以成为“类变量”
- 静态成员通常使用类名直接调用
- 静态方法中不允许使用this或是super关键字。
- 静态方法可以继承,不能重写、没有多态。
-
被static修饰的成员将最优先加载到内存
-
静态成员存在方法区(HotSpot虚拟机JDK1.7之前永久代,JDK1.8元空间)中
- 常量池在jdk1.6之前在永久代 jdk1.7在堆中
-
注意事项
- 实例属性是每个对象各自持有的独立空间(多份),对象单方面修改,不会影响其他对象。
- 静态属性是整个类共同持有的共享空间(一份),任何对象修改,都会影响其他对象。
-
static用法
-
修饰属性
-
修饰方法
-
修饰代码块
-
修饰内部类
-
静态导入
- import static java.lang.System.out;
-
-
-
类加载
-
JVM首次使用某个类时,需通过CLASSPATH查找该类的.class文件。
-
将.class文件中对类的描述信息加载到内存中,进行保存。
如:包名、类名、父类、属性、方法、构造方法… -
加载时机
- 创建对象。
- 创建子类对象。
- 访问静态属性。
- 调用静态方法。
- 主动加载:Class.forName(“全限定名”);
-
-
注意事项:
- abstract不能和static同时使用
- abstract不能和final同时使用
- abstract不能与private连用。private方法不能被继承,因此子类就无法覆盖父类的private方法。而abstract方法要求一定要被子类覆盖,矛 盾。
- abstract方法的访问修饰符也不能是(default),abstract修饰方法时,只能与访问修饰符public或protected连用。
-
-
代码块
-
局部代码块
- 局部代码块,会缩小变量的使 用范围,提前释放局部变量, 节省内存。
-
动态代码块(构造代码块)
- 创建对象时,触发动态代码块的执行。
- 执行地位:初始化属性之后、构造方法代码之前。
- 作用:可为实例属性赋值,或必要的初始行为。
-
静态代码块
- 类加载时,触发静态代码块的执行(仅一次)。
- 执行地位:静态属性初始化之后。
- 作用:可为静态属性赋值,或必要的初始行为。
-
对象创建过程
-
-
接口
-
作用
- 结构设计工具,用来解耦合
-
概念
-
微观概念: 接口是一种能力和约定。
- 接口的定义:代表了某种能力。
- 方法的定义:能力的具体要求。
-
宏观概念:接口是一种标准。
-
-
接口中只能定义
- 公共的常量
- 公共的抽象方法
- 公共的内部类、内部接口
-
用interface替代class
-
用implements替代extends
-
接口的规范
- 任何类在实现接口时,必须实现接口中所有的抽象方法,否则此类为抽象类。
- 实现接口中的抽象方法时,访问修饰符必须是public。
-
特征:
- 没有构造方法(不能创建对象)、动态代码块、静态代码块。
- 所有属性都是公开静态常量,隐式使用public static final修饰。
- 所有方法都是公开抽象方法,隐式使用public abstract修饰。
-
经验:Java为单继承,当父类的方法种类无法满足子类需求时,可实现接 口扩充子类能力。(接口多实现)
接口支持多实现,可为类扩充多种能力。 -
如果父类实现了某些接口,则子类自动就实现了这些接口。
-
接口引用
-
同父类一样,接口也可声明为引用,并指向实现类对象
-
注意:
- 仅可调用接口中所声明的方法,不可调用实现类中独有的方法。
- 可强转回实现类本身类型,进行独有方法调用。
-
-
接口的多态
- 多种不同类型的引用指向同一个对象时, 表示看待对象的视角不同。
- 不同引用所能看到的对象范围不同, 只能调用自身类型中所声明的部分。
-
接口回调
- 先有接口的使用者,后有接口的实现者
(先创造,等调用) - 回调原理
- 先有接口的使用者,后有接口的实现者
-
常见关系
-
类与类:
- 单继承
- extends 父类名称
-
类与接口
- 多实现
- implements 接口名称1 , 接口名称2 , 接口名称n
-
接口与接口
- 多继承
- extends 父接口1 , 父接口2 , 父接口n
-
-
常量接口
- 将多个常用于表示状态或固定值的变量,以静态常量的形式定义在接口中统一管理, 提高代码可读性。
-
标记接口
- 接口没有任何成员,仅仅是一个标记。Serializable、Cloneable
-
JDK1.8之后接口中可以包含静态方法和默认方法
- 静态方法:不能被继承,通过接口名.方法名调用
- 默认方法:可以被继承,通过实现类调用
-
接口的好处
- 程序的耦合度降低。
- 更自然的使用多态。
- 设计与实现完全分离。
- 更容易搭建程序框架。
- 更容易更换具体实现。
-
-
Java包
- 包主要用来对类和接口进行分类。
- 当开发Java程序时,可能编写成百上千的类,因此很有必要对类和接口进行分类
-
源文件声明规则
- 一个源文件中只能有一个public类
- 一个源文件可以有多个非public类
- 源文件的名称应该和public类的类名保持一致
- 如果一个类定义在某个包中,那么package语句应该在源文件的首行
- 如果源文件包含import语句,那么应该放在package语句和类定义之间。如果没有package语句,那么import语句应该在源文件中最前面
- import语句和package语句对源文件中定义的所有类都有效。在同一源文件中,不能给不同的类不同的包声明。
-
Import语句
- Import语句就是用来提供一个合理的路径,使得编译器可以找到某个类
三大特性
-
封装
-
概念:
- 尽可能隐藏对象的内部实现细节,控制对象的修改及访问权限
-
访问控制符
-
private
- 成员变量和方法只能在类内被访问,具有类可见性.
-
default
- 成员变量和方法只能被同一个包里的类访问,具有包可见性
-
protected
- 可以被同一个包中的类访问,被同一个项目中不同包中的子类访问
-
public
- 可以被同一个项目中所有类访问,具有项目可见性,这是最大的访问权限
-
-
好处
- 隐藏类的实现细节
- 让使用者只能通过程序员规定的方法来访问数据
- 可以方便的加入存取控制语句,限制不合理操作
-
具体步骤
- 修改属性的可见性来限制对属性的访问
- 公共访问方法: 为每个属性创建一对赋值(setter)方法和取值(getter)方法,用于对这些属性的存取
- 过滤有效数据: 在赋值方法中,加入对属性的存取控制语句
-
-
继承
-
概念
- 继承通过extends关键字来实现,其中SubClass称为子类,SuperClass称为父类,基类,或超类
类与类之间特征和行为的一种赠与或者获得. - 在一组相同或类似的类中,抽取出共性的特征和行为,定义在父类中,实现重用
- 继承通过extends关键字来实现,其中SubClass称为子类,SuperClass称为父类,基类,或超类
-
作用
- 代码重用,代码复用, 代码可扩展性
- 产生继承关系以后,子类可以使用父类的属性和方法,也可以定义子类独有的属性和方法.
-
单继承
- 一个类只能有一个直接父类
- 可以多级继承,属性和方法逐级叠加。
-
不可继承
-
构造方法不继承
-
没有访问权限的成员不继承
- private修饰的属性和方法
- 父子类不在同一个package中时,default修饰的属性和方法
-
静态成员不继承
-
-
重写:
- jdk1.7重写要求变化: 方法返回值类型(引用类型)和父类返回值类型相同或者是父类返回值的子类
- 方法重写原则: 方法名称,参数列表,返回值类型都必须与父类相同
访问修饰符可与父类相同或者比父类更宽泛
子类中的方法,不能抛出比父类更多、更宽的检查时异常。 - 优先执行子类重写后方法
-
super
-
在子类中,可直接访问从父类继承到的属性和方法,但如果父子类的属性或 方法存在重名(属性遮蔽、方法重写)时,需要加以区分,才可专项访问。
-
在具有继承关系的对象创建中,构建子类对象会先构建父类对象。
由父类的共性内容,叠加子类的独有内容,组合成完整的子类对象。 -
super关键字的用法
- 在子类的方法中使用“super.”的形式访问父类的属性和方法。
- 在子类的构造方法的首行,使用“super()”或“super(实参)”,调用父类构造方法。
-
注意
- 如果子类构造方法中,没有显示定义super()或super(实参),则默认提供super()。
- 同一个子类构造方法中,super()、this()不可同时存在。
-
-
-
多态
-
多态指的是编译器(申明变量是)和运行期(创建对象后)表现为不同的形态(数据类型)
概念:父类引用指向子类对象,从而产生多种形态。 -
三个条件
- 继承的存在(继承是多态的基础,没有继承就没有多态)
- 子类重写父类的方法(多态下调用子类重写的方法)
- 父类引用变量指向子类对象(子类到父类的类型转换)
-
子类转父类规则
- 将一个父类的引用指向一个子类的对象,称为向上转型(upcastiog),自动进行类型转换.
- 此时通过父类引用调用的方法是子类覆盖或继承父类的方法,不是父类的方法.
- 此时通过父类引用变量无法调用子类特有的方法
- 如果父类要调用子类的特有方法就得将一个指向子类对象的父类引用赋给一个子类的引用,称为向下转型,此时必须进行强制类型转换
-
instanceof关键字
- 向下转型前,应判断引用中的对象真实类型,保证类型转换的正确性。
- 语法:父类引用 instanceof 类型
-
多态使用形式
-
- 父类作为方法的形式参数,实际传递子类对象
(使用父类作为方法形参实现多态,使方法参数的类型更为宽泛。)
- 父类作为方法的形式参数,实际传递子类对象
-
- 父类作为方法的返回值,实际传递子类对象
(使用父类作为方法返回值实现多态,使方法可以返回不同子类对象。)
- 父类作为方法的返回值,实际传递子类对象
-
-
注意事项
- 父类引用仅可调用父类所声明的属性和方法,
不可调用子类独有的属性和方法。 - 依旧遵循重写原则,如果子类重写了父类中的方法,执行 子类中重写后的方法,否则执行父类中的方法。
- 父类引用仅可调用父类所声明的属性和方法,
-
面向对象的设计原则
(开口里合最单依)
-
总则:开闭原则(Open Close Principle,OCP)
- 开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改 原有的代码,而是要扩展原有代码,实现一个热插拔的效果。
-
接口隔离原则(Interface Segregation Principle,ISP)
- 每个接口中不存在子类用不到却必须实现的方法,如果不然,就要将接口拆分。使用 多个隔离的接口,比使用单个接口(多个接口方法集合到一个的接口)要好。
-
里氏替换原则(Liskov Substitution Principle,LSP)
- 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。(父类出现的地方,子类也可以出现)
- 里氏替换原则中,子类对父类的方法尽量不要重写和重载。因为父类代表了定义好的 结构,通过这个规范的接口与外界交互,子类不应该随便破坏它。
-
合成复用原则(Composite Reuse Principle,CRP)
- 原则是尽量首先使用合成/聚合的方式,而不是使用继承。(尽量创建对象调用方法,而不是继承)
-
迪米特法则(最少知道原则)(Demeter Principle,DP)
- 一个类对自己依赖的类知道的越少越好。也就是说无论被依赖的类多么复杂,都应该 将逻辑封装在方法的内部,通过public方法提供给外部。这样当被依赖的类变化时, 才能最小的影响该类。
-
单一职责原则(Single Responsibility Principle,SRP)
- 不要存在多于一个导致类变更的原因,也就是说每个类应该实现单一的职责,如若不 然,就应该把类拆分。
-
依赖倒置原则(Dependence Inversion Principle,DIP)
- 面向接口编程,依赖于抽象而不依赖于具体。写代码时用到具体类时,不与具体类交 互,而与具体类的上层接口交互。