昨天去宣讲会现场做了一份java开发工程师的笔试题,碰到一个关于java中子类和父类中的静态方法和构造方法的执行顺序,当时一直没明白父类的静态代码块、静态变量、和构造方法和子类的静态代码块、静态变量、构造方法的一个加载执行顺序。回来后便做了如下测试:
(1)子类和父类中的静态代码块、非静态代码块、构造代码块的执行顺序。
父类Fu:
package pack1;
public class Fu {
//静态代码块
static{
System.out.println("这是父类的静态代码块");
}
//静态变量
static String i=lcy();
public static String lcy()
{
System.out.println("父类静态变量");
return "静态变量";
}
//非静态代码块
{
System.out.println("父类的非静态代码块");
}
//非静态变量
String j=pplcy();
public String pplcy()
{
System.out.println("父类非静态变量");
return "父类非静态变量";
}
//构造方法
Fu(){
System.out.println("这是父类的构造代码块");
}
//无参数构造方法
Fu(String name){
System.out.println("这是父类含有String参数的构造代码块");
}
}
子类Zi:
package pack1;
public class Zi extends Fu{
//静态代码块
static{
System.out.println("这是子类的静态代码块");
}
//静态变量
static String a=lcyhh();
public static String lcyhh()
{
System.out.println("子类静态变量");
return "静态变量";
}
//非静态代码块
{
System.out.println("子类的非静态代码块");
}
//非静态变量
String b=ppplcy();
public String ppplcy()
{
System.out.println("子类非静态变量");
return "子类非静态变量";
}
//子类构造方法
Zi(){
System.out.println("这是子类的构造代码块");
}
//无参数构造方法
Zi(String name){
System.out.println("这是子类含有String参数的构造代码块");
}
}
测试类Test:
package pack1;
public class Test {
public static void main(String[] args) {
Fu fu=new Zi();
System.out.println("----------------");
Zi zi=new Zi();
}
}
运行结果:
这是父类的静态代码块
父类静态变量
这是子类的静态代码块
子类静态变量
父类的非静态代码块
父类非静态变量
这是父类的无参构造代码块
子类的非静态代码块
子类非静态变量
这是子类的构造代码块
----------------
父类的非静态代码块
父类非静态变量
这是父类的无参构造代码块
子类的非静态代码块
子类非静态变量
这是子类的构造代码块
从上面的测试中可以发现:
- 当创建子类对象时jvm会先加载父类执行父类中静态代码块,再加载子类执行子类的静态代码块,然后再执行父类的非静态代码块、父类的构造代码块、子类的非静态代码块、子类的构造代码块。
- 当创建多个对象时,类的静态代码块在类的加载过程中只执行一次,而非静态代码块和构造代码块的执行次数和对象的创建相关。每创建一个对象都会执行类中的非静态代码块和构造代码块。
(2)创建子类对象时会先执行父类的构造方法吗?下面我们来测试一下:
父类:
package pack1;
public class Fu {
//无参数构造方法
Fu(){
System.out.println("这是父类的无参构造代码块");
}
//无参数构造方法
Fu(String name){
System.out.println("这是父类含有String参数的构造代码块");
}
}
子类:
package pack1;
public class Zi extends Fu{
//无参构造方法
Zi(){
System.out.println("这是子类的无参数的构造代码块");
}
//有参构造方法
Zi(String name){
System.out.println("这是子类含有String参数的构造代码块");
}
}
测试:
package pack1;
public class Test {
public static void main(String[] args) {
Fu fu=new Zi();
System.out.println("-------------");
Zi zi1=new Zi();
System.out.println("-------------");
Zi zi2=new Zi("zhangsan");
}
}
运行结果:
这是父类的无参构造代码块
这是子类的无参数的构造代码块
-------------
这是父类的无参构造代码块
这是子类的无参数的构造代码块
-------------
这是父类的无参构造代码块
这是子类含有String参数的构造代码块方法
当把父类的无参数的构造去掉后,会出现编译错误,但是当在子类的构造方法的第一天加入"super(name);"时候就可以同通过编译。
package pack1;
//父类
public class Fu {
//无参数构造方法
Fu(String name){
System.out.println("这是父类含有String参数的构造代码块");
}
}
//子类
public class Zi extends Fu{
//有参构造方法
Zi(String name){
super(name);
System.out.println("这是子类含有String参数的构造代码块");
}
}
//测试类
public class Test {
public static void main(String[] args) {
Zi zi2=new Zi("zhangsan");
}
}
运行结果:
这是父类含有String参数的构造代码块
这是子类含有String参数的构造代码块
从上面的测试中可以发现:
- 创建子类对象时会先调用其父类的构造方法
- 当java中创建子类对象的时候会默认调用父类的无参数的构造方法,因为子类的构造方法的第一行隐藏了一条super()语句,即调用父类的无参构造,当把父类I中的无参数的构造方法去掉后程序就会出现编译错误。因为在给一个类添加了一个有参数的构造方法后系统就不会再为该类提供一个无参数的构造方法(除非自己再写一个无参数的构造方法)。当没有手动给父类添加无参构造时,子类的构造方法的第一行隐藏了一条super()就会出错(此时没有给父类写一个无参数的构造方法,所以系统不会提供一个无参数的构造方法,因此父类中没有无参构造),但是可以在子类构造方法的第一行添加super(参数),即先调用父类中参数列表为super(参数)中的参数的构造方法。
总结一下:
- java中子类和父类中的方法执行顺序为:父类中静态代码块,子类的静态代码块父类的非静态代码块、父类的构造代码块、子类的非静态代码块、子类的构造代码块。
- 类的静态代码块在类的加载过程中只执行一次。
- 当java中创建子类对象的时候会默认调用父类的无参数的构造方法。当父类中含有有参构造方法又手动给父类添加无参构造时,此时会出现编译错误。解决方法:在子类构造方法的第一行添加一条语句”super(参数)”,即先调用父类中参数列表为super(参数),中的参数的构造方法。而不是默认调用super(),因为此时 系统不会给父类提供无参构造,父类中没有无参构造方法了。