你真的了解final关键字了么?
许多编程语言都有某种方法来告诉编译器有一块数据是恒定不变的,Java中就是通过final关键字来实现;
1、一个永不改变的编译时常量
2、一个在运行时被初始化的值,而你不希望他被改变
也就是说这个恒定不变的数值不一定一定得是在编译时就定死了的,也有可能是在运行时期生成的,一个static 又是 final的域只占据一段不能改变的存储空间
对于基本数据类型来说,final表示的是他的数值是恒定不变的,但是如果是一个对象引用,final时引用恒定不变,但是对象自身是可以有变化的。
空白final
就是说在被声明为final但又未给定初值的域。无论有什么情况,编译器都确保空白final在使用前会被初始化,但是空白final在关键字final的使用上提供了更大的灵活性
必须在域的定义出或者每个构造器中用表达式对final进行赋值,也保证了final域在使用前总是被初始化
package finaltest;
public class Poppet {
private int i;
Poppet (int ii){
i = ii;
}
}
//-----------------------------------------------------------
package finaltest;
public class BlankFinal {
private final int i = 0;
private final int j;
private final Poppet p;
public BlankFinal() {
j = 1;
p = new Poppet(1);
}
}
final参数
在刚学Android的时候,经常写方法的时候代码被标红,然后AndroidStudio中alt + Enter帮助更改代码,经常会在这个方法的参数列表里给他加一个final关键字;
Java中是允许以声明的方式将参数指明为final的。表示没办法在方法中更改参数引用所指向的对象
final方法
1、把方法锁定,防止任何继承类修改他的含义。
2、确保继承中是方法保持不变,不会被覆盖。
final和private:
所有的private方法其实也就是指定成了final。因为没办法取用private方法,所以更不用谈覆盖了。当然如果你继承了一个类,并且写了一个方法,跟父类的final修饰的方法是一样的,即按照方法重写的样子去实现,编译会成功并在执行的时候看起来也是预期的结果,但是实际上并不是方法重写,而仅是生成了一个新的方法
“覆盖”只是在某方法是积累的接口的一比分才会出现,必须能将一个对象向上转型为它的基类,并调用相同的方法。如果某一个方法是private,那么他就不是基类的接口的一部分。他是一些隐藏于类中的程序代码,只不过是具有相同的名称而已;
package extend;
public class Base {
private int date1;
private int date2;
public int date3;
public int getDate1() {
return date1;
}
public void setDate1(int date1) {
this.date1 = date1;
}
public int getDate2() {
return date2;
}
public void setDate2(int date2) {
this.date2 = date2;
}
public int getDate3() {
return date3;
}
public void setDate3(int date3) {
this.date3 = date3;
}
public final void base() {
System.out.println("this is base");
}
}
final类
将某个类定义成final时,就是说不可以在继承这个类,不希望他拥有子类;
final类是禁止继承的,所以final类中所有的方法其实也是final的,因为肯定没办法覆盖他们。
初始化及类的加载
每个类的编译代码都存在于他自己的独立文件中,这个文件只在需要使用程序代码的时候才会被加载。可以说“类的代码在初次使用时才加载”。通常是发生在new第一个对象的时候或者是访问他的static变量/方法的时候;
继承和初始化
package extend;
public class Insect {
private int i = 9;
protected int j;
Insect(){
System.out.println("i = " + i + " j = " + j);
j = 39;
}
private static int x1 = printInit("static Insect.x1 initialized");
static int printInit(String s) {
System.out.println(s);
return 47;
}
}
//-------------------------------------------------------
package extend;
public class Beetle extends Insect {
private int k = printInit("Beetle.k initialized");
public Beetle() {
System.out.println("k = " + k);
System.out.println("j = " + j);
}
private static int x2 = printInit("static Beetle.x2 initialized");
public static void main(String[] args) {
System.out.println("Beetle constructor");
Beetle b = new Beetle();
}
}
//------------------------------------------
//output:
static Insect.x1 initialized
static Beetle.x2 initialized
Beetle constructor
i = 9 j = 0
Beetle.k initialized
k = 47
j = 39
反正我刚开始的时候很懵逼,这执行main方法第一句就是打印,但是为什么前面多了两行打印呢。因为忽略了类的加载和初始化过程;
当运行的时候,发生的第一件事就是试图访问Beetle里面的main()方法,这个方法是static修饰的,所以类加载器就会开始找出Beetle.class文件。在对它加载的时候,发现它有一个基类,那又要对他进行加载,不管是不是打算产生一个基类的独享,这一步都是会进行的;
这时候基类中的static要初始化,所以就出现了前面有两行打印的情况!