Java数组分析

这篇文章主要是分享给同时学习JAVA和C语言的朋友。经常会在两种语言切换的时候对数组进行使用,产生的一些问题。


先简单的介绍下JAVA中的数组:

一维数组的声明方式:
type var[]; 或type[] var;

声明数组时不能指定其长度(数组中元素的个数),

Java中使用关键字new创建数组对象,格式为:
数组名 = new 数组元素的类型 [数组元素的个数]

实例:
TestNew.java:

程序代码:

  1. public class TestNew  
  2. {  
  3.      public static void main(String args[]) {  
  4.          int[] s ;  
  5.          int i ;  
  6.          s = new int[5] ;  
  7.          for(i = 0 ; i < 5 ; i++) {  
  8.              s[i] = i ;  
  9.          }  
  10.          for(i = 4 ; i >= 0 ; i--) {  
  11.              System.out.println("" + s[i]) ;  
  12.          }  
  13.      }   


初始化:

1.动态初始化:数组定义与为数组分配空间和赋值的操作分开进行;
2.静态初始化:在定义数字的同时就为数组元素分配空间并赋值;
3.默认初始化:数组是引用类型,它的元素相当于类的成员变量,因此数组分配空间后,每个元素也被按照成员变量的规则被隐士初始化。
实例:


TestD.java(动态):

程序代码:

  1. public class TestD  
  2. {  
  3.      public static void main(String args[]) {  
  4.          int a[] ;  
  5.          a = new int[3] ;  
  6.          a[0] = 0 ;  
  7.          a[1] = 1 ;  
  8.          a[2] = 2 ;  
  9.          Date days[] ;  
  10.          days = new Date[3] ;  
  11.          days[0] = new Date(2008,4,5) ;  
  12.          days[1] = new Date(2008,2,31) ;  
  13.          days[2] = new Date(2008,4,4) ;  
  14.      }  
  15. }  
  16.  
  17. class Date  
  18. {  
  19.      int year,month,day ;  
  20.      Date(int year ,int month ,int day) {  
  21.          this.year = year ;  
  22.          this.month = month ;  
  23.          this.day = day ;  
  24.      }  
  25. }  
  26.  


TestS.java(静态):

程序代码:

  1. public class TestS     
  2. {     
  3.      public static void main(String args[]) {     
  4.          int a[] = {0,1,2} ;     
  5.          Time times [] = {new Time(19,42,42),new Time(1,23,54),new Time(5,3,2)} ;     
  6.      }     
  7. }     
  8.  
  9. class Time     
  10. {     
  11.      int hour,min,sec ;     
  12.      Time(int hour ,int min ,int sec) {     
  13.          this.hour = hour ;     
  14.          this.min = min ;     
  15.          this.sec = sec ;     
  16.      }     
  17. }    


TestDefault.java(默认):

程序代码:

  1. public class TestDefault     
  2. {     
  3.      public static void main(String args[]) {     
  4.          int a [] = new int [5] ;     
  5.          System.out.println("" + a[3]) ;     
  6.      }     
  7. }   

刚刚开始接触java数组的人都会听到一句类似的话:java是纯面向对象的语言,他的数组也是一个对象。于是乎,笔者就按照一个对象的方式来使用数组,心安理得。直到我接触到C的数组后,才发现将数组作为一个类来使用在实现上是多么的“不自然”。

  
  首先我们看一下表面现象,数组创建的时候采用的是如下语句:

  MyClass[] arr = new MyClass[9];

  而普通类采用的是如下语句:

  MyClass obj = new MyClass();

  就是说,创建数组的时候不使用小括号传参。使得数组和普通类看起来就有很多不同,因为小括号里的参数是传递给构造方法的,进而让人感觉数组类是没有构造方法的。

  
再往深了想,还有很多让人感觉不自然的东西。可以肯定的是,java确实将数组作为了一个类来处理。还是用上面的例子说明:

  可以通过以下方法得到MyClass[]的Class实例:arr.getClass()或MyClass[].class。这样,我就可以向数组类里面“窥探”了。

  
  Class clazz = MyClass[].class;
  System.out.println(clazz.getConstructors().length);

  
  打印出来的结果是0;证明数组类确实没有构造方法。

  
  如果强行执行clazz.newInstance();就会得到下面的错误。

  java.lang.InstantiationException: [Larraytest.MyClass;

  证明数组类不能够通过普通的反射方式来创建一个实例。

  
  再看看数组类的“庐山真面目”:

  System.out.println(clazz);

  输出是:

  [Larraytest.MyClass

  对Java Class文件结构稍有了结就知道,这个字符串的意思就是一个元素类型为arraytest.MyClass的一维数组。也就是说,数组类型不是和普通类一样,以一个全限定路径名+类名来作为自己的唯一标示的,而是以[+一个或者多个L+数组元素类全限定路径+类来最为唯一标示的。这个()也是数组和普通类的区别。而这个区别似乎在某种程度上说明数组和普通java类在实现上有很大区别。因为java虚拟机(java指令集)在处理数组类和普通类的时候,肯定会做出区分。笔者猜想,可能会有专门的java虚拟机指令来处理数组。

既然我们可以得到数组的Class类实例,就说明肯定需要调用ClassLoader的 defineClass(不一定非要是loadClass方法)方法,来构造一个Class实例。java虚拟机规范规定,任何一个可以被加载的类,如果其类文件存储在文件系统上,那么一个*.class文件只能存储一个类信息,也就是说,数组类的信息不可能以类文件的形式存储在本地磁盘上(否则任意一个类都要配有255个数组类了.....),既然这样,那就说明java虚拟机肯定内置了一块用来声明数组类的数据(不管是几级数组)。这是符合java虚拟机规范的,规范规定class类数据可以来自任意介质,包括本地磁盘、网络、数据库、内存等等。

  分析到这里,我基本上可以肯定:java对数组对象化的操作的支持是指令级的,也就是说java虚拟机有专门针对数组的指令。数组的Class类实例是java虚拟机动态创建动态加载的,其结构与普通java类的Class实例有一些不同。

  JDK API中有一个java.lang.reflect.Array类,这个类提供了很多方法(绝大多数是native方法,这在另一个方面证明了java对数组的支持是专用指令支持的,否则用本地方法干嘛^_^),用来弥补我们对数组操作的局限性。

  下面这句话用来创建一个一维的、长度为10的、类型为arraytest.MyClass的数组:

  arraytest.MyClass[] arr = (arraytest.MyClass[]) Array.newInstance(arraytest.MyClass, 10);

  下面这句话用来创建一个二维的、3乘5的、类型为arraytest.MyClass的数组:

  int[] arrModel = new int[]{3,5};
Object arrObj = Array.newInstance(Sub.class, arrModel);

  当然你可以用一个数组的引用指向上面的二维数组,这里我们用一个Object的引用指向他。
使用的时候,我们也是可以利用Array类提供的方法来实现:

  System.out.println(Array.getLength(arrObj);//第一维长度为3
  System.out.println(Array.getLength(Array.get(arrObj, 2)));//第二维长度为5,这里如果写3,就会得到你意想之中的java.lang.ArrayIndexOutOfBoundsException

  打印结果是如我们所想的:

  3
  5

  对于数组的Class类实例,还有一些奇怪的现象:

  在运行代码 java.lang.reflect.Field fieldarr = clazz.getField("length");的时候,会抛出异常:java.lang.NoSuchFieldException: length,这似乎在说数组类没有length这个域,而这个域其实是我们用的最多的一个(也就是说这个域是肯定存在的)。笔者认为关于数组的Class类实例、数组的实现等,还有很多“猫腻”在里面。
 
  顺便说一句,java数组最多只能是255维的。这个让人看到了C的影子,嘿嘿。“Java把数组当作一个java类来处理”说起来容易,用起来自然,但是细细想来,还是有很多不简单的地方。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值