简介
反射是框架设计的灵魂 (使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码))
- JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
- 反射是通过Class对象(字节码文件),来知道某个类的所有属性和方法。也就是说通过反射我们可以获取构造器,对象,属性,方法(原本不知道)
- 要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象
Java的四大机制
- java反射机制
- 垃圾回收机制
- 异常处理机制
- 事件处理机制
java反射过程
获取Class对象的三种方式
要想使用反射,必须先得到代表的字节码的Class对象,Class类用于表示.class文件(字节码)
1.通过该类的对象去获取到对应的Class对象(基本不用它)
//第一种方式: student--->Class对象 通过getClass()方法
//Student是一个空类
Student student = new Student();
//这里我们就省去泛型了,Class<?>
Class stuClass = student.getClass(); //获取到了对应的Class对象
System.out.println(stuClass);
System.out.println(stuClass.getName()); //获取Class对象的名字
输出:
class fanshe.Student
fanshe.Student
但是需要注意的是,第一种我们基本不用,这里显然和反射机制相悖(你有类对象 student 还去用反射获取Class类对象干嘛,多此一举)
2.通过类名.class静态属性获取(比较简单)
//第二种方式: 每个类创建后 都会有一个默认的静态的class属性 用于返回该类的class对象
//需要注意的是: 任何数据类型(包括基本数据类型)都有“静态”的class属性
Class stuClass2 = Student.class;
System.out.println("是否为同一个class对象?"+(stuClass==stuClass2));
结果:true
这里需要注意的是,这种方式虽然比较简单,但是需要导包,不然会编译错误(对比第三种,全限定类名方式)
3.通过Class类中的静态方法 forName()方法获取(最为常见)
//第三种方式: Class.forName("fanshe.Student"); 注意参数一定为该类的全限定类名
try {
Class stuClass3 = Class.forName("fanshe.Student");
//System.out.println(stuClass3); 输出仍然是class fanshe.Student
System.out.println("是否为同一个class对象?"+(stuClass3==stuClass2));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
结果:true
结论:同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。
根据反射入口对象(class)获取类的各种信息
- getConstructors() 获取所有的构造方法
Constructor<?>[] con=cls.getConstructors();
- getSuperclass() 获取父类
Class<?> sup=cls.getSuperclass();
- 获取当前类(只有本类的)的所有方法和属性,包括私有的
Method[] ms = cls.getDeclaredMethods(); //获取当前类的所有方法
Field[] fs = cls.getDeclaredFields(); //所有属性
- getMethods() 获取此类的所有public方法(父类的,实现接口的,自己的)
try {
Class<?> cls = Class.forName("entity.User");
Method[] ms=cls.getMethods();
for(Method m:ms) { //遍历所有方法
System.out.println(m.getName());
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
- 通过获取到的构造器创建对象:
//这里我们需要捕获一下异常
//因为我们的构造器是私有的,不能在其他类去创建,所以我们用了setAccessible()这个方法,从而可以创建
/*
private Student(String name,int age,boolean sex){
System.out.println("用private修饰的Student的含有三个参数的构造器:"+name+age+sex);
}*/
try {
con2.setAccessible(true); //忽略构造器的访问修饰符,解除私有限定
Object object = con2.newInstance("张三",10,true); //这句话就相当于new Student("张三",10,true);
Student student = (Student) object; //指向子类对象的父类引用重新转为子类引用
} catch (Exception e) {
e.printStackTrace();
}
//输出:用private修饰的Student的含有三个参数的构造器:张三10true
注意: xxx.setAccessible(true) 是为了解除私有限定
通过反射获取对象的实例,并操作对象
- class.newInstance() ,并强转类型,然后就可以操作对象了,主要是调用方法。
try{
Class<?> cls = Class.forName("entity.User");
User user = (User) cls.newInstance();
user.setUsername("zs");
user.setSax("男");
System.out.println(user.getUsername()+"\t"+user.getSex());
} catch (ClassNotFoundException){
e.printStackTrace();
}
- 操作属性,可以操作类里面的public属性和private属性
如果属性是private,正常情况下是不允许外界操作属性值,这里可以用Field类的setAccessible(true)方法,暂时打开操作的权限
- 调用方法也一样,可以调用私有的方法,null是因为这个方法没有参数
invoke方法
public class TestClassLoad {
public static void main(String[] args) throws Exception {
Class<?> clz = Class.forName("A");
Object o = clz.newInstance();
Method m = clz.getDeclaredMethod("hello", null);
m.invoke(o);
}
static class A{
public void hello() {
System.out.println("hello world");
}
}
}
上面就是最常见的反射使用的例子,前两行实现了类的装载、链接和初始化(newInstance方法实际上也是使用反射调用了方法),后两行实现了从class对象中获取到method对象然后执行反射调用。下面简单分析一下后两行的原理。
设想一下,如果想要实现method.invoke(action,null)调用action对象的myMethod方法,只需要实现这样一个Method类即可:
Class Method{
public Object invoke(Object obj,Object[] param){
A instance=(A)obj;
return instance.foo();
}
}
反射的原理之一其实就是动态的生成类似于上述的字节码,加载到jvm中运行。
Java反射实例
先创建一个封装的User的对象
package entity;
public class User {
private String username;
private String sex;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public void eat() {
System.out.println("jdmd");
}
}
测式类Tset,使用java反射
package test;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import entity.User;
public class Test {
public static void main(String[] args) {
try {
Class<?> cls = Class.forName("entity.User"); //注意此字符串必须是真实路径,就是带包名的类路径,包名.类名
System.out.println(cls.getName()); //获取包名的类路径名
System.out.println(cls.getClass()); //获取包的类型
//获取所有属性
Field[] fs = cls.getDeclaredFields();
for(Field f: fs) {
System.out.println(f.getName());
}
//获取所有方法,并且包括父类的方法
Method[] ms=cls.getMethods();
for(Method m:ms) {
System.out.println(m.getName());
}
//通过反射获取公开方法并使用
Object object = cls.newInstance();
Method m = cls.getDeclaredMethod("eat", null);
m.invoke(object);
//通过反射获取构造方法并使用
User obj = (User) cls.newInstance();
Method method = cls.getDeclaredMethod("setUsername", java.lang.String.class);
method.invoke(obj,"aaa");
System.out.println(obj.getUsername());
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}