首页 > 编程语言 >Java反射概述

Java反射概述

时间:2024-09-20 11:52:34浏览次数:3  
标签:反射 Java 对象 代理 Class 获取 概述 方法 public

反射

Java反射(Reflection)是一种允许程序在运行时动态地获取有关类、方法和属性的信息,甚至可以对它们进行操作的机制。通过反射,程序可以在编译时并不知道一个类的具体信息的情况下,运行时获取该类的结构,并进行相应的操作。反射的核心是在运行时操作类和对象的元信息,这为开发提供了更大的灵活性。

1.Java反射的基本概念

反射是通过 Java 提供的一组类和接口来实现的,这些类和接口位于 java.lang.reflect 包下,主要包括以下几个核心类:

  1. Class类:表示正在运行的 Java 应用程序中的类或接口。每个类或接口都有一个与之关联的 Class 对象,可以通过它获取类的元数据。
  2. Constructor类:表示类的构造方法对象。通过反射,你可以调用一个类的构造方法,实例化该类。
  3. Field类:表示类的字段(成员变量)。通过反射,可以获取类的属性,并对属性进行读写操作。
  4. Method类:表示类的方法对象。可以通过反射调用一个类的具体方法。

2.反射的主要功能

  1. 在运行时获取类的结构信息:
    • 可以获取类的包、类名、父类、接口等信息。
    • 可以获取类的字段(属性)、方法、构造方法等。
  2. 动态创建类的实例:
    • 即使在编译时不知道具体类,也可以通过反射在运行时根据类名实例化对象。
  3. 动态调用方法:
    • 通过反射,可以在运行时调用对象的方法,甚至可以调用私有方法。
  4. 动态访问和修改字段:
    • 通过反射,可以访问类的属性(包括私有属性),并且可以修改它们的值。

3.反射的使用

1. 获取 Class 对象

Class 对象是反射的基础,Java 反射的所有操作都从 Class 对象开始。获取 Class 对象的方式有以下几种:

1.1 通过类名获取 Class 对象

这是在编译时就知道类名的常用方式。

Class<?> clazz = SomeClass.class;
1.2 通过对象获取 Class 对象

通过对象的 getClass() 方法可以获取该对象所属类的 Class 对象。

SomeClass obj = new SomeClass();
Class<?> clazz = obj.getClass();
1.3 通过 Class.forName() 获取 Class 对象

通过类的全限定名(带包名的类名)可以动态加载类。

Class<?> clazz = Class.forName("com.example.SomeClass");

2. 通过反射创建对象

使用 Constructor 类可以动态创建对象。可以通过 Class 对象获取类的构造方法,并使用它创建对象。

2.1 获取无参构造器并创建对象
Constructor<?> constructor = clazz.getConstructor();
Object obj = constructor.newInstance();
2.2 获取带参构造器并创建对象
// 获取带一个String参数的构造器
Constructor<?> constructor = clazz.getConstructor(String.class);
// 传入参数并创建对象
Object obj = constructor.newInstance("参数值");
2.3 处理私有构造器

如果构造方法是私有的,可以用getDeclaredConstructor()创建

并通过 setAccessible(true) 绕过访问控制。

//获取全部构造器,包括私有的
Constructor<?> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true); // 绕过访问权限
Object obj = constructor.newInstance();

3. 通过反射操作成员变量

使用 Field 类可以操作类的属性。可以获取类的属性,并对其进行读写。

3.1 获取成员变量
// 获取某个public字段
Field field = clazz.getField("fieldName");
3.2 访问和修改成员变量值

通过反射可以直接访问和修改类的字段值,即使是私有字段。

// 获取某个字段的值
Object value = field.get(obj);
// 修改字段的值
field.set(obj, "新值");
3.3 访问私有成员变量

如果字段是私有的,可以通过 setAccessible(true) 来访问它。

Field field = clazz.getDeclaredField("privateField");
field.setAccessible(true);  // 允许访问私有字段
Object value = field.get(obj);  // 获取字段值
field.set(obj, "新值");  // 修改字段值

4. 通过反射操作成员方法(Method)

使用 Method 类可以动态调用类的方法。可以获取类的方法对象,并在运行时调用它。

4.1 获取成员方法

通过 getMethod()getDeclaredMethod() 可以获取方法,前者只能获取 public 方法,而后者可以获取包括私有方法在内的所有方法。

// 获取public方法
Method method = clazz.getMethod("methodName", String.class);

// 获取私有方法
Method method = clazz.getDeclaredMethod("privateMethod", String.class);
method.setAccessible(true);  // 允许访问私有方法
4.2 调用成员方法

通过 Method.invoke() 可以调用对象的方法,参数为方法所属的对象和实际参数。

// 调用方法并传入参数
Object returnValue = method.invoke(obj, "参数值");
4.3 静态方法的调用

调用静态方法时,invoke() 方法的第一个参数传 null

Method staticMethod = clazz.getMethod("staticMethod", int.class);
Object returnValue = staticMethod.invoke(null, 123);

5. 通过反射操作数组

反射不仅可以操作类、字段、方法,还可以用于操作数组。

5.1 创建数组

可以通过 Array.newInstance() 方法动态创建数组。

// 创建一个包含10个String元素的数组
Object array = Array.newInstance(String.class, 10);
5.2 操作数组元素

可以使用反射方法访问和修改数组中的元素。

// 设置数组中的某个元素
Array.set(array, 0, "第一个元素");

// 获取数组中的某个元素
Object value = Array.get(array, 0);

6. 获取类的完整信息

反射还可以用于获取类的所有成员信息,如构造方法、成员变量和成员方法。

6.1 获取类的所有构造器
Constructor<?>[] constructors = clazz.getConstructors();  // 获取所有public构造器
Constructor<?>[] allConstructors = clazz.getDeclaredConstructors();  // 获取所有构造器(包括私有的)
6.2 获取类的所有成员变量
Field[] fields = clazz.getFields();  // 获取所有public成员变量
Field[] allFields = clazz.getDeclaredFields();  // 获取所有字段(包括私有的)
6.3 获取类的所有成员
//getMethods可以获取父类中public修饰的方法
Method[] methods = clazz.getMethods();  // 获取所有public方法

Method[] allMethods = clazz.getDeclaredMethods();  // 获取所有方法(包括私有的,不包括父类中的)

4. 动态代理(Dynamic Proxy)

反射是 Java 动态代理机制的基础。通过动态代理可以在运行时创建接口的代理类,拦截对接口方法的调用,并执行自定义逻辑。

1.动态代理三要素:

1.真正干活的对象

2.代理对象

3.利用代理调用方法

切记一点:代理可以增强或者拦截的方法都在接口中,接口需要写在newProxyInstance的第二个参数里。

2.代码实现:

public class Test {
    public static void main(String[] args) {
    /*
        需求:
            外面的人想要大明星唱一首歌
             1. 获取代理的对象
                代理对象 = ProxyUtil.createProxy(大明星的对象);
             2. 再调用代理的唱歌方法
                代理对象.唱歌的方法("只因你太美");
     */
        //1. 获取代理的对象
        BigStar bigStar = new BigStar("鸡哥");
        Star proxy = ProxyUtil.createProxy(bigStar);

        //2. 调用唱歌的方法
        String result = proxy.sing("只因你太美");
        System.out.println(result);
    }
}
/*
*
* 类的作用:
*       创建一个代理
*
* */
public class ProxyUtil {
    /*
    *
    * 方法的作用:
    *       给一个明星的对象,创建一个代理
    *
    *  形参:
    *       被代理的明星对象
    *
    *  返回值:
    *       给明星创建的代理
    *
    *
    *
    * 需求:
    *   外面的人想要大明星唱一首歌
    *   1. 获取代理的对象
    *      代理对象 = ProxyUtil.createProxy(大明星的对象);
    *   2. 再调用代理的唱歌方法
    *      代理对象.唱歌的方法("只因你太美");
    * */
    public static Star createProxy(BigStar bigStar){
       /* java.lang.reflect.Proxy类:提供了为对象产生代理对象的方法:

        public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
        参数一:用于指定用哪个类加载器,去加载生成的代理类
        参数二:指定接口,这些接口用于指定生成的代理长什么,也就是有哪些方法
        参数三:用来指定生成的代理对象要干什么事情*/
        Star star = (Star) Proxy.newProxyInstance(
                ProxyUtil.class.getClassLoader(),//参数一:用于指定用哪个类加载器,去加载生成的代理类
                new Class[]{Star.class},//参数二:指定接口,这些接口用于指定生成的代理长什么,也就是有哪些方法
                //参数三:用来指定生成的代理对象要干什么事情
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        /*
                        * 参数一:代理的对象
                        * 参数二:要运行的方法 sing
                        * 参数三:调用sing方法时,传递的实参
                        * */
                        if("sing".equals(method.getName())){
                            System.out.println("准备话筒,收钱");
                        }else if("dance".equals(method.getName())){
                            System.out.println("准备场地,收钱");
                        }
                        //去找大明星开始唱歌或者跳舞
                        //代码的表现形式:调用大明星里面唱歌或者跳舞的方法
                        return method.invoke(bigStar,args);
                    }
                }
        );
        return star;
    }
}
public interface Star {
    //我们可以把所有想要被代理的方法定义在接口当中
    //唱歌
    public abstract String sing(String name);
    //跳舞
    public abstract void dance();
}
public class BigStar implements Star {
    private String name;


    public BigStar() {
    }

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

    //唱歌
    @Override
    public String sing(String name){
        System.out.println(this.name + "正在唱" + name);
        return "谢谢";
    }

    //跳舞
    @Override
    public void dance(){
        System.out.println(this.name + "正在跳舞");
    }

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

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

    public String toString() {
        return "BigStar{name = " + name + "}";
    }
}

3.额外扩展

动态代理,还可以拦截方法

比如:

​ 在这个故事中,经济人作为代理,如果别人让邀请大明星去唱歌,打篮球,经纪人就增强功能。

​ 但是如果别人让大明星去扫厕所,经纪人就要拦截,不会去调用大明星的方法。

/*
* 类的作用:
*       创建一个代理
* */
public class ProxyUtil {
    public static Star createProxy(BigStar bigStar){
        public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
        Star star = (Star) Proxy.newProxyInstance(
                ProxyUtil.class.getClassLoader(),
                new Class[]{Star.class},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        if("cleanWC".equals(method.getName())){
                            System.out.println("拦截,不调用大明星的方法");
                            return null;
                        }
                        //如果是其他方法,正常执行
                        return method.invoke(bigStar,args);
                    }
                }
        );
        return star;
    }
}

标签:反射,Java,对象,代理,Class,获取,概述,方法,public
From: https://blog.csdn.net/qq_74181819/article/details/142335302

相关文章

  • java线程池闭坑指南
    专业在线打字练习网站-巧手打字通,只输出有价值的知识。一前言线程池作为初学者常感困惑的一个领域,本次“巧手打字通课堂”将深入剖析其中几个最为普遍的误区。为了更清晰地阐述这些知识点,让我们以一个具体定义的线程池为例来展开说明。如下:ThreadPoolExecutorexecutor=ne......
  • 【Java DTO、VO类型转换工具类,对象与对象之间赋值】全部一行代码搞定!爽
    对象转换工具一、modelmapper介绍二、安装(一)引入依赖(二)添加工具类三、使用示例(一)单个对象转换(一)简单转换(二)转换时对转换后的对象处理(二)转换时对转换后的对象、源对象处理(一)批量转换(一)简单转换(二)转换时对转换后的对象处理(二)转换时对转换后的对象、源对象处理一、m......
  • 代码随想录算法训练营第十六天 | Javascript | 力扣Leetcode | 回溯 | 77. 组合、216.
    目录前言简介题目链接:77.组合题目链接:216.组合总和3题目链接:17.电话号码的字母组合前言踏平坎坷成大道,斗罢艰险又出发!自律的尽头是自控,自控的尽头是硬控。愿道友们披荆斩棘,终能得偿所愿。简介本人是小几年经验的前端开发,算法基础只有力扣几十道题,非常薄......
  • 代码随想录算法训练营第十五天 | Javascript | 继续二叉树的一天 | 力扣Leetcode | 补
    目录前言简介题目链接:501.二叉搜索树中的众数题目链接:236.二叉树的最近公共祖先题目链接:235.二叉搜索树的最近公共祖先前言踏平坎坷成大道,斗罢艰险又出发!自律的尽头是自控,自控的尽头是硬控。愿道友们披荆斩棘,终能得偿所愿。简介本人是小几年经验的前端开发,......
  • JavaScript基础语法
    JavaScript基本语法一、变量及数据类型1.变量1.1.变量的定义方法在JavaScript中,你可以使用var、let或const关键字来声明变量。其中,var声明的变量会提升(hoisting)到其作用域的顶部,而let和const(ES6及以后版本引入)声明的变量具有块级作用域,并且const声明的变量一旦赋值后不可重新......
  • JavaScript可视化
    引言随着大数据时代的到来,数据可视化成为了信息表达和知识发现的重要手段。JavaScript,凭借其广泛的浏览器支持、强大的交互能力以及丰富的生态系统,成为了数据可视化领域的重要工具。无论是前端开发中的数据图表展示,还是更高级的数据可视化应用,JavaScript都扮演着至关重要的角......
  • JavaScript期末大作业 基于HTML+CSS+JavaScript技术制作web前端开发个人博客(48页) (1
    ......
  • 阅读周·你不知道的JavaScript | 无人不识又无人不迷糊的this
    背景去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。没有计划的阅读,收效甚微。新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。这个“玩法”虽然常见且板正,但是有效,已经坚持阅读两个月。《你不知道的JavaScr......
  • java 正则表达式 匹配日期格式的字符串
    这个正则表达式 ^\d{4}-\d{2}-\d{2}$ 用于匹配特定格式的字符串,具体来说,它匹配一个由四位数字、一个短横线(-)、接着是两位数字、再一个短横线、最后是两位数字组成的字符串。这种格式通常用于表示日期(年-月-日),但需要注意的是,它并不验证日期的有效性(比如,它不会检查月份是否超过12或......
  • 【Java】【Swagger】——接口过滤
    在前后端分离时代,Swagger能够实时更新API,十分好用。那么如果根据实际业务需要,展示接口呢?前提已经成功使用Swagger。知道增加 @Bean注解增加分组。此时不同的分组就涉及到不同的过滤。如何过滤接口?增加注解@ApiIgnoreapis():指定包名paths:过滤url增加注解@ApiIgnor......