深入Final关键字
2016-07-19 11:25
369 查看
1.类的final变量和普通变量有什么区别?
结果是:true false true
结果是:false
2.被final修饰的引用变量指向的对象内容可变吗?
结果是:1
3.final和static
结果是:0.05766174928636447
0.7614580638924601
0.9017659828502815
0.7614580638924601
运行这段代码就会发现,每次打印的两个j值都是一样的,而i的值却是不同的。
4.匿名内部类中使用的外部局部变量为什么只能是final变量?
这段代码会被编译成两个class文件:Test.class和Test1.class。默认情况下,编译器会为匿名内部类和局部内部类起名为Outter1.class。默认情况下,编译器会为匿名内部类和局部内部类起名为Outterx.class(x为正整数)。
根据上图可知,test方法中的匿名内部类的名字被起为 Test$1。
上段代码中,如果把变量a和b前面的任一个final去掉,这段代码都编译不过。我们先考虑这样一个问题:
当test方法执行完毕之后,变量a的生命周期就结束了,而此时Thread对象的生命周期很可能还没有结束,那么在Thread的run方法中继续访问变量a就变成不可能了,但是又要实现这样的效果,怎么办呢?Java采用了 复制 的手段来解决这个问题。
5.关于final参数的问题
关于网上流传的”当你在方法中不需要改变作为参数的对象变量时,明确使用final进行声明,会防止你无意的修改而影响到调用方法外的变量“这句话,我个人理解这样说是不恰当的。
因为无论参数是基本数据类型的变量还是引用类型的变量,使用final声明都不会达到上面所说的效果。
看这个例子就清楚了:
上面这段代码好像让人觉得用final修饰之后,就不能在方法中更改变量i的值了。殊不知,方法changeValue和main方法中的变量i根本就不是一个变量,因为java参数传递采用的是值传递,对于基本类型的变量,相当于直接将变量进行了拷贝。所以即使没有final修饰的情况下,在方法内部改变了变量i的值也不会影响方法外的i。
再看下面这段代码:
package com.wisdom.effective.java;
结果是:holleword
所以关于网上流传的final参数的说法,我个人不是很赞同。
把final去掉
结果仍然是:holleword
public class FinalTest { public static void main(String[] args) { String a = "hello2"; final String b = "hello"; String d = "hello"; String c = b + 2; String e = d + 2; System.out.println((a == c)); System.out.println((a == e)); System.out.println((a.equals(e))); System.out.println("结果是:-true------false--true"); } }
结果是:true false true
public class FinalTest { public static void main(String[] args) { String a = "hello2"; final String b = getHello(); String c = b + 2; System.out.println((a == c)); System.out.println("结果是:--false--"); } public static String getHello() { return "hello"; } }
结果是:false
原因:
第一个结果是true,第二个结果是false。这里面就是final变量和普通变量的区别,当final变量是基本数据类型以及String类型时,付过在编译期间能知道它的确切值,则编译会把它当做编译期常量使用。也就是说在用到该final变量的时候,相当于直接访问这个常量,不需要再运行时确定。这种和C语言中的宏替换有点儿像。因此变量b被final修饰,因此会被当做编译期常量,所以在使用到b的地方直接将变量b替换为它的值。而对于变量d的访问却需要在运行时通过连接来进行。注意在只有在编译期间能确切知道final变量的情况下才会进行这样的优化。2.被final修饰的引用变量指向的对象内容可变吗?
package com.wisdom.effective.java; public class FinalTest_2 { public static void main(String[] args) { final MyClass myClass = new MyClass(); System.out.println(++myClass.i); } } class MyClass { public int i = 0; }
结果是:1
原因:
这段代码可以顺利编译通过并且有输出结果,输出结果为1。这说明引用变量被final修饰之后,虽然不能再指向其他对象,但是它指向的对象的内容是可变的。3.final和static
package com.wisdom.effective.java; public class FinalTest_2 { public static void main(String[] args) { MyClass myClass1 = new MyClass(); MyClass myClass2 = new MyClass(); System.out.println(myClass1.i); System.out.println(myClass1.j); System.out.println(myClass2.i); System.out.println(myClass2.j); } } class MyClass { public final double i = Math.random(); public static double j = Math.random(); }
结果是:0.05766174928636447
0.7614580638924601
0.9017659828502815
0.7614580638924601
运行这段代码就会发现,每次打印的两个j值都是一样的,而i的值却是不同的。
原因是:
很多时候会容易把static和final关键字混淆,static作用于成员变量用来表示只保存一份副本,而final的作用是用来保证变量不可变。4.匿名内部类中使用的外部局部变量为什么只能是final变量?
public class Test { public static void main(String[] args) { } public void test(final int b) { final int a = 10; new Thread(){ public void run() { System.out.println(a); System.out.println(b); }; }.start(); } }
这段代码会被编译成两个class文件:Test.class和Test1.class。默认情况下,编译器会为匿名内部类和局部内部类起名为Outter1.class。默认情况下,编译器会为匿名内部类和局部内部类起名为Outterx.class(x为正整数)。
根据上图可知,test方法中的匿名内部类的名字被起为 Test$1。
上段代码中,如果把变量a和b前面的任一个final去掉,这段代码都编译不过。我们先考虑这样一个问题:
当test方法执行完毕之后,变量a的生命周期就结束了,而此时Thread对象的生命周期很可能还没有结束,那么在Thread的run方法中继续访问变量a就变成不可能了,但是又要实现这样的效果,怎么办呢?Java采用了 复制 的手段来解决这个问题。
5.关于final参数的问题
关于网上流传的”当你在方法中不需要改变作为参数的对象变量时,明确使用final进行声明,会防止你无意的修改而影响到调用方法外的变量“这句话,我个人理解这样说是不恰当的。
因为无论参数是基本数据类型的变量还是引用类型的变量,使用final声明都不会达到上面所说的效果。
看这个例子就清楚了:
上面这段代码好像让人觉得用final修饰之后,就不能在方法中更改变量i的值了。殊不知,方法changeValue和main方法中的变量i根本就不是一个变量,因为java参数传递采用的是值传递,对于基本类型的变量,相当于直接将变量进行了拷贝。所以即使没有final修饰的情况下,在方法内部改变了变量i的值也不会影响方法外的i。
再看下面这段代码:
package com.wisdom.effective.java;
public class FinalTest_4 { public static void main(String[] args) { MyClass_3 myClass = new MyClass_3(); StringBuffer buffer = new StringBuffer("hello"); myClass.changeValue(buffer); System.out.println(buffer.toString()); } } class MyClass_3 { void changeValue(final StringBuffer buffer) { buffer.append("world"); } }
结果是:holleword
原因是:
运行这段代码就会发现输出结果为 helloworld。很显然,用final进行修饰并没有阻止在changeValue中改变buffer指向的对象的内容。有人说假如把final去掉了,万一在changeValue中让buffer指向了其他对象怎么办。有这种想法的朋友可以自己动手写代码试一下这样的结果是什么,如果把final去掉了,然后在changeValue中让buffer指向了其他对象,也不会影响到main方法中的buffer,原因在于java采用的是值传递,对于引用变量,传递的是引用的值,也就是说让实参和形参同时指向了同一个对象,因此让形参重新指向另一个对象对实参并没有任何影响。所以关于网上流传的final参数的说法,我个人不是很赞同。
把final去掉
package com.wisdom.effective.java; public class FinalTest_4 { public static void main(String[] args) { MyClass_3 myClass = new MyClass_3(); StringBuffer buffer = new StringBuffer("hello"); myClass.changeValue(buffer); System.out.println(buffer.toString()); } } class MyClass_3 { void changeValue(StringBuffer buffer) { buffer.append("world"); } }
结果仍然是:holleword
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树