简述反射机制
- 2019 年 10 月 3 日
- 筆記
一,什么是反射
1.1,反射:所谓框架的灵魂
框架:半成品软件,可以在框架的基础上进行软件开发,简化代码。
反射:将类的各个组成部分封装为其他对象,反射机制。
简单来说反射就是在程序运行期间,动态的获取类或者对象中的属性。
什么是动态获取。
反射的原理就是通过类的字节码文件(class文件)反向获取该类或者对象中的属性,既然是通过字节码获取,这就需要JVM的操作了。下面请看API文档的说明:
在上图最后一句话中,文档说的很清楚,反射是在加载类时由JVM进行操作。
1.2,动静态编译
- 静态编译:在编译期就确定类或者方法的属性,有点一次到位的意思。
- 动态编译:在运行期确定类或者方法的属性,好比什么时候用就什么时候编译。
但是这两种编译方式有什么区别,先说静态编译吧。我想大家都遇到过项目需求频繁变更的情况,可能是更改需求,可能是添加新的需求。对于静态编译,因为这是一次性编译,对于确定的代码是不能更改的,除非下线,更改,测试,再重新上线,显然这是不妥的。
因此就需要动态编译,即在程序运行期间也可以进行相应的操作,一切操作方式都是灵活的,所以说反射对于程序是多么重要。
1.3,优缺点
先来说说反射的优点:
1,可以在程序运行的过程中,操作这些对象。
2,可以解耦,提高程序的可扩展性。
缺点:
1,因为是JVM操作,所以对于性能来说会有所下降。
2,容易对程序源码造成一定的混乱。
1.4,反射图解
**注意:同一个字节码文件(*.class)在程序运行过程中,只会被加载一次。**
1.5,反射获取方式
获取Class对象的方式:
1,Class.forName("全类名"):将字节码文件加载进内存,返回class对象
多用于配置文件中,将类名定义在配置文件中,读取文件并加载类。
2,类名.class:通过类名的属性class获取。
多用于参数的构造。
3,对象.getClass():该方法定义在Object中
多用于对象的字节码获取。
public class ReflectDemo { public static void main(String[] args) throws ClassNotFoundException { // Class.forName(""); Class c1 = Class.forName("com.api.reflect.User"); System.out.println(c1); // 类名.class Class<User> c2 = User.class; System.out.println(c2); // .getClass User user = new User(); Class c3 = user.getClass(); System.out.println(c3); System.out.println(c1 == c2); System.out.println(c1 == c3); System.out.println(c2 == c3); } }
运行结果:
分析:
通过以上三种反射方式都可以获得实例对象,同样也证明三个对象是相同的,也就是对象只会被创建一次。
二,反射常用方法
2.1,成员变量
public Field getField(String name)
:获取指定名称的成员变量(public)。public Field[] getFields()
:获取全部成员变量(public)。public Field getDeclaredField(String name)
:不考虑修饰符。public Field[] getDeclaredFields()
:不考虑修饰符。
创建一个User对象。
public class User implements Serializable { public String name; protected Integer age; Integer sex; private String phone; public User() { } public User(String name, Integer age, Integer sex, String phone) { this.name = name; this.age = age; this.sex = sex; this.phone = phone; } private User(String name, Integer age){ this.name = name; this.age = 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 Integer getSex() { return sex; } public void setSex(Integer sex) { this.sex = sex; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } @Override public String toString() { return "User{" + "name='" + name + ''' + ", age=" + age + ", sex=" + sex + ", phone='" + phone + ''' + '}'; } public void run() { System.out.println("跑步..."); } private void eat(String username) { System.out.println(username + "正在吃饭..."); } }
public class ReflectDemo2 { public static void main(String[] args) throws Exception { // 1,通过Class.forName方式获取User对象 Class aClass = Class.forName("com.api.reflect.User"); // 获取public修饰的成员变量 Field[] fields = aClass.getFields(); for (Field field : fields) { System.out.println("1,public修饰的成员变量--->" + field); } // 2,获取指定成员变量名称 Field name = aClass.getField("name"); System.out.println("2,指定成员变量名称--->" + name); // 3,获取全部的成员变量,忽略修饰符 Field[] declaredFields = aClass.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println("3,不考虑修饰符,获取全部成员变量--->" + declaredField); } // 4,获取指定成员变量 Field phone = aClass.getDeclaredField("phone"); // 获取访问权限,暴力反射 phone.setAccessible(true); System.out.println("4,暴力反射--->" + phone); } }
以上代码运行结果:
2.2,构造方法
public Constructor<T> getConstructor(Class<?>... parameterTypes)
public Constructor<?>[] getConstructors()
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
public Constructor<?>[] getDeclaredConstructors()
public class ReflectDemo3 { public static void main(String[] args) throws Exception { // 通过Class.forName方式获取User对象 Class aClass = Class.forName("com.api.reflect.User"); // 1,获取指定构造方法(public),参数是可变参数 Constructor constructor = aClass.getConstructor(String.class, Integer.class, Integer.class, String.class); // 实例化对象 Object user = constructor.newInstance("张三", 20, 1, "123456"); System.out.println(user); // 2,获取全部构造方法(public) Constructor[] constructors = aClass.getConstructors(); for (Constructor constructor1 : constructors) { System.out.println(constructor1); } // 3,不考虑修饰符,获取指定构造方法 Constructor declaredConstructor = aClass.getDeclaredConstructor(String.class, Integer.class); // 获取权限 declaredConstructor.setAccessible(true); Object declaredUser = declaredConstructor.newInstance("李四", 21); System.out.println("不考虑修饰符--->" + declaredUser); // 4,获取全部构造方法 Constructor[] declaredConstructors = aClass.getDeclaredConstructors(); for (Constructor declaredConstructor1 : declaredConstructors) { System.out.println("不考虑修饰符--->" + declaredConstructor1); } } }
以上代码运行结果:
2.3,成员方法
public Method getMethod(String name,Class<?>... parameterTypes)
public Method[] getMethods()
public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
public Method[] getDeclaredMethods()
public class ReflectMethod { public static void main(String[] args) throws Exception { // 通过Class.forName方式获取User对象 Class aClass = Class.forName("com.api.reflect.User"); // 1,获取指定成员方法,public修饰 Method methodRun = aClass.getMethod("run"); // 实例化User,并调用invoke()执行方法 Object user = aClass.newInstance(); methodRun.invoke(user); // 2,获取全部成员方法,public修饰 Method[] methods = aClass.getMethods(); for (Method method : methods) { System.out.println(method); } // 3,获取私有成员方法 Method eat = aClass.getDeclaredMethod("eat", String.class); // 获取权限 eat.setAccessible(true); // 执行方法 eat.invoke(user,"小李"); } }
执行结果为:
也许你有疑惑,哪里来这么多的方法。
请详细看除了User对对象中的属性构造方法外,还有Object类中的方法。如下:
这是为什么,请看API文档解释说明:
包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口。
Method[] declaredMethods = c1.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.println(declaredMethod); }
三,总结
关于反射中的常用方法就总结到此,反射的用处还是很多的,比如Spring中IOC,DI都是利用反射机制实现的,当然这些会在另一篇博客中总结出来。
以上内容均是自主总结,如有不适之处欢迎留言指正。
感谢阅读!