疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

Java之final修饰符


 

 
final修饰符是Java语言中比较常见的一个修饰符,我们经常用的String类就是一个final类。 
final的用法主要有以下几种:
 
final可以修饰变量,被final修饰的变量被赋初始值之后,不能对它重新赋值。
final可以修饰方法,被final修饰的方法不能被重写。
final可以修饰类,被final修饰的类不能派生子类。
final修饰的变量
 
被final修饰的实例变量必须显式指定初始值,而且只能在如下3个位置指定初始值。
 
定义final实例变量时指定初始值
在非静态初始化块中为final实例变量指定初始值
在构造器中为final实例变量指定初始值
对于普通实例变量,Java程序可以对它执行默认的初始化,也就是将实例变量的值指定为默认的初始值0或null,但对于final实例变量,则必须显式指定初始值。
 
public class Test {
    // 定义final实例变量时指定初始值
    final String var1 = "hello";
    final String var2;
    final String var3;
 
    // 在初始化块中为var2赋值
    {
        var2 = "itmyhome";
    }
 
    // 在构造器中为var3赋值
    public Test() {
        this.var3 = "hi";
    }
 
    public static void main(String[] args) {
        Test t = new Test();
        System.out.println(t.var1);
        System.out.println(t.var2);
        System.out.println(t.var3);
    }
}
 
上面程序中定义了3个final实例变量var1、var2和var3,分别为在定义var1时为其赋初始值,在初始化块中为var2指定初始值,在构造器中为var3指定初始值。
 
对于final类变量而言,同样必须显式指定初始值,而且final类变量只能在2个地方指定初始值
 
定义final类变量时指定初始值
在静态初始化块中为final类变量指定初始值
public class Test {
    // 定义final类变量时赋初始值
    final static String var1 = "hello";
    final static String var2;
 
    // 在静态初始化块中赋值
    static {
        var2 = "itmyhome";
    }
 
    public static void main(String[] args) {
        Test t = new Test();
    }
}
 
上面程序中定义了2个final类变量var1和var2,在定义var1时为其赋初始值,在静态初始化块中为var2指定初始值。
 
执行“宏替换”的变量
 
对一个final变量,不管它是类变量、实例变量,还是局部变量,只要定义该变量时使用了final修饰符修饰,并在定义该final类变量时指定了初始值,而且该初始值可以在编译时就被确定下来,那么这个final变量本质上已经不再是变量,而是相当于一个直接量。
 
final修饰符的一个重要用途就是定义“宏变量”,当定义final变量时就为该变量指定了初始值,而且该初始值可以在编译时就确定下来,那这个final变量本质上就是一个“宏变量”,编译器会把程序中所有用到该变量的地方直接替换成该变量的值。
 
public class Test {
    public static void main(String[] args) {
        String str1 = "itmyhome";
        String str2 = "it" + "myhome";
        System.out.println(str1 == str2); // true
 
        String s1 = "it";
        String s2 = "myhome";
        String str3 = s1 + s2;
        System.out.println(str1 == str3); // false
    }
}
 
上面程序中分别判断str1和str2是否相等,以及str1和str3是否相等。str1是一个普通的字符串直接量“itmyhome”,str2的值是两个字符串直接量进行拼接运算,由于编译器可以在编译阶段就确定str2的值为“itmyhome”,所以系统会让str2直接指向字符串池中缓存中的“itmyhome”字符串。由此可见,str1==str2将输出true。
 
对于str3而已,它的值由s1和s2进行连接运算后得到。由于s1、s2只是两个普通变量,编译器不会执行“宏替换”,因此编译器无法在编译时确定str3的值,不会让str3指向字符串池中缓存中的“itmyhome”,由此可见,str1==str3将输出false。
 
为了让str1==str3输出true也很简单,只要编译器可以对s1、s2两个变量进行“宏替换”。这样编译器即可在编译阶段就确定str3的值,程序改为如下形式
public class Test {
    public static void main(String[] args) {
        String str1 = "itmyhome";
        String str2 = "it" + "myhome";
        System.out.println(str1 == str2); // true
 
        final String s1 = "it";
        final String s2 = "myhome";
        String str3 = s1 + s2;
        System.out.println(str1 == str3); // true
    }
}
 
对于实例变量而言,除了可以在定义该变量时赋初始值之外,还可以在非静态初始化块、构造器中对它赋初始值,而且在这3个地方指定初始值的效果基本一样。但对于final实例变量而言,只有在定义该变量时指定初始值才会有“宏变量”的效果,在非静态初始化块、构造器中为final实例变量指定初始值则不会有这种效果,示例如下:
 
 
public class Test {
    // 定义3个final实例变量
    final String str1;
    final String str2;
    final String str3 = "Java";
 
    // str1、str2分别在非静态初始化块、构造器中初始化
    {
        str1 = "Java";
    }
 
    public Test() {
        str2 = "Java";
    }
 
    public void print() {
        System.out.println((str1 + str1) == "JavaJava"); // false
        System.out.println((str2 + str2) == "JavaJava"); // false
        System.out.println((str3 + str3) == "JavaJava"); // true
    }
 
    public static void main(String[] args) {
        Test t = new Test();
        t.print();
    }
}
 
上面程序中定义了3个final实例变量,但只有str3在定义该变量时指定了初始值,另外的str1、str2分别在非静态初始化块、构造器中指定初始值,因此系统不会对str1、str2执行“宏替换”,但会对str3执行“宏替换”。
 
于此类似的是,对于普通类变量,在定义时指定初始值、在静态初始化块中赋初始值的效果基本一样。但对于final类变量而已,只有在定义final类变量时指定初始值,系统才会对该final类变量执行“宏替换”
 
public class Test {
    // 定义两个final类变量
    static final String str1;
    static final String str2 = "Java";
 
    // str1在静态初始化块中初始化
    static {
        str1 = "Java";
    }
 
    public static void main(String[] args) {
        System.out.println((str1 + str1) == "JavaJava"); // false
        System.out.println((str2 + str2) == "JavaJava"); // true
    }
}
 
上面程序中定义了2个final类变量,但只有str2在定义该变量时指定了初始值,str1则在静态初始化块中指定初始值,因此系统不会对str1执行“宏替换”,但会对str2执行“宏替换”