Java中的final变量

对与Java中的final变量,java编译器是进行了优化的。每个使用了final类型变量的地方都不会通过连接而进行访问。比如说Test类中使用了Data类中一个final的int数字fNumber=77,这时候,java编译器会将77这个常数编译到Test类的指令码或者常量池中。这样,每次Test类用到fNumber的时候,不会通过引用连接到Data类中进行读取,而是直接使用自己保存在类文件中的副本。
用程序说话:
Test.java:

public class Test{
   public static void main(String[] args){
      System.out.println(Data.fNumber);
   }
}


Data.java:

public class Data{
public static final int fNumber=77;
}

执行命令和结果:

Microsoft Windows XP [版本 5.1.2600]
(C) 版权所有 1985-2001 Microsoft Corp.

C:\Documents and Settings\zangmeng>cd ..

C:\Documents and Settings>cd ..

C:\>javac Test.java

C:\>java Test
77

C:\>


这时候,我们更改Data.java的内容:
public class Data{
public static final int fNumber=777;
}

然后执行如下命令:

C:\>javac Data.java

C:\>java Test
77

C:\>

这里我们看到,虽然Data.java中的fNumber已经更改为777,而且已经重新编译了,但是因为编译器把fNumber的副本保存Test类中,所以在重新编译Test类的前,Test类一直把fNumber认为是77而不是777。下面我们变异Test.java,再执行,看看结果。

C:\>javac Test.java

C:\>java Test
777

C:\>
这时候,我们看到,重新编译的Test类将新的777数值封装到了自己类中。

整个过程如下:
Microsoft Windows XP [版本 5.1.2600]
(C) 版权所有 1985-2001 Microsoft Corp.

C:\Documents and Settings\zangmeng>cd ..

C:\Documents and Settings>cd ..

C:\>javac Test.java

C:\>java Test
77
//在这里改变了Data.java的内容
C:\>javac Data.java

C:\>java Test
77

C:\>javac Test.java

C:\>java Test
777

static和final的使用:

package inchestofeet;

/*public class inchestofeet {
      //初始化静态方法
      private static final int inches=12;
      //构造方法
      protected inchestofeet(){}
      public static double convert(double in){
      return (in/inches);
      }
      //在调用任何方法之前先调用静态块
      static {System.out.println("这是首先执行的静态块");}
public static void main(String args[]){
//初始化变量
      double inch=66;
      double feet=inchestofeet.convert(inch);
      System.out.println(inch+"inch is"+feet+"feet.");

}
}*/
public class inchestofeet{
int height;
      inchestofeet(int h){
      height=h;
      }
public static void main(String args[]){
final inchestofeet box1=new inchestofeet(25);
      //final修饰过的成员变量就是常量,内存不能更改
      //box1=new inchestofeet(32); 出错!!box1不能重写,这样将改变内存状态
      box1.height=32;
}

}

final变量的定义:

       final变量的定义本身并不复杂,就是变量一经初始化就不能再指向其它对象,在c++中它是一个const指针,而不是指向const变量的指针。const指针的意思是说它只能一直指向初始化时的那个地址,但那个地址中对象本身是可以修改的,而指向const变量的指针是说所指对象本身是不能修改的。

如:final StringBuffer sb = new StringBuffer("Axman");
     sb = new StringBuffer("Sager");//错误,sb不能再指向其它对象.
    sb.append(" was changed!"); //sb指向的地象本身可以修改.

final变量初始化:

        final变量可以在任何可以被始化的地方被始化,但只能被初始化一次。一旦被初始化后就不能再次赋值(重新指向其它对象),作为成员变量一定要显式初始化,而作为临时变量则可以只定义不初始化(当然也不能引用)。即使是作为一个类中的成员变量,也还可以在初始化块中初始化,所以“其初始化可以在两个地方,一是其定义处,二是在构造函数中,两者只能选其一"是错误的。

final设计不变类:

       作为成员变量时,final字段可以设计不变类,是不变类的一个必要条件,但不是一个充要条件。至少可以保证字段不会以setXXX()这样的方式来改变,但无法保证字段本身不被修改(除非字段本身也是不变类)。

对于方法参数的final变量:

       对于方法参数的变量定义为final,90%以上的文章都说"当你在方法中不需要改变作为参数的对象变量时,明确使用final进行声明,会防止你无意的修改而影响到调用方法外的变量。"这个是不正确的。
    如果是说重新赋值,那么:
    public static void test(int[] x){
    x = new int[]{1,2,3};
    }

    int[] out = new int[]{4,5,6};
    test(out);
    System.out.println(out[0]);
    System.out.println(out[1]);
    System.out.println(out[2]);
    调用test(out);无论如何也不会影响到外面变量out,你加不加final根本没有意义。final只会强迫方法内
多声明一个变量名而已,即把x = new int[]{1,2,3};改成int y = new int[]{1,2,3}; 其它没有任何实际意义.
    如果说是修改对象本身:
    public static void test(final int[] x){
   x[0] = 100;
   }
    int[] out = new int[]{4,5,6};
    test(out);
    System.out.println(out[0]);
   难道你用final修饰就不可以修改了?所以说对于方法参数中final是为了不影响调用方法外的变量那是错误的,那我们到底为什么要对参数加上final?其实对方法参数加final和方法内变量加上final的作用是相同的,即为了将它们传给内部类回调方法:

abstract class ABSClass{
public abstract void m();
}

现在我们来看,如果我要实现一个在一个方法中匿名调用ABSClass.应该:
public static void test(String s){
     //或String s = "";
   ABSClass c = new ABSClass(){
    public void m(){
      int x = s.hashCode();

      System.out.println(x);

    }
   };
   //其它代码.
}
注意这里,一般而言,回调方法基本上是在其它线程中调用的.如果我们在上面的
     ABSClass c = new ABSClass(){
    public void m(){
       int x = s.hashCode();

       System.out.println(x);

    }
   };
       后面直接调用c.m();应该是没有意义的.但这不重要,重要的是只要有可能是在其它线程中调用,那我们就必须为s保存引用句柄.

       我们先来看GC工作原理,JVM中每个进程都会有多个根,每个static变量,方法参数,局部变量,当然这都是指引用类型.基础类型是不能作为根的,根其实就是一个存储地址.GC在工作时先从根开始遍历它引用的对象并标记它们,如此递归到最末梢,所有根都遍历后,没有被标记到的对象说明没有被引用,那么就是可以被回收的对象(有些对象有finalized方法,虽然没有引用,但JVM中有一个专门的队列引用它们直到finalized方法被执行后才从该队列中移除成为真正没有引用的对象,可以回收,这个与本主题讨论的无关,包括代的划分等以后再说明).这看起来很好.

       但是在内部类的回调方法中,s既不可能是静态变量,也不是方法中的临时变量,也不是方法参数,它不可能作为根,在内部类中也没有变量引用它,它的根在内部类外部的那个方法中,如果这时外面变量重指向其它对象,则这个对象就失去了引用,可能被回收,而由于内部类回调方法大多数在其它线程中执行,可能还要在回收后还会继续访问它.这将是什么结果?

       而使用final修饰符不仅会保持对象不会改变,而且编译器还会持续维护这个对象在回调方法中的生命周期.所以这才是final变量和final参数的根本意义.


你可能感兴趣的:(java,windows,String,Microsoft,Class,编译器)