反射和注解
admin
2024-02-14 09:36:42
0

文章目录

  • java反射机制
    • Class类
    • 获取一个类的类对象有三种方式:
    • 使用反射机制进行对象的实例化
      • Class提供的方法:
      • 使用有参构造器实例化对象
    • 使用反射机制调用方法
      • 调用无参数方法:
      • 调用有参方法:
      • 访问私有方法:
      • Method对象提供了获取表示的方法相关信息的一组操作
  • 注解
    • 定义注解语法:
    • 在注解上常用的注解
    • 注解参数
      • 语法
      • 注意事项
    • 反射机制中使用注解
    • 获取注解参数

java反射机制

反射是java中的动态机制,它允许我们在程序【运行期间】再确定类的实例化,方法的调用,属性的调用等,而不是传统意义上的在编码期间确定。

因此,反射可以大大的提高代码的灵活度,但是随之而来的是更多的系统开销和较慢的运行速度,因此不能过度的依赖反射。

Class类

  • Class的每一个实例用于表示JVM加载的一个类,所以我们也称Class的实例
    为类的类对象
  • 当JVM加载一个类时会同时实例化一个Class的实例与之对应,这个Class实例
    中会保存该类的一切信息(类名,有哪些方法,构造器,属性,注解等等)
  • 我们在程序运行期间通过某个类的类对象来操作这个类。因此使用反射操作某个类的第一件事就是获取该类的类对象

获取一个类的类对象有三种方式:

  • 1:类名.class(基本类型只能通过这种方式获取类对象)
    例如: Class cls = String.class; Class cls = int.class
  • 2:Class.forName(String className)
    通过指定一个类的完全限定名来获取该类的类对象。
    完全限定名格式:包名.类名
 Class cls = Class.forName("java.lang.String");//获取String的类对象Class.forName()该方法要求必须处理异常:ClassNotFoundException

当指定的字符串(对应类的完全限定名)有误时会抛出该异常,或指定的
路径下无法找到该类时也会抛出该异常(多发生于通过反射加载第三方
jar文件里的类,有时我们忘记将该jar导入环境变量中,导致JVM无法
通过正确的包路径找到它)。

  • 3:通过类加载器ClassLoader加载类对象
    类加载器有很多中不同的实现,创建方式也各不相同。
    最常用的是如下方式:
    ClassLoader loader = 当前类.class.getClassLoader();
    类加载器除了可以加载类对象,还可以做很多和环境变量相关的操作,功能多。
package reflect;import java.lang.reflect.Method;
//反射
public class ReflectDemo1 {public static void main(String[] args) throws ClassNotFoundException {//获取String的类对象
//        Class cls = String.class;
//        Class cls = Class.forName("java.lang.String");//  类加载器ClassLoaderClassLoader classLoader = ReflectDemo1.class.getClassLoader();Class cls = classLoader.loadClass("java.lang.String");//查看类名//获取类的完全限定名(包名.类名)String className = cls.getName();System.out.println("类名:"+className);//仅获取类名className = cls.getSimpleName();System.out.println("类名:"+className);//通过类对象获取其表示的类的所有方法//获取所有公开方法和从超类继承的方法java.lang.reflect.Method方法对象,与类对象都是反射对象,只不过它的每一个实例仅表示一个方法。通过方法对象可获获取其表示的方法的一切信息(方法名,返回值类型,参数列表,访问修饰符)*///Class提供的getMethods()方法可以获取其表示的类的所有公开方法(包含从超类继承的方法)
//        Method[] methods = cls.getMethods();
//        for(Method method : methods){
//            System.out.println(method.getName());
//        }//获取本类定义的方法(包含私有方法,但是不含有从超类继承的方法)Method[] methods = cls.getDeclaredMethods();for(Method method : methods){System.out.println(method.getName());}}
}

使用反射机制进行对象的实例化

Class提供的方法:

Object newInstance()
Class的newInstance()方法使用的是其表示的类的【无参的,公开的构造器实例化】

package reflect;/*** 使用反射机制进行对象的实例化*/
public class ReflectDemo2 {public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {Person p = new Person();//硬编码,编码期间确定实例化那个类System.out.println(p);/*使用反射机制实例化1:获取要实例化类的类对象2:通过类对象的newInstance方法实例化*///1加载类对象
//        Class cls = Class.forName("reflect.Person");ClassLoader classLoader = ReflectDemo2.class.getClassLoader();Class cls = classLoader.loadClass("reflect.Person");//2通过类对象实例化//Object o = cls.newInstance();//调用无参构造器System.out.println(o);}
}

使用有参构造器实例化对象

java.lang.reflect.Constructor
反射对象中的构造器对象
它的每一个实例仅表示一个类中的某个构造器

package reflect;import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;/*** 使用有参构造器实例化对象*/
public class ReflectDemo3 {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {Person p = new Person("刘苍松",55);System.out.println(p);//加载类对象Class cls = Class.forName("reflect.Person");//获取无参构造器
//        Constructor c = cls.getConstructor();
//        Object o  = c.newInstance();//先获取指定的构造器:Person(String name,int age)Constructor c = cls.getConstructor(String.class,int.class);Object o  = c.newInstance("刘苍松",55);System.out.println(o);}
}

使用反射机制调用方法

调用无参数方法:

package reflect;import java.lang.reflect.Method;
import java.util.Scanner;/*** 使用反射机制调用方法*/
public class ReflectDemo4 {public static void main(String[] args) throws Exception {Person p = new Person();p.sayHello();Scanner scanner = new Scanner(System.in);System.out.println("请输入类名:");String className = scanner.nextLine();System.out.println("请输入方法名:");String methodName = scanner.nextLine();//实例化ClassLoader classLoader = ReflectDemo4.class.getClassLoader();
//        Class cls = classLoader.loadClass("reflect.Person");Class cls = classLoader.loadClass(className);Object o = cls.newInstance();//new Person()//调用方法//1通过类对象获取要调用的方法
//        Method method = cls.getMethod("sayHello");//获取无参方法sayHelloMethod method = cls.getMethod(methodName);//2通过方法对象执行该方法method.invoke(o);//o.sayHello()   o实际上是一个Person对象}
}

调用有参方法:

package reflect;import java.lang.reflect.Method;/*** 调用有参方法*/
public class ReflectDemo5 {public static void main(String[] args) throws Exception {Class cls = Class.forName("reflect.Person");Object o = cls.newInstance();//say(String info)Method m = cls.getMethod("say",String.class);m.invoke(o,"hello~~");//p.say("hello~~")//say(String info,int sum)Method m2 = cls.getMethod("say",String.class,int.class);m2.invoke(o,"嘿嘿",5);}
}

访问私有方法:

package reflect;import java.lang.reflect.Method;/*** 使用反射机制调用私有方法*/
public class ReflectDemo6 {public static void main(String[] args) throws Exception {Person p = new Person();
//        p.hehe();//编译不通过!Class cls = Class.forName("reflect.Person");Object o = cls.newInstance();/*报错:java.lang.NoSuchMethodException没有这个方法异常上述报错可能原因:1:确实没有这个方法(方法名写错了,参数列表不对)2:方法为私有方法,而使用了getMethod()获取。Class中定义的:Method getMethod(String name,Class... pt)Method[] getMethods()上述两个操作仅能获取到类中的所有公开方法(含从超类继承的)下面两个方法是用于获取类中自己定义的所有方法(不含从超类继承的,但是包含私有的)Method getDeclaredMethod(String name,Class... pt)Method[] getDeclaredMethods()*///获取私有方法:private void hehe()
//        Method m = cls.getMethod("hehe");//获取私有方法不能用getMethod,(这用来获取公开方法)Method m = cls.getDeclaredMethod("hehe");m.setAccessible(true);//强行打开访问权限m.invoke(o);//o.hehe()m.setAccessible(false);//用完后关闭访问权限(好习惯)}
}

Method对象提供了获取表示的方法相关信息的一组操作

package reflect;import java.lang.reflect.Method;
import java.lang.reflect.Modifier;/*** Method对象提供了获取表示的方法相关信息的一组操作*/
public class ReflectDemo7 {public static void main(String[] args) throws Exception {Class cls = Class.forName("reflect.Person");Method[] methods = cls.getDeclaredMethods();//获取Person类定义的所有方法for (Method m : methods) {String name = m.getName();//获取方法名System.out.println(name);int modifier = m.getModifiers();//获取访问修饰符if(modifier== Modifier.PUBLIC){System.out.println("公开方法");}else if(modifier== Modifier.PRIVATE){System.out.println("私有方法");}int count = m.getParameterCount();//获取当前方法的参数个数System.out.println("有"+count+"个参数");}}
}

注解

注解可以协助反射机制做更多的操作(我们常用的场景)。

  • 注解可以被作用在如:类上,方法上,属性上,构造器上,参数上等。
  • 可以使用JAVA内置的注解来加以限制可被作用的位置:
    @Target注解用于说明当前注解仅被作用域某些地方,这些位置可使用ElementType指定
  • 例如:当前注解仅在类上或构造器上使用
@Target({ElementType.TYPE,ElementType.CONSTRUCTOR})

定义注解语法:

public @interface 注解名 {
}

在注解上常用的注解

  • @Retention注解用于说明当前注解的保留级别,级别用RetentionPolicy指定

    RetentionPolicy.SOURCE 注解仅保留在源码中
    RetentionPolicy.CLASS(默认) 注解保留在字节码文件中,但不可被反射机制访问
    RetentionPolicy.RUNTIME 注解保留在字节码文件中且可被反射机制访问

    注:在反射机制中如果想访问注解,注解的保留级别要用RUNTIME

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoRunClass {
}

注解参数

语法

类型 参数名() [default 默认值]

注意事项

  • 如果一个注解中只有一个参数时,参数名建议使用value.
    原因:
    实际使用注解时,传参格式为:参数名=参数值
    例如:
    如果当前注解参数定义如下:
    int sum() default 1;
    那么在使用该注解时为sum传参要写作:
    @AutoRunMethod(sum=2)

  • 当一个注解定义了多个参数时,上述传参机制不考虑顺序
    例如,当前注解有两个参数时:
    int sum() default 1;
    String name();
    在使用当前注解传参时,可写作:
    @AutoRunMethod(sum=1,name=“xxx”)

    @AutoRunMethod(name=“xxx”,sum=1)

  • 但是对于仅有一个参数时,每次传参都指定名字过于累赘
    @AutoRunMethod(sum=2)
    因此,如果仅有一个参数时,若参数名为value时,则可以忽略参数名。
    int value();
    使用时:
    @AutoRunMethod(2)

  • 若有2个及以上参数时,就算其中一个参数名字为value也不能传参时省略
    例如,当前注解有两个参数时:
    int value() default 1;
    String name();
    在使用当前注解传参时,可写作:
    @AutoRunMethod(value=1,name=“xxx”)

    @AutoRunMethod(name=“xxx”,value=1)

@AutoRunMethod(name=“xxx”,1) 编译不通过!!

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoRunMethod {int value() default 1;
}

反射机制中使用注解

所有反射对象:
Class,Method,Constructor,Field等都提供了下面的方法:
boolean isAnnotationPresent(Class cls)
用于判断当前反射对象表示的内容是否被参数类对象表示的注解标注了

public class ReflectDemo8 {public static void main(String[] args) throws ClassNotFoundException {Class cls = Class.forName("reflect.Person");//判断当前cls表示的类是否被注解@AutoRunClass标注了boolean is = cls.isAnnotationPresent(AutoRunClass.class);if(is){System.out.println("被标注了!");}else{System.out.println("没有被标注");}}
}

获取注解参数

若希望获取一个注解的参数需要两步:

  1. 获取指定的注解

    所有的反射对象都提供了方法:

    public  T getAnnotation(Class annotationClass)
    
  2. 通过注解对象获取对应的参数

public class ReflectDemo9 {public static void main(String[] args) throws Exception {Class cls = Class.forName("reflect.Person");Method method = cls.getDeclaredMethod("sayHello");//确定该方法被注解@AutoRunMethod标注了if(method.isAnnotationPresent(AutoRunMethod.class)){//通过方法对象获取其表示的方法上的指定注解AutoRunMethod arm = method.getAnnotation(AutoRunMethod.class);//通过注解对象调用对应的参数方法获取该参数的值(这里是获取value参数的值)int value = arm.value();System.out.println("参数值:"+value);}}
}

相关内容

热门资讯

linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
C++ 机房预约系统(六):学... 8、 学生模块 8.1 学生子菜单、登录和注销 实现步骤: 在Student.cpp的...
A.机器学习入门算法(三):基... 机器学习算法(三):K近邻(k-nearest neigh...
数字温湿度传感器DHT11模块... 模块实例https://blog.csdn.net/qq_38393591/article/deta...
有限元三角形单元的等效节点力 文章目录前言一、重新复习一下有限元三角形单元的理论1、三角形单元的形函数(Nÿ...
Redis 所有支持的数据结构... Redis 是一种开源的基于键值对存储的 NoSQL 数据库,支持多种数据结构。以下是...
win下pytorch安装—c... 安装目录一、cuda安装1.1、cuda版本选择1.2、下载安装二、cudnn安装三、pytorch...
MySQL基础-多表查询 文章目录MySQL基础-多表查询一、案例及引入1、基础概念2、笛卡尔积的理解二、多表查询的分类1、等...
keil调试专题篇 调试的前提是需要连接调试器比如STLINK。 然后点击菜单或者快捷图标均可进入调试模式。 如果前面...
MATLAB | 全网最详细网... 一篇超超超长,超超超全面网络图绘制教程,本篇基本能讲清楚所有绘制要点&#...
IHome主页 - 让你的浏览... 随着互联网的发展,人们越来越离不开浏览器了。每天上班、学习、娱乐,浏览器...
TCP 协议 一、TCP 协议概念 TCP即传输控制协议(Transmission Control ...
营业执照的经营范围有哪些 营业执照的经营范围有哪些 经营范围是指企业可以从事的生产经营与服务项目,是进行公司注册...
C++ 可变体(variant... 一、可变体(variant) 基础用法 Union的问题: 无法知道当前使用的类型是什...
血压计语音芯片,电子医疗设备声... 语音电子血压计是带有语音提示功能的电子血压计,测量前至测量结果全程语音播报࿰...
MySQL OCP888题解0... 文章目录1、原题1.1、英文原题1.2、答案2、题目解析2.1、题干解析2.2、选项解析3、知识点3...
【2023-Pytorch-检... (肆十二想说的一些话)Yolo这个系列我们已经更新了大概一年的时间,现在基本的流程也走走通了,包含数...
实战项目:保险行业用户分类 这里写目录标题1、项目介绍1.1 行业背景1.2 数据介绍2、代码实现导入数据探索数据处理列标签名异...
记录--我在前端干工地(thr... 这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 前段时间接触了Th...
43 openEuler搭建A... 文章目录43 openEuler搭建Apache服务器-配置文件说明和管理模块43.1 配置文件说明...