在分析android应用层的代码时,时常遇到类初始化,重载等相关知识点,在此进行总结,该部分内容是对java类的初始化进行总结分析。
首先见下图所示:
继承基类B的子类C的对象创建的初始化过程如下:
1、加载子类的C.class文件;
2、通过关键字得知子类C有一个基类B,继续加载基类B的B.class文件;若基类B还有它的上一层基类A则会继续加载A.class;
3、static域的初始化是在所有类的加载完之后就会进行的,最上一层基类A加载完,该类的static域便会初始化,然后再处理第二层基类B的static域,对其进行初始化,直到最下层子类C的static域初始化完成,此时子类C及其所有基类A,B的加载和static域已经初始化完成。
总结:以上三步是在没有创建对象,或者说子类的main方法里没有任何语句时都会执行的,只要运行子类,都会执行类的加载和静态域的初始化。
注意:静态域和实例域(包含成员变量和成员方法)的初始化时机不同,静态域的初始化是在类加载完就一定会进行的,但是实例域的初始化是在创建对象之后才进行的。也就是说,只有在调用构造器时才会进行实例域的初始化。
案例代码:(参考:https://blog.csdn.net/chen_yuxi/article/details/81945314)
class Insect{
private int i=9;
protected int j;
Insect(){
System.out.println("i="+i+","+"j="+j);
j=39;
System.out.println("i="+i+","+"j="+j);
}
private static int x1=printInit("static Insect.x1 initialized");
static int printInit(String s){
System.out.println(s);
return 47;
}
}
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");
}
class Beatles extends Beetle{
private int m=printInit("beatles.m initialized");//为了证明static和非static域的初始化时机不一样
private static int x3=printInit("static Beatles.x3 initialized");
public Beatles(){
System.out.println("m="+m);
}
public static void main(String[] args) {
System.out.println("beatles initialized");
Beatles b3=new Beatles();//加载类之后static域初始化结束,只有创建对象,调用构造器的时候才会进行初始化。
System.out.println("x3="+b3.x3);
//System.out.println(m);
}
}
输出结果:
static Insect.x1 initialized
static Beetle.x2 initialized
static Beatles.x3 initialized
beatles initialized
i=9,j=0
i=9,j=39
Beetle.k initialized
k=47
j=39
beatles.m initialized
m=47
x3=47
从运行结果可以看出,与我们图中分析的一样。首先从子类开始往上加载所有的类,然后从基类开始逐步初始化static域,最后在main中调用构造器触发该类的实例域初始化,实例域的初始化是从其子类继承的最上层基类开始。
当父类调用被子类重写的方法时,会出现怎样的情况呢?
可以总结为:
先成员变量再构造方法,先父类再子类
多态表现:有同名方法执行子类的
具体案列如下所示:
class father{
int a=10;
int b;
String c = "I'm father\n";
private int father=fatherlog("variable father value\n");
father(){
log();
}
void log(){
System.out.print(c);
logout();
}
void logout(){
System.out.print("test father\n");
}
int fatherlog(String a){
System.out.print(a);
return 88;
}
}
public class son extends father{
private String baseName = "sub";
private int son=sonlog("variable son value\n");
son(){
System.out.print("\nI'm son\n");
}
@Override
void logout(){
System.out.print("override test son\n");
//System.out.print(baseName);
}
int sonlog(String a){
System.out.print(a);
return 22;
}
public static void main(String[] args){
new son();
}
}
输出结果为:
variable father value
I'm father
override test son
variable son value
I'm son
该结果证明了上述中的总结:先加载父类再加载子类,先初始成员化变量再初始化方法。
那我们再来将上述代码修改一下
class father{
int a=10;
int b;
String c = "I'm father\n";
father(){
log();
}
void log(){
System.out.print(c);
logout();
}
void logout(){
System.out.print("test father\n");
}
}
public class son extends father{
private String baseName = "sub";
son(){
System.out.print("\nI'm son\n");
}
@Override
void logout(){
//System.out.print("override test son\n");
System.out.print(baseName);
}
public static void main(String[] args){
new son();
}
}
I'm father
null
I'm son
这次的结果,为什么logout的输出为null呢?
原因是这样的,在子类中实例化son,会先父类再子类。父类中初始化变量String c,然后执行father构造函数,在其构造函数中调用log(),log()中会调用logout,由于logout在子类中被重写(多态表现:有同名方法执行子类的),所以会执行子类的logout,但是子类的logout函数中会引用子类的变量String baseName,此时的子类成员变量并没有被初始化,所以会提示:null。证明了:先成员变量再构造方法