在学习《effective Java》一书时接触到了构建器,也是第一次实际接触静态内部类这一概念,学习过程中产生了对静态内部类的加载和初始化产生了一些疑问,因此特地查阅了一点资料。此外还复习了一点类的加载顺序。
1.类加载顺序
1. 静态代码块
随着类的加载而运行,只执行一次
格式如下:
public class Order {
static {
System.out.println("静态代码块");
}
}
当出现多个静态代码块时,按顺序执行,如下:
public class Order {
static int i = 1;
static {
i = 2;
}
public static void main(String[] args) {
System.out.println(Order.i);
}
}
--------------------------------------------
输出为2
public class Order {
static {
i = 2;
}
static int i = 1;
public static void main(String[] args) {
System.out.println(Order.i);
}
}
--------------------------------------------
输出为1
2. 构造代码块
随着对象的创建而运行,且先于构造函数的执行。有多个构造代码块时,也将按顺序执行。
格式如下:
public class Order {
{
System.out.println("构造代码块");
}
}
3. 构造器
与类名同名的,通过new运算符来新建一个类的实例的函数。可以同时拥有多个不同参数类型、顺序的构造器。当未定义任何构造器时,编译器会默认提供一个无参的构造器。
格式如下:
public class Order {
public Order(){
System.out.println("构造器");
}
}
4. 执行顺序
public class Order {
static {
System.out.println("静态代码块");
}
{
System.out.println("构造代码块");
}
public Order(){
System.out.println("构造器");
}
public static void main(String[] args) {
new Order();
System.out.println("===============");
new Order();
}
}
--------------------------------------------
输出结果:
静态代码块
构造代码块
构造器
===============
构造代码块
构造器
可以看出,静态代码块>>构造代码块>>构造器,而且不管new几个对象,静态代码块都只执行一次,构造代码块和构造器每次创建对象时都会执行。
5. 考虑继承的情况
public class Inheritance {
public static void main(String[] args) {
new Child();
System.out.println("===============");
new Child();
}
}
class Parent{
static {
System.out.println("父类的静态代码块");
}
{
System.out.println("父类的构造代码块");
}
Parent(){
System.out.println("父类的构造器");
}
}
class Child extends Parent{
static {
System.out.println("子类的静态代码块");
}
{
System.out.println("子类的构造代码块");
}
Child(){
System.out.println("子类的构造器");
}
}
--------------------------------------------
结果如下:
父类的静态代码块
子类的静态代码块
父类的构造代码块
父类的构造器
子类的构造代码块
子类的构造器
===============
父类的构造代码块
父类的构造器
子类的构造代码块
子类的构造器
- 首先加载父类和子类,与此同时,各自的静态代码块也被执行。
- 接着开始创建父类对象,因为构造代码块是依托于构造器的,所以构造代码块和构造器是同时执行的。
- 最后创建子类对象。
- 因为类已经被加载了,所以之后再创建对象不会再执行静态代码块
注意的点:
- 当父类定义了有参构造器而没定义无参构造器,此时子类的构造器会报错,提示父类中没有可用的默认构造函数,这时候,相当于自动调用了super(),而父类中没有对应的构造器而报错,此时在子类构造器内用super语句调用对应参数的构造器,则可以解决报错。
而如果父类中未定义任何构造器,也不会报错,这是因为未定义任何构造器时,编译器会提供默认的构造器。
6. 静态内部类
public class InnerClassTest {
public InnerClassTest(){
System.out.println("构造器");
}
static void testMethod(){
System.out.println("静态方法");
}
static {
System.out.println("静态代码块");
}
{
System.out.println("构造代码块");
}
public static class StaticInnerClass{
StaticInnerClass(){
System.out.println("静态内部类的构造器");
}
static {
System.out.println("静态内部类的静态代码块");
}
{
System.out.println("静态内部类的构造代码块");
}
public static void testMethod(){
System.out.println("静态内部类的静态方法");
}
}
public class InnerClass{
InnerClass(){
System.out.println("内部类的构造器");
}
{
System.out.println("内部类的构造代码块");
}
}
}
测试结果:
@Test public void test01(){
new ClassTest();
}
--------------------------------------------
输出:
静态代码块
构造代码块
构造器
@Test public void test02(){
ClassTest.testMethod();
}
--------------------------------------------
输出:
静态代码块
静态方法
@Test public void test03(){
new ClassTest().new InnerClass();
}
--------------------------------------------
输出:
静态代码块
构造代码块
构造器
内部类的构造代码块
内部类的构造器
@Test public void test04(){
new ClassTest.StaticInnerClass();
}
--------------------------------------------
输出:
静态内部类的静态代码块
静态内部类的构造代码块
静态内部类的构造器
@Test public void test05(){
InnerClassTest.StaticInnerClass.testMethod();
}
--------------------------------------------
输出:
静态内部类的静态代码块
静态内部类的静态方法
结论:
当在顶层类中定义内部类时,静态内部类并不会随着顶层类的初始化而被初始化,而是当静态内部类的方法被调用时,静态内部类才被初始化。反之,静态内部类也可以独立于外部类被使用。(个人见解,如有不对欢迎大家指导)