对于一个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执行”宏替换“。