2. Static

介绍

static 是一个关键字,同时是一个修饰符。

static 可以修饰什么

  1. 属性
  2. 方法
  3. 代码块
  4. 内部类

修饰属性

用 static 修饰的属性称为 静态变量,类变量。

静态变量和非静态的变量的区别:

  1. 值的存储位置不同,非静态的变量存储在堆中,静态变量存储在方法区中。另外拓展,局部变量存储在栈中。
  2. 值的初始化(赋值)的时机不同。非静态的变量在创建实例对象的时候,在 <init>() 实例初始化方法中完成。静态变量的初始化在类初始化,<clinit>() 类初始化中完成。所以,静态变量的初始化比非静态的变量初始化要早。
  3. 非静态的变量每一个是独立的,每个类都有一份。而静态变量是所有类共享一份。
  4. 生命周期不同。非静态的变量,随着对象的创建而创建,当对象被垃圾回收器回收时就消失。而静态变量随着类的初始化而初始化,随着类的卸载而卸载。
  5. get/set 方法不同。非静态的变量对应的 get/set 也是非静态的。静态的变量对应的 get/set 也是静态的。

修饰方法

用 static 修饰的方法称为 静态方法或者类方法。

  1. 静态方法可以通过 “类名.方法名” 进行调用。当然也可以通过 “对象.方法名” 进行调用。
  2. 静态方法中是不可以出现 this,super 等关键字。
  3. 静态方法中不能直接使用本类的非静态的成员(属性,方法,内部类)。但是反过来可以,即,非静态的方法中可以调用静态的方法。
  4. 静态方法是不能被重写的。

修饰代码块

代码块的作用:为属性初始化。

代码块可以分为静态代码块和非静态代码块。非静态代码块的作用是为非静态的属性进行初始化。静态代码块的作用是为静态的属性进行初始化。

无论创建几个对象,静态代码块只执行一次(非静态代码块是每创建一个对象就会执行一次),如果有多个静态代码块,那么就按照顺序执行。

静态代码块优先于非静态代码块执行。在类的<clinit>()初始化方法中执行。

<clinit>() 方法由两部分组成。分别是 静态变量的显式赋值 和 静态代码块。两者的执行顺序是谁在前面就谁先执行。在子类初始化的时候,如果发现父类还没有初始化,那么就会先初始化父类,即先执行父类的<clinit>()方法。

类和实例初始化过程总结

  1. 先执行类的初始化。

    1.1 如果有父类,那么就先执行父类的初始化(执行父类 static 相关的代码,例如,静态变量赋值,静态代码块等),在执行子类的初始化(执行子类 static 相关的代码,例如,静态变量赋值,静态代码块等)

  2. 实例初始化。

    2.1 如果有父类,那么就先执行父类的实例初始化方法。即执行非静态属性和方法部分,二者谁在前面谁就先执行。然后在执行构造方法。父类执行完成之后在执行子类的初始化。

例子:

执行结果:

在子类的构造器中,如果没有 super,那么默认有一个 super(), 用于调用父类的无参构造。