java 数组越界判断和获取数组长度的实现方式

 更新时间:2021年12月22日 10:07:33   投稿:jingxian  
这篇文章主要介绍了java 数组越界判断和获取数组长度的实现方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

1. 背景介绍

java中的数组比c语言中的数组,多了两个很重要的功能

  • 当索引越界时, 会自动抛出ArrayIndexOutOfBoundsException, 避免一错再错
  • 另一个很重要的方法是获取数组长度

这两个功能都不是通过java代码层面实现的, 而是在jvm中通过c++来实现的. 本文就针对这连个点来一探究竟

2. 原始java代码

public class TestArrayIndexOutOfBoundsException {
    public static void main(String[] args) {
        int[] is = new int[2];
        int x = is[5];
        System.out.println(x);
        int len = is.length;
        System.out.println(len);
    }
}

3. java代码对应的反编译字节码

 0 iconst_2
 1 newarray 10 (int) // 创建长度为2的int型数组
 3 astore_1 // 将数组is存储到local stack的slot1中
 4 aload_1 // 将数组is压入操作数栈
 5 iconst_5 // 将常量5压入操作数栈
 6 iaload // 这个指令就是通过数组索引获取元素, is[5]
 7 istore_2
 8 getstatic #2 <java/lang/System.out>
11 iload_2
12 invokevirtual #3 <java/io/PrintStream.println>
15 aload_1 // 将数组is压入操作数栈
16 arraylength // 获取is数组的长度
17 istore_3
18 getstatic #2 <java/lang/System.out>
21 iload_3
22 invokevirtual #3 <java/io/PrintStream.println>
25 return

4. jvm实现分析

4.1 获取数组长度arraylength指令核心代码分析

// hotspot\src\share\vm\interpreter\bytecodeInterpreter.cpp
void
BytecodeInterpreter::run(interpreterState istate) {
// 省略无关代码
 CASE(_arraylength):
 {
  // java中的对象实例, 对应的c++实例就是arrayOopDesc
     arrayOop ary = (arrayOop) STACK_OBJECT(-1);
     CHECK_NULL(ary);
     SET_STACK_INT(ary->length(), -1); // 就是通过ary->length()这个方法来获取数组长度
     UPDATE_PC_AND_CONTINUE(1);
 }
}
// 省略无关代码

4.2 获取数组元素iaload指令分析

// hotspot\src\share\vm\interpreter\bytecodeInterpreter.cpp
void
BytecodeInterpreter::run(interpreterState istate) {
// 省略无关代码
 #define ARRAY_INTRO(arrayOff)                                                  \
       arrayOop arrObj = (arrayOop)STACK_OBJECT(arrayOff);                      \ // 从局部变量表中获取数组对象is
       jint     index  = STACK_INT(arrayOff + 1);                               \ // 从局部变量表中获取索引5
       char message[jintAsStringSize];                                          \
       CHECK_NULL(arrObj);                                                      \
       if ((uint32_t)index >= (uint32_t)arrObj->length()) {                     \ // 判断索引是否大于或等于数组长度
           sprintf(message, "%d", index);                                       \
           VM_JAVA_ERROR(vmSymbols::java_lang_ArrayIndexOutOfBoundsException(), \ // 如果越界, 则抛出ArrayIndexOutOfBoundsException异常
                         message);                                              \
       }
// 省略无关代码
 #define ARRAY_LOADTO32(T, T2, format, stackRes, extra)                                \
       {                                                                               \
           ARRAY_INTRO(-2);                                                            \ // 获取数组所在的地址
           (void)extra;                                                                \
           SET_ ## stackRes(*(T2 *)(((address) arrObj->base(T)) + index * sizeof(T2)), \ // 根据数组所在地址,加上索引计算的偏移地址, 获得数组指定元素
                            -2);                                                       \
           UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);                                      \
       }
// 省略无关代码
 CASE(_iaload): // 此处对应的就是iaload指令的具体实现
          ARRAY_LOADTO32(T_INT, jint,   "%d",   STACK_INT, 0);
}
// 省略无关代码

通过上面代码的分析, 可以jvm是通过(uint32_t)index >= (uint32_t)arrObj->length()来判断数组越界

5. 小结一下

java中的数组和c语言数组差异很大, c语言中的数组更原始,直接和内存对应。而java中的数组类型其实是经过了arrayOopDesc的封装,因而能获得更丰富的信息,做更多语言层面的检查。

java 数组越界问题

Java中数组初始化和OC其实是一样的,分为动态初始化和静态初始化,

  • 动态初始化:指定长度,由系统给出初始化值
  • 静态初始化:给出初始化值,由系统给出长度

在我们使用数组时最容易出现的就是数组越界问题,好了,下面来演示一下

int [][] array = {{1,2,3},{1,4}};
        System.out.println(array[1][2]);

这是一个二维数组,很明显,数组越界了,控制台中会打印如下信息:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 2

at demo.Array.main(Array.java:31)

很准确的定位到31行

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • php上传文件分类实例代码

    php上传文件分类实例代码

    这篇文章主要介绍了php上传文件分类实例代码,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2017-02-02
  • idea中引入了gb2312编码的文件的解决方法

    idea中引入了gb2312编码的文件的解决方法

    这篇文章主要介绍了idea中引入了gb2312编码的文件的解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-08-08
  • 使用SpringCloudApiGateway之支持Cors跨域请求

    使用SpringCloudApiGateway之支持Cors跨域请求

    这篇文章主要介绍了使用SpringCloudApiGateway之支持Cors跨域请求的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • Java PriorityQueue数据结构接口原理及用法

    Java PriorityQueue数据结构接口原理及用法

    这篇文章主要介绍了Java PriorityQueue数据结构接口原理及用法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-10-10
  • JavaSE面试题之this与super关键字的区别详解

    JavaSE面试题之this与super关键字的区别详解

    this关键字用于引用当前对象的引用,super关键字用于引用父类对象的引用,下面这篇文章主要给大家介绍了关于JavaSE面试题之this与super关键字区别的相关资料,需要的朋友可以参考下
    2023-12-12
  • 解决RabbitMq消息队列Qos Prefetch消息堵塞问题

    解决RabbitMq消息队列Qos Prefetch消息堵塞问题

    这篇文章主要为大家介绍了关于如何解决解决RabbitMq Qos Prefetch消息堵塞的问题分析,有需要的朋友可以借鉴参考下,希望能够有所帮助
    2022-01-01
  • MAC上IntelliJ IDEA的svn无法保存密码解决方案

    MAC上IntelliJ IDEA的svn无法保存密码解决方案

    今天小编就为大家分享一篇关于MAC上IntelliJ IDEA的svn无法保存密码解决方案,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-10-10
  • java使用Jsoup组件生成word文档

    java使用Jsoup组件生成word文档

    java使用Jsoup组件生成word文档的方法
    2013-11-11
  • java读取文件显示进度条的实现方法

    java读取文件显示进度条的实现方法

    当读取一个大文件时,一时半会儿无法看到读取结果,就需要显示一个进度条,是程序员明白已经读了多少文件,可以估算读取还需要多少时间,下面的代码可以实现这个功能
    2014-01-01
  • 获取Java加载器和类完整结构的方法分享

    获取Java加载器和类完整结构的方法分享

    这篇文章主要为大家详细介绍了获取Java加载器和类完整结构的方法,文中的示例代码讲解详细,对我们学习Java有一定的帮助,需要的可以参考一下
    2022-12-12

最新评论