final
final变量
final变量有成员变量和非成员变量(方法内的局部变量),在类成员中final经常和static一起使用,作为类常量使用。其中类常量必须在声明时候立即初始化,final成员变量可以在构造函数初始化。
public class main{
public static final int i ;
报错,必须初始化,因为常量在常量池中就存在了,调用时不需要类的初始化,
所以必须在声明时初始化。
public static final int j;
Main(){
i=2;
j=3;
}
}
- 对于类常量,JVM会在常量池中缓存,在读取该变量的时候不会加载这个类。
(对于上面也就是说类是否初始化不影响这个常量池中的常量)
public class Main {
public static final int i = 2;
Main(){
System.out.println("调用构造函数");
^该方法不会被调用
}
public static void main(String 【】args){
System.out.println(Main.i);
}
@final修饰基本数据类型变量和引用
@Test
public void final修饰基本类型变量和引用(){
final int a = 1;
final int【】 b ={1};
final int【】 c ={1};
//b=c;报错
b【0】=1;
final String aa ="a";
final Fi f = new Fi();
//aa = "b";报错
//f = null 报错
f.a =1;
}
final方法不能够被子类的方法重写,将方法声明为final,在编译的时候就已经静态绑定了,不需要在运行时动态绑定。final方法调用时使用的是invokespecial指令。
(静态和动态绑定将在继承中学习)
final类
final类不能被继承,其中的方法也都会是final类型的,java中的String类和Integer类都是final类型的
class Si{
//一般情况下final修饰的变量一定要被初始化。
//只有下面这种除外,要求变量必须在构造方法中被初始化。
//并且不允许有空参数的构造方法
//这样就能让每个实例都有一个不同的变量,并且此变量在每个实例中只会被初始化一次
//于是这个变量在单个实例中就是常量了
final int s;
Si (int s){
this.s = s;
}
}
class Bi{
final int a = 1;
final void go(){
//final修饰方法无法被继承
}
}
class Ci extends Bi{
final int a =1;
final void go(){
//final修饰方法无法被继承
}
}
class Ci extends Bi{
final int a = 1;
// void go(){
// final修饰方法无法被继承
//}
}
final char[]a = {'a'};
final int[]b = {1};
final关键字的知识点
-
final成员变量必须在声明的时候初始化或者在构造器中初始化,否则就会编译错误。final
变量一旦被初始化之后不能再次赋值。 -
本地变量必须在声明的时候赋值。因为没有初始化的过程
-
在匿名类中的所有变量都必须是final变量。
-
final方法不能被重写,final类不能被继承
-
接口中声明的所有变量本身是final的。类似于匿名类
-
final和abstract这两个关键字是反相关的,final的就不可能是abstract(抽象)的
(一个要求抽象,一个要求实现) -
final方法在编译阶段绑定,称为静态绑定(static binding)
-
将类,方法,变量声明为final能够提高性能,这样JVM就有机会进行统计,然后优化。
final方法
前面介绍过。。
好处
- 提高了性能,JVM在常量池中会缓存final变量
- final变量在多线程中并发安全,无需额外的同步开销
- final的方法是静态编译的,提高了调用速度
- final类创建的对象是只可读的,在多线程可以安全共享
实际操作
final用法
- 对于常量来说,意味着值永远不能改变,例如final int i = 100。
(这里i的值永远都是100。)但是对变量来说又不一样,只能是标识这个引用不能被改
变,例如final File f = new File(“c:\test.txt”);
那么这个f是一定不能被修改的,但如果f本身有方法修改其中的成员变量,例如是否可读,
是允许修改的——实际上是这个变量绑定了常量池里的常量,绑定关系不可变,但是有
可能对常量本身进行修改。
【【【【【拓展部分】】】】】
@@关于空白final
final修饰的变量有三种:静态变量、动态变量、实例变量和局部变量,分别表示三种类型的常量。另外,final变量定义的时候,可以先声明,不赋初始值,这其中的变量也称为final空白,无论什么情况,编译器都确保空白final在使用之前必须初始化。但是,final空白在final关键字final的使用上提供了更大的灵活性,为此,一个类中的final数据成员就可以体现依据对象而有所不同,却有着保持其恒定不变的特征。
public class FinalTest{
final int p;
final int q = 3;
FinalTest(){
p=1;
}
FinalTest(int i){
p=i;//可以赋值,相当于重新定义p
q=i;//不能为一个final变量重复赋值
}
@@final内存分配
调用一个函数,除了函数本身执行的时间复杂度外,还有寻找这个函数所需的额外时间
(类内部有一个函数签名和其地址的映射表)。
所以减少函数调用次数就等于降低了性能消耗。
final修饰的函数会被编译器优化,优化的结果是减少了函数调用的次数。实现方式看下例:
public class Test {
final void func(){System.out.println("g");};
public void main(String[]args){
for(int j=0;j<1000;j++)
func();
}}
经过编译器优化以后,这个类就变成相当于这样:
public class Test{
final void func(){System.out.println("g");};
public void main(s=String []args){
for (int j=0;j<1000;j++)
{System.out.println("g");}
}}