zoukankan      html  css  js  c++  java
  • Java面试系列第3篇-类的加载及Java对象的创建

     下面介绍Java面试中常见的对象加载及创建题目。

    1、Java对象初始化顺序

    先看一下如下笔试题目:

    class Parent {
    	public static int a = 2;
    	public int b = 3;
    	// 2
    	{
    		System.out.println("this is anonymity b=" + b);
    	}
    	// 1
    	static {
    		a = 4;
    		System.out.println("this is static and a=" + a);
    	}
    
    	// 3
    	public Parent() {
    		System.out.println("this is Parent constructor b="+b);
    	}
    
    }
    
    public class Son extends Parent {
    	public int b = 0;
    	// 4
    	public Son() {
    		System.out.println("this is Son constructor b="+b);
    	}
    
    
    	public static void main(String[] args) {
    		new Son();
    	}
    }

    当创建对象时,涉及到静态和非静态变量的初始化、静态和非静态匿名块的初始化以及构造函数的初始化,所以好多面试者不容易记住也不容易理解,下面我把这个类重构一下,变为如下的形式:

    class Parent {
    	public static int a;
    	public int b;
    	
    	
    	public <cinit>(){
    		a = 2;
    		
    		static {
    			a = 4;
    			System.out.println("this is static and a=" + a);
    		}
    	}
    	
    	public <init>(){
    		b = 3;
    		 
    		System.out.println("this is Parent constructor b="+b);
    		 
    		{
    			System.out.println("this is anonymity b=" + b);
    		}
    	}
    }
    
    public class Son extends Parent {
    	public <cinit>(){
    		
    	}
    	
    	public <init>(){
    		System.out.println("this is  Son constructor");
    	}
    	
    	// ...
    }
    

    Javac编译器在生成字节码之前会将类调整为如上的样子,其中合成的函数<cinit>()可以看作静态构造函数,在类的加载阶段调用,而合成的函数<init>()可以看作是实例构造函数,在对象创建的时候调用。调用子类的<cinit>()方法之前必会先调用父类的<cinit>()方法,这是由类的加载顺序决定的。等类加载完成后就可以创建对象了,同样初始化子类必先初始化父类,也就是先调用父类的<init>()方法,后调用子类的<init>()方法。那么现在的调用顺序就是:

    Parent.<cinit>() -> Son.<cinit>() -> Parent.<init>() -> Son.<init>()  

    在重构<cinit>()方法时,将变量的初始化写在前面,匿名块写在后面;在重构<init>()方法时,将变量的初始化写在前面,将构造函数原有的内容写在中间,最后写匿名块的内容。如果有多个变量或匿名块,就按源代码的顺序写即可。

    那么打印的结果肯定一目了然了,打印的结果如下:

    this is static and a=4
    this is anonymity b=3
    this is Parent constructor b=3
    this is Son constructor b=0
    

    如果还想了解更多,比如Javac编译器为什么要进行这样的高速,可以参考《深入解析Java编译器:源码剖析与实例详解》一书。 

    2、对象创建的几种方式

     要熟记对象创建的几种方式,如下:

    (1)使用new 关键字 使用 new 关键字创建对象,实际上是做了两个工作,一是在内存中开辟空间,二是初始化对象;

    (2)使用反射创建对象 反射创建对象分为两种方式,一是使用Class类的newInstance() 方法,二是使用Constructor类的newInstatance() 方法。Class.newInstance() 只能够调用无参的构造函数,即默认的构造函数; Constructor.newInstance() 可以根据传入的参数,调用任意构造构造函数;

    (3)使用clone()方法;

    (4)使用序列化。

    最广泛使用的应该就是使用new关键字和使用反射创建了。对于反射创建来说,如下:

    Class.forName(className).newInstance();
    

    调用forName()只会加载类,要得到对象需要调用newInstance()方法。这种创建方式在框架中用的多,就是因为forName()方法的参数是字符串类型,通过这个字符串来指定要加载的类,我们就可以通过配置的形式来指定加载的类型了,用起来很方便。另外需要注意,由于Class.forName()需要调用本地方法加载类,所以过程比较耗时,为了提高反射的性能,可以适当缓存这个获取到的对象。

    3、对象的创建过程

     

  • 相关阅读:
    关于TensorFlow2的tf.function()和AutoGraph的一些问题解决
    voxelmorph配置
    python处理nii格式文件
    mysql总结
    JVM内存模型
    Java线程池面试
    java NIO基础
    面试日记
    PhoenixFD插件流体模拟——UI布局【Gird】详解
    PhoenixFD插件流体模拟——UI布局【Resimulation】详解
  • 原文地址:https://www.cnblogs.com/extjs4/p/12776762.html
Copyright © 2011-2022 走看看