java中的final
java中的final关键字可以用来声明变量、方法、类。主要起到的作用就是达到引用不可变的效果。具体分别介绍如下:
1.1 final变量
被final修饰的变量在赋值之后引用关系就确定了,也就是不能对于基本数据类型来说不可以另赋新值,对于对象数据类型来说不能为这个变量指派一个新的对象。也就是类似于以下语句都是会在静态检查的时候报错的:
final int a=1;
a=2;
final String s="GOOGLE";
s="BAIDU";
那么仔细考虑来看,如果我们指定的一个变量是immutable的,那么就意味着如果用final修饰,在初始化之后这个变量就不可以修改了,如果是mutable的话,内部的值还可以修改但是不可以改变引用关系。
那么是不是意味着如果在我们定义的ADT内部如果我们用final修饰了一个immutable的变量的时候就不用担心表示泄露了呢?事实上不是这样的,如果在这种情况下存在表示泄露可能会存在使用者分析运行时内存的具体情况判断ADT内部一些实现功能的危险,也就是说即使使用了final,还是需要注意表示泄露的问题。
1.2 final方法
下面这段话摘自《Java编程思想》第四版第143页:
“使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的
含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用
。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java
版本中,不需要使用final方法进行这些优化了。“
也就是说,如果一个方法使用了final修饰,那么这个方法将不再能被子类重写。需要注意的是由于重写首先建立在继承的关系上,如果父类中一个final方法是private的,子类自然无法访问,那么子类中定义相同的方法名和参数是不会报错的,但是这时候不再是重写关系。例如如下代码是不会报错的
public class Person {
private final void decision() {
System.out.println("父类");
}
}
public class concretePerson extends Person{
public void decision(){
System.out.println("子类");
}
public static void main(String[] args) {
Person p=new concretePerson();
//p.decision();
}
}
但是可以发现的是如果我们去掉注释的话IDE会直接在注释那一行下报错,显示没有这个方法。这是由于静态检查阶段p的类型还是Person,而Person的decison方法是private的,不可访问,因此静态检查报错。
1.3 final类
与上面两类类似,如果使用final修饰类的话,那么这个类是不可以被继承的。
2.final一些实例
2.1 final变量与普通变量
很重要的一点就是final变量的指向已经不可变了,因此可以看下面这个例子
public static void main(String[] args) {
String a="helloworld";
String b="helloworld";
String c="hello"+"world";
final String d="hello";
String e="hello";
String f=d+"world";
String g=e+"world";
System.out.println(a==b);
System.out.println(a==c);
System.out.println(d==e);
System.out.println(a==f);
System.out.println(a==g);
}
输出如下:
true
true
true
true
false
这个结果让我困惑了一段时间,查阅了资料之后有了一定的了解。接下来对于每一个结果进行分析。
首先需要明确的就是对于这种直接通过双引号""声明字符串的方式, 虚拟机首先会到字符串常量池中查找该字符串是否已经存在,如果存在会直接返回该引用, 如果不存在则会在堆内存中创建该字符串对象, 然后到字符串常量池中注册该字符串。
也就是说a和b和c事实上是指向同一段地址的,因此a==b和a==c成立。
接下来分析一下a==g为什么是false的。这是因为g是在运行的时候才分配的一段地址,因此和a的地址是不一致的,因此返回false。
那么为什么a==f就是对的呢?这是因为d是由final修饰的,在静态编译的时候就当做常量使用,因此f的地址是在常量池中分配的,和a是指向一段地址的,因此a==f成立。