一,能够分析类能力的程序称为“反射”,反射库(java.lang.reflect)提供了精心设计的工具集,以便编写能够动态操作Java代码的程序。

用一句经典的话概括反射:反射就是把java类中的各种成分映射成相应的java类。

二,在程序运行期间,java运行时系统始终为所有的对象维护一个类型标识。这个标识跟踪着每个对象所属的类。虚拟机利用该标识选择相应的方法执行。可以通过专门的Java类访问这些信息。保存这些信息的类被称为Class类。


如同一个Person对象表示一个特定的人一样,一个Class对象表示一个特定类的属性。

三,创建Class类对象有3中形式:

1,已知某对象,调用该对象的getClass()方法将返回一个Class类型的实例。

Person p;
Class cc = p.getClass();
2,调用静态方法forName获得类名对应的Class对象。

String ss = "java.util.Date";
Class cc = Class.forName(ss);
3,若T是任意的Java类型,T.class将代表匹配的类对象。

Class cc1 = java.util.Date.class;
Class cc2 = int.class;
Class cc3 = Double[].class
Class对象表示的是一个类型,而这个类型未必是一种类,如整型(int),数组(double[])


四,最常见的关于Class对象的两个操作

1,getName()方法获得类的名字

Person pp;
syso(pp.getClass().getName()); //syso是System.out.println()的简写。
2,newInstance()方法快速创建一个类的实例

Person pp;
Person pp2 = pp.getClass().newInstance();
pp和pp2是2个不同的对象,newInstance()方法调用默认构造器初始化新对象,若没有默认构造器,会抛出异常。


五,在java.lang.reflect包中有3个类Field,Method和Constructor分别用于描述类的域(成员变量),方法和构造器。

Class类中的getField(),getMethod(),getConstructor()方法分别返回类提供的公有域,公有方法和公有构造器。

Class类中的getDeclaredField(),getDeclaredMethod(),getDeclaredConstructor()方法分别返回类提供的私有域,私有方法和私有构造器。

下面是一个关于上述这几个类和方法使用的示例。

package com.jimmy.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

//创建一个类
class Son2{
	//私有的成员变量
	private double i;
	//默认构造函数和有参构造函数
	public Son2() {}
	public Son2(double i) {this.i = i;}
	//成员变量的set和get方法
	public void setter(double i) {this.i = i;}
	public double getter() {return i;}
}
//测试Class类的函数和反射库中的函数
public class test2 {
	public static void main(String[] args) throws Exception {
		//获得Son2类的Class对象。
		Class<?> cc = Son2.class;
		
		//先得到有参构造器的信息,再根据构造器的信息,由newInstance()函数创建一个Son2对象
		Constructor<?> constructor = cc.getConstructor(double.class);//要指定构造器参数的类型
		Son2 son = (Son2)constructor.newInstance(123);//newInstance()带上参数类型的实例。
		System.out.println(son.getter()); //输出被实例化对象son的成员变量的值:123.0。
		
		//先得到无参构造器的信息,再创建Son2对象
		Constructor<?> constructor1 = cc.getConstructor();
		Son2 son2 = (Son2)constructor1.newInstance();//Son2类中要有默认构造器
		System.out.println(son2.getter());//输出被实例化对象son2的成员变量的值:0.0。
		
		//由无参构造器创建对象时,可不必获得构造器,直接由Class对象调用newInstance()方法。
		Class<?> cc2 = Son2.class;
		Son2 son22 = (Son2)cc2.newInstance();
		System.out.println(son22.getter()); //输出被实例化对象son的成员变量的值:0.0
		
		//下面2个输出语句可看出cc保存类信息,输出的是“class + 类名”。cc.newInstance()是具体类的对象。
		System.out.println(cc);  //输出:class com.jimmy.reflect.Son2
		System.out.println(cc.newInstance());  //输出:com.jimmy.reflect.Son2@15db9742(对象的地址)
		
		//首先得到有参构造函数的信息,然后根据构造函数实例化一个对象。
		//由getDeclaredField()函数得到类里面的私有成员变量,访问私有成员变量要用setAccessible()函数设置访问权限。
		//Field类对象得到成员变量后还可以设置该变量的值,使用set()方法。
		Constructor<?> constructor2 = cc.getConstructor(double.class);
		Son2 son23 = (Son2)constructor2.newInstance(321);
		Field field = cc.getDeclaredField("i");
		field.setAccessible(true);
		field.set(son23, 213);
		System.out.println(field.get(son23));  //field.get(对象)返回获得的对应域的值。
		
		//首先根据获得的构造函数信息实例化一个对象
		//然后由函数名获得类中的公有函数,getMethod("函数名")
		//invoke()方法执行由getMethod()获得的函数,这里获得的函数是getter()
		//对于获得的无参函数,invoke(对象)里只添加对象名。
		Constructor<?> constructor3 = cc.getConstructor(double.class);
		Son2 son24 = (Son2)constructor3.newInstance(231);
		System.out.println(cc.getMethod("getter").invoke(son24)); //输出函数执行返回的结果
		
		//对于获得到的有参函数,在调用getMethod()函数时,要在getMethod()中指定被获得函数的"函数名"和"参数类型"
		//并且在执行该函数(即调用invoke()函数时),要指定对象和参数类型的具体实例。
		Son2 son25 = (Son2)cc.newInstance(); 
		Method method = cc.getMethod("setter", double.class);
		method.invoke(son25, 123);
		System.out.println(son25.getter());  //输出函数执行返回的结果。
		
		//反射包里还有一个重要的类:Modifier,该类是静态类,其中的方法也都是静态方法
		//Class类中getModifiers()函数返回一个用于描述类,构造器,方法和域的修饰符的整形数值。、
		//调用Modifier.toString()方法将整型数值转变成字符串,也是就我们熟悉的public,private,static,final等修饰符。
		System.out.println(Modifier.toString(cc.getModifiers()));
		System.out.println(Modifier.toString(constructor.getModifiers()));
		System.out.println(Modifier.toString(field.getModifiers()));
		System.out.println(Modifier.toString(method.getModifiers()));
		//同时,Modifier类还有一些判断修饰符是不是某一类型的方法。
		System.out.println(Modifier.isPublic(cc.getModifiers()));
		System.out.println(Modifier.isPublic(constructor.getModifiers()));
	}
}

六,应用反射调用其他类的main()方法

首先有一个类,main()方法输出信息:

package com.jimmy.reflect;

public class testPrint {
	public static void main(String[] args) {
		for(String ss:args)
			System.out.println(ss);
	}
}

在其他类中反射调用上面类的main()方法。

package com.jimmy.reflect;

import java.lang.reflect.Method;

public class getMain {
	public static void main(String[] args) throws Exception {
		//传统方法
		testPrint.main(new String[]{"haha","xixi"});
		//反射调用
		Class<?> cc = Class.forName("com.jimmy.reflect.testPrint");
		Method mm = cc.getMethod("main", String[].class);
		mm.invoke(null, (Object)new String[]{"hello","jimmy"});//实例化字符串数组对象时要在前面加(Object),不然会报参数个数不匹配的错误。
	}
}

七,数组的反射

package com.jimmy.reflect;

import java.lang.reflect.Array;

public class test3 {
	public static void main(String[] args) throws Exception {
		//新建4个不同的数组
		int[] a = new int[3];
		int[] b = new int[4];
		int[][] c = new int[3][4];
		String[] d = new String[3];
		//可以对比各个数组所属的类是否相同
		System.out.println(a.getClass() == b.getClass());
//		System.out.println(a.getClass() == c.getClass()); //不同的类不能比较是否相等,会报错
//		System.out.println(a.getClass() == d.getClass());
		System.out.println(a.getClass().getName());
		System.out.println(d.getClass().getName());
		System.out.println(a.getClass().getSuperclass().getName());
		//利用静态方法反射生成一个数组
		int[] aa = (int[]) Array.newInstance(int.class, 3);
		Array.set(aa, 0, 123);//set()静态方法赋值
		Array.set(aa, 1, 321);
		Array.set(aa, 2, 213);
		System.out.println(Array.get(aa, 0));//get()静态方法取值
		System.out.println(Array.get(aa, 1));
		System.out.println(Array.get(aa, 2));
		//获得数组类型的常用方式
		Class<?> cc = String[].class;
		Class<?> cc1 = Class.forName("[I");
		Class<?> cc2 = Class.forName("[Ljava.lang.String;");		
		System.out.println(cc);
		System.out.println(cc1);
		System.out.println(cc2);
		//通过已有对象获取数组类型
		Class<?> cc3 = aa.getClass();
		System.out.println(cc3);
		Class<?> cc4 = cc3.getComponentType();//根据类型信息获取数组内成员的类型
		System.out.println(cc4);
	}
}





Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐