多个jvm实例_java类的实例

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

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

一、概述

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

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

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

二、类的四种实例化方式

1.使用new关键字

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

Parent p = new Parent();

2.使用newInstance()方法

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

Class p = Class.forName("com.huang.Parent");
Parent parent = (Parent) p.newInstance();

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

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

//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方法创建对象的过程中并不会调用任何构造函数。这里涉及到一个深拷贝和浅拷贝的知识点,我会另起一篇随笔介绍,这里就多费笔墨了。

Parent parent = new Parent();
Parent p2 = (Parent) parent.clone();

4.使用反序列化机制

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

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.对象变量的初始化

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

//父类
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.代码块的初始化

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

我们举个例子:

//父类
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类,往下一层一层的返回,完成对象的三个初始化:

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

类实例化的递归过程

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

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

验证一下:

//父类
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.net/170793.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • linux中退出vim编辑器的命令_linux打开vim编辑器

    linux中退出vim编辑器的命令_linux打开vim编辑器编辑完保存退出的四种方式        1.Esc+:+wq+回车(w是write,q是quit)        2. Esc+:+x+回车(x=wq)        3. Esc+shift+zz         4.Esc+ZZ(在大写开启下)如果是进入了编辑,但是未进行任何改动的保存退出命令如下: Esc+:+q+回车不保存退出的命令:                 Esc+:+q+…

    2022年10月1日
    2
  • hashmap的扩容原理_HashMap

    hashmap的扩容原理_HashMap本篇文章分别讲解JDK1.7和JDK1.8下的HashMap底层实现原理文章目录一、什么是HashMap?二、为什么要使用HashMap?三、HashMap扩容为什么总是2的次幂四、JDk1.7扩容死循环问题五、JDK1.8的新结构1.为什么非要使用红黑树呢?2.什么是红黑树?3.红黑树的特性一、什么是HashMap?HashMap数据结构为数组+链表(JDk1.7),JDK1.8中增加了红黑树,其中:链表的节点存储的是一个Entry对象,每个Entry对象存储四个属性(hash,key,v

    2022年9月21日
    3
  • zookeeper install j脚本

    zookeeper install j脚本

    2022年4月2日
    38
  • 可能错误使用了‘offsetof’宏

    可能错误使用了‘offsetof’宏最近代码里引进了一个宏offsetof(s,m),原来一直在windows上开发,今天发现在linux编译的日志中出现了如下的警告:xxxx.cpp:8:警告:对NULL对象非静态数据成员‘XXX::xxx’的访问无效xxxx.cpp:8:警告:(可能错误使用了‘offsetof’宏)

    2022年8月22日
    5
  • 网络传真的安装及使用方法「建议收藏」

    网络传真的安装及使用方法「建议收藏」在宽带网迅速普及的今天,Modem好像已经“廉颇老矣”,传真Modem已变成了一块“食之无味,弃之可惜”的鸡肋。而且windows的传真模块,已经远远无法满足今天人们快节奏的工作效率。在国外已经非常普遍的网络传真(efax),终于来到了国内,从2004年的引进到根据国内人们使用习惯的不断改进,近10年来,已拥有了百万级的客户群体,特别是近年来,传真营销被企业广泛应用,带来了越来越多的垃圾传

    2022年6月28日
    49
  • python模块之psutil详解

    python模块之psutil详解python模块之psutil详解一、psutil模块:1.psutil是一个跨平台库(http://pythonhosted.org/psutil/)能够轻松实现获取系统运行的进程和系统利用率(包括CPU、内存、磁盘、网络等)信息。它主要用来做系统监控,性能分析,进程管理。它实现了同等命令行工具提供的功能,如ps、top、lsof、netstat、ifconfig、who、df、kill…

    2022年6月12日
    34

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号