反射是框架设计的灵魂
(使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码))
一、反射概念
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法。所以先要获取到每一个字节码文件对应的Class类型的对象。
反射就是把java类中的各种成分映射成一个个的Java对象。
熟悉一下加载的时候:Class对象的由来是将class文件读入内存,并为之创建一个Class对象。
二、Class类
图是Class类的api(图片来自于Java基础之—反射(非常重要))
从图中可以得出以下几点:
- Class 类的实例对象表示正在运行的 Java 应用程序中的类和接口。也就是jvm中有很多的实例,每个类都有唯一的Class对象。
- Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass 方法自动构造的。也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了。
- Class 对象用于提供类本身的信息,如图下
三、反射的操作
假设我们现在有一个User类
package com.javabasics.reflect;
public class User {
private String name;
protected String sex;
public static String address;
Integer age;
private User(String name, String sex) {
this.name = name;
this.sex = sex;
}
public User() {}
public User(String name) {this.name = name;}
private void jump() {
System.out.println("jump.....");
}
public static void say(String say) {
System.out.println("say....." + say);
}
}
3.1基本操作
1.获取类对象的四种方式
// 获取类对象的四种方式
Class<User> clazz1 = User.class;
Class<?> clazz2 = Class.forName("com.javabasics.reflect.User");
Class<? extends User> clazz3 = new User().getClass();
//ReflectDemo类 为运行main方法的类
Class<?> clazz4 = ReflectDemo.class.getClassLoader().loadClass("com.javabasics.reflect.User");
注意:在一个JVM中,一种类,只会有一个类对象存在。所以以上四种方式取出来的类对象,都是一样。(此处准确是在ClassLoader下,只有一个类对象)
第一种:需要导入类的包,依赖太强,不导包就抛编译错误。
第二种(常用):一个字符串可以传入也可写在配置文件中等多种方法。
第三种:对象都有了还要反射干什么。
第四种:类似第二种
2.基本信息操作
// 获取类的相关结构
System.out.println(clazz1.getModifiers()); // 获取类的修饰符
System.out.println(clazz1.getPackage());
System.out.println(clazz1.getName());
System.out.println(clazz1.getSuperclass());
System.out.println(clazz1.getClassLoader());
System.out.println(clazz1.getSimpleName());
System.out.println(clazz1.getInterfaces().length); // 获取类似实现的所有的接口
System.out.println(clazz1.getAnnotations().length);
类修饰符获取的值 如图下
注意:.getModifiers()返回值为,每一个修饰符 占二进制一个位数,最后通过各个位数求和,返回最后结果。
3.字段的操作
public static void main(String[] args) throws Exception {
Class<User> userClass = User.class;
// 获取User对象
User user = userClass.newInstance();
/** 获取类型中定义的字段 共有的字段以及父类中共有的字段*/
Field[] fields1 = userClass.getFields();
for(Field f:fields1){
System.out.println(f.getModifiers() + " " + f.getName());
}
System.out.println("--------------------");
/** 可以获取私有的字段 只能够获取当前类中*/
Field[] fields2 = userClass.getDeclaredFields();
for(Field f:fields2){
System.out.println(f.getModifiers() + " " + f.getName());
}
// 获取name字段对应的Field
Field nameField = userClass.getDeclaredField("name");
// 如果要修改私有属性信息那么我们需要放开权限
nameField.setAccessible(true);
nameField.set(user,"张三");
System.out.println(user.getName());
// 如果对静态属性赋值
Field addressField = userClass.getDeclaredField("address");
addressField.set(null,"湖南长沙");
System.out.println(User.address);
}
4.构造的操作
public static void main(String[] args) throws Exception {
Class<User> userClass = User.class;
/** 获取所有的公有的构造器*/
Constructor<?>[] constructors = userClass.getConstructors();
for (Constructor c:constructors){
System.out.println(c.getModifiers() + " " + c.getName() );
}
System.out.println("************************");
/** 获取所有的构造器*/
Constructor<?>[] declaredConstructors = userClass.getDeclaredConstructors();
for (Constructor c:declaredConstructors){
System.out.println(c.getModifiers() + " " + c.getName() );
}
// 1.直接通过newInstance创建对象
User user = userClass.newInstance();
System.out.println(user);
// 2.获取对应的Construcator对象获取实例
Constructor<User> declaredConstructor = userClass.getDeclaredConstructor(String.class, String.class);
// 私有的构造器调用需要放开权限
declaredConstructor.setAccessible(true);
System.out.println(declaredConstructor.newInstance("张三","男"));
}
5.方法的操作
public static void main(String[] args) throws Exception {
User user = new User();
Class<User> userClass = User.class;
/** 可以获取当前类及其父类中的所有的共有的方法 */
Method[] methods = userClass.getMethods();
for (Method m : methods) {
System.out.println(m.getModifiers() + " " + m.getName());
}
System.out.println("**********");
/** 获取本类中的所有的方法 包括私有的 */
Method[] declaredMethods = userClass.getDeclaredMethods();
for (Method m : declaredMethods) {
System.out.println(m.getModifiers() + " " + m.getName());
}
Method jumpMethod = userClass.getDeclaredMethod("jump");
// 放开私有方法的调用
jumpMethod.setAccessible(true);
jumpMethod.invoke(user);
Method sayMethod = userClass.getDeclaredMethod("say", String.class);
// 静态方法调用
sayMethod.invoke(null, "我很烦java");
}
3.2单例的漏洞
产生的原因是:反射可以调用私有的构造器造成的
public class PersonSingle {
private static PersonSingle instance;
private PersonSingle(){
if(instance != null){
throw new RuntimeException("实例已经存在了,不允许再创建...");
}
}
public static PersonSingle getInstance(){
if(instance == null){
instance = new PersonSingle();
}
return instance;
}
}
解决方案:在私有构造其中加入逻辑判断结合RuntimeException
处理即可
public static void main(String[] args) throws Exception {
ReflectSimpleDemo p1 = ReflectSimpleDemo.getInstance();
ReflectSimpleDemo p2 = ReflectSimpleDemo.getInstance();
ReflectSimpleDemo p3 = ReflectSimpleDemo.getInstance();
System.out.println(p1);
System.out.println(p2);
System.out.println(p3);
System.out.println(ReflectSimpleDemo.getInstance());
// 通过反射获取实例
Constructor<? extends ReflectSimpleDemo> declaredConstructor = p1.getClass().getDeclaredConstructor();
declaredConstructor.setAccessible(true);
System.out.println(declaredConstructor.newInstance());
}
反射的使用场景:
- jdbc封装
- SpringIOC
- JdbcTemplate
- Mybatis
…