反射reflect知识点用于编写框架,框架开发者需要重点掌握。
什么是反射?
什么是动态语言?
一个标准类中无非就是三个部分,构造器、属性、方法。
构造器:创建对象,完成对象初始化。
属性: 封装数据。
方法: 实现功能,执行功能。
java反射机制提供了对这三部分的具体操作方法。
现有一员工类:
public class Employee {
public String name ="Jerry";
private int pass;
public Employee(){
}
private Employee(List<?> list){
}
public Employee(String name, int pass){
}
private void add(){
}
private void add(String str,int b){
}
private void arr(String [] str){
}
}
反射第一步:加载类 (有三种方式),获取类的字节码文件并将其加载到内存中。
//方式一
Class className1 = Class.forName("com.test.Employee.java");
//方式二
Class className2 = new Employee().getClass();
//方式三
Class className3 = Employee.class;
反射第二步:反射类中的对象,调用对应的方法反射指定的构造or方法or属性
调用getConstructors()方法,反射类中指定的构造函数。
调用getMethod()方法,反射类中指定的方法。
调用getField()方法,反射类中指定的属性。
以上三种方法只针对调用的对象修饰符为public时使用
getDeclaredConstructor();
getDeclaredMethod();
getDeclaredField();
反射第三步:操作对象
创建测试类,创建测试类需要junit jar包的支持。
public class Test {
/**反射无参构造*/
@org.junit.Test
public void test() throws Exception{
//加载类
Class className = Employee.class;
//反射员工类的无参构造
Constructor c = className.getConstructor();
//创建实例,因为反射的是无参构造,newInstance无需传值
Employee e = (Employee) c.newInstance();
}
/**反射多参构造*/
@org.junit.Test
public void test2() throws Exception{
Class className = Employee.class;
//反射员工类的多参构造,构造参数传类对象,其他类型同理
Constructor c = className.getConstructor(String.class,int.class);
//创建实例,因反射的是多参构造,需传入构造方法的参数
Employee e = (Employee) c.newInstance("hello",211);
}
/**反射构造参数为List集合,且为private构造*/
@org.junit.Test
public void test3() throws Exception{
Class className = Employee.class;
//构造方法参数为List集合,且为private构造
Constructor c = className.getDeclaredConstructor(List.class);
c.setAccessible(true); //暴力反射,如果调用的构造方法为private,则使用此方法强制反射
Employee e = (Employee) c.newInstance(new ArrayList());
}
/**反射add的无参方法*/
@org.junit.Test
public void test4() throws Exception{
Class className = new Employee().getClass();
//反射add的无参方法
Method method = className.getDeclaredMethod("add");
method.setAccessible(true); //强制反射
//invoke()方法运行对象,invoke方法第一个参数是对象,后面的参数传add方法的参数
Employee e = (Employee) method.invoke(new Employee());
}
/**反射add的多参方法*/
@org.junit.Test
public void test5() throws Exception{
Class className = new Employee().getClass();
//反射add的多参方法
Method method = className.getDeclaredMethod("add",String.class, int.class);
method.setAccessible(true);
Employee e = (Employee) method.invoke(new Employee(),"hello", 16);
}
/**反射 arr数组参方法*/
@org.junit.Test
public void test6() throws Exception{
Class className = new Employee().getClass();
//反射arr数组参方法
Method method = className.getDeclaredMethod("arr",String[].class);
method.setAccessible(true);
/*因javaAPI版本问题,因此会将传入String数组拆分成三个String对象,但被反射
的类中没有三个String类型参数的arr方法,因此会报错找不到该方法,此时可将该数组对象
强转为Object类型,欺骗JVM将该数组对象当成一个参数来解析,则JVM就不会拆分该数组,因此
当反射一个形参为数组类型的方法时,在传参时将传入的数组对象强转为Object类型即可 */
Employee e = (Employee) method.invoke(new Employee(),(Object)new String[]{"hello","world","haha"});
}
/**反射获取字段*/
@org.junit.Test
public void test7() throws Exception{
Class className = new Employee().getClass();
//拿取员工类的name字段
Field f = className.getField("name");
//要获取name得要说明是哪个对象的属性,此处传入员工对象,f接收到的name为String类型,则此处用String接收
String str = f.get(new Employee()).toString();
}
/**通过反射设置字段的值*/
@org.junit.Test
public void test8() throws Exception{
Employee e= new Employee();
Class className = new Employee().getClass();
//拿取员工类的name字段
Field f = className.getField("name");
f.set(e, "hello ");
System.out.println(e.name); //结果为hello
}
/**反射获取private字段的值*/
@org.junit.Test
public void test9() throws Exception{
Class className = new Employee().getClass();
//拿取员工类的pass字段,private修饰用Declared取值
Field f = className.getDeclaredField("pass");
f.setAccessible(true); //强制反射
//f是取自Employee的对象
int i = (int)f.get(new Employee());
System.out.println(i);
}
}
此处涉及一面试知识点:private修饰的对象在类外部是否可以访问(除过get/set)?
答:在外部类中不能访问私有对象,但是java反射可以,可通过setAccessible(true)方法设置强制反射。
通常情况即使是当前类,private属性或方法也是不能访问的,你需要设置压制权限setAccessible(true)
来取得private的访问权。但这已经破坏了面向对象的规则,所以除非万不得已,尽量少用。
java反射的应用:Spring框架 IOC(控制反转)、Hibernate框架:关联映射、白盒测试等。
Spring框架的IOC简化实现: