浅谈java中final数据
来源:互联网 发布:装多客软件多少钱 编辑:程序博客网 时间:2024/06/05 15:26
引言
在说说final之前,我们先了解下类被加载到内存中所需要的几个步骤,一个类被加载到内存中需要经过如下几个阶段:
编译: java文件必须编译成Class文件(也称为字节码文件)才可以被JVM识别,JVM并不关心Class的来源是什么语言,只要它符合一定的结构,就可以在Java中运行。
装载:查找和导入必要的Class文件,在Java虚拟机执行过程中,只有他需要一个类的时候,才会调用类加载器来加载这个类,并不会在开始运行时加载所有的类。
链接: 检查载入Class文件数据的正确性、静态变量分配内存空间,并设置默认值、将符号引用转成直接引用
初始化:对类的静态变量,静态代码块执行初始化操作
了解完成上面后,我们进入final,一个被final修饰的数据会有两种情况的存在:
- 永不改变的编译时常量
- 在运行时被初始化的值
一、编译时的常量
对于编译时常量这种情况就在于编译成Class文件后,常量的值就已经存在于编译时常量池中了。当我们使用该常量时,类就不需要为它进行任何初始化操作,我们直接拿来用就行了。要成为编译时的常量必须要满足以下这点:
- 必须关键字final修饰
- 数据类型必须基本数据类型或String类型
- 对这个常量进行定义的时候,必须对其进行赋值
下面的例子在编译的时候就会生成编译时的常量:
package test;public class Test { private final String valueOne = "aaaaaaa"; public final static String VALUE_TWO = "bbbbbb";}
我们通过javap -c -v -p Test 对Class文件进行反汇编,并只列出了关键的部分:
public class test.Test SourceFile: "Test.java" minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPERConstant pool: .... #21 = Utf8 aaaaaaa .... #25 = Utf8 bbbbbb{ private final java.lang.String valueOne; flags: ACC_PRIVATE, ACC_FINAL ConstantValue: String aaaaaaa public static final java.lang.String VALUE_TWO; flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL ConstantValue: String bbbbbb ....}
我们可以看出编译后常量的值aaaaaaa 和 bbbbbb已经存在于Constant pool中。以后在类装载的时候就不需要为这两个变量进行初始化的操作了。
注意,我们发现这两个常量字段被ConstantValue属性所修饰,根据深入理解JVM这本书中所描述:
ConstantValue属性的作用是通知虚拟机自动为静态变量赋值,只有被static修饰的变量才可以使用这项属性。
也就是说static修饰的变量才可以使用ConstantValue的属性,可是我们上面valueOne变量明明是非static修饰,难道java7以后对ConstantValue属性进行了重新定义也允许非static的final变量使用该属性? 这点暂时还没搞懂,后再查查资料。
编译时的常量还有个特点:常量在编译阶段会存入调用它的类的常量池中,所以不会触发定义常量的类的初始化。
在Test类中定义了一个常量:
public class Test { public final static String VALUE_TWO = "bbbbbb";}
在Main类中使用Test类的常量:
public class Main { public static void main(String[] args) { System.out.println(Test.VALUE_TWO); }}
我们知道一个类要使用另一个类时,必须要把该类装载进来,并进行初始操作,方可以使用,但是如果调用编译时的常量则不需要。在编译阶段会将此常量的值“bbbbbb”存储到了调用它的类Main 的常量池中,对常量Test.VALUE_TWO 的引用实际上转化为了Main 类对自身常量池的引用。也就是说,实际上Main 的Class文件之中并没有Test 类的符号引用入口,这两个类在编译成Class文件后就不存在任何联系了。
我们通过javap 反汇编Main字节码文件就可以直观的看出:
public class test.Main SourceFile: "Main.java" minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPERConstant pool: ..... #4 = String #25 // bbbbbb ...... #25 = Utf8 bbbbbb{ public test.Main(); public static void main(java.lang.String[]); flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #4 // String bbbbbb 5: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 9: 0 line 10: 8 LocalVariableTable: Start Length Slot Name Signature 0 9 0 args [Ljava/lang/String;}
bbbbbb值直接存储在了Main的常量池中了,所以跟Test 类没有任何关系。
二、运行时被初始化
我们并不能因为某数据是final的就认为在编译的时候可以知道它的值了,就如下面:
public class Test { public static final int INT_1 = new Random().nextInt(); public final int i1 = new Random().nextInt();}
我们说过要成为编译时常量的条件类型必须基本类型或String,很明显上面的例子不符合这个条件。上面的例子只有当在运行时才会进行初始化。 INT_1 变量在类被装载时已经被初始化,每次调用该变量数值都是一样的。而i1的初始化的阶段则在于创建实例对象时,所以每个对象的i1值都是不一样的:
public static void main(String[] args) { Test test=new Test(); System.out.println(test.i1); test=new Test(); System.out.println(test.i1); test=new Test(); System.out.println(test.i1); }
打印后的值分别是2145916241 、 465177656 、 -680509643。
空白final
java 允许生成空白final,空白final是指被声明为final但又未给定初始化值的域。 无论什么情况,编译器都要确保空白final在使用前必须被初始化,所以我们可以不必在声明final变量时就给定值,可以在后面再进行赋值,这给关键字final的使用上提供了更大的灵活性。
- 对于static修饰的空白final字段,我们只能在static代码块中进行赋值:
public class Test { public static final String INT_1; static { INT_1 = "aaaaaaaa"; }}
- 对于非static修饰的空白final字段,我们可以在动态代码块 或 构造器中进行赋值:
public class Test { public final String i1; { i1="bbbbbbbbbb"; }}
或
public class Test { public final String i1; public Test(){ i1="bbbbbbbb"; }}
- 对于局部变量,编译器也允许先声明后赋值:
public static void main(String[] args) { final int a; a=4; System.out.println(a);}
不管哪种情况编译器总要保证被final修饰的变量,在使用前必须先进行赋值操作,才可编译通过。
参考:
《Thinking in Java》
- 浅谈java中final数据
- 浅谈Java中final,finalized,finally
- 浅谈Java中final,finalized,finally
- 浅谈Java中final,finalized,finally
- 浅谈Java中final,finalized,finally
- 浅谈Java中final,finalized,finally
- 浅谈Java中final,finalized,finally
- 浅谈Java中final,finalized,finally(转)
- 浅谈Java中final,finalized,finally
- java中final和static关键字浅谈
- 浅谈Java中final与static
- 浅谈java中final关键字的作用
- 浅谈Java Final关键字
- 浅谈Java的final关键字
- 浅谈java关键字static、final
- 浅谈Java中的final关键字
- 浅谈方法中参数加上final
- Java中的final,finalized,finally浅谈
- leetcode:暴力枚举法之Combinations
- 微信 模版消息 (六)
- Android 关于内存泄漏的检测与优化
- Oracle:日期函数
- 详解ASP.NET配置文件Web.config
- 浅谈java中final数据
- mybatis入门
- BZOJ1984: 月下“毛景树”
- 工资支撑不了房价,旧金山26%的软件工程师欲逃离
- 计算机网络一些知识
- 库克:AI和AR是苹果未来的核心技术
- 利用ffmpeg+opencv实现画中画
- 嵌套矩形——DAG上的dynamic programing
- Oracle:转换函数