JVM处理Java数组方法的思考

OpenMM | PDBFixer 用于蛋白质预处理 PDBFixer 用于蛋白质预处理 阅读详情

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

记得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有所偏爱,或者抛硬币拍脑袋随便决定的吧。 形式不重要,重要的是我们明白了背后的机理。

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

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

    【swjtu】算法实验2.4分析算法时间复杂度 分析给定算法的时间复杂度。 上机实验,绘制不同输入情况下的运行时间曲线,并与(1)中的分析结果进行比较。 撰写相应的实验报告,实验报告内容包括:实验目的、实验任务、 实验环境、实验步骤、实验结果及其分析以及实验总结等部分内容。 阅读详情

    相关推荐

    BUCK电路为何无法实现100%占空比?自举电路原理与替代方案解析

    在开关电源设计中,DC-DC转换器是实现电压变换的核心器件,其工作原理基于功率开关管的周期性导通与关断。BUCK(降压)电路作为一种经典拓扑,通过调节开关管的占空比来控制输出电压。然而,其占空比存在理论上限,无法达到100%,这一限制源于高边开关管的驱动需求。为了驱动高边N沟道MOSFET(NMOS),需要栅极电压高于源极电压,而自举(Bootstrap)电路通过利用开关节点电压的跳变,巧妙地生成了这一高压驱动信号。自举电路必须在每个开关周期内存在续流阶段(低边管导通)为电容充电,这从根本上限制了最大占空比

    weixin_32239523的博客 309

    为什么如此获取Java数组的长度

    记得vamcily 曾问我:“为什么获取数组的长度用.length(成员变量的形式),而获取String的长度用.length()(成员方法的形式)?”   我当时一听,觉得问得很有道理。做同样一件事情,为什么采用两种风格迥异的风格呢?况且,Java中的数组其实是完备(full-fledged)的对象,直接暴露成员变量,可能不是一种很OO的风格。那么,设计Java的那帮天才为什么这么做呢? ...

    Follow My Heart 2714

    如何配置 OpenClaw 使用免费搜索工具:摆脱付费 API 限制

    本文介绍了如何配置OpenClaw使用免费搜索工具来替代付费API。主要内容包括:1)启用内置的web_fetch工具直接获取网页内容;2)创建自定义脚本聚合免费AI资讯源;3)配置其他免费网络工具。文章详细说明了配置步骤,推荐了多个优质免费AI资讯网站,并分析了该方案的优劣势。这种配置方式完全免费且灵活,特别适合预算有限的用户,能够在不依赖付费API的情况下获取所需信息。

    dongdeaiziji的专栏 9814

    Java 数组 & JVM & 方法

    逻辑控制语句数组Array1、数组类型2、数组长度3、数组属性:4、数组元素:5、数组初始化的三种方式:6、数组遍历:7、数组内存图JVM内存空间方法区:栈:堆:类加载方法:1、语法/结构:2、参数:3、返回值:常用方法:ScannerSystemMathString:Arrays:案例猜字母游戏 数组Array 数组是最基本的一种数据结构 - 数据的排列方式 数组是引用类型,是可以存储多个变量的容器。 1、数组类型 数组的类型是固定的,只能存相同类型的数据 2、数组长度 数组一旦确定,长度就不可变 3、数

    Magiquitc的博客 760

    2023东华大学OJ机试题22-70

    东华大学计算机考研复试机试22 约瑟夫环223、整除的尾数24、回文质数25、汽水瓶26、阶乘最后的非0位27、算菜价28、 水果价格29、求奇数的乘积30、求最晚和最早日期31、素数32、计算e33 数字之和34 繁殖问题35 奇妙的数字36 整除的尾数37 黑色星期五38 树39 约瑟夫环40 最大与最小41 环42 求数列项43 最高频率44 三艘船45 回文数46 特殊四位数47 最大值48 数列149 修理牛棚50 按要求输出序列51 部落人乘法52 序列53 双重回文数54 等差数列55 人见人

    m0_49263811的博客 2598

    MATLAB实战:手把手教你绘制发动机万有特性曲线(附完整代码)

    本文提供了一份详细的MATLAB实战指南,手把手教你从发动机台架试验数据出发,通过数据预处理、核心插值算法和可视化技巧,绘制出专业、精确的发动机万有特性曲线图。文章深入解析了万有特性图的核心构成与工程意义,并附带了完整的模块化代码,帮助工程师和学生快速掌握这一关键技能。

    weixin_29094539的博客 410

    jvm七:数组创建本质

    package com.atChina.jvm; /* * 对于数组实例来说,其类型是由JVM在运行期动态生成的,表示为[Lcom.atChina.jvm.Father4 * 这种方式,动态生成的类型,其父类型就是Object * * 对于数组来说,JavaDoc经常将构成数组的元素称为Component,实际上就是将数组降低一个维度的类型 * * 助记符: * a...

    m0_37564426的博客 697

    jmeter小白下载安装步骤

    本文仅仅简单介绍了jmeter工具的下载安装,以及基础的操作步骤。

    昔年@曦的博客 3494

    Java中二维数组array.length和array[i].length的区别及实例

    Java中二维数组array.length和array[i].length的区别及实例 这两个length其实很容易区分。array.length代表的是二维数组行的长度,也可以看成每个二维数组中 所包含的数组的数量。array[i].length代表的是对应行的长度。 实例分析 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请...

    Sid的小站 8357

    第十篇|网络子系统 ②——无损以太网、PFC/ECN、DCQCN 与拥塞可视化

    网络部分了解了解

    qq_37535542的博客 1874

    架构设计之架构治理

    架构治理的本质是“用规则约束复杂性,用流程保障落地,用数据驱动优化”先定目标:明确量化的治理目标,避免盲目行动;立标准:建立覆盖分层、选型、接口、拆分的统一规范,奠定治理基础;强评审:通过架构评审把控设计环节,提前规避风险;全流程:将治理规则贯穿设计、开发、测试、部署、运维全生命周期;重保障:通过组织、流程、工具确保治理落地,而非“纸上谈兵”;持续迭代:随业务与技术演进优化治理规则,避免“一劳永逸”。

    xiaolyuh的专栏 810

    数组/JVM内存图/方法

    文章目录一、数组1.1数组初始化三种方式:1.1.1静态初始化省略格式(不能拆分)1.1.2静态初始化1.1.3动态初始化1.2数组扩容的三种方式1.2.1 例子:1.3数组排序快捷方法1.4数组遍历二、JVM内存图1.JVM内存划分2.数组在内存中的存储三、方法3.1 定义⽅法的两个明确总结 一、数组 1.1数组初始化三种方式: 注意:数组有定长特性,长度一旦指定,不可更改 1.1.1静态初始化省略格式(不能拆分) int []a={1,2,3,4}; 静态初始化,再次赋值时,不能整体赋值 1.1.2

    若葉的博客 891

    微信小程序 - 详细实现随机生成订单号,微信小程序创建订单号思路,不重复订单号生成的方法,小程序自动生成固定规则的订单号流水号(必须是唯一不重复的订单号,生成唯一订单号)详细生成方法,示例代码教程最新

    微信小程序,vue2,vue3,订单号,流水号思路,微信小程序 详细实现随机生成订单号(JavaScript),随机生成商户订单号,根据当前时间随机生成流水号或者订单号,微信小程序之商家订单,订单号随机生成器,自动生成固定规则的订单号(前缀+采购日期+6位流水号)微信小程序 生成外卖顺序号,生成订单号或流水号简单方法,实现订单号生成唯一的解决方案,如何按日期生成订单号,14位编号=8位日期(+6位流水号),自动生成订单号(固定+日期+数字),销售订单号自动生成。在微信小程序开发中,详解实现随机生成创建订单号

    🏆 前端领域优质创作者 1186

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

    在Java中,获取数组的长度和String的长度是两种不同的方法,这引起了本文作者的一番思考。本文从JVM的角度,探讨了Java数组在JVM中是什么对象,有哪些成员,以及声明方法。 作者:jarfield来源:JavaEye博客|2010-01-08 09:30 移动端 收藏 分享 本文来自jarfield的博客,原文标题为《为什么如此获取Java数组的长...

    a944145054的博客 93

    RU DOS版、EFI版32bit/64bit BIOS常用工具

    DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

    Outlook Express 6 (OE6) for Win7/Win8/Win10

    英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

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

    在Java中,获取数组的长度和String的长度是两种不同的方法,这引起了我的一番思考。本文从JVM的角度,探讨了Java数组在JVM中是什么对象,有哪些成员,以及声明方法。      记得有人问我:“为什么获取数组的长度用.length(成员变量的形式),而获取String的长度用.length()(成员方法的形式)?”      我当时一听,觉得问得很有道理。做同样一件事情,为什么...

    icanfly20000的博客 164

    VC中sqlite的使用

    在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

    读《有关JVM处理Java数组方法的思考》记录

    本来想转载的,结果比较懒,就直接用链接了。有关JVM处理Java数组方法的思考==================================================了解:1、数组并不存在于任何一个Java包中。2、数组是由jvm创建的。 [I, [[I ,  [[[I  , 分别是一维,二维,三维数组。3、数组获取长度的方法, arr.length. ---为啥?  因为通过编译器通...

    阳光灿烂的中午 335

    VB摄像头监控

    该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

    2019.10.19学习心得

    java对于每一个方法,每一个变量都会有签名。 方法的重载:(overload) 1、方法名相同 2、参数列表不同。包括参数个数和参数类型。 3、方法的返回值不做要求。 4、必须在一个类上,或者在继承关系上。 递归:二叉树、排序、快速、归并、 抽象1、调用自己。 2、有一个趋近于终止的条件。 思考递归:横向思考代码:纵向执行。 大规模问题:小规模问题。栈溢出异常。 栈:先进后出。方法的调用是发生在...

    excellent_coder的博客 240

    关于JVM处理数组方式的思考

    本文来自jarfield的博客,原文标题为《为什么如此获取Java数组的长度》。记得vamcily 曾问我:“为什么获取数组的长度用.length(成员变量的形式),而获取String的长度用.length()(成员方法的形式)?”我当时一听,觉得问得很有道理。做同样一件事情,为什么采用两种风格迥异的风格呢?况且,Java中的数组其实是完备(full-fledged)的对象,直接暴露成员变

    268

    JVM学习第三篇思考:一个Java类在Jvm内存中是怎么存在的

    JVM学习第三篇思考:一个Java类在Jvm内存中是怎么存在的 又名:Java虚拟机的内存模型(JMM)是什么样的. 通过前面两篇文章的学习,我们知道了一个Java类的生命周期及类加载器。我们可以得到如下两幅图: 类生命周期: 父类委托机制: 思考: 我们编写的类中的变量、方法、对象这些都需要内存存放的。那么在运行时候这些数据在Java虚拟机内存中是怎么存放的呢? 本文目标: 希望通过本文学习,大家对Java虚拟机运行时数据区域有更深的了解 我们写的代码在JVM中是怎么存在的? 1:我们现在看看总体J

    m0_67621429的博客 422
    上一篇: Java 7中的TransferQueue
    下一篇: 漫谈Java理念的转变与JVM的五大优势
    kobejayandy
    博客等级 码龄18年 2853粉丝 43原创
    评论
    成就一亿技术人!
    拼手气红包6.0元
    还能输入1000个字符
     
     条评论被折叠 查看
    添加红包

    请填写红包祝福语或标题

    个

    红包个数最小为10个

    元

    红包金额最低5元

    当前余额3.43元 前往充值 >
    需支付:10.00元
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付元
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值