Java实例化对象过程中的内存分配

问题引入

这里先定义一个很不标准的“书”类,这里为了方便演示就不对类的属性进行封装了。

class Book{String name;    //书名double price;   //价格public void getInfo(){System.out.println("name:"+name+";price:"+price);}
}

在这个类中定义了两个属性和一个方法,当然也是可以定义多和类和多个方法的。 类现在虽然已经定义好了,但是一个类要使用它必须要实例化对象,那么对象的定义格式有一下两种格式:

//声明并实例化对象: 类名称 对象名称 = new 类名称()
Book book = new Book();
//分步完成声明和实例操作: 
// |- 声明对象: 类名称 对象名称 = null;
Book book = null;
// |- 实例化对象: 对象名称 = new 类名称();
book = new Book();

对象属于引用数据类型,其和基本数据类型最大的不同在于引用数据类型需要进行内存分配,而关键字new主要的功能就是开辟内存空间,也就是说只要是使用引用数据类型就必须使用关键字new来开辟空间。有些时候我们需要对对象属性进行操作,那么其中的堆栈内存空间又是如何分配的呢?接下来我们来分析一下其中的过程。

堆内存与栈内存

如果想对对象操作的过程进行内存分析,首先要了解两块内存空间的概念:

  • 堆内存:保存每一个对象的属性内容,堆内存需要用关键字new才能开辟。
  • 栈内存:保存的是一块堆内存的地址。

堆内存很好理解,可能有人会有疑问为什么会有栈内存,举个例子,好比学校有很多教室,每个教室有一个门牌号,教室内放了很多的桌椅等等,这个编号就好比地址,老师叫小明去一个教室拿东西,老师必须把房间号告诉小明才能拿到,也就是为什么地址必须存放在一个地方,而这个地方在计算机中就是栈内存。

对象空属性

我们先实例化一个对象,并对其的属性不设置任何值

public class Test{public static void main(String args[]){Book book = new Book();book.getInfo();}
}

运行结果如下:

name:null;price:0.0

其内存变化图如下:

使用关键字new就在栈内存中开辟一个空间存放book对象,并且指向堆内存的一个空间,此时并未对其赋值,所以始终指向默认的堆内存空间。

操作对象属性

我们先声明并实例化Book类,并对实例出的book对象操作其属性内容。

public class Test{public static void main(String args[]){Book book = new Book();book.name = "深入理解JVM";book.price = 99.8;book.getInfo();}
}

编译执行后的结果如下:

name:深入理解JVM;price:99.8

内存变化图如下:

分步实例化对象

示例代码如下:

public class Test{public static void main(String args[]){Book book = null;  //声明对象book = new Book(); //实例化对象book.name = "深入理解JVM";book.price = 99.8;book.getInfo();}
}

很明显结果肯定和前面一样

name:深入理解JVM;price:99.8

表面没什么区别,但是内存分配过程却不一样,接下来我们来分析一下

任何情况下只要使用了new就一定要开辟新的堆内存空间,一旦堆内存空间开辟了,里面就一定会所有类中定义的属性内容,此时所有的属性内容都是其对应数据类型的默认值。

直观的说就是栈内存先要指向一个null,然后等待开辟新的栈内存空间后才能指向其属性内容。

NullPointerException的出现

那么如果使用了没有实例化的对象,就会出现最常见也是最让人头疼的一个异常NullPointerException,像下面的代码

public class Test{public static void main(String args[]){Book book = null;
//         book = new Book();   //实例化的这一步被注释book.name = "深入理解JVM";book.price = 99.8;book.getInfo();}
}

在编译的过程是不会出错的,因为只有语法错误才会在编译时中断,而这种逻辑性错误能成功编译,但是执行的时候却会抛出NullPointerException异常。 运行结果:

Exception in thread "main" java.lang.NullPointerException at language.Test.main(Test.java:19)

空指针异常是平时遇到最多的一类异常,只要是引用数据类型都有可能出现它。这种异常的出现也是很容易理解的,犹如你说今天被一只恐龙追着跑,恐龙早就在几个世纪前就灭绝了,现实生活中不可能存在,当然人们就会认为你说的这句话是谎言。在程序中也一样,没有被实例化的对象直接调用其中的属性或者方法,肯定会报错。

引用数据分析

引用是整个java中的核心精髓,引用类似于C++中的指针概念,但是又比指针的概念更加简单。 举个简单的例子,比如李华的小名叫小华,一天李华因为生病向老师请假了,老师问今天谁请假了,说李华请假了和小华请假了都是一个意思,小华是李华的别名,他们两个都是对应一个个体。 如果代码里面声明两个对象,并且使用了关键字new为两个对象分别进行了对象的实例化操作,那么一定是各自占用各自的堆内存空间,并且不会互相影响。

例如:声明两个对象

public class Test{public static void main(String args[]){Book bookA = new Book();Book bookB = new Book();bookA.name = "深入理解JVM";bookA.price = 99.8;bookA.getInfo();bookB.name = "Java多线程";bookB.price = 69.8;bookB.getInfo();}
}

运行结果如下:

name:深入理解JVM;price:99.8
name:Java多线程;price:69.8

我们来分析一下内存的变化

接下来我们看看那对象引用传递

例如:对象引用传递

public class Test{public static void main(String args[]){Book bookA = new Book();   //声明并实例化对象Book bookB = null;         //声明对象bookA.name = "深入理解JVM";bookA.price = 99.8;bookB = bookA;             //引用传递bookB.price = 69.8;bookA.getInfo();}
}

运行结果如下:

name:深入理解JVM;price:69.8

严格来讲bookA和bookB里面保存的是对象的地址信息,所以以上的引用过程就属于将bookA的地址赋给了bookB,此时两个对象指向的是同一块堆内存空间,因此任何一个对象修改了堆内存之后都会影响其他对象。

一块堆内存可以同时被多个栈内存所指向,但是反过来,一块栈内存只能保存一块堆内存空间的地址。

垃圾的产生

先看如下代码:

public class Test{public static void main(String args[]){Book bookA = new Book();   //声明并实例化对象Book bookB = new Book();   //声明并实例化对象bookA.name = "深入理解JVM";bookA.price = 99.8;bookB.name = "Java多线程";bookB.price = 69.8;bookB = bookA;             //引用关系bookB.price = 120.8;bookA.getInfo();}
}

运行结果如下:

name:深入理解JVM;price:120.8

整个过程内存又发生了什么变化呢?我们来看一下

在此过程中原来bookB所指向的堆内存无栈内存指向,一块没有任何栈内存指向的堆内存空间就将成为垃圾,等待被java中的回收机制回收,回收之后会释放掉其占用的空间。

虽然在java中支持了自动的垃圾收集处理,但是在代码的编写过程中应该尽量减少垃圾空间的产生。

END

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/439490.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Python学习】 - sklearn学习 - KNN

前言: 针对一个完整的机器学习框架目前还没有总结出来,所以目前只能总结每一个单独的算法。由于现在研究的重点是算法,所以对于数据的处理,数据的分析和可视化呈现,在现阶段并不进行展示(这样容易陷入纠结…

重读经典:《End-to-End Object Detection with Transformers》

DETR 论文精读【论文精读】这一次朱毅博士给大家精读的论文是 DETR,是目标检测领域里程碑式的一个工作,文章收录于 ECCV20 。DETR 是 Detection Transformer 的缩写,作者使用 Transformer 简化了目标检测流程,不再需要进行 NMS&am…

Execute SQL Task 参数和变量的映射

Execute SQL Task能够执行带参数的SQL查询语句或存储过程(SP),通过SSIS的变量(Variable)对参数赋值。对于不同的Connection Manager,在Task中需要使用不同的符号(Parameter marker)来…

【Python学习】 - 手写数字识别 - python读入mnist数据集的多种方法

写在前面: 其实网上有很多读入mnist数据的代码,但是都是比较麻烦冗长的函数,本篇文章介绍几种不算很麻烦的,借用库函数读入数据的方法。 方法1: 方法2: 方法3:

Coursera自动驾驶课程第21讲:Dynamic Object Interactions

在第20讲《Coursera自动驾驶课程第20讲:Mission Planning in Driving Environments》 我们学习了任务规划中常用的三种图搜索算法:Breadth First Search、Dijkstra 和 A* 搜索。 在本讲中我们将讨论运动规划器中使用的方法,以处理动态物体和…

sql server 数据库忘记sa账户密码/ 无管理员账户解决办法

一、计算机超级管理员账户有数据库的管理员权限 用管理员账户登录数据库,直接修改sa账户密码即可。 二、数据库中没有管理员权限的账户 SQL Server 2005/2008提供了针对该情况的更好的灾难恢复方法,无需侵入master数据库,不会对master数据库…

机器学习编译第1讲:机器学习编译概述

MLC-机器学习编译-第一讲-机器学习编译概述课程主页:https://mlc.ai/summer22-zh/ 文章目录1.0 概述1.1 什么是机器学习编译1.2 为什么学习机器学习编译1.3 机器学习编译的关键要素1.3.1 备注:抽象和实现1.4 总结1.0 概述 机器学习应用程序已经无处不在…

【Python学习】 - Pandas学习 sort_value( ),sort_index( )排序函数的区别与使用

按索引对DataFrame或Series进行排序(注意ascendingfalse的意思是按照降序排序,若不写参数则默认升序排序) DataFrame的构造函数默认参数是(值,列名,行索引),行索引不填则默认0,1,2,3这样? In …

powerdesign 缩写AK,PK,IX,CK,FK,DF,UQ

PK - Primary Key IX - Non-Unique Index AK - Unique Index (AX should have been AK (Alternate Key)) CK - Check Constraint DF - Default Constraint FK - Foreign Key UQ - Unique Constraint

重读经典:《The Craft of Research(1)》

跟读者建立联系【研究的艺术一】这一次李沐博士给大家精读的是一本关于论文写作的书籍。这本书总共包含四个大的章节,本期视频李沐博士介绍的是第一个章节:Research,Researchers,and Readers。 0. 前言 视频开头,李沐…

【PAT - 甲级1045】Favorite Color Stripe(30分)(dp,LIS类问题)

题干: Eva is trying to make her own color stripe out of a given one. She would like to keep only her favorite colors in her favorite order by cutting off those unwanted pieces and sewing the remaining parts together to form her favorite color s…

机器学习编译第2讲:张量程序抽象

02 张量程序抽象 【MLC-机器学习编译中文版】课程主页:https://mlc.ai/summer22-zh/ 文章目录2.1 元张量函数2.2 张量程序抽象2.2.1 张量程序抽象中的其它结构2.3 张量程序变换实践2.3.1 安装相关的包2.3.2 构造张量程序2.3.3 编译与运行2.3.4 张量程序变换2.3.5 通…

迁移TFS 2012服务至新的电脑硬件

迁移TFS 2012的时候碰到一些问题, 中文记录很少, 英文的记录也比较零散. 这里记录最直接和简单的方法. 环境: 1. 公司域环境, 所有TFS用户都是公司域帐户. 2. TFS从一台服务器转移至另一台服务器. 都加入了公司域. 机器名分别为PC1和PC2. 域内不能有同名的电脑。 准备两台…

【PAT - 甲级1005】Spell It Right (20分) (递归输出,水题)

题干: Given a non-negative integer N, your task is to compute the sum of all the digits of N, and output every digit of the sum in English. Input Specification: Each input file contains one test case. Each case occupies one line which contain…

详解自动驾驶仿真数据集 SHIFT:A Synthetic Driving Dataset for Continuous Multi-Task Domain Adaptation

SHIFT:A Synthetic Driving Dataset for Continuous Multi-Task Domain Adaptation本文介绍一个新的自动驾驶仿真数据集:SHIFT,论文收录于 CVPR2022。适应连续变化的环境是自动驾驶系统一直以来要面临的挑战。然而,目前现有的图像…

TFS下的源代码控制

以下主要描述了: TFS源代码控制系统的基本场景如何把一个项目添加到源代码管理中如何与服务器同步如何做Check-In如何做分支与合并什么是上架与下架 我们知道工作项是项目管理的基本元素,但是一个项目的成功,光有工作项还是不够的。工作项说…

【PAT - 甲级1004】Counting Leaves (30分) (dfs,递归)

题干&#xff1a; A family hierarchy is usually presented by a pedigree tree. Your job is to count those family members who have no child. Input Specification: Each input file contains one test case. Each case starts with a line containing 0<N<100,…

地平线:面向规模化量产的智能驾驶系统和软件开发

导读 7月27日&#xff0c;地平线在智东西公开课开设的「地平线自动驾驶技术专场」第3讲顺利完结&#xff0c;地平线智能驾驶应用软件部负责人宋巍围绕 《面向规模化量产的智能驾驶系统和软件开发》这一主题进行了直播讲解。本次分享主要分为以下4个部分&#xff1a; 1、智能驾驶…

【转】TFS上分支和标签的用法

什么时候用分支: 例如为某个客户定制的专用版本,和主干的特性有很大差别.不具通用性的需求. 大的版本修改,例如2.0 到3.0 加了很多特性,但2.0 还有维护.改bug 什么时候用标签: 小版本的发布, 如2.1.1到2.1.2. 分支的优点: 清晰,容易操作,程序员只要get latest/checkin latest就…

【PAT - 甲级1034】Head of a Gang (30分)(并查集)

题干&#xff1a; One way that the police finds the head of a gang is to check peoples phone calls. If there is a phone call between A and B, we say that A and B is related. The weight of a relation is defined to be the total time length of all the phone c…