java中的final关键字:
final可以修饰变量,被final修饰的变量被赋值之后,不能对它重新赋值
final可以修饰方法,被final修饰的方法不能被重写
final可以修饰类,被final修饰的类不能派生子类
1)final修饰变量
被final修饰的变量必须显式指定初始值,且只能在下面3个位置指定初始值:
定义final实例变量时指定初始值
在非静态代码块中为final实例变量指定初始值
在构造器中为final实例变量指定初始值
使用javap工具编译final修饰的实例变量可以看到,变量的赋值都是放在构造方法中执行的,理论上说final修饰的实例变量的初始化只能
在构造方法中进行。
同样对于final修饰的类变量也必须指定初始值,且只能在2个地方指定初始值:
定义final的类变量时指定初始值
在静态代码块中为final类变量指定初始值
使用javap工具编译final修饰的类变量可以看到,变量的赋值都是放在静态代码块中执行的,理论上说final修饰的类变量的初始化只能
在静态代码块中进行。
2)宏变量
请先看http://blog.youkuaiyun.com/xxqi1229/article/details/6905188中的关于"类变量与实例变量的初始化过程"下面的程序是在原来的程序
上面稍作修改后的。
请看程序:
public class Price {
final static Price INSTANCE = new Price(2.8) ;
final static double initPrice = 20 ;//此变量声明为final类型,此处和源程序不同。
double currentPrice ;
public Price(double discount){
currentPrice = initPrice - discount ;
}
}
测试用例如下:
public class Test {
public static void main(String[] args) {
System.out.println(Price.INSTANCE.currentPrice);
Price p = new Price(2.8) ;
System.out.println(p.currentPrice);
}
}
此时程序的运行结果为:
17.2
17.2
(注:原来程序的运行结果为-2.8 17.2)
为什么加上final关键字后结果就变为17.2呢?对于一个用final修饰的变量而言,如果定义该final变量的时就指定了初始值,而且这个
初始值在编译的时候就可以确定下来,那么这个变量将不再是一个变量,系统会将其当成一个“宏变量”处理,也就是说,在程序中出现
该变量的地方全部把它当成对应的值来处理。
什么叫值在编译时可以确定下来。比如说String s = "aaaaaa";此时在编译的时候就可以确定下来为“aaaaaa”,但是如果定义为
String s = str ;str也是一个字符串类,则此时编译的时候就不能确定s的值是多少。
对于上面的程序而言,由于使用了final关键字修饰initPrice类变量,因此Price类的构造器中执行currentPrice=initPrice-discount;的
时候会将initPrice直接替换为20.因此,执行该代码效果相当于currentPrice=20-discount;
如果把程序中定义initPrice的代码改为如下:
static double d = 20 ;
final static double initPrice = d ;
则程序运行结果为:
-2.8
17.2
这事因为在编译initPrice的时候不能确定initPrice的值。
3)宏替换
对于一个final变量,不管它是类变量,实例变量还是局部变量,只要定义了该变量时使用了final修饰符修饰,并在定义该final类变量的
时候指定了初始值,而且该初始值可以在编译的时候就确定下来,那么这个final变量本质上已经不再是一个变量,而是一个直接量(宏)。
请看如下程序:
public class Test {
public static void main(String[] args) {
String str1 = "疯狂";
String str2 = "java" ;
final String str3 = "疯狂java";
final String str4 = "疯狂" + "java";
final String str5 = str1 + str2 ;
final String str6 = "疯狂" + String.valueOf("java") ;
System.out.println(str3 == str4);
System.out.println(str3 == str5);
System.out.println(str3 == str6);
}
}
程序运行结果为:
true
false
false
str3 == str4的结果为true,这是因为str4在编译的时候就确定了值。
str3 == str5的结果为false,这事因为str5在编译的时候不能确定值,因为str1和str2不是final类型的,如果把str1和str2定义为final
类型,则str5在编译的时候就可以确定值,此时结果就应该为true。
str3 == str6的结果为false,这是因为表达式中使用了String的方法valueOf,因此在编译的时候无法确定str6的值。
注:对于宏变量只有在定义该变量的时候赋初始值才会有“宏变量”的效果,在非静态代码块、构造方法中为fianl实例变量指定初始值则不会
有这样的效果。
请看下面的程序:
public class FinalInitTest {
final String str1 ;
final String str2 ;
final String str3 = "java" ;
{//非静态代码块中初始化s
str1 = "java" ;
}
public FinalInitTest(){//构造方法中初始化
str2 = "java" ;
}
public void display(){
System.out.println(str1 + str1 == "javajava");
System.out.println(str2 + str2 == "javajava");
System.out.println(str3 + str3 == "javajava");
}
public static void main(String[] args){
FinalInitTest fit = new FinalInitTest() ;
fit.display() ;
}
}
程序的运行结果为:
false
false
true
从程序结果中可以看出,str1和str2没有达到宏替换的母的,这是因为str1在初始化在飞静态代码块中,str2的初始化在构造方法中。
只有在定义final变量的时给定初始值(编译的时候可以确定值)才会达到宏替换的效果。静态final类型的变量也一样。
3)final变量在内部类中的使用
程序需要在匿名内部类中使用局部变量,则局部变量必须声明为final类型。否则会编译错误。为什么必须声明为final类型呢?这是应为
对于普通变量而言,它的作用域是停留在该方法内,当方法执行结束,该局部变量也就随之消失,但内部类则可能产生隐式的“闭包”,
闭包使得局部变量脱离它所在的方法继续存在。
看下面的例子:
public class ClosureTest {
public static void main(String[] args) {
final String str = "java" ;//定义局部变量
new Thread(new Runnable(){
public void run(){
for(int i=0 ;i<100 ; i++){
System.out.println(str + i);
try {
Thread.sleep(100) ;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start() ;
}
}
程序首先定义了一个局部变量str,当程序main方法执行完成后,str的生命周期就结束了,但是子线程还没有执行结束,而且子线程要
使用main中的局部变量str,这个时候就扩大了str的作用范围。这个时候如果str没有被修饰为final类型,而可以随便改变,则会引起
极大的混乱,因此java编译器要求所有的内部类访问的局部变量必须使用final修饰符修饰。
final可以修饰变量,被final修饰的变量被赋值之后,不能对它重新赋值
final可以修饰方法,被final修饰的方法不能被重写
final可以修饰类,被final修饰的类不能派生子类
1)final修饰变量
被final修饰的变量必须显式指定初始值,且只能在下面3个位置指定初始值:
定义final实例变量时指定初始值
在非静态代码块中为final实例变量指定初始值
在构造器中为final实例变量指定初始值
使用javap工具编译final修饰的实例变量可以看到,变量的赋值都是放在构造方法中执行的,理论上说final修饰的实例变量的初始化只能
在构造方法中进行。
同样对于final修饰的类变量也必须指定初始值,且只能在2个地方指定初始值:
定义final的类变量时指定初始值
在静态代码块中为final类变量指定初始值
使用javap工具编译final修饰的类变量可以看到,变量的赋值都是放在静态代码块中执行的,理论上说final修饰的类变量的初始化只能
在静态代码块中进行。
2)宏变量
请先看http://blog.youkuaiyun.com/xxqi1229/article/details/6905188中的关于"类变量与实例变量的初始化过程"下面的程序是在原来的程序
上面稍作修改后的。
请看程序:
public class Price {
final static Price INSTANCE = new Price(2.8) ;
final static double initPrice = 20 ;//此变量声明为final类型,此处和源程序不同。
double currentPrice ;
public Price(double discount){
currentPrice = initPrice - discount ;
}
}
测试用例如下:
public class Test {
public static void main(String[] args) {
System.out.println(Price.INSTANCE.currentPrice);
Price p = new Price(2.8) ;
System.out.println(p.currentPrice);
}
}
此时程序的运行结果为:
17.2
17.2
(注:原来程序的运行结果为-2.8 17.2)
为什么加上final关键字后结果就变为17.2呢?对于一个用final修饰的变量而言,如果定义该final变量的时就指定了初始值,而且这个
初始值在编译的时候就可以确定下来,那么这个变量将不再是一个变量,系统会将其当成一个“宏变量”处理,也就是说,在程序中出现
该变量的地方全部把它当成对应的值来处理。
什么叫值在编译时可以确定下来。比如说String s = "aaaaaa";此时在编译的时候就可以确定下来为“aaaaaa”,但是如果定义为
String s = str ;str也是一个字符串类,则此时编译的时候就不能确定s的值是多少。
对于上面的程序而言,由于使用了final关键字修饰initPrice类变量,因此Price类的构造器中执行currentPrice=initPrice-discount;的
时候会将initPrice直接替换为20.因此,执行该代码效果相当于currentPrice=20-discount;
如果把程序中定义initPrice的代码改为如下:
static double d = 20 ;
final static double initPrice = d ;
则程序运行结果为:
-2.8
17.2
这事因为在编译initPrice的时候不能确定initPrice的值。
3)宏替换
对于一个final变量,不管它是类变量,实例变量还是局部变量,只要定义了该变量时使用了final修饰符修饰,并在定义该final类变量的
时候指定了初始值,而且该初始值可以在编译的时候就确定下来,那么这个final变量本质上已经不再是一个变量,而是一个直接量(宏)。
请看如下程序:
public class Test {
public static void main(String[] args) {
String str1 = "疯狂";
String str2 = "java" ;
final String str3 = "疯狂java";
final String str4 = "疯狂" + "java";
final String str5 = str1 + str2 ;
final String str6 = "疯狂" + String.valueOf("java") ;
System.out.println(str3 == str4);
System.out.println(str3 == str5);
System.out.println(str3 == str6);
}
}
程序运行结果为:
true
false
false
str3 == str4的结果为true,这是因为str4在编译的时候就确定了值。
str3 == str5的结果为false,这事因为str5在编译的时候不能确定值,因为str1和str2不是final类型的,如果把str1和str2定义为final
类型,则str5在编译的时候就可以确定值,此时结果就应该为true。
str3 == str6的结果为false,这是因为表达式中使用了String的方法valueOf,因此在编译的时候无法确定str6的值。
注:对于宏变量只有在定义该变量的时候赋初始值才会有“宏变量”的效果,在非静态代码块、构造方法中为fianl实例变量指定初始值则不会
有这样的效果。
请看下面的程序:
public class FinalInitTest {
final String str1 ;
final String str2 ;
final String str3 = "java" ;
{//非静态代码块中初始化s
str1 = "java" ;
}
public FinalInitTest(){//构造方法中初始化
str2 = "java" ;
}
public void display(){
System.out.println(str1 + str1 == "javajava");
System.out.println(str2 + str2 == "javajava");
System.out.println(str3 + str3 == "javajava");
}
public static void main(String[] args){
FinalInitTest fit = new FinalInitTest() ;
fit.display() ;
}
}
程序的运行结果为:
false
false
true
从程序结果中可以看出,str1和str2没有达到宏替换的母的,这是因为str1在初始化在飞静态代码块中,str2的初始化在构造方法中。
只有在定义final变量的时给定初始值(编译的时候可以确定值)才会达到宏替换的效果。静态final类型的变量也一样。
3)final变量在内部类中的使用
程序需要在匿名内部类中使用局部变量,则局部变量必须声明为final类型。否则会编译错误。为什么必须声明为final类型呢?这是应为
对于普通变量而言,它的作用域是停留在该方法内,当方法执行结束,该局部变量也就随之消失,但内部类则可能产生隐式的“闭包”,
闭包使得局部变量脱离它所在的方法继续存在。
看下面的例子:
public class ClosureTest {
public static void main(String[] args) {
final String str = "java" ;//定义局部变量
new Thread(new Runnable(){
public void run(){
for(int i=0 ;i<100 ; i++){
System.out.println(str + i);
try {
Thread.sleep(100) ;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start() ;
}
}
程序首先定义了一个局部变量str,当程序main方法执行完成后,str的生命周期就结束了,但是子线程还没有执行结束,而且子线程要
使用main中的局部变量str,这个时候就扩大了str的作用范围。这个时候如果str没有被修饰为final类型,而可以随便改变,则会引起
极大的混乱,因此java编译器要求所有的内部类访问的局部变量必须使用final修饰符修饰。