Java的反射机制

本文深入介绍了Java反射机制,包括核心概念、意义、流程和实际应用。通过反射,程序能在运行时动态加载类、创建对象并调用其属性和方法,极大地增强了程序的灵活性。文中还展示了反射在构造方法、字段和方法访问上的具体用法,并讨论了防止暴力访问的方法。然而,反射可能导致性能下降、安全限制和程序健壮性问题,应当谨慎使用。
摘要由CSDN通过智能技术生成

反射简介

Java反射

(1)Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。

(2)Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。

反射的意义

  • 首先,反射机制极大的提高了程序的灵活性和扩展性,降低模块的耦合性,提高自身的适应能力。
  • 其次,通过反射机制可以让程序创建和控制任何类的对象,无需提前硬编码目标类。
  • 再次,使用反射机制能够在运行时构造一个类的对象、判断一个类所具有的成员变量和方法、调用一个对象的方法。
  • 最后,反射机制是构建框架技术的基础所在,使用反射可以避免将代码写死在框架中。

正是反射有以上的特征,所以它能动态编译和创建对象,极大的激发了编程语言的灵活性,强化了多态的特性,进一步提升了面向对象编程的抽象能力,因而受到编程界的青睐。

Java反射流程

反射实践

了解了那么多关于Java反射的概念,接下来通过代码层面感受一下反射的优势吧。

Java四种访问权限介绍

public > protected > default > private

基本用法

新建Fruit类

public class Fruit {
    // public > protected > default > private

    private Double price; // 私有(内部访问)

    String name; // 缺省默认(内部访问,同个包)

    protected String color; // 受保护(内部访问,同个包,子孙类)

    public String from; // 公共(内部访问,同个包,子孙,其他包)

    public String weight;

    public Fruit(){
        System.out.println("这是无参构造方法=====");
    }

    private Fruit(String name){
        this.name = name;
    }

    protected Fruit(String name,String color){
        this(name);
        this.color = color;
    }

    public Fruit(String name,String color,Double price){
        this(name,color);
        this.price = price;
    }

    Fruit(Double price){
        this.price = price;
        System.out.println("这是无参构造方法");
    }

    @Override
    public String toString() {
        return "Fruit{" +
                "name='" + name + '\'' +
                ", color='" + color + '\'' +
                ", price=" + price +
                ", from='" + from + '\'' +
                ", weight='" + weight + '\'' +
                '}';
    }

    private void show1(String name){
        System.out.println("私有方法,输入名称:"+name);
    }

    public void show2(){
        System.out.println("公共方法");
    }

    protected void show3(){
        System.out.println("保护方法");
    }

    void show4(){
        System.out.println("默认方法");
    }

}

测试反射

import org.junit.Test;

import java.lang.reflect.*;
import java.util.ArrayList;

/**
 * 测试反射
 */
public class ReflectTest {

    /**
     * 测试获取构造方法
     * @throws Exception
     */
    @Test
    public void test() throws Exception{
        Class fruit = Class.forName("reflect.Fruit");
        //2.获取所有公有构造方法
        System.out.println("**********************所有公有构造方法*********************************");
        Constructor[] constructors = fruit.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
        // 根据参数获取构造器
        Constructor constructor = fruit.getConstructor(String.class,String.class,Double.class);
        System.out.println(constructor);

        System.out.println("************所有的构造方法(包括:私有、受保护、默认、公有)***************");
        Constructor[] declaredConstructors = fruit.getDeclaredConstructors();
        for (Constructor declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor);
        }
        Constructor declaredConstructor = fruit.getDeclaredConstructor(String.class);
        System.out.println(declaredConstructor);

        System.out.println("*****************获取公有、无参的构造方法*******************************");
        Constructor constructor1 = fruit.getConstructor(null);
        System.out.println(constructor1);

        // 调用构造方法
        Object o = fruit.newInstance();
        System.out.println(o);

        System.out.println("******************获取私有构造方法,并调用*******************************");
        constructor = fruit.getDeclaredConstructor(String.class);
        constructor.setAccessible(true); // 暴力访问
        o = constructor.newInstance("apple");
        System.out.println(o);
    }

    /**
     * 测试获取字段
     * @throws Exception
     */
    @Test
    public void test1() throws Exception{
        // 获取Class对象
        Class fruit = Class.forName("reflect.Fruit");
        // 获取字段
        System.out.println("============获取所有公用字段===========");
        Field[] fields = fruit.getFields();
        for (Field field : fields) {
            System.out.println(field.getName());
        }
        // 根据名字获取指定
        Field weight = fruit.getField("weight");
        System.out.println(weight);
        System.out.println("============获取所有的字段(包括私有、受保护、默认的)===========");
        Field[] declaredFields = fruit.getDeclaredFields();
        for (Field field : declaredFields) {
            System.out.println(field.getName());
        }
        // 根据名字获取指定
        Field price = fruit.getDeclaredField("price");
        System.out.println(price);


        System.out.println("============获取私有字段并调用=============");
        // 获取对象
        Object o = fruit.getConstructor(null).newInstance();
        weight.set(o,"100kg");
        Fruit result = (Fruit) o;
        System.out.println(result.weight);

        price.setAccessible(true); // 暴力访问
        price.set(o,200.00);
        result = (Fruit) o;
        System.out.println(result);
    }

    /**
     * 测试获取方法
     * @throws ClassNotFoundException
     */
    @Test
    public void test2() throws Exception {
        Class fruit = Class.forName("reflect.Fruit");
        System.out.println("============获取所有公用方法===========");
        Method[] methods = fruit.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println("============获取所有的方法(包括私有、受保护、默认的)===========");
        methods = fruit.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        // 根据名称获取指定
        Method show1 = fruit.getDeclaredMethod("show1", String.class);
        System.out.println(show1);

        System.out.println("============反射获取类并且调用私有方法===========");
        // 获取私有构造器
        Constructor constructor = fruit.getDeclaredConstructor(String.class);
        // 设置暴力访问
        constructor.setAccessible(true);
        // 获取对象
        Object o = constructor.newInstance("apple");
        // 私有方法暴力访问
        show1.setAccessible(true);
        show1.invoke(o,"apple");
    }

    /**
     * 测试静态方法
     * @throws Exception
     */
    @Test
    public void test3() throws Exception{
        Class fruit = Class.forName("reflect.Fruit");
        Method main = fruit.getMethod("main", String[].class);
        //第一个参数,对象类型,因为方法是static静态的,所以为null可以,第二个为参数(无参数可不填)
        main.invoke(null,(Object)new String[]{"1","2","3"});
        Method show5 = fruit.getMethod("show5");
        show5.invoke(null);
    }

    /**
     * 利用反射创建数组
     * @throws Exception
     */
    @Test
    public void test4() throws Exception{
        Class<?> aClass = Class.forName("java.lang.String");
        Object instance = Array.newInstance(aClass, 20);
        Array.set(instance,0,"1");
        Array.set(instance,1,"2");
        Array.set(instance,2,"3");
        Array.set(instance,3,"4");
        // 强转对象
        String[] strings = (String[])instance;
        System.out.println(strings[0]);
        // 通过Array获取
        System.out.println(Array.get(instance,1));
    }

    /**
     * 通过反射越过泛型检查
     * 在一个String类型的数组里面添加Integer类型的数据
     * @throws Exception
     */
    @Test
    public void test5() throws Exception{
        ArrayList<String> list = new ArrayList<>();
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");

        Class aClass = list.getClass();
        Method method = aClass.getMethod("add", Object.class);
        method.invoke(list,100);
        for (Object s : list) {
            System.out.println(s);
        }
    }

}

如何防止暴力访问

Java的反射机制可以暴力访问类中的私有变量和私有方法,这违背了我们设计程序的初衷,也对程序的安全提出了一些质疑,如何保护类中的私有变量和私有方法呢,其实也有~~

设定SecurityManager。默认的情况下SecurityManager是空的。

另外有的人可能会想,你既然可以set,我也可以重新给set为null啊,注意一旦set之后,重新set会抛出异常。

// 防暴力访问
static{
    System.setSecurityManager(new SecurityManager());
}

总结

Java反射机制为我们带来了很多便利之处,特别是配合一些设计模式的时候更能显示他的便利,但凡事有利有弊,尽管反射机制带来了极大的灵活性及方便性,但反射也有缺点。反射机制的功能非常强大,但不能滥用。在能不使用反射完成时,尽量不要使用,原因有以下几点:

1、性能问题。

Java反射机制中包含了一些动态类型,所以Java虚拟机不能够对这些动态代码进行优化。因此,反射操作的效率要比正常操作效率低很多。我们应该避免在对性能要求很高的程序或经常被执行的代码中使用反射。而且,如何使用反射决定了性能的高低。如果它作为程序中较少运行的部分,性能将不会成为一个问题。

2、安全限制。

使用反射通常需要程序的运行没有安全方面的限制。如果一个程序对安全性提出要求,则最好不要使用反射。

3、程序健壮性。

反射允许代码执行一些通常不被允许的操作,所以使用反射有可能会导致意想不到的后果。反射代码破坏了Java程序结构的抽象性,所以当程序运行的平台发生变化的时候,由于抽象的逻辑结构不能被识别,代码产生的效果与之前会产生差异。

希望以上关于Java反射的介绍对大家有帮助~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值