Java基础-反射

反射是框架设计的灵魂

(使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码))

一、反射概念

 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
 要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法。所以先要获取到每一个字节码文件对应的Class类型的对象。
反射就是把java类中的各种成分映射成一个个的Java对象。
熟悉一下加载的时候:Class对象的由来是将class文件读入内存,并为之创建一个Class对象。

二、Class类

图是Class类的api(图片来自于Java基础之—反射(非常重要))

在这里插入图片描述
从图中可以得出以下几点:

  1. Class 类的实例对象表示正在运行的 Java 应用程序中的类和接口。也就是jvm中有很多的实例,每个类都有唯一的Class对象。
  2. Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass 方法自动构造的。也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了。
  3. 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());
}

反射的使用场景:

  1. jdbc封装
  2. SpringIOC
  3. JdbcTemplate
  4. Mybatis
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值