面试题 1
class Root{
static {
System.out.println("Root的静态初始化快");
}
{
System.out.println("Root的普通初始化快");
}
public Root() {
System.out.println("Root的无参数的构造器");
}
}
class Mid extends Root{
static {
System.out.println("Mid静态代码块");
}
{
System.out.println("MId的普通初始代块");
}
public Mid() {
System.out.println("Mid的无参数的构造器");
}
public Mid(String msg) {
//通过this调用同一类中重载的构造器
this();
System.out.println("Mid的带参数的构造器,其参数值:"+msg);
System.out.println("Mid的无参构造器");
}
}
class Leaf extends Mid{
static int age;
static {
System.out.println("Left的静态初始化快");
}
{
System.out.println("Left的普通初始化快");
}
public Leaf() {
//通过super调用父类中带有一个字符串参数逇构造器
super("郭京伟");
System.out.println("Left的构造器");
}
}
public class LeafTest {
public static void main(String[] args) {
// System.out.println(Leaf.age);
new Leaf();
}
}
Root的静态初始化快
Mid静态代码块
Left的静态初始化快
Root的普通初始化快
Root的无参数的构造器
MId的普通初始代块
Mid的无参数的构造器
Mid的带参数的构造器,其参数值:郭京伟
Mid的无参构造器
Left的普通初始化快
Left的构造器
Process finished with exit code 0
总结:由父及子
- 当你调用main方法时,其实是通过类名.main调用的
- 然后类需要先加载,加载当前类的时候会先把依赖的类先加载
- 加载类的时候会执行其中的静态代码块
- 调用构造器创建对象的时候会先执行普通代码块中的方法,其实你看字节码文件的时候你会发现,普通代码块的内容会出现在构造器中
//class字节码文件
class Root {
public Root() {
System.out.println("Root的普通初始化快");
System.out.println("Root的无参数的构造器");
}
static {
System.out.println("Root的静态初始化快");
}
}
- 在构造器的首行,没有显式的声明this(形参列表) 或 super(形参列表) 则默认调用的是父类的构造器
面试题 2
class Father{
static {
System.out.println("111父亲的静态代码块");
}
{
System.out.println("222父亲的非静态代码块");
}
public Father() {
System.out.println("333父亲的无参构造");
}
}
public class Son extends Father{
static {
System.out.println("444Son中静态代码块");
}
{
System.out.println("555Son中非静态代码块");
}
public Son() {
System.out.println("666Son中的无参构造");
}
//虽然作为方法的入口,但是也是个静态方法,通过类去调,类去调之前要先
//加载
public static void main(String[] args) {
System.out.println("777Son中静态main方法");
System.out.println("******************");
new Son();
System.out.println("------------------");
new Son();
System.out.println("++++++++++++++++++");
new Father();
}
}
111父亲的静态代码块
444Son中静态代码块
777Son中静态main方法
******************
222父亲的非静态代码块
333父亲的无参构造
555Son中非静态代码块
666Son中的无参构造
------------------
222父亲的非静态代码块
333父亲的无参构造
555Son中非静态代码块
666Son中的无参构造
++++++++++++++++++
222父亲的非静态代码块
333父亲的无参构造
Process finished with exit code 0
面试题 3
public class OrderTest {
public static void main(String[] args) {
Order order = new Order();
System.out.println(order.orderId);
}
}
class Order{
{
orderId=4;
// System.out.println(orderId); // 非法前向引用
System.out.println("执行到这");
}
int orderId=3;
}
执行到这
3
结果: 这两个执行顺序是看是谁在前就先执行谁,谁在后就后执行谁. 但是不能非法向前引用
可以看看对应的class文件,编译器会进行优化整理的
class Order {
int orderId = 4;
Order() {
System.out.println("执行到这");
this.orderId = 3;
}
}
面试题 4
public class Test {
public static Test t1=new Test();①
{
System.out.println("blockA");②
}
static {
System.out.println("blockB");
}
public static void main(String[] args)
{
Test t2=new Test();
}
}
结果:
blockA
blockB
blockA
那我们通过断点和class文件看看为什么
- 通过类名.main调用main方法,那么就会去加载这个类
- 然后到达① 执行,这时候又会重新进入这个类,这是会认为这个类是已经加载完成的,就会直接执行普通代码块方法② 输出:blockA,然后构造器方法,最后出出栈回到上次的地方
- 接着执行静态代码块的方法,静态代码块优先于普通代码块,输出blockB,最后在执行普通代码块中的方法。
变形:我加个无参构造器会怎么样
public class Test {
public static Test t1=new Test();
{
System.out.println("blockA");
}
static {
System.out.println("blockB");
}
public Test() {
System.out.println("AA");
}
public static void main(String[] args)
{
Test t2=new Test();
}
}
结果:
blockA
AA
blockB
blockA
AA