具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象(此处比较与值无关)。例如,
此处,我再介绍两个方法,如下:
方法 | 描述 |
---|---|
public String getName() | 以String的形式返回此Class对象所表示的实体(类、接口、数组类、基本类型或void)名称 |
public Class<? super T> getSuperclass() | 返回表示此Class所表示的实体(类、接口、基本类型或 void)的超类的Class |
若代表数组的Class实例对象调用上述两个方法,会有什么结果呢?示例如下,
从上述代码运行的结果可知,代表数组的Class实例对象的getSuperclass()方法返回的父类为Object类对应的Class。
这里还要注意,基本类型的一维数组可以被当作Object类型使用,但不能当作Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。
Arrays.asList()方法处理int[]和String[]时的差异
在学集合框架中的Arrays工具类时,详情可参考第三十六讲 集合框架工具类,有结论:如果数组中的元素都是对象,那么变成集合时,数组中的元素就直接转成集合中的元素。如果数组中的元素都是基本数据类型,那么会将该数组(例如[[I@15db9742]
)作为集合中的元素存在。
第一行代码会输出诸如[[I@15db9742]
这样的内容,这是因为asList()方法按照JDK1.5的语法,将int[]当作一个Object对待;第二行代码会输出[a, b, c]
,这是因为asList()方法按照JDK1.4的语法,将String[]当作一个Object[]数组来对待。
数组的反射应用
我们可以使用Array工具类完成对数组的反射操作。例如,利用反射打印一个数组。
package cn.liayun.reflect;
import java.lang.reflect.Array;
public class ReflectArrayDemo {
public static void main(String[] args) {
printObject(new int[]{1,2,3});
printObject(new String[]{"a","b","c"});
printObject("xyz");
}
private static void printObject(Object obj) {
Class clazz = obj.getClass();
if (clazz.isArray()) {//如果是数组
int len = Array.getLength(obj);//得到数组的长度
for (int i = 0; i < len; i++) {
//得到数组中的每一个元素
System.out.println(Array.get(obj, i));
}
} else {
System.out.println(obj);
}
}
}
或许我们会思考这样一个问题,怎么得到数组中的元素类型?想得到数组中的元素类型,是没有办法的,只能是得到某一个具体元素的类型,不能得到整个数组的元素类型。需要取出每个元素对象,然后再对各个对象进行判断,因为其中每个具体元素的类型都可以不同,例如
Object[] x = new Object[]{"abc", Integer.Max};
反射的应用——实现框架功能
什么是框架?
框架就好比开发商做的毛坯房,开放商将房子卖给用户,需要住户自己去装修,比如安装门窗。房子就是框架,用户将门窗插入框架中。框架与工具类的区别:框架调用用户提供的类,工具类则是被用户的类调用。
框架要解决的核心问题
我在写框架(房子)时,你这个用户可能还在上小学,还不会写程序呢?我写的框架程序怎样能调用到你以后写的类(门窗)呢?因为在写程序时无法知道要被调用的类名,所以,在程序中无法直接new某个类的实例对象了,而要用反射方式来做。
综合案例
这里,我会采用配置文件加反射的方式创建ArrayList和HashSet的实例对象,然后比较观察运行后的结果差异。首要前提就是加载配置文件,例如config.properties,加载资源文件的方式有好几种,下面我会一一讲解。
-
第一种方式:首当其冲的一种方式,在Java Web开发中,利用API中XX类的getRealPath方法得到整个项目在硬盘上的真实位置(绝对位置),然后在项目内部找到资源文件config.properties的位置。记住,一定要用完整的路径,但完整的路径不是硬编码,而是运算出来的。
例如,将资源文件放在工程的根目录下,一般我们是自己写代码这么干,但是在实际开发中不合适。
-
第二种方式:一个类加载器能加载.class文件,那它当然也能加载classpath路径下的其他文件,既然它有如此能力,它没有理由不顺带提供这样一个方法,但它也只能加载classpath路径下的那些文件。
注意:直接使用类加载器加载资源文件时,不能以/
打头。Hibernate/Struts2/Spring等框架用的配置文件都是放在classpath指定的目录下的,其内部就是使用类加载器加载配置文件。 -
第三种方式:Class类提供了一个便利方法getResourceAsStream,用加载当前类的那个类加载器去加载相同包目录下的文件,既可用相对路径又可用绝对路径。
- 相对路径
- 绝对路径
在上面的路径中,最前面加斜杠表示绝对路径,斜杠代表根目录,不加斜杠表示相对路径,即相对于ReflectTest.class所在目录的路径,不管是相对还是绝对,其内部还是通过类加载器进行加载。
- 相对路径
接着,使用反射的方式创建ArrayList或者HashSet的实例对象。
package cn.liayun.reflect;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Collection;
import java.util.Properties;
import cn.liayun.domain.ReflectPoint;
public class ReflectTest {
public static void main(String[] args) throws Exception {
/*
* 得到资源文件的方式,方式一。
* getRealPath()://得到金山词霸整个项目的在硬盘上对应的真实(绝对)位置。
* //金山词霸(整个项目)/资源文件内部地址
* 一定要记住用完整的路径,但完整的路径不是硬编码,而是运算出来的。
*/
//加载properties文件
// InputStream ips = new FileInputStream("config.properties");
/*
* 方式二。
* 用类加载器的方式加载配置文件。
* getResourceAsStream():在classpath指定的根目录下,逐一地去查找你要加载的文件。
* 但是只能读,不能存进去。
*/
// InputStream ips = ReflectTest.class.getClassLoader().getResourceAsStream("cn/liayun/reflect/config.properties");
//用类提供的简便方法加载的时候,只需要写上配置文件的名字(用的是相对路径),不需要写上它的完整目录名。
// InputStream ips = ReflectTest.class.getResourceAsStream("resource/config.properties");
//用类提供的简便方法加载的时候,用的是绝对路径,只要打上/了,而是相对于classpath的根
InputStream ips = ReflectTest.class.getResourceAsStream("/cn/liayun/reflect/resource/config.properties");
Properties props = new Properties();
props.load(ips);
/*
* 释放本对象(ips)关联的操作系统资源,
* 本对象(ips)以后由JVM作为垃圾回收。
*/
ips.close();
String className = props.getProperty("className");
Collection collections = (Collection) Class.forName(className).newInstance();
ReflectPoint pt1 = new ReflectPoint(3, 3);
ReflectPoint pt2 = new ReflectPoint(5, 5);
ReflectPoint pt3 = new ReflectPoint(3, 3);
collections.add(pt1);
collections.add(pt2);
collections.add(pt3);
collections.add(pt1);
System.out.println(collections.size());
}
}