Java关键字介绍之final

来源:互联网 发布:全球域名注册量排名 编辑:程序博客网 时间:2024/05/21 22:23
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://zhangjunhd.blog.51cto.com/113473/20664
总结关键字final的用法。
author: ZJ 07-3-16
Blog: [url]http://zhangjunhd.blog.51cto.com/[/url]
Java中声明属性、方法和类时,可使用关键字final来修饰。final变量即为常量,只能赋值一次;final方法不能被子类重写;final类不能被继承。
1final成员
声明 final 字段有助于优化器作出更好的优化决定,因为如果编译器知道字段的值不会更改,那么它能安全地在寄存器中高速缓存该值。final 字段还通过让编译器强制该字段为只读来提供额外的安全级别。
 
1.1关于final成员赋值
1)在java中,普通变量可默认初始化。但是final类型的变量必须显式地初始化。
 
2final 成员能且只能被初始化一次。
 
3final成员必须在声明时(在final变量定义时直接给其赋值)或者在构造函数中被初始化,而不能在其它的地方被初始化。
示例1 Bat.java
publicclass Bat {
    finaldoublePI = 3.14; // 在定义时赋值
    finalinti;// 因为要在构造函数中进行初始化所以此处便不可再赋值
    final List<Bat>list;// 因为要在构造函数中进行初始化所以此处便不可再赋值
 
    Bat() {
       i = 100;
       list =new LinkedList<Bat>();
    }
 
    Bat(int ii, List<Bat> l) {
       i = ii;
       list = l;
    }
 
    publicstaticvoid main(String[] args) {
       Bat b = new Bat();
       b.list.add(new Bat());
       // b.i=25;
       // b.list=new ArrayList<Bat>();
       System.out.println("I=" + b.i +" List Type:" + b.list.getClass());
       b = new Bat(23,new ArrayList<Bat>());
       b.list.add(new Bat());
       System.out.println("I=" + b.i +" List Type:" + b.list.getClass());
    }
}
 
结果:
I=100 List Type:class java.util.LinkedList
I=23 List Type:class java.util.ArrayList
 
main方法中有两行语句注释掉了,如果你去掉注释,程序便无法通过编译,这便是说,不论是i的值或是list的类型,一旦初始化,确实无法再更改。然而b可以通过重新初始化来指定i的值或list的类型。
 
1.2 final引用字段的无效初始化
要正确使用final字段有点麻烦,对于其构造子能抛出异常的对象引用来说尤其如此。因为 final字段在每个构造器中必须只初始化一次,如果 final 对象引用的构造器可能抛出异常,编译器可能会报错,说该字段没有被初始化。编译器一般比较智能化,足以发现在两个互斥代码分支(比如,if...else 块)的每个分支中的初始化恰好只进行了一次,但是它对 try...catch 块通常不会如此“宽容”。
下面这段代码通常会出现问题。
class Thingie {
    publicstatic Thingie getDefaultThingie() {
       returnnew Thingie();
    }
}
 
publicclass Foo {
    privatefinal Thingiethingie;
 
    public Foo() {
       try {
          thingie =new Thingie();
       } catch (Exception e) {
          thingie = Thingie.getDefaultThingie();//Error:The final field thingie may already have been assigned
       }
    }
}
 
你可以这样修改。
publicclass Foo {
    privatefinal Thingiethingie;
 
    public Foo() {
       Thingie tempThingie;
       try {
          tempThingie = new Thingie();
       } catch (Exception e) {
          tempThingie = Thingie.getDefaultThingie();
       }
       thingie = tempThingie;
    }
}
 
1.3关于final成员使用
当你在类中定义变量时,在其前面加上final关键字,那便是说,这个变量一旦被初始化便不可改变,这里不可改变的意思对基本类型来说是其值不可变,而对于对象变量来说其引用不可再变。然而,对象其本身却是可以被修改的,Java并未提供使任何对象恒定不变的途径。这一限制同样适合数组,它也是对象。
示例2
privatefinalintVAL_ONE=9;
privatestaticfinalintVAL_TWO=99;
publicstaticfinalintVAL_THREE=999;
由于VAL_ONE VAL_TOW 是带有编译期数值的final 原始类型,所以它们二者均可以用作编译期常量,并且没有重大区别。VAL_THREE是一种更加典型的对常量进行定义的方式:定义为 public,则可以被用于包之外;定义为 static来强调只有一份;定义为 final 来说明它是一个常量。
final标记的变量即成为常量,但这个常量也只能在这个类的内部使用,不能在类的外部直接使用。但是当我们用public static final共同标记常量时,这个常量就成为全局的常量(一个既是static又是final的字段只占据一段不能改变的存储空间)。而且这样定义的常量只能在定义时赋值,其他地方都不行。
示例3
class Value {
    inti;
 
    public Value(int i) {
       this.i = i;
    }
}
 
publicclass FinalData {
    privatestatic Randomrand =new Random();
 
    private Stringid;
 
    public FinalData(String id) {
       this.id = id;
    }
 
    privatefinalinti4 = rand.nextInt(20);
 
    staticfinalinti5 = rand.nextInt(20);
 
    public String toString() {
       returnid +":" +"i4:" +i4 +", i5=" +i5;
    }
 
    publicstaticvoid main(String[] args) {
       FinalData fd1 = new FinalData("fd1");
       System.out.println(fd1);
       System.out.println("Creating new FinalData");
       FinalData fd2 = new FinalData("fd2");
       System.out.println(fd1);
       System.out.println(fd2);
    }
}
 
结果
fd1:i4:6, i5=3
Creating new FinalData
fd1:i4:6, i5=3
fd2:i4:17, i5=3
示例部分展示了将final数值定义为statici5和非statici4的区别。此区别只有在数值在运行期内被初始化时才会显现,这是因为编译器对编译期数值一视同仁。(并且它们可能因优化而消失。)当你运行程序时,就会看到这个区别。请注意,在fd1fd2 中, i5的值是不可以通过创建第二个FinalData 对象而加以改变的。这是因为它是 static,在装载时已被初始化,而不是每次创建新对象时都初始化。
示例4
class Value {
    inti;
 
    public Value(int i) {
       this.i = i;
    }
}
 
publicclass … {
    private Valuev1=new Value(11);
    privatefinal Valuev2=new Value(22);
    privatestaticfinal Value v3=new Value(33);
  
}
 
publicstaticvoid main(String[] args) {
   
    fd1.v2.i++;// OK--Object isn't constant!
    fd1.v1=new Value(9);//OK--not final
    fd1.v2=new Value(0);//Error:Can't change reference
    fd1.v3=new Value(1);//Error:Can't change reference
   
}
v1 v3 的变量说明了final 引用的意义。正如你在main( )中所看到的,不能因为v2 final 的,就认为你无法改变它的值。由于它是一个引用,final 意味着你无法将v2 再次指向另一个新的对象。
示例5
publicclass … {
    privatefinalint[]a={1,2,3,4,5,6};
   
}
 
publicstaticvoid main(String[] args) {
   
    for(int i=0;i<fd1.a.length;i++)
 fd1.a[i]++;// OK--Object isn't constant!
    fd1.a=newint[3];//Error:Can't change reference   
}
对数组具有同样的意义(可以改变它的值,但不能指向一个新的对象),数组是另一种引用。
 
1.4解决final数组的局限性
尽管数组引用能被声明成 final,但是该数组的元素却不能。这意味着暴露 public final数组字段的或者通过它们的方法将引用返回给这些字段的类都不是不可改变的。
// Not immutable -- the states array could be modified by a malicious
// callerpublic
class DangerousStates {
    privatefinal String[]states =new String[] {"Alabama","Alaska","ect" };
 
    public String[] getStates() {
       returnstates;
    }
}
 
同样,尽管对象引用可以被声明成 final字段,而它所引用的对象仍可能是可变的。如果想要使用 final 字段创建不变的对象,必须防止对数组或可变对象的引用“逃离”你的类。要不用重复克隆该数组做到这一点,一个简单的方法是将数组转变成 List
// Immutable -- returns an unmodifiable List insteadpublic
class SafeStates {
    privatefinal String[]states =new String[] {"Alabama","Alaska","ect" };
 
    privatefinal ListstatesAsList =new AbstractList() {
       public Object get(int n) {
          returnstates[n];
       }
 
       publicint size() {
          returnstates.length;
       }
    };
 
    public List getStates() {
       returnstatesAsList;
    }
}
 
1.5关于final参数使用
还有一种用法是定义方法中的参数为final,对于基本类型的变量,这样做并没有什么实际意义,因为基本类型的变量在调用方法时是传值的,也就是说你可以在方法中更改这个参数变量而不会影响到调用语句,然而对于对象变量,却显得很实用,因为对象变量在传递时是传递其引用,这样你在方法中对对象变量的修改也会影响到调用语句中的对象变量,当你在方法中不需要改变作为参数的对象变量时,明确使用final进行声明,会防止你无意的修改而影响到调用方法。
 
1.6关于内部类中的参数变量
另外方法中的内部类在用到方法中的参变量时,此参数变量必须声明为final才可使用。
示例6 INClass.java
publicclass INClass {
    void innerClass(final String str) {
       class IClass {
          IClass() {
             System.out.println(str);
          }
       }
       IClass ic = new IClass();
    }
 
    publicstaticvoid main(String[] args) {
       INClass inc = new INClass();
       inc.innerClass("Hello");
    }
}
2final方法
2.1final方法用途
1)为了确保某个函数的行为在继承过程中保持不变,并且不能被覆盖(overridding),可以使用final方法。
 
2class中所有的privatestatic方法自然就是final
 
2.2 finalprivate关键字
类中所有的private方法都隐式地指定是final的。由于无法取用private方法,所以也就无法覆盖它。
“覆盖”只有在某方法是基类的接口的一部分时才会出现。即,必须能将一个对象向上转型为它的基本类型并调用相同的方法。如果某方法为private,它就不是基类的接口的一部分。它仅是一些隐藏于类中的代码,只不过是具有相同的名称而已。但如果在导出类以相同的方法生成一个publicprotected或包访问权限方法的话,该方法就不会产生在基类中出现的“仅具有相同名称”的情况。此时,你并没有覆盖该方法,仅是生成了一个新的方法。由于private方法无法触及而且能有效隐藏,所以除了把它看成是因为它所归属的类的组织结构的原因而存在外,其他任何事物都不需要考虑它。
3final
将某个类的整体定义为final时,该类无法被继承。而且由于final类禁止继承,所以final类中所有的方法都隐式指定为final的,因为无法覆盖它们。
final 用于类或方法是为了防止方法间的链接被破坏。例如,假定类 X的某个方法的实现假设了方法 M 将以某种方式工作。将 X M 声明成 final 将防止派生类以这种方式重新定义 M,从而导致 X的工作不正常。尽管不用这些内部相关性来实现 X 可能会更好,但这不总是可行的,而且使用 final 可以防止今后这类不兼容的更改。
4.参考资料
[1]Thinking in Java 3rd
[2] 关于javafinal方法,类,成员变量的讨论
[url]http://www.cublog.cn/u/11794/showart.php?id=117818[/url]
[3] java中的final
[url]http://blog.csdn.net/getmc/archive/[/url]2006/02/20/603878.aspx

本文出自 “子 孑” 博客,请务必保留此出处http://zhangjunhd.blog.51cto.com/113473/20664

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 三岁宝宝爱打人怎么办 1岁宝宝喜欢打人怎么办 ps图层解锁不了怎么办 沈腾结婚马丽怎么办 延长甲没有纸托怎么办 高考第一志愿没录取怎么办 电子画颜料干了怎么办 数字画颜料干了怎么办 彩砂纸画不好了怎么办 宝宝吃了油画棒怎么办 2岁宝宝不爱刷牙怎么办 两岁宝宝不刷牙怎么办 1岁宝宝不爱刷牙怎么办 3岁宝宝不肯刷牙怎么办 20岁没学历迷茫怎么办 四岁了不长头发怎么办 17岁掉头发严重怎么办 头发很油,又少怎么办 25岁头发变稀怎么办 宝宝头发少又黄怎么办 头旋附近头发少怎么办 25岁掉头发严重怎么办 2岁宝宝头发稀少怎么办 掉头发很厉害怎么办吧 头发点的很厉害怎么办 为什么掉头发很厉害怎么办 产后2年脱发严重怎么办 产妇掉头发很厉害怎么办 头发掉了怎么办小妙招 头顶头发掉的厉害怎么办 20岁头发有些少怎么办 头痒头发掉厉害怎么办 头油头痒掉头发怎么办 头发痒掉发严重怎么办 宝宝的脸皴了怎么办 宝宝冬天脸皴了怎么办 白衬衣领子变黄怎么办 白衬衣领子烂了怎么办 玩手机眼睛红了怎么办 吃了发芽的土豆怎么办 散尾竹叶子发黄怎么办