一、new 对象的几种说法

初学 Java 面向对象的时候,实例化对象的说法有很多种,我老是被这些说法给弄晕。

public class Test {
    public static void main(String[] args) {
        // 创建一个 ProgramLanguage 对象, 对象名是 java
        ProgramLanguage java = new ProgramLanguage();
        // 实例化一个 ProgramLanguage 对象, 对象名是 c
        ProgramLanguage c = new ProgramLanguage();
        // 把 ProgramLanguage 类实例化, 实例化后的对象的对象名是 python
        ProgramLanguage python = new ProgramLanguage();
    }
}

class ProgramLanguage {
    private Integer id;
    private String name;
}

下面的三种说法的操作都是实例化对象,只是说法不一样而已

  • 创建一个 xxx 对象
  • 实例化一个 xxx 对象
  • ③ 把 xxx 类实例化

二、Java 对象在内存中的存在形式

这里先简单看一看 Java 对象在内存中的存在形式和几个内存相关的概念,后面还会详细介绍的。先看下面的几个知识点:

1. 栈帧(Frame)

  • ① 方法被调用则栈帧创建,方法执行结束则栈帧销毁
  • ② 栈帧中存储了方法的局部变量信息
  • ③ 栈帧是分配给方法的一段栈空间

  • main 方法作为程序的入口肯定是第一个被调用的方法,所以会先创建 main 方法的栈帧
  • 在 main 方法中调用了 test1 方法,并传入【55】作为参数给 test1 方法的局部变量 v,所以第二个创建的是 test1 方法的栈帧
  • test1 方法中的代码很快就执行完了,所以 test1 的栈帧很快会被销毁(方法执行结束后该方法的栈帧销毁)
  • 在 main 方法中调用了 test2 方法,并传入【88】作为参数给 test2 方法的局部变量 v,所以第三个创建的是 test2 方法的栈帧
  • 在 test2 方法中调用了 test3 方法,并传入【666】作为参数给 test3 方法的局部变量 v,所以第四个创建的是 test3 方法的栈帧
  • 当 test3 方法执行完毕后,test3 方法的栈帧被销毁
  • test3 方法的结束也正是 test2 方法的结束,所以 test2 方法的栈帧也被销毁
  • test2 方法的结束表示 main 方法的结束,所以 main 方法的栈帧会被销毁

2. 对象在内存中的存在形式 ①

  • Java 中的所有对象都是通过 new 关键字创建出来的(new 关键字:实例化一个对象;向空间申请一段内存,用于存放刚刚实例化的对象)
  • 所有的对象都存储在空间
  • 所有保存对象的变量都是引用类型
  • 局部变量是放在空间
  • Java 运行时环境中有个垃圾回收器(garbage collector),会自动回收没有被使用的(堆空间的)内存
  • 当一个对象没有被任何引用指向的时候,会被 GC 回收掉内存

分析下面的代码的内存布局:

public class DogDemo {
    public static void main(String[] args) {
        Dog doggy = new Dog();
        doggy.age = 6;
        doggy.weight = 3.14;
    }
}

  • main 方法被调用,会在栈空间创建 main 方法的栈帧,main 方法的栈帧中会存放 main 方法中的局部变量信息(包括 args 和 main 方法中对象的引用 doggy
  • 在 main 方法中,通过 new 关键字实例化了 Dog 对象,Dog 对象存储在空间
  • 堆空间中有一段内存用于存储类的对象的数据,这段内存中存放了 Dog 对象的属性信息(如 age、weight)
  • 栈空间中的 doggy 变量代表堆空间中的对象的地址(通过地址可以访问对象)

分析下面的代码的内存布局(稍微复杂)

public class Dog {
    public int price;
}
public class Person {
    public int age;
    public Dog dog;
}
public class Test {
    public static void main(String[] args) {
        Dog doggy = new Dog();
        doggy.price = 255;

        Person yeTing = new Person();
        yeTing.age = 20;
        yeTing.dog = doggy;
    }
}

  • main 方法被调用,会在栈空间创建 main 方法的栈帧,main 方法的栈帧中会存放 main 方法中的局部变量信息(包括 args、main 方法中对象的引用 doggy、对象的引用 yeTing)
  • 在 main 方法中,通过 new 关键字实例化了 Dog 对象,Dog 对象存储在空间。堆空间中有一段内存用于存储 Dog 对象的属性信息(如 price = 255)
  • 在 main 方法中,通过 new 关键字实例化了 Person 对象,Person 对象存储在空间。堆空间中有一段内存用于存储 Person 对象的属性信息(如 age = 20),堆空间中,Person 对象的属性 dog 是 Dog 对象的引用,所以它指向的是堆空间中的 Dog 对象(dog 指向的是栈空间中的 doggy 引用的堆空间的 Dog 对象。doggy 和 yeTing 指向的 Person 对象中的 dog 属性指向的是同一个对象)
  • 引用变量不一定是在栈空间(也可能是在堆空间,如上图中 yeTing 指向的 Person 对象中 dog,这个 dog 就是引用变量。但是,它是在堆空间。)
  • 引用变量指向对象实际上保存的是对象在堆空间中的地址值(如:doggy 保存的是 Dog 对象在堆空间的地址值、yeTing 保存的是 Person 对象在堆空间的地址值)

3. 对象中的方法存储在那儿?

看下面的代码,思考对象中的方法存储在那儿?

public class Dog {
    public int price;

    public void run() {
        System.out.println(price   "_"   "run()");
    }

    public void eat() {
        System.out.println(price   "_"   "eat()");
    }
}
public class Test {
    public static void main(String[] args) {
        Dog dog1 = new Dog();
        dog1.price = 255;
        dog1.run();
        dog1.eat();

        Dog dog2 = new Dog();
        dog2.price = 552;
        dog2.run();
        dog2.eat();
    }
}

Java 虚拟机执行 Java 程序时会把内存划分为若干个不同的数据区域,主要包括:

  • ①  PC 寄存器(Program Counter Register):存储 Java 虚拟机正在执行的字节码指令的地址
  • Java 虚拟机栈(Java Virtual Machine Stack):存储 Java 方法的栈帧(① 方法被调用的时候会在栈空间创建该方法的栈帧,该方法执行完毕后,该方法对应的栈帧会销毁;② 栈帧中会存放方法中的局部变量信息)【栈空间】
  • ③  堆空间(Heap):存储被 GC(垃圾回收器) 所管理的各种对象(GC 管理的是通过 new 关键字创建的对象)
  • ④  方法区(Method Area):存储每个类的结构信息(如:字段和方法信息、构造方法和普通方法的字节码信息)
  • 本地方法栈(Native Method Stack):用来支持 native 方法的调用(如:用 C 语言编写的方法)

4. Java 对象在内存中的存在形式 ②

String:

  • 是字符串,在 Java 编程中被频繁地使用,但它是引用类型
  • Java 中双引号包裹的内容默认就是字符串类型
  • Java 中被双引号包裹的内容叫做字符串常量
  • 字符串常量存储在字符串常量池中(String Constant Pool)
  • jdk1.7 之前,字符串常量池在方法区;后来被移动到了堆空间。所以,jdk1.8的字符串常量存储在堆空间的字符串常量池中
  • 后面学习 String 的时候还会细说

分析下面代码的内存布局:

public class Dog {
    String name;
    int age;
    String color;
}
public class DogDemo {
    public static void main(String[] args) {
        Dog doggy = new Dog();
        doggy.name = "笑天";
        doggy.age = 6;
        doggy.color = "黑";
    }
}

三、类中属性详细说明

  •  现实世界中的对象有状态(State)和行为(Behavior),面向编程中的对象有属性(Field)和方法(Method)。
  •  类是创建单个对象的蓝图(模板)

下面详细说明一下类中【属性】这个概念。其实上篇文章已经能够很好理解,这里只是再补充一下而已。

  • 属性、成员变量、字段(field)指的是同一个东西(即一个类的状态)习惯上把现实世界的对象的状态(State)和编程中的属性联系在一起,便于理解
  • 属性可以是基本数据类型或引用类型(自定义类,接口,数组 …)
  • 定义属性的语法:访问修饰符 属性类型(eg: String、int、Dog、Bicycle) 属性名
  • 访问修饰符(控制属性被访问的范围)有四种:public、protected、默认(不写)、private【后面会详细说】
/**
 * 访问修饰符有四种:public、protected、默认(不写)、private
 */
public class Dog {
    public String name;
    protected int age;
    String color;
    private double weight;
}

如果不给对象的属性赋值,属性会有初始值

/**
 * 测试若不给对象的属性赋初始值, 它们的默认初始值
 */
public class FiledInitialValueTest {
    private int score;
    private short age;
    private byte month;
    private long salary;
    private float height;
    private double pi;
    private char firstName;
    private boolean isTrueLove;
    private Person person;

    public static void main(String[] args) {
        FiledInitialValueTest test = new FiledInitialValueTest();
        System.out.println("\n若不给对象的属性赋值, 初始值如下所示:");
        System.out.println("score【int】 = "   test.score);
        System.out.println("age【short】 = "   test.age);
        System.out.println("month【byte】 = "   test.month);
        System.out.println("salary【long】 = "   test.salary);
        System.out.println("height【float】 = "   test.height);
        System.out.println("pi【double】 = "   test.pi);
        // 字符类型的属性的初始值是空串(在控制台无法看到)
        System.out.println("firstName【char】 = "   test.firstName);
        // 字符类型的属性的初始值强制类型转换为 int 后是【0】
        System.out.println("firstName【(int)char】 = "   (int) test.firstName);
        System.out.println("isTrueLove【boolean】 = "   test.isTrueLove);
        System.out.println("person【person】 = "   test.person);
    }
}

四、细小知识点

1. 如何创建对象

必须先有类(模板)才能创建对象

通过【new】关键字创建类的对象。【new】:向堆空间申请一块内存存储对象数据

public class TestCreateObject {
    public static void main(String[] args) {
        // (1) 先声明再创建
        Dog dog; // 声明 
        dog = new Dog(); // 通过 new 关键字创建对象

        // (2) 声明并创建对象 
        Dog doggy = new Dog();
    }
}

2. 如何访问属性

可通过【.】号访问属性或调用方法

可把 . 看做【的】、【の】

五、Exercise

看代码,画图:

public class Person {
    private int age;
    private String name;

    public static void main(String[] args) {
        Person yangJiaLi = new Person();
        yangJiaLi.age = 17;
        yangJiaLi.name = "杨嘉立";

        // 下面的一行代码有2种说法:
        // 1. 把 yangJiaLi 赋值给 yjl
        // 2. yjl 指向 yangJiaLi
        Person yjl = yangJiaLi;
        System.out.println(yjl.age); // 17
    }
}

六、总结

本篇文章的重点是第二节【Java 对象在内存中的存在形式】

需重点知道:

到此这篇关于Java 对象在 JVM 中的内存布局超详细解说的文章就介绍到这了,更多相关Java JVM 内存布局内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

Java 对象在 JVM 中的内存布局超详细解说的更多相关文章

  1. android – 基于JVM的语言,没有语言运行时

    ProGuard可以删除特定程序中未使用的部分运行时.如果速度和时间非常关键,另一种方法是使用NDK.

  2. android – 如何在gradle中调整dex内存的jvm args?

    我有一个Android项目,它在dex步骤中当前没有堆空间:我想在gradle中提高jvmmin/max设置,就像我们以前使用Maven插件一样:但是在gradle中的android插件的文档中我只看到这些选项:有办法吗?

  3. android – Timer()作为守护进程与非守护进程

    什么时候应该在Android应用程序中作为守护进程启动计时器?

  4. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  6. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  7. Java实现世界上最快的排序算法Timsort的示例代码

    Timsort 是一个混合、稳定的排序算法,简单来说就是归并排序和二分插入排序算法的混合体,号称世界上最好的排序算法。本文将详解Timsort算法是定义与实现,需要的可以参考一下

  8. Java日期工具类的封装详解

    在日常的开发中,我们难免会对日期格式化,对日期进行计算,对日期进行校验,为了避免重复写这些琐碎的逻辑,我这里封装了一个日期工具类,方便以后使用,直接复制代码到项目中即可使用,需要的可以参考一下

  9. Java设计模式之模板方法模式Template Method Pattern详解

    在我们实际开发中,如果一个方法极其复杂时,如果我们将所有的逻辑写在一个方法中,那维护起来就很困难,要替换某些步骤时都要重新写,这样代码的扩展性就很差,当遇到这种情况就要考虑今天的主角——模板方法模式

  10. Java 中 Class Path 和 Package的使用详解

    这篇文章主要介绍了Java 中 Class Path和Package的使用详解,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的朋友可以参考一下

随机推荐

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  4. (jsp/html)网页上嵌入播放器(常用播放器代码整理)

    网页上嵌入播放器,只要在HTML上添加以上代码就OK了,下面整理了一些常用的播放器代码,总有一款适合你,感兴趣的朋友可以参考下哈,希望对你有所帮助

  5. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  6. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  7. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  8. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  9. Mybatis-Plus接口BaseMapper与Services使用详解

    这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. mybatis-plus雪花算法增强idworker的实现

    今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,进一步增强实现生成分布式唯一ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部