先推荐安装一个 eclipse 的反编译插件 Enhanced Class Decompiler
是什么?
在说反射之前先说一下编译时类型和运行时类型,大家都知道List是一个接口,它是不可以被实例化的,但是可以通过多态实现:List list = new ArrayList(); 对于变量list来说编译时期他被声明为List类型,但是真正使用的确实ArrayList中的成员(在堆中创建的是ArrayList的对象),所以list的运行时类型是ArrayList,它的运行时类就是ArrayList被编译之后加载到内存的字节码类文件。
再说反射,反射是java中一种可以在执行期间获取类内部信息的技术,说白一点就是根据加载的类字节码文件去获取类本身的一种技术,之前介绍了类加载,加载过程中会创建一个Class类的对象来表示这个类,每一个Class类的对象都是一个运行时类,所以反射就是根据运行时类获取其本类信息并使用这个类的一种技术,反射号称是java实现动态编程的关键。为什么说反射是实现动态编程的关键呢?我自己的理解是这样的:假设我写了一个方法用于处理很多类,这个时候呢,我不可能为每一个类都写一个方法,可是呢,我知道每一个运行时类都是Class的对象,并且我可以根据class对象获取其本类的对象,所以这个时候,我接受的参数就可以是一个Class类型的参数,这样就使得代码可以高度复用了,这就是反射的魅力。通过使用反射,可以使得代码高度复用,大大的降低代码的耦合性。
有什么用?
具体说一下反射的能力:使用反射可以在运行时判断任意一个对象所属的类、构造一个类的对象、判断一个类具有的成员变量和方法、用一个对象的成员变量和方法等等。通过使用反射可以通过运行时类去操作类本身的所有属性。
怎么用?
想要使用反射,就要获取运行时类,那么运行时类如何获取和表示呢?在java中每一个运行时类都是java.lang.Class的对象,所以可以使用Class来声明运行时类,而获取运行时类的方式有四种,代码如下:
public static void main(String[] args) {//1、通过类的.class属性Class<User> c1 = User.class;//2、通过类对象的.getClass方法User user = new User();Class c2 = user.getClass();//3、通过Class类的forName方法,一般用这个try {Class c3 = Class.forName("com.czp.reflection.User");} catch (ClassNotFoundException e) {e.printStackTrace();}//4、通过类的加载器获取(这种一般用的不多)try {Class<?> c4 = ClassLoader.getSystemClassLoader().loadClass("com.czp.reflection.User");} catch (ClassNotFoundException e) {e.printStackTrace();}
}
这里需要说明的一点是,在一个进程中,一个类的字节码文件添加到内存中后就是一个运行时类,每个运行时类只会加载一次。
获取了运行时类也就是Class的对象之后可以做些什么呢?如何利用反射?下边具体说一下
获取类的构造方法、创建类的对象:
public class ReflectionTest {public static void main(String[] args) {ReflectionTest test = new ReflectionTest();test.getClassConstructors();}//获取User的运行时类
@SuppressWarnings("unchecked")
public Class<User> getUser()
{String name = "com.czp.reflection.User";Class<User> user= null;try {user = (Class<User>) Class.forName(name);} catch (ClassNotFoundException e) {e.printStackTrace();}return user;
}
//测试获取类中的构造方法,创建类的实例
public void getClassConstructors()
{try{Class<User> userClass = getUser();//1、直接调用Class中方法创建类的对象,这种方法的前提是类中必须要有一个公共的无参构造方法User user = userClass.newInstance();//2、通过类中对应的构造方法创建对象// 首先就需要先获取这个类中的构造方法//Declared 有声明之意,可理解为只要是声明的构造方法都可以获取到,获取所有构造方法(不管权限如何)Constructor<?>[] declaredConstructors = userClass.getDeclaredConstructors();//只能获取所有公共的构造方法Constructor<?>[] constructors = userClass.getConstructors();//获取指定参数的构造方法(所有的)Constructor<User> declaredConstructor = userClass.getDeclaredConstructor(String.class);//如果构造方法是私有的,通过setAccessible(true)设置可以使用私有构造方法进行创建对象declaredConstructor.setAccessible(true);User privateUser = declaredConstructor.newInstance("zhangsan");//User [name=zhangsan, age=null]System.out.println(privateUser);//获取指定参数的构造方法(共有的)Constructor<User> constructor = userClass.getConstructor(Integer.class);//再根据构造方法创建类的对象User user2 = constructor.newInstance(5);//User [name=null, age=5]}catch (Exception e) {e.printStackTrace();}
}
}
获取运行时类的成员:
//测试获取类中的成员信息
public void getClassMethods() {try{Class<User> userClass = getUser();User user = userClass.newInstance();//---------------------------处理方法----------------------////1、获取所有的方法Method[] declaredMethods = userClass.getDeclaredMethods();//2、根据方法的名称参数获取方法Method declaredMethod = userClass.getDeclaredMethod("show");//使用方法,如果是私有方法则先强制设置以下declaredMethod.setAccessible(true);declaredMethod.invoke(user, null);//3、获取所有公共的方法(包括父类的)Method[] methods = userClass.getMethods();//4、获取方法,不写了,就是根据名字获取,和上边一样//---------------------------处理变量----------------------////1、获取所有变量Field[] declaredFields = userClass.getDeclaredFields();//2、获取指定变量Field declaredField = userClass.getDeclaredField("name");declaredField.setAccessible(true);declaredField.set(user, "zhangsan");System.out.println(declaredField.get(user));//zhangsan//3、还有两个获取所有共有的变量}catch (Exception e) {e.printStackTrace();}
}
获取运行时类的其他:
public void getClassOther(){try{Class<User> userClass = getUser();User user = userClass.newInstance();//1、可以获取注解Annotation[] declaredAnnotations = userClass.getDeclaredAnnotations();//2、所在包名Package p = userClass.getPackage();//3、获取父类Class<? super User> superclass = userClass.getSuperclass();//4、获取实现的接口Class<?>[] interfaces = userClass.getInterfaces();//还有其他,查看API}catch (Exception e) {e.printStackTrace();}
}