Java new一个对象

1. Java内存结构

不同虚拟机的运行时数据区可能略微有所不同,但都会遵从 Java 虚拟机规范, Java 虚拟机规范规定的区域分为以下 5 个部分:

程序计数器(Program Counter Register):当前线程所执行的字节码的行号指示器,字节码解析器的工作是通过改变这个计数器的值,来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能,都需要依赖这个计数器来完成;

Java 虚拟机栈(Java Virtual Machine Stacks):用于存储局部变量表、操作数栈、动态链接、方法出口等信息;

本地方法栈(Native Method Stack):与虚拟机栈的作用是一样的,只不过虚拟机栈是服务 Java 方法的,而本地方法栈是为虚拟机调用 Native 方法服务的;

Java 堆(Java Heap):Java 虚拟机中内存最大的一块,是被所有线程共享的,几乎所有的对象实例都在这里分配内存;

方法区(Methed Area):用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。

2. Java数据类型

Java尽力保证:所有变量在使用前都能得到恰当的初始化。对于方法的局部变量,Java以编译时错误的形式来贯彻这种保证。

思考:

堆 是所有线程共享的内存区域,栈 是每个线程独享的,如果你将一个实例变量放在栈内,那么就不存在多个线程访问同一个对象资源了,这显然是不对的,所以实例变量要在堆上创建,也不是线程安全的。

对于局部变量,是在栈上创建的,每一次方法调用创建一个栈,独享一份内存区域,其他的线程是不会访问到该线程的资源,在 栈上创建也会减轻GC的压力,随着该方法的结束,帧出栈,相对应的内存消除,这种局部变量占用的内存自然就消失了,因此局部变量是线程安全的

2.1基本类型

Java有8种基本类型(boolean,char,byte,short,int,long,float,double),存储在堆栈中,不用new来创建变量,若类的某个成员是基本数据类型,即使没有进行初始化,Java也会确保它获得一个默认值

  • 存储位置

    • 局部变量:存储在栈中

    • 成员变量:存储在堆中

  • 默认值

    • 数值类型:0

    • boolean:false

    • char:0,所以显示为空白

2.2 new出来的对象

存储在”堆“中

  • 存储位置

    • 局部变量:变量在栈中,变量指向的对象在堆中

    • 成员变量:变量名和变量名指向的对象都在堆中

  • 默认值:null

2.2.1 String

String对象是不可变的,String对象具有只读性(String类中每一个看起来会修改String值的方法,实际上都是创建了一个全新的String对象,以包含修改后的字符串内容),所以指向他的任何引用都不可能改变它的值,因此,也就不会对其他的引用有什么影响

2.3 数组

数组存储在堆中(new一个数据对象)

对象数组保存的是引用,基本类型数组直接保存基本类型的值,对象数据保存的是对象的引用

3. 初始化

3.1 类成员初始化

无法阻止自动初始化的进行,它将在构造器被调用之前发生

Java初始化具有顺序性,跟代码的编写顺序有关

public class MethodInit{
    
    int i = f();
    int j = g(i);
    int f(){return 1;}
    int g(int n){ return n+10;}
}

以上代码可以正常初始化,但是下面这样写就不对了

public class MethodInit{

    int j = g(i);    
    int i = f();
    int f(){return 1;}
    int g(int n){ return n+10;}
}

3.2 构造器初始化

构造器的调用顺序是很重要的。当进行继承时,我们已经知道基类的一切,并且可以访问基类中任何声明为public和protected的成员。这就意味着在导出类中,必须保证基类的所有成员都是有效的:

1)调用基类构造器。这个步骤会不断地反复递归下去,首先是构造这种层次结构的根,然后是下一层导出类,等等,直到最低层的导出类

2)按声明顺序调用成员的初始化方法(不管类的成员变量定义顺序在哪他都是最先被初始化的)

3)调用导出类构造器的主体

4 new一个对象的过程

1)检查.class文件(.java文件在编译后会生成一个相同名称的.class文件)是否加载到了jvm中,若未加载.class文件:

  1. 加载.class文件存储在运行时内存区的方法区,然后将其转换为一个与目标类型对应的java.lang.Class对象实例

  2. 验证:格式验证、语义验证、操作验证

  3. 为类中的所有静态变量分配内存空间,并为其设置一个初始值(初始值的规则同【Java数据类型】模块所讲,如果变量被final修饰会直接赋值)

  4. 解析:将常量池中的符号引用转为直接引用(得到类或者字段、方法在内存中的指针或者偏移量,以便直接调用该方法)

  5. 初始化(先父后子):为静态变量赋值,执行static代码块(静态变量和静态代码块的执行顺序取决于其定义的顺序。static代码块只有jvm能够调用。如果是多线程需要同时初始化一个类,仅仅只能允许其中一个线程对其执行初始化操作,其余线程必须等待,只有在活动线程执行完对类的初始化操作之后,才会通知正在等待的其他线程)

2)给对象分配内存(包括本类和父类的所有实例变量,不包括静态变量),并设置默认值

3)父类成员变量赋值

4)父类构造器执行

5)子类非静态变量赋值

6)子类构造器执行

补充(重写父类方法的调用是怎么实现的):

  • 会先从子类的方法区寻找,找不到的话再递归地去父类找

  • 如果集成的层次比较深上述方法效率较低,会采用一种称为需方法表的方法来优化调用效率

5 举例

代码运行的结果是什么呢?


public class ExampleA {
    Long x = 123l;
    int a = f1();
    static int b = fx1();
    static {
        System.out.println("我是父类静态代码块1");
    }
    static int c = fx2();

    public ExampleA(){
        System.out.println("我是父类构造器");
    }

    int f1(){
        System.out.println("我是父类非静态变量f1");
        return 1;
    }

    static int fx1(){
        System.out.println("我是父类静态变量fx1");
        return 2;
    }

    static int fx2(){
        System.out.println("我是父类静态变量fx2");
        return 2;
    }
}

public class ExampleB extends ExampleA {
    int ba = bf1();
    static int bb = bfx1();
    static {
        System.out.println("我是子类静态代码块1");
    }
    static int bc = bfx2();

    public ExampleB(){
        System.out.println("我是子类构造器");
    }

    int bf1(){
        System.out.println("我是子类非静态变量bf1");
        return 1;
    }

    static void fun1(){
        System.out.println("我是静态方法fun1");
    }

    void fun2(){
        System.out.println("我是非静态方法fun2");
    }

    static int bfx1(){
        System.out.println("我是子类静态变量bfx1");
        return 2;
    }

    static int bfx2(){
        System.out.println("我是子类静态变量bfx2");
        return 2;
    }

    public static void main(String[] args){
        System.out.println("我是main1");
        ExampleB exampleB = new ExampleB();
        System.out.println(exampleB.x);
        System.out.println("我是main2");
        exampleB.fun2();
    }
}


==================================================
我是父类静态变量fx1
我是父类静态代码块1
我是父类静态变量fx2
我是子类静态变量bfx1
我是子类静态代码块1
我是子类静态变量bfx2
我是main1
我是父类非静态变量f1
我是父类构造器
我是子类非静态变量bf1
我是子类构造器
123
我是main2
我是非静态方法fun2

6 一些名词

类的元数据:类里的一些数值常量、方法、类信息等

虚方法表:在类加载的时候,为每个类创建一个表,这个表包括该类的对象所有动态绑定的方法及其地址,包括父类的方法,但一个方法只有一条记录,子类重写了父类方法后只会保留子类的。当通过对象动态绑定方法的时候,只需要查找这个表就可以了,而不需要挨个查找每个父类

JVM内存结构、 Java内存模型 以及 Java对象模型:JVM内存结构,和Java虚拟机的运行时区域有关。 Java内存模型,和Java的并发编程有关。 Java对象模型,和Java对象在虚拟机中的表现形式有关

7 参考

在java中new一个对象的流程是怎样的_圈外的健美猪的博客-CSDN博客_java new一个对象过程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值