类的初始化、static与final关键字

类的加载、连接、初始化

要理解static关键字,首先要了解类的加载流程。下面简单介绍一下:

当程序主动使用某个类时,如果该类还未加载到jvm内存中,则系统会通过加载、连接、初始化三个步骤对该类进行初始化。如果没有意外,jvm一般情况下会连续完成这三个步骤,所有这三个步骤有时统称为类加载类初始化,是一种泛指。

类的加载

类加载指的是将类的class文件读入内存,并为之创建一个java.lang.Class对象。

类的加载由类加载器完成,通过使用不同的类加载器,可以从不同来源加载类的二进制数据,通常有以下几种来源:

  1. 从本地文件系统加载编译后生成的class文件,这是绝大部分程序的类加载方式。
  2. 从JAR包加载class文件。
  3. 通过网络加载class文件。
  4. 把一个Java源文件动态编译,并执行加载。

类的连接

当类被加载之后,jvm中对应的Class对象已经创建完毕。连接截断会将类的二进制数据合并到JRE中。

类的初始化

类的初始化阶段,主要是对类变量进行初始化。在Java中对类变量指定初始值有两种方式:

  1. 声明类变量时指定初始值
  2. 使用静态代码块为类变量指定初始值。

总的来说,JVM初始化一个类步骤如下:

  1. 假如这个类还没有被加载和连接,则程序执行加载和连接改类。
  2. 加入该类的直接父类还没有初始化,则先初始化其直接父类。
  3. 加入类中有初始化语句(为静态变量指定初始化值或静态代码块),则JVM依代码顺序依次执行这些初始化语句。

类的初始化时机

当Java程序首次通过下面5种方式来使用某个类时,系统就会初始化该类:

  • 创建类的实例:1.包括new;2.通过反射来创建实例;3.通过反序列化方式来创建实例。
  • 调用某个类的静态方法。
  • 访问某个类的类变量,或为该类变量赋值。
  • 初始化某个类的子类,则会初始化这个父类。
  • 通过反射方式创建某个类的java.lang.Class对象,例如Class.forName("bear");

static

理解了类的初始化流程之后,static就很好理解了,有以下几个特性:

  1. static域无论创建多少个对象,静态数据都只占用一份存储区域,所有对象共享这个区域。因为一个类只会被初始化一次。所以static域又被称为类变量。
  2. 静态方法会引发类的初始化,也就是会执行静态代码块和给类变量赋值。
  3. 构造方法会引发类的初始化。
  4. static不能修饰局部变量。
  5. 通常常量会用static修饰,旨在强调常量只有一份。

final

通常常量也会用final修饰,旨在强调常量不可变。

final变量

修饰基本类型

用final修饰基本类型数据可以保证变量的值永不改变;

修饰引用类型

用final修饰引用类型却只能保证引用不变:一旦被指向了一个对象,就无法再把它改为指向另一个对象。但是对象自身确实可以修改的。Java并未提供任何使对象恒定不变的方法,不可变类只能靠程序员自己保证

空白final域

Java允许使用空白final变量,所谓空白final是指声明时未给定初始值的域。相比于声明时指定初始值,空白final更加灵活:一个类中的final域可以做到根据对象不同而使用构造器传入不同的初始化值,却又保持恒定不变的特性。

public class Diamond {
    final int price;
    public Diamond(int price) {
        this.price = price;
    }

    public static void main(String[] args) {
        Diamond diamond = new Diamond(1);
//      diamond.price=3; 编译错误,price不可变
    }
}
final参数

final可以用来修饰方法中的参数,这样使用的结果是:可以读参数,但是无法在方法中更改引用类型参数指定的对象或者基本类型参数的值。这一特性主要用来向匿名内部类传递数据。

final方法

  1. 用final修饰方法来禁止子类去修改方法实现。

  2. 所有private方法都隐式地被final修饰。

final类

  1. 用final修饰类来禁止有任何类继承它。
  2. final类的域并不是隐式final,他们可以根据个人意愿选择是或不是final。
  3. final类的所有方法都隐式的被final修饰。
posted @ 2019-11-24 22:29  孔令翰  阅读(535)  评论(0编辑  收藏  举报