首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

对象创建的边界在哪里?那些你可能不知道的Java黑魔法

  • 25-04-17 17:21
  • 4552
  • 11312
juejin.cn

平时在写java代码的时候,创建对象都是使用的new关键字,框架中呢,常使用反射创建对象。后来在看八股的时候看到一个问题,问java中创建对象的方式,当时没专门了解过,思来想去也只想到了这两个方法,还是平时的积累太薄弱了,于是我就想来专门的记录一篇文章,用来记录一下这个问题。


我们在介绍之前,先准备一个自己的对象
我这里简单创建一个User类,后续的代码都会在这个User类上面进行操作

java
代码解读
复制代码
public class User { private String name; private int age; public User() { // 在构造函数中打印一句话,后面可以用来查看创建对象时是否使用了构造函数 System.out.println("通过构造函数创建 User 对象"); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "User{" + "name='" + name + ''' + ", age=" + age + '}'; } }

1. new 关键字

第一个方法毫无疑问就是我们最常用的new关键字了,这个大家都知道,就不做过多的描述了。
直接给出main函数的代码。

java
代码解读
复制代码
public class Main { public static void main(String[] args) { User user = new User(); } }

2. 反射

我们通过 User 的Class 类对象获取这个类的构造器对象,然后调用 Constructor 对象的newInstance()方法来创建对象。

java
代码解读
复制代码
public class Main { public static void main(String[] args) { try { Constructor constructor = User.class.getDeclaredConstructor(); User user = constructor.newInstance(); user.setName("张三"); user.setAge(18); System.out.println(user); } catch (Exception e) { System.out.println("出异常了!"); } } }

从运行截图可以看出来,对象创建成功,并且调用了 User类的构造器。

该方法可以通过暴力反射调用私有的构造器 constructor.setAccessible(true);

image.png

3. 克隆

我这里重点是创建对象,就不介绍深克隆和浅克隆了。
克隆方式创建对象需要让User实现Cloneable接口并重写clone()方法,此方法是 protected 的,不重写无法调用。
User类修改部分代码:

java
代码解读
复制代码
public class User implements Cloneable{ private String name; private int age; @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }

main函数

java
代码解读
复制代码
public class Main { public static void main(String[] args) { try { User user = new User(); user.setAge(18); user.setName("张三"); User clone = (User) user.clone(); System.out.println(clone); } catch (CloneNotSupportedException e) { throw new RuntimeException(e); } } }

运行结果:

注意:

  • 出来的对象和原来的对象的值是一样的,用类型只是复制了引用类型的值(浅克隆)
  • 克隆的方式创建对象不会调用该类型的构造方法,运行结果图中的构造器调用是创建原始对象是调用的,克隆对象没有调用构造方法

image.png

4. 反序列化

使用反序列化的方式创建对象,需要实现Serializable接口。
我这里是直接序列化成的字节数组,持久化到磁盘文件是一样的

java
代码解读
复制代码
public class Main { public static void main(String[] args) { User user = new User(); user.setAge(18); user.setName("张三"); try { // 序列化 byte[] bytes = serialize(user); // 反序列化为对象 User user1 = deserialize(bytes); System.out.println(user1); System.out.println(user1 == user); } catch (Exception e) { throw new RuntimeException(e); } } // 把对象序列化成 byte[] 数组 public static byte[] serialize(User user) throws IOException { ByteArrayOutputStream bs = new ByteArrayOutputStream(); ObjectOutputStream os = new ObjectOutputStream(bs); // 使用对象流把对象写入 os.writeObject(user); byte[] bytes = bs.toByteArray(); return bytes; } // 反序列化为对象 public static User deserialize(byte[] bytes) throws IOException, ClassNotFoundException { ByteArrayInputStream bs = new ByteArrayInputStream(bytes); ObjectInputStream os = new ObjectInputStream(bs); return (User) os.readObject(); } }

运行截图:

  • 反序列化不会调用User的构造方法,但是会调用父类的构造方法,这里就不演示了
  • 反序列化的对象和原来的对像不是用一个对象

image.png

5. MethodHandle

这种方法类似于反射,但是它是另一套 API
indConstructor(User.class, MethodType.methodType(void.class))这个方法是找到对应的构造器,第一个参数的类的类型,第二个参数是MethodType,分别填入返回值类型,和对应参数的类型。

java
代码解读
复制代码
public class Main { public static void main(String[] args) { try { MethodHandle constructor = MethodHandles.lookup().findConstructor(User.class, MethodType.methodType(void.class)); // 调用构造方法 User user = (User) constructor.invoke(); user.setName("张三"); user.setAge(21); System.out.println(user); } catch (Exception e) { throw new RuntimeException(e); } catch (Throwable e) { throw new RuntimeException(e); } } }

运行截图:

image.png

6. Unsafe

可以通过Unsafe来创建对象,因为Unsafe有些特别,我这里使用反射来获取Unsafe的对象。

java
代码解读
复制代码
public class Main { public static void main(String[] args) { try { Class klass = Unsafe.class; Field field = klass.getDeclaredField("theUnsafe"); field.setAccessible(true); Unsafe unsafe = (Unsafe) field.get(null); User user = (User) unsafe.allocateInstance(User.class); user.setName("张三"); user.setAge(16); System.out.println(user); } catch (Exception e) { throw new RuntimeException(e); } } }

运行截图:

  • 通过Unsafe创建的对象不会调用构造器

image.png

注:本文转载自juejin.cn的未完结小说的文章"https://juejin.cn/post/7480098720123830322"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

103
后端
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top