`
rainworlder
  • 浏览: 30389 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

java

 
阅读更多

 

随笔 - 42  文章 - 0  评论 - 118

Java学习之旅基础知识篇:数组及引用类型内存分配

 

      在上一篇中,我们已经了解了数组,它是一种引用类型,本篇将详细介绍数组的内存分配等知识点。数组用来存储同一种数据类型的数据,一旦初始化完成,即所占的空间就已固定下来,即使某个元素被清空,但其所在空间仍然保留,因此数组长度将不能被改变。当仅定义一个数组变量(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);//4
        for(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));//true
        System.out.println("5在a中的位置:" + Arrays.binarySearch(a, 5));//2
        int[] c = Arrays.copyOf(a, 6);
        System.out.println("a和c是否相等:" + Arrays.equals(a, c));//false
        System.out.println("c的元素:" + Arrays.toString(c));//3,4,5,6,0,0
        Arrays.fill(c, 2, 4, 1);//将c中第3个到第5个元素(不包含)赋值为1
        System.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
    }
}
复制代码

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

数字转化为人民币大写(简易版)
五子棋游戏实现(简易版)

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

 
posted @ 2012-10-22 21:50 Miracle He 阅读(6396) 评论(2编辑 收藏
 

 
  
#1楼2012-10-22 22:46 dataexcel  
  
#2楼2016-04-22 01:03 倔强的鸭子  
 

公告

昵称:Miracle He
园龄:7年6个月
粉丝:159
关注:2
日 一 二 三 四 五 六
< 2012年10月 >
30 1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31 1 2 3
4 5 6 7 8 9 10
 
 
Copyright ©2016 Miracle He
分享到:
评论

相关推荐

    java源码包---java 源码 大量 实例

    Applet钢琴模拟程序java源码 2个目标文件,提供基本的音乐编辑功能。编辑音乐软件的朋友,这款实例会对你有所帮助。 Calendar万年历 1个目标文件 EJB 模拟银行ATM流程及操作源代码 6个目标文件,EJB来模拟银行ATM...

    Java 面经手册·小傅哥.pdf

    这是一本以面试题为入口讲解 Java 核心内容的技术书籍,书中内容极力的向你证实代码是对数学逻辑的具体实现。当你仔细阅读书籍时,会发现Java中有大量的数学知识,包括:扰动函数、负载因子、拉链寻址、开放寻址、...

    Java OCR 图像智能字符识别技术,可识别中文

    Java OCR(Optical Character Recognition,光学字符识别)技术是一种计算机视觉领域的应用,它能将图像中的文字转换成可编辑的文本格式。这项技术在各种场景下都有广泛应用,比如文档扫描、车牌识别、发票处理等。...

    Java API文档 中文网页版

    Java API文档是Java开发者的重要参考资料,它包含了Java开发工具包(JDK)中的所有类、接口、方法和常量的详细说明。这份中文网页版的Java API文档为中国的开发者提供了便利,无需通过英文版本来学习和查找API信息,...

    java源码包2

    Applet钢琴模拟程序java源码 2个目标文件,提供基本的音乐编辑功能。编辑音乐软件的朋友,这款实例会对你有所帮助。 Calendar万年历 1个目标文件 EJB 模拟银行ATM流程及操作源代码 6个目标文件,EJB来模拟银行...

    java源码包3

    Applet钢琴模拟程序java源码 2个目标文件,提供基本的音乐编辑功能。编辑音乐软件的朋友,这款实例会对你有所帮助。 Calendar万年历 1个目标文件 EJB 模拟银行ATM流程及操作源代码 6个目标文件,EJB来模拟银行...

    java电商源代码 java电商源代码

    java电商源代码java电商源代码java电商源代码java电商源代码java电商源代码java电商源代码java电商源代码java电商源代码java电商源代码java电商源代码java电商源代码java电商源代码java电商源代码java电商源代码java...

    java sql操作工具类 java sql操作工具类

    java sql操作工具类 java sql操作工具类java sql操作工具类 java sql操作工具类java sql操作工具类 java sql操作工具类java sql操作工具类 java sql操作工具类java sql操作工具类 java sql操作工具类java sql操作...

    java景点导航系统java景点导航系统

    java景点导航系统java景点导航系统java景点导航系统java景点导航系统java景点导航系统java景点导航系统java景点导航系统java景点导航系统java景点导航系统java景点导航系统java景点导航系统java景点导航系统java景点...

    java开源包4

    JoSQL(SQLforJavaObjects)为Java开发者提供运用SQL语句来操作Java对象集的能力.利用JoSQL可以像操作数据库中的数据一样对任何Java对象集进行查询,排序,分组。 搜索自动提示 Autotips AutoTips是为解决应用系统对于...

    java开源包9

    JoSQL(SQLforJavaObjects)为Java开发者提供运用SQL语句来操作Java对象集的能力.利用JoSQL可以像操作数据库中的数据一样对任何Java对象集进行查询,排序,分组。 搜索自动提示 Autotips AutoTips是为解决应用系统对于...

    JAVA上百实例源码以及开源项目源代码

    日历表格面板 [ConfigLine.java] 控制条类 [RoundBox.java] 限定选择控件 [MonthMaker.java] 月份表算法类 [Pallet.java] 调色板,统一配色类 Java扫雷源码 Java生成自定义控件源代码 2个目标文件 Java实现HTTP连接...

    Java算法集题大全.zip

    Java算法集题大全Java算法集题大全Java算法集题大全Java算法集题大全Java算法集题大全Java算法集题大全Java算法集题大全Java算法集题大全Java算法集题大全Java算法集题大全Java算法集题大全Java算法集题大全Java算法...

    java开源包8

    JoSQL(SQLforJavaObjects)为Java开发者提供运用SQL语句来操作Java对象集的能力.利用JoSQL可以像操作数据库中的数据一样对任何Java对象集进行查询,排序,分组。 搜索自动提示 Autotips AutoTips是为解决应用系统对于...

    java开源包10

    JoSQL(SQLforJavaObjects)为Java开发者提供运用SQL语句来操作Java对象集的能力.利用JoSQL可以像操作数据库中的数据一样对任何Java对象集进行查询,排序,分组。 搜索自动提示 Autotips AutoTips是为解决应用系统对于...

    Java2Pas Java代码转pas代码

    Java2Pas是一个实用工具,主要用于将Java编程语言编写的源代码转换为Pascal语言的等效代码。这个工具对于那些需要在两种语言之间迁移代码或者理解不同编程语言语法的开发者来说非常有价值。Java和Pascal虽然都是面向...

    Java开发技术大全(500个源代码).

    HelloWorldApp.java 第一个用Java开发的应用程序。 firstApplet.java 第一个用Java开发的Applet小程序。 firstApplet.htm 用来装载Applet的网页文件 第2章 示例描述:本章介绍开发Java的基础语法知识。 ...

    java错误处理:java.lang.OutOfMemoryError: Java heap space

    ### Java 错误处理:java.lang.OutOfMemoryError: Java heap space 在Java应用程序开发过程中,经常遇到的一个问题就是内存溢出错误,特别是在处理大量数据或长时间运行的应用时。其中,“java.lang....

    smali2java——直接将smali转换成java

    标题"smali2java——直接将smali转换成java"揭示了本文的核心主题,即一个名为"smali2java"的工具,它的主要功能是将编程语言Smali转换为Java。Smali是一种低级的、汇编式的语言,通常用于Android应用的逆向工程,而...

Global site tag (gtag.js) - Google Analytics