对修饰符final和static的浅析

1、static修饰符

     被static修饰的变量和方法,被类的所有实例所共享。加载类时只分配一次内存。

  • 静态变量:可以直接通过类名来访问 Person.a;
  • 态方法:可以直接通过类名来访问 Person.say();静态方法内,可以访问静态变量,但是不能使用this关键字且不能访问实例变量,因为被所有实例所共有,无法判断属于哪个实例对象;
  • 静态代码块:java虚拟机在加载类时就执行静态代码块。

2、final修饰符

     有final修饰表示最终(不可再修改)的意思:

  • final类:不能被继承(即没有子类);
  • final方法:不能被子类的方法覆盖(final不用来修饰构造方法,父类与子类之间的构造方法不存在覆盖关系,final修饰是没有意义的);
  • final修饰的变量:即常量,常量只能被赋值一次,之后不能改变。

3、静态常量

     静态常量属于编译时常量(final static int a=2*3;)在编译的时就能计算出具体的值),在编译的时候将这个值就放入到常量池中,a被访问时类是不会被初始化的(这是类的被动使用)。访问类的静态变量或静态方法的时候类会被初始化(这是类的主动使用)。而静态变量只会在类初始化的时候才会被赋值。具体细节请参考“类的生命周期”:加载(堆区、方法区)——连接(验证、准备(静态变量分配内存、设置默认初始值为0)、解析(符号引用替换为直接引用,即指针指向方法区的内存位置))——初始化(程序对类或接口主动使用的时候才会被初始化)。

4、类的初始化化时机

     类的初始化阶段java虚拟机会为类的static静态变量赋予初始值(这和准备阶段设置默认初始值为0是不一样的)。只有类的主动使用才会初始化类。

4.1、类的主动使用

  • 创建类的实例:用new语句创建实例 Person ps=new Person()。

  • 调用类的静态变量或对静态变量赋值:

1
2
3
4
5
6
7
8
9
public class Person{ 
//这和final static int a=2*3;是有区别的
static int a=2*3;

static{
//Java在定义一个类的时候里面只能放方法和属性,这是规定死了的。System.out.println()是在调用一个叫println的函数,这里是函数的调用而不是类里面定义一个函数。所以需要用static代码块
System.out.println("init Person"); //static声明的静态代码块,使得类在初始化的时候会被调用而不需要创建实例对象。它这时候就不在任何一个方法中。
}
}

     调用的时候写:

1
2
//这样就可以在不new一个Person实例的情况下,来初始化Person类了。调用类的静态方法
System.out.println("a="+Person.a);
  • 调用java API中的反射方法:Class.forName(“Person”);
  • 初始化子类的时候会先初始化父类(但”父类”是接口的时候,不会先初始化它所实现的接口的,只有在程序在使用接口的静态变量时才会使静态接口初始化)。
  • Java 虚拟机启动时被标明为启动类的类。

4.2、类的被动使用

  • final 类型的静态变量在编译的时候能计算出值(即编译时常量,在编译的时候将这个值就放入到常量池中了):注: final类型的静态变量在编译的时候不能计算出变量的值(即运行时常量)的时候是会被初始化的
1
2
3
4
//变量a是编译时常量
final static int a=2*3;
//变量a不是是编译时常量(即运行时常量)
final static int a=(int)Math.random();
  • “父类”是接口的时候,不会先初始化它所实现的接口的,只有在程序在使用接口的静态变量时才会使静态接口初始化。
  • ClassLoader类的loadClass(“Person”)方法的时候,只是对类的加载,不是初始Class.forName(“Person”);才会初始化。
打赏
  • Copyrights © 2023-2024 杨海波
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信