java对象初始化面试问题总结
来源:互联网 发布:华大基因 待遇 算法 编辑:程序博客网 时间:2024/05/29 18:37
这是一道阿里巴巴的关于Java对象初始化的面试题,但是需要面试者对Java中对象初始化有一个透彻的认识,首先这道题对我有所启发,所以我将记录下来,大家相互学习。
代码如下:
public class InitializeDemo { private static int k = 1; private static InitializeDemo t1 = new InitializeDemo("t1"); private static InitializeDemo t2 = new InitializeDemo("t2"); private static int i = print("i"); private static int n = 99; static { print("静态块"); } private int j = print("j"); { print("构造块"); } public InitializeDemo(String str) { System.out.println((k++) + ":" + str + " i=" + i + " n=" + n); ++i; ++n; } public static int print(String str) { System.out.println((k++) + ":" + str + " i=" + i + " n=" + n); ++n; return ++i; } public static void main(String args[]) { new InitializeDemo("init"); }}
结果如下:
1:j i=0 n=0 2:构造块 i=1 n=1 3:t1 i=2 n=2 4:j i=3 n=3 5:构造块 i=4 n=4 6:t2 i=5 n=5 7:i i=6 n=6 8:静态块 i=7 n=99 9:j i=8 n=100 10:构造块 i=9 n=101 11:init i=10 n=102
那么为什么会出现上面的结果呢。首先我们要写了解对象的初始化过程是按照什么顺序来进行的。
这里的核心理念有:
1.静态属性和静态代码块都是在类加载的时候初始化和执行,两者的优先级别是一致的,且高于非静态成员,执行按照编码顺序。 2.非静态属性和匿名构造器在所有的构造方法之前执行,两者的优先级别一致,执行按照编码顺序。 3.以上执行完毕后执行构造方法中的代码。
总结一句话就是:(静态属性=静态代码块)> (非静态属性 = 构造块)> 构造方法。
然后我们来逐步分析代码执行过程。
1.运行main方法的时候,JVM会调用ClassLoader类加载器来加载InitializeDemo类,那么一起源于这次加载。 2.上面有五个静态属性,所以会按顺序逐一初始化这五个静态属性。 3.private static int k = 1; 此时将k初始化为1。 4.private static InitializeDemo t1 = new InitializeDemo("t1");创建InitializeDemo对象,这里我的理解是创建对象,初始化非静态属性和构造代码块。因此先执行private int j = print("j");,打印出j,然后执行构造块,最后执行构造方法。 5.private static InitializeDemo t2 = new InitializeDemo("t2");同步骤4。 6.private static int i = print("i");打印i。 7.private static int n = 99;直到这一步,n才被赋值为99,之前是从默认的0开始++的。 8.静态属性初始化完毕,代码走到静态块,打印出静态块,此时n=99。 9.静态属性和静态块执行完毕,然后执行main方法中的代码new InitializeDemo("init"); 10.main方法中创建对象,先初始化非静态属性,private int j = print("j");打印j,然后执行构造块,最后执行构造方法。
这里没有提到基类,如果遇到,加上一点,基类静态优先于衍生类静态执行;
继承对于初始化的影响
这里主要是理解编译时类型和运行时类型的不同,从这个不同中可以看出 this 关键字 和 super 关键字的一些本质区别。例如:
class Fruit{ String color = "unknow"; public Fruit getThis(){ return this; } public void info(){ System.out.println("fruit's method"); }}public class Apple extends Fruit{ String color = "red";//与父类同名的实例变量 @Override public void info() { System.out.println("apple's method"); } public void accessFruitInfo(){ super.info(); } public Fruit getSuper(){ return super.getThis(); } //for test purpose public static void main(String[] args) { Apple a = new Apple(); Fruit f = a.getSuper(); //Fruit f2 = a.getThis(); //System.out.println(f == f2);//true System.out.println(a == f);//true System.out.println(a.color);//red System.out.println(f.color);//unknow a.info();//"apple's method" f.info();//"apple's method" a.accessFruitInfo();//"fruit's method" }}
值得注意的地方有以下几个:
⒈ 第35行 引用变量 a 和 f 都指向内存中的同一个对象,36-37行调用它们的属性时,a.color是red,而f.color是unknow
因为,f变量的声明类型(编译时类型)为Fruit,当访问属性时是由声明该变量的类型来决定的。
⒉ 第39-40行,a.info() 和 f.info()都输出“apple’s method”
因为,f 变量的运行时类型为Apple,info()是Apple重载的父类的一个方法。调用方法时由变量的运行时类型来决定。
⒊ 关于 this 关键字
当在29行new一个Apple对象,在30行调用 getSuper()方法时,最终是执行到第4行的 return this
this 的解释是:返回调用本方法的对象。它返回的类型是Fruit类型(见getThis方法的返回值类型),但实际上是Apple对象导致的getThis方法的调用。故,这里的this的声明类型是Fruit,而运行时类型是Apple
⒋ 关于 super 关键字
super 与 this 是有区别的。this可以用来代表“当前对象”,可用 return 返回。而对于super而言,没有 return super;这样的语句。
super 主要是为了:在子类中访问父类中的属性 或者 在子类中 调用父类中的方法 而引入的一个关键字。比如第24行。
⒌ 在父类的构造器中不要去调用被子类覆盖的方法(Override),或者说在构造父类对象时,不要依赖于子类覆盖了父类的那些方法。这样很可能会导致初始化的失败(没有正确地初始化对象)
因为:前面第1点和第2点谈到了,对象(变量 )有 声明时类型(编译时类型)和运行时类型。而方法的调用取决于运行时类型。
当new子类对象时,会首先去初始化父类的属性,而此时对象的运行时类型是子类,因此父类的属性的赋值若依赖于子类中重载的方法,会导致父类属性得不到正确的初始化值。
示例如下:
class Fruit{ String color; public Fruit() { color = this.getColor();//父类color属性初始化依赖于重载的方法getColor// color = getColor(); } public String getColor(){ return "unkonw"; } @Override public String toString() { return color; } } public class Apple extends Fruit{ @Override public String getColor() { return "color: " + color; }// public Apple() {// color = "red";// } public static void main(String[] args) { System.out.println(new Apple());//color: null } }
Fruit类的color属性 没有正确地被初始化为”unknow”,而是为 null
主要是因为第5行 this.getColor()调用的是Apple类的getColor方法,而此时Apple类的color属性是直接从Fruit类继承的。
- java对象初始化面试问题总结
- Java面试问题总结
- java-----面试问题总结
- Java面试问题总结
- java面试问题总结
- java面试问题总结
- Java面试问题总结
- Java对象初始化顺序问题
- 面试中的问题总结++java
- Java主要面试问题总结
- Java面试问题归纳总结
- 关于java对象初始化的问题
- Java对象数组初始化与NullPointerException问题
- Java对象数组初始化与NullPointerException问题
- Java对象数组初始化与NullPointerException问题
- Java对象数组初始化与NullPointerException问题
- 剖析一个java对象初始化顺序问题
- 剖析一个java对象初始化顺序问题
- 《Netty5.0架构剖析和源码解读》【PDF】下载
- linux C 正则表达式函数库
- IntelliJ Idea 2017 免费激活方法
- <input type="button"/>和<button></button>的区别
- java心得
- java对象初始化面试问题总结
- 小插件,通过js实现邮箱自动提示功能
- Oracle 学习笔记(三)
- 双系统重启进入GRUB解决方案
- bootstrap tabs
- iOS 摘要的生成
- openstack搭建过程中遇到的问题
- Android中原http请求的https实现(URLConnection 、volley)(volley不修改源码)
- HDOJ 1020 究极水题