“类”有“广义”和“狭义”之分。“广义的类”包括:狭义的类(Class),抽象类(Abstract Class),接口(Interface),枚举(Enum),注解(Annotation)。为了进行区分,在接下来的叙述中,“类”指代“广义的类”,“具体类”指代“狭义的类”。
一、类的本质分类
根据以上叙述可知,“类”包括:具体类(Class),抽象类(Abstract Class),接口(Interface),枚举(Enum),注解(Annotation)。但是本质上“类”只包括“具体类和接口”两种,原因描述如下:
- “抽象类”本质上是“含有抽象方法的具体类”
- “枚举”本质上是“具体类”,可以使用
javap
命令进行查看 - “注解”本质上是“接口”,可以使用
javap
命令进行查看
而内部类只是处于类中的“具体类,抽象类,接口,枚举,注解”的特定称呼而已。
在接下来的描述中,“类”就只包含“具体类”和“接口”。
二、复用类的3种方式
2.1、组合
复用现有类的功能。
2.2、继承
复用现有类的形式。
2.3、代理
组合和继承之间的中庸之道,我们将一个实例对象(只有具体类才具有实例对象)作为字段置于所要构造的新类(具体类)中(就像组合),但与此同时在新类中暴露该实例对象的某个方法子集(就像继承)。
三、继承
关于“继承”有以下几点描述:
- 当创建一个类时,总是在继承,除非已明确指出要从其他类继承,否则就是在隐式地从Java的标准根类Object继承 (接口跟Object类的关系在“接口”章节进行详细介绍)
- 继承时,会自动继承基类中可被继承的字段和方法
- 继承时,可以继承基类方法,覆盖基类方法,重载基类方法,可以通过
@Override
注解断言“覆盖基类方法”,从而防止意外重载基类的方法 - 生成导出类实例对象(导出类为具体类)时,会有基类子实例对象(基类为具体类)生成。当创建了一个导出类的实例对象时,该实例对象包含了一个基类的子实例对象,这个子实例对象与你用基类直接创建的实例对象是一样的。二者区别在于,后者来自外部,而基类的子实例对象被包装在导出类实例对象内部
- 在生成导出类实例对象时,对基类子实例对象的正确初始化也是至关重要的,这个只能通过调用基类构造器来完成。JVM会在导出类的构造器中插入对基类构造器的调用,只有基类构造器才能完成基类子实例对象的正确初始化。如果基类构造器为带参构造器,那么在导出类构造器中必须通过
super
语句显式调用基类的带参构造器
四、完备介绍“初始化与清理”
在包含“具体类,抽象类,接口,枚举,注解”(本质上只有“具体类”和“接口”),且存在继承关系的背景下,对Java编程思想#初始化与清理作补充,完备介绍“初始化与清理”相关内容。
一个综合复杂的继承关系示例图如图1所示。
图1
4.1、完备介绍“初始化”
4.1.1、完备介绍“类对象初始化”
触发时机:第一次访问类的“静态字段,静态方法,构造方法”,Class.forName(相应类类路径)
语句第一次执行。类对象初始化最多执行一次。
触发后初始化过程:
1 | public void initializeForClassObject(当前类) { |
备注:
类对象的默认初始化有可能已经先于其他初始化(定义初始化,静态初始化语句初始化)完成,具体机制跟虚拟机的具体实现相关,且默认初始化不是整个初始化流程的关键所在,因此这里不再深究。
4.1.2、完备介绍“实例对象初始化”
触发时机:创建实例对象(再次提醒:只能通过构造器创建实例对象,接口没有实例对象,只能对应于具体类)。
触发后初始化过程:
1 | public void initializeForInstanceObject(当前具体类) { |
4.2、完备介绍“清理”
由Java编程思想#初始化与清理可知,当我们需要清理一些自定义的资源时,不能依赖finalize
方法,得自己编写特殊的清理方法。在继承场景下,另外还需要注意这些清理方法的调用顺序:跟资源初始化顺序相反,防止出现“在清理资源时,该资源所依赖资源已经被提前清理,而导致错误”的情形。
五、其他
5.1、super关键词
跟this关键词类似,关于this关键词可参见Java编程思想#初始化与清理。super关键词有两个用途:“指代基类实例对象”和“在构造器中调用基类构造器”。
跟this关键词不一样的是,可执行return this;
语句,不可执行return super;
语句,可认为这个区别基于“基类的子实例对象被包装在导出类实例对象内部”的特性。
使用super调用基类构造器有以下限制:
- “super调用基类构造器”只能在构造器中使用
- “super调用基类构造器”只能置于构造器最起始处
- “super调用基类构造器”在同一个构造器中至多使用1次
5.2、java命令能够作用于具有包访问权限的类生成的class文件
现在有一个“Main.java”文件,其中内容如下:
1 | public class Main { |
执行javac Main.java
命令,得到“Main.class”和“SecondClass.class”两个文件。执行java Main
命令,得到打印的“Hello World”内容;执行java SecondClass
命令,也能够得到打印的“Hello World”内容。
以上说明了java命令不只能够作用于具有public访问权限的类生成的class文件,也能够作用于具有包访问权限的类生成的class文件。
参考文献: [1]https://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html [2]https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html