许多程序设计语言都有自己的办法告诉编译器某个数据是“常数”。常数主要应用于下述两个方面:
  (1) 编译期常数,它永远不会改变
  (2) 在运行期初始化的一个值,我们不希望它发生变化
  对于编译期的常数,编译器(程序)可将常数值“封装”到需要的计算过程里。也就是说,计算可在编译期间提前执行,从而节省运行时的一些开销。在Java中,这些形式的常数必须属于基本数据类型(Primitives),而且要用final关键字进行表达。在对这样的一个常数进行定义的时候,必须给出一个值。
无论static还是final字段,都只能存储一个数据,而且不得改变。
  若随同对象句柄使用final,而不是基本数据类型,它的含义就稍微让人有点儿迷糊了。对于基本数据类型,final会将值变成一个常数;但对于对象句柄,final会将句柄变成一个常数。进行声明时,必须将句柄初始化到一个具体的对象。而且永远不能将句柄变成指向另一个对象。然而,对象本身是可以修改的。Java对此未提供任何手段,可将一个对象直接变成一个常数(但是,我们可自己编写一个类,使其中的对象具有“常数”效果)。这一限制也适用于数组,它也属于对象。
下面是演示final字段用法的一个例子:

  1. // The effect of final on fields
  2.  
  3. class Value {
  4. int i = 1;
  5. }
  6.  
  7. public class FinalData {
  8. // Can be compile-time constants
  9. final int i1 = 9;
  10. static final int I2 = 99;
  11. // Typical public constant:
  12. public static final int I3 = 39;
  13. // Cannot be compile-time constants:
  14. final int i4 = (int)(Math.random()*20);
  15. static final int i5 = (int)(Math.random()*20);
  16.  
  17. Value v1 = new Value();
  18. final Value v2 = new Value();
  19. static final Value v3 = new Value();
  20. //! final Value v4; // Pre-Java 1.1 Error:
  21. // no initializer
  22. // Arrays:
  23. final int[] a = { 1, 2, 3, 4, 5, 6 };
  24.  
  25. public void print(String id) {
  26. System.out.println(
  27. id + ": " + "i4 = " + i4 +
  28. ", i5 = " + i5);
  29. }
  30. public static void main(String[] args) {
  31. FinalData fd1 = new FinalData();
  32. //! fd1.i1++; // Error: can't change value
  33. fd1.v2.i++; // Object isn't constant!
  34. fd1.v1 = new Value(); // OK -- not final
  35. for(int i = 0; i < fd1.a.length; i++)
  36. fd1.a[i]++; // Object isn't constant!
  37. //! fd1.v2 = new Value(); // Error: Can't
  38. //! fd1.v3 = new Value(); // change handle
  39. //! fd1.a = new int[3];
  40.  
  41. fd1.print("fd1");
  42. System.out.println("Creating new FinalData");
  43. FinalData fd2 = new FinalData();
  44. fd1.print("fd1");
  45. fd2.print("fd2");
  46. }
  47. }

  由于i1和I2都是具有final属性的基本数据类型,并含有编译期的值,所以它们除了能作为编译期的常数使用外,在任何导入方式中也不会出现任何不同。I3是我们体验此类常数定义时更典型的一种方式:public表示它们可在包外使用;Static强调它们只有一个;而final表明它是一个常数。注意对于含有固定初始化值(即编译期常数)的fianl static基本数据类型,它们的名字根据规则要全部采用大写。也要注意i5在编译期间是未知的,所以它没有大写。
不能由于某样东西的属性是final,就认定它的值能在编译时期知道。i4和i5向大家证明了这一点。它们在运行期间使用随机生成的数字。例子的这一部分也向大家揭示出将final值设为static和非static之间的差异(文末对此结合实例作出了更加详细的解答)。只有当值在运行期间初始化的前提下,这种差异才会揭示出来。因为编译期间的值被编译器认为是相同的。这种差异可从输出结果中看出:

  1. fd1: i4 = 15, i5 = 9
  2. Creating new FinalData
  3. fd1: i4 = 15, i5 = 9
  4. fd2: i4 = 10, i5 = 9

  注意对于fd1和fd2来说,i4的值是唯一的,但i5的值不会由于创建了另一个FinalData对象而发生改变。那是因为它的属性是static,而且在载入时初始化,而非每创建一个对象时初始化。
  从v1到v4的变量向我们揭示出final句柄的含义。正如大家在main()中看到的那样,并不能认为由于v2属于final,所以就不能再改变它的值。然而,我们确实不能再将v2绑定到一个新对象,因为它的属性是final。这便是final对于一个句柄的确切含义。我们会发现同样的含义亦适用于数组,后者只不过是另一种类型的句柄而已。将句柄变成final看起来似乎不如将基本数据类型变成final那么有用。

2. 空白final
  Java允许我们创建“空白final”,它们属于一些特殊的字段。尽管被声明成final,但却未得到一个初始值。无论在哪种情况下,空白final都必须在实际使用前得到正确的初始化。而且编译器会主动保证这一规定得以贯彻。然而,对于final关键字的各种应用,空白final具有最大的灵活性。举个例子来说,位于类内部的一个final字段现在对每个对象都可以有所不同,同时依然保持其“不变”的本质。下面列出一个例子:

  1. // "Blank" final data members
  2.  
  3. class Poppet { }
  4.  
  5. public class BlankFinal {
  6. final int i = 0; // Initialized final
  7. final int j; // Blank final
  8. final Poppet p; // Blank final handle
  9. // Blank finals MUST be initialized
  10. // in the constructor:
  11. BlankFinal() {
  12. j = 1; // Initialize blank final
  13. p = new Poppet();
  14. }
  15. BlankFinal(int x) {
  16. j = x; // Initialize blank final
  17. p = new Poppet();
  18. }
  19. public static void main(String[] args) {
  20. BlankFinal bf = new BlankFinal();
  21. }
  22. }

  现在强行要求我们对final进行赋值处理——要么在定义字段时使用一个表达 式,要么在每个构建器中。这样就可以确保final字段在使用前获得正确的初始化。

3. final自变量
  Java允许我们将自变量设成final属性,方法是在自变量列表中对它们进行适当的声明。这意味着在一个方法的内部,我们不能改变自变量句柄指向的东西。如下所示:

  1. // Using "final" with method arguments
  2.  
  3. class Gizmo {
  4. public void spin() {}
  5. }
  6.  
  7. public class FinalArguments {
  8. void with(final Gizmo g) {
  9. //! g = new Gizmo(); // Illegal -- g is final
  10. g.spin();
  11. }
  12. void without(Gizmo g) {
  13. g = new Gizmo(); // OK -- g not final
  14. g.spin();
  15. }
  16. // void f(final int i) { i++; } // Can't change
  17. // You can only read from a final primitive:
  18. int g(final int i) { return i + 1; }
  19. public static void main(String[] args) {
  20. FinalArguments bf = new FinalArguments();
  21. bf.without(null);
  22. bf.with(null);
  23. }
  24. }

  注意此时仍然能为final自变量分配一个null(空)句柄,同时编译器不会捕获它。这与我们对非final自变量采取的操作是一样的。
  方法f()和g()向我们展示出基本类型的变量为final时会发生什么情况:我们只能读取自变量,不可改变它。

static final 与 final 与 static 的区别

我们先看个小例子,代码如下:

  1. public final class EmployeeModel {
  2. private final String firstName;
  3. private final String lastName;
  4. //按上面方式定义,运行正常
  5. //进行如下改动
  6. //private static final String firstName;
  7. //private static final String lastName;
  8. //结果出错,显示“空白终态字段 firstName 可能尚未初始化”
  9. //再进行相应改动
       //private static String firstName;
  10. //private static String lastName;
  11. //结果运行正常
  12. public EmployeeModel(String fn,String ln)
  13. {
  14. firstName=fn;
  15. lastName=ln;
  16. }
  17.  
  18. public String getFirstName(){
  19. return firstName;
  20. }
  21.  
  22. public String getLastName(){
  23. return lastName;
  24. }
  25. }

对于以上3中情况结合上知识作出以下解释说明:

第一种情况:

  java 中final定义的成员变量并不一定必须在定义的时候初始化,final本身的含义是一旦赋值以后不可更改变量的值,final定义的变量只要在实际的类的对象在使用前得到初始化即可

故可在构造方法中进行初始化(2.空白final中的实例亦可说明该问题)

第二种情况:

  进行如下修改后便不会出现问题:

  1. private static final String firstName;
  2. private static final String lastName;
  3. static{
  4. firstName = "";
  5. lastName = "";
  6. }

  原因是:static是独立于对象实例的,static的final变量,要么在静态代码块里面进行初始化,要么在定义的时候进行初始化。(JAVA没有实现静态的构造方法)
  而非static的final变量,要么在构造函数进行初始化,要么在实例代码块进行初始化。

  因此final如果没有在定义时初始化,则一定要在构造器里面初始化,该种情况将其声明成static后,它不能在构造器里面初始化,转而要用静态代码块才能初始化。(静态方法只能初始化静态变量)

第三种情况:

  定义为static类型变量后,若未进行初始化,JAVA将自己对其进行默认的初始化,如int类型默认为0.

java中的final与static的更多相关文章

  1. 理解Java中的final和static关键字

    回顾这两个关键字前,先考虑一个问题: Static变量存储在JVM中的位置,或者说static变量是如何被加载的? JVM会把类的静态方法和静态变量在类加载的过程中读入方法区(Method Area) ...

  2. [转] Java中的final、static、this、super

    final 关键字 final关键字主要用在三个地方:变量.方法.类. 对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改:如果是引用类型的变量,则在对其初始化之后便 ...

  3. Java中的final和static

    final final可以用在类.方法.变量上. 1.final用在类上,表明当前类它不能被继承,没有子类. 2.final用在方法上,表明当前方法不能被override,不能被重写. 3.final ...

  4. 浅析Java中的final关键字(转载)

    自http://www.cnblogs.com/dolphin0520/p/3736238.html转载 一.final关键字的基本用法 在Java中,final关键字可以用来修饰类.方法和变量(包括 ...

  5. 关于Java中的final关键字

    Java中的final关键字是用来限制用户行为的,说白了,就是用来限制我们这些程序员的.final可以用来修饰:变量.方法.类. 1)Java final variable final用来修饰变量时, ...

  6. 深入理解Java中的final关键字

    Java中的final关键字非常重要,它可以应用于类.方法以及变量.这篇文章中我将带你看看什么是final关键字?将变量,方法和类声明为final代表了什么?使用final的好处是什么?最后也有一些使 ...

  7. 浅析Java中的final关键字

    浅析Java中的final关键字 谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来 ...

  8. (转)深入理解Java中的final关键字

    转自:http://www.importnew.com/7553.html Java中的final关键字非常重要,它可以应用于类.方法以及变量.这篇文章中我将带你看看什么是final关键字?将变量,方 ...

  9. [转载]浅析Java中的final关键字

    浅析Java中的final关键字 谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来 ...

随机推荐

  1. [iOS Animation]-CALayer 显示动画

    显式动画 如果想让事情变得顺利,只有靠自己 -- 夏尔·纪尧姆 上一章介绍了隐式动画的概念.隐式动画是在iOS平台创建动态用户界面的一种直接方式,也是UIKit动画机制的基础,不过它并不能涵盖所有的动 ...

  2. laravel memcached使用

    当第一次使用cache时,想用 memcached 的方式,但是它直接报错: 说明你的php没安装 memcached 这个扩展,在ubuntu下直接 sudo apt-get install mem ...

  3. js 截取字符串里的ip

    var ip_reg = /([\d\.]*)/ig; ip = ip_reg.exec(str); return ip; ip_reg会截取 '(' 开始的字符串,中间包含数字和 '.' .

  4. 使用LIBUSB实现和自定义通讯设备通讯--MFC代码在末尾

    LIBUSB是一款简单好用的USB通讯开发库,一般HID设备用该库通讯能大大降低开发周期,使用如下,首先需要为设备安装驱动 在libusb的bin目录下有一个inf_wirzed.exe的文件,该文件 ...

  5. ie6,ie7兼容性总结

    摘自: http://www.cnblogs.com/li0803/archive/2009/08/22/1552094.html 其实浏览器的不兼容,我们往往是各个浏览器对于一些标准的定义不一致导致 ...

  6. php 中文切割字符串长度

    function msubstr($str, $start=0, $length, $charset="utf-8", $suffix=true) { if(function_ex ...

  7. HDU 1255 覆盖的面积 ——(线段树+扫描线)

    又做了一题扫描线以后对节点的覆盖标记理解的更加深刻了. 代码如下: #include <stdio.h> #include <algorithm> #include <s ...

  8. Linux之文件备份与恢复

    文件备份与恢复 1.dump命令 dump命令用于备份ext2或者ext3文件系统.可将目录或整个文件系统备份至指定的设备,或备份成一个大文件. 语法 dump(选项)(参数) 选项 -0123456 ...

  9. Android px、dp和sp单位区别

    [转]http://www.cnblogs.com/bjzhanghao/archive/2012/11/06/2757300.html 在调整布局的时候,某些控件的大小需要设置单位: px: 即像素 ...

  10. DataTable.DataRow的复制

    经常遇到这种错误,“此行已属于另一个表”的错误,导致这个错误的语句如下: dtPriceTable.Rows.InsertAt(aDataRow,i); 或者 dtPriceTable.Rows.Ad ...