Java基础加强第二讲 反射(下)——数组的反射

具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象(此处比较与值无关)。例如,
Java基础加强第二讲 反射(下)——数组的反射
此处,我再介绍两个方法,如下:

方法 描述
public String getName() 以String的形式返回此Class对象所表示的实体(类、接口、数组类、基本类型或void)名称
public Class<? super T> getSuperclass() 返回表示此Class所表示的实体(类、接口、基本类型或 void)的超类的Class

若代表数组的Class实例对象调用上述两个方法,会有什么结果呢?示例如下,
Java基础加强第二讲 反射(下)——数组的反射
从上述代码运行的结果可知,代表数组的Class实例对象的getSuperclass()方法返回的父类为Object类对应的Class。
这里还要注意,基本类型的一维数组可以被当作Object类型使用,但不能当作Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。
Java基础加强第二讲 反射(下)——数组的反射

Arrays.asList()方法处理int[]和String[]时的差异

在学集合框架中的Arrays工具类时,详情可参考第三十六讲 集合框架工具类,有结论:如果数组中的元素都是对象,那么变成集合时,数组中的元素就直接转成集合中的元素。如果数组中的元素都是基本数据类型,那么会将该数组(例如[[[email protected]])作为集合中的元素存在。
Java基础加强第二讲 反射(下)——数组的反射
第一行代码会输出诸如[[[email protected]]这样的内容,这是因为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,加载资源文件的有好几种方式,下面我会一一讲解。

  1. 第一种方式:首当其冲的一种方式,在Java Web开发中,利用API中XX类的getRealPath方法到整个项目在硬盘上的真实位置(绝对位置),然后在项目内部找到资源文件config.properties的位置。记住,一定要记住用完整的路径,但完整的路径不是硬编码,而是运算出来的。
    例如,将资源文件放在工程的根目录下,一般我们是自己写代码这么干,但是在实际开发中不合适。
    Java基础加强第二讲 反射(下)——数组的反射

  2. 第二种方式:一个类加载器能加载.class文件,那它当然也能加载classpath路径下的其他文件,既然它有如此能力,它没有理由不顺带提供这样一个方法,但它也只能加载classpath路径下的那些文件。
    Java基础加强第二讲 反射(下)——数组的反射
    注意:直接使用类加载器加载资源文件时,不能以/打头。Hibernate/Struts2/Spring等框架用的配置文件都是放在classpath指定的目录下的,其内部就是使用类加载器加载配置文件。

  3. 第三种方式:Class类提供了一个便利方法getResourceAsStream,用加载当前类的那个类加载器去加载相同包目录下的文件,既可用相对路径又可用绝对路径。

    • 相对路径
      Java基础加强第二讲 反射(下)——数组的反射
    • 绝对路径
      Java基础加强第二讲 反射(下)——数组的反射
      在上面的路径中,最前面加斜杠表示绝对路径,斜杠代表根目录,不加斜杠表示相对路径,即相对于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());
	}

}