Java 关键字 final 知识点巩固
Java中关键字 final
这个关键字在代码中使用的很多,但其原理跟多人不会关注。
一位面试官朋友闲聊是曾说过,他常问的关于final的一个问题是,“当Java中final修饰HashMap集合时,HashMap集合数据能修改吗?向这个HashMap集合add数据的时候,程序会不会报错。”这个问题,80% 的面试人员会答错。
在此再整理下关于final的用法。
1、基本用法
基本用法,final可以修饰类、方法、变量(成员变量+局部变量)
修饰类:
用final修饰类,表明该类不能被继承。final类中成员变量可以根据需要设置为final,但是final类中所有成员方法都会隐式指定为final方法。
要谨慎使用final修饰类,除非这个类真的在以后不会用来继承。
修饰方法:
用final修饰方法,会把方法锁定,以防止任何继承类修改它的含义。只有在想明确禁止该方法在子类中被覆盖的情况才将方法设置为final。
类的private方法会隐式的指定为final方法。
修饰变量:
用final修饰变量,如果是基本数据类型,数值一旦初始化之后就不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
2、深入理解
类的final变量和普通变量的区别
当final作用于类的成员变量时,成员变量必须在定义时或者构造器中进行初始化赋值,且final变量一旦被初始化赋值,就不能再被赋值了。
import com.google.common.collect.Maps; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author wgy * @version 1.0 * @description * @date 2020/8/30 15:54 */ public class TestFinal { private static Logger log = LoggerFactory.getLogger(TestFinal.class); public static void main(String[] args) { String str_1 = "test_1"; final String str_2 = "test_"; String str_3 = "test_"; /** 这里访问 final str_2 是直接访问它的值 ,相当于 "test_" + 1 */ String val_1 = str_2 + 1; String val_2 = "test_" + 1; /** 这里访问str_3是根据连接进行, */ String val_3 = str_3 + 1; log.info("str_1 == val_1 : {}" , (str_1 == val_1)); log.info("str_1 == val_2 : {}" , (str_1 == val_2)); log.info("str_1 == val_3 : {}" , (str_1 == val_3)); log.info("str_1 eq val_3 : {}" , (str_1.equals(val_3))); } }
输出结果:
16:18:19.304 [main] INFO com.ycb.iot.finaltest.TestFinal - str_1 == val_1 : true 16:18:19.310 [main] INFO com.ycb.iot.finaltest.TestFinal - str_1 == val_2 : true 16:18:19.310 [main] INFO com.ycb.iot.finaltest.TestFinal - str_1 == val_3 : false 16:18:19.310 [main] INFO com.ycb.iot.finaltest.TestFinal - str_1 eq val_3 : true
当final变量是基本数据类型以及String类型时,如果编译期间能知道它的确切值,则编译器会把它当作编译期常量使用。注意是确切知道final变量值,才会这样。
import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author wgy * @version 1.0 * @description * @date 2020/8/30 15:54 */ public class TestFinal { private static Logger log = LoggerFactory.getLogger(TestFinal.class); public static void main(String[] args) { String str_1 = "test_1"; final String str_2 = getStrVal(); String str_3 = "test_"; /** 这里访问 final str_2 是直接访问它的值 ,相当于 "test_" + 1 */ String val_1 = str_2 + 1; String val_2 = "test_" + 1; /** 这里访问str_3是根据连接进行, */ String val_3 = str_3 + 1; log.info("str_1 == val_1 : {}" , (str_1 == val_1)); log.info("str_1 == val_2 : {}" , (str_1 == val_2)); log.info("str_1 == val_3 : {}" , (str_1 == val_3)); log.info("str_1 eq val_3 : {}" , (str_1.equals(val_3))); } public static String getStrVal(){ return "test_"; } }
输出结果:
16:20:35.015 [main] INFO com.ycb.iot.finaltest.TestFinal - str_1 == val_1 : false 16:20:35.019 [main] INFO com.ycb.iot.finaltest.TestFinal - str_1 == val_2 : true 16:20:35.019 [main] INFO com.ycb.iot.finaltest.TestFinal - str_1 == val_3 : false 16:20:35.019 [main] INFO com.ycb.iot.finaltest.TestFinal - str_1 eq val_3 : true
被final修饰的引用变量内容是否可变
引用变量被final修饰后,虽然不能再指向其他对象,但是它指向的对象的内容是可变的。
/** * @author wgy * @version 1.0 * @description * @date 2020/8/30 15:54 */ public class TestFinal { private static Logger log = LoggerFactory.getLogger(TestFinal.class); public static void main(String[] args) { final TestFinalVal testFinalVal = new TestFinalVal(); int value_1 = testFinalVal.value + 10; log.info("value_1 : {} ", value_1); } } class TestFinalVal{ public int value = 100; }
输出结果:
16:26:52.604 [main] INFO com.ycb.iot.finaltest.TestFinal - value_1 : 110
3、对于开头的问题:
很多人第一反应: HashMap一旦存进去值,就不能变了。结果是错的,存进去之后还能变。
第二反应:HashMap第一次初始化之后,它的各项属性值不能变了,比如Capacity、size等。结果还是错的,这些属性还是可以变化。
当final修饰基本类型和String时,变量值不可变。当修饰其他类型的对象时,final使其引用恒定不变,但是对象自身却可以自由修改变换。