反射的艺术:
概念:类字节码文件是在硬盘上存储的,是一个个的.class文件。我们在new一个对象时,JVM会先把字节码文件的信息读出来放到内存中,第二次用时,就不用在加载了,而是直接使用之前缓存的这个字节码信息。
字节码的信息包括:类名、声明的方法、声明的字段等信息。在Java中“万物皆对象”,这些信息当然也需要封装一个对象,这就是Class类、Method类、Field类。
通过Class类、Method类、Field类等等类可以得到这个类型的一些信息,甚至可以不用new关键字就创建一个实例,可以执行一个对象中的方法,设置或获取字段的值,这就是反射技术。
--Class类:
获取Class类的三种方式:
Java中有一个Class类用于代表某一个类被jvm编译后的字节码。
- forName().forName方法用于加载某个类的字节码到内存中,并使用class对象进行封装 推荐使用,后端很多封装框架都利用此原理+代理模式封装
- 类名.Class();
- 对象.getClass();
package com.kk.reflect; public class Person { private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Person(String name, Integer age) { super(); this.name = name; this.age = age; } public Person() { super(); // TODO Auto-generated constructor stub } public void eat(){ System.out.println("eat doing........"); } public void eat(String hello){ System.out.println("eat doing........"+hello); } public static void play(){ System.out.println("play static doing........"); } private void movie(String name){ System.out.println("movie private doing........"+name); }
public String publicId;
public String publicType;
public String getPublicType() {
return publicType;
}
public void setPublicType(String publicType) {
this.publicType = publicType;
}
public String getPublicId() {
return publicId;
}
public void setPublicId(String publicId) {
this.publicId = publicId;
} }/** * 加载类的字节码的3种方式 * @throws Exception * */ public void test1() throws Exception { // 方式一 Class clazz1 = Class.forName("cn.kk.reflect.Person"); // 方式二 Class clazz2 = Person.class; // 方式三 Person p1 = new Person(); Class clazz3 = p1.getClass(); }
通过Class类获取类型的信息:
- getName()类的名称(全名,全路径名)
- getSimpleName()类的的简单名称(不带包名)
- getModifiers(); 类的的修饰符
- newInstance()//创建对象无参数构造创建对象
- Constructor<T> getConstructor(Class<?>... parameterTypes)//获取指定参数的构造器对象,并可以使用Constructor对象创建一个实例
/** * 通过Class对象获取类的一些信息 * * @throws Exception * */ private static void test2() throws Exception { Class clazz1 = Class.forName("cn.kk.reflect.Person"); // 获取类的名称 String name = clazz1.getName(); System.out.println(name); //cn.kk.reflect.Person // 获取类的简单名称 System.out.println(clazz1.getSimpleName()); // Person // 获取类的修饰符 int modifiers = clazz1.getModifiers(); System.out.println(modifiers); // 构建对象(默认调用无参数构造.) Object ins = clazz1.newInstance(); Person p = (Person) ins; System.out.println(p); //cn.kk.reflect.Person@15db9742 // 获取指定参数的构造函数 Constructor<?> con = clazz1.getConstructor(String.class, Integer.class); // 使用Constructor创建对象. Object p1 = con.newInstance("jack", 28); System.out.println(((Person) p1).getName()); }
通过Class类获取类型的信息:
- 获取公共方法包括继承的父类的方法 getMethods()返回一个数组,元素类型是Method;
- 获取指定参数的公共方法 getMethod("setName", String.class);
- 获得所有的方法,包括私有 Method[] getDeclaredMethods();
- 获得指定参数的方法,包括私有 Method getDeclaredMethod(String name, Class<?>... parameterTypes);
-
/**
* 获取公有方法.
* @throws Exception
* */
private static void test3() throws Exception {
Class clazz1 = Class.forName("com.kk.reflect.Person"); // 1.获取非私用方法(包括父类继承的方法) Method[] methods = clazz1.getMethods(); System.out.println(methods.length); for (Method m : methods) { System.out.println(m.getName()); if ("eat".equals(m.getName())) { //Method的invoke()方法的使用 m.invoke(clazz1.newInstance(), null); } } }
/** * 获取指定方法签名的方法 * * @throws Exception * */ private static void test4() throws Exception { Class clazz1 = Class.forName("com.kk.reflect.Person"); // 获取指定名称的函数 Method method1 = clazz1.getMethod("eat", null); method1.invoke(new Person(), null); }
/** * 获取指定方法名且有参数的方法 * * @throws Exception * */ private static void test5() throws Exception { /** * public void eat(String hello){ System.out.println("eat doing........"+hello); } */ Class clazz1 = Class.forName("com.kk.reflect.Person"); Method method = clazz1.getMethod("eat",String.class); method.invoke(new Person(), "joker"); } }
/** * 获取指定方法名,参数列表为空的方法. * * @throws Exception * */ private static void test4() throws Exception { /** * public void eat(){ System.out.println("eat doing........"); } */ Class clazz1 = Class.forName("com.kk.reflect.Person"); // 获取指定名称的函数 Method method1 = clazz1.getMethod("eat", null); method1.invoke(new Person(), null); }
/** * 反射静态方法 * @throws Exception * */ private static void test7() throws Exception { /** * public static void play(){ System.out.println("play static doing........"); } */ Class clazz1 = Class.forName("com.kk.reflect.Person"); Method method = clazz1.getMethod("play", null); method.invoke(null, null); }
/** * 访问私有方法 暴力反射 * @throws Exception * */ private static void test6() throws Exception { /** * private void movie(String name){ System.out.println("movie private doing........"+name); } */ Class clazz1 = Class.forName("com.kk.reflect.Person"); Method method = clazz1.getDeclaredMethod("movie", String.class); method.setAccessible(true); method.invoke(new Person(), "苍老师"); }
通过Class类获取类型中的字段的信息
- 获取公共字段 Field[] getFields()
- 获取指定参数的公共字段 Field getField(String name)
- 获取所有的字段 Field[] getDeclaredFields()
- 获取指定参数的字段,包括私用 Field getDeclaredField(String name)
/** * 获取公有的字段 实体类中添加共有属性级getsetter方法 public String publicId; public String publicType; * */ private static void test8() throws Exception { Person p = new Person(); Class clazz1 = Class.forName("com.kk.reflect.Person"); //获取公共字段 Field[] fields = clazz1.getFields(); System.out.println(fields.length); for (Field f : fields) { System.out.println(f.getName()); if ("publicId".equals(f.getName())) { System.out.println(f.getType().getName()); f.set(p, "1922"); } if ("publicType".equals(f.getName())) { System.out.println(f.getType().getName()); f.set(p, "typeZS"); } } System.out.println(p.getPublicId()); System.out.println(p.getPublicType()); }
/** * 获取私有的字段 * @throws Exception * */ private static void test9() throws Exception { Person p = new Person(); Class clazz1 = Class.forName("com.kk.reflect.Person"); Field field = clazz1.getDeclaredField("age"); System.out.println(field.getName());
//私有方法不能直接访问,因为权限不够。非要访问,可以通过暴力的方式。 field.setAccessible(true);//一般很少用,因为私有就是隐藏起来,所以尽量不要访问。 field.set(p, 100); System.out.println(p.getAge()); } }补充Method的invoke()方法的使用
- 对带有指定参数的指定对象调用由此
Method
对象表示的底层方法。个别参数被自动解包,以便与基本形参相匹配,基本参数和引用参数都随需服从方法调用转换。 - 如果底层方法是静态的,那么可以忽略指定的
obj
参数。该参数可以为 null。 - 如果底层方法所需的形参数为 0,则所提供的
args
数组长度可以为 0 或 null。 - 如果底层方法是静态的,并且尚未初始化声明此方法的类,则会将其初始化。
- 如果方法正常完成,则将该方法返回的值返回给调用者;如果该值为基本类型,则首先适当地将其包装在对象中。但是,如果该值的类型为一组基本类型,则数组元素不 被包装在对象中;换句话说,将返回基本类型的数组。如果底层方法返回类型为 void,则该调用返回 null。
obj
- 从中调用底层方法的对象(简单的说就是调用谁的方法用谁的对象)args
- 用于方法调用的参数总结:对反射理解深入有利于对后端底层框架的深入了解,及源码分析,会让你明白java框架底层到底是怎么一回事,所以挺总要的,本文简单总结了一下知识点,言简意赅,不足之处望多指教。
- 2018年1月28日 星期天,我在西安,好多年没下过这么大的雪,在家写写博客记录下知识,希望能帮助到同我一样向往着诗和远方朋友!