Java学习笔记—类的详解
一花一世界,一叶一菩提。一切皆对象,万物有三问:我是谁?我从哪里来?我到哪里去?
——本章节内容来自于《码出高效—Java开发手册》
Java中认为世间万物都是抽象的,都可以抽象成类。在Java中Object
是任何类的默认父类,而Object也体现哲学的三大经典问题:
- 我是谁?
getClass
说明本质是谁,而toString
是当前我的名片。 - 我从哪里来?
Object
构造方法是产生对象的基本方法,clone()
是繁殖对象的另一种方式。 - 我到哪里去?
finalize()
是在对象销毁时触发的基本方法。
类的定义
类的定义有访问级别、类型、类名、是否抽象、是否静态、泛型标识、继承或实现关键字、父类或接口名称等组成。类的级别有public和无访问控制符,类型分为class、interface、enum。
Java类定义主要由两部分组成:成员和方法。
接口和抽象类
语法纬度 | 抽象类 | 接口 |
---|---|---|
定义关键字 | abstract | interface |
子类继承或实现关键字 | extends | implements |
方法实现 | 可以有 | 不能有,但在JDK8 及以后,允许有default实现 |
方法访问控制符 | 无限制 | 有限制,默认是public abstract类型 |
属性访问控制符 | 无限制 | 有限制,默认是public static final类型 |
静态方法 | 可以有 | 不能有,但是在JDK8 及以后,允许有 |
static{}静态代码块 | 可以有 | 不能有 |
本类型之间扩展 | 单继承 | 多继承 |
本类型之间扩展关键字 | extends | extends |
- 抽象类在被继承时体现的是is-a关系,接口在被实现时体现的是can-do关系。
- 抽象类是模板设计,而接口是契约设计。
- 接口是顶级的“类”,虽然关键字是
interface
,但是编译之后的字节码文件扩展名还是.class
。 - 在纠结使用接口还是抽象类时,优先推荐使用接口,遵循接口隔离原则,按照某个纬度划分多个接口,然后再用抽象类去
implements
某些接口,这样做方便后续的扩展和重构。
内部类
- 静态内部类,如:static class StaticInnerClass{};
- 成员内部类,如:private class InstanceInnerClass{};
- 局部内部类,定义在方法或者表达式内部
- 匿名内部类,如:(new Thread(){}).start()。
public class OuterClass{
// 成员内部类
private class InstanceInnerClass{}
// 静态内部类
static class StaticInnerClass{}
public static void main(String[] args){
// 两个匿名内部类,分别对应OuterClass$1和OuterClass$2
(new Thread(){}).start();
(new Thread(){}).start();
// 两个方法内部类,分别对应OuterClass$1MethodClass1.class,OuterClass$1MethodClass2.class
class MethodClass1{}
class MethodClass2{}
}
}
- 静态内部类作用
- 作用域不会扩散到包外
- 可以通过“外部类.内部类"的方式直接访问
- 内部类可以访问外部类中的所有静态属性和方法
访问权限
访问权限控制符 | 任何地方 | 包外子类 | 包内 | 类内 |
---|---|---|---|---|
public | OK | OK | OK | OK |
protected | NO | OK | OK | OK |
无 | NO | NO | OK | OK |
private | NO | NO | NO | OK |
- public:可以修饰外部类、属性、方法,表示公开的、无限制的、是访问权限最松一级别,被其修饰的类、属性和方法不仅可以被内部访问,还可以跨类、跨包访问,甚至允许跨工程访问。
- protected:只能修饰属性和方法,表示受保护的、有限制的,被其修饰的属性和方法能被包内及包外子类访问。注意,即使并非继承关系,protected属性和方法在同一包内也是可见的。
- 无:即无任何访问权限控制符,千万不要说成default,它并非访问权限控制符的关键字,另外,在JDK8接口中引入了default默认方法实现,更加容易混淆两者的释义。无访问权限控制符仅对包内可见。虽然无访问权限控制符还可以修饰外部类,但是定义外部类极少使用无控制符的方式,要么定义为内部类,功能内聚;要么定义公开类,即public class,包外也可以实例化。
- private:只能修饰属性、方法、内部类。表示“私有的”,是访问限制最严格的一级,被其修饰的属性或方法只能在该类内部访问,子类、包内均不能访问,更不允许跨包访问。
定义类时,推荐访问控制符级别从严处理:
- 如果不允许外部直接通过new创建对象,构造方法必须是private。
- 工具类不允许有public或default构造方法。
- 类非static成员变量并且与子类共享,必须是protected。
- 类非static成员变量并且仅在本类使用,必须是private。
- 类static成员变量如果仅在本类使用,必须是private。
- 若是static成员变量,必须考虑是否为final。
- 类成员方法只提供内部调用,必须是private。
- 类成员方法只对继承类公开,那么限制为protected。
this和super
概念 | this | super |
---|---|---|
基本概念 | 访问本类实例属性和方法 | 由子类访问父类中的实例属性和方法 |
查找范围 | 先找本类、没有则找父类 | 直接查找父类 |
特异功能 | 单独使用时,表示当前对象 | 在子类覆写父类方法时,访问父类同名方法 |
共同点 | 1. 都是关键字,起指代作用 2. 在构造方法中必须出现在第一行 | 1. 都是关键字,起指代作用 2. 在构造方法中必须出现在第一行 |
类关系
类关系 | 英文名 | 描述 | 权利强制 | 类图示例 | 示例说明 |
---|---|---|---|---|---|
继承 | Generalization | 父类和子类之间的关系:is-a | 父类方 | 小狗继承与动物,完全符合里氏替换 | |
实现 | Realization | 接口与实现类之间的关系:can-do | 接口方 | 小狗实现了狗叫的接口行为 | |
组合 | Composition | 比聚合更强的关系:contains-a | 整体方 | 头只能是身体强组合的一部分,两者完全不可分,具有相同的生命周期 | |
聚合 | Aggregation | 暂时组装的关系:has-a | 组装方 | 小狗和狗绳之间是暂时聚合关系,狗绳完全可以复用在另一条小狗上 | |
依赖 | Dependency | 一个类用到另一个类:depends-a | 被依赖方 | 人喂养小狗,小狗作为参数传入,是一种依赖关系 | |
关联 | Association | 类与类之间存在互相平等的使用关系:links-a | 平等 | 人可以用信用卡消费,信用卡可以提取到人信息 |
类的序列化
对象序列化:将数据对象转换为二进制流的过程称为对象的序列化。
内存中的数据只有转换为二进制流才可以进行数据持久化和网络传输。
常用三种序列化:
方式 | 优点 | 缺点 | 推荐 | 注意事项 |
---|---|---|---|---|
Java原生序列化 | 1.兼容性最好 | 1.不支持跨语言 2.性能一般 | 基于性能和兼容性考虑, 不推荐使用 | 1. 如果兼容升级,请不要修改serialVersionUID,避免反序列化失败 2. 如果是不兼容升级,需要吸怪serialVersionUID值,避免反序列化混乱 3. Java反序列化时不会调用类的无参构造方法,而是调用native方法将成员变量赋值为对应类型的初始值。 |
Hessian序列化 | 1. 支持动态类型、跨语言、基于对象传输的网络协议 2. 字描述序列化类型。不依赖外部描述文件或接口定义,用一个字节表示常用基础类型,极大缩短二进制流。 3. 语言无关,支持脚本语言 4. 协议简单,比Java原生序列化高效 | 1. 反序列化时,会导致类同名成员变量被父类的值覆盖 | Hessian序列化的效率更高,且序列化的数据更小,在基于RPC的调用方式中性能更好。 推荐 | 使用hessian序列化时,一定要注意子类和父类不能有同名字段 |
JSON序列化 | 相比Java原生和Hessian序列化,JSON可读性比较好,方便调试 | 在序列化过程中抛弃了类型信息,所以反序列化时只有提供类型信息才能准确地反序列化。 | 推荐 | JSON序列化就是将数据对象转换为JSON字符串 |