`
yanguz123
  • 浏览: 557196 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

\(^_^)/ Java 关键字解析

 
阅读更多

final 关键字

 

 

 

final--大家都不陌生的单词,可能脑海中浮现的是"不可修改"的概念,但是这个”不可修改“是真的不能修改么?我们探讨下final不为人知的一面。还是分几个方面,final大哥娶的几个老婆搭配讲解,一个是大老婆”类“,二个是二姨太”方法“,三个是三姨太”域“。

    final 类   

final类顾名思义,就是在声明一个类的时候,加上关键字final,这样的类有什么特点呢?第一个想到的是,这个类我不想别人用,这个类不能被继承,还有呢?也许你就可以不知道了,还有就是所有的方法都是隐式的加上了final,那么属性呢?我可以告诉你,属性还是属性,和普通的类的属性没有任何区别。

final方法

final方法可以想象也是不能被覆盖的,你可能想在子类中加上同样的方法,来实现覆盖,可是事实是你行不通,编译器会无法通过编译。这个就很类似于private的方法了,其实从大的归类来看,我们可以把private方法归类到final方法大类中去。在下篇讲到”多态“的时候就会出现这个分类。

告诉大家一个秘密,构造器都会隐式的加了final。

final域

这个可能是最有意思的,因为大多时候,我们也用的最多,也是很多初学者或者老师讲解的时候,给了一句根深蒂固的话,它是不能修改的。但是这个修改我们要重新的定义下,从字面上理解,可能就是说不能动它了,它就在那里放着,我们用就可以了,如果这个属性是基本类型,那没问题,如果他是一个对象的引用呢?我们就要重新的审视这个问题,他是可以被"修改"的,其实这个修改是修改的是对象的内容,而不是改变的是引用。指向的还是原来的引用,只是内存里面的值改变了,从某种意义上说,这种改变可以称为”修改“。

 

 

 

1、final变量

 编译期常量

 运行期常量

 

final基本类型;final对象引用是不可以改变,但是对象里面的变量值是可以改变的;

 

2、空白final

 在使用前必须初始化否则编译不通过,包括final对象

 

3、final方法

 是不可以重载

 

4、final类

 不可以被继续

 

5、final参数

 参数不可以修改,如果是对象则不可以修改引用

 

 

 

 

Java关键字final有“这是无法改变的”或者“终态的”含义,它可以修饰非抽象类、非抽象类成员方法和变量。你可能出于两种理解而需要阻止改变:设计或效率。    

可以修饰的对象:    

final 域   

final 方法   

final 类    

final类不能被继承,没有子类,final类中的方法默认是final的。    

final方法不能被子类的方法覆盖,但可以被继承。    

final成员变量表示常量,只能被赋值一次,赋值后值不再改变。    

final不能用于修饰构造方法。    

注意:父类的private成员方法是不能被子类方法覆盖的,因此private类型的方法默认是final类型的。 

 

1、final类   final类不能被继承,因此final类的成员方法没有机会被覆盖,默认都是final的。在设计类时候,如果这个类不需要有子类,类的实现细节不允许改变,并且确信这个类不会再被扩展,那么就设计为final类。    

2、final方法   如果一个类不允许其子类覆盖某个方法,则可以把这个方法声明为final方法。    

使用final方法的原因有二:    

第一、把方法锁定,防止任何继承类修改它的意义和实现。    

第二、高效。编译器在遇到调用final方法时候会转入内嵌机制,大大提高执行效率。    

3、final变量(常量)   用final修饰的成员变量表示常量,值一旦给定就无法改变!   final修饰的变量有三种:静态变量、实例变量和局部变量,分别表示三种类型的常量。    

从下面的例子中可以看出,一旦给final变量初值后,值就不能再改变了。    

另外,final变量定义的时候,可以先声明,而不给初值,这中变量也称为final空白,无论什么情况,编译器都确保空白final在使用之前必须被初始化。但是,final空白在final关键字final的使用上提供了更大的灵活性,为此,一个类中的final数据成员就可以实现依对象而有所不同,却有保持其恒定不变的特征。    

4、final参数   当函数参数为final类型时,你可以读取使用该参数,但是无法改变该参数的值。    

示例:   public void f1(final int i){ } //i是final类型的,值不允许改变的.

 

 

 

 

Java中的final共有三种用法:  

 

final成员变量  

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

 

其初始化可以在三个地方,  

一是其定义处,也就是说在final变量定义时直接给其赋值,  

二是在构造函数中。而且在Java1.1以前,只能是在定义时给值。  

三是在初如化代码块中{} 或者 static{}  

下面这段代码演示了这一点:更加详细的探讨请参考关于final变量的初始化探讨  

 

import java.util.List;  

import java.util.ArrayList;  

import java.util.LinkedList;  

 

public class Bat {  

 

    final double PI = 3.14; // 在定义时便给址值  

    final int i; // 因为要在构造函数中进行初始化,所以此处便不可再给值  

    final List list; // 此变量也与上面的一样  

 

    Bat() {  

        i = 100;  

        list = new LinkedList();  

    }  

 

    Bat(int ii, List l) {  

        i = ii;  

        llist = l;  

    }  

 

    public static void main(String[] args) {  

        Bat b = new Bat();  

        b.list.add(new Bat()); //引用不可变,但引用指向的内容是可以变的  

        // b.i=25;   syntax error  i是不可变的  

        //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 StringBuffer a=new StringBuffer("immutable");  

  执行如下语句将报告编译期错误:  

 

a=new StringBuffer("");  

  但是,执行如下语句则可以通过编译:  

 

a.append(" broken!");   

 

有人在定义方法的参数时,可能想采用如下形式来阻止方法内部修改传进来的参数对象:  

 

public void method(final  StringBuffer  param)  

 

{  

 

}  

 

实际上,这是办不到的,在该方法内部仍然可以增加如下代码来修改参数对象,其它对象亦如此:  

 

  param.append("a");  

 

 

在看一个例子  

//: FinalData.java  

//The effect of final on fields  

class Value {  

    int i = 1;  

}  

 

public class FinalData {  

    // Can be compile-time constants  

    final int i1 = 9;  

    static final int I2 = 99;  

 

    // Typical public constant:  

    public static final int I3 = 39;  

 

    // Cannot be compile-time constants:  

    final int i4 = (int) (Math.random() * 20);  

    static final int i5 = (int) (Math.random() * 20);  

 

    Value v1 = new Value();  

    final Value v2 = new Value();  

    static final Value v3 = new Value();  

    // ! final Value v4; // Pre-Java 1.1 Error:for no initializer  

 

    // 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: can't change value,因为i1是final的  

        fd1.v2.i++; // Object isn't constant! 这个可以是因为类Value中的i是普通变量  

        fd1.v1 = new Value(); // OK -- not final  

        for (int i = 0; i < fd1.a.length; i++)  

            fd1.a[i]++; // Object isn't constant!  

 

    //下面三个犯同样的错误 Error: Can't change handle  

        // ! fd1.v2 = new Value();  

        // ! fd1.v3 = new Value(); //  

        // ! fd1.a = new int[3];  

        fd1.print("fd1");  

        System.out.println("Creating new FinalData");  

 

        FinalData fd2 = new FinalData();  

        fd1.print("fd1");  

        fd2.print("fd2");  

    }  

}  

输出结果(由于上面用了随机函数,所以输出结果可能不一样,但是规律是一样的):  

fd1: i4 = 4, i5 = 6  

Creating new FinalData  

fd1: i4 = 4, i5 = 6  

fd2: i4 = 10, i5 = 6  

结果分析:fd1与fd2的i4发现值总是不一样,但i5的值却是一样的,这是为什么呢?  

 我们可以发现定义i4与i5惟一的区别是后者多了一个static,由于final变量和static变量可以说  

 都只能存一个数据,他们惟一的区别是static变量是属于类的,是类变量,只会被加载一次。  

 请参考该文章:Java_父类与子类的加载原理  

 而i4虽然是final的,但是它仍是个普通变量,属于实例变量,每创建一个对象还是会被加载一次,又  

 由于是个随机函数,所以最终的结果不一样。  

所以:一定要区分final与static final的细微差别。  

 

使用final关键字修饰一个变量时,是指引用变量不能变,引用变量所指向的对象中的内容还是可以改变的。  

 

 

 

 

此程序很简单的演示了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进行方法定义。  

 

类中所有的private方法从某种意义上讲都是属于final的,因为他们在其它地方没法覆盖,你可以在一个private方法前加final修饰符,但没什么意义。  

 

 

final类  

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

下面的程序演示了final方法和final类的用法:  

 

final class finals {  

    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) {  

        finals f = new finals();  

        f.what();  

        f.print();  

    }  

}  

 

从程序中可以看出,final类与普通类的使用几乎没有差别,只是它失去了被继承的特性。final方法与非final方法的区别也很难从程序行看出,只是记住慎用。  

 

附注:  

final在设计模式中的应用  

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

 

 

 

 

 

 

1.final实例域,(用final声明类成员变量)

 

 3基本数据类型(声明的成员变量为基本数据类型)

         这种成员变量只能在两处初始化,一处在声明他的地方,另一处在构造函数中,而且两者只能选一,然后就不能在其他处修改了。

 

 3类(声明的成员变量为类)

        这种成员变量的初始化情况和基本数据类型类似,也只能在那两处之一初始化,且不能在其他处修改。但是,类型为类的成员变量可以被修改。

Java代码  

package lib;  

 

public class FinalFiekdTest {  

 

    /** 

     * @param args 

     */  

    private final int num;  

    private final FinalField ffd ;    

    FinalFiekdTest(){  

        num = 1;  

        ffd = new FinalField();  

    }  

 

    public static void main(String[] args) {  

        // TODO Auto-generated method stub    

        FinalFiekdTest fft = new FinalFiekdTest();  

        fft.ffd.setX(9);  

    }  

}  

class FinalField{  

    private int x;  

    FinalField(){}  

    public void setX(int i){  

        x = i;  

    }  

}  

2.final类,(用final声明的类)

 

 3这种类不能被继承;

 3当这种类作为其他类的成员变量时,(只要没被final修饰)可以被多次定义(他不同于final实例域);

3.final方法,(用final声明的方法)

 

3final方法不能被重构,只能被子类直接使用;

3另外有一种被称为inline的机制,它会使你在调用final方法时,直接将方法主体插入到调用处,而不是进行例行的方法调用,例如保存断点,压栈等,这样可能会使你的程序效率有所提高,然而当你的方法主体非常庞大时,或你在多处调用此方法,那么你的调用主体代码便会迅速膨胀,可能反而会影响效率,所以你要慎用final进行方法定义。

4.定义方法中的参数为final

 

还有一种用法是定义方法中的参数为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是指这个变量不可再次被赋值,而变量所代表的对象,其具有的任何属性都可以变化;

 

1)final成员变量

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

 

2)final方法

          将方法声明为final,那就说明你已经知道这个方法提供的功能已经满足你要求,不需要进行扩展,并且也不允许任何从此类继承的类来覆写这个方法,但是继承仍然可以继承这个方法,也就是说可以直接使用。

          另外有一种被称为inline的机制,它会使你在调用final方法时,直接将方法主体插入到调用处,而不是进行例行的方法调用,例如保存断点,压栈等,这样可能会使你的程序效率有所提高,然而当你的方法主体非常庞大时,或你在多处调用此方法,那么你的调用主体代码便会迅速膨胀,可能反而会影响效率,所以你要慎用final进行方法定义。

           类中所有的private方法从某种意义上讲都是属于final的,因为他们在其它地方没法覆盖,你可以在一个private方法前加final修饰符,但没什么意义。

 

3)final类

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

     final类与普通类的使用几乎没有差别,只是它失去了被继承的特性。final方法与非final方法的区别也很难从程序行看出,只是记住慎用。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics