Java final关键字详解
final关键字的作用
-
修饰变量:被它修饰的变量不可改变,一旦赋了初始值,就不能被重新赋值
被final修饰的常量名称,所有字母要大写
修饰方法:该方法不可被子类重写,但是可以被重载
修饰类:修饰的类不能被继承,比如:Math、String等类
修饰变量
final修饰变量相比修饰方法、修饰类,是使用最多的情况。
final修饰基本数据类型的变量:变量一旦初始化之后其数值就不能变更
final修饰对象引用类型的变量:变量一旦初始化之后就不能让其再指向其他的对象
代码示例如下:
可以看出,变量name和person都不能重新赋值了
修饰方法
下面这段话摘自《Java编程思想》第四版第143页:
“使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了。“
因此,如果只想明确禁止该方法在子类中被覆盖的情况下才将方法设置为final的。
注:类的private方法会隐式地被指定为final方法。
修饰类
前面有提到过,final修饰类时,该类就不能被继承。
代码示例如下:
如果某个类你不想被让其他类继承,就可以用final修饰。final类中的成员变量可以根据需要用final修饰,不过final类的所有的成员方法都会被隐式的指定为final修饰
使用final修饰类时要慎重考虑下,该类是否真的不会被用来继承或者出于安全的考虑,否则尽量不要用final修饰类
final和private的区别
-
final修饰的类可以访问;
private不可以修饰外部类,但可以修饰内部类(其实把外部类私有化是没有意义的)
-
final修饰的方法不可以被子类重写;
private修饰的方法表面上是可以被子类重写的,其实是不可以的,子类是看不到父类的私有方法的
-
final修饰的变量只能在显示初始化或者构造函数初始化的时候赋值一次,以后不允许修改;
private修饰的变量,也不允许直接被子类或一个包中的其他类访问或修改,但是他可以通过set和get方法对其进行改值和取值
深入理解final关键字
1、final变量和普通变量的区别
final修饰类的成员变量时,成员变量必须在定义时或者构造器中进行初始化赋值,一定被初始化赋值后,就不能再赋值了。
先看段代码:
public class FinalTest {
public static void main(String[] args) {
String a = "java11";
final String b = "java";
String c = "java";
String d = b + 11;
String e = c + 11;
System.out.println((a == d));
System.out.println((a == e));
}
}
输出结果:
true
false
为什么输出结果是这样的?我们来分析分析:
final修饰变量b,在编译期就能确定变量b的确切值,则编译器会把变量b当做常量来使用。只要用到变量b的地方,就相当于直接访问变量b的常量值,而不需要在运行期才知道具体的变量值。所以我们可以知道,变量b的值在编译期间就能知道,是java11,这样
System.out.println((a == d))
输出true也就好理解了变量c没有用final来修饰,需要在运行期调用时才能知道具体的值,这样
System.out.println((a == e))
输出false
这里要注意的是,只有在编译期能明确知道final变量值的情况下,编译器才会进行这样的优化。
如下这段代码则不会进行优化:
public class FinalTest2 {
public static void main(String[] args) {
String a = "java11";
final String b = getLanguage();
String c = b + 11;
System.out.println((a == c));
}
public static String getLanguage() {
return "java";
}
}
输出结果:
false
2、被final修饰的引用变量指向的对象内容可变吗?
文章上面的内容有提到过:final修饰对象引用类型的变量时,该变量一旦初始化之后就不能再指向其他的对象。那么,该引用变量指向的对象内容可以改变吗?
我们来看一段代码:
public class FinalTest3 {
public static void main(String[] args) {
final Demo demo = new Demo();
++demo.i;
System.out.println(demo.i);
}
}
class Demo {
public int i = 0;
}
输出结果:
1
可以看出,final修饰的对象引用类型变量,指向的对应内容是可以被改变的。
3、final和static的区别
final和static这两个关键字,大家很容易混淆,这样说明下:
static修饰成员变量时,只是在内存中保存一个副本,关于static关键字的详细介绍,可以参考我的另外一篇博客static关键字详解
final修饰变量时,只是让变量的值不可变
我们来看一下这段代码:
public class FinalTest4 {
public static void main(String[] args) {
MyClass1 myClass1 = new MyClass1();
MyClass1 myClass2 = new MyClass1();
System.out.println(myClass1.i);
System.out.println(myClass1.j);
System.out.println(myClass2.i);
System.out.println(myClass2.j);
}
}
class MyClass1 {
public final double i = Math.random();
public static double j = Math.random();
}
输出结果为:
0.14420367652868182
0.8907081784319505
0.77280060728079
0.8907081784319505
可以看出,static修饰的变量j的值是相同的,没有变化。如果我们对final修饰的变量i进行赋值是编译报错的
上一篇: 以“己”为鉴 分享一次创业失败的体会
下一篇: 淮阴工学院值得上吗?淮阴工学院靠谱吗?