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

  

 

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

  1. 必须知道的Java八大排序算法

    冒泡排序.简单选择.直接插入.快速排序.堆排序.希尔排序.归并排序.基数排序. 将其按排序方式分类如下图所示: 1.冒泡排序: 基本思想——在要排序的一组数中,对当前还未排好序的范围内的全部数据,自上 ...

  2. 从零开始学习jQuery(剧场版) 你必须知道的javascript

    原文:从零开始学习jQuery(剧场版) 你必须知道的javascript 一.摘要 本文是jQuery系列教程的剧场版, 即和jQuery这条主线无关, 主要介绍大家平时会忽略的一些javascri ...

  3. [你必须知道的.NET]第二十五回:认识元数据和IL(中)

    发布日期:2009.02.25 作者:Anytao © 2009 Anytao.com ,Anytao原创作品,转贴请注明作者和出处. 说在,开篇之前 书接上回[第二十四回:认识元数据和IL(上)], ...

  4. C语言学习书籍推荐《你必须知道的495个C语言问题》

    萨米特 (Steve summit) (作者), 孙云 (译者), 朱群英 (译者) 下载地址:点我 <你必须知道的495个C语言问题>以问答的形式组织内容,讨论了学习或使用C语言的过程中 ...

  5. c语言学习书籍推荐《C语言学习路线图•C语言必须知道的300个问题》下载

    下载地址:点我 <C语言学习路线图•C语言必须知道的300个问题>以基础知识为框架,介绍了c语言各部分知识所对应的常见开发疑难问题,并作了透彻地解析.<C语言学习路线图•C语言必须知 ...

  6. C#刨根究底:《你必须知道的.NET》读书笔记系列

    一.此书到底何方神圣? <你必须知道的.NET>来自于微软MVP—王涛(网名:AnyTao,博客园大牛之一,其博客地址为:http://anytao.cnblogs.com/)的最新技术心 ...

  7. (转)【推荐】初级.NET程序员,你必须知道的EF知识和经验

    转自:http://www.cnblogs.com/zhaopei/p/5721789.html [推荐]初级.NET程序员,你必须知道的EF知识和经验   阅读目录   [本文已下咒.先顶后看,会涨 ...

  8. 《你必须知道的.NET》读书笔记一:小OO有大智慧

    此篇已收录至<你必须知道的.Net>读书笔记目录贴,点击访问该目录可以获取更多内容. 一.对象  (1)出生:系统首先会在内存中分配一定的存储空间,然后初始化其附加成员,调用构造函数执行初 ...

  9. 《你必须知道的.NET》读书笔记二:小OO有大原则

    此篇已收录至<你必须知道的.Net>读书笔记目录贴,点击访问该目录可以获取更多内容. 一.单一职责原则 (1)核心思想:一个类最好只做一件事,只有一个引起它变化的原因 (2)常用模式:Fa ...

随机推荐

  1. MySQL设置慢查询

    MySQL的慢查询日志是用来记录在MySQL中响应时间超过阀值的语句,则会被记录到慢查询日志中(运行时间超过long_query_time值的SQL语句):   慢查询相关参数: slow_query ...

  2. spark load data from mysql

    spark load data from mysql code first 本机通过spark-shell.cmd启动一个spark进程 SparkSession spark = SparkSessi ...

  3. Zotero导入Markdown here插件

    1. 下载Markdown Here源码包 网址:https://github.com/adam-p/markdown-here 2. 创建.xpi后缀文件 将文件夹 中的这几个文件放入同一个文件夹中 ...

  4. Redis基础(二)数据库

    数据库 Redis服务器的所有数据库都保存在redisServer.db数组中,而数据库的数量则由redisServer.dbnum属性保存. struct redisServer { // .. / ...

  5. 梯度vs Jacobian矩阵vs Hessian矩阵

    梯度向量 定义: 目标函数f为单变量,是关于自变量向量x=(x1,x2,-,xn)T的函数, 单变量函数f对向量x求梯度,结果为一个与向量x同维度的向量,称之为梯度向量: 1. Jacobian 在向 ...

  6. hello world .net core 微服务框架 Viper

    1.Viper是什么? Viper 是.NET平台下的Anno微服务框架的一个示例项目.入门简单.安全.稳定.高可用.全平台可监控.底层通讯可以随意切换thrift grpc. 自带服务发现.调用链追 ...

  7. mysql 两主一从环境搭建(5.7.24)

    搭建说明 两主一从,从本质上说,只不过是机器 master-a 和 master-b 互为主从机(热备),然后通过 keepalived 进行高可用配置,使得在同一时间内只会有一台对外提供服务,实现单 ...

  8. 部署Dotnet Core应用到Kubernetes(一)

    最近闲了点,写个大活:部署Dotnet应用到K8s.   写在前边的话 一直想完成这个主题.但这个主题实在太大了,各种拖延症的小宇宙不时爆发一下,结果就拖到了现在.   这个主题,会是一个系列.在这个 ...

  9. 内网渗透 day8-linux提权和后门植入

    linux提权和后门植入 目录 1. 脏牛漏洞复现 3 (1) 去网上把代码复制然后touch一个.c文件,vi或者vim打开将代码复制进去保存 3 (2) 进入shell然后从kali开的apach ...

  10. NIO源码分析:SelectionKey

    SelectionKey SelectionKey,选择键,在每次通道注册到选择器上时都会创建一个SelectionKey储存在该选择器上,该SelectionKey保存了注册的通道.注册的选择器.通 ...