Java中数组的特性
转载:http://blog.csdn.net/zhangjg_blog/article/details/16116613
数组是基本上所有语言都会有的一种数据类型,它表示一组相同类型的数据的集合,具有固定的长度,并且在内存中占据连续的空间。在C,C++等语言中,数组的定义简洁清晰,而在Java中确有一些会让人迷惑的特性。本文就尝试分析这些特性。
Java中的数组是对象吗?
Java和C++都是面向对象的语言。在使用这些语言的时候,我们可以直接使用标准的类库,也可以使用组合和继承等面向对象的特性构建自己的类,并且根据自己构建的类创建对象。那么,我们是不是应该考虑这样一个问题:在面向对象的语言中,数组是对象吗?
要判断数组是不是对象,那么首先明确什么是对象,也就是对象的定义。在较高的层面上,对象是根据某个类创建出来的一个实例,表示某类事物中一个具体的个体。对象具有各种属性,并且具有一些特定的行为。而在较低的层面上,站在计算机的角度,对象就是内存中的一个内存块,在这个内存块封装了一些数据,也就是类中定义的各个属性,所以,对象是用来封装数据的。以下为一个Person对象在内存中的表示:
注意:
1)小的红色矩形表示一个引用(地址)或一个基本类型的数据,大的红色矩形表示一个对象,多个小的红色矩形组合在一块,可组成一个对象。
2)name在对象中只表示一个引用, 也就是一个地址值,它指向一个真实存在的字符串对象。在这里严格区分了引用和对象。
那么在Java中,数组满足以上的条件吗?在较高的层面上,数组不是某类事物中的一个具体的个体,而是多个个体的集合。那么它应该不是对象。而在计算机的角度,数组也是一个内存块,也封装了一些数据,这样的话也可以称之为对象。以下是一个数组在内存中的表示:
这样的话, 数组既可以是对象, 也可以不是对象。至于到底是不是把数组当做对象,全凭Java的设计者决定。数组到底是不是对象, 通过代码验证:
- int[] a = new int[4];
- //a.length; //对属性的引用不能当成语句
- int len = a.length; //数组中保存一个字段, 表示数组的长度
- //以下方法说明数组可以调用方法,java中的数组是对象.这些方法是Object中的方法,所以可以肯定,数组的最顶层父类也是Object
- a.clone();
- a.toString();
在数组a上, 可以访问他的属性,也可以调用一些方法。这基本上可以认定,java中的数组也是对象,它具有java中其他对象的一些基本特点:封装了一些数据,可以访问属性,也可以调用方法。所以,数组是对象。
而在C++中,数组虽然封装了数据,但数组名只是一个指针,指向数组中的首个元素,既没有属性,也没有方法可以调用。如下代码所示:
- int main(){
- int a[] = {1, 2, 3, 4};
- int* pa = a;
- //无法访问属性,也不能调用方法。
- return 0;
- }
所以C++中的数组不是对象,只是一个数据的集合,而不能当做对象来使用。
Java中数组的类型
- 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;
打印出a1的类型为[ I ,s 的类型是[Ljava.lang.String; , ss的类型是[[Ljava.lang.String;
- public class int[] {
- // ...
- // ...
- // ...
- }
这只能有一个解释,那就是虚拟机自动创建了数组类型,可以把数组类型和8种基本数据类型一样, 当做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[]类型的对象。那么他们的继承关系有点像这样:
- 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;
上面的代码可以这样理解:
- int[] aa = new int[4];
- //Object[] objaa = aa; //错误的,不能通过编译
这是错误的, 因为int不是引用类型,Object不是int的父类,在这里自动装箱不起作用。但是这种方式是可以的:
- Object[] objss = {"aaa", 1, 2.5};
这种情况下自动装箱可以工作,也就是说,Object数组中可以存放任何值,包括基本数据类型。
Java为什么会为数组提供这样一种语法特性呢?也就是说这种语法有什么作用?编写过Android中Sqlite数据库操作程序的同学可能发现过这种现象,用一个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){
- //如果不适用数组而是依次传递参数,会使参数列表变得冗长,难以阅读
- }
到此为止,数组的特性就总结完了
Java中数组的特性的更多相关文章
- 将java中数组转换为ArrayList的方法实例(包括ArrayList转数组)
方法一:使用Arrays.asList()方法 1 2 String[] asset = {"equity", "stocks", "gold&q ...
- java中数组以及集合
java中数组: 数组在Java里是一种特殊类型,有别于普通的“类的实例”的对象.但实际数组也是一种对象类型,int[]a = new int[5] a是在java栈中分配的引用变量,类型是int[ ...
- java中数组的相关知识
1. 2.数组的命名方法 1)int[]ages=new int[5]; 2) int[]ages; ages=new int[5]; 3)int[]ags={1,2,3,4,5}; 4)int[ ...
- 在java 中,数组与 List<T> 类型的相互转换
在java中,数组与List<T> 之前进行互相转换,转换方法可总结为以下几种: 一. 将 数组转换成List<T> 1. 使用 Collections 的addAll 方法 ...
- Java中数组的初始化方式
Java中数组的初始化方式 初始化方式有两种: 1.静态初始化:初始化时由程序猿显式指定每一个数组元素的初始值,由系统指定数组长度 2.动态初始化:初始化时由程序猿仅仅指定数组长度,由系统为数组 ...
- Java中的继承性特性
继承性是java中的第二特性之一.而继承性最为关键的地方为:代码重用性的问题,利用继承性可以从已有的类中继续派生出新的子类,也可以利用子类扩展出更多的操作功能. 继承性的实现代码为:class 子类 ...
- java中数组复制的两种方式
在java中数组复制有两种方式: 一:System.arraycopy(原数组,开始copy的下标,存放copy内容的数组,开始存放的下标,需要copy的长度); 这个方法需要先创建一个空的存放cop ...
- Java中数组的插入,删除,扩张
Java中数组是不可变的,但是可以通过本地的arraycop来进行数组的插入,删除,扩张.实际上数组是没变的,只是把原来的数组拷贝到了另一个数组,看起来像是改变了. 语法: System.arrayc ...
- java中数组、集合、字符串之间的转换,以及用加强for循环遍历
java中数组.集合.字符串之间的转换,以及用加强for循环遍历: @Test public void testDemo5() { ArrayList<String> list = new ...
随机推荐
- String类型方法
String类型 //1.返回长度 length var a="lynn_hello"; console.log(a.length); //2.相加 concat() 返回一个新的 ...
- 批量文本读取URL获取正常访问且保留对应IP
#coding=utf-8 import sys import requests for i in range(3000,4999,1): url = 'http://192.168.88.139:8 ...
- 判断java中两个对象是否相等
java中的基本数据类型判断是否相等,直接使用"=="就行了,相等返回true,否则,返回false. 但是java中的引用类型的对象比较变态,假设有两个引用对象obj1,obj2 ...
- C++多线程调试和测试的注意事项
在一个程序中,这些独立运行的程序片断叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理”.利用线程,用户可按下一个按钮,然后程序会立即作出响应,而不是让用户等待程序完成了当前任务以后才开 ...
- yii遍历行下的每列数据(小1月考)
效果图: 控制器(1种): //显示列表 public function actionList() { //实例化对象 $model= new Qiu(); ...
- linux时间的查看与修改
1.查看时间和日期 date 2.设置时间和日期 将系统日期设定成1996年6月10日的命令 date -s 06/22/96 将系统时间设定成下午1点52分0秒的命令 date -s 13:52:0 ...
- java通过LinkedList实现堆栈和队列数据结构
package shb.java.demo3; import java.util.LinkedList; public class TestLinkedList { /** * @author sha ...
- 同时启动多个Tomcat
一,修改配置文件server.xml的端口 C:\apache-tomcat-5.5.23-1\conf\server.xml用记事本什么的打开修改3个地方 第一: <Server port ...
- Delphi 取得桌面文件夹的路径和取得我的文档的路径
Uses Windows,Registry; function GetShellFolders(strDir: string): string; const regPath = '\Software\ ...
- 为 Macbook 增加锁屏热键技巧
第一步,找到“系统偏好设置”下的“安全性与隐私”,在“通用”页里勾上“进入睡眠或开始屏幕保护程序后立即要求输入密码”. 第二步,要用快捷键启动屏幕保护程序,相对复杂一点.在“应用程序”里找到“Auto ...