/**
* @author 张家琛
* @version 1.0
* @date 2023/1/24 20:23
*/
public class FinalDemo {
public static void main(String[] args) {
final var a=5;
System.out.println(a);
}
}
对于上面的程序来说,变量a其实根本就不存在,这段代码本质上执行的是转换成的:
System.out.println(5);
final修饰符的一个重要用途就是定义“宏变量”,final变量有了初始值且可以在编译的时候就确定下来,那么它就是个“宏变量”,编译器会把程序中所有用到该变量的地方直接替换成该变量的值
再看一段代码
public static void main(String[] args) {
//宏变量
final var a = 7 + 8;
final var b = 5.8 / 6;
final var str = "final" + "关键字";
final var book = "final关键字" + 66.66;
//不会当成宏变量来处理,因为调用了方法,编译时确定不下来这个值是多少
final var book2 = "final关键字" + String.valueOf(66.66);
System.out.println(book=="final关键字66.66");
System.out.println(book2=="final关键字66.66");
}
输出结果:
如果被赋的表达式只是基本的算数表达式或字符串连接运算,没有访问普通变量和方法,Java编译器会将它当成宏变量来处理
即使book变量进行的是隐式字符串连接转换,但编译器仍然可以在编译时就确定下来book的值,因此a,b,str,book都是宏变量
所以,book会被编译器直接替换成"final关键字66.66",输出true
反之book2在编译的时候确定不下来到底是个啥值,得经过方法调用,就不是宏变量咯,不会让它指向字符串连接池中已经缓存好的"final关键字66.66"
再看下面程序
public static void main(String[] args) {
var s1 = "海城你刀哥";
var s2 = "海城" + "你刀哥";
System.out.println(s1 == s2);
var str1 = "海城";
var str2 = "你刀哥";
var s3 = str1 + str2;
System.out.println(s1 == s3);
}
输出结果
对于s3而言,它是由str1和str2进行连接运算后得到,由于str1、str2只是两个普通变量,编译器不会执行宏替换,因此无法在编译时确定s3的值,也就无法让s3指向字符串常量池中缓存的”海城你刀哥“,固s1==s3为false
final修饰的成员变量未初始化的时候,到底能不能访问呢?
/**
* @author 张家琛
* @version 1.0
* @date 2023/1/24 20:50
*/
public class FinalUnassignedTest {
final int age;
{
System.out.println(age);
}
public static void main(String[] args) {
new FinalUnassignedTest();
}
}
运行结果:
说明这种方式不太可行,如果换种形式:
public class FinalUnassignedTest {
final int age;
{
printAge();
age=6;
System.out.println(age);
}
public void printAge(){
System.out.println(age);
}
public static void main(String[] args) {
new FinalUnassignedTest();
}
}
输出结果:
刀哥来了也得寻思一会啊!!!!怎么先输出了个0???不是说没事先定义好的final不能输出吗??
其实Java是允许通过方法来访问final成员变量的,默认给了个0。但是这显然违背了final成员变量的设计初衷,所以建议别这么干,不然刀哥友情提醒:”有你好果纸吃“。