热修复Java探索之final static域编译

本文探讨了Java中final static字段的编译处理,解释了为何未在类初始化时显式初始化的final static字段仍能得到默认值。在DVM执行dvmInitClass方法前,静态变量会先被赋予默认值,引用类型默认为NULL。对于final static修饰的原始类型和非引用String,其值会在类初始化时通过方法赋值,而final static修饰的引用类型则在初始化时赋值,不支持热部署。相反,final static修饰的原始类型和非引用String会编译到DEX文件,支持热部署时的域替换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先看个例子:

public class Demo{
 static Temp t1 = new Temp();
 final static Temp t2 = new Temp();

 static String s1 = new String("heihei");
 final static String s2 = "haha";

 static int i1 = 1;
 final static int i2 = 2;
}

编译器处理可以理解下面的情形:

public class Demo{
 //类初始化调用,只会调用一次
 void static constructor <clinit>(){
   //初始化t1
   new-instance v0,Temp
   invoke-direct v0,Temp;<init>()V
   sput-object v0,t1

   //初始化t2
   new-instance v0,Temp
   invoke-direct v0,Temp;<init>()V
   sput-object v0,t2

   //初始化s1
   new-instance v0,Ljava/lang/String
   const-string v1,"heihei"
   invoke-direct {v0,v1} ,Ljava/lang/String;-><init>(Ljava/lang/String)V
   sput-object v0,Demo;->s1:Ljava/lang/String
   //初始化i1
   const/4 v0,0x1
   sput v0,Demo;->i1:I
 }
}

可以看到final static String s2 = "haha";final static int i2 = 2;本没有在<clinit>中初始化,看起来好像和上一篇的域编译矛盾。

实际上在执行dvmInitClass执行<clinit>前还有执行init$Fields方法。init$Fields方法会给static变量赋初值,如果是引用类型默认为NULL。
因此在执行init$Fields方法后:

 t1 = NULL;
 t2 = NULL;

 s1 = NULL;
 s2 = "haha";

 i1 = 0;
 i2 = 2;

其中t1、t2,s1,i1首先赋予了默认值,然后在<clinit>得到初始化。

static 和 final static修饰field区别:

  1. final static修饰的原始类型和非引用String类型不会被翻译到<clinit>,而是在类初始化执行init$Fields方法时得到了初始化赋值。
  2. final static修饰的引用类型,仍然会在<clinit>初始化。

热部署解决方案

  1. final static修饰的引用类型由于是在<clinit>初始化初始化,所以没法走热部署。
  2. final static修饰的原始类型参数和非引用String类型参数会编译到dex文件中,所以当final static修饰的原始类型被引用到时会被立即替换,final static修饰的非引用String类型会被常量池索引id替换,因此热部署模式下,最终所有引用该final static的域都会被修改,所以支持热部署。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值