深入Final关键字

来源:互联网 发布:淘宝刷佣金是真是假 编辑:程序博客网 时间:2024/05/22 23:27

1.类的final变量和普通变量有什么区别?

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

1 0
原创粉丝点击