Java反射机制

软件发布|下载排行|最新软件

当前位置:首页IT学院IT技术

Java反射机制

不想起床的小张   2022-05-28 我要评论

反射

反射就是把Java类中的各个成分映射成一个个的Java对象。即在运行状态中,对于任意一个类,都能够知道这个类的所以属性和方法;对于任意一个对象,都能调用它的任意一个方法和属性。这种动态获取信息及动态调用对象方法的功能叫Java的反射机制

每一个Java程序执行必须通过编译、加载、链接和初始化四个阶段

1.编译:将.java.文件编译成字节码.class文件

2.加载:查找并加载类的二进制数据

3.链接:

  • 验证:确保被加载类的正确性
  • 为类的静态变量分配内存,并将其初始化为默认值
  • 将类中的符号转换为直接引用

4.初始化:为类的静态变量赋予正确的初始值

什么是Class类

在Java中,每个class都有一个相应的Class对象。也就是说,当我们编写一个类,编译完成后,在生成的.class文件中,就会产生一个Class对象,用于表示这个类的类型信息

也就是说,无论你是什么对象,总会有有一个隐藏的Class对象与你相对应,而Class的实例表示正在运行的 Java 应用程序中的类和接口。借此,实现了我们Java的反射机制。

获取Class实例的三种方式

实例化对象调用getClass()方法

使用Class类的静态方法forName(),用类的名字获取一个Class实例

运用.class的方式来获取Class实例,对于基本数据类型的封装类,还可以采用.TYPE来获取相对应的基本数据类型的Class实例

用代码来看一看:

public class reflect {

    public static void main(String[] args) throws ClassNotFoundException {

        Apple apple = new Apple();

        // 使用对象的getClass()方法
        Class a1 = apple.getClass();

        // 使用Class类的静态方法forName()
        Class a2 = Class.forName("p1.apple");

        // 运用.class的方式来获取Class实例
        Class a3 = Apple.class;

        System.out.printf("a1: %s\na2: %s\na3: %s", a1, a2, a3);

    }

}

class Apple {

    private Integer weight;

    private String color;
    
}

打印结果:

a1: class p1.apple
a2: class p1.apple
a3: class p1.apple
进程已结束,退出代码0

通过反射创建类对象

通过反射创建类对象主要有两种方式:通过 Class 对象的 newInstance() 方法、通过 Constructor 对象的 newInstance() 方法。

public class reflect {

    public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {

        // 通过 Class 对象的 newInstance() 方法
        Class temp1 = Apple.class;
        Apple apple1 = (Apple) temp1.newInstance();
        
        // 通过 Constructor 对象的 newInstance() 方法
        Class temp2 = Apple.class;
        Constructor constructor1 = temp2.getConstructor();
        Apple apple2 = (Apple)constructor1.newInstance();

        // 通过 Constructor 对象创建类对象可以选择特定构造方法,而通过 Class 对象则只能使用默认的无参数构造方法。
        Class temp3 = Apple.class;
        Constructor constructor2 = temp3.getConstructor(Integer.class,String.class);
        Apple apple = (Apple)constructor2.newInstance(2, "Red");
        System.out.println(apple);

    }

}

class Apple {

    private Integer weight;

    private String color;

    // 无参构造器
    public Apple() {
        System.out.println("我是无参构造!");
    }

    // 有参构造器
    public Apple(Integer weight,String color) {
        this.weight = weight;
        this.color = color;
    }

    // 重写方法 方便打印显示对象内容
    @Override
    public String toString() {
        return "Apple{" +
                "weight=" + weight +
                ", color='" + color + '\'' +
                '}';
    }

}

打印结果:

我是无参构造!
我是无参构造!
Apple{weight=2, color='Red'}

进程已结束,退出代码0

通过反射获取类属性、方法、构造器

public class reflect {

    public static void main(String[] args) throws NoSuchFieldException {

        // 返回一个类中所有可访问的公共字段,包括该类的公共字段和其继承的类的公共字段
        Field[] fields1 = Apple.class.getFields();
        System.out.println("getFields结果");
        Arrays.stream(fields1).forEach(System.out::println);

        // 返回一个类中全部字段,但只包括该类的字段
        Field[] fields2 = Apple.class.getDeclaredFields();
        System.out.println("getDeclaredFields结果");
        Arrays.stream(fields2).forEach(System.out::println);

        // 根据字段名返回一个公开字段
        Field field1 = Apple.class.getField("noThing");
        System.out.println("getField结果");
        System.out.println(field1);

        // 根据字段名返回一个字段
        Field field2 = Apple.class.getDeclaredField("color");
        System.out.println("getDeclaredField结果");
        System.out.println(field2);

        // 同Field Method也有四种获取方式
        // 这里举其中一个例子
        Method[] methods = Apple.class.getDeclaredMethods();
        System.out.println("getDeclaredMethods结果");
        Arrays.stream(methods).forEach(System.out::println);

        // 同上 举一个获取构造器的例子
        Constructor[] constructors = Apple.class.getDeclaredConstructors();
        System.out.println("getDeclaredConstructors结果");
        Arrays.stream(constructors).forEach(System.out::println);

    }

}

class Apple {

    private Integer weight;

    private String color;

    public String noThing;

    public Apple() {

    }

    public Apple(Integer weight, String color) {
        this.weight = weight;
        this.color = color;
    }

    @Override
    public String toString() {
        return "Apple{" +
                "weight=" + weight +
                ", color='" + color + '\'' +
                '}';
    }

}

打印结果:

getFields结果
public java.lang.String p1.Apple.noThing
getDeclaredFields结果
private java.lang.Integer p1.Apple.weight
private java.lang.String p1.Apple.color
public java.lang.String p1.Apple.noThing
getField结果
public java.lang.String p1.Apple.noThing
getDeclaredField结果
private java.lang.String p1.Apple.color
getDeclaredMethods结果
public java.lang.String p1.Apple.toString()
getDeclaredConstructors结果
public p1.Apple()
public p1.Apple(java.lang.Integer,java.lang.String)

更改访问权限和实例赋值

首先,通过field.setAccessible()可更改属性的访问权限

public class reflect {

    public static void main(String[] args) throws NoSuchFieldException {

        // 实例化一个Apple
        Apple apple = new Apple();

        // 获取所有字段 并统一设定为公有属性
        Field[] fields = Apple.class.getDeclaredFields();
        Arrays.stream(fields).forEach( field -> {
            field.setAccessible(true);
            // 打印结果
            System.out.println(field);
            try {
                if (field.getType() == Integer.class) {
                    field.set(apple, 5);
                } else if (field.getType() == String.class) {
                    field.set(apple, "Red");
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        });
        // 查看apple结果
        System.out.println(apple);
      
    }

}

class Apple {

    private Integer weight;

    private String color;

    @Override
    public String toString() {
        return "Apple{" +
                "weight=" + weight +
                ", color='" + color + '\'' +
                '}';
    }

}

打印结果:

private java.lang.Integer p1.Apple.weight
private java.lang.String p1.Apple.color
Apple{weight=5, color='Red'}

进程已结束,退出代码0

通过源码文档和打印结果,可见setAccessable()方法并没有改变类字段的访问权限,而是作为一个标志,使得我们反射获取实例过程中可以对其进行操作

运用场景

在我看来,反射机制实际上就是上帝模式,如果说方法的调用是 Java 正确的打开方式,那反射机制就是上帝偷偷开的后门,只要存在对应的class,一切都能够被调用。

众所周知,语言有静态语言和动态语言两大分类,静态语言例如C/C++、Java、C#等,动态语言有Python、PHP、JavaScript等。为了让Java语言也有动态语言的特性,有了反射机制,解耦以及提高代码的灵活性。

反射在开发过中或许并不常见,可我们使用的框架工具底层都有反射的存在。动态代理设计模式、JDBC 的数据库的连接、Spring 框架的使用等都应用到了反射机制。

Copyright 2022 版权所有 软件发布 访问手机版

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 联系我们