精华内容
下载资源
问答
  • Java对象创建流程

    千次阅读 2019-05-07 22:05:39
    文章目录Java对象创建流程1.Java普通对象创建1.1new指令1.2分配内存1.3初始化1.4对象的初始设置1.5\方法2.Java对象内存布局2.1对象头2.2实例数据2.3对齐填充 1.Java普通对象创建 这里讨论的仅仅是普通Java...

    Java对象创建的流程


    1.Java普通对象的创建

    这里讨论的仅仅是普通Java对象,不包含数组和Class对象。

    1.1new指令

    ​ 虚拟机遇到一条new指令时,首先去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有,那么须先执行相应的类加载过程。

    1.2分配内存

    ​ 接下来虚拟机将为新生代对象分配内存。对象所需的内存的大小在类加载完成后便可完全确定。分配方式有“指针碰撞(Bump the Pointer)”和“空闲列表(Free List)”两种方式,具体由所采用的垃圾收集器是否带有压缩整理功能决定。

    1.3初始化

    ​ 内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头),这一步操作保证了对象的实例字段在Java代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。

    1.4对象的初始设置

    ​ 接下来虚拟机要对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息。这些信息存放在对象的对象头(Object Header)之中。根据虚拟机当前的运行状态的不同,如对否启用偏向锁等,对象头会有不同的设置方式。

    1.5<init>方法

    ​ 在上面的工作都完成了之后,从虚拟机的角度看,一个新的对象已经产生了,但是从Java程序的角度看,对象创建才刚刚开始—<init>方法还没有执行,所有的字段都还为零。所以,一般来说,执行new指令后悔接着执行init方法,把对象按照程序员的意愿进行初始化(应该是将构造函数中的参数赋值给对象的字段),这样一个真正可用的对象才算完全产生出来。

    2.Java对象内存布局

    ​ 在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header)、实例数据(Instance Data)、对其填充(Padding)。

    在这里插入图片描述

    2.1对象头

    ​ HotSpot虚拟机的对象头包含两部分信息,第一部分用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。
    ​ 对象的另一部分类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例(并不是所有的虚拟机实现都必须在对象数据上保留类型指针,也就是说,查找对象的元数据信息并不一定要经过对象本身)。
    ​ 如果对象是一个Java数组,那在对象头中还必须有一块用于记录数组长度的数据。

    • 元数据:描述数据的数据。对数据及信息资源的描述信息。在Java中,元数据大多表示为注解

    2.2实例数据

    ​ 实例数据部分是对象真正存储的有效信息,也是在程序代码中定义的各种类型的字段内容,无论从父类继承下来的,还是在子类中定义的,都需要记录起来。这部分的存储顺序会虚拟机默认的分配策略参数和字段在Java源码中定义的顺序影响(相同宽度的字段总是被分配到一起)。

    2.3对齐填充

    ​ 对齐填充部分并不是必然存在的,也没有特别的含义,它仅仅起着占位符的作用。由于HotSpot VM的自动内存管理系统要求对象的起始地址必须是8字节的整数倍,也就是说,对象的大小必须是8字节的整数倍。而对象头部分正好是8字节的倍数(1倍或者2倍),因此,当对象实例数据部分没有对齐时,就需要通过对齐填充来补全。

    展开全文
  • java创建对象创建过程分析

    千次阅读 2017-11-27 21:10:45
    2.2对象创建十分频繁,如何解决并发带来的不安全问题   方法一 、同步,即对分配内存空间的动作进行同步处理-------采用CAS加上失败重试的方式保证更新操作的原子性。   方法二 、TLAB,把内存分配的动作按照...

    ####        以Object obj = new Object();的过程进行分析这一行代码的背后,JVM都做了些什么。
    ####一、检查常量池中是否存在该类的符号引用,如果没有,则先加载该类,并解析和初始化
    ####二、为对象分配内存
            内存大小在类加载的时候已经确定大小,如下图:
    这里写图片描述
            分配内存过程中需要考虑的问题:
    2.1内存是否规整
             ① 假设Java 堆中内存是连续规整的,也就是说Heap中一侧是已经使用过的内存空间,而另一侧是空闲空间,则此时使用指针指向起始空闲内存,当需要分配新的空间时,只需要将指针向后移动制定空间大小位置即可完成内存的分配,这种分配方式称为“指针碰撞”(Bump the Pointer
             ② 假设内存并不是连续规整的,空闲和使用的相互交错,则此时JVM就需要记录哪些内存块石可用的,分配时,分配足够大的空间给对象实例,同事更新记录列表。这种方式称为“空闲列表”(Free List)
    而是采用Bump the Pointer的方式还是Free List的方式由JVM Heap是否规整决定,而Heap的规整与否又由所采用的垃圾收集器是否带有亚索整理的功能决定。使用Serial、ParNew等带有Compact过程的收集器时则采用指针碰撞,而使用CMS这种给予Mark Sweep算法收集器时,采用Free List方式。
    2.2对象的创建十分频繁,如何解决并发带来的不安全问题
             方法一、同步,即对分配内存空间的动作进行同步处理-------采用CAS加上失败重试的方式保证更新操作的原子性。
             方法二、TLAB,把内存分配的动作按照线程划分在不同的空间上进行,即每个线程预先分配一小块内存,这种方式称为本地线程分配缓冲(Thread Local Allocation Buffer),这种方式只需要在分配新的TLAB时进行同步锁定
    是否采用TLAB方式可以通过使用-XX:+/-UserTLAB决定
    ####三、内存分配完成后的必要设置
             如:

    1. 该实例为那一个类的实例
    2. 类的元数据信息
    3. 对象的哈希码
    4. 对象的GC分代信息
    5. 等等
      以上信息位于对象的头信息中(Object Header),除此之外,对象的其他字段都还是0,至此一个新的对象已经产生,但是尚未进行初始化。

    ####四、对象的初始化,执行init方法
    至此,一个对象完成创建过程

    展开全文
  • 在本文将 ,我对 Java 对象创建对象内存布局、对象访问定位的三个过程 进行了详细介绍,希望你们会喜欢

    前言

    • 了解 Java 对象从被创建、存储 & 怎么被使用的整个过程十分重要
    • 对应过程则是:对象创建、对象内存布局、对象访问定位的三个过程。
    • 在本文将 ,我对 Java 对象创建、对象内存布局、对象访问定位的三个过程 进行了详细介绍,希望你们会喜欢

    在接下来的日子,我会推出一系列讲解JVM的文章,具体如下;感兴趣可持续关注Carson_Ho的安卓开发笔记

    示意图


    目录

    示意图


    1. 对象创建

    • 在开发使用时,创建 Java 对象仅仅只是是通过关键字new
    A a = new A();
    
    • 可是 Java对象在虚拟机中创建则是相对复杂。今天,我将详解Java对象在虚拟机中的创建过程

    限于普通对象,不包括数组和Class对象等

    1.1 创建过程

    当遇到关键字new指令时,Java对象创建过程便开始,整个过程如下:

    Java对象创建过程

    下面我将对每个步骤进行讲解。

    1.2 过程步骤

    步骤1:类加载检查

    1. 检查 该new指令的参数 是否能在 常量池中 定位到一个类的符号引用
    2. 检查 该类符号引用 代表的类是否已被加载、解析和初始化过

    如果没有,需要先执行相应的类加载过程

    关于类加载请看文章:JVM)Java虚拟机:类加载的5个过程


    步骤2:为对象分配内存

    • 虚拟机将为对象分配内存,即把一块确定大小的内存从 Java 堆中划分出来

    对象所需内存的大小在类加载完成后便可完全确定

    • 关于分配内存,此处主要讲解内存分配方式
    • 内存分配 根据 Java堆内存是否绝对规整 分为两种方式:指针碰撞 & 空闲列表
    1. Java堆内存 规整:已使用的内存在一边,未使用内存在另一边
    2. Java堆内存 不规整:已使用的内存和未使用内存相互交错

    示意图

    方式1:指针碰撞

    • 假设Java堆内存绝对规整,内存分配将采用指针碰撞
    • 分配形式:已使用内存在一边,未使用内存在另一边,中间放一个作为分界点的指示器

    正常状态

    • 那么,分配对象内存 = 把指针向 未使用内存 移动一段 与对象大小相等的距离

    分配内存空间

    方式2:空闲列表

    • 假设Java堆内存不规整,内存分配将采用 空闲列表
    • 分配形式:虚拟机维护着一个 记录可用内存块 的列表,在分配时从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录

    额外知识

    • 分配方式的选择 取决于 Java堆内存是否规整;
    • Java堆是否规整 由所采用的垃圾收集器是否带有压缩整理功能决定。因此:
      1. 使用带 Compact 过程的垃圾收集器时,采用指针碰撞;

    Serial、ParNew垃圾收集器

    1. 使用基于 Mark_sweep算法的垃圾收集器时,采用空闲列表。

    CMS垃圾收集器

    特别注意

    • 对象创建在虚拟机中是非常频繁的操作,即使仅仅修改一个指针所指向的位置,在并发情况下也会引起线程不安全

    如,正在给对象A分配内存,指针还没有来得及修改,对象B又同时使用了原来的指针来分配内存

    所以,给对象分配内存会存在线程不安全的问题。

    解决 线程不安全 有两种方案:

    1. 同步处理分配内存空间的行为

    虚拟机采用 CAS + 失败重试的方式 保证更新操作的原子性

    1. 把内存分配行为 按照线程 划分在不同的内存空间进行
    1. 即每个线程在 Java堆中预先分配一小块内存(本地线程分配缓冲(Thread Local Allocation BufferTLAB)),哪个线程要分配内存,就在哪个线程的TLAB上分配,只有TLAB用完并分配新的TLAB时才需要同步锁。
    2. 虚拟机是否使用TLAB,可以通过-XX:+/-UseTLAB参数来设定。

    步骤3: 将内存空间初始化为零值

    内存分配完成后,虚拟机需要将分配到的内存空间初始化为零(不包括对象头)

    1. 保证了对象的实例字段在使用时可不赋初始值就直接使用(对应值 = 0)
    2. 如使用本地线程分配缓冲(TLAB),这一工作过程也可以提前至TLAB分配时进行。

    步骤4: 对对象进行必要的设置

    如,设置 这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息。

    这些信息存放在对象的对象头中


    • 至此,从 Java 虚拟机的角度来看,一个新的 Java对象创建完毕
    • 但从 Java 程序开发来说,对象创建才刚开始,需要进行一些初始化操作。

    1.3 总结

    下面用一张图总结 Java对象创建的过程

    示意图


    2. 对象的内存布局

    • 问题:在 Java 对象创建后,到底是如何被存储在Java内存里的呢?
    • 答:在Java虚拟机(HotSpot)中,对象在 Java 内存中的 存储布局 可分为三块:
      1. 对象头 存储区域
      2. 实例数据 存储区域
      3. 对齐填充 存储区域

    内存布局

    下面我会详细说明每一块区域。

    2.1 对象头 区域

    此处存储的信息包括两部分:

    • 对象自身的运行时数据(Mark Word
    1. 如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等
    2. 该部分数据被设计成1个 非固定的数据结构 以便在极小的空间存储尽量多的信息(会根据对象状态复用存储空间)
    • 对象类型指针
    1. 即对象指向它的类元数据的指针
    2. 虚拟机通过这个指针来确定这个对象是哪个类的实例

    特别注意

    如果对象 是 数组,那么在对象头中还必须有一块用于记录数组长度的数据

    因为虚拟机可以通过普通Java对象的元数据信息确定对象的大小,但是从数组的元数据中却无法确定数组的大小。


    2.2 实例数据 区域

    • 存储的信息:对象真正有效的信息

    即代码中定义的字段内容

    • 注:这部分数据的存储顺序会受到虚拟机分配参数(FieldAllocationStyle)和字段在Java源码中定义顺序的影响。
    // HotSpot虚拟机默认的分配策略如下:
    longs/doubles、ints、shorts/chars、bytes/booleans、oop(Ordinary Object Pointers)
    // 从分配策略中可以看出,相同宽度的字段总是被分配到一起
    // 在满足这个前提的条件下,父类中定义的变量会出现在子类之前
    
    CompactFields = true;
    // 如果 CompactFields 参数值为true,那么子类之中较窄的变量也可能会插入到父类变量的空隙之中。
    

    2.3 对齐填充 区域

    • 存储的信息:占位符

    占位作用

    • 因为对象的大小必须是8字节的整数倍
    • 而因HotSpot VM的要求对象起始地址必须是8字节的整数倍,且对象头部分正好是8字节的倍数。
    • 因此,当对象实例数据部分没有对齐时(即对象的大小不是8字节的整数倍),就需要通过对齐填充来补全。

    2.4 总结

    示意图


    3. 对象的访问定位

    • 问:建立对象后,该如何访问对象呢?

    实际上需访问的是 对象类型数据 & 对象实例数据

    • 答:Java程序 通过 栈上的引用类型数据(reference) 来访问Java堆上的对象

    由于引用类型数据(reference)在 Java虚拟机中只规定了一个指向对象的引用,但没定义该引用应该通过何种方式去定位、访问堆中的对象的具体位置

    所以对象访问方式取决于虚拟机实现。目前主流的对象访问方式有两种:

    • 句柄 访问
    • 直接指针 访问

    具体请看如下介绍:

    示意图


    4. 总结

    示意图


    请点赞!因为你的鼓励是我写作的最大动力!

    展开全文
  • Java创建对象的四种方式

    万次阅读 多人点赞 2016-05-09 16:47:56
    1.创建对象     通过反射来生成对象有如下两种方式: (1)使用Class对象的newInstance()方法来创建该Class对象对应类的实例。但是这种方式要求该Class对象的对应类有默认的构造器,而执行new...

    1.new
    2.clone
    3.newInstance
    4.反序列化
    5.String s = "abc"(这个是比较特殊的)

    以String类为例

    String sring = null;

     

      Class class1 = String.class;// 该方法最为安全可靠,程序性能更高。
      Class class2 = string.getClass();
      Class class3 = Class.forName("java.lang.String");// 可能抛出ClassNotFoundException异常

     

      一旦获取了该类所对应的Class对象之后,就可以通过调用Class对象的方法来获得该对象和该类的真实信息了。



    一.new出一个对象

    二.利用反射创建对象

    1.创建对象 

     

      通过反射来生成对象有如下两种方式:

    (1)使用Class对象的newInstance()方法来创建该Class对象对应类的实例。但是这种方式要求该Class对象的对应类有默认的构造器,而执行newInstance()方法时实际上是利用默认构造器来创建该类的实例。

    (2)先使用Class对象获取指定的Constructor对象,再调用Construtor对象的newInstance()方法来创建该Class对象对应类的实例。通过这种方式可以选择使用某个类的指定构造器来创建实例。

     

      另外,如果我们不想利用默认构造器来创建java对象,而想利用指定的构造器来创建java对象,则需要利用Construtor对象,每个Construtor对应一个构造器,为了利用指定构造器来创建java对象,需要如下三个步骤:

    (1)获取该Class对象;

    (2)利用该Class对象的getConstrutor方法来获取指定的构造器; 

    (3)调用Construtor的newInstance方法来创建Java对象。

     

    2.调用方法

     

      获取到某个类的Class对象之后,可以通过该Class对象的getMethods方法或者getMethod方法获取全部或指定方法。

      每个Method对象对应一个方法,获得Method对象后,程序就可通过该Method来调用对应的方法,在Method里包含一个invoke方法,该方法签名如下:

      Object invoke(Object obj,Object... args);该方法中的obj是执行该方法的主调,后面的args是执行该方法时传入该方法的实参。

      当通过Method的invoke方法来调用对应的方法时,Java会要求程序必要要有调用该方法的权限。如果程序确实需要调用该对象的私有方法,则可先调用Method对象的:

      setAccessible(boolean flag);方法,将Method对象的accessoble标志设置为指示的布尔值。

      布尔值为true,则表示该Method在使用时应该取消Java语言访问权限检查;

      布尔值为false,则表示该Method在使用时应该实施Java语言访问权限检查;

     

    3.访问属性值

     

      通过Class对象的getFields或getField方法可以获取该类所包括的全部Field(属性)或指定Field,Field提供了如下两组方法来访问属性:

      getXxx(Object obj);获取obj对象该Field的属性值,此处的Xxx对应8个基本类型,如果该属性的类型是引用类型则取消get后面的Xxx。

      setXxx(Object obj,Xxx val);将obj对象的该Field设置成val值,此处的Xxx对应8个基本类型,如果该属性的类型是引用类型则取消set后面的Xxx。

     

    4示例代码如下:

    import java.lang.reflect.Constructor;
    import java.lang.reflect.Method;
    public class ClassTest {
        public static void main(String[] args) throws Exception {
            Object object;
            Class cl = Class.forName("TestMe");
            Method method = cl
                    .getDeclaredMethod("print", new Class[]{String.class});
            Constructor constructor = cl
                    .getDeclaredConstructor(new Class[]{String.class});
            object = constructor.newInstance(new Object[]{"Hello"});
            method.invoke(object, new Object[]{"zhouxianli"});
        }
    }
    class TestMe {
        private String str;
        public TestMe(String str) {
            this.str = str;
            System.out.println("In Constructor str = " + str);
        }
        public void print(String name) {
            System.out.println("In print str = " + str + " and name = " + name);
        }
    } 

    三.利用反序列化创建对象

    1、为什么要进行序列化

    再介绍之前,我们有必要先了解下对象的生命周期,我们知道Java中的对象都是存在于堆内存中的,而堆内存是可以被垃圾回收器不定期回收的。从对象被创建到被回收这一段时间就是Java对象的生命周期,也即Java对象只存活于这个时间段内。

    对象被垃圾回收器回收意味着对象和对象中的成员变量所占的内存也就被回收,这意味着我们就再也得不到该对象的任何内容了,因为已经被销毁了嘛,当然我们可以再重新创建,但这时的对象的各种属性都又被重新初始化了。所以如果我们需要保存某对象的状态,然后再在未来的某段时间将该对象再恢复出来的话,则必须要在对象被销毁即被垃圾回收器回收之前保存对象的状态。要保存对象状态的话,我们可以使用文件、数据库,也可以使用序列化,这里我们主要介绍对象序列化。我们很有必要了解这方面的内容,因为对象序列化不仅在保存对象状态时可以被用到(对象持久化),在Java中的远程方法调用RMI也会被用到,在网络中要传输对象的话,则必须要对对象进行序列化,关于RMI有机会我会再专门开贴介绍。

    简单总结起来,进行对象序列化的话的主要原因就是实现对象持久化和进行网络传输,这里先只介绍怎样通过对象序列化保存对象的状态。

    下面我们通过一个简单的例子来介绍下如何进行对象序列化。

    2、怎样进行对象序列化

    假设我们要保存Person类的某三个对象的nameageheight这三个成员变量,当然这里只是简单举例

    我们先看下Person类,要序列化某个类的对象的话,则该类必要实现Serializable接口,从Java API中我们发现该接口是个空接口,即该接口中没声明任何方法。

    import java.io.Serializable;  
    public class Person implements Serializable {  
    int age;  
    int height;  
    String name;  
    public Person(String name, int age, int height){  
    this.name = name;  
    this.age = age;  
    this.height = height;  
    }  
    }  
    下面我们看一下如何来进行序列化,这其中主要涉及到JavaI/O方面的内容,主要用到两个类FileOutputStreamObjectOutputStreamFileOutputStream用于将字节输出到文件,ObjectOutputStream通过调用writeObject方法将对象转换为可以写出到流的数据。所以整个流程是这样的:ObjectOutputStream将要序列化的对象转换为某种数据,然后通过FileOutputStream连接某磁盘文件,再对象转化的数据转化为字节数据再将其写出到磁盘文件。下面是具体代码:
    import java.io.FileOutputStream;  
    import java.io.IOException;  
    import java.io.ObjectOutputStream;  
    public class MyTestSer {  
    /** 
     * Java对象的序列化与反序列化 
     */  
    public static void main(String[] args) {  
    Person zhangsan = new Person("zhangsan", 30, 170);  
    Person lisi = new Person("lisi", 35, 175);  
    Person wangwu = new Person("wangwu", 28, 178);  
    try {  
    //需要一个文件输出流和对象输出流;文件输出流用于将字节输出到文件,对象输出流用于将对象输出为字节  
    ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("person.ser"));  
    out.writeObject(zhangsan);  
    out.writeObject(lisi);  
    out.writeObject(wangwu);  
    out.close();  
    } catch (IOException e) {  
    e.printStackTrace();  
    }  
    }  
    }  
    3、对象的反序列化

    我们存储的目的主要是为了再恢复使用,下面我们来看下加上反序列化后的代码:

    import java.io.FileInputStream;  
    import java.io.FileOutputStream;  
    import java.io.IOException;  
    import java.io.ObjectInputStream;  
    import java.io.ObjectOutputStream;  
    public class MyTestSer {  
    /** 
     * Java对象的序列化与反序列化 
     */  
    public static void main(String[] args) {  
    Person zhangsan = new Person("zhangsan", 30, 170);  
    Person lisi = new Person("lisi", 35, 175);  
    Person wangwu = new Person("wangwu", 28, 178);  
    try {  
    //需要一个文件输出流和对象输出流;文件输出流用于将字节输出到文件,对象输出流用于将对象输出为字节  
    ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("person.ser"));  
    out.writeObject(zhangsan);  
    out.writeObject(lisi);  
    out.writeObject(wangwu);  
    } catch (IOException e) {  
    e.printStackTrace();  
    }  
    try {  
    ObjectInputStream in = new ObjectInputStream(new FileInputStream("person.ser"));  
    Person one = (Person) in.readObject();  
    Person two = (Person) in.readObject();  
    Person three = (Person) in.readObject();  
    System.out.println("name:"+one.name + " age:"+one.age + " height:"+one.height);  
    System.out.println("name:"+two.name + " age:"+two.age + " height:"+two.height);  
    System.out.println("name:"+three.name + " age:"+three.age + " height:"+three.height);  
    } catch (Exception e) {  
    e.printStackTrace();  
    }  
    }  
    }  
    输出结果如下:
    name:zhangsan age:30 height:170  
    name:zhangsan age:35 height:175  
    name:zhangsan age:28 height:178  
    从添加的代码我们可以看到进行反序列化也很简单,主要用到的流是FileInputstreamObjectInputstream正好与存储时用到的流相对应。另外从结果顺序我们可以看到反序列化后得到对象的顺序与序列化时的顺序一致。

    4、总结

    进行对象序列化主要目的是为了保存对象的状态(成员变量)。

    进行序列化主要用到的流是FileOutputStreamObjectOutputStreamFileOutputStream主要用于连接磁盘文件,并把字节写出到该磁盘文件;ObjectOutputStream主要用于将对象写出为可转化为字节的数据。

    要将某类的对象序列化,则该类必须实现Serializable接口,该接口仅是一个标志,告诉JVM该类的对象可以被序列化。如果某类未实现Serializable接口,则该类对象不能实现序列化。

    保存状态的目的就是为了在未来的某个时候再恢复保存的内容,这可以通过反序列化来实现。对象的反序列化过程与序列化正好相反,主要用到的两个流是FileInputstreamObjectInputStream

    反序列化后得到的对象的顺序与保存时的顺序一致。

    5、补充

    补充一:上面我们举得例子很简单,要保存的成员变量要么是基本类型的要么是String类型的。但有时成员变量有可能是引用类型的,这是的情况会复杂一点。那就是当要对某对象进行序列化时,该对象中的引用变量所引用的对象也会被同时序列化,并且该对象中如果也有引用变量的话则该对象也将被序列化。总结说来就是在序列化的时候,对象中的所有引用变量所对应的对象将会被同时序列化。这意味着,引用变量类型也都要实现Serializable接口。当然其他对象的序列化都是自动进行的。所以我们只要保证里面的引用类型是都实现Serializable接口就行了,如果没有的话,会在编译时抛出异常。如果序列化的对象中包含没有实现Serializable的成员变量的话,这时可以使用transient关键字,让序列化的时候跳过该成员变量。使用关键字transient可以让你在序列化的时候自动跳过transient所修饰的成员变量,在反序列化时这些变量会恢复到默认值。

    补充二:如果某类实现了Serializable接口的话,其子类会自动编程可序列化的,这个好理解,继承嘛。

    补充三:在反序列化的时候,并不会调用对象的构造器,这也好理解,如果调用了构造器的话,对象的状态不就又重新初始化了吗。

    补充四:我们说到对象序列化的是为了保存对象的状态,即对象的成员变量,所以静态变量不会被序列化。


    四.clone创建对象

    什么是"clone"? 

      在实际编程过程中,我们常常要遇到这种情况:有一个对象A,在某一时刻A中已经包含了一些有效值,此时可能 会需要一个和A完全相同新对象B,并且此后对B任何改动都不会影响到A中的值,也就是说,A与B是两个独立的对象,但B的初始值是由A对象确定的。在 Java语言中,用简单的赋值语句是不能满足这种需求的。要满足这种需求虽然有很多途径,但实现clone()方法是其中最简单,也是最高效的手段。 

       Java的所有类都默认继承java.lang.Object类,在java.lang.Object类中有一个方法clone()。JDK API的说明文档解释这个方法将返回Object对象的一个拷贝。要说明的有两点:一是拷贝对象返回的是一个新对象,而不是一个引用。二是拷贝对象与用 new操作符返回的新对象的区别就是这个拷贝已经包含了一些原来对象的信息,而不是对象的初始信息。 

      1. Clone&Copy
         假设现在有一个Employee对象,Employee tobby =new Employee(“CMTobby”,5000),通
    常我们会有这样的赋值Employee cindyelf=tobby,这个时候只是简单了copy了一下reference,cindyelf和tobby都指向内存中同一个object,这样cindyelf或者tobby的一个操作都可能影响到对方。打个比方,如果我们通过cindyelf.raiseSalary()方法改变了salary域的值,那么tobby通过getSalary()方法得到的就是修改之后的salary域的值,显然这不是我们愿意看到的。我们希望得到tobby的一个精确拷贝,同时两者互不影响,这时候我们就可以使用Clone来满足我们的需求。Employee cindy=tobby.clone(),这时会生成一个新的Employee对象,并且和tobby具有相同的属性值和方法。
          2. Shallow Clone&Deep Clone
    Clone是如何完成的呢?Object在对某个对象实施Clone时对其是一无所知的,它仅仅是简单地执行域对域的copy,这就是Shallow Clone。这样,问题就来了咯,以Employee为例,它里面有一个域hireDay不是基本型别的变量,而是一个reference变量,经过Clone之后就会产生一个新的Date型别的reference,它和原始对象中对应的域指向同一个Date对象,这样克隆类就和原始类共享了一部分信息,而这样显然是不利的,过程下图所示:

     

    这个时候我们就需要进行deep Clone了,对那些非基本型别的域进行特殊的处理,例如本例中的hireDay。我们可以重新定义Clone方法,对hireDay做特殊处理,如下代码所示:

    [java] view plain copy
     print?
    1.    class Employee implements Cloneable  
    2.   
    3. {  
    4.         public Object clone() throws CloneNotSupportedException  
    5.         {  
    6.          Employee cloned = (Employee) super.clone();  
    7.       cloned.hireDay = (Date) hireDay.clone()  
    8.       return cloned;  
    9.         }  
    10. }  

     

    3. Clone()方法的保护机制

    在Object中Clone()是被申明为protected的,这样做是有一定的道理的,以Employee

    类为例,通过申明为protected,就可以保证只有Employee类里面才能“克隆”Employee对象,原理可以参考我前面关于public、protected、private的学习笔记。

    4. Clone()方法的使用

    Clone()方法的使用比较简单,注意如下几点即可:

    a. 什么时候使用shallow Clone,什么时候使用deep Clone,这个主要看具体对象的域是什么性质的,基本型别还是reference variable

    b. 调用Clone()方法的对象所属的类(Class)必须implements Clonable接口,否则在调用Clone方法的时候会抛出CloneNotSupportedException。








    展开全文
  • 对象创建流程图 总结 逃逸分析 什么是逃逸分析? 对象逃逸状态 逃逸分析优化 TLAB区 对象如何进入老年代 有问题可以直接留言讨论~ 对象创建流程图 1.编译器通过逃逸分析,确定对象是在栈上分配还是在堆上...
  • Java 对象创建到销毁,简单来讲就是: Java 代码编译成字节码(.class)文件,然后通过 JVM 解析并运行,在通过垃圾回收器回收分配的内存。这个过程要搞清楚你需要了解: Java 运行时数据区域 类的加载过程 对象...
  • JAVA对象创建过程

    千次阅读 2019-08-01 18:00:16
    中可以清楚的看到jvm在创建对象时候的执行流程,下面具体讨论一下每个步骤: 在程序进行加载的时候,他的类信息,常量,静态变量等数据就会存放在方法区(常量池)中。 在执行new操作的时候,就会去方法区去...
  • 文章目录面试题——new一个对象/创建对象的过程第一步,类加载和... 将堆区对象的地址赋值给栈区的引用变量流程图 面试题——new一个对象/创建对象的过程 过程:第一步类加载和初始化(第一次使用该类),第二步创建...
  • 对于Java程序在运行时创建对象的内存分配流程,先上  首先说一下类的结构,看上,这个Person类的结构很简单,名字、年龄、和国籍(静态变量),并在PersonDemo类中对Person创建了三个实例。在Java程序运行开始...
  • JVM对象创建流程

    千次阅读 2017-12-21 19:32:37
    上章介绍完java虚拟机的运行时数据区之后,我们大致了解了java虚拟机的概况,java虚拟机...在语言层面上,创建对象(例如克隆,反序列化)通常仅仅是一个new关键字而已,而在虚拟机中,对象(限于普通java对象,不包
  • java面向对象

    万次阅读 多人点赞 2018-08-21 16:51:59
    包括面向对象概念、类与对象的关系、封装、构造函数、this关键字、static关键字、单例设计模式、继承、多态、内部类、异常、包等java基础知识。 1、面向对象 面向对象是相对面向过程而言 面向对象和面向过程都是...
  • Java程序员必备的一些流程图(拿走不谢)

    万次阅读 多人点赞 2019-07-08 15:09:35
    整理了一些Java基础流程图/架构图,做一下笔记,大家一起学习。 (想自学习编程的小伙伴请搜索圈T社区,更多行业相关资讯更有行业相关免费视频教程。完全免费哦!) 1.spring的生命周期 Spring作为当前Java最流行、最...
  • 深入理解Java对象创建过程:类的初始化与实例化

    万次阅读 多人点赞 2017-05-18 14:17:45
    Java中,一个对象在可以被使用之前必须要被正确地初始化,这一点是Java规范规定的。...本文试图对JVM执行类初始化和实例化的过程做一个详细深入地介绍,以便从Java虚拟机的角度清晰解剖一个Java对象创建过程。
  • java new对象创建过程

    万次阅读 2018-07-29 07:00:54
    关于对象创建过程一般是从new指令(我说的是JVM的层面)开始的(具体请看图1),JVM首先对符号引用进行解析,如果找不到对应的符号引用,那么这个类还没有被加载,因此JVM便会进行类加载过程(具体加载过程可参见我的...
  • Java 线程线程状态流程图

    千次阅读 2018-02-24 17:19:49
    新建(new):新创建了一个线程对象。 可运行(runnable):线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权 。 运行...
  • Java类加载及对象创建过程详解

    千次阅读 2019-06-27 08:00:00
    类加载过程 类加载的五个过程:加载、验证、准备、解析、初始化。 ... 在Java堆中生成一个代表这个类的java.lang.Class对象,作为方法区域数据的访问入口。 验证 验证阶段作用是保证Class...
  • activiti动态创建流程图

    千次阅读 2017-07-04 10:50:41
    利用100行代码动态创建并部署流程 英文原文:Dynamic Process Creation and Deployment in 100 Lines of Code这是一篇迟到的博文,几个月前我就准备把它整理出来发布,由于时间原因就搁置了。。。1. 关于Activiti...
  • java 对象创建、使用和清除

    千次阅读 2019-02-12 21:02:03
    有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学...
  • 吃人的那些 Java 名词:对象、引用、堆、栈

    万次阅读 多人点赞 2019-09-05 15:57:09
    作为一个有着 8 年 Java 编程经验的 IT 老兵,说起来很惭愧,我被 Java 当中的四五个名词一直困扰着:**对象、引用、堆、栈、堆栈**(栈可同堆栈,因此是四个名词,也是五个名词)。每次我看到这几个名词,都隐隐...
  • Java面向对象(知识点整理)

    万次阅读 多人点赞 2021-02-22 08:53:55
    文章目录面向对象类与对象匿名对象创建对象的内存分析栈(stack)堆(heap)方法区PC寄存器本地方法栈内部类成员内部类局部内部类匿名内部类静态内部类包装类拆箱和装箱操作字符串转换基本数据类型和包装类型的区别...
  • Java类及对象创建内存分配过程

    千次阅读 2016-06-10 21:53:38
    Java是面向对象的程序设计语言,而面向对象的程序都是由对象组成的,而类就是该对象的抽象描述。 那么什么类,什么又是对象了? 类:具体事物的属性和行为的抽象,概念上的定义 对象:就是该类的具体事物...
  • java面向对象最全入门笔记(通俗易懂,适合初学者)

    万次阅读 多人点赞 2019-11-14 12:00:29
    一、为什么使用面向对象 前言: 面向对象的三大特征 封装 (Encapsulation) 继承 (Inheritance) 多态 (Polymorphism) ...1.现实世界就是由对象组成的,如下,万物皆对象(不是单指女朋友也不是单指男朋友哈~) 2.面...
  • Java使用阿里云OSS对象存储上传图片

    万次阅读 多人点赞 2017-03-27 10:47:28
    示例说明   该案例是OSS Java SDK的示例程序,您可以...本示例中的并不包括OSS Java SDK的所有功能,详细功能及使用方法,请参看“SDK手册 > Java-SDK”, 链接地址是:https://help.aliyun.com/document_detail/os
  • Java面向对象系统理解

    千次阅读 2019-03-13 17:54:56
    学过C语言的同学就知道,用C语言编程,我们需要去解析题意,分析目的,画出流程图,按着步骤实现。这就是一种面向过程的思考方式。一般的面向过程是从上往下步步求精,所以面向过程最重要的是模块化的思想方法。因此...
  • 主要原因是:在脑海中没有建立一张程序执行流程图,不了解这些虚拟机为什么这么设计?规则的实现原理是什么?Java 程序执行时栈、堆、方法区里都是什么样的状态? 在本 Chat 中,会用“Java 程序 + 画图 + 字节码 + ...
  • Java面向对象习题练习

    万次阅读 多人点赞 2017-10-09 20:42:53
    【练习题】01.类的成员变量 猜数字游戏一个类A有一个成员变量v有一个初值100。定义一个类对A类的成员变量v进行...import java.util.*; public class lianxi { public static void main(String[] dsa) { A a=new A();
  • java在类的定义中创建自己的对象?

    千次阅读 2020-01-14 17:45:28
    在书上经常看到这样的例子 ...我想知道的是,A是一个类,而main方法是A类中的一个方法,为什么可以在main方法里创建一个自己的对象?那是不是也可以使用aa.main(参数)的形式来调用它?请高人解...
  • JVM(二)JVM 创建对象过程分析

    千次阅读 2018-01-19 11:15:11
    首先来看一下JVM创建对象时的流程图: Created with Raphaël 2.1.2开始JVM 执行 new 指令类已加载?为新生对象分配内存内存规整?指针碰撞并发处理:同步或TLAB将分配到的内存空间初始化为零值JVM
  • java对象池化技术

    千次阅读 2019-01-07 22:33:25
    java池化技术新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义...
  • 第一部分 Java面向对象 1.开发环境搭建及开发工具eclipse使用 2.类和对象 3. 成员变量和局部变量区别 4. 数据类型 5.构造函数 6.函数 7. 成员变量的初始化 8. this关键字 9. static关键字 9.1 静态成员...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 189,020
精华内容 75,608
关键字:

java创建对象流程图

java 订阅