Java对象的前世今生

news2024/9/27 23:29:01

文章目录

    • 一、创建对象的步骤
    • 二、类加载机制
    • 三、内存分配
      • 指针碰撞 (内存连续)
      • 空闲列表 (内存不连续)
    • 四、创建对象的5种方法
    • 五、浅拷贝与深拷贝

以下一行代码内部发生了什么?

Person person = new Person();

一、创建对象的步骤

  根据JLS中的规定,Java对象的创建过程可以简要地描述为以下几个步骤:类加载、分配内存、初始化、调用构造方法、返回对象应用
在这里插入图片描述

  1. 类加载:包括查找并加载类的字节码.class文件,并将其转换成可执行代码,使得JVM可以执行这些类。
  2. 分配内存:当使用new关键字创建一个对象时,Java虚拟机会在堆内存中分配一块连续的内存空间来存储对象的实例变量和引用类型。
  3. 初始化:分配内存后,Java虚拟机对新创建的对象进行初始化。这包括对对象的实例变量进行默认初始化(数值类型为0,引用类型为null),并调用相应的构造方法来完成对象的初始化过程。如果对象存在继承关系,会从父类到子类依次调用构造方法,确保所有父类的构造方法都得到执行。
  4. 调用构造方法:构造方法是特殊的方法,用于初始化对象的状态。在对象初始化的过程中JVM会调用与类对应的构造方法来完成实例变量的初始化以及其他必要的初始化操作。
  5. 返回对象引用:对象初始化完成后new表达式会返回对新创建对象的引用,使得程序可以通过该引用访问和操作对象。

二、类加载机制

  JVM提供了三个类加载器,分别是BootStrap > Extension > Application,也可以自定义。
为了保证安全性和避免重复加载,设计了双亲委派机制
  即按照类加载器的层级关系逐层进行委派。如果父加载器无法加载,自己再尝试加载。如果已经加载过,自己就不用再重复加载。
  只要是符合 JVM 规范的字节码,不管通过 Java 源码编译生成的还是使用Java 字节码操纵工具类库(ASM、Javassist、cglib)生成的,都可以交给类加载器去加载。

三、内存分配

  JVM在运行Java程序时,需要管理内存的分配和回收。在内存管理的过程中,有两种常见的方式:指针碰撞(Bump the Pointer)和空闲列表(Free List)。JVM根据堆的不同区域和不同用途,选择不同的分配方式,具体的实现取决于不同的JVM实现和垃圾收集器策略。

指针碰撞 (内存连续)

  常用于实现固定大小的内存分配。JVM维护一个指向空闲内存区域的指针(称为"指针碰撞指针"),并且假定堆中的内存是连续的,分配内存就是将指针往前移动一定的字节数,然后将移动后的位置返回给请求分配内存的程序。

  该方式是以堆的内存是连续(内存规整)的作为前提条件的,这就要求Java虚拟机在启动时就要知道堆的大小,这样才能在堆的起始地址设置指针碰撞指针。因此,该方式不适用于动态扩展堆内存的情况。

空闲列表 (内存不连续)

  适用于动态扩展堆内存的情况。JVM维护一个空闲内存块列表,记录哪些内存块是可用的,而分配内存时,会遍历空闲列表,找到合适大小的内存块,并将其标记为已分配。这样,内存的分配可以更加灵活,不需要连续的内存空间。

  缺点是会产生一定的内存碎片。该问题JVM采用了内存整理算法来解决,如压缩、分代回收等。

四、创建对象的5种方法

  1. 使用new关键字:可以调用类的构造方法来创建对象,并在堆内存中为对象分配空间。
Person person = new Person();
  1. 使用反射:通过Class类的newInstance()方法或者Constructor类的newInstance()方法可以创建对象。
Class<?> personClass = Class.forName("com.example.Person");
Person person= (Person) personClass.newInstance();
  1. 使用clone()方法:可以实现对象的浅拷贝或者深拷贝。
Person person = new Person();
Person clonedPerson= (Person) person.clone();
  1. 使用对象工厂:对象工厂用于封装对象的创建过程,可以根据需要返回不同类型的对象实例。
public class PersonFactory {
    public static Person createPerson() {
        return new Person();
    }
}

Person person = PersonFactory.createPerson();
  1. 使用静态工厂方法:类中的静态方法可以作为工厂方法,用于创建对象实例。静态工厂方法通常有自定义的名称,便于描述对象的创建方式。
public class Person{
    private Person() { }

    public static Person createPerson() {
        return new Person();
    }
}
Person person = Person.createPerson();

五、浅拷贝与深拷贝

浅拷贝(Shallow Copy)
  浅拷贝是复制对象本身和对象中的基本数据类型的字段,但不会复制对象中的引用类型字段。
  在浅拷贝中,被复制对象和新创建的对象会共享引用类型字段,这意味着如果改变了一个对象中的引用类型字段,那么另一个对象中的对应字段也会受到影响。

class Person {
    private String name;
    private Address address;
}

Person originalPerson = new Person("Patrick", new Address("BJ"));
Person copiedPerson = (Person) originalPerson.clone();

  originalPerson对象和copiedPerson对象是两个独立的对象,但是它们共享同一个Address对象,如果修改了copiedPerson对象的Address对象,那么originalPerson对象的Address对象也会随之改变。
  浅拷贝常用的API有Spring的BeanUtils、Apache commons包中的PropertyUtils、实现Cloneable接口、Arrays的copyOf()方法

深拷贝(Deep Copy)
  深拷贝是复制对象本身和对象中的所有字段,包括引用类型字段。不是复制该对象的地址,而是递归复制该对象所有引用类型成员的副本。即被复制对象和新创建的对象完全独立,它们拥有各自的引用类型字段的副本,因此修改一个对象的引用类型字段不会影响另一个对象。

class Person {
    private String name;
    private Address address;
    
    public Person deepCopy() {
        Person newPerson = new Person(this.name, this.address.deepCopy());
        return newPerson;
    }
}

class Address {
    private String city;
    
    public Address deepCopy() {
        return new Address(this.city);
    }
}

Person originalPerson = new Person("Patrick", new Address("BJ"));
Person copiedPerson = originalPerson.deepCopy();

deepCopy()方法分别在PersonAddress类中实现了深拷贝。

深拷贝常用的5个API

  1. 重写clone方法,每个对象都要实现Cloneable接口并重写Object类中的clone方法
  2. 序列化时必须实现Serializable接口
  3. Apache commons工具包SerializationUtils.clone(T object)
  4. 通过JSON工具类实现深拷贝
  5. 通过手动new对象实现构造器方法的深拷贝

动态代理,本质上就是在特定的时机,去修改已有类型实现,或者创建新的类型。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/838256.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

牢记这16个SpringBoot 扩展接口,写出更加漂亮的代码

1、背景 Spring的核心思想就是容器&#xff0c;当容器refresh的时候&#xff0c;外部看上去风平浪静&#xff0c;其实内部则是一片惊涛骇浪&#xff0c;汪洋一片。Springboot更是封装了Spring&#xff0c;遵循约定大于配置&#xff0c;加上自动装配的机制。很多时候我们只要引…

Scala编程语言入门教程

Scala教程 方便个人学习和查阅 学习目标 Scala介绍 简介 Scala创始人Martin Odersky马丁奥德斯基 再回到我们的scala语言&#xff0c;在Scala官网https://www.scala-lang.org/介绍了其六大特征。 Java和scala可以混编 类型推测(自动推测类型) 并发和分布式&#xff08;Ac…

nginx的优化和防盗链 重要!!!

实验一、隐藏版本号 要把nginx的版本号隐藏起来&#xff0c;防止恶意攻击 方法一&#xff1a;修改配置文件 在http模块中加入一个命令 server_token off&#xff1b; 过程&#xff1a; 备份&#xff0c;改配置文件一定要备份 修改配置文件 在http模块中添加 server_tokens …

城市与AI,正待济沧海

厦门&#xff0c;自古以来是海上丝绸之路的战略支点&#xff0c;是中国海洋贸易网络中的明珠。外来的理念与技术自厦门而入&#xff0c;中国的文化与商品自厦门而出。新事物、新科技往往在这里焕发最耀眼的生机——比如说&#xff0c;人工智能。 多年来&#xff0c;厦门持续推进…

外部排序算法总结

一.内排总结 在之前博客里&#xff0c;博主已经介绍了各种内部排序算法的原理和C语言代码实现&#xff0c;不懂的朋友可以在同系列专栏里选择查看&#xff0c;今天介绍常见排序算法的最后一点&#xff0c;也就是外部排序。在此之前&#xff0c;我们先对外部排序的各种算法做一…

Centos7/8 安装/配置 Redis5

目录 一、安装 Redis 二、创建符号链接 1.针对可执行程序设置符号链接 2.针对配置文件设置符号链接 三、修改配置文件 1.修改 ip 地址 2.关闭保护模式 四、设置工作目录 1.创建工作目录 2.在配置文件中&#xff0c;配置工作目录 五、设置日志文件 1.创建日志目录 2…

vue3中的自定义指令用法

我们都知道vue2中自定义指令全局和局部是这样写的 局部&#xff1a; 全局&#xff1a; 可vue3写法发生改变&#xff0c;如下&#xff1a; 全局&#xff1a; 局部&#xff1a;

python高阶技巧

目录 设计模式 单例模式 具体用法 工厂模式 优点 闭包 案例 修改闭包外部变量 闭包优缺点 装饰器 装饰器原理 装饰器写法 递归 递归的调用过程 递归的优缺点 用递归计算阶乘 设计模式 含义&#xff1a;设计模式是一种编程套路&#xff0c;通过这种编程套路可…

Docker极速安装Jenkins

安装 Jenkins 是一个常见的任务&#xff0c;使用 Docker 进行安装可以简化该过程并确保环境一致性。以下是在 Docker 中安装 Jenkins 的详细步骤&#xff1a; 安装 Docker: 首先&#xff0c;请确保您已在目标机器上安装了 Docker。根据您的操作系统&#xff0c;可以在 Docker 官…

展示Streamlit文本魔力(六):从头顶到脚尖

文章目录 1 前言✨2 st.markdown - 引入丰富的Markdown文本3 st.title - 引入引人注目的大标题4 st.header - 引入简洁的小标题5 st.subheader - 添加次级标题6 st.caption - 添加解释性文字7 st.code - 显示代码块8 st.text - 显示文本9 st.latex - 显示LaTeX公式10 st.divide…

【导出Word】如何使用Java+Freemarker模板引擎,根据XML模板文件生成Word文档(只含文本内容的模板)

这篇文章&#xff0c;主要介绍如何使用JavaFreemarker模板引擎&#xff0c;根据XML模板文件生成Word文档。 目录 一、导出Word文档 1.1、基础知识 1.2、制作模板文件 1.3、代码实现 &#xff08;1&#xff09;引入依赖 &#xff08;2&#xff09;创建Freemarker工具类 &…

linux下绑定进程到指定CPU的操作方法

taskset简介 # taskset Usage: taskset [options] [mask | cpu-list] [pid|cmd [args...]] Show or change the CPU affinity of a process. Options: -a, --all-tasks operate on all the tasks (threads) for a given pid -p, --pid operate on ex…

高级web前端开发工程师的职责说明(合集)

高级web前端开发工程师的职责说明1 职责&#xff1a; 1、根据需求文档&#xff0c;完成PC端、移动端页面及交互的开发&#xff0c;并保证兼容性和确保产品具有优质的用户体验; 2、熟练使用 HTML 、 CSS 、 JS 、 Ajax 等技术&#xff0c;能解决各种浏览器兼容性问题&#xff…

小鱼深度产品测评之:阿里云容器服务器ASK,一款不需购买节点,即可直接部署容器应用。

容器服务器ASK测评 1、引言2、帮助文档3、集群3.1集群列表3.1.1 详情3.1.1.1概览 tab3.1.1.2基本信息 tab3.1.1.4集群资源 tab3.1.1.5 集群日志 tab3.1.1.6 集群任务 tab 3.1.2 应用管理3.1.2.1 详情3.1.2.2 详情3.1.2.3 伸缩3.1.2.4 监控 3.1.3 查看日志3.1.3.1 集群日志3.1.3…

【网络基础实战之路】设计网络划分的实战详解

系列文章传送门&#xff1a; 【网络基础实战之路】设计网络划分的实战详解 【网络基础实战之路】一文弄懂TCP的三次握手与四次断开 【网络基础实战之路】基于MGRE多点协议的实战详解 【网络基础实战之路】基于OSPF协议建立两个MGRE网络的实验详解 PS&#xff1a;本要求基于…

机器学习笔记之优化算法(十)梯度下降法铺垫:总体介绍

机器学习笔记之优化算法——梯度下降法铺垫&#xff1a;总体介绍 引言回顾&#xff1a;线搜索方法线搜索方法的方向 P k \mathcal P_k Pk​线搜索方法的步长 α k \alpha_k αk​ 梯度下降方法整体介绍 引言 从本节开始&#xff0c;将介绍梯度下降法 ( Gradient Descent,GD ) …

vue2-组件和插件的区别

1、组件是什么&#xff1f; 组件就是把图形、非图形的各种逻辑均抽象为一个统一的概念&#xff08;组件&#xff09;来实现开发的模式&#xff0c;在vue中每一个.vue文件都可以被视为一个组件。 组件的优势&#xff1a; 降低整个系统的耦合度&#xff0c;在保持接口不变的情况下…

学C的第三十二天【动态内存管理】

相关代码gitee自取&#xff1a;C语言学习日记: 加油努力 (gitee.com) 接上期&#xff1a; 学C的第三十一天【通讯录的实现】_高高的胖子的博客-CSDN博客 1 . 为什么存在动态内存分配 学到现在认识的内存开辟方式有两种&#xff1a; 创建变量&#xff1a; int val …

ardupilot 中坐标变换矩阵和坐标系变换矩阵区别

目录 文章目录 目录摘要1.坐标变换矩阵与坐标系变换矩阵摘要 本节主要记录ardupilot 中坐标变换矩阵和坐标系变换矩阵的区别,这里非常重要,特别是进行姿态误差计算时,如果理解错误,很难搞明白后面算法。 1.坐标变换矩阵与坐标系变换矩阵 坐标变换矩阵的本质含义:是可以把…

webpack基础知识八:说说如何借助webpack来优化前端性能?

一、背景 随着前端的项目逐渐扩大&#xff0c;必然会带来的一个问题就是性能 尤其在大型复杂的项目中&#xff0c;前端业务可能因为一个小小的数据依赖&#xff0c;导致整个页面卡顿甚至奔溃 一般项目在完成后&#xff0c;会通过webpack进行打包&#xff0c;利用webpack对前…