在上一篇中,我们已经了解了数组,它是一种引用类型,本篇将详细介绍数组的内存分配等知识点。数组用来存储同一种数据类型的数据,一旦初始化完成,即所占的空间就已固定下来,即使某个元素被清空,但其所在空间仍然保留,因此数组长度将不能被改变。当仅定义一个数组变量(int[] numbers)时,该变量还未指向任何有效的内存,因此不能指定数组的长度,只有对数组进行初始化(为数组元素分配内存空间)后才可以使用。数组初始化分为静态初始化(在定义时就指定数组元素的值,此时不能指定数组长度)和动态初始化(只指定数组长度,由系统分配初始值)。

//静态初始化
int[] numbers = new int[] { 3, 5, 12, 8, 7 };
String[] names = { "Miracle", "Miracle He" };//使用静态初始化的简化形式

//动态初始化
int[] numbers = new int[5];
String[] names = new String[2];

建议不要混用静态初始化和动态初始化,即不要既指定数组的长度的同时又指定每个元素的值。当初始化完毕后,就可以按索引位置(0~array.length-1)来访问数组元素了。当使用动态初始化时,如在对应的索引位未指定值的话,系统将指定相应数据类型对应的默认值(整数为0,浮点数为0.0,字符为'\u0000',布尔类型为false,引用类型为null)。

public class TestArray {public static void main(String[] args) {String[] names = new String[3];names[0] = "Miracle";names[1] = "Miracle He";//以下代码将输出Miracle Miracle He null/*for(int i = 0; i < names.length;i++) {System.out.print(names[i] + " ");}*///还可以使用foreach来遍历for(String name : names) {System.out.print(name + " ");}}
}

请注意:java中是没有foreach这个关键字的,其语法是for(type item : items)来表示,但foreach只能用于遍历元素的值而不能改变,必须使用for才能实现。

public class TestForEach {public static void main(String[] args) {int[] numbers = { 3, 5, 12, 8, 7 };for(int number : numbers) {int num = number * 10;System.out.print(num + ",");}System.out.println("");//numbers仍然未发生变化(如果换成for将改变)for(int i = 0;i < numbers.length;i++) {System.out.print(numbers[i] + ",");}}
}

以上简单的介绍了数组的初始化和应用,接下来讲详细介绍数组(数组引用和数组元素)在内存中的存放形式。首先给出结论:数组引用变量是存放在栈内存(stack)中,数组元素是存放在堆内存(heap)中,通过栈内存中的指针指向对应元素的在堆内存中的位置来实现访问,以下图来说明数组此时的存放形式。

那什么是栈内存和堆内存呢?我举例作一一解释。当执行方法时,该方法都会建立自身的内存栈,以用来将该方法内部定义的变量逐个加入到内存栈中,当执行结束时方法的内存栈也随之销毁,我们说所有变量存放在栈内存中,即随着寄存主体的消亡而消亡;反之,当我们创建一个对象时,这个对象被保存到运行时数据区中,以便反复利用(因为创建成本很高),此时不会随着执行方法的结束而消亡,同时该对象还可被其他对象所引用,只有当这个对象没有被任何引用变量引用时,才会在垃圾回收在合适的时间点回收,我们说此时变量所指向的运行时数据区存在堆内存中。

只有类型兼容(即属于同一数据类型体系且遵守优先级由低到高原则),才能将数组引用传递给另一数组引用,但仍然不能改变数组长度(仅仅只是调整数组引用指针的指向)。

public class TestArrayLength {public static void main(String[] args) {int[] numbers = { 3, 5, 12 };int[] digits = new int[4];System.out.println("digits数组长度:" + digits.length);//4for(int number : numbers) {System.out.print(number + ",");//3,5,12,
        }System.out.println("");for(int digit : digits) {System.out.print(digit + ",");//0,0,0,0,
        }System.out.println("");digits = numbers;System.out.println("digits数组长度:" + digits.length);//3
    }
}

虽然看似digits的数组长度看似由4变成3,其实只是numbers和digits指向同一个数组而已,而digits本身失去引用而变成垃圾,等待垃圾回收来回收(但其长度仍然为4),但其内部运行机制如下图所示。

因此当我们看一个数组时(或者其他引用变量),通常看成两部分:数组引用变量和数组元素本身,而数据元素是存放在堆内存中,只能通过数组引用变量来访问。

从上述的示例中看出数组中存放的是基本类型,其实数组中还可以存放引用类型的。而存放基本类型的内存分布已经解释了,而存放引用类型的内存分布则相对复杂了。来看一段非常简单的程序。

public class TestPrimitiveArray {public static void main(String[] args) {//1.定义数组int[] numbers;//2.分配内存空间numbers = new int[4];//3.为数组元素指定值for(int i = 0;i < numbers.length;i++) {numbers[i] = i * 10;}}
}

按以上步骤的内存分布示意图:

从图中可看出数组元素直接存放在堆内存中,当操作数组元素时,实际上是操作基本类型的变量。接下来再看一段程序:

class Person {public int age;public String name;public void display() {System.out.println(name + "的年龄是: " + age);}
}
public class TestReferenceArray {public static void main(String[] args) {//1.定义数组
        Person[] persons;//2.分配内存空间persons = new Person[2];//3.为数组元素指定值Person p1 = new Person();p1.age = 28;p1.name = "Miracle";Person p2 = new Person();p2.age = 30;p2.name = "Miracle He";persons[0] = p1;persons[1] = p2;//输出元素的值for(Person p : persons) {p.display();}}
}

对于数组元素为引用类型在内存中的存储与基本类型不一样,此时数组元素仍然存放引用,指向另一块内存,在其中存放有效的数据。

谈到这里,不知是否有朋友要问:Java的多维数组是什么样的?我的回答是:可以有。为什么呢?从底层来看,数组元素可以存放引用类型,包含数组。也就是说在数组元素的内部还可以包含数组(如int[][] numbers = new int[length][]),也即二维数组可当作一维数组(数组长度为length)来处理,也可以同时指定多个维度的长度(如int[][] matrix = new int[length][width]),不过必须至少指定最左端的数组长度length。由此我们得出结论: 任何多维数组(维度为n,n>1)都当作一维数组,其数组元素为n-1维数组。

public class TestMultiArray {public static void main(String[] args) {//1.定义二维数组int[][] numbers;//2.分配内存空间numbers = new int[3][];//可以把numbers看作一维数组来处理for(int i = 0;i < numbers.length;i++) {System.out.print(numbers[i] + ",");//null,null,null
        }System.out.println("");//3.为数组元素指定值numbers[0] = new int[2];numbers[0][1] = 1;for(int i = 0;i < numbers[0].length;i++) {System.out.print(numbers[0][i] + ",");//0,1
        }}
}

最后,简单介绍一下Arrays(位于java.util下)的静态方法:binarySearch、copyOf、copyOfRange、equals、fill、sort、toString等方法(具体用法参见JDK)。

import java.util.Arrays;
public class TestArrays {public static void main(String[] args) {int[] a = {3, 4, 5, 6};int[] b = {3, 4, 5, 6};System.out.println("a和b是否相等:" + Arrays.equals(a, b));//trueSystem.out.println("5在a中的位置:" + Arrays.binarySearch(a, 5));//2int[] c = Arrays.copyOf(a, 6);System.out.println("a和c是否相等:" + Arrays.equals(a, c));//falseSystem.out.println("c的元素:" + Arrays.toString(c));//3,4,5,6,0,0Arrays.fill(c, 2, 4, 1);//将c中第3个到第5个元素(不包含)赋值为1System.out.println("c的元素:" + Arrays.toString(c));//3,4,1,1,0,0
        Arrays.sort(c);System.out.println("c的元素:" + Arrays.toString(c));//0,0,1,1,3,4
    }
}

接下来,给出两个数组实际应用场景的示例。

import java.util.Arrays;
public class NumberToRMB {private String[] numbers = { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" };private String[] units = { "拾", "佰","仟" };/*** 把一个浮点数分成整数部分和小数部分* @param number 要进行分割的浮点数* @return 由整数部分和小数部分组成的字符串数组*/private String[] divide(double number) {long zheng = (long)number;long xiao = Math.round((number - zheng) * 100);return new String[] { zheng + "", String.valueOf(xiao) };}/*** 把一个四位数字字符串转化四位人民币大写字符串* @param str 要转化的四位数字字符串* @return 四位人民币大写字符串*/private String toRMBString(String str) {String money = "";for(int i = 0, len = str.length(); i < len; i++) {int num = str.charAt(i) - 48;if(i != len - 1 && num != 0) {money += numbers[num] + units[len - 2 - i];} else {money += numbers[num];}}return money;}public static void main(String[] args) {NumberToRMB rmb = new NumberToRMB();System.out.println(Arrays.toString(rmb.divide(2346.789)));System.out.println(rmb.toRMBString("2346"));}
}

import java.io.*;
public class WZQ {//定义一个二维数组当作棋盘private String[][] board;//定义棋盘大小private static int BOARD_SIZE = 15;//初始化棋盘private void initBoard() {board = new String[BOARD_SIZE][BOARD_SIZE];for(int i = 0; i < BOARD_SIZE; i++) {for(int j = 0; j < BOARD_SIZE; j++) {board[i][j] = "+";}}}//打印棋盘private void printBoard() {for(int i = 0; i < BOARD_SIZE; i++) {for(int j = 0; j < BOARD_SIZE; j++) {System.out.print(board[i][j]);}System.out.println("");}}//开始下棋public void play() throws Exception {initBoard();printBoard();//获取键盘输入BufferedReader br = new BufferedReader(new InputStreamReader(System.in));String input = null;do {if(input != null) {String[] pos = input.split(",");int x = Integer.parseInt(pos[0]);int y = Integer.parseInt(pos[1]);board[x - 1][y - 1] = "●";printBoard();}System.out.print("请输入你下棋的坐标(以x,y的形式):");} while((input = br.readLine()) != null);}public static void main(String[] args) throws Exception {WZQ wzq = new WZQ();    wzq.play();}
}

数字转化为人民币大写程序中,利用了一维数组表示大写及单位;五子棋游戏中,利用了二维数组表示棋盘。从程序中可看到throws Exception表示不处理任何异常,将在后续的篇章中继续讲解。

转载于:https://www.cnblogs.com/archermeng/p/7537663.html

数组及引用类型内存分配相关推荐

  1. 二维数组及其二维数组的动态内存分配

    本文为大一时所写的文章(2017/4/9),文笔还很生疏,在很多问题上认识不深,算是在学校的微信公众号上的一个编程探究模块上的投稿,本人当时也参与了本模块的维护和管理.补档. 今天我们来聊聊二维数组及 ...

  2. 二维数组及其动态内存分配

    一.二维数组的基本概念 1.1 二维数组的内存映像 从内存角度看,二维数组和一维数组一样,在内存中都是连续分布的多个内存单元,并没有本质差别,只是内存的管理方式不一样,如下图所示 一维数组int a[ ...

  3. Java数组初始化的内存分配

    1.Java数组是静态的 Java是静态语言,所以Java的数组也是静态的,即:数组被初始化后,长度不可变 静态初始化:显式指定每个数组元素的初始值,系统决定数组长度 String[] books = ...

  4. C/C++动态二维数组的内存分配和释放

    C语言: 1 //二维数组动态数组分配和释放 2 //数组指针的内存分配和释放 3 //方法一 4 char (*a)[N];//指向数组的指针 5 a = (char (*)[N])malloc(s ...

  5. C++/C--动态二维数组的内存分配与释放【转载】

    1 C语言_二维数组动态数组分配和释放 1.1 数组指针的内存分配和释放 //方法一 char (*a)[N];//指向数组的指针 a = (char (*)[N])malloc(sizeof(cha ...

  6. 数组大小分配(动态内存分配)

    在使用数组的时候,总是有一个问题,数组应该有多大? 在很多情况下,我们无法确定要使用多大的数组.一般申请大于估计数目的固定大小,这样程序在运行时就申请了固定的大小,你觉得数组定义足够大,但是如果某种原 ...

  7. Memcache内存分配策略

    转自:http://tank.blogs.tkiicpp.com/2010/12/14/memcache%e5%86%85%e5%ad%98%e5%88%86%e9%85%8d%e7%ad%96%e7 ...

  8. C++中的动态内存分配

    1.Cpp中的内存分配 了解动态内存在C++中是如何工作的是成为一名合格的C++程序员必不可少的.C++程序中的内存分为两个部分: 栈:在函数内部声明的所有变量都将占用栈内存. 堆:这是程序中未使用的 ...

  9. C++ 高级数据类型(四)—— 动态内存分配

    到目前为止,我们的程序中我们只用了声明变量.数组和其他对象(objects)所必需的内存空间,这些内存空间的大小都在程序执行之前就已经确定了.但如果我们需要内存大小为一个变量,其数值只有在程序运行时 ...

  10. 4-数组、指针与字符串1.4-动态内存分配

    这种在程序运行过程中申请和释放的存储单元也称为堆对象,申请和释放过程过程一般称为建立和删除. 1.new运算和delete运算 运算符new的功能是动态分配内存,或者称为动态创建堆对象,语法形式为: ...

最新文章

  1. [zz]GNU C __attribute__ 机制简介
  2. java 包权限_Java基础(十二)之包和权限访问
  3. 1 uC/OS工程目录
  4. c++随机数函数rand()
  5. 如何用ACM简化你的Spring Cloud微服务环境配置管理
  6. 如何优雅地进行接口管理?(大厂内部分享)
  7. Struts2 多方法的Action
  8. 【ASP.NET Core】处理异常(下篇)
  9. python获取a股报表数据_python获取A股基础数据
  10. python实现简单银行管理系统
  11. iOS 屏幕旋转的实践解析
  12. 克隆硬盘后进不去系统_克隆硬盘后进不去系统_如何将硬盘克隆到较小的固态硬盘?...
  13. php递归算法计算n 介乘,递归算法示例——计算N的阶乘
  14. 什么叫SOC-新能源充电桩
  15. android热修复技术tinker,Android热修复方案第一弹——Tinker篇
  16. 关于手机QQ-好友的秘密 发送秘密者的位置信息获取
  17. skinsdog 狗网CSGO饰品皮肤开箱网站可取回的开箱网站
  18. python基础--绘制棋盘图形
  19. vue如何新建一个项目(详细步骤)
  20. vue字符云-------把老婆放到云端

热门文章

  1. 全自动mysql数据监控平台_Prometheus+Grafana打造Mysql监控平台
  2. 二元函数偏导数公式_高等数学六:(3)复合函数与微分性质
  3. 【2019银川网络赛A:】Maximum Element In A Stack(动态求栈中最大值)
  4. 【2019杭电多校第七场1006=HDU6651】Final Exam(思维转换)
  5. java访问kudu,KUDU的java操作
  6. mysql如何设置数据库内存溢出_MySQL数据库之关于MySQL的整型数据的内存溢出问题的应对方法...
  7. Pandas:金融数据下载和分析
  8. 190.颠倒二进制位
  9. linux mysql 开发_Linux64下mysql安装和开发
  10. 【生信进阶练习1000days】day22-复习day1~day14的知识点