java 数组 内存_图解Java数组的内存分配

1. Java数组是静态的

Java是静态语言,所以Java的数组也是静态的,即:数组被初始化后,长度不可变

静态初始化:显式指定每个数组元素的初始值,系统决定数组长度

String[] books = new String[]{"疯狂Java讲义","Thinking in Java","Java核心技术卷"};

String[] names = new String[]{"张三","李四","王五","赵六"};

动态初始化:显式指定数组的长度,系统决定每个数组元素的初始值

String[] strArr = new String[5];

以上两种初始化的内存使用情况如图所示:

4c80a972aedb

动态初始化数组由系统决定每个数组元素的初始值,遵循以下规则:

byte、short、int、long类型的数组的初始值为0

float、double类型的数组的初始值为0.0

char类型的数组的初始值为'\u0000',表现为空字符'',但要占位

boolean类型的数组的初始值为false

引用类型的数组的初始值为null

一旦数组初始化完成,则长度不可改变,以下代码造成了数组初始化后长度可改变的假象:

books = names;

strArr = books;

System.out.println(books.length); // 4

System.out.println(strArr.length); // 4

Java的数组变量是一种引用类型的变量,数组变量并不是数组本身,它只是指向堆内存中的数组对象,改变一个数组变量所引用的数组,可以造成数组长度可变的假象。

上述代码让books数组变量和strArr数组变量都指向了names数组变量引用的数组,这样做的结果就是让三个数组变量都引用了相同的数组对象。内存图如下:

4c80a972aedb

而原来strArr和books引用的数组对象的长度不会改变,由于已经没有变量引用它们,它们成为垃圾,等待垃圾回收机制来回收。此时,程序使用strArr、books、和names这三个变量时,将会访问同一个数组对象。

JavaScript是动态语言,其数组的长度可以改变:

var arr = [];

document.write("arr的长度为:" + arr.length + "
"); // 0

arr[0] = 1;

document.write("arr的长度为:" + arr.length); // 1

2. 数组一定要初始化吗?

实际上,懂得了Java数组的内存机制后,我们可以说:数组不是必须初始化,或者说,数组初始化不仅有之前介绍的两种方法,可以完全换一种方法:

int[] nums = new int[]{1, 2, 3, 4, 5};

int[] arr;

arr = nums;

对于数组对象来说,必须初始化,也就是为该数组对象分配一块连续的内存空间,连续内存空间的长度就是数组对象的长度。

对于数组变量来说,不需要进行初始化,只需让其指向一个有效的数组对象就可以。

实际上,所有引用类型的变量,其变量本身不需要任何初始化,需要初始化的是它所引用的对象。

3. 基本类型数组的初始化

程序直接先为数组分配内存空间,再将数组元素的值存入对应的内存中。

int nums; ①

System.out.println(nums); ②

nums = new int[]{1, 2, 3, 4}; ③

System.out.println(nums.length); ④

我们来分析①-④步的内存:

① 定义nums数组变量后的存储示意图如下:

4c80a972aedb

对于②行代码来说没有任何问题,虽然nums变量并未引用到有效的数组对象,但此时并未通过nums变量访问数组对象的方法或属性,所以没有问题。

③ 静态初始化nums数组后的存储示意图如下:

4c80a972aedb

对于④行代码来说,此时访问了nums数组对象的属性,所以要求nums必须引用一个有效的对象。

当通过引用变量来访问实例属性或者调用非静态方法时,如果该引用变量还未指向有效的对象,程序就会抛出运行时异常:NullPointerException,例如,第二行代码如果为:

System.out.println(nums.length);

就会抛出NullPointerException

误区:基本类型的数据的值存储在栈内存中

实际上,应该是所有的局部变量都保存在栈内存中,不管是基本类型还是引用类型,局部变量都保存在各自的方法栈中。

4. 引用类型数组的初始化

引用类型数组的数组元素仍然是引用类型,因此数组元素里存储的还是引用,它指向另一块内存,这块内存里存储了该引用变量所引用的对象,包括数组和Java对象。

下面程序的执行代表了引用类型数组初始化的典型过程:

class Person{

String name;

int age;

public void info(){

System.out.println("name:" + name + ", age:" + age);

}

}

// 创建两个Person对象

Person p1 = new Person();

p1.name = "张三";

p1.age = 20;

Person p2 = new Person();

p2.name = "李四";

p2.age = 30;

// 将两个Person对象赋给数组元素

person[0] = p1;

person[1] = p2;

// 结果一样,p1和person[0]指向同一个对象

p1.info();

person[0].info();

分析内存:

4c80a972aedb

4c80a972aedb

4c80a972aedb

5. 数组元素就是变量

当数组引用变量指向一个有效的数组对象之后,程序就可以通过该数组变量访问数组对象。

Java语言不允许直接访问堆内存中的对象,因此无法直接访问堆内存中的数组对象,程序将通过数组引用变量来访问数组对象。

Java语言避免直接访问堆内存可以使程序更加健壮,如果程序直接访问并修改堆内存中的数据,可能破坏内存的数据完整性,从而导致程序Crash。

无论哪种类型的数组,其数组元素其实相当于一个普通变量。

下面程序师范了将数组元素和普通变量相互赋值的情形:

class Cat{

String name;

double height;

public Cat(String name, double height){

this.name = name;

this.height = height;

}

public void info(){

System.out.println("name:" + name + ", height:" + height);

}

}

public class TestArray {

public static void main(String[] args) {

int[] a = new int[5];

for(int i=0; i

a[i] = ++i;

}

int x = a[0];

int y = 99;

a[4] = 99;

Cat[] cats = new Cat[2];

cats[0] = new Cat("咪咪", 10.0);

cats[1] = new Cat("卡卡", 12.2);

Cat c1 = cats[0];

Cat c2 = new Cat("豆豆", 11.1);

cats[1] = c2;

}

}

内存分析:

4c80a972aedb

4c80a972aedb

6. 没有多维数组

所谓多维数组,其实只是数组元素元素依然是数组的数组,1维数组的数组元素的基本类型或者引用类型,2维数组的数组元素是1维数组,3维数组的数组元素是2维数组...N维数组的数组元素是N-1维数组。

Java允许将多维数组当成1维数组来处理,初始化多维数组时可以先初始化最左边的维数,此时该数组的每个数组元素都相当于一个数组引用变量,这些数组还需要进一步初始化。

如下程序示范了多维数组的用法:

public static void main(String[] args) {

int[][] a;

a = new int[4][];

// 把a当成一维数组,遍历a数组的每个数组元素

for(int i=0; i

System.out.println(a[i]); // 输出都为null

}

a[0] = new int[2];

a[0][1] = 2;

for(int i=0; i

System.out.println(a[0][i]); // 输出0和2

}

}

内存分析:

4c80a972aedb

说明:一般我们认为通过数组的length属性可以获取数组的长度,其实这个说法不够准确,例如数组a,它是一个传统的“二维数组”,它的长度好像定义不是很明确,其实,数组的长度指的是数组所分配的连续内存空间的长度,所以说a数组的长度为4.

我们用一个“极端”的程序来更好的理解数组在内存中的分配机制:

public static void main(String[] args) {

Object[] objArr1 = new Object[3];

objArr1[1] = new Object[2];

Object[] objArr2 = (Object[]) objArr1[1];

objArr2[1] = new Object[3];

Object[] objArr3 = (Object[]) objArr2[1];

objArr3[1] = new int[5];

int[] iArr = (int[]) objArr3[1];

for(int i=0; i

iArr[i] = ++i;

}

}

内存分析:

4c80a972aedb

4c80a972aedb

4c80a972aedb

4c80a972aedb

4c80a972aedb

4c80a972aedb

此时的objArr1不再是一个简单的一维数组,它是一个四维数组。

最后我们执行如下语句:

System.out.println(((int[])((Object[])((Object[])objArr1[1])[1])[1])[2]);

通过以上内存分析不难得出结果为3.

通过上面的分析不难得出,多维数组的本质仍然是一维数组。

因为Java是强类型语言,所以上诉代码需要多次强制类型转化,如果用弱类型语言实现会简单很多,比如用JavaScript实现:

var objArr1 = [];

objArr1[1] = [];

var objArr2 = objArr1[1];

objArr2[1] = [];

var objArr3 = objArr2[1];

objArr3[1] = [];

var iArr = objArr3[1];

for(var i=0; i<5; i++){

iArr[i] = i;

}

document.write(objArr1[1][1][1][2]);

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

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

相关文章

libgdx和Kotlin –类[2D平台原型]

这篇文章是libgdx和Kotlin文章的后续文章。 我已经决定开发一个简单的2D平台程序的原型&#xff08;沿着我的早期文章中的Star Assault进行介绍&#xff09;&#xff0c;但是我一直在使用和学习Kotlin&#xff0c;而不是Java。 对于本教程&#xff0c;该项目应处于上一篇文章…

spring jmx_JMX和Spring –第2部分

spring jmx这篇文章从本教程的第1部分继续。 嗨&#xff0c;在我的上一篇文章中&#xff0c;我解释了如何通过Spring设置JMX服务器以及如何通过身份验证和授权保护对它的访问。 在本文中&#xff0c;我将展示如何实现一个简单的MBean&#xff0c;该MBean允许用户在运行时更改L…

LeetCode:位运算实现加法

LeetCode&#xff1a;位运算实现加法 写在前面 位运算符 实现加法的思路 两个加数&#xff0c;比如5(101)和6(110)&#xff0c;如何不用加法就能得出两者之和呢&#xff1f; 我们知道二进制计算中&#xff0c;如果使用异或将会产生无进位的两者之和&#xff0c;而两数相与将会产…

[机器学习] 模型评价参数,准确率,召回率,F1-score

很久很久以前&#xff0c;我还是有个建筑梦的大二少年&#xff0c;有一天&#xff0c;讲图的老师看了眼我的设计图&#xff0c;说&#xff1a;“我觉得你这个设计做得很紧张”&#xff0c;当时我就崩溃&#xff0c;对紧张不紧张这样的评价标准理解无能。多年后我终于明白老师当…

java记录登陆时间_Spring security如何实现记录用户登录时间功能

一、原理分析spring security提供了一个接口 AuthenticationSuccessHandler,该接口中只有一个方法&#xff0c;用来进行登录成功后的操作public interface AuthenticationSuccessHandler {/*** Called when a user has been successfully authenticated.** param request the r…

bzoj3680

$模拟退火$ $这种全局最优的问题用模拟退火$ $模拟退火就是每次向四周随机移动&#xff0c;移动的幅度和温度成正比&#xff0c;如果新的位置更优就接受&#xff0c;否则按一定概率接收&#xff0c;概率和温度成正比$ $最后稳定后再在最优解附近蹦跶几下看看有没有更好的$ $你问…

01背包(修订版)

由于时间比较充裕&#xff0c;重新修订一部分。 这次把一些补充的放进来&#xff0c;其他的基础说明见后半部分 这些一共说明&#xff1a;01背包、完全背包、多重背包 将会详细说明。 三种背包混合、二维背包、分组背包、依赖背包、泛化背包 将大致说明。 01背包 如上次说明一…

java 马踏棋盘优化_我所知道的十大常用算法之马踏棋盘算法(深度搜索、贪心思想优化 )...

前言需求今天我们学习的是马踏棋盘算法&#xff0c;我们还是从一个场景里引入看看马踏棋盘算法也被称为骑士周游问题将马随机放在国际象棋的66棋盘Board0&#xff5e;5的某个方格中提示&#xff1a;马按走棋规则(马走日字)进行移动要求&#xff1a;每个方格只进入一次&#xff…

app engine_App Engine中的Google Services身份验证,第2部分

app engine在本教程的第一部分中&#xff0c; 我介绍了如何使用OAuth进行Google API服务的访问/身份验证。 不幸的是&#xff0c;正如我稍后发现的那样&#xff0c;我使用的方法是OAuth 1.0&#xff0c;显然现在Google正式弃用了OAuth 1.0&#xff0c;改用OAuth 2.0版本。 显然…

Django知识总结(一)

壹 ● 有关http协议 一 ● 全称 超文本传输协议(HyperText Transfer Protocol) 二 ● 协议 双方遵循的规范 http协议是属于应用层的协议(还有ftp, smtp等), 即浏览器请求消息和服务器响应消息的一系列规则 三 ● http协议的特性 http是无状态、无连接的协议(stateless, connect…

mysql+if+x+mod+2_Windows 下 MantisBT 2.X + Apache 2.4 + PHP 7 + MySQL 5.7 的环境配置

Apache 2.4.25~ Visual C Redistributable for Visual Studio 2015PHP 7.1.3~ mod_fcgid-2.3.9Mantisbt-2.2.1MySQL Database 5.7.17~ NET Framework 3.5安装 Apache前期准备1.若计算机已安装了 Apache2.4 以前的版本&#xff0c;请自行卸载并删除安装目录。2.以管理员身份运行…

如何在Java中对文件进行模式匹配和显示相邻行

最近&#xff0c;我们在jOOλ0.9.9中发布了有关超棒的窗口函数支持的文章 &#xff0c;我相信这是对我们所做的库的最佳补充。 今天&#xff0c;我们将在一个用例中研究窗口函数的出色应用&#xff0c;该用例受到以下堆栈溢出问题Sean Nguyen的启发&#xff1a; 如何从Java 8…

64位内核第二讲,进程保护之对象钩子

64位内核第二讲,进程保护. 一丶什么是保护. 什么是保护. 比如我们安装了xxx杀毒软件.那么此时你用任务管理器关闭.是关闭不了的.原因是内核已经做了保护. 那么去掉保护的前提就是你要给自己的软件做保护. 比如我们给计算器做保护. 例如下图. 做保护.以前的病毒作者.都是想要退出…

jpql hql_无需部署即可测试JPQL / HQL

jpql hql您是否曾经想在不完全部署应用程序的情况下测试JPQL / HQL&#xff1f; 我们今天在这里看到的是适用于任何JPA实现的简单解决方案&#xff1a;Hibernate&#xff0c;OpenJPA&#xff0c;EclipseLink等。 这篇文章中找到的基本源代码来自本书&#xff1a;“ Pro JPA 2&a…

eclipse 代码上传github 笔记

第一步 先share project 如图所示 第二步 如果所示 第三步 点击 下面的create 然后点击完成 第四步提交 第五步&#xff1a; 第六步&#xff1a; 转载于:https://www.cnblogs.com/a8457013/p/8410471.html

Keycloak SSO集成到jBPM和Drools Workbench中

介绍 单一登录&#xff08;SSO&#xff09;和相关令牌交换机制正在成为Web上不同环境中进行身份验证和授权的最常见方案&#xff0c;尤其是在迁移到云中时。 本文讨论了Keycloak与jBPM或Drools应用程序的集成&#xff0c;以便使用Keycloak上提供的所有功能。 Keycloak是用于浏…

接口 java性能_接口测试性能测试

接口测试 接口测试是测试系统组件间接口的一种测试&#xff0c;主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。测试的重点是要检查数据的交换&#xff0c;传递和控制管理过程&#xff0c;以及系统间的相互逻辑依赖关系等。 原理 通过测试程序模拟客户端向服务…

Redis 小结

一、redis简介 redis是一款基于C语言编写的&#xff0c;开源的非关系型数据库&#xff0c;由于其卓越的数据处理机制&#xff08;按照规则&#xff0c;将常用的部分数据放置缓存&#xff0c;其余数据序列化到硬盘&#xff09;&#xff0c;大家也通常将其当做缓存服务器来使用。…

NetBeans Java EE技巧#1 –数据库中的实体类

NetBeans IDE是开发各种应用程序的绝佳选择。 具体来说&#xff0c;我每天都使用它来开发和维护Java EE应用程序。 在过去的几个发行版中&#xff0c;不仅Java EE的生产力提高了&#xff0c;而且NetBeans IDE还减少了开发应用程序的时间……使Java EE和NetBeans成为绝配&#x…

asp mysql添加数据_ASP:ado.net 实例向数据库添加数据。

我在这使用的是老师给的数据库1&#xff0c;web窗体设计。设计添加图书窗体&#xff0c;窗体属性有图书种类(下拉列表框控件)&#xff0c;图书名称&#xff0c;作者&#xff0c;编号&#xff0c;出版社&#xff0c;价格均为文本框&#xff0c;日期(第三方日期控件)&#xff0c;…