Java 进阶 -- final 解析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011033906/article/details/87176084

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

1. final 关键字的基本用法

在Java中,final 关键字可以用来修饰类、方法和变量(包括成员变量和局部变量)。下面就从这三个方面来了解一下 final 关键字的基本用法。

1.1 修饰类

当用 final 修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用 final 进行修饰。final 类中的成员变量可以根据需要设为 final,但是要注意 final 类中的所有成员方法都会被隐式地指定为 final 方法。

在这里插入图片描述
在使用 final 修饰类的时候,要注意谨慎选择,除非这个类真的在以后不会用来继承或者出于安全的考虑,尽量不要将类设计为 final类。

1.2 修饰方法

下面这段话摘自《Java编程思想》第四版第143页:

“使用 final 方法的原因有两个:

  1. 把方法锁定,以防任何继承类修改它的含义
  2. 第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用 final 方法进行这些优化了。

因此,如果只有在想明确禁止 该方法在子类中被覆盖的情况下才将方法设置为final的。

注:类的 private 方法会隐式地被指定为final方法。

1.3 修饰变量

修饰变量是 final 用得最多的地方,也是本文接下来要重点阐述的内容。首先了解一下 final 变量的基本语法:

对于一个 final 变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象

举个例子:
在这里插入图片描述

2. 深入理解 final 关键字

2.1 类的 final 变量和普通变量有什么区别?

当用 final 作用于类的成员变量时,成员变量(注意是类的成员变量,局部变量只需要保证在使用之前被初始化赋值即可)必须在定义时或者构造器中进行初始化赋值,而且 final 变量一旦被初始化赋值之后,就不能再被赋值了。

那么 final 变量和普通变量到底有何区别呢?下面请看一个例子:

public class Test {
    public static void main(String[] args)  {
        String a = "hello2"; 
        final String b = "hello";
        String d = "hello";
        String c = b + 2; 
        String e = d + 2;
        System.out.println((a == c));
        System.out.println((a == e));
    }
}

Result:

true
false

这里面就是 final 变量和普通变量的区别了, 当 final 变量是 基本数据类型 以及 String 类型时,如果在编译期间能知道它的确切值,则编译器会把它当做编译期常量使用。也就是说在用到该 final 变量的地方,相当于直接访问的这个常量,不需要在运行时确定。

因此在上面的一段代码中,由于变量 bfinal 修饰,因此会被当做编译器常量,所以在使用到 b 的地方会直接将变量 b 替换为它的 值。而对于变量 d 的访问却需要在运行时通过链接来进行。

不过要注意,只有在编译期间能确切知道 final 变量值的情况下,编译器才会进行这样的优化,比如下面的这段代码就不会进行优化:

public class Test {
    public static void main(String[] args)  {
        String a = "hello2"; 
        final String b = getHello();
        String c = b + 2; 
        System.out.println((a == c));
 
    }
     
    public static String getHello() {
        return "hello";
    }
}

这段代码的输出结果为 false

2.2 被 final 修饰的引用变量指向的对象内容可变吗?

在上面提到被 final 修饰的引用变量一旦初始化赋值之后就不能再指向其他的对象,那么该引用变量指向的对象的内容可变吗?看下面这个例子:

public class Test {
    public static void main(String[] args)  {
        final MyClass myClass = new MyClass();
        System.out.println(++myClass.i);
 
    }
}
 
class MyClass {
    public int i = 0;
}

这段代码可以顺利编译通过并且有输出结果,输出结果为 1。这说明引用变量被 final 修饰之后,虽然不能再指向其他对象,但是它指向的对象的内容是可变的

2.3 finalstatic

很多时候会容易把 staticfinal 关键字混淆,static 作用于成员变量用来表示只保存一份副本,而 final 的作用是用来保证变量不可变。看下面这个例子:

public class Test {
    public static void main(String[] args)  {
        MyClass myClass1 = new MyClass();
        MyClass myClass2 = new MyClass();
        System.out.println(myClass1.i);
        System.out.println(myClass1.j);
        System.out.println(myClass2.i);
        System.out.println(myClass2.j);
 
    }
}
 
class MyClass {
    public final double i = Math.random();
    public static double j = Math.random();
}

运行这段代码就会发现,每次打印的两个j值都是一样的,而i的值却是不同的。从这里就可以知道final和static变量的区别了。

2.3 匿名内部类中使用的外部局部变量为什么只能是 final 变量?

参见海子博文中 Java内部类详解中的解释.

转载自博客

https://www.cnblogs.com/dolphin0520/p/3736238.html – 海子

猜你喜欢

转载自blog.csdn.net/u011033906/article/details/87176084
今日推荐