反射、注解、元注解、动态代理

news2024/11/19 5:56:28

反射

反射就是:加载类,并允许以编程的方式解剖类中的各种成分(成员变量、方法、构造器等

学习反射就是学习如何获取类的信息并操作它们

  1. 加载类,获取类的字节码:Class对象
  2. 获取类的构造器:Constructor对象
  3. 获取类的成员变量:Field对象
  4. 获取类的成员方法:Method对象

1.获取类的三种方式

  1. Class c1 = 类名.class

  2. 调用Class提供方法:public static Class forName(String package)

  3. Object供的方法:public Class getClass(); Class c3 = 对象.getClass()

public class Test1Class {
    public static void main(String[] args) throws Exception {
        //1.使用class c1 = 类名.class方式加载类
        Class c1 = Student.class;
        String name = c1.getName();//获取全类名
        System.out.println(name);// com.lmh.reflect.Student
        String simpleName = c1.getSimpleName();//获取简类名,不包含包名
        System.out.println(simpleName);// Student

        //2.使用public static Class forName(String package)加载类
        //入参为类的全类名
        Class c2 = Class.forName("com.lmh.reflect.Student");

        //3.使用对象.getClass()方法加载类
        Student student = new Student();
        Class c3 = student.getClass();
        System.out.println(c1 == c2);//true
        System.out.println(c1 == c3);//true
        //因为Student类的字节码文件只有一份,所以该类在内存中也只有一个,故c1、c2、c3都是同一个对象
    }
}

2.获取类的构造器Constructor对象

       Class对象提供的从类中获取构造器的方法
方法说明
Constructor<?>[ ] getConstructors( )获取所有public修饰的构造器
Constructor<?>[ ] getDeclaredConstructors( )获取所有构造器,无论什么关键字修饰
Constructor<T> getConstructor(Class<?> … parameterTypes)入参为可变参数类型,根据参数类型获取public修饰的某个构造器
Constructor<T> getDeclaredConstructor(Class<?> … parameterTypes)入参为可变参数类型,根据参数类型获取某个构造器,无论什么关键字修饰
public class Test2Constructor {
    @Test
    public void testGetConstructors() throws Exception {
        //1. 加载类
        Class c = Student.class;
        //2. 获取所有public修饰的构造方法
        Constructor[] cs1 = c.getConstructors();
        //3. 获取所有构造方法(推荐使用)
        Constructor[] cs2 = c.getDeclaredConstructors();
        //4. 遍历构造方法
        print(cs1);
        print(cs2);
        //5. 获取public修饰的指定的构造方法
        Constructor c1 = c.getConstructor(String.class, int.class, double.class);
        //6. 获取指定的构造方法
        Constructor c2 = c.getDeclaredConstructor(String.class);
        System.out.println(c1.getName() + "----->"
                + c1.getParameterCount());
        System.out.println("--------------------");
        System.out.println(c2.getName() + "----->"
                + c2.getParameterCount());
    }
    
    public void print(Constructor[] cs){
        for (Constructor constructor : cs) {
            //获取构造方法的名字和参数个数
            System.out.println(constructor.getName() + "----->"
                    + constructor.getParameterCount());
        }
        System.out.println("--------------------");
    }
}
Constructor对象的方法

获取类构造器的作用:初始化对象并返回

方法说明
T newInstance( Object … initargs )调用此构造器对象表示的构造器,并传入参数,完成对象的初始化并返回
public void setAccessible( boolean flag )设置为true,表示禁止检查访问控制(暴力反射),private修饰的构造器也可以直接使用
public class Test2Constructor {
    @Test
    public void testConstructorMethod() throws Exception {
        Class c = Student.class;
        Constructor constructor = c.getDeclaredConstructor(String.class);
        /*
        无论是否为public修饰,均可以用以下方法创建对象,是通用模板
         */
        //当构造方法是private修饰时,需要先设置为ture,才能调用,否则会报错
        constructor.setAccessible(true);
        //调用构造方法
        Student s = (Student) constructor.newInstance("张三");
        System.out.println(s.getName());//张三
    }
}


3.获取类的成员变量

获取成员变量的方法
方法说明
public Field[ ] getFields( )获取所有public修饰的成员变量
public Field[ ] getDeclaredFields( )获取所有成员变量,无论什么关键字修饰
public Field getField( String name )获取public修饰的指定成员变量,入参为变量名
public Field getDeclaredField( String name )获取任意关键字修饰的指定成员变量,入参为变量名
public class Test3Field {
    private Class c;
    private Constructor constructor;
    Student student;
    @Before
    public void init() throws Exception {
        c = Student.class;
        constructor = c.getDeclaredConstructor(String.class, int.class, double.class);
        constructor.setAccessible(true);
        student = (Student) constructor.newInstance("lmh", 18, 100);
    }
    @Test
    public void testGetFields() throws Exception {
        // 获取所有的public修饰的成员变量
        Field[] fs1 = c.getFields();
        print(fs1);
        // 获取所有的成员变量
        Field[] fs2 = c.getDeclaredFields();
        print(fs2);
        // 获取指定的public修饰的成员变量
        Field address = c.getField("address");
        System.out.println(address.getName() + "---->"
                + address.getType());
        System.out.println("===================================");
        // 获取指定的成员变量
        Field age = c.getDeclaredField("age");
        System.out.println(age.getName() + "---->"
                + age.getType());
    }
    public void print(Field[] fs) {
        for (Field f : fs) {
            System.out.println(f.getName() + "---->"
                    + f.getType());
        }
        System.out.println("===================================");
    }
}
Field对象的方法

获取成员变量的作用:对成员变量进行赋值与取值

方法   说明
void set( object obj, object value )赋值,第一个参数为实体类对象,第二个参数为要赋的值
public void setAccessible( boolean flag )设置为true,表示禁止检查访问控制(暴力反射),private修饰的成员变量也可以直接操作
object get( object obj )取值,入参为实体类对象
public class Test3Field {
    private Class c;
    private Constructor constructor;
    Student student;
    @Before
    public void init() throws Exception {
        c = Student.class;
        constructor = c.getDeclaredConstructor(String.class, int.class, double.class);
        constructor.setAccessible(true);
        student = (Student) constructor.newInstance("lmh", 18, 100);
    }

    @Test
    public void testFieldMethod() throws Exception{
        Field Fname = c.getDeclaredField("name");
        //设置访问权限
        Fname.setAccessible(true);
        //获取成员变量的值
        String name = (String) Fname.get(student);
        System.out.println(name);//lmh
        //设置成员变量的值
        Fname.set(student, "dadada");
        System.out.println(student.getName());//dadada
    }
}

4.获取类的成员方法

Class对象提供的从类中获取成员方法的方法
方法说明
Method[ ] getMethods( )获取所有public修饰的成员方法
Method[ ] getDeclaredMethods( )获取所有成员方法,无论什么关键字修饰
Method getMethod(String name, class<?> … parameterTypes)入参为可变参数类型,根据方法名和参数类型获取public修饰的某个成员方法
Method getDeclaredMethod(String name, class<?> … parameterTypes)入参为可变参数类型,根据方法名和参数类型获取某个成员方法,无论什么关键字修饰
public class Test4Method {
    private Class c;
    private Constructor constructor;
    Student student;
    @Before
    public void init() throws Exception {
        c = Student.class;
        constructor = c.getDeclaredConstructor(String.class, int.class, double.class);
        constructor.setAccessible(true);
        student = (Student) constructor.newInstance("lmh", 18, 100);
    }

    @Test
    public void testGetMethod() throws Exception {
        // 获取所有的public修饰的方法
        Method[] methods1 = c.getMethods();
        print(methods1);
        // 获取所有的方法
        Method[] methods2 = c.getDeclaredMethods();
        print(methods2);
        // 获取public修饰的指定的方法
        Method m1 = c.getMethod("show");
        System.out.println(m1.getName() + "---->"
                + m1.getReturnType() + "---->"
                + m1.getParameterCount());
        System.out.println("===================================");
        // 获取指定的方法
        Method m2 = c.getDeclaredMethod("privateMethod", String.class);
        System.out.println(m2.getName() + "---->"
                + m2.getReturnType() + "---->"
                + m2.getParameterCount());
    }
    public void print(Method[] ms) {
        for (Method m : ms) {
            System.out.println(m.getName() + "---->"
                    + m.getReturnType() + "---->"
                    + m.getParameterCount());
        }
        System.out.println("===================================");
    }
}

Method对象的方法

获取成员变量的作用:运行方法

方法说明
public object invoke( object obj, object … args )运行方法,第一个参数为实体类对象,第二个参数为可变参数,为传入的成员方法的参数
public void setAccessible( boolean flag )设置为true,表示禁止检查访问控制(暴力反射),private修饰的成员方法也可以直接访问
public class Test4Method {
    private Class c;
    private Constructor constructor;
    Student student;
    @Before
    public void init() throws Exception {
        c = Student.class;
        constructor = c.getDeclaredConstructor(String.class, int.class, double.class);
        constructor.setAccessible(true);
        student = (Student) constructor.newInstance("lmh", 18, 100);
    }
    
    @Test
    public void testMethod() throws Exception{
        Method m = c.getDeclaredMethod("privateMethod", String.class);
        m.setAccessible(true);
        m.invoke(student, "ddd");
        //我是一个重载的私有方法,名字是:ddd
    }
}

5.反射的作用

基本作用:可以解析一个类的全部成分并进行操作

可以通过设置访问权限破坏对象的封装性

最主要用途:适合做Java的框架,基本上主流的框架都会基于反射设计出一些通用的功能

注解

注解概述

  • 就是Java代码里的特殊标记,比如:@Override、@Test等,作用是让其他程序根据注解信息来决定怎么执行该程序
  • 注意:注解可以用在类上、构造器上、方法上、成员变量上、参数上等位置

格式

//定义自定义注解
public @interface MyTest1 {
    String name();
    int age() default 18;//默认值为18
    String[] hobby();
}

/*
使用自定义注解的简单实例
 */
@MyTest1(name = "lmh", age = 20, hobby = {"basketball", "football"})
public class AnnotationTest1 {
    /*
    因为age有默认值,所以可以不用赋值,默认为18
     */
    @MyTest1(name = "ddd", hobby = {"basl", "fl"})
    public void test() {
        System.out.println("test");
    }
}
特殊属性名:value

当自定义注解只有value这一个属性或者有其他属性但是都有默认值时,在使用注解时无需写value,直接传值即可

public @interface MyTest2 {
    String value();
    int age() default 18;//默认值为18
}
//只有value或者只有一个属性时,可以省略属性名
//如果其他属性都有默认值,只有value没有默认值,那么只需要给value赋值,value不必写出来

@MyTest2("lmh")//默认给value赋值
public class AnnotationTest2 {
    
}

 

元注解

用于修饰注解的注解

//用于声明注解可以使用的位置,下面代码表示该注解只能用于类和方法上
@Target({ElementType.TYPE, ElementType.METHOD})
//用于表示注解的保留周期,下面代码表示该注解会一直保留到运行阶段,可以通过反射机制读取到
@Retention(RetentionPolicy.RUNTIME)//开发中一般都是RUNTIME
public @interface MyTest3 {
}
@MyTest3()
public class AnnotationTest2 {
    @MyTest3()//会报错,因为声明的范围是TYPE和METHOD,而FIELD不在范围内
    private String s;
}

注解的解析

判断类上、方法上、成员变量上是否存在注解,并把注解里的内容给解析出来

  • 如果要解析类上面的注解,就要先通过反射拿到该类对应的Class对象,再通过Class对象解析注解
  • 如果要解析方法上面的注解,就要先通过反射拿到该方法对应的Method对象,再通过Method对象解析注解
  • Class、Method、Field、Constructor都实现了AnnotatedElement接口,所以都拥有解析注解的能力
AnnotatedElement接口提供的方法说明
public Annotation[ ] getDeclaredAnnotations( )获取当前对象上面的注解对象的集合
public T getDeclaredAnnotation(Class<T> annotationClass)获取指定的注解对象,入参为注解的类对象
public boolean isAnnotationPresent( Class<Annotation> annotationclass )判断当前对象上是否存在某个注解,入参为注解的类对象
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest4 {
    String value();
    double aaa() default 100;
    String[] bbb();
}
@MyTest4(value = "abc", bbb = {"aaa", "bbb"})
public class Demo {
    @MyTest4(value = "dadada", aaa = 1, bbb = {"a", "b"})
    public void test1(){

    }
}
public class AnnotationTest3 {
    @Test
    public void parseClass(){
        //获取类对象
        Class c = Demo.class;
        //解析类上的注解
        if (c.isAnnotationPresent(MyTest4.class)){
            MyTest4 myTest4 =
                    (MyTest4) c.getDeclaredAnnotation(MyTest4.class);
            System.out.println(myTest4.value());
            System.out.println(myTest4.aaa());
            System.out.println(Arrays.toString(myTest4.bbb()));
            System.out.println("====================================");
        }
    }

    @Test
    public void parseMethod() throws Exception {
        Class c = Demo.class;
        Method m = c.getDeclaredMethod("test1");
        Annotation[] annotations = m.getDeclaredAnnotations();
        if (annotations.length > 0){
            for (Annotation annotation : annotations) {
                //instanceof判断左边的对象是否是右边类的实例
                if (annotation instanceof MyTest4){
                    MyTest4 myTest4 = (MyTest4) annotation;
                    System.out.println(myTest4.value());
                    System.out.println(myTest4.aaa());
                    System.out.println(Arrays.toString(myTest4.bbb()));
                }
            }
        }
    }
}

动态代理

实现动态代理的前提是代理的对象和被代理的对象必须实现同一个接口

动态代理对应的类:java.lang.reflect.Proxy,提供了为对象产生代理对象的方法

小案例:

//接口,必须让代理类和被代理类实现同一个接口
public interface Star {
    String sing(String SongName);
    void dance();
}
//被代理类,实现接口
public class SuperStar implements Star{
    private String name;//明星的名字

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

    @Override
    public String sing(String SongName) {
        System.out.println(name + "唱了一首歌:" + SongName);
        return "歌唱完了,谢谢大家!";
    }

    @Override
    public void dance() {
        System.out.println(name + "跳了一支舞!");
    }
}
//代理类
public class ProxyUtil {
    public static Star createProxy(SuperStar superStar){
        //第一个参数固定,第二个参数用于绑定被代理类实现的接口,
        // 第三个参数是一个匿名内部类,用于实现代理对象需要实现的功能
        Star starProxy =
                (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 (method.getName().equals("sing")) {
                            System.out.println("准备话筒,收钱20万");
                        } else if (method.getName().equals("dance")) {
                            System.out.println("准备场地,收钱1000万");
                        }
                        //指定被代理对象的方法并执行
                        //如果有返回值,需要将返回值返回给代理对象
                        return method.invoke(superStar, args);
                    }
                });
        //返回代理对象
        return starProxy;
    }
}

//测试类
public class Test {
    public static void main(String[] args) {
        SuperStar s = new SuperStar("吴晗");
        Star proxy = ProxyUtil.createProxy(s);

        System.out.println(proxy.sing("蓝莲花"));
        proxy.dance();
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1118099.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

滚珠螺杆应如何存放避免受损

滚珠螺杆是一种高精度的机械零件&#xff0c;保存或使用不当&#xff0c;会直接损坏&#xff0c;影响生产效率&#xff0c;因此我们在使用时需要注意以下事项&#xff1a; 1、避免垂直放置&#xff1a;没有施加预压的螺杆垂直放置时&#xff0c;螺母会因自重而从螺杆轴上脱荐下…

【斗破年番】谣言不攻自破,萧潇造型曝光,制作进度已达中州,风尊者帅翻

Hello,小伙伴们&#xff0c;我是小郑继续为大家深度解析重要国漫资讯。 斗破苍穹动画中&#xff0c;萧炎与小医仙重聚&#xff0c;也即将与美杜莎女王回蛇人族见家长&#xff0c;剧情一度变得愈加的炸裂&#xff0c;颇有逐鹿鹅厂国漫第一把交椅的架势。正因此&#xff0c;斗破动…

强化学习 | Python强化学习

强化学习在近年来取得了巨大的突破,使机器能够在不断的试错中自动学习并做出决策。 本文将介绍强化学习的基本概念、原理和应用,同时提供详细的公式解释和Python代码示例。 强化学习是什么? 强化学习是一种机器学习方法,用于让智能体(例如机器人、自动驾驶汽车或游戏玩家…

Keil 5 安装教程(最新最全最详细)附网盘资源

一.简介 文章转自其他平台 链接: keil5下载连接. 官方下载地址&#xff1a;https://www.keil.com/download/product/ Keil5&#xff08;32/64&#xff09;位下载地址&#xff1a; 链接&#xff1a; https://pan.baidu.com/s/1Jn15jeb0Aa1cSietvXfcwg 密码&#xff1a;8ji…

基于springboot实现财务管理系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现财务管理系统演示 摘要 随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应运而生&#x…

记录阿里云服务器(Centos7.9)部署Thingsboard(3.4.2)遇到的一些问题

记录编译Thingsboard遇到的一些问题 部署了一个thingsboard项目到阿里云服务器上&#xff0c;历时十一天&#xff0c;遇到了很多困难&#xff0c;国内关于Thingsboard的资料确实很少&#xff0c;所以想着写一篇博客记录一下&#xff0c;或许能够给以后编译遇到类似问题的人一些…

Pandas数据处理分析系列3-数据如何预览

Pandas-数据预览 Pandas 导入数据后,我们通常需要对数据进行预览,以便更好的进行数据分析。常见数据预览的方法如下: ①head() 方法 功能:读取数据的前几行,默认显示前5行 语法结构:df.head(行数) df1=pd.read_excel("销售表.xlsx",sheet_name="手机销…

AUTOSAR EcuM休眠阶段的具体实现详解

在AUTOSAR EcuM SWS里对于Sleep阶段做出了一个宏观的流程设计,如下: 从BswM过渡到EcuM的规则仲裁这里暂时不讲,有兴趣可以看之前我遇到的工程问题分析,特别是BswM状态迁移图: BswM状态分析 今天主要聊AUTOSAR规定的sleep两种模式:Halt和Poll,以及这两种模式下,…

【趣味随笔】农业机器人的种类与发展前景

&#x1f4e2;&#xff1a;如果你也对机器人、人工智能感兴趣&#xff0c;看来我们志同道合✨ &#x1f4e2;&#xff1a;不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】 &#x1f4e2;&#xff1a;文章若有幸对你有帮助&#xff0c;可点赞 &#x1f44d;…

在Mac上使用安卓桌面模式

在安装Homeblew的基础上 替换国内源 export HOMEBREW_API_DOMAIN"https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles/api" export HOMEBREW_BREW_GIT_REMOTE"https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/brew.git" brew update 安装Scrcpy …

【2023_10_21_计算机热点知识分享】:机器学习中的神经网络

今天的分享主题是机器学习中的神经网络。神经网络是一种模拟人类神经系统的计算模型&#xff0c;它由一系列的神经元组成&#xff0c;每个神经元接收一组输入&#xff0c;经过计算后产生一个输出。神经网络的学习过程是通过调整神经元之间的连接权重来实现的&#xff0c;这个过…

Unity--用户界面

目录 “使用工具栏”&#xff1a; “层次结构”窗口&#xff1a; 层次结构窗口 制作子GameObject “游戏”视图&#xff1a; “场景视图“&#xff1a; ”项目窗口“&#xff1a; 项目窗口工具栏&#xff1a; "Inspector" 窗口&#xff1a; Inspector 游戏…

UG\NX二次开发 实时查看 NX 日志文件

文章作者:里海 来源网站:王牌飞行员_里海_里海NX二次开发3000例,里海BlockUI专栏,C\C++-CSDN博客 感谢粉丝订阅 感谢 a18037198459 订阅本专栏,非常感谢。 简介 实时查看 NX 日志文件,有助于分析保存时间等。打开WindowsPowerShell并实时获取日志文件内容的小功能。 效果 代…

力扣每日一题52:N皇后问题||

题目描述&#xff1a; n 皇后问题 研究的是如何将 n 个皇后放置在 n n 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整数 n &#xff0c;返回 n 皇后问题 不同的解决方案的数量。 示例 1&#xff1a; 输入&#xff1a;n 4 输出&#xff1a;2 解释&#…

Pyside6 QFileDialog

Pyside6 QFileDialog Pyside6 QFileDialog常用函数getOpenFileNamegetOpenFileNamesgetExistingDirectorygetSaveFileName 程序界面程序主程序 Pyside6 QFileDialog提供了一个允许用户选择文件或目录的对话框。关于QFileDialog的使用可以参考下面的文档 https://doc.qt.io/qtfo…

Easyx趣味编程7,鼠标消息读取及音频播放

hello大家好&#xff0c;这里是dark flame master&#xff0c;今天给大家带来Easyx图形库最后一节功能实现的介绍&#xff0c;前边介绍了绘制各种图形及键盘交互&#xff0c;文字&#xff0c;图片等操作&#xff0c;今天就可以使写出的程序更加生动且容易操控。一起学习吧&…

CUDA编程入门系列(十)并行规约

一、什么是规约&#xff1f; 规约&#xff0c;用白话来说&#xff0c;就是将多个值变成一个值的操作&#xff0c;如向量求和&#xff0c;向量内积操作。 以向量求和为例&#xff0c;如果使用串行规约的话&#xff0c;那么就是依靠for循环来进行操作 for(int i 0; i < nums.…

泛微 E-Office download.php 任意文件读取漏洞

一、漏洞描述 泛微E-Office是一款企业级的全流程办公自动化软件&#xff0c;它包括协同办公、文档管理、知识管理、工作流管理等多个模块&#xff0c;涵盖了企业日常工作中的各个环节。泛微E-Office能够帮助企业实现全流程数字化、自动化&#xff0c;提高工作效率和管理质量&a…

#define 宏定义看这一篇文章就够了

前言&#xff1a;在c/c学习的过程中&#xff0c;宏定义&#xff08;#define&#xff09;是作为初学者学习到的为数不多的预处理指令&#xff0c;在学习的时候我们被告知他可以帮助我们更高效的写程序&#xff0c;可以增加程序的可读性&#xff0c;但宏定义&#xff08;#define&…

电脑出现xinput1_3.dll的错误提示怎么办?有什么办法可以解决

电脑如果缺失了xinput1_3.dll还是一件比较复杂的事情&#xff0c;那么电脑出现xinput1_3.dll的错误提示怎么办&#xff0c;又有什么办法可以解决xinput1_3.dll&#xff1f;今天我们就来聊聊xinput1_3.dll丢失的解决办法&#xff0c;来看看都有哪些办法可以解决吧。 一.常见的问…