final关键字详解(超详细)参考(Thinking in java)

  注:文章内容由网络及相关书籍整理而来,如此只为共享知识,给予帮助。

final 数据:

可以在编译时执行的计算,减轻了一些运行时的负担。基本数据类型在定义时,final修饰则必须赋值。对于基本 数据类型,final使数值恒定不变,而对于对象引用,则使得该引用不能指向其他对象,但对象本身可以修改!

空白final:

空白final是指被声明为final但为给定初值的域。 无论什么情况,编译器都确保空白final在使用前必须被初始化。一个类的final域就可以做到根据对象而有所不同,却又保持其恒定不变的特性。


final参数:

 java允许在参数列表中以声明的方式将参数声明为final。这意味着 你无法在方法中更改引用所指向的对象!


final方法:原因:1.把方法锁定,以防止任何继承类修改它的定义。想确保在继承中使方法行为保持不变,并且不会 被覆盖。

final类:当讲某个类整体定义成final ,就表明你不打算继承该类,而且也不允许别人这么做。换句话说,你对该类的设计永远不需要做任何变动,不希望它有子类。final类中的方法 被隐式的指定为final。方法不能被覆盖。 private方法被默认的声明为final。不能被覆盖。


final关键字详解

  
     final在Java中并不常用,然而它却为我们提供了诸如在C语言中定义常量的功能,不仅如此,final还可以让你控制你的变量、方法或者是一个类是否可被覆写或继承等功能,这些特点使final在Java中拥有了一个不可或缺的地位,也是学习Java时必须要知道和掌握的关键字之一。

一、final变量

  当你在类中定义变量时,在其前面加上final关键字,那便是说,这个变量一旦被初始化便不可改变,这里不可改变的意思对基本类型来说是其值不可变,而对于对象变量来说其引用不可再变。

    其初始化可以在两个地方,一是其定义处,也就是说在final变量定义时直接给其赋值,二是在构造函数中。这两个地方只能选其一,要么在定义时给值,要么在构造函数中给值,不能同时既在定义时给了值,又在构造函数中给另外的值。下面这段代码演示了这一点:

import java.util.List;
import java.util.ArrayList;
import java.util.LinkedList; 

public class Bat{
    final PI=3.14;          //在定义时便给址值
    final int i;            //因为要在构造函数中进行初始化,所以此处便不可再给值
    final List list;        //此变量也与上面的一样
    Bat(){
        i=100;
        list=new LinkedList();
    }
    Bat(int ii,List l){
        i=ii;
        list=l;
    }
    public static void main(String[] args){
        Bat b=new Bat();
        b.list.add(new Bat());
        //b.i=25;
        //b.list=new ArrayList();
        System.out.println("I="+b.i+" List Type:"+b.list.getClass());
        b=new Bat(23,new ArrayList());
        b.list.add(new Bat());
        System.out.println("I="+b.i+" List Type:"+b.list.getClass());
    }
}    
    
    此程序很简单的演示了final的常规用法。在这里使用在构造函数中进行初始化的方法,这使你有了一点灵活性。如Bat的两个重载构造函数所示,第一个缺省构造函数会为你提供默认的值,重载的那个构造函数会根据你所提供的值或类型为final变量初始化。然而有时你并不需要这种灵活性,你只需要在定义时便给定其值并永不变化,这时就不要再用这种方法。在main方法中有两行语句注释掉了,如果你去掉注释,程序便无法通过编译,这便是说,不论是i的值或是 list的类型,一旦初始化,确实无法再更改。然而b可以通过重新初始化来指定i的值或list的类型,输出结果中显示了这一点:

I=100 List Type:class java.util.LinkedList
I=23 List Type:class java.util.ArrayList

   还有一种用法是定义方法中的参数为final,对于基本类型的变量,这样做并没有什么实际意义,因为基本类型的变量在调用方法时是传值的,也就是说你可以在方法中更改这个参数变量而不会影响到调用语句,然而对于对象变量,却显得很实用,因为对象变量在传递时是传递其引用,这样你在方法中对对象变量的修改也会影响到调用语句中的对象变量,当你在方法中不需要改变作为参数的对象变量时,明确使用final进行声明,会防止你无意的修改而影响到调用方法。
另外方法中的内部类在用到方法中的参变量时,此参变也必须声明为final才可使用,如下代码所示:

public class INClass{
   void innerClass(final String str){
        class IClass{
            IClass(){
                System.out.println(str);
            }
        }
        IClass ic=new IClass();
    }
  public static void main(String[] args){
      INClass inc=new INClass();
      inc.innerClass("Hello");
  }
}

二、final方法

   将方法声明为final,那就说明你已经知道这个方法提供的功能已经满足你要求,不需要进行扩展,并且也不允许任何从此类继承的类来覆写这个方法,但是继承仍然可以继承这个方法,也就是说可以直接使用。另外有一种被称为inline的机制,它会使你在调用final方法时,直接将方法主体插入到调用处,而不是进行例行的方法调用,例如保存断点,压栈等,这样可能会使你的程序效率有所提高,然而当你的方法主体非常庞大时,或你在多处调用此方法,那么你的调用主体代码便会迅速膨胀,可能反而会影响效率,所以你要慎用final进行方法定义。

三、final类

   当你将final用于类身上时,你就需要仔细考虑,因为一个final类是无法被任何人继承的,那也就意味着此类在一个继承树中是一个叶子类,并且此类的设计已被认为很完美而不需要进行修改或扩展。对于final类中的变量,你可以定义其为final,也可以不是final。而对于方法,由于所属类为final的关系,自然也就成了final型的。你也可以明确的给final类中的方法加上一个final,但这显然没有意义。

   下面的程序演示了final方法和final类的用法:
final class fin{
    final String str="final Data";
    public String str1="non final data";

    final public void print(){
        System.out.println("final method.");
    }
    public void what(){
        System.out.println(str+"\n"+str1);
    }
}
public class FinalDemo {   //extends final 无法继承 
    public static void main(String[] args){
        fin f=new fin();
        f.what();
        f.print();
    }
}
   从程序中可以看出,final类与普通类的使用几乎没有差别,只是它失去了被继承的特性。final方法与非final方法的区别也很难从程序行看出,只是记住慎用。

四、final在设计模式中的应用

   在设计模式中有一种模式叫做不变模式,在Java中通过final关键字可以很容易的实现这个模式,在讲解final变量时用到的程序Bat.java就是一个不变模式的例子。如果你对此感兴趣,可以参考阎宏博士编写的《Java与模式》一书中的讲解。

 final 关键字:
    1 final数据:对于基本类型前加以final修饰,表示被修饰的变量为常数,不可以修改。一个既是static又是final的字段表示只占据一段不能改变的存储空间。
     final用于对象应用时,final使应用恒定不变。一旦引用被初始化指向一个对象,就无法再把它改为指向另一个对象。
class Value {
      int i = 1;
    }
public class FinalData {
      // Can be compile-time constants
      final int i1 = 9;
      static final int VAL_TWO = 99;
      //i1和VAL_TWO是编译期常量此类常量必须是基本数字类型,必须是以关键字        fianl进行修饰,定义该类型的常量的时候,必须给顶初值
     
      public static final int VAL_THREE = 39;
      //注意VAL_THREE的书写格式,通常把带有final,和static关键字修饰的常量        一大写,并以"_"隔开
     
      final int i4 = (int)(Math.random()*20);
      static final int i5 = (int)(Math.random()*20);
      //i4,i5并不是便宜期常量,而是执行期常量,因为他们的初值只有在执行的        时候才能确定,所以i5并没有大写,
      //i5是静态常量,而且i4不是,这就可以体现二者的不同,从测试的打印语        句来看,fd1和fd2的i4的值不同
      //而fd1和fd2的i5都是相同的,因为i5是static类型,只有在装载class的时        候才进行初始化,不会在每次
      //产生新的对象的时候都再初始化一次
     
      Value v1 = new Value();
      final Value v2 = new Value();
      static final Value v3 = new Value();
      // Arrays:
      final int[] a = { 1, 2, 3, 4, 5, 6 };

      public void print(String id) {
        System.out.println(
          id + ": " + "i4 = " + i4 +
          ", i5 = " + i5);
      }
      public static void main(String[] args) {
        FinalData fd1 = new FinalData();
        //! fd1.i1++; // Error: final类型的基本类型是不可以改变的
        fd1.v2.i++; // v2是final类型但是可以改变起属性的值,但是不能将
                        v2重新指向另一个对象

        fd1.v1 = new Value(); // OK -- not final
        for(int i = 0; i < fd1.a.length; i++)
          fd1.a[i]++; // Object isn't constant!
        //! fd1.v2 = new Value(); // Error: Can't
        //! fd1.v3 = new Value(); // change reference
        //! fd1.a = new int[3];

        fd1.print("fd1");
        System.out.println("Creating new FinalData");
        FinalData fd2 = new FinalData();
        fd1.print("fd1");
        fd2.print("fd2");
      }
}

     2 final方法:一是把方法锁定,也许你希望确保某个函数的行为在继承的过程中保持不变,而且无法被覆写,以防止继承类修改它的含义,第二个原因可能是效率,如果你将某个函数申明为final类型,等于告诉编译器将所有对此函数的调用转化为行内调用。类中所有的private方法都隐式地指定为是final。因为你无法取用private方法,当然就无从覆写,你可以将关键字final加于private方法上,但是这样做不会带来任何额外的意义.java中的所有方法,除了被申明为final者,皆采用后期绑定,这么做是告诉编译器动态绑定是不需要的,于是就产生效率叫佳的代码,但这么做并不会给你的程序带来整体效率的提升
class WithFinals {
    // Identical to "private" alone:申明为final类型即是该方法不能被覆写,
    //将关键字加于private函数上,实际上并没有任何的意义
    private final void f() {
        System.out.println("WithFinals.f()");
    }
    //即使被覆写了,也就相当于子类重新定义了一个方法而已
    // Also automatically "final":当一个方法申明为private的时候,自动为final类型
    private void g() {
        System.out.println("WithFinals.g()");
    }
}

class OverridingPrivate extends WithFinals {
    private final void f() {
        System.out.println("OverridingPrivate.f()");
    }

    private void g() {
        System.out.println("OverridingPrivate.g()");
    }
}

class OverridingPrivate2 extends OverridingPrivate {
    public final void f() {
        System.out.println("OverridingPrivate2.f()");
    }

    public void g() {
        System.out.println("OverridingPrivate2.g()");
    }
}

public class FinalOverridingIllusion {
    public static void main(String[] args) {
        OverridingPrivate2 op2 = new OverridingPrivate2();
        op2.f();
        op2.g();
        // You can upcast:
        OverridingPrivate op = op2;
        // But you can't call the methods:
        // ! op.f();
        // ! op.g();
        // Same here:
        WithFinals wf = op2;
        // ! wf.f();
        // ! wf.g();
    }
}

     3 final参数:对于基本类型的变量,这样做并没有什么实际意义,因为基本类型的变量在调用方法时是传值的,也就是说你可以在方法中更改这个参数变量而不 会影响到调用语句,然而对于对象变量,却显得很实用,因为对象变量在传递时是传递其引用,这样你在方法中对对象变量的修改也会影响到调用语句中的对象变 量,当你在方法中不需要改变作为参数的对象变量时,明确使用final进行声明,会防止你无意的修改而影响到调用方法。
     4 final类:当将某个类的整体定义为final时,就表明了该类不允许被继承。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值