1、什么是反射
Java
反射就是在运行时,对于任意一个类,程序都能获取这个类的所有属性和方法。对于任意一个对象,程序都能调用它的所有方法和属性。这种动态获取类的信息和动态调用对象方法和属性的功能成为Java
的反射机制。
Java反射就是把类的各种成分映射成一个个Java对象
要想解析一个类,首先需要获取到这个类的字节码文件对象,也就是.class
文件。而且一个类有且只有一个.class
文件。
2、怎么获取类的字节码文件
Java提供了四种方式来获取字节码文件对象。如cn.com.kk.TestClass
这个类
(1)知道具体的类时
Class tem = TestClass.class;
通过此方法获取的Class对象不会初始化。
(2)通过实例对象获取Class对象
TestClass test = new TestClass();
Class tem = test.getClass();
(3)通过类的加载器获取
Class clazz = ClassLoader.LoadClass("cn.com.kk.TestClass");
通过此方法获取的Class对象不会初始化。
(4)通过Class.forName()
传入类路径获取(推荐)
Class tem = Class.forName("cn.com.kk.TestClass");
Class.forName()
方法内部调用的是一个native
方法 forName0(className, true, ClassLoader.getClassLoader(caller), caller)
;第二个参数表示类是否需要初始化,Class.forName(className)
方法默认是需要初始化的。
一旦初始化,就是出发类的static
代码块执行,static
参数也会初始化。
3、如何使用反射
先设定一个Person类
package cn.com.kk.test;
public class Person {
private Integer age;
protected String name;
public Boolean sex;
Double money;
Person(String name) {};
public Person(){}
public Person(Integer age) {
}
public Person(Integer age, String name) {
super();
this.age = age;
this.name = name;
}
protected Person(Integer age, String name, String sex) {
}
private Person(Boolean sex) {
}
public String getName() {
return name;
}
protected boolean getSex() {
return sex;
}
private Integer setAge(Integer age) {
this.age = age;
return age;
}
// 测试main方法的反射
public static void main(String[] args) {
System.out.println("main方法执行了。。。");
}
}
通过反射获取并操作这个类的相关属性和方法
public class Test {
public static void main(String[] args) throws Exception {
Class clazz = Class.forName("com.jiuqi.kk.test.Person");
// 调用构造方法
// 获取所有的公有构造方法
Constructor[] conArray = clazz.getConstructors();
for (Constructor con : conArray) {
System.out.println(con);
}
System.out.println("----------------------------------------");
// 获取所有的构造方法
conArray = clazz.getDeclaredConstructors();
for (Constructor con : conArray) {
System.out.println(con);
}
System.out.println("----------------------------------------");
// 获取公有、无参的构造方法
Constructor con = clazz.getConstructor(null);
System.out.println(con);
Object obj = con.newInstance(); // 实例化一个Person对象
System.out.println("----------------------------------------");
// 获取私有、有参的构造方法
con = clazz.getDeclaredConstructor(Boolean.class);
System.out.println(con);
con.setAccessible(true); // 暴力访问,忽略访问修饰符
obj = con.newInstance(true); // 调用构造方法
System.out.println("----------------------------------------");
// 调用方法
// 获取所有的公有方法
Method[] methods = clazz.getMethods();
for (Method m : methods) {
System.out.println(m);
}
System.out.println("----------------------------------------");
// 获取所有的方法
methods = clazz.getDeclaredMethods();
for (Method m : methods) {
System.out.println(m);
}
System.out.println("----------------------------------------");
// 获取公有的getName()方法
Method m = clazz.getMethod("getName", null);
System.out.println(m);
System.out.println("----------------------------------------");
// 获取私有的setAge()方法
m = clazz.getDeclaredMethod("setAge", Integer.class);
System.out.println(m);
Object o = clazz.getConstructor().newInstance(); // 实例化一个person对象
m.setAccessible(true); // 忽略私有修饰符
Object result = m.invoke(o, 24); // 调用方法给参数赋值,第一个是要调用的对象(反射获取),
//第二个是传入的实参。返回值是该方法的返回值。
System.out.println(result); // 24
// 获取main方法
Method methodMain = clazz.getMethod("main", String[].class);//第一个参数:方法名称,第二个参数:方法形参的类型,
//3、调用main方法
// methodMain.invoke(null, new String[]{"a","b","c"});
//第一个参数,对象类型,因为方法是static静态的,所以为null可以,
//第二个参数是String数组,这里要注意在jdk1.4时是数组,jdk1.5之后是可变参数
//这里拆的时候将 new String[]{"a","b","c"} 拆成3个对象。。。所以需要将它强转。
methodMain.invoke(null, (Object)new String[]{"a","b","c"});
}
}
4、反射的实际运用
(1)通过反射运行配置文件内容
还是以Person类为例,相关配置文件pro.txt
如下
className = com.jiuqi.kk.test.Person
methodName = setAge
methodValue = 25
测试类
public class Test {
public static void main(String[] args) throws Exception {
//通过反射获取Class对象
Class stuClass = Class.forName(getValue("className"));
//2获取setAge()方法
Method m = stuClass.getMethod(getValue("methodName"), Integer.class);
//3.调用setAge()方法
Object o = m.invoke(stuClass.getConstructor().newInstance(),
Integer.valueOf(getValue("methodValue")));
System.out.println(o); // 25
}
//此方法接收一个key,在配置文件中获取相应的value
public static String getValue(String key) throws IOException{
Properties pro = new Properties();//获取配置文件的对象
FileReader in = new FileReader("pro.txt");//获取输入流
pro.load(in);//将流加载到配置文件对象中
in.close();
return pro.getProperty(key);//返回根据key获取的value值
}
}
(2)通过反射越过泛型检查
Java泛型是伪泛型,因为Java在编译期间,所有的泛型信息会被擦除掉,也叫类型擦除。
所以可以通过反射来越过泛型检查
public class Test {
public static void main(String[] args) throws Exception {
List<String> list = new ArrayList<String>();
list.add("aa");
list.add("bb");
System.out.println(list);// [aa,bb]
//通过反射获取Class对象
Class clazz = list.getClass();
//2获取add()方法
Method m = clazz.getMethod("add", Object.class);
//3.调用add()方法
m.invoke(list, 25);
System.out.println(list); // [aa,bb,25]
}
}