Java——数组(概念理解+应用举例)

简介: Java——数组(概念理解+应用举例)

1.关于Java中的数组:  


Java语言中,数组是一个非常重要的概念,我们经常使用的数组,在这里和C语言基本上是一样的,无非是一维数组二维数组,那么对数组的初始化定义有两种方法,一个是静态初始化,另一个自然就是动态初始化

数组是一种数据结构,系统为数组分配的存储空间是连续的、指定长度的、且大小固定不变的,用来存储一组大小固定并且类型相同的数据。这些数据可以通过索引进行访问,数组的下标索引是从 0 开始,其取值范围必需是:0~数组长度-1 数组是JVM的核心类型,是 Java 语言的基元类型。数组类和数组实例的数据结构具有特殊性,由JVM 决定。运行时,数组越界的检查通过数组实例的 length 属性来完成,数组元素类型匹配的检查通过数组类中的元素类型属性来完成。

Java数组具有以下几个特点:

多维数组均是一维指针数组(数组实例)

Java 中的数组是静态数组,数组被创建后,其大小是不能改变的。

JVM扫描时产生数组类型(数组类型动态产生)。

数组越界,Java 中是运行时错误,产生异常(数组的 length 字段)。在 C 语言中是逻辑错误。而JVM在运行时判断,如果未越界则继续运行,如果越界则抛出异常。例如:数组越界、显式强类型转换等。

当然了,我们在使用Java数组的时候,难免的会出现一些错误问题,在处理这些问题的时候,一般是下面的步骤👇👇👇

存在的问题检测问题解决问题

1. 存在的问题:数组越界问题,数组元素类型匹配问题。(这两个相信大家在C语言的学习中,一定不陌生)

2. 检测问题:数组越界问题由JVM在运行时来检测,如果存在问题,则抛出异常。

                     数组元素类型匹配,值类型匹配错误是逻辑错误。

                     数组元素类型匹配,引用类型匹配错误是异常。

3. 解决问题:捕获异常,处理异常。

下面,我们来看一个例子:👇👇👇

public class Test {
  public static void main(String[] args) {
    int[] a= {4,5,6};//静态初始化
    a[0]=1234567890;//修改a[0]的内容
    System.out.println("int a[0]="+a[0]);//输出修改之后的a[0]
    try {
      int i=3;
                        //由于所创建的数组a中只有三个值,而数组下标从0开始,所以只有a[0],a[1]和a[2]
      a[i]=8;
                        //这里的a[3]显然会产生越界问题
    }
    catch (Exception e) {//在这里直接捕获,并处理异常
      System.out.println("数组越界异常!");
    }
  }
}

运行结果如下:

可以看到程序输出了上图的结果!!!

在这里,我们首先使用静态初始化来创建一个数组a,包含3个数,因为数组的下标是从0开始的,所以这三个数分别对应a[0]a[1]a[2],那么,我们修改a[0]是完全可以的。但是后面又企图对a[3]进行赋值,显然已经超出数组下标的最大范围,这里会产生越界问题,直接跳转到下面的catch语句来捕获并处理异常!!!


2.一维数组:


1. JVM 在运行一个方法之前,先扫描方法体,如果类对象不存在,则先创建该类对象。然后,单步解释执行 main()方法体内的代码。

2. 数组类对象非常特殊,是由 JVM 扫描时自动产生的。在扫描 main()方法体时, JVM 将首先在Java 方法区,建立int[ ]数组类对象、double[ ]数组类对象、char[ ]数组类对象、boolean[ ]数组类对象、String[ ]数组类对象。数组实例的数据结构具有特殊性,也由 JVM 决定。

3. 然后,单步解释执行 main()方法体内的代码。

例如:double[ ] b = new double[10]; 赋值语句

赋值号左边,首先在堆栈压栈一个变量b,该变量b的类型是一个double[ ]数组类型,是一个引用类型。

然后赋值号右边,生成double[ ]数组类型的一个数组实例。

最后赋值号,将生成的数组实例的引用赋予变量b,即变量b引用数组实例。

下面,我们来看一个例子!!!👇👇👇

import java.util.*;
public class Main
{
  public static void main(String[] args)
  {
    int[] a=new int[10];
    double[] b=new double[10];
    char[] c=new char[100];
    boolean[] d=new boolean[20];
    String[] s=new String[5];
    /*下面输出各数组的数组名,注意输出的内容 */ 
    System.out.println("输出数组对象*****************");
    System.out.println(a);// 输出整型数组对象的哈希玛(地址)   
    System.out.println(b);// 输出双精度浮点型数组对象的哈希玛(地址)   
    System.out.println(c);// 输出字符型数组中的内容   
    System.out.println(d);// 输出布尔型数组对象的哈希玛(地址)   
    System.out.println(s);// 输出字符串数组对象的哈希玛(地址)
    System.out.println("-------------------------"); 
    /*下面输出各数组中第一个元素的值,注意输出的内容 */ 
    System.out.println("输出数组对象的成员************");
    System.out.println(a[0]);   
    System.out.println(b[0]);   
    System.out.println(c[0]); 
    System.out.println(d[0]);
    System.out.println(s[0]);   
    System.out.println("-------------------------"); 
    /*下面输出各数组的长度 */ 
    System.out.println("输出数组对象的长度***********");
    System.out.println("a.length="+a.length);   
    System.out.println("b.length="+b.length);   
    System.out.println("c.length="+c.length);   
    System.out.println("d.length="+d.length);   
    System.out.println("s.length="+s.length);   
    System.out.println("-------------------------"); 
    //getClass()是Object的方法成员  
    //getSuperclass()、getName()是Class的方法成员
    System.out.println("输出数组类的超类和反射类***********");        
    System.out.println("int数组实例的类:"+a.getClass().getName());
    System.out.println("double数组实例的类:"+b.getClass().getName());
    System.out.println("char数组实例的类:"+c.getClass().getName());
    System.out.println("boolean数组实例的类:"+d.getClass().getName());
    System.out.println("String数组实例的类:"+s.getClass().getName());
    //输出类的超类Object
    System.out.println("int数组实例的类的父类:"+a.getClass().getSuperclass().getName());
    //输出类的反射类类Class
    System.out.println("int数组实例的类的反射类:"+a.getClass().getClass().getName());
    Class c1=a.getClass();
    Class c2=b.getClass();
    System.out.println("a、b相同的父类:"+(c1==c2));
    Class d1=a.getClass().getSuperclass();
    Class d2=b.getClass().getSuperclass();
    System.out.println("a、b相同的父类:"+(d1==d2));
  }
}

运行结果如下:

Java语言中,Object类是所有类的父类,在代码中,我们通过getClass()来获取当前类的对象,而getSuperclass()是获取父类的对象,后面的getName()则是获取对应的名称。

最后6行中,因为数组aint类型的,而数组bdouble类型的,所以它们通过getClass()获取当前类的对象并不是相同的,而通过getSuperclass()获取当前父类的对象是相同的,因为其父类都是Object

(这段代码的输出结果中,有哈希码、反射类这些知识,博主也还在学习的过程中,所以在这里先不谈这个)


3.初始化数组


public class Test {
  public static void main(String[] args) {
    int[] a= {5,7,20};//定义并初始化数组,使用静态初始化 
    int[] b=new int[4];//定义并初始化数组,使用动态初始化 
    for(int i=0;i<b.length;i++) {
      b[i]=i+1;
    } 
    //循环输出a数组的元素   
    System.out.println("数组a中的元素是:");
    for(int i=0;i<a.length;i++) {
      System.out.print(a[i]+" ");
    }
    System.out.println();
    //输出b数组的长度
    System.out.println("b数组的长度为:"+b.length);   
    System.out.println("数组b中的元素是:");
    //循环输出b数组的元素   
    for(int i=0;i<b.length;i++) {
      System.out.print(b[i]+" ");
    }
    System.out.println();
    //因为a是int[]类型,b也是int[]类型,所以可以将a的值赋给b。   
    //也就是让b引用指向a引用指向的数组
    b=a;
    //再次输出b数组的长度   
    System.out.println("b数组的长度为:"+b.length);
    //再次循环输出b数组的元素  
    for(int i=0;i<b.length;i++) {
      System.out.print(b[i]+" ");
    }
  }
}

运行结果如下:

代码中注释写的挺详细的,大家可以看一下,对比程序运行结果,理解一下Java初始化数组的这样一个过程!!!

下面,我们在初始化数组的过程中,如果想要遍历输出数组元素,有下面两种方法👇👇👇

for(int i=0;i<a.length;i++) {
    System.out.print(a[i]+" ");
}
for(int e:a) {
    System.out.print(e+" ");
}
public class Test {
  public static void main(String[] args) {
    int[] a=new int[5];//定义并初始化数组,使用动态初始化 
    for(int i=0;i<a.length;i++) {
      a[i]=i+1;
    }
    System.out.println("数组a中的元素是:");
    //使用foreach语句遍历输出a数组中的元素 
    for(int e:a) {
      System.out.print(e+" ");
    }
  }
}

运行结果如下:

4.二维数组


Java中的多维数组本质上是多维的指针数组,均由多个一维数组组成。 

Java的二维数组实际上是一维的指针数组。在Java中不存在矩阵数组。 

我们来看下面的例子!!!👇👇👇

public class Test {
  public static void main(String[] args) {
    int[][] a={ {1,2,3 },{4,5,6} };//二维数组静态初始化    
    System.out.println("数组a一维长度:"+a.length);   
    System.out.println("数组a二维长度:"+a[0].length);   
    System.out.println("数组a中的元素:");   
    //使用嵌套的for循环输出   
    for(int i=0;i<a.length;i++) {    
      for(int j=0;j<a[i].length;j++) {     
        System.out.print(a[i][j]+" ");    
      }    
      System.out.println();   
    }   
    System.out.println("-------------------------");    
    int[][] c=new int[3][4];//二维数组动态初始化,一维和二维都指定长度    
    //使用嵌套的for循环初始化二维数组  
    for(int i=0;i<c.length;i++) {    
      for(int j=0;j<c[i].length;j++) {     
        c[i][j]=i+j;    
      }   
    }   
    System.out.println("数组c中的元素:");
    //使用嵌套的for循环输出   
    for(int i=0;i<c.length;i++) {    
      for(int j=0;j<c[i].length;j++) {     
        System.out.print(c[i][j]+" ");
      }    
      System.out.println();   
    }   
    System.out.println("-------------------------"); 
    int[][] d=new int[2][];//声明二维数组时,只给出一维长度   
    //二维长度不等   
    d[0]=new int[3];   
    d[1]=new int[4];   
    //初始化   
    for(int i=0;i<d.length;i++) {    
      for(int j=0;j<d[i].length;j++) {     
        d[i][j]=i+j;    
      }   
    }   
    System.out.println("数组d中的元素:");   
    //使用嵌套的for循环输出   
    for(int i=0;i<d.length;i++) {    
      for(int j=0;j<d[i].length;j++) {     
        System.out.print(d[i][j]+" ");    
      }    
      System.out.println();   
    }   
    System.out.println("-------------------------"); 
    //数组类的超类和反射类   
    System.out.println("a数组实例的类:"+a.getClass().getName());   
    System.out.println("a[0]数组实例的类:"+a[0].getClass().getName());   
    System.out.println("a数组实例的类的父类:"+a.getClass().getSuperclass().getName());   
    System.out.println("a数组实例的类的反射类:"+a.getClass().getClass().getName());
    Class c1=a.getClass();   
    Class c2=d.getClass();   
    System.out.println("a、d相同的父类:"+(c1==c2));
  }
}

运行结果如下:

Java语言的二维数中,它的一维长度其实就是对应的二维数组的行数,而二维长度其实就是对应的二维数组的列数。而二维长度可以明确指定,也可以不指定,即二维长度不一定是相等的,也可能在第一行中是3列,在第二行中变成了4列。

在遍历输出二维数组的时候,要注意双重for循环👇👇👇

for(int i=0;i<a.length;i++) {//对应的是二维数组a的行数
    for(int j=0;j<a[i].length;j++) {//对应的是二维数组a的列数
  System.out.print(a[i][j]+" ");    
    }    
    System.out.println();   
}

在这里,我们仍然可以通过getClass().getSuperclass().getName()来获取其父类的名字,也就是Object类。

因为二维数组a和二维数组d都是int类型的,所以直接通过getClass()获取的当前类的对象就是相同的,即true

在看完了这篇博文之后,我们主要对Java数组有了一定的理解,包括:使用数组会产生的一些问题及解决方法,一维和二维数组的定义初始化、赋值以及遍历输出。

这就是博主总结的Java数组的相关内容,希望对你们有所帮助!!!欢迎大家留言评论!!!😀😀😀

相关文章
|
4月前
|
人工智能 算法 Java
Java与AI驱动区块链:构建智能合约与去中心化AI应用
区块链技术和人工智能的融合正在开创去中心化智能应用的新纪元。本文深入探讨如何使用Java构建AI驱动的区块链应用,涵盖智能合约开发、去中心化AI模型训练与推理、数据隐私保护以及通证经济激励等核心主题。我们将完整展示从区块链基础集成、智能合约编写、AI模型上链到去中心化应用(DApp)开发的全流程,为构建下一代可信、透明的智能去中心化系统提供完整技术方案。
386 3
|
4月前
|
Java 编译器 Go
【Java】(5)方法的概念、方法的调用、方法重载、构造方法的创建
Java方法是语句的集合,它们在一起执行一个功能。方法是解决一类问题的步骤的有序组合方法包含于类或对象中方法在程序中被创建,在其他地方被引用方法的优点使程序变得更简短而清晰。有利于程序维护。可以提高程序开发的效率。提高了代码的重用性。方法的名字的第一个单词应以小写字母作为开头,后面的单词则用大写字母开头写,不使用连接符。例如:addPerson。这种就属于驼峰写法下划线可能出现在 JUnit 测试方法名称中用以分隔名称的逻辑组件。
263 4
|
4月前
|
消息中间件 缓存 Java
Spring框架优化:提高Java应用的性能与适应性
以上方法均旨在综合考虑Java Spring 应该程序设计原则, 数据库交互, 编码实践和系统架构布局等多角度因素, 旨在达到高效稳定运转目标同时也易于未来扩展.
337 8
|
5月前
|
人工智能 Java API
Java与大模型集成实战:构建智能Java应用的新范式
随着大型语言模型(LLM)的API化,将其强大的自然语言处理能力集成到现有Java应用中已成为提升应用智能水平的关键路径。本文旨在为Java开发者提供一份实用的集成指南。我们将深入探讨如何使用Spring Boot 3框架,通过HTTP客户端与OpenAI GPT(或兼容API)进行高效、安全的交互。内容涵盖项目依赖配置、异步非阻塞的API调用、请求与响应的结构化处理、异常管理以及一些面向生产环境的最佳实践,并附带完整的代码示例,助您快速将AI能力融入Java生态。
911 12
|
5月前
|
Java
Java 数组学习笔记
本文整理Java数组常用操作:遍历、求和、查找、最值及二维数组行求和等典型练习,涵盖静态初始化、元素翻倍、去极值求平均等实例,帮助掌握数组基础与应用。
|
5月前
|
安全 Java API
Java SE 与 Java EE 区别解析及应用场景对比
在Java编程世界中,Java SE(Java Standard Edition)和Java EE(Java Enterprise Edition)是两个重要的平台版本,它们各自有着独特的定位和应用场景。理解它们之间的差异,对于开发者选择合适的技术栈进行项目开发至关重要。
986 1
|
6月前
|
设计模式 XML 安全
Java枚举(Enum)与设计模式应用
Java枚举不仅是类型安全的常量,还具备面向对象能力,可添加属性与方法,实现接口。通过枚举能优雅实现单例、策略、状态等设计模式,具备线程安全、序列化安全等特性,是编写高效、安全代码的利器。
|
6月前
|
存储 缓存 Java
Java数组全解析:一维、多维与内存模型
本文深入解析Java数组的内存布局与操作技巧,涵盖一维及多维数组的声明、初始化、内存模型,以及数组常见陷阱和性能优化。通过图文结合的方式帮助开发者彻底理解数组本质,并提供Arrays工具类的实用方法与面试高频问题解析,助你掌握数组核心知识,避免常见错误。
|
4月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
266 1