Heim  >  Artikel  >  Java  >  Wenig bekannte Besonderheiten von Java-Arrays

Wenig bekannte Besonderheiten von Java-Arrays

Y2J
Y2JOriginal
2017-05-17 09:08:581522Durchsuche

Array ist ein Datentyp, der grundsätzlich in allen Sprachen existiert. Es stellt einen Satz von Daten desselben Typs dar, hat eine feste Länge und belegt einen kontinuierlichen Speicherplatz. In Sprachen wie C und C++ ist die Definition von Arrays prägnant und klar, in Java gibt es jedoch tatsächlich einige verwirrende Funktionen. In diesem Artikel wird versucht, diese Funktionen zu analysieren

Sind Arrays in Java-Objekten?

Java und C++ sind beide objektorientierte Sprachen. Wenn wir diese Sprachen verwenden, können wir direkt die Standard-Klassenbibliothek verwenden oder objektorientierte Funktionen wie Komposition und Vererbung verwenden, um unsere eigenen Klassen zu erstellen und unsere eigenen Klassen zu erstellen basierend darauf Objekt erstellen. Sollten wir uns also mit dieser Frage befassen: Sind Arrays in objektorientierten Sprachen Objekte?

Um festzustellen, ob ein Array ein Objekt ist, klären Sie zunächst, was ein Objekt ist, also die Definition eines Objekts. Auf einer höheren Ebene ist ein Objekt eine aus einer Klasse erstellte Instanz und repräsentiert ein bestimmtes Individuum in einer Klasse von Dingen. Objekte haben verschiedene Eigenschaften und einige spezifische Verhaltensweisen. Auf einer niedrigeren Ebene ist ein Objekt aus Sicht eines Computers ein Speicherblock im Speicher, bei dem es sich um die verschiedenen in der Klasse definierten Attribute handelt. Das Folgende ist die Darstellung eines Personenobjekts im Speicher:

Hinweis:

1) Ein kleines rotes Rechteck stellt eine Referenz (Adresse) oder einen Basisdatentyp dar. Ein großes rotes Rechteck stellt ein Objekt dar.

2) Name stellt nur eine Referenz im Objekt dar, also einen Adresswert, der auf ein real existierendes String-Objekt verweist. Hierbei wird strikt zwischen Referenzen und Objekten unterschieden.

Erfüllt ein Array in Java also die oben genannten Bedingungen? Auf einer höheren Ebene ist ein Array kein bestimmtes Individuum in einer bestimmten Art von Sache, sondern eine Sammlung mehrerer Individuen. Dann sollte es kein Objekt sein. Aus Computersicht ist ein Array auch ein Speicherblock, der auch einige Daten kapselt. In diesem Fall kann es auch als Objekt bezeichnet werden. Das Folgende ist die Darstellung eines Arrays im Speicher:

In diesem Fall kann das Array ein Objekt sein oder kein Objekt. Ob Arrays als Objekte behandelt werden sollen, hängt ganz vom Java-Designer ab. Ob das Array ein Objekt ist oder nicht, kann durch Code überprüft werden:


int[] a = new int[4]; 
//a.length; //对属性的引用不能当成语句 
int len = a.length; //数组中保存一个字段, 表示数组的长度 
//以下方法说明数组可以调用方法,java中的数组是对象.这些方法是Object中的方法,所以可以肯定,数组的最顶层父类也是Object 
a.clone(); 
a.toString();

Auf Array a können Sie auf seine Eigenschaften zugreifen und einige Methoden aufrufen. Grundsätzlich kann man daraus schließen, dass ein Array in Java auch ein Objekt ist. Es weist einige grundlegende Eigenschaften anderer Objekte in Java auf: Es kapselt einige Daten, kann auf Eigenschaften zugreifen und kann auch Methoden aufrufen. Arrays sind also Objekte.

Obwohl das Array in C++ Daten kapselt, ist der Array-Name nur ein Zeiger, der auf das erste Element im Array zeigt, ohne dass Attribute oder Methoden aufgerufen werden müssen. Wie im folgenden Code gezeigt:


int main(){ 
 int a[] = {1, 2, 3, 4}; 
 int* pa = a; 
 //无法访问属性,也不能调用方法。 
 return 0; 
}

Das Array in C++ ist also kein Objekt, es ist nur eine Datensammlung und kann nicht als verwendet werden ein Objekt.

Array-Typen in Java

Java ist eine stark typisierte Sprache. Da es sich um ein Objekt handelt, muss es zu einem Typ gehören. Wenn Sie beispielsweise ein Objekt basierend auf der Person-Klasse erstellen, ist der Typ dieses Objekts Person. Was ist also der Array-Typ? Schauen Sie sich den folgenden Code an:


int[] a1 = {1, 2, 3, 4}; 
System.out.println(a1.getClass().getName()); 
//打印出的数组类的名字为[I 
String[] s = new String[2]; 
System.out.println(s.getClass().getName()); 
//打印出的数组类的名字为 [Ljava.lang.String; 
String[][] ss = new String[2][3]; 
System.out.println(ss.getClass().getName()); 
//打印出的数组类的名字为 [[Ljava.lang.String;

Drucken Sie den Typ von a1 aus [ I , der Typ von s ist [Ljava.lang.String , der Typ von ss Es ist [[Ljava.lang.String;

Arrays haben also auch Typen. Es ist nur so, dass dieser Typ seltsam erscheint. Man kann sagen, dass der Typ von a1 int[] ist, was verständlich ist. Aber wir haben diese Klasse weder selbst erstellt, noch haben wir sie in der Standardbibliothek von Java gefunden. Mit anderen Worten, egal ob es sich um unseren eigenen Code oder im JDK handelt, es gibt keine folgende Definition:


public class int[] { 
 // ... 
 // ... 
 // ... 
}

Dafür kann es nur eine Erklärung geben , und das ist Die virtuelle Maschine erstellt automatisch einen Array-Typ , der wie die 8 grundlegenden Datentypen als integrierter Typ von Java verwendet werden kann. Die Namenskonvention für diesen Typ lautet wie folgt:

* 每一维度用一个[表示;开头两个[,就代表是二维数组

* [后面是数组中元素的类型(包括基本数据类型和引用数据类型)

在java语言层面上,s是数组,也是一个对象,那么他的类型应该是String[],这样说是合理的。但是在JVM中,他的类型为[java.lang.String。顺便说一句普通的类在JVM里的类型为 包名+类名,也就是全限定名。同一个类型在java语言中和在虚拟机中的表示可能是不一样的。

Java中数组的继承关系

上面已经验证了,数组是对象,也就是说可以以操作对象的方式来操作数组。并且数组在虚拟机中有它特别的类型。既然是对象,遵循Java语言中的规则 -- Object是上帝, 也就是说所有类的顶层父类都是Object。数组的顶层父类也必须是Object,这就说明数组对象可以向上直接转型到Object,也可以向下强制类型转换,也可以使用instanceof关键字做类型判定。 这一切都和普通对象一样。如下代码所示:


//1 在test1()中已经测试得到以下结论: 数组也是对象, 数组的顶层父类是Object, 所以可以向上转型 
int[] a = new int[8]; 
Object obj = a ; //数组的父类也是Object,可以将a向上转型到Object 
//2 那么能向下转型吗? 
int[] b = (int[])obj; //可以进行向下转型 
//3 能使用instanceof关键字判定吗? 
if(obj instanceof int[]){ //可以用instanceof关键字进行类型判定 
 System.out.println("obj的真实类型是int[]"); 
}

Java中数组的另一种“继承”关系

如下代码是正确的,却很容易让我们疑惑:


String[] s = new String[5]; 
Object[] obja = s; //成立,说明可以用Object[]的引用来接收String[]的对象

Object[]类型的引用可以指向String[]类型的数组对象? 由上文的验证可以得知数组类型的顶层父类一定是Object,那么上面代码中s的直接父类是谁呢?难道说String[]继承自Object[],而Object[]又继承自Object? 让我们通过反射的方式来验证这个问题:


//5 那么String[] 的直接父类是Object[] 还是 Object? 
System.out.println(s.getClass().getSuperclass().getName()); 
//打印结果为java.lang.Object,说明String[] 的直接父类是 Object而不是Object[]

由代码可知,String[]的直接父类就是Object而不是Object[]。可是Object[]的引用明明可以指向String[]类型的对象。那么他们的继承关系有点像这样:

这样的话就违背了Java单继承的原则。String[]不可能即继承Object,又继承Object[]。上面的类图肯定是错误的。那么只能这样解释:数组类直接继承了Object,关于Object[]类型的引用能够指向String[]类型的对象,这种情况只能是Java语法之中的一个特例,并不是严格意义上的继承。也就是说,String[]不继承自Object[],但是我可以允许你向上转型到Object[],这种特性是赋予你的一项特权。

其实这种关系可以这样表述:如果有两个类A和B,如果B继承(extends)了A,那么A[]类型的引用就可以指向B[]类型的对象。如下代码所示:


public static class Father { 
} 
public static class Son extends Father { 
} 
//6 下面成立吗? Father是Son的直接父类 
Son[] sons = new Son[3]; 
Father[] fa = sons; //成立 
//7 那么Son[] 的直接父类是Father[] 还是 Object[] 或者是Object? 
System.out.println(sons.getClass().getSuperclass().getName()); 
//打印结果为java.lang.Object,说明Son[]的直接父类是Object

上面的结论可以扩展到二维数组和多维数组


Son[][] sonss = new Son[2][4]; 
Father[][] fathers = sonss;

上面的代码可以这样理解:

将Father[][]数组看成是一维数组, 这是个数组中的元素为Father[],将Son[][]数组看成是一维数组, 这是个数组中的元素为Son[],因为Father[]类型的引用可以指向Son[]类型的对象,所以,根据上面的结论,Father[][]的引用可以指向Son[][]类型的对象。

数组的这种用法不能作用于基本类型数据:


int[] aa = new int[4]; 
//Object[] objaa = aa; //错误的,不能通过编译

这是错误的, 因为int不是引用类型,Object不是int的父类,在这里自动装箱不起作用。但是这种方式是可以的:


 Object[] objss = {"aaa", 1, 2.5};

这种情况下自动装箱可以工作,也就是说,Object数组中可以存放任何值,包括基本数据类型。

Java为什么会为数组提供这样一种语法特性呢?也就是说这种语法有什么作用?编写过AndroidSqlite数据库操作程序的同学可能发现过这种现象,用一个Object[]引用接收所有的数组对象,在编译SQL语句时,为SQL语句中的占位符提供对应的值。


 db.execSQL("INSERT INTO person VALUES (NULL, ?, ?)", new Object[]{person.name, person.age});

所以这种特性主要是用于方法中参数的传递。如果不传递数组,而是依次传递各个值,会使方法参数列表变得冗长。如果使用具体的数组类型,如String[],那么就限定了类型,失去了灵活性。所以传递数组类型是一种比较好的方式。但是如果没有上面的数组特性(如果有两个类A和B,如果B继承(extends)了A,那么A[]类型的引用就可以指向B[]类型的对象),那么数组类型就只能通过Object类型接收,这样就无法在方法内部访问或遍历数组中的各个元素。如下代码:


private static void test3() { 
 String[] a = new String[3]; 
 doArray(a); 
} 
private static void doArray(Object[] objs){ 
} 
private static void doArray1(Object obj){ 
 //不能用Object接收数组,因为这样无法对数组的元素进行访问 
 // obj[1] //错误 
 //如果在方法内部对obj转型到数组,存在类型转换异常的风险 
 // Object[] objs = (Object[]) obj; 
} 
private static void doArray2(String[] strs){ 
 //如果适用特定类型的数组,就限制了类型,失去灵活性和通用性 
} 
private static void doArray3(String name, int age, String id, float account){ 
 //如果不适用数组而是依次传递参数,会使参数列表变得冗长,难以阅读 
}

到此为止,数组的特性就总结完了。上文中加粗的部分为重要结论。下面贴出整个源码:

源码


package com.pansoft.zhangjg.testarray; 
public class ArrayTest { 
 /** 
  * @param args 
  */ 
 public static void main(String[] args) { 
  test1(); 
  test2(); 
  test3(); 
 } 
 /** 
  * 数组具有这种特性: 
  * 如果有两个类A和B,如果B继承(extends)了A,那么A[]类型的引用就可以指向B[]类型的对象 
  * 测试数组的特殊特性对参数传递的便利性 
  */ 
 private static void test3() { 
  String[] a = new String[3]; 
  doArray(a); 
 } 
 private static void doArray(Object[] objs){ 
 } 
 private static void doArray1(Object obj){ 
  //不能用Object接收数组,因为这样无法对数组的元素进行访问 
  // obj[1] //错误 
  //如果在方法内部对obj转型到数组,存在类型转换异常的风险 
  // Object[] objs = (Object[]) obj; 
 } 
 private static void doArray2(String[] strs){ 
  //如果适用特定类型的数组,就限制了类型,失去灵活性和通用性 
 } 
 private static void doArray3(String name, int age, String id, float account){ 
  //如果不适用数组而是依次传递参数,会使参数列表变得冗长,难以阅读 
 } 
 /** 
  * 测试数组的集成关系, 并且他的继承关系是否和数组中元素的类型有关 
  */ 
 private static void test2() { 
  //1  在test1()中已经测试得到以下结论: 数组也是对象, 数组的顶层父类是Object, 所以可以向上转型 
  int[] a = new int[8]; 
  Object obj = a ; //数组的父类也是Object,可以将a向上转型到Object 
  //2  那么能向下转型吗? 
  int[] b = (int[])obj; //可以进行向下转型 
  //3  能使用instanceof关键字判定吗? 
  if(obj instanceof int[]){ //可以用instanceof关键字进行类型判定 
   System.out.println("obj的真实类型是int[]"); 
  } 
  //4  下面代码成立吗? 
  String[] s = new String[5]; 
  Object[] obja = s; //成立,说明可以用Object[]的引用来接收String[]的对象 
  //5  那么String[] 的直接父类是Object[] 还是 Object? 
  System.out.println(s.getClass().getSuperclass().getName()); 
  //打印结果为java.lang.Object,说明String[] 的直接父类是 Object而不是Object[] 
  //6 下面成立吗? Father是Son的直接父类 
  Son[] sons = new Son[3]; 
  Father[] fa = sons; //成立 
  //7  那么Son[] 的直接父类是Father[] 还是 Object[] 或者是Object? 
  System.out.println(sons.getClass().getSuperclass().getName()); 
  //打印结果为java.lang.Object,说明Son[]的直接父类是Object 
  /** 
   * 做一下总结, 如果A是B的父类, 那么A[] 类型的引用可以指向 B[]类型的变量 
   * 但是B[]的直接父类是Object, 所有数组的父类都是Object 
   */ 
  //8  上面的结论可以扩展到二维数组 
  Son[][] sonss = new Son[2][4]; 
  Father[][] fathers = sonss; 
  //将Father[][]数组看成是一维数组, 这是个数组中的元素为Father[] 
  //将Son[][]数组看成是一维数组, 这是个数组中的元素为Son[] 
  //因为Father[]类型的引用可以指向Son[]类型的对象 
  //所以,根据上面的结论,Father[][]的引用可以指向Son[][]类型的对象 
  /** 
   * 扩展结论: 
   * 因为Object是所有引用类型的父类 
   * 所以Object[]的引用可以指向任何引用数据类型的数组的对象. 如: 
   * Object[] objs = new String[1]; 
   * Object[] objs = new Son[1]; 
   * 
   */ 
  //9  下面的代码成立吗? 
  int[] aa = new int[4]; 
  //Object[] objaa = aa; //错误的,不能通过编译 
  //这是错误的, 因为Object不是int的父类,在这里自动装箱不起作用 
  //10 这样可以吗? 
  Object[] objss = {"aaa", 1, 2.5};//成立 
 } 
 /** 
  * 测试在java语言中,数组是不是对象 
  * 如果是对象, 那么他的类型是什么? 
  */ 
 private static void test1() { 
  int[] a = new int[4]; 
  //a.length; //对属性的引用不能当成语句 
  int len = a.length; //数组中保存一个字段, 表示数组的长度 
  //以下方法说明数组可以调用方法,java中的数组是对象.这些方法是Object中的方法,所以可以肯定,数组的最顶层父类也是Object 
  a.clone(); 
  a.toString(); 
  /** 
   * java是强类型的语言,一个对象总会有一个特定的类型,例如 Person p = new Person(); 
   * 对象p(确切的说是引用)的类型是Person类, 这个Person类是我们自己编写的 
   * 那么数组的类型是什么呢? 下面使用反射的方式进行验证 
   */ 
  int[] a1 = {1, 2, 3, 4}; 
  System.out.println(a1.getClass().getName()); 
  //打印出的数组类的名字为[I 
  String[] s = new String[2]; 
  System.out.println(s.getClass().getName()); 
  //打印出的数组类的名字为 [Ljava.lang.String; 
  String[][] ss = new String[2][3]; 
  System.out.println(ss.getClass().getName()); 
  //打印出的数组类的名字为 [[Ljava.lang.String; 
  /** 
   * 所以,数组也是有类型的,只不过这个类型不是有程序员自己定义的类, 也不是jdk里面 
   * 的类, 而是虚拟机在运行时专门创建的类 
   * 类型的命名规则是: 
   *  每一维度用一个[表示; 
   *  [后面是数组中元素的类型(包括基本数据类型和引用数据类型) 
   * 
   * 在java语言层面上,s是数组,也是一个对象,那么他的类型应该是String[], 
   * 但是在JVM中,他的类型为[java.lang.String 
   * 
   * 顺便说一句普通的类在JVM里的类型为 包名+类名, 也就是全限定名 
   */ 
 } 
 public static class Father { 
 } 
 public static class Son extends Father { 
 } 
}

【相关推荐】

1. 特别推荐“php程序员工具箱”V0.1版本下载

2. Java免费视频教程

3. 全面解析Java注解

Das obige ist der detaillierte Inhalt vonWenig bekannte Besonderheiten von Java-Arrays. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn