反射首先要得到类的字节码
得到字节码有三种方式
- 对象的getClass()方法,这是Object的方法;
- 类的classs属性
- Class的forName()方法
class Father{
public Father(){
}
public Father(int a){
}
public void test(){
}
public void test(int a){
}
}
Class a = f.getClass();
Class b = Father.class;
try {
Class c = Class.forName("test.java.Father");
System.out.println(c.getName());
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
这里需要注意,第三种方法需要抛出异常或者catch,并且forName中的参数是字符串。
这样得到的三个Class对象都是一样的,因为每一个class只会有一个Class对象。所以如果用==来判断这三个引用的关系的话,是相等的。Class对象在类加载的时候就会创建在堆中。
获取了Class对象之后,可以通过该对象得到类的属性和方法
import java.lang.reflect.*;
Class a = Father.getClass();
Constructors[] cons = a.getConstructors(); //得到public的构造方法
Constructors[] cons = a.getDeclaredConstructors(); //得到public, protected, default(package) access, and private 的构造方法
Methods[] meths = a.getMethods(); //public 方法,包括父类或者父接口的方法
Methods[] meths = a.getDeclaredMethods(); //public, protected, default (package)access, and private 方法, 不包含继承的方法,也不包含构造方法
Field[] fils = a.getFields(); //public 的字段,包括父类或者父接口的字段
fils = a.getDeclaredFileds(); //public, protected, default(package) access, and private 字段, 不包含继承的字段
注意Constructor、Method、Field是reflect中的类,需要导包,字段包含static修饰的字段还有final修饰的字段
得到了构造方法之后,可以通过该构造方法新建对象,同样的,需要throws 或catch异常
Class a = Father.class();
Constructor[] conArray = a.getConstructors();
for(Constructor i : conArray) {
Father fa = (Father) i.newInstance(); //创建的对象是Object,所以需要转型,有些构造方法可能需要参数,newInstance中也可以放入参数。
}
Constructor con = a.getConstructor(int.class); //也可以直接得到某个具体的构造方法,参数就是构造方法的参数的型材类型,也就是后面添加class
得到特定的字段并且调用
Father father = new Father();
Class a = father.getClass();
Field fie = a.getField("a");
Constructor con = a.getConstructor();
Object o = con.newInstance();
fie.set(o, 5); //或者fie.set(f, 5);
得到特定的方法并且调用
package java
Class a = Class.forName("java.Father");
Constructor con = a.getConstructor();
Method med1 = a.getMethod("test");
Method med2 = a.getMethod("test", int.class); //找到的方法可能重载过,所以有时候要传入形参的类型
Object o = con.newInstance();
med.invoke(o, null); //两个参数,前面是对象,后面是参数,如果是null,可以不写
反射也可以调用main方法
package java
Class a = Class.forName("java.Father");
Constructor con = a.getConstructor();
Method med = a.getMethod("main", String[].class);
Object o = con.newInstance();
med.invoke(o, new String[]{"a"});
反射的作用
数据库的驱动加载的时候,一般使用的是反射来动态加载,好处在于耦合度低,在需要更换驱动的时候,只需要更改配置文件,如下;
package learnforwhat;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
public class LearnReflect {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {
String root = System.getProperty("user.dir");
Properties pro = new Properties();
FileReader fil = new FileReader("./src/learnforwhat/properties.txt");
pro.load(fil);
fil.close();
String className = (String) pro.get("className");
String methodName = (String) pro.get("methodName");
Class clazz = Class.forName(className);
Method med = clazz.getMethod(methodName, null);
med.invoke(clazz.getConstructor().newInstance());
}
}
package jartestforwhat;
public class myDriver {
static {
System.out.println("it is my driver from jar");
}
public void showTheCode() {
System.out.println("it is method");
}
public static void main(String[] args) {
}
}
jartestforwhat已经打成jar包,导入到项目中
properties.txt中的内容
className = jartestforwhat.myDriver
methodName = showTheCode
这里说一句FileReader,参数中可以是相对路径,但是得知道现在的根目录是什么,一开始一直报错,后来通过System.getProperty("user.dir")知道了根目录
如果我们需要更改反射加载的class,只需要更改配置文件就好了。
反射还可以越过泛型检查
class MyList<E>{
Object[] o = new Object[1];
public void add(E e) {
o[0] = e;
}
}
存在这么一个类
那么如何添加一个String类型的参数呢,如果直接new一个对象,并且调用add方法,编译器会报错
可以通过反射来越过,因为反射是在运行的时候动态加载的,而泛型检查是在编译阶段,之后 就会被擦写掉;
Class clazz2 = Class.forName("learnforwhat.MyList");
MyList<Integer> my = new MyList<Integer>();
my.add("String"); //报错
Method med2 = clazz2.getMethod("add", Object.class);
med2.invoke(my, "String"); //没问题