`
wade6
  • 浏览: 274230 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

有关JVM处理Java数组方法的思考

    博客分类:
  • jvm
 
阅读更多

 

在Java中,获取数组的长度和String的长度是两种不同的方法,这引起了本文作者的一番思考。本文从JVM的角度,探讨了Java数组在JVM中是什么对象,有哪些成员,以及声明方法。

 

 

本文来自jarfield的博客,原文标题为《为什么如此获取Java数组的长度》。

记得vamcily 曾问我:“为什么获取数组的长度用.length(成员变量的形式),而获取String的长度用.length()(成员方法的形式)?”

我当时一听,觉得问得很有道理。做同样一件事情,为什么采用两种风格迥异的风格呢?况且,Java中的数组其实是完备(full-fledged)的对象,直接暴露成员变量,可能不是一种很OO的风格。那么,设计Java的那帮天才为什么这么做呢?

带着这个疑问,我查阅了一些资料,主要是关于“JVM是如何处理数组”的。

数组对象的类是什么?

既然数组都是对象,那么数组的类究竟是什么呢?当然不是java.util.Arrays啦!我们以int一维数组为例,看看究竟。

  1. public class Main {   
  2. public static void main(String args[]){   
  3. int a[] = new int[10]; Class clazz = a.getClass();   
  4. System.out.println(clazz.getName());   
  5. }   
  6. }  
  7.  

在SUN JDK 1.6上运行上述代码,输出为:

[I

看起来数组的类很奇怪,非但不属于任何包,而且名称还不是合法的标识符(identifier)。具体的命名规则[1]可以参见java.lang.Class.getName()的javadoc。简单的说,数组的类名由若干个'['和数组元素类型的内部名称组成,'['的数目代表了数组的维度。

具有相同类型元素和相同维度的数组,属于同一个类。如果两个数组的元素类型相同,但维度不同,那么它们也属于不同的类。如果两个数组的元素类型和维度均相同,但长度不同,那么它们还是属于同一个类。

数组的类有哪些成员呢?

既然我们知道了数组的类名是什么,那么就去看看数组的类究竟是什么样的吧?有哪些成员变量?有哪些成员方法?length这个成员变量在哪?是不是没有length()这个成员方法?

找来找去,在JDK的代码中没有找打'[I'这个类。想想也对,'[I'都不是一个合法的标识符,肯定不会出现public class [I {...}这样的Java代码。我们暂且不管[I类是谁声明的,怎么声明的,先用反射机制一探究竟吧。

  1. public class Main {   
  2. public static void main(String[] args) {   
  3. int a[] = new int[10]; 
  4. Class clazz = a.getClass();   
  5. System.out.println(clazz.getDeclaredFields().length);   
  6. System.out.println(clazz.getDeclaredMethods().length);   
  7. System.out.println(clazz.getDeclaredConstructors().length);   
  8. System.out.println(clazz.getDeclaredAnnotations().length);   
  9. System.out.println(clazz.getDeclaredClasses().length);   
  10. System.out.println(clazz.getSuperclass());   
  11. }   
  12. }  
  13.  

在SUN JDK 1.6上运行上述代码,输出为:

  1. 0 
  2. 0 
  3. 0 
  4. 0 
  5. 0 
  6. class java.lang.Object  
  7.  

可见,[I这个类是java.lang.Object的直接子类,自身没有声明任何成员变量、成员方法、构造函数和Annotation,可以说,[I就是个空类。我们立马可以想到一个问题:怎么连length这个成员变量都没有呢?如果真的没有,编译器怎么不报语法错呢?想必编译器对Array.length进行了特殊处理哇!

数组的类在哪里声明的?

先不管为什么没有length成员变量,我们先搞清楚[I这个类是哪里声明的吧。既然[I都不是合法的标识符,那么这个类肯定在Java代码中显式声明的。想来想去,只能是JVM自己在运行时生成的了。JVM生成类还是一件很容易的事情,甚至无需生成字节码,直接在方法区中创建类型数据,就差不多完工了。

还没有实力去看JVM的源代码,于是翻了翻The JavaTM Virtual Machine Specification  Second Edition,果然得到了验证,相关内容参考5.3.3 Creating Array Classes。

规范的描述很严谨,还掺杂了定义类加载器和初始化类加载器的内容。先不管这些,简单概括一下:

类加载器先看看数组类是否已经被创建了。如果没有,那就说明需要创建数组类;如果有,那就无需创建了。

如果数组元素是引用类型,那么类加载器首先去加载数组元素的类。

JVM根据元素类型和维度,创建相应的数组类。

呵呵,果然是JVM这家伙自个偷偷创建了[I类。JVM不把数组类放到任何包中,也不给他们起个合法的标识符名称,估计是为了避免和JDK、第三方及用户自定义的类发生冲突吧。

再想想,JVM也必须动态生成数组类,因为Java数组类的数量与元素类型、维度(最多255)有关,相当相当多了,是没法预先声明好的。

居然没有length这个成员变量!

我们已经发现,偷懒的JVM没有为数组类生成length这个成员变量,那么Array.length这样的语法如何通过编译,如何执行的呢?

让我们看看字节码吧!编写一段最简单的代码,使用jclasslib查看字节码。

  1. public class Main {   
  2. public static void main(String[] args)   
  3. int a[] = new int[2]; int i = a.length;   
  4. }   
  5. }  
  6.  

使用SUN JDK 1.6编译上述代码,并使用jclasslib打开Main.class文件,得到main方法的字节码:

  1. 0 iconst_2                   //将int型常量2压入操作数栈  
  2. 1 newarray 10 (int)    //将2弹出操作数栈,作为长度,创建一个元素类型为int, 维度为1的数组,并将数组的引用压入操作数栈  
  3. 3 astore_1                 //将数组的引用从操作数栈中弹出,保存在索引为1的局部变量(即a)中  
  4. 4 aload_1                  //将索引为1的局部变量(即a)压入操作数栈  
  5. 5 arraylength            //从操作数栈弹出数组引用(即a),并获取其长度(JVM负责实现如何获取),并将长度压入操作数栈  
  6. 6 istore_2                 //将数组长度从操作数栈弹出,保存在索引为2的局部变量(即i)中  
  7. 7 return                    //main方法返回  
  8.  

可见,在这段字节码中,根本就没有看见length这个成员变量,获取数组长度是由一条特定的指令arraylength实现(怎么实现就不管了,JVM总有办法)。编译器对Array.length这样的语法做了特殊处理,直接编译成了arraylength指令。另外,JVM创建数组类,应该就是由newarray这条指令触发的了。

很自然地想到,编译器也可以对Array.length()这样的语法做特殊处理,直接编译成arraylength指令。这样的话,我们就可以使用方法调用的风格获取数组的长度了,这样看起来貌似也更加OO一点。那为什么不使用Array.length()的语法呢?也许是开发Java的那帮天才对.length有所偏爱,或者抛硬币拍脑袋随便决定的吧。 形式不重要,重要的是我们明白了背后的机理。

Array in Java

最后,对Java中纯对象的数组发表点感想吧

相比C/C++中的数组,Java数组在安全性要好很多。C/C++常遇到的缓存区溢出或数组访问越界的问题,在Java中不再存在。因为Java使用特定的指令访问数组的元素,这些指令都会对数组的长度进行检查。如果发现越界,就会抛出java.lang.ArrayIndexOutOfBoundsException。

 

Java数组元素的灵活性比较大。一个数组的元素本身也可以是数组,只要所有元素的数组类型相同即可。我们知道数组的类型和长度无关,因此元素可以是长度不同的数组。这样,Java的多维数组就不一定是规规矩矩的矩阵了,可以千变万化。

 

分享到:
评论

相关推荐

    怎样用Jvm处理Java数组.doc

    ### 如何理解Java数组在JVM中的处理方式 在探讨如何使用JVM处理Java数组之前,我们首先需要明确几个概念。Java数组本质上是一种特殊的对象,它具有固定长度,并且存储同种类型的元素。本文将围绕Java数组的一些核心...

    java基础知识思考题+答案(个人整理)

    ### Java基础知识思考题详解 #### 1. 缩写JDK的含义是什么? JDK,全称Java Development Kit,即Java开发工具包。它是Java软件开发的基础,包含了编写、编译和运行Java程序所需的所有工具。JDK不仅包括Java编译器...

    Java复习---思考题

    Java 复习资料中的思考题涵盖了Java的基础知识,包括JDK的含义、Java跨平台技术、垃圾收集机制、J2SE、J2ME、J2EE的区别、包的作用、J2SE类库的主要包以及Java编程规范和程序结构。下面将详细解答这些知识点。 1. ...

    Java程序设计教程,电子教案,实例源程序,思考练习参考答案

    1. **Java基础知识**:Java是一种面向对象的编程语言,它的设计目标是具有平台无关性,通过Java虚拟机(JVM)可以在不同的操作系统上运行。基础知识点包括变量、数据类型、运算符、控制流(如if语句、for循环、while...

    Java基础-18天入门版

    ### Java基础-18天入门版 #### 第一天:开班+Java 入门 ...接下来的几天将继续深入学习Java的基础知识,包括数据类型、控制结构、数组、方法等。通过每天的学习和练习,逐步建立起扎实的Java基础知识体系。

    JAVA课程总复习

    11. **JVM内存模型**:解释Java虚拟机的工作原理,包括堆内存、栈内存、方法区、本地方法栈等区域,以及垃圾回收机制。 12. **设计模式**:介绍常见的设计模式,如单例、工厂、观察者、装饰者等,并探讨它们在实际...

    魔乐Java 李兴华 Java面向对象

    【Java面向对象】是编程语言Java的核心特性,它允许我们以更加抽象和模块化的方式思考问题,通过模拟现实世界中的对象来创建程序。在Java中,数据类型分为两类:基本数据类型和引用数据类型。基本数据类型包括数值型...

    Thinking Java Programming Code

    5. **集合框架**:Java集合框架是处理对象数组的关键,包括List、Set、Queue和Map接口,以及ArrayList、LinkedList、HashSet、HashMap等实现类。 6. **输入输出(I/O)**:Java的I/O流系统允许程序读写文件、网络和...

    java习题代码集

    了解Java虚拟机(JVM)的内存区域,如堆、栈、方法区、本地方法栈以及垃圾回收机制,对于优化程序性能和解决内存泄漏问题至关重要。 总的来说,“java习题代码集”是一个极好的实践平台,它涵盖了Java编程的诸多...

    Head First Java (清晰版)

    书中还会涵盖数组、集合框架、接口、异常处理、输入/输出流以及线程等Java核心特性。此外,作者通过趣味性的例子和互动式练习,帮助读者理解并巩固这些知识。 在学习过程中,读者将逐步学会如何创建、编译和运行...

    中南大学Java实验报告三

    Java程序的执行依赖于Java虚拟机(JVM),它使得代码能在不同的操作系统上运行,实现了“一次编写,到处运行”。 二、面向对象编程(OOP) 在Java中,面向对象编程是核心特性。实验报告可能会涵盖以下概念: 1. *...

    Java大作业

    字节码可以在任何支持 Java 虚拟机(JVM)的平台上解释执行,这就是 Java 的平台无关性。这意味着 Java 程序可以在不同的操作系统和硬件架构上运行,只需要有对应的 JVM。 Java 的技术特点包括但不限于以下几个方面...

    java实验报告模板

    6. **编写使用Java数组的程序**:学习如何声明和使用数组存储数据。 ##### 实验内容 1. **示例程序分析**: - **复合语句程序**:分析复合语句的使用,特别关注块内定义变量的影响。 - **水仙花数程序**:找出...

    java第二章语言基础(课件、开发的软件及其源码)

    Java程序可以在任何支持Java虚拟机(JVM)的平台上运行,实现了“一次编写,到处运行”的理念。 第二章主要涉及的内容可能包括: 1. **基本语法**:变量、数据类型、运算符、流程控制(如if-else,switch,for,...

    《Java语言程序设计(一)》课后习题答案(课程编号04747)

    3. **加载和链接**:Java解释器(即JVM)加载字节码文件到内存,并进行链接处理。 4. **执行**:JVM解释执行字节码,或通过JIT编译器将其编译成本地机器码执行。 #### 1.2 开发与运行JAVA程序的重要步骤 1. **环境...

    广工Java实验+课设

    它以其“一次编写,到处运行”的特性而闻名,因为Java代码可以在任何支持Java虚拟机(JVM)的平台上运行。Java的设计理念是简单性、健壮性、安全性、可移植性和高性能。 【广工Java实验】 广东工业大学(简称...

    java编的五子棋

    "包含中间生成文件"可能是指编译过程中的.class文件,这些是Java字节码文件,由Java编译器将.java源文件转换而来,可供JVM(Java虚拟机)直接运行。 在Java中开发五子棋游戏,主要涉及以下几个核心知识点: 1. **...

    Java语言程序设计-基础篇(原书第8版).pdf

    5. 数组和字符串的处理:Java中数组是一种引用数据类型,用来存储一系列类型相同的变量。字符串(String)在Java中是不可变的,即一旦创建,其内容不能改变。 6. 面向对象高级特性:如继承、封装和多态等。继承允许...

    Java程序设计基础教程(慕课版)电子教案.zip

    数组和集合框架是Java编程中处理数据的重要工具。数组是一种线性的数据结构,存储相同类型的元素,而集合框架如ArrayList、LinkedList、HashSet等提供了更灵活的数据管理方式。学生需要掌握它们的创建、添加、删除...

    java编程练习20题

    3. **数组**:数组是Java中存储一组相同类型数据的重要结构,题目可能会要求处理一维或二维数组,包括初始化、遍历、排序等操作。 4. **字符串操作**:Java的String类提供了丰富的操作字符串的方法,如...

Global site tag (gtag.js) - Google Analytics