前言
该篇文章讲了关于java final
关键字的一些内容 , 大部分内容参考 : https://www.cnblogs.com/dolphin0520/p/3736238.html
概述
final 可以修饰 :
- 变量
- 类
- 方法
final 的主要作用有 :
- final 变量一旦被初始化赋值之后,就不能再被赋值了。
- final 引用不能被更改
- final 修饰类时, 该类不能被继承
- final 修饰方法, 该方法不会被子类重写
修饰
修饰类
可以看到加了 final
的类不能给继承
修饰方法
下面这段话摘自《Java编程思想》第四版第143页:
“使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了。“
修饰变量
来自海子的文章 :
对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
1. 作为变量
2. 作为方法形参
如上面的代码 , 传参数到 changeValue
并没有限制实参(即 i ) , 而在 changeValue
方法内形参就受到了限制 .
3. 作为内部类参数
public class Test {
public static void main(String[] args) {
}
public void test(final int b) {
final int a = 10;
new Thread(){
public void run() {
System.out.println(a);
System.out.println(b);
};
}.start();
}
}
这个地方内部类使用了 a
,必须使用 final
修饰 ,原因就是为了防止 a
这个变量在内部类对象被修改了 . 具体的我们在内部类后面的文章详细分析 .
深入 final
这个章节来自 : https://www.cnblogs.com/dolphin0520/p/3736238.html , 非原创
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));
}
}
true
false
大家可以先想一下这道题的输出结果。为什么第一个比较结果为true,而第二个比较结果为fasle。这里面就是final变量和普通变量的区别了,当final变量是基本数据类型以及String类型时,如果在编译期间能知道它的确切值,则编译器会把它当做编译期常量使用。也就是说在用到该final变量的地方,相当于直接访问的这个常量,不需要在运行时确定。这种和C语言中的宏替换有点像. 因此在上面的一段代码中,由于变量b被final修饰,因此会被当做编译器常量,所以在使用到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。
final 常见问题
final 与 static
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的值却是不同的 , 从这里就可以看出两者的区别了, (static 变量和类优先于普通类提前加载)
被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;
}
public class Test {
public static void main(String[] args) {
MyClass myClass = new MyClass();
StringBuffer buffer = new StringBuffer("hello");
myClass.changeValue(buffer);
System.out.println(buffer.toString());
}
}
class MyClass {
void changeValue(final StringBuffer buffer) {
buffer.append("world");
}
}
这段代码是可以编译运行的也就是说 final 限制的是对象的指针 , 而指针所指的对象里的内容是发生更改的.
补充
- java采用的是值传递,对于引用变量,传递的是引用的值,对于基本类型的变量,相当于直接将变量进行了拷贝。
- 假如使用 xxMethod(final int b) , 那么 b 只能是读取, 不能修改 , 如果 b 是对象,则是不能改变引用
- final 与 volate 不能共用
参考内容
- https://www.cnblogs.com/dolphin0520/p/3736238.html
- https://www.zhihu.com/question/66083114 (深入-R大的问答-值得一看)
- https://www.zhihu.com/question/66083114/answer/242241071 final修饰递归方法会提高效率吗? - RednaxelaFX的回答 - 知乎