java关键字final完全总结
如有错误遗漏请指正,如需转载请注明出处
1.final修饰变量
final变量一旦显式初始化后,就不能再次赋值。
注意:
- final修饰的变量,无论是类变量、成员变量、局部变量还是形参,这些变量都需要进行显式初始化才能使用。final变量中保存的值不变。
- 对于final修饰的形参,在传递实参时就完成了显示初始化,很好理解。
- 对于final修饰的局部变量,和普通局部变量一样都需要显示初始化之后才能使用。因为java中局部变量没有隐式初始化。
- 对于final修饰的成员变量,必须在声明final成员变量时显示初始化,或在动态代码块或在构造函数中初始化,不然报错。
- 对于final修饰的类变量,必须声明final类变量时显式初始化,或在静态代码块中显式初始化,不然报错。
- 空白final(blank final),所有的为初始化的final局部变量,隐式初始化的final成员变量 final类变量都是blank final 变量,必须显示初始化之后才能使用。
- final变量中保存的值不能变化,但不表示对象不能变化。对于基本数据类型,变量中直接保存的就是基本类型的具体值,不变就不变了。但对于引用类型(对象 数组),变量中保存的是对象的地址,变量中的值不变,表示地址不变,表示该变量始终指向一个对象,但我们可以改变对象的状态(对象的属性)。但要注意java中的不可变类型(Immutable Objects),例如Integer Long等基本类型包装类,String等的都是不可变类型,不可变类型的对象是不可变的。
final修饰变量后导致的“宏替换”问题
计算机科学里的宏(Macro),是一种批量处理的称谓。一般说来,宏是一种规则或模式,或称语法替换 ,用于说明某一特定输入(通常是字符串)如何根据预定义的规则转换成对应的输出(通常也是字符串)。
c里面的宏:
格式: #define PI 3.1415926
作用:在预编译时把源码中的所有PI换成3.1415926java里面的宏:
条件:- final变量且声明时同时显式初始化
变量必须是String或基本数据类型
作用:在编译期把变量替换为具体的值。package com.wzm; /** * @author 王宗明 * @version 创建时间:2017年1月9日 下午5:10:34 * 程序的简单说明 */ public class TestFinal { public void add(){ int a=1; int b=1; int c=a+b; } public static void main(String[] args) { final int a=1; final int b=1; int c=a+b;//等价于 c=1+1;因为发生了宏替换,a替换成了1,b也替换成了1。 } }
查看TestFinal的字节码可以发现确实发生了宏替换。
下面给出了add方法和main方法的部分字节码,因为我们也只关注这部分....... public void add(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=4, args_size=1 0: iconst_1 1: istore_1 2: iconst_1 3: istore_2 4: iload_1 5: iload_2 6: iadd 7: istore_3 8: return ....... public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=1, locals=4, args_size=1 0: iconst_1 1: istore_1 2: iconst_1 3: istore_2 4: iconst_2 5: istore_3 6: return .......
2.final修饰方法
final修饰方法表示”最终”方法,子类不能重写父类的final方法。
但是我们需要注意的一点是:因为重写的前提是子类可以从父类中继承此方法,如果父类中final修饰的方法同时访问控制权限为private,
将会导致子类中不能直接继承到此方法,因此,此时可以在子类中定义相同的方法名和参数,此时不再产生重写与final的矛盾,而是
在子类中重新定义了新的方法。
public class Child extends Parent {
public void getName(){
}
}
class Parent {
/**
* 因为private修饰,子类中不能继承到此方法,因此,子类中的getName方法是重新定义的、
* 属于子类本身的方法,编译正常
*/
private final void getName(){
}
// 因为pblic修饰,子类可以继承到此方法,子类中不能重写这个方法,否则编译出错
public final void getAge(){
}
}
3.final修饰类
final修饰类表示”最终类”,final类不能被继承,即final类不能有子类。