case4:子类和父类有同名同类型的静态变量的时候
结论就不用解释了:静态变量属于类的,和继承无关。
case5:静态代码块属于类的,并且优先于main方法执行(有难度)
public class StaticDemo1 { public static void main(String[] args) { speak(); //StaticDemo1 t1 = new StaticDemo1(); //System.out.println(t1.i); } // 静态变量 static int i = 1; // 静态方法 static void speak() { System.out.println("a"); } // 静态代码块 static { i = i + 3; System.out.println(i); } // 构造函数 public StaticDemo1() { i = i + 5; System.out.println(i); } }
输出:
4 a
4在a之前输出,证明:毕竟mian方法属于StaticDemo1类的方法,所以会先执行此类的静态变量 + 静态代码块。
其它不变,改为这样:
public static void main(String[] args) { StaticDemo1 t1 = new StaticDemo1(); System.out.println(t1.i); speak(); } 4 9 9 a
- 执行静态代码块:打印4
- 执行构造方法:打印9
- System.out.println(t1.i)直接输出,打印9(此时i的值是9)
- 执行speak():打印1
public static void main(String[] args) { speak(); StaticDemo1 t1 = new StaticDemo1(); System.out.println(t1.i); } 4 a 9 9
这个输出,在意料之中,不再解释喽。
这是一道面试题,考察的是:static块真正的执行时机。若想真正了解类的装载,请去了解JVM吧~
注解对执行顺序的影响
特别的,这里我介绍一下各种注解影响的执行顺序,如下代码:
@Component public class InitBeanTest implements InitializingBean,ApplicationListener<ContextRefreshedEvent> { @Autowired DemoService demoService; public InitBeanTest() { System.err.println("----> InitSequenceBean: constructor: "+demoService); } @PostConstruct public void postConstruct() { System.err.println("----> InitSequenceBean: @PostConstruct: "+demoService); } @Override public void afterPropertiesSet() throws Exception { System.err.println("----> InitSequenceBean: afterPropertiesSet: "+demoService); } @Override public void onApplicationEvent(ContextRefreshedEvent arg0) { System.err.println("----> InitSequenceBean: onApplicationEvent"); } } 执行结果: ----> InitSequenceBean: constructor: null ----> InitSequenceBean: @PostConstruct: com.xxx.service.impl.DemoServiceImpl@40fe544 ----> InitSequenceBean: afterPropertiesSet: com.xxx.service.impl.DemoServiceImpl@40fe544 ----> InitSequenceBean: onApplicationEvent
根据代码演示,得出文字版结论:
构造函数是每个类最先执行的,这个时候,bean属性还没有被注入
@PostConstruct优先于afterPropertiesSet执行:在这执行,属性已经完成了赋值(注入)
继续补充:子类默认调用父类构造函数问题
Java有个很有趣的现象:父类有N多个构造函数,子类如果只写一个的话那么子类最终就只有一个构造函数可用,因此子类在这方面要特别的注意喽。
默认情况下,子类在使用构造函数初始化时(不管是子类使用有参构造还是无参构造),默认情况下都会调用父类的无参构造函数(相当于调用了super())。看看下面几个变种如下:
// 父类木有空的构造 只有一个有参构造 class Parent{ private Integer id; public Parent(Integer id){ System.out.println("this is parent cons..."); } }
此时候我们发现发现如下三问题:
1、子类Child必须有对应的有参构造
2、super(id)必须显示的写出,否则编译不通过
3、原则上,子类的构造函数不能多于父类的
4、子类构造函数若多余父类(或者类型啥的和父类不匹配),需要显示的调用父类构造函数
结论:
1、子类构造器执行之前必须能够先执行父类的构造函数(super(xxx)必须放在第一行代码)
2、若父类有空构造,子类构造默认都会调用super()。若父类木有空构造,子类所有构造都必须显示调用super(xxx)·
总结
据反馈,看了此篇文章后,很多小伙伴感觉自己学的是另外一个Java,其实这就是JavaSE,很多架构师认为,Java基础才是精华中的精华(一流),Spring才属于应用级别的技术(二流)。