1.final关键字的基本用法
在Java中,final关键字可以用来修饰类、方法和变量(包括成员变量和局部变量)。
1.修饰类
final关键字修饰的类不能被继承。final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法。
package com.chensan.test02;
public final class Employee {
private String userName;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
class Manager extends Employee {
}
2.修饰方法
下面这段话摘自《Java编程思想》第四版第143页:
“使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了。“
final修饰的方法不能被重写;
注:类的private方法会隐式地被指定为final方法。
package com.chensan.test02;
public class Employee02 {
private Double salary;
public Double getSalary() {
return salary;
}
public final void setSalary(Double salary) {
this.salary = salary;
}
}
class Manager02 extends Employee02 {
public void setSalary(Double salary) {
this.salary = salary;
}
}
3.修饰变量
1)final变量修饰基本数据类型变量,则在数值初始化后不能修改;
2)final变量修饰引用类型的变量,则初始化后不能再指向其他对象;
package com.chensan.test02;
import java.util.Date;
public class Employee03 {
private int age;
private final double salary = 0.0;
Date d = new Date();
private final Date hireDate = d;
public Employee03() {
age = 18;
salary = 600_000;
d.setTime(315360000000L);
hireDate = new Date();
}
}
final修饰的变量表示赋值之后不能再进行更改,系统赋默认值也算赋值,因此系统也不会赋默认值。
如果不在定义的时候或者构造函数中对final变量进行赋值的话,则生成的对象中final变量的值是未知的(编译器也会直接报错),因此必须进行初始化。
2.深入理解final关键字
1.final变量和普通变量的区别?
当final作用于成员变量时,成员变量必须在定义时或者构造器中进行初始化赋值,而且final变量一旦被初始化赋值之后,就不能再被赋值了。(注意是成员变量,局部变量只需要保证在使用之前被初始化赋值即可)
public class testVariable {
public static void main(String[] args) {
String a = "hello2";
final String b = "hello";
String d = "hello";
String c = b + 2;
String e = d + 2;
String f = getHello();
System.out.println(a == c);//true
System.out.println(a == e);//false
System.out.println(a == f);//false
}
public static String getHello() {
return "hello";
}
}
当final变量是基本数据类型以及String类型时,如果在编译期间能知道它的确切值,则编译器会把它当做编译期常量使用。也就是说在用到该final变量的地方,相当于直接访问的这个常量,不需要在运行时确定。这种和C语言中的宏替换有点像。因此在上面的一段代码中,由于变量b被final修饰,因此会被当做编译器常量,所以在使用到b的地方会直接将变量b替换为它的值。而对于变量d的访问却需要在运行时通过链接来进行。想必其中的区别大家应该明白了,不过要注意,只有在编译期间能确切知道final变量值的情况下,编译器才会进行这样的优化,如f就不会进行这样的优化。
2.被final修饰的引用变量指向的对象不可变,内容可变
public class Employee04 {
Date d = new Date();
private final Date hireDate = d;
public static void main(String[] args) {
Employee04 employee = new Employee04();
System.out.println(employee.hireDate);//Sun Apr 08 14:56:14 CST 2018
employee.d.setTime(315360000000L);
System.out.println(employee.hireDate);//Sun Dec 30 08:00:00 CST 1979
employee.hireDate = new Date();
}
}
3.final参数的问题
网上流传的“当你在方法中不需要改变作为参数的对象变量时,明确使用final进行声明,会防止你无意的修改而影响到调用方法外的变量”。
public class TestVariable03 {
void changeValue(final int i) {
++i;
}
void changeStringValue(final StringBuffer buffer) {
buffer.append("World");
}
public static void main(String[] args) {
TestVariable03 t1 = new TestVariable03();
int i = 3;
t1.changeValue(i);
System.out.println(i);
StringBuffer buffer = new StringBuffer("hello");
t1.changeStringValue(buffer);
System.out.println(buffer.toString());//helloWorld
}
}
java的传值方式为值传递。changeValue(final int i)无final,i值也无法修改,final修饰了形参i,则局部变量不能被重新赋值;而StringBuffer为对象,实参和形参仍指向同一个对象,对象内容的修改,与是否有final无关。
4.final和static变量的区别:
public class Test1 {
public final double i = Math.random();
public static double j = Math.random();
public static void main(String[] args) {
Test1 test1 = new Test1();
Test1 test2 = new Test1();
System.out.println(test1.i);//0.09135584356249371
System.out.println(test1.j);//0.14954652472778074
System.out.println(test2.i);//0.08916328064290513
System.out.println(test2.j);//0.14954652472778074
}
}
每次的i值不同,j值是一样的。final用来保证变量不变;static用来保存一份副本。
参考:海子博客园 浅析Java中的final关键字
熔岩51CTO博客 Java关键字final、static使用总结