关于final字段修饰静态变量的一些思考

今天上午看到了一段代码,感觉挺有意思的,就试着敲了一下,看了下输出结果,有点出人意料。

代码是这样的:

package com.marsyoung.test;

import java.util.ArrayList;
import java.util.List;

public class Test {

	private static List<Test> objs=new ArrayList<Test>();
	
	static {
		objs.add(new Test(Test.S_NAME,Test.NAME,Test.COUNT));
		objs.add(new Test(Test.S_NAME,Test.NAME,Test.COUNT));
	}
	
	private final static String S_NAME="aaa";
	private final static String NAME=new String("aaa");
	private final static Long COUNT=1l;
	
	private String name;
	private String title;
	private Long count;
	public Test(String name,String title,Long count){
		this.name=name;
		this.title=title;
		this.count=count;
	}
	public static void main(String[] args) {
		System.out.println(objs);
	}
	@Override
	public String toString() {
		return "Test [name="+name+",title="+title+",count="+count+"]";
	}
	
	
}


输出结果为:


首先理解这样的结果,得先理解代码。

我们可以看出Test类中有一个静态化的List<Test>,同时这个List中通过静态代码块放了两个新new的Test。

虽然存在一个循环引用,但是对于静态的变量,代码只会执行一次,所以这里不会有内存溢出。

接着思考,按照静态代码块和静态变量的加载顺序是正序的,那么在静态代码块加载的时候,S_NAME,NAME,COUNT中应该没有值,所以最后的输出结果中,Test的name,title,count应该是null。

那么为什么最终结果name会等于aaa?

我猜想是由于aaa的传参类型有别于另外两个,在调用new的时候使用的是构造方法,需要传参,"string"进行的是值传递,而Long和new String是引用传递,所以前者可以传到后者传不到。

但是这种想法并不对,因为在静态代码块调用的时候,静态变量还没有被加载到,传参的时候也不可能获取到。

为了证明这点,做个试验,如果我们在上面的代码中,把final去掉之后,则输出结果是:


可以看出关键是final字段,那么final到底做了什么?

查看Test.class文件的反编译代码:


可以看到在静态代码块中,对于被final修饰的S_NAME变量直接就被编译成了aaa放到了new Test()的参数中。而NAME和COUNT还是引用。

为什么final修饰的S_NAME在编译时会直接传个值?这样不会很混乱么?如果也传引用不行么?

猜想这样做可能和string最终要存储在常量池中有关,这么编译能加快最终class文件的加载和执行速度。。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值