目录
- 为什么是null
- 回顾类加载
- 原因
- 问题重现
- 总结
- 类加载顺序
- 子类重写被父类构造函数调用的函数注意
- 不能放过不起眼的日志
一条日志引发的案子
[11:12:58.505][D][Gen][RTLive][getIns ins = 4414717]
[11:12:58.774][I][Gen][null][updateShowMode]
[11:12:58.864][D][Gen][VideoCameraCapture][setOrientation ortation = 1]
[11:12:58.865][D][Gen][VideoCapture][setOrientation ortation = 1]
[11:12:58.865][D][Gen][VideoCapture][setHardEncode isHardEncode false]
为什么是null
第二行日志中的 “null” 引起了我的关注,正常情况下这里应该是该条日志所在的类的类名称,现出现了null,首先从日志阅读习惯上就不顺了。其次是TAG的值是给null,需要弄清楚。还存在TAG 没有初始化的嫌疑。
首先通过"updateShowMode"找到对应的代码位置。函数第一行就打了日志,而TAG是个成员变量,定义如下:
private String TAG = getClass().getSimpleName();public synchronized void updateShowMode() {GenLog.i(TAG,"updateShowMode");
}
这样的模型,TAG的值为什么是null。那就是类加载时机的问题了。
回顾类加载
先定义如下类
public class Person {private static String staticVar = staticVar(“def”);//目的是打日志,从日志上看顺序private String var = var();//目的是打日志,从日志上看顺序public Person(){System.out.println("Person init");}private String var() {System.out.println("Person var");return "var";}public static String (String param){System.out.println("Person staticVar" + param);return "staticVar";}static {System.out.println("Person static code");}
}
当Person person = new Person();
被执行时输出如下:
Person staticVar def
Person static code
Person var
Person init
加载顺序依次如下:
- 静态变量
- 静态代码块
- 成员变量
- 构造函数
当Person.staticVar(“main”);
参数是为了区别外部与内部谁先调用,被执行时输出如下:
Person staticVar def
Person static code
Person staticVar main
执行顺序:
静态变量的初始化
静态代码块
外部调用的执行
加载顺序还是保持
- 静态变量
- 静态代码块
出现了上面的null,getClass().getSimpleName() 会有问题吗?我们将成员函数稍微变一下:
public class Person {private static String staticVar = staticVar("def");private String var = var();public Person(){System.out.println("Person init");}private String var() {//这里输出getClass().getSimpleName()的结果String name = getClass().getSimpleName();System.out.println("Person var name = " + name);return "var";}public static String staticVar(String param){System.out.println("Person staticVar " + param);return "staticVar";}static {System.out.println("Person static code");}
}
再次执行Person person = new Person();
结果如下:
Person staticVar def
Person static code
Person var name = Person
Person init
说明getClass().getSimpleName()没有问题。
到底什么情况呢,经过一番折腾,发现还有一个继承关系,updateShowMode覆盖父类的函数,且updateShowMode是在父类构造函数中调用的。
我们再加一个类Any 作为person的父类
public class Any {private static String TAG = staticVar();private String var = var();private static String staticVar() {System.out.println("Any staticVar");return "Any staticVar";}public Any(){System.out.println("Any init");}public void test(){System.out.println("Any test");}private String var(){System.out.println("Any var");return "Any var";}static {System.out.println("Any static code");}
}//Person 类变化如下 继承Any
class Person extends Any
执行Person person = new Person();
结果如下:
Any staticVar
Any static code
Person staticVar def
Person static code
Any var
Any init
Person var name = Person
Person init
执行顺序依次:
- 父类静态变量
- 父类静态代码块
- 子类静态变量
- 子类静态代码块
- 父类成员变量
- 父类构造函数
- 子类成员变量
- 子类构造函数
原因
出现null是因为没有把握好6和7的顺序。父类构造函数(6)调用了子类重载函数,重载函数又访问了子类的成员变量。此时还在执行父类的构造函数,子类的成员变量还没有被初始化(7),所以出现了null
。
问题重现
我们再次修改Any和Person 的test重载
Any 构造函数调用test()
public class Any {private static String TAG = staticVar();private String var = var();private static String staticVar() {System.out.println("Any staticVar");return "Any staticVar";}public Any(){System.out.println("Any init");//构造函数调用成员函数test();}public void test(){System.out.println("Any test");}private String var(){System.out.println("Any var");return "Any var";}static {System.out.println("Any static code");}
}
Person
覆盖Any的test(),并输出var
public class Person extends Any{private static String staticVar = staticVar("def");private String var = var();public Person(){System.out.println("Person init");}private String var() {String name = getClass().getSimpleName();System.out.println("Person var name = " + name);return "var";}public static String staticVar(String param){System.out.println("Person staticVar " + param);return "staticVar";}@Overridepublic void test() {//覆盖重写父类的test函数,并访问var 输出varSystem.out.println("Person test var= " + var);}static {System.out.println("Person static code");}
}
再次执行Person person = new Person();
输出:
Any staticVar
Any static code
Person staticVar def
Person static code
Any var
Any init
Person test var= null
Person var name = Person
Person init
Person test var= null 得到应证。
总结
类加载顺序
- 父类静态变量
- 父类静态代码块
- 子类静态变量
- 子类静态代码块
- 父类成员变量
- 父类构造函数
- 子类成员变量
- 子类构造函数
没有继承关系的情况下
- 静态变量
- 静态代码块
- 成员变量
- 构造函数
子类重写被父类构造函数调用的函数注意
重载函数被调用的过程中,成员变量是没有被初始化的。
不能放过不起眼的日志
我们要多看日志,养成日志规范,并充分利用日志解决问题。