对于一个final变量,不管它是类变量、实例变量,还是局部变量,只要定义该变量时使用了final修饰符修饰,并在定义该final类变量时指定了初始值,而且该初始值可以在编译时就被确定下来,那么这个final变量本质上已经不再是变量,而是相当于一个直接量。

public class FinalTest{
	
	public static void main(String[] args){
		<strong>final int a=5+2;
		final double b=1.2/3;
		final String str="疯狂" + "Java";
		final String book="疯狂Java讲义"+99.0;</strong>
		//下面的book2变量的值因为调用了方法,所以无法在编译时确定下来
		final String book2="疯狂Java讲义"+String.valueOf(99.0);                //              1
		System.out.println(book=="疯狂Java讲义99.0");
		System.out.println(book2=="疯狂Java讲义99.0");
		
	}
}

上面程序中的粗体字代码定义了四个final变量,程序为这四个变量赋予初始值,指定的初始值要么是算术表达式,要么是字符串连接运算。即使字符串连接运算中包含隐式类型(将数值转换为字符串)转换,编译器依然可以在编译时就确定a、b、str、book这四个变量的值,因此它们都是“宏变量”

“宏变量”:final修饰符的一个重要用途就是定义“宏变量”。当定义final变量时就为该变量指定了初始值,而且该初始值可以在编译时就确定下来,那么这个final变量本质上就是一个“宏变量”,编译器会把程序所有用到该变量的地方直接替换成该变量的值。

从表面上看,1行代码定义的book2与book没有太大的区别,只是定义book2变量时的显式将数值99.0转换为字符串。但由于该变量的值需要调用String类的方法,因此编译器无法在编译时确定book2的值,book2不会被当成“宏变量”类处理。


例如下列示例:

public class FinalTest{
	
	public static void main(String[] args){
		String s1="疯狂Java";
		String s2="疯狂"+"Java";
		System.out.println(s1==s2);
		
		String str1="疯狂";
		String str2="Java";
		<span style="color:#ff0000;">String s3=str1+str2;</span>
		System.out.println(s1==s3);
	}
}

对于s3而言,它的值由str1和str2进行连接后得到。由于str1/str2只是两个普通变量,编译器不会执行“宏替换”,因此编译器无法在编译时确定s3的值,不会让s3指向字符串池中缓存的“疯狂Java”。由此可见,s1==s3将输出false

为了让s1==s3输出为true也很简单,只要编译器可以对str1和str2两个变量执行“宏替换”这样编译器既可以在编译阶段就确定s3的值,就会让s3指向字符串池中缓存的“疯狂Java".即把程序改成如下形式:

public class FinalTest{
	
	public static void main(String[] args){
		String s1="疯狂Java";
		String s2="疯狂"+"Java";
		System.out.println(s1==s2);
		
		<strong>final String str1="疯狂";
		final String str2="Java";</strong>
		String s3=str1+str2;
		System.out.println(s1==s3);
	}
}

如下列示例:

public class FinalTest{
	final static String str1;
	final static String str2="Java";
	static {
		str1="Java";
	}
	public static void main(String[] args){
		System.out.println(str1+str1=="JavaJava");
		System.out.println(str2+str2=="JavaJava");
	}
}

上面程序定义了两个final类变量,但是只有str2在定义该变量时指定了初始值,str1则在静态初始化块中指定了初始值,因此系统不会对str1进行宏替换,但会对str2执行”宏替换“。