a我考网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 171|回复: 1

[专业语言] JAVA认证:Java数组方法的思考

[复制链接]
发表于 2012-8-4 12:44:44 | 显示全部楼层 |阅读模式
 “为什么获取数组的长度用.length(成员变量的形式),而获取String的长度用.length()(成员体例的形式)?”   我那时一听,感受问得很有事理。做同样一件工作,为什么采用两种气概迥异的气概呢?况且,Java中的数组其实是完整(full-fledged)的对象,直接吐露成员变量,可能不是一种很OO的气概。那么,设计Java的那帮天才为什么这么做呢?
  E- [( a; V/ o  带着这个疑问,我查阅了一些资料,主若是关于“JVM是若何措置数组”的。. `$ ]* _; s$ G  a+ A' V) b
  数组对象的类是什么?
2 T$ P% l/ m+ q! S2 p) R, o& G# _  既然数组都是对象,那么数组的类事实是什么呢?当然不是java.util.Arrays啦!我们以int一维数组为例,看看事实。
) ]3 J2 D  G. S  public class Main {   public static void main(String args[]){   int a[] = new int[10]; Class clazz = a.getClass();   System.out.println(clazz.getName());   }   }   在SUN JDK 1.6上运行上述代码,输出为:
% d5 l5 G  f4 R  [I
; O' |1 E4 A; P4 r) g/ }  看起来数组的类很奇异,非但不属于任何包,而且名称还不是正当的标识符(identifier)。具体的命名轨则[1]可以参见 java.lang.Class.getName()的javadoc。简单的说,数组的类名由若干个'['和数组元素类型的内部名称组成,'['的数目代表了数组的维度。* @. Y. F) [# C; {7 ]; z
  具有不异类型元素和不异维度的数组,属于统一个类。如不美观两个数组的元素类型不异,但维度分歧,那么它们也属于分歧的类。如不美观两个数组的元素类型和维度均不异,但长度分歧,那么它们仍是属于统一个类。" o0 W1 Q7 Z, j" y6 i5 H4 ?( S
  数组的类有哪些成员呢?- _( X: i7 ^% d% K  n# f, p0 z7 p" s
  既然我们知道了数组的类名是什么,那么就去看看数组的类事实是什么样的吧?有哪些成员变量?有哪些成员体例?length这个成员变量在哪?是不是没有length()这个成员体例?
: `- ^- F3 Q9 B5 i! g2 H  找来找去,在JDK的代码中没有找打'[I'这个类。想想也对,'[I'都不是一个正当的标识符,必定不会呈现public class [I {...}这样的Java代码。我们暂且不管[I类是谁声明的,怎么声明的,先用反射机制一探事实吧。5 U$ d% _+ M* e$ X7 O
  public class Main {   public static void main(String[] args) {   int a[] = new int[10]; Class clazz = a.getClass();   System.out.println(clazz.getDeclaredFields().length);   System.out.println(clazz.getDeclaredMethods().length);   System.out.println(clazz.getDeclaredConstructors().length);   System.out.println(clazz.getDeclaredAnnotations().length);   System.out.println(clazz.getDeclaredClasses().length);   System.out.println(clazz.getSuperclass());   }   }   在SUN JDK 1.6上运行上述代码,输出为:
) n/ C* q$ `, n; R7 W7 u3 T6 J  0 0 0 0 0 class java.lang.Object   可见,[I这个类是java.lang.Object的直接子类,自身没有声明任何成员变量、成员体例、机关函数和Annotation,可以说,[I就是个空类。我们立马可以想到一个问题:怎么连length这个成员变量都没有呢?如不美观真的没有,编译器怎么不报语法错呢?想必编译器对 Array.length进行了非凡措置哇!: U  t" I( k- S. @" c' c
  数组的类在哪老小明的?) X: F2 Y" y- n' u& \2 u. S* R
  先不管为什么没有length成员变量,我们先搞清嚣张[I这个类是哪老小明的吧。既然[I都不是正当的标识符,那么这个类必定在Java代码中显式声明的。想来想去,只能是JVM自己在运行时生成的了。JVM生成类仍是一件很轻易的工作,甚至无需生成字节码,直接在体例区中建树类型数据,就差不多落成了。4 c& d* m( y  z1 D
  还没有实力去看JVM的源代码,于是翻了翻The JavaTM Virtual Machine Specification  Second Edition,不美观真获得了验证,相关内容参考5.3.3 Creating Array Classes。
: d  ?1 T; P: K- D$ U  A1 }8 U5 q  规范的描述很严谨,还掺杂了界说类加载器和初始化类加载器的内容。先不管这些,简单归纳综合一下:
6 H$ c8 L9 u; J* c- R6 p) B$ l  Z  类加载器先看看数组类是否已经被建树了。如不美观没有,那就声名需要建树数组类;如不美观有,那就无需建树了。
2 Z+ W+ U1 D1 m5 n  如不美观数组元素是引用类型,那么类加载器首先去加载数组元素的类。7 c: \/ E& l" g4 j+ O
& [4 }" S: R9 ~4 ^5 f
  JVM按照元素类型和维度,建树响应的数组类。
回复

使用道具 举报

 楼主| 发表于 2012-8-4 12:44:45 | 显示全部楼层

JAVA认证:Java数组方法的思考

</p>  呵呵,不美观真是JVM这家伙自个偷偷建树了[I类。JVM不把数组类放到任何包中,也不给他们起个正当的标识符名称,估量是为了避免和JDK、第三方及用户自界说的类发生冲突吧。0 P) A3 T% T' e0 O
  再想想,JVM也必需动态生成数组类,因为Java数组类的数目与元素类型、维度(最多255)有关,相当相当多了,是没法预先声明好的。
" V# \9 J. \' d8 D, v1 ~  居然没有length这个成员变量!0 c8 Z$ t' X- r# {2 e8 R
  我们已经发现,偷懒的JVM没有为数组类生成length这个成员变量,那么Array.length这样的语法若何经由过程编译,若何执行的呢?
$ y( Q% S3 {% ~* A  |  让我们看看字节码吧!编写一段最简单的代码,使用jclasslib查看字节码。/ R+ i3 h, |/ z
  public class Main {   public static void main(String[] args)   { int a[] = new int[2]; int i = a.length;   }   }   使用SUN JDK 1.6编译上述代码,并使用jclasslib打开Main.class文件,获得main体例的字节码:
; M3 S- w. S0 Q# Z  0 iconst_2                   //将int型常量2压入操作数栈  1 newarray 10 (int)    //将2弹出操作数栈,作为长度,建树一个元素类型为int, 维度为1的数组,并将数组的引悠揭捉入操作数栈  3 astore_1                 //将数组的引用年夜操作数栈中弹出,保留在索引为1的局部变量(即a)中  4 aload_1                  //将索引为1的局部变量(即a)压入操作数栈  5 arraylength            //年夜操作数栈弹出数组引用(即a),并获取其长度(JVM负责实现若何获取),并将长度压入操作数栈  6 istore_2                 //将数组长度年夜操作数栈弹出,保留在索引为2的局部变量(即i)中  7 return                    //main体例返回   可见,在这段字节滤鱿脯根柢就没有看见length这个成员变量,获取数组长度是由一条特定的指令arraylength实现(怎么实现就不管了,JVM 总有法子)。编译器对Array.length这样的语法做了非凡措置,直接编译成了arraylength指令。此外,JVM建树数组类,应该就是由 newarray这条指令触发的了。
" a: W& o$ G4 y5 Y3 E" w0 r% s  很自然地想到,编译器也可以对Array.length()这样的语法做非凡措置,直接编译成arraylength指令。这样的话,我们就可以使用体例挪用的气概获取数组的长度了,这样看起来貌似也加倍OO一点。那为什么不使用Array.length()的语法呢?也许是开发Java的那帮天才对.length有所偏幸,或者抛硬币拍脑壳随便抉择的吧。 形式不主要,主要的是我们年夜白了背后的机理。+ T% r6 d! ]7 ^
  Array in Java
; E* s- S$ H% _7 n. h: h( P- J) [  最后,对Java中纯对象的数组揭晓点感应吧。) T( @# c9 O3 |$ D. X: @' _3 t
  对比C/C++中的数组,Java数组在平安性要好良多。C/C++常碰着的缓存区溢出或数组访谒越界的问题,在Java中不再存在。因为Java使用特定的指令访谒数组的元素,这些指令城市对数组的长度进行搜检。如不美观发现越界,就会抛出java.lang.ArrayIndexOutOfBoundsException。4 ~; Q' f- k8 z3 I9 M$ ^7 K
  Java数组元素的矫捷性斗劲年夜。一个数组的元素自己也可所以数组,只要所有元素的数组类型不异即可。我们知道数组的类型和长度无关,是以元素可所以长度分歧的数组。这样,Java的多维数组就不必然是规端方矩的矩阵了,可以千变万化。
, A6 B" M7 }, }1 Z
" m6 t9 ]' T, k) v  栈有一个很主要的非凡性,就是存在栈中的数据可以共享。假设我们同时界说:
4 g% `7 K2 Z; W- M$ n, e  int a = 3;
7 E+ I: D& p) O) {- |+ T# n  int b = 3;
- h8 ?2 L+ V5 @' v! g  编译器先措置int a = 3;首先它会在栈中建树一个变量为a的引用,然后查找栈中是否有3这个值,如不美观没找到,就将3存放进来,然后将a指向3。接着措置int b = 3;在建树完b的引用变量后,因为在栈中已经有3这个值,便将b直接指向3。这样,就呈现了a与b同时均指向3的情形。这时,如不美观再令a=4;那么编译器 会年夜头搜索栈中是否有4值,如不美观没有,则将4存放进来,并令a指向4;如不美观已经有了,则直接将a指向这个地址。是以a值的改变不会影响到b的值。要注重这 种数据的共享与两个对象的引用同时指向一个对象的这种共享是分歧的,因为这种情形a的改削并不会影响到b, 它是由编译器完成的,它有利于节约空间。而一个对象引用变量改削了这个对象的内部状况,会影响到另一个对象引用变量。) e0 C' e3 D& ?
ps:关于c++的内存分配
3 \; m# E! l# V. Q( t8 E/ U& B: ~  一个由C/C++编译的轨范占用的内存分为以下几个部门: k! G: m5 A1 U" \
  1、栈区(stack)— 由编译器自动分潘晔着 ,存放函数的参数值,局部变量的值等。其操作体例近似于数据结构中的栈。  C6 A/ f0 ~% q1 `, e$ i2 x
  2、堆区(heap) — 一般由轨范员分潘晔着, 若轨范员不释放,轨范竣事时可能由OS收受接管 。注重它与数据结构中的堆是两回事,分配体例却是近似于链表,呵呵。
  g) K! R0 a7 f  3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 – 轨范竣事后有系统释放
5 a$ [0 J0 q4 b4 H8 W7 Z6 c  4、文字常量区 —常量字符串就是放在这里的。 轨范竣事后由系统释放
- n" S4 m; g% e' a% y, m  5、轨范代码区—存放函数体的二进制代码。
. p" s$ G+ @7 v) J$ j: a8 m  二、例子轨范1 F% f1 F" n0 Y1 ^6 `
  这是一个前辈写的,很是具体
  Q; G! C: X; Q0 e7 t  //main.cpp, J% V1 X& e/ w2 G! M( a; s
  int a = 0; 全局初始化区# r7 }' Q5 }$ @. p0 z1 ^: d
  char *p1; 全局未初始化区
' v# p" r. d" L# d" C  main()
1 }7 q' B  j9 X5 K; X, s9 ^  {) @& x. _  o4 f0 d: C% ~1 n
  int b; 栈) U8 \# j- A8 z+ t* M
  char s[] = “abc”; 栈
  i/ c6 Q+ L4 I2 L# ?  char *p2; 栈/ z5 A" ?! p  I
  char *p3 = “123456″; 123456在常量区,p3在栈上。
! e4 J& z, m- P% c9 l0 P- @  static int c =0; 全局(静态)初始化区* v, u2 ~$ F: E/ @: {+ L9 X  f
  p1 = (char *)malloc(10);
+ Q$ y% c; I5 T3 O4 H  p2 = (char *)malloc(20);: x# V9 @( a2 ~3 a# z8 T
  分配得来得10和20字节的区域就在堆区。
' Y3 K7 W& |! U" p, ?  strcpy(p1, “123456″); 123456放在常量区,编译器可能会将它与p3所指向的”123456″优化成一个处所。
/ a5 B# c* Y1 k0 C% @% r! U8 L. V  }
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|Woexam.Com ( 湘ICP备18023104号 )

GMT+8, 2024-5-7 20:10 , Processed in 0.328217 second(s), 23 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表