面试阿里,字节跳动,华为必须知道的Java创建对象的5种方式

Java创建对象的5种方式

1.直接new,调用了构造器

2.通过clone(),没有调用构造器

3.通过反射,调用了构造器

4.通过反序列化,没有调用构造器

5.通过Unsafe类的allocateInstance()方法,没有调用构造器

1. 直接new

public class CreateByNew {

    public CreateByNew() {
        System.out.println("调用了构造...");
    }

    public static void main(String[] args) {
        CreateByNew c1 = new CreateByNew();
        CreateByNew c2 = new CreateByNew();
        System.out.println(c1 == c2);//false
    }
}

输出:

调用了构造...
调用了构造...
false

2. 通过clone()

需要实现Cloneable接口,可分为深克隆和浅克隆。clone()后的新对象会复制原对象的属性,但是并不会调用构造函数。

public class CreateByClone implements Cloneable {

    public int temp;

    public CreateByClone() {
        System.out.println("调用了构造..");
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        CreateByClone c1 = new CreateByClone();
        c1.temp = 222;
        CreateByClone c2 = (CreateByClone) c1.clone();
        System.out.println(c2.temp);
        System.out.println(c1 == c2);
    }
}

输出:

调用了构造..
222
false

3. 通过反射

反射创建对象:

class.newInstance():调用了无参构造

获取对应的Constructor,调用constructor的newInstance(),调用对应构造函数创建对象

public class CreateByReflection {

    private int temp;

    public int getTemp() {
        return temp;
    }

    public CreateByReflection() {
        System.out.println("调用了空参构造...");
    }

    public CreateByReflection(int temp) {
        this.temp = temp;
        System.out.println("调用了带参构造...");
    }

    public static void main(String[] args) throws Exception {
        Class clazz = CreateByReflection.class;
        //通过无参构造反射创建
        CreateByReflection c1 = (CreateByReflection) clazz.newInstance();
        //通过带参构造反射创建
        Constructor constructor = clazz.getDeclaredConstructor(int.class);
        CreateByReflection c2 = (CreateByReflection) constructor.newInstance(10);
        System.out.println(c2.getTemp());

        System.out.println(c1 == c2);
    }
}

输出:

调用了空参构造...
调用了带参构造...
10
false

4. 反序列化创建对象

需要被序列化的对象实现Serializable接口,不会调用构造,反序列化回来的对象的属性值与序列化之前一致,但是是一个新对象。

public class CreateBySerializable {
    public static void main(String[] args) {
        Person p1 = new Person("二狗", 18);
        writeObject(p1);
        Person p2 = readObjcet();
        System.out.println(p2);
        System.out.println(p1 == p2);
    }


    public static void writeObject(Person person) {
        FileOutputStream fileOut = null;
        ObjectOutputStream out = null;
        try {
            fileOut = new FileOutputStream("person.txt");
            out = new ObjectOutputStream(fileOut);
            out.writeObject(person);
            System.out.println("Serialized data is saved");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                fileOut.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    public static Person readObjcet() {
        Person temp = null;
        FileInputStream fileIn = null;
        ObjectInputStream in = null;
        try {
            fileIn = new FileInputStream("person.txt");
            in = new ObjectInputStream(fileIn);
            temp  = (Person) in.readObject();
            System.out.println("Deserialized Person...");
            return temp;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                fileIn.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}


class Person implements Serializable {
    public Person() {
        System.out.println("调用了空参构造...");
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("调用了带参构造...");
    }

    public String name;
    public int age;

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '/'' +
                ", age=" + age +
                '}';
    }
}

输出:

调用了带参构造...
Serialized data is saved
Deserialized Person...
Person{name='二狗', age=18}
false

5. 通过Unsafe类

Unsafe类通过native方法直接操作内存分配空间,创建对象。此时对象并没有执行构造,只是在内存中分配了空间,所有属性此时都是对应类型的0值。而且该对象并不被JVM管理,需要我们自己回收。

Unsafe类的构造为私有,且通过@CallerSensitive方法保证只有BootStrap类加载器加载的类才可以调用Unsafe类中的方法。

所以只能通过反射获取Unsafe类实例

public class CreateByUnsafe {
    public static void main(String[] args) throws Exception {
        //基于反射获取Unsafe实例
        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        Unsafe unsafe = (Unsafe) field.get(null);

        People p1 = (People) unsafe.allocateInstance(People.class);
        People p2 = (People) unsafe.allocateInstance(People.class);
        p1.age = 18;
        System.out.println(p1);
        System.out.println(p1 == p2);

        //返回成员变量在内存中的地址相对于对象内存地址的偏移量
        Field f = People.class.getDeclaredField("age");
        long offset = unsafe.objectFieldOffset(f);
        System.out.println(offset);//12
        // markword:8bytes(64bits) + class pointer:4bytes(32bits) == 12 bytes
    }
}

class People {
    public int age;

    @Override
    public String toString() {
        return "People{" +
                "age=" + age +
                '}';
    }
}

输出:

People{age=18}
false
12

原文 

https://segmentfault.com/a/1190000022584331

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » 面试阿里,字节跳动,华为必须知道的Java创建对象的5种方式

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址