说到java反射,不得不说说它的功能;
通过java反射机制,可以在程序中访问已经装载到JVM中的java对象的描述,实现访问、检测和修改描述java对象本身的信息的功能。java反射机制的功能十分强大,在java.lang.reflect对该功能的支持。
注意:在Object类中定义了一个getClass()方法,该方法会返回了一个类型为Class的对象。
例如: Class NameA = a.getClass(); // a为A类的一个对象
以下表格描述的是通过反射可以访问的主要描述信息:
通过反射能够访问以下类的信息:
1.访问构造方法:
Constructor类的常用方法:
实例1:首先创建一个Demo1类,在该类中声明一个String类型的成员变量和3个int型的成员变量,并提供一个构造方法。代码如下:
package com.study.reflect;
public class Demo1 {
String s;
int i,i1,i2;
private Demo1() {
}
protected Demo1(String s,int i) {
this.s=s;
this.i=i;
}
public Demo1(String...strings) throws NumberFormatException{
if(strings.length>0) {
i=Integer.valueOf(strings[0]);
}
if(strings.length>1) {
i1=Integer.valueOf(strings[1]);
}
if(strings.length>2) {
i2=Integer.valueOf(strings[2]);
}
}
public void print() {
System.out.println("s="+s);
System.out.println("i="+i);
System.out.println("i1="+i1);
System.out.println("i2="+i2);
}
}
下面再编写测试类Main1,在类中通过反射访Demo1类中的所有构造方法,并将该构造方法是否允许带有可变数量的参数、入口参数类型和可能抛出的异常类型输出到控制台。代码如下:
package com.study.reflect;
import java.lang.reflect.Constructor;
public class Main1 {
public static void main(String []args) {
//创建一个Demo1类的对象
Demo1 demo1 = new Demo1("11","22","33");
//获取demo1对象的Class类的对象
Class<? extends Demo1> demoA = demo1.getClass();
//获取所有的构造方法即构造方法的数组,一个Constructor代表一个构造方法
Constructor<?>[] declaredConstructors = demoA.getDeclaredConstructors();
//遍历构造方法
for(int i=0;i<declaredConstructors.length;i++) {
//获取当前构造方法
Constructor<?> constructor = declaredConstructors[i];
System.out.println("查看是否允许带有可以变数量的参数:"+constructor.isVarArgs());
System.out.println("该构造方法的入口参数类型依次为:");
//以Class数组的形式获得该构造方法的各个参数的类型
Class<?>[] parameterTypes = constructor.getParameterTypes();
for(int j=0;j<parameterTypes.length;j++) {
//以字符串的形式输出参数的class类型
System.out.println(" "+parameterTypes[j]);
}
System.out.println("该构造方法可能抛出的异常为:");
//以Class数组的形式获得该构造方法可能抛出的异常类型
Class<?>[] exceptionTypes = constructor.getExceptionTypes();
for (int j = 0; j < exceptionTypes.length; j++) {
//以字符串的形式输出可能抛出异常的class类型
System.out.println(" "+exceptionTypes[j]);
}
Demo1 demo2 = null;
while(demo2==null) {
try {//如果该成员变量的访问权限为private,则抛出异常,即不允许访问
if(i==2) {//通过执行默认没有参数的创建对象
demo2 = (Demo1) constructor.newInstance();
}
else if(i==1) {
//通过执行两个参数的构造方法创建对象
demo2 = (Demo1) constructor.newInstance("7",5);
}else {
//通过执行具有可变数量参数的构造方法创建对象
Object [] parameters = new Object [] {new String [] {"100","200","300"}};
//Object [] strings = new String[] {"100","200","300"};
demo2 = (Demo1) constructor.newInstance(parameters);
}
} catch (Exception e) {
// TODO: handle exception
System.out.println("在创建对象时抛出异常,下面执行setAccessible()方法");
/**
* 如果当前构造方法的权限为private,默认不允许创建通过反射利用newInstance(Object... initargs)方法创建对象。
* 如果先执行该方法,并将入口参数设为true,则允许创建
*/
constructor.setAccessible(true);//设置为允许访问
}
if(demo2!=null) {
demo2.print();
System.out.println();
}
}
}
}
}
运行这个实例,当通过反射Demo1()构造方法时,输出结果如下:
当通过反射Demo1(String s,int i)构造方法时,输出结果如下:
当通过反射构造方法Demo1(String...strings) 时,输出结果如下:
2.访问成员变量 :
Field类的常用方法:
实例2:创建一个Demo2类,在该类中一次声明一个int、float、boolean、String型的成员变量,并将他们设置为不同的权限。代码如下:
package com.study.reflect;
public class Demo2 {
int i;
public float fl;
protected boolean bl;
private String s;
}
通过反射Demo2类中的所有成员变量,将成员变量的名称和类型信息输出到控制台,并分别设置为不同的访问权限;代码如下:
package com.study.reflect;
import java.lang.reflect.Field;
public class Main2 {
public static void main(String []args) {
Demo2 demo = new Demo2();
Class<? extends Demo2> demo2 = demo.getClass();
//获取所有成员变量
Field[] declaredFields = demo2.getDeclaredFields();
for(int i=0;i<declaredFields.length;i++) {//遍历成员变量
Field f = declaredFields[i];
//获取成员变量的名称
System.out.println("名称为"+f.getName());
//获取成员变量类型
Class<?> fieldType = f.getType();
System.out.println("类型为"+fieldType);
boolean start = true;
while(start) {
//如果该成员变量的访问权限为private,则抛出异常,即不允许访问
try {
start=false;
//获取成员变量的值
System.out.println("修改前的值为"+f.get(demo));
//判断成员变量的类型是否为int型
System.out.println("ni"+fieldType);
if(fieldType.equals(int.class)) {
System.out.println("利用方法setInt()方法修改成员变量的值");
f.setInt(demo, 123);
System.out.println("------");
}else if(fieldType.equals(float.class)) {//判断成员变量的类型是否为float型
System.out.println("利用方法setFloat()方法修改成员变量的值");
System.out.println("wwwwwww");
f.setFloat(demo, 111.1f);
}else if(fieldType.equals(boolean.class)) {//判断成员变量的类型是否为boolean型
System.out.println("利用方法setBoolean()方法修改成员变量的值");
f.setBoolean(demo, true);
}else {
System.out.println("利用方法set()修改车成员变量的值");
//可以为各种类型的成员变量赋值
f.set(demo, "Success");
}
//获取成员变量值
System.out.println("修改后的值为"+f.get(demo));
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
System.out.println("在设置成员变量值时抛出异常,"+"下面执行setAccessible()方法");
f.setAccessible(true);//设置为允许访问
start= true;
}
}
}
}
}
运行实例2,输出结果如下所示:
3.访问方法:
Method类的常用方法:
实例3:创建一个Demo3类,并编写4个典型的方法。具体代码如下:
package com.study.reflect;
public class Demo3 {
static void staticMethod() {
System.out.println("执行staticMethod()方法");
}
public int publicMethod(int i) {
System.out.println("执行publicMethod()方法");
return i*10;
}
protected int protectedMethod(String s,int i) throws NumberFormatException{
System.out.println("执行protectedMethod()方法");
return Integer.valueOf(s)+i;
}
private String privateMethod(String...strings) {//里面参数是一个可变长度的数组,名为strings
System.out.println("执行privateMethod()方法");
StringBuffer stringBuffer = new StringBuffer();
for(int i=0;i<strings.length;i++) {
stringBuffer.append(strings[i]);
}
return stringBuffer.toString();
}
}
通过反射访问Demo3类中的所有方法,将各个方法的名称、入口参数类型、返回值类型等信息输出到控制台,并执行方法。代码如下:
package com.study.reflect;
import java.lang.reflect.Method;
public class Main3 {
public static void main(String []args) {
Demo3 demo = new Demo3();
Class<? extends Demo3> demo3 = demo.getClass();
//获取所有方法
Method[] declaredMethods = demo3.getDeclaredMethods();
for(int i=0;i<declaredMethods.length;i++) {//遍历方法
Method method = declaredMethods[i];
//获取方法名称
String name = method.getName();
System.out.println("名称为:"+name);
System.out.println("是否允许带有可变数量的参数:"+method.isVarArgs());
System.out.println("入口参数依次为:");
//获得所有参数类型
Class<?>[] parameterTypes =method.getParameterTypes();
for(int j=0;j<parameterTypes.length;j++) {
System.out.println(" "+parameterTypes[j]);
}
//获取方法的返回值类型
System.out.println("返回值类型为"+method.getReturnType());
System.out.println("可能抛出的异常类型有:");
//获取方法可能抛出的所有异常类型
Class<?>[] exceptionTypes = method.getExceptionTypes();
for (int j = 0; j < exceptionTypes.length; j++) {
System.out.println(" "+exceptionTypes[j]);
}
boolean start = true;
while(start) {
//如果该方法的权限为private则不允许访问
try {
start = false;
if("staticMethod".equals(method.getName())) {//执行没有入口参数的方法
method.invoke(demo);
}
else if("publicMethod".equals(method.getName())) {
method.invoke(demo, 12); //执行方法
}
else if("protectedMethod".equals(method.getName())) {
method.invoke(demo, "34",6); //执行方法
}
else if("privateMethod".equals(method.getName())) {
Object [] parameters = new Object[] {new String[] {"HELLO","WORLD!"}}; //定义二维数组
System.out.println("返回值为:"+method.invoke(demo,parameters ));
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
System.out.println("在执行方法时抛出异常"+"下面执行setAccessible()方法:");
method.setAccessible(true); //设置为允许
start=true;
}
}
System.out.println();
}
}
}
运行实例,输出结果:
4.得到Class对象的3种方式
首先编写一个普通类Test类,代码如下:
package com.study.reflect;
/**
*一个普通类
* @author 哎呦不错呦
*
*/
public class Test {
public Test() {
//构造方法
}
void work() {
System.out.println("调用了work()方法");
}
void like() {
System.out.println("调用了like()方法");
}
}
然后在编写一个测试类ReflectTest,代码如下:
package com.study.reflect;
/**
* 测试类,得到Class对象的3种方式
* @author 哎呦不错呦
*
*/
public class ReflectTest {
public static void main(String []args) throws ClassNotFoundException {
//1.通过对象调用getClass()方法获取Class对象
Test test = new Test();
Class<? extends Test> A = test.getClass();
//2.通过直接通过 类名.class 的方式得到,该方法最为安全可靠,程序性能更高
// 因为任何一个类都有一个隐含的静态成员变量 class
Class B = Test.class;
//3.通过 Class 对象的 forName() 静态方法来获取,用的最多,
// 但可能抛出 ClassNotFoundException 异常
Class C = Class.forName("com.study.reflect");
}
}
参考:java从入门到精通(第4版)