Java 基础知识-反射

发布时间:2024年01月18日

大家好我是苏麟 , 今天聊聊反射 .

反射

?专业的解释:

?反射允许对封装类的字段,方法和构造函数的信息进行编程访问

?是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法

? 对于任意一个对象,都能够调用它的任意属性和方法

? 这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制

通俗的理解:

  • 利用反射创建的对象可以无视修饰符调用类里面的内容

  • 可以跟配置文件结合起来使用,把要创建的对象信息和方法写在配置文件中。

    读取到什么类,就创建什么类的对象

    读取到什么方法,就调用什么方法

    此时当需求变更的时候不需要修改代码,只要修改配置文件即可。

反射涉及到四个核心类:

  • java.lang.Class.java:类对象;
  • java.lang.reflect.Constructor.java:类的构造器对象;
  • java.lang.reflect.Method.java:类的方法对象;
  • java.lang.reflect.Field.java:类的属性对象;

学什么?

反射都是从class字节码文件中获取的内容。

  • 如何获取class字节码文件的对象
  • 利用反射如何获取构造方法(创建对象)
  • 利用反射如何获取成员变量(赋值,获取值)
  • 利用反射如何获取成员方法(运行)

获取字节码文件对象的三种方式

        //1.Class这个类里面的静态方法forName
        //Class.forName("类的全类名"): 全类名 = 包名 + 类名
        Class user = Class.forName("org.example.User");
        //源代码阶段获取 --- 先把User加载到内存中,再获取字节码文件的对象
        //clazz 就表示User这个类的字节码文件对象。
        //就是当User.class这个文件加载到内存之后,产生的字节码文件对象
        //2.通过class属性获取
        //类名.class
        Class userClass = User.class;
        //因为class文件在硬盘中是唯一的,所以,当这个文件加载到内存之后产生的对象也是唯一的
        System.out.println(user == userClass);//true
        //3.通过Student对象获取字节码文件对象
        User u = new User();
        Class us = u.getClass();
        System.out.println(user == userClass);//true
        System.out.println(userClass == us);//true

哪些类型可以获取class

public static void main(String[] args) {
    String s = new String("1");
    //继承
    Class aClass = s.getClass();

    //.class
    Class stringClass = String.class;

    Class<Integer> integerClass = int.class;

    Class<int[]> aClass1 = int[].class;

    Class<Void> voidClass = void.class;

    Class<Runnable> runnableClass = Runnable.class;
}

?字节码文件和字节码文件对象

java文件:就是我们自己编写的java代码。

字节码文件:就是通过java文件编译之后的class文件(是在硬盘上真实存在的,用眼睛能看到的)

字节码文件对象:当class文件加载到内存之后,虚拟机自动创建出来的对象。

? 这个对象里面至少包含了:构造方法,成员变量,成员方法。

而我们的反射获取的是什么?字节码文件对象,这个对象在内存中是唯一的。

创建对象

一般情况下我们通过反射创建类对象主要有两种方式:

  • 通过 Class 对象的 newInstance() 方法
  • 通过 Constructor 对象的 newInstance() 方法
// 通过 Class 对象的 newInstance() 方法。       
Class user = Class.forName("org.example.User");
User newUser = (User) user.newInstance();
//通过 Constructor 对象的 newInstance() 方法
Class user = Class.forName("org.example.User");
User newUser = (User) user.getConstructor().newInstance();

通过 Constructor 对象创建类对象可以选择特定构造方法,而通过 Class 对象则只能使用默认的无参数构造方法

API 可以看到 Class 有很多方法:

  • getName():获得类的完整名字。
  • getFields():获得类的public类型的属性。
  • getDeclaredFields():获得类的所有属性。包括private 声明的和继承类
  • getMethods():获得类的public类型的方法。
  • getDeclaredMethods():获得类的所有方法。包括private 声明的和继承类
  • getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型。
  • getDeclaredConstructors():返回类中所有的构造方法。
  • getConstructors():获得类的public类型的构造方法。
  • getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型。
  • newInstance():通过类的不带参数的构造方法创建这个类的一个对象。
  • getModifiers():获取属性的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】
  • getSuperclass():返回调用类的父类。
  • getInterfaces():返回调用类实现的接口集合。

入门案例

反编译一个类

package org.example;

/**
 * @className: User
 * @author: SL 苏麟
 **/
public class User {
    private String name;


    public User() {
    }

    public User(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    public String toString() {
        return "User{name = " + name + "}";
    }
}
 @Test
    public void testUser() throws Exception {
        StringBuffer stringBuffer = new StringBuffer();

        Class<?> userClass = Class.forName("org.example.User");

        stringBuffer.append(Modifier.toString(userClass.getModifiers()) + " class " + userClass.getSimpleName() + "{");


        Constructor<?>[] constructors = userClass.getDeclaredConstructors();
        for (Constructor<?> constructor : constructors) {
            stringBuffer.append("\n\t" + Modifier.toString(constructor.getModifiers()) + " " + "(");
            Class<?>[] parameterTypes = constructor.getParameterTypes();
            int flag = 0;
            for (Class<?> parameterType : parameterTypes) {
                StringBuffer buffer = flag == 0 ? stringBuffer.append(parameterType.getSimpleName()) : stringBuffer.append(" ," + parameterType.getSimpleName());
                flag++;
            }
            stringBuffer.append(")" + "\n");
        }


        Field[] fields = userClass.getDeclaredFields();
        for (Field field : fields) {
            stringBuffer.append("\n\t" + Modifier.toString(field.getModifiers()) + " " + field.getType().getSimpleName() + " " + field.getName() + ";\n");
        }


        Method[] methods = userClass.getDeclaredMethods();
        for (Method method : methods) {
            stringBuffer.append("\n\t" + Modifier.toString(method.getModifiers()) + " " + method.getReturnType().getSimpleName() + " " + method.getName() + "{}\n");
        }


        stringBuffer.append("}");
        System.out.println(stringBuffer);
    }

打破封装

Field类的方法

public void setAccessible(boolean flag) 默认false,设置为true为打破封装

    @Test
    public void testUserPrivate() throws Exception {

        Class user = Class.forName("org.example.User");
        Field nameField = user.getDeclaredField("name");
        //破坏封装
        nameField.setAccessible(true);

        User newUser = (User) user.getConstructor().newInstance();
        nameField.set(newUser, "sl");
        System.out.println(newUser);
    }

这期就到这里 , 下期见!

文章来源:https://blog.csdn.net/sytdsqzr/article/details/135630368
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。