final是Java中的一个修饰符,可以用来修饰类,变量,方法。而对于final的理解,也主要围绕这三者来进行。
一、final修饰类
被final修饰的类我们称为不可变类,例如Java中的String类就是不可变类,他表示该类不能被继承,并且该类
中的方法会默认为final类型,但被final修饰的类中的成员属性并不会默认为final,我们在变成中可根据需求,确定属性
是否为final的。将类设计为final是出于该类不可被继承和一些安全性的考虑。否则就不要将类设计为final了。
二、final修饰方法
使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在
早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。
在最近的Java版本中,不需要使用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)); }}
结果:
truefalse
这个例子可以很完美体现出二者之间的区别。final修饰基本数据类型或者String类型,要么在定义处进行初始化赋值,要么在
构造器中进行初始化赋值,否则编译报错,赋值之后这个值便不能再被改变,因此Java的编译器很人性化的做了这样一个功能,
就是编译时优化,凡是被final修饰的变量都会进行编译期优化,因为他们的值在编译期已经确定了,因此String c = b + 2直接被
优化为"hello2",而且这个hello2是在字符串常量池中创建的,然而String a = "hello2",已经在字符串常量池中创建了一个hello2,
因此c就不会再创建了,而是直接指向了a所指向的那个hello2,因此a == c为true。而a == e为什么为false是因为当进行
String e = d + 2操作时相当于进行的是new String("hello2"),此时的e指向的是堆中的一个hello2对象,而a指向的是常量池中的,
因此为false。
换应该注意,只有在编译期确切知道变量值才会进行优化,否则则不会进行优化。
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修饰变量的区别?
public class Test { public static void main(String[] args) { MyClass myClass1 = new MyClass(); MyClass myClass2 = new MyClass(); System.out.println("final i:" + myClass1.i); System.out.println("static j:" + myClass1.j); System.out.println("final i:" + myClass2.i); System.out.println("static j:" + myClass2.j); }} class MyClass { public final double i = Math.random(); public static double j = Math.random();}
结果:
final i:0.19008381769339489static j:0.04553435249234561final i:0.24184639133515862static j:0.04553435249234561
观察发现,尽管调用了两次j,但是值是一样的,所以可知,final修饰变量强调的是该变量不可再被修改,而static修饰变量强调的是
该变量是全局的,静态的,整个类中只能存在一份静态的j,所有该类的实例对象对它的调用都调用的是同一份数据。
final也可以修饰方法中的参数
同理上面也讲过了,当为值传递时,在该方法内这个参数不能再被修改,当为引用传递时,引用的指向不能再被改变,但对象内容还是可以改变的
三、final与线程安全
final修饰引用或变量表示不可变,而且该引用或变量也不能被缓存,所以缓存变量的问题也不复存在,因此java可以让你安全的访问
final的属性而无需同步。因此他与volatile不能连用。