前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >多个jvm实例_java类的实例

多个jvm实例_java类的实例

作者头像
全栈程序员站长
发布于 2022-09-23 10:22:25
发布于 2022-09-23 10:22:25
2K00
代码可运行
举报
运行总次数:0
代码可运行

大家好,又见面了,我是你们的朋友全栈君。

一、概述

我们知道,一个对象在可以被使用之前必须要被正确地实例化。而实例化实际指的就是以一个java类为模板创建对象/实例的过程。比如说常见的 Person = new Person()代码就是一个将Person类实例化并创建引用的过程。

对于类的实例化,我们关注两个问题:

  • 如何实例化?(类的四种实例化方式)
  • 什么时候实例化?(类的一个初始化过程和对象的三个初始化过程)

二、类的四种实例化方式

1.使用new关键字

这也是最常见最简单的创建对象的方法。通过这种方法,我们可以借助类的构造函数实例化对象。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Parent p = new Parent();

2.使用newInstance()方法

我们可以先通过类的全限定名获取类,然后通过Class类的newInstance()方法去调用类的无参构造方法创建一个对象:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Class p = Class.forName("com.huang.Parent");
Parent parent = (Parent) p.newInstance();

或者通过java.lang.relect.Constructor类里的newInstance()方法去构造对象,这个方法比起Class自带的更强大:

它可以调用类中有参构造方法私有构造方法创建对象!

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//Parent私有的含参构造方法
public Parent(int a) {
    System.out.println("Parent创建了!");
}

//通过Constructor调用
Class p = Class.forName("com.huang.Parent");
Constructor<Parent> parentConstructor = p.getConstructor(int.class);
Parent parent = (Parent) p.newInstance();

3.使用clone()方法

当我们调用clone方法,JVM会帮我们创建一个新的、一样的对象,特别需要说明的是,用clone方法创建对象的过程中并不会调用任何构造函数。这里涉及到一个深拷贝和浅拷贝的知识点,我会另起一篇随笔介绍,这里就多费笔墨了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Parent parent = new Parent();
Parent p2 = (Parent) parent.clone();

4.使用反序列化机制

当我们反序列化一个对象时,JVM会给我们创建一个单独的对象,在此过程中,JVM并不会调用任何构造函数。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Parent parent = new Parent();

// 写对象
ObjectOutputStream outputStream = new ObjectOutputStream(
    new FileOutputStream("parent.bin"));
outputStream.writeObject(parent);
outputStream.close();

// 读对象
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(
    "parent.bin"));
Parent parent2 = (Parent) inputStream.readObject();

三、类实例化的过程

我们以 Person p = new Person()这条语句为例,当这条语句执行的时候,jvm进行了四步操作:

  • 先执行new语句,以Person类为模板,在堆中创建Person对象
  • 为Person对象执行构造方法(如果有父类会先执行父类构造方法)
  • 创建Person类的引用变量p
  • 将引用变量p指向内存中Person对象

我们不难看出,其实实例化的过程其实就是第一和第二步,在这两步里,jvm其实也进行了四步操作:

  • Person的初始化
  • Person对象变量的初始化(如果有父类会先执行父类变量的初始化)
  • Person对象代码块的初始化
  • Person对象构造函数的初始化(如果有父类会先执行父类初始化)

1.类的初始化

对于第一次被实例化的对象,第一步是必定是类的初始化,所以静态变量和静态代码块中的代码必然被赋值和执行。

这点在我关于类加载机制的文章中已有解释,这里就不多费笔墨。

2.对象变量的初始化

我们在定义对象中的变量的同时,还可以直接对对象变量进行赋值。它们会在构造函数执行之前完成这些初始化操作

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//父类
public class Parent{
    int i = 1;
    int j = i + 1;
    
    public Parent() {
        System.out.println("Parent的构造方法执行了!");
        j += 10;
    }
}

//子类
public class Child extends Parent {

    int k = 1;
    int l = k + 1;

    public Child() {
        System.out.println("i:"+i);
        System.out.println("j:"+j);
        System.out.println("k:"+k);
        System.out.println("l:"+l);
        System.out.println("Child的构造方法执行了!");
        k += 8;
        System.out.println("k:"+k);
        System.out.println("l:"+l);
    }
}

public static void main( String[] args ) {
    Child child = new Child();
}
//执行结果
Parent的构造方法执行了!
i:1
j:12
k:1
l:2
Child的构造方法执行了!
k:9
l:2

我们可以知道执行顺序是这样的:

  • 父类的变量初始化i = 1,j=2;
  • 执行父类的构造函数j = 2 + 10 = 12;
  • 子类的变量初始化k = 1,l = 2;
  • 执行子类构造函数k = 1 + 8 = 9

这里有人认为父类的变量初始化了,而且父类的构造函数也执行了,那父类是不是也一起实例化了?

答案是没有,我们可以认为实例化的时候子类从父类一起拷贝了一份变量,构造函数的执行也是为了能让父类的变量初始化,最后实例化放到内存里的其实是子类+父类的一个混合体!

3.代码块的初始化

我们一般指的代码块是构造代码块和静态代码块,静态代码块在类初始化时就执行,而构造代码块在类一创建就执行,也优先于构造方法

我们举个例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//父类
public class Parent{
    {
        System.out.println("Child的代码块被执行了!");
    }
    public Parent() {
        System.out.println("Parent创建了!");
    }
}

//子类
public class Child extends Parent {

    public Child() {
        System.out.println("Child创建了!");
    }

    static {
        System.out.println("Child的构造方法执行了!");
    }

    {
        System.out.println("Child的代码块被执行了!");
    }
}

//执行代码
public static void main( String[] args ) {
    Child child = new Child();
}

//打印结果
Parent的代码块被执行了!
Parent的构造方法执行了!
Child的代码块被执行了!
Child的构造方法执行了!

我们可以知道执行顺序是这样的:

  • 父类代码块
  • 父类的构造方法
  • 子类的代码块
  • 子类的构造方法

4.构造函数的初始化

我们可以从上文知道,实例变量初始化与实例代码块初始化总是发生在构造函数初始化之前,那么我们下面着重看看构造函数初始化过程。众所周知,每一个Java中的对象都至少会有一个构造函数,如果我们没有显式定义构造函数,那么它将会有一个默认无参的构造函数。在编译生成的字节码中,这些构造函数会被命名成<init>()方法。

事实上,Java强制要求Object对象之外的所有对象构造函数的第一条语句必须是父类构造函数的调用语句,如果没有就会默认生成讴歌构造函数。这就保证了不管要实例化的类继承了多少父类,我们最终都能让实例继承到所有从父类继承到的属性。

5.小结

结合以上文,我们可以看出类的实例化其实是一个递归的过程。

从子类不断向上递归,然后一直递归到直到抵达基类Object,然后一层一层的返回,先完成类的初始化:

  • 如果有类未初始化就先初始化(初始化静态块)

再回到Object类,往下一层一层的返回,完成对象的三个初始化:

  • 初始化变量
  • 初始化代码块
  • 初始化构造函数

所以最终我们可以总结出类初始化过程中类的各种代码块的执行顺序:

  • 父类静态块
  • 子类静态块
  • 父类代码块
  • 父类构造函数
  • 子类代码块
  • 子类构造函数

验证一下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//父类
public class Parent{
    static {
        System.out.println("Parent的静态块执行了!");
    }

    public Parent() {
        System.out.println("Parent的构造方法执行了!");
    }

    {
        System.out.println("Parent的代码块被执行了!");
    }
}

//子类
public class Child extends Parent {
    static {
        System.out.println("Child的静态块执行了!");
    }

    public Child() {
        System.out.println("Child的构造方法执行了!");
    }

    {
        System.out.println("Child的代码块被执行了!");
    }
}

public static void main( String[] args ) {
    Child child = new Child();
    System.out.println();
}

//输出结果
Parent的静态块执行了!
Child的静态块执行了!
Parent的代码块被执行了!
Parent的构造方法执行了!
Child的代码块被执行了!
Child的构造方法执行了!

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/170793.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
夯实Java基础系列7:一文读懂Java 代码块和执行顺序
本系列文章将整理到我在GitHub上的《Java面试指南》仓库,更多精彩内容请到我的仓库里查看
程序员黄小斜
2019/09/27
6380
Java this 关键字用法
构造方法是一个类的对象在通过new关键字创建时自动调用的,在程序中不能向调用其他方法一样通过方法名(也就是类名)来调用。但如果一个类有多个构造方法,可以在一个构造方法中通过this(paras…)来调用其他的构造方法。 使用this来调用其他构造方法有如下几个约束。 1) 只能在构造方法中通过this来调用其他构造方法,普通方法中不能使用。 2) 不能通过this递归调用构造方法,即不能在一个构造方法中通过this直接或间接调用该构造方法本身。 例如:
全栈程序员站长
2022/09/13
2280
【java基础☞初始化顺序】java继承中的初始化顺序
1、初始化顺序:父类的静态变量-->父类的静态代码块-->子类的静态变量-->子类的静态代码快-->父类的非静态变量(父类的非静态代码块)-->父类的构造函数-->子类的非静态变量(子类的非静态代码块)-->子类的构造函数
用户5640963
2019/07/25
1K0
深度解析Java中的5个“黑魔法”
现在的编程语言越来越复杂,尽管有大量的文档和书籍,这些学习资料仍然只能描述编程语言的冰山一角。而这些编程语言中的很多功能,可能被永远隐藏在黑暗角落。本文将为你解释其中5个Java中隐藏的秘密,可以称其为Java的“黑魔法”。对于这些魔法,会描述它们的实现原理,并结合一些应用场景给出实现代码。
蒙娜丽宁
2020/04/15
6930
重学Java基础篇—Java类加载顺序深度解析
Remember_Ray
2025/03/18
1240
重学Java基础篇—Java类加载顺序深度解析
java三大特征_java三大特性是什么?
java三大特性:1、封装,是指隐藏对象的属性和实现细节,仅对外提供公共访问方式;2、继承,从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力;3、多态,一个方法可以有多种实现版本,即“一种定义, 多种实现”。
全栈程序员站长
2022/09/08
1.7K0
java三大特征_java三大特性是什么?
【小家java】类中静态代码块、构造代码块、静态变量、成员变量执行顺序和继承逻辑
诚如各位所知,java的三大特性:封装、继承、多态。其中继承,是java中最有学问的一点也是最相对来说最难理解的一些东西,本文针对于此,做一些实例分析,希望能够帮助大家理解java中的继承机制
YourBatman
2019/09/03
1.5K0
【小家java】类中静态代码块、构造代码块、静态变量、成员变量执行顺序和继承逻辑
深入理解Java对象的创建过程:类的初始化与实例化
在Java中,一个对象在可以被使用之前必须要被正确地初始化,这一点是Java规范规定的。在实例化一个对象时,JVM首先会检查相关类型是否已经加载并初始化,如果没有,则JVM立即进行加载并调用类构造器完成类的初始化。在类初始化过程中或初始化完毕后,根据具体情况才会去对类进行实例化。本文试图对JVM执行类初始化和实例化的过程做一个详细深入地介绍,以便从Java虚拟机的角度清晰解剖一个Java对象的创建过程。
Java团长
2018/08/03
3.3K0
充电篇:再也不怕面试官问你Java基础了
Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程 。
小熊学Java
2023/07/12
1580
充电篇:再也不怕面试官问你Java基础了
Java继承、final/protected说明、super/this辨析
上面的这个animal就是基类,我们的这个dog和bird都是继承这个基类的特征,使用的是extends这个关键字,表示我们的子类继承父类,父类的这个成员变量和成员方法我们子类都会拥有的;
阑梦清川
2025/02/24
850
Java继承、final/protected说明、super/this辨析
Java代码块执行顺序初探
Java继承中对构造函数是不继承的,只是显式或者隐式调用,并且必须是在构造函数第一行。这里是隐式调用了super()。
WindCoder
2018/09/19
2.7K0
Java面向对象三大特性
利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体。 数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节, 只保留一些对外接口使之与外部发生联系。用户无需知道对象内部的细节, 但可以通过对象对外提供的接口来访问该对象。
李红
2019/06/06
4310
Java面向对象三大特性
深入理解Java对象的创建过程:类的初始化与实例化
在Java中,一个对象在可以被使用之前必须要被正确地初始化,这一点是Java规范规定的。在实例化一个对象时,JVM首先会检查相关类型是否已经加载并初始化,如果没有,则JVM立即进行加载并调用类构造器完成类的初始化。在类初始化过程中或初始化完毕后,根据具体情况才会去对类进行实例化。本文试图对JVM执行类初始化和实例化的过程做一个详细深入地介绍,以便从Java虚拟机的角度清晰解剖一个Java对象的创建过程。
用户7886150
2020/12/07
2.7K0
JVM类加载过程
加载、验证、准备、初始化和卸载这5个阶段的顺序是确定的,类的加载过程必须按照这种顺序进行,而解析阶段则不一定,它在某些情况下可能在初始化阶段后在开始,因为java支持运行时绑定。
全栈程序员站长
2022/07/21
7530
JVM类加载过程
Java类加载原理机制
1.类的加载过程 JVM将类加载过程分为三个步骤:装载(Load),链接(Link)和初始化(Initialize)链接又分为三个步骤,如下图所示: 1) 装载:查找并加载类的二进制数据; 2)链
汤高
2018/01/11
1.5K0
Java类加载原理机制
【Java 基础】类和对象(构造&this&封装&static&代码块)
类:把具有相同属性和行为的一类对象抽象为类。类是抽象概念,如人类、犬类等,无法具体到每个实体。
IsLand1314
2024/10/15
1710
【Java 基础】类和对象(构造&this&封装&static&代码块)
静态代码块、静态变量,构造代码块、实例变量的执行顺序和继承逻辑
各位小伙伴大家好,我是A哥。如果问:Java的三大特性是什么?你顺口就能答出:封装、继承、多态。如果继续问:你真的了解Java中的继承吗?
YourBatman
2022/05/06
1K0
静态代码块、静态变量,构造代码块、实例变量的执行顺序和继承逻辑
代码块总结
这种形式的程序段我们将其称之为代码块,所谓代码块就是用大括号({})将多行代码封装在一起,形成一个独立的数据体,用于实现特定的算法。一般来说代码块是不能单独运行的,它必须要有运行主体。在Java中代码块主要分为四种:
栋先生
2018/09/29
8560
面试题丨Java的类/实例初始化过程
昨天看到群里面有人分享了一道题目,我答错了,于是趁机了解了下Java的类/对象初始化过程:
本人秃顶程序员
2019/05/13
9800
面试题丨Java的类/实例初始化过程
Java 面向对象
概述:对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,仍然需要面向过程的思路去处理。
星姮十织
2021/12/25
8950
推荐阅读
相关推荐
夯实Java基础系列7:一文读懂Java 代码块和执行顺序
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验