- final修饰变量时,表示该变量一旦获得了初始值就不可被改变。
- final可修饰成员变量(包括类变量和实例变量)
- final也可修局部变量、形参。
- final成员变量(包括类变量和实例变量)必须由程序员显式初始化
final修饰成员变量
类变量:
初始值只能在以下两个地方之一指定:
- 静态初始化块
- 声明该类变量时
实例变量
初始值只能在以下三个地方之一指定:
- 非静态初始化块
- 声明该实例变量
- 构造器
final修饰局部变量
- 局部变量必须由程序员显式初始化
- final修饰局部变量时没有指定默认值,可在后面代码中对其赋初始值,但只能一次。
- 如果局部变量在定义时已指定默认值,则后面代码中不可再对其赋值。
public class A
{
public void test(final int a)
{
//不能对修饰的形参赋值,下面语句非法
a=5;
}
}
final修饰引用类型变量
使用final修饰的引用类型变量不能被重新赋值
但可以改变引用类型变量所引用对象的内容
例:
public static void main(String[] args)
{
final int[] iArr={5,6,12,9};
System.out.println(Arrays.toString(iArr));
Arrays.sort(iArr);
System.out.println(Arrays.toString(iArr));
//下面对数组元素赋值合法
iArr[2]=-8;
System.out.println(Arrays.toString(iArr));
//下面这句对final修饰的引用类型变量重新赋值,非法
iArr=null;
}
例:
假设有Person类
public static void main(String[] args)
{
final Person p=new Person(20);
//改变Person对象的age实例变量,合法
p.setAge(11);
//对final修饰的引用类型变量重新赋值,非法
p=null;
}
可执行“宏替换”的final变量
final修饰符的一个重要用途就是定义“宏变量”。
当定义final变量时就为该变量指定了初始值,而且该初始值可以在编译时就确定下来,
那么这个final变量本质上就是一个“宏变量”,
编译器会把程序中所有用到该变量的地方直接替换成该变量的值。
如下程序:
public static void main(String[] args)
{
final int a=5;
System.out.println(a);
}
a变量指定初始值为5,
对于上面程序而言,变量a其实根本不存在,
当程序执行System.out.println(a);代码时,
实际转换为System.out.println(5);执行的
看下面程序:
public class StringJoinTest
{
public static void main(String[] args)
{
//定义4个宏变量
final int a=5+2;
final double b=1.2/3;
final String str="bbb";
final String book="aaa:"+99.0;
//下面的book2因为调用了方法,所以无法在编译时被确定下来
final String book2="aaa"+String.valueOf(99.0);
//因为book是宏变量,故输出true
System.out.println(book=="aaa:99.0");
//输出false
System.out.println(book2=="aaa:99.0");
}
}
/*
Java会使用常量池来管理曾经使用过的字符串,例执行
String a="Java";后,常量池中会缓存一个字符串"java"
如果程序再执行String b="Java"
系统会让b直接指向常量池中的"Java"字符串,
因此a==b,将返回true
*/
book2变量显示将数值99.0转换为字符串,但由于该变量的值需要调用String类的方法,故无法在编译时确定book2的值,故不会被当成“宏”处理。
再看下面程序:
public class FinalReferenceTest
{
public static void main(String[] args)
{
//编译时,就已确定s2的值为"aaabbb",
//系统会让s2直接指向常量池中缓存的"aaabbb"
//故输出true
String s1="aaabbb";
String s2="aaa"+"bbb";
System.out.println(s1==s2);//输出true
//str1、str2只是普通变量,编译器不会执行“宏替换”,故输出false
//编译器无法确定s3的值,也就无法让其指向字符串中缓存的"aaabbb"
String str1="aaa";
String str2="bbb";
String s3=str1+str2;
System.out.println(s1==s3);//输出false
//因为加了final修饰,编译时会执行“宏替换”,故输出true
final String str3="aaa";
final String str4="bbb";
String s4=str3+str4;
System.out.println(s1==s4);//输出true
}
}
只有定义该变量时指定初始值才会有“宏变量”的效果。