当前位置: 首页 > news >正文

【Java学习】反射和枚举详解

  所属专栏:Java学习 

在这里插入图片描述

🍁1. 反射

在程序运行时,可以动态地创建对象、调用方法、访问和修改字段,以及获取类的各种属性信息(如成员变量、方法、构造函数等),这种机制就称为反射

反射相关的类
类名用途
Class类代表类的实体,在运行的Java应用程序中表示类和接口
Field类代表类的成员变量 / 类的属性
Method类代表类的方法
Constructor类代表类的构造方法

🍁1.1 反射获取Class对象 

获取字节码文件对象

1. Class.forName(全类名) 包名 + 类名

2. 类名.class

3. 对象名.getClass()

public class Demo1 {public static void main(String[] args) throws ClassNotFoundException {//获取字节码文件对象的三种方式// Class.forName(全类名)包名 + 类名(常用)Class clazz1 = Class.forName("reflect.Student");//类名.classClass clazz2 = Student.class;//对象名.getClass()Student student = new Student();Class clazz3 = student.getClass();System.out.println(clazz1 == clazz2);System.out.println(clazz2 == clazz3);}
}

🍁1.2 反射获取构造方法

🍁1.2.1 获取构造方法的方式

方法用途
getConstructor(Class...<?> parameterTypes)获得该类中与参数类型匹配的公有构造方法
getConstructors()获得该类的所有公有构造方法对象的数组
getDeclaredConstructor(Class...<?> parameterTypes)获得该类中与参数类型匹配的构造方法
getDeclaredConstructors()获得该类所有构造方法对象的数组

使用 getConstructors()时,只会获取类中的公共的构造方法

可以看出,使用getDeclaredConstructors()时,不论构造方法的权限修饰符是什么,都可以获取到 

来看获取单个构造方法的例子,在调用方法的时候,传入方法里面的参数要和需要获取的构造方法的参数一致 

 getConstructor 只能获取 public 修饰的构造方法,getDeclaredConstructor可以获取任意修饰符修饰的构造方法,所以如果要获取的构造方法如果不是 public 修饰的,但是使用了getConstructor获取,就会报错

public class Demo2 {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {//获取class字节码文件对象Class clazz = Class.forName("com.li.reflect.Student");//获取构造方法Constructor[]  cons1 = clazz.getConstructors();for (Constructor con : cons1) {System.out.println(con);}Constructor[] cons2 = clazz.getDeclaredConstructors();for (Constructor con2 : cons2) {System.out.println(con2);}//获取单个的构造方法Constructor con1 = clazz.getDeclaredConstructor();System.out.println(con1);Constructor con2 = clazz.getDeclaredConstructor(String.class);System.out.println(con2);Constructor con3 = clazz.getDeclaredConstructor(String.class,int.class);System.out.println(con3);Constructor con4 = clazz.getDeclaredConstructor(int.class);System.out.println(con2);}
}

 student类:

public class Student {private String name;private int age;public Student() {}private Student(int age){this.age = age;}protected Student(String name){this.name = name;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}public Student(String name, int age) {this.name = name;this.age = age;}
}

🍁1.2.2 获取构造方法的用途

通过反射,可以获取到构造方法的所有内容

首先第一个,就是可以获取到构造方法的权限修饰符

//获取权限修饰符,这里获取的是其对应的常量值
int modifiers = con1.getModifiers();
System.out.println(modifiers); //public 对应的常量是1

idea中的提示功能就是使用了反射的机制,获取到了构造方法的权限修饰符,这里只提示了除空参构造之外,可以使用的两种构造方法,private由于在其他类中不能调用,这里就没有显示

之后,还可以获取到构造方法的参数 

//获取参数
Parameter[] parameters = con3.getParameters();
System.out.println(Arrays.toString(parameters));//[java.lang.String arg0, int arg1]

 应用场景还是上面的代码提示功能

既然可以获取构造方法的所有内容,那以此来创建一个对象也是可以的:

        //创建对象Student student1 = (Student) con3.newInstance("张三", 18);System.out.println(student1);//获取到private修饰的构造方法时con4.setAccessible(true); //表示临时取消权限校验Student student2 = (Student) con4.newInstance(18);System.out.println(student2);

用这种方式创建对象需要注意:

传进去的参数要和获取到的构造方法的参数一致

如果获取到 private修饰的构造方法时,需要临时取消权限校验

🍁1.3 反射获取成员变量

🍁1.3.1 获取成员变量的方式

方法用途
getField(String name)获得某个公有的属性对象
getFields()获得所有公有的属性对象的数组
getDeclaredField(String name)获得某个属性对象
getDeclaredFields()获得所有属性对象的数组

  

public class Demo3 {public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {//获取字节码文件的对象Class clazz = Class.forName("com.li.reflect.Student");//获取所有的成员变量Field[] fields1 = clazz.getFields();System.out.println(Arrays.toString(fields1));Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {System.out.println(field);}//获取单个成员变量Field res1 = clazz.getField("gender");System.out.println(res1);Field res2 = clazz.getDeclaredField("name");System.out.println(res2);}
}

这里的方法的区别对比着获取构造方法的区别是一样的 

在获取单个的成员变量时,传入的参数是要获取的变量名 

🍁1.3.2 获取成员变量的用途

可以获取到成员变量的权限修饰符,名称,数据类型

        //获取成员变量的权限修饰符int modifiers = res1.getModifiers();System.out.println(modifiers);//获取成员变量的名称String name = res1.getName();System.out.println(name);//获取成员变量的数据类型Class<?> type = res1.getType();System.out.println(type);

此外,还可以获取到当前成员变量记录的值

        //获取成员变量记录的值Student student = new Student("张三",18,"男");//临时取消权限校验res1.setAccessible(true);Object value = res1.get(student);System.out.println(value);

 这里需要注意的是,如果获取的值是private修饰的,还是需要设置临时取消权限校验

 获取到值之后,还可以进行修改

//修改
res1.set(student,"女");
System.out.println(student);

🍁1.4 反射获取成员方法

🍁1.4.1 获取成员方法的方式

方法用途
getMethod(String name, Class...<?> parameterTypes)获得该类某个公有的方法
getMethods()获得该类所有公有的方法的数组,包括继承的
getDeclaredMethod(String name, Class...<?> parameterTypes)获得该类某个方法
getDeclaredMethods()获得该类所有方法的数组,不包括继承的

getMethods()  获取的是该类所有公有的方法的数组,包括继承的,所以显示出了很多Person类本身没有定义的方法

 getDeclaredMethods()获取的是该类所有方法的数组,不包括继承的,

获取指定的方法:

在使用getDeclaredMethod获取指定方法时,第一个参数需要指定方法的名称,第二个参数需要指定方法的形参,如果是空参,那么第二个参数就空着不写

        //获取指定方法Method m = clazz.getDeclaredMethod("eat",String.class);System.out.println(m);

🍁1.4.2 获取成员方法的用途

可以获取方法的修饰符,名字,方法的形参,以及方法抛出的异常

        //获取方法修饰符int modifiers = m.getModifiers();System.out.println(modifiers);//获取方法的名字String name = m.getName();System.out.println(name);//获取方法的形参Parameter[] parameters = m.getParameters();for (Parameter parameter : parameters) {System.out.println(parameter);}//获取方法抛出的异常Class[] exceptionTypes = m.getExceptionTypes();for (Class exceptionType : exceptionTypes) {System.out.println(exceptionType);}

获取到的方法也可以通过调用 invoke 方法运行

    /*Object invoke(Object obj,Object...args):运行方法参数一:用obj对象调用该方法参数二:调用方法的传递的参数(如果没有就不写)返回值:方法的返回值(如果没有就不写)* */Person person = new Person();m.setAccessible(true);//如果有返回值可以接收Object res = m.invoke(person, "饭");//打印返回值System.out.println(res);

🍁2. 枚举

 枚举是一种特殊的类,枚举中的每个元素都是该类的一个唯一实例,主要用途是把一组常量组织起来

在之前我们定义常量的时候是这样定义的:

public static final int RED = 1;

但是如果恰好此时有一个数字1,就可能被误认为是RED,这时就可以通过枚举来组织常量 

在创建类时选择Enum类进行创建

🍁2.1 枚举的使用 

Enum类中的常用方法 

方法名称描述
values()以数组形式返回枚举类型的所有成员
ordinal()获取枚举成员的索引位置
valueOf()将普通字符串转换为枚举实例
compareTo()比较两个枚举成员在定义时的顺序
public enum EnumDemo1 {RED, BLACK, YELLO, BLUE;public static void main(String[] args) {EnumDemo1[] enumDemo1s = EnumDemo1.values();for (int i = 0; i < enumDemo1s.length; i++) {System.out.println(enumDemo1s[i] + " " + enumDemo1s[i].ordinal());}//根据字符串返回类中的实例EnumDemo1 res = EnumDemo1.valueOf("RED");System.out.println(res);//比较定义顺序System.out.println(RED.compareTo(BLUE));}
}

可以和switch语句一起使用:

public enum EnumDemo1 {RED, BLACK, YELLO, BLUE;public static void main(String[] args) {EnumDemo1 e1 = RED;switch (e1) {case RED:System.out.println("RED");break;case BLACK:System.out.println("BLACK");default:System.out.println("无法匹配");}}
}

🍁2.2 枚举类的构造方法

在之前提到过,枚举本身是一个类,如果想要类里面的实例对象有参数的话,需要提供相应的构造方法,并且构造方法默认是私有的

public enum EnumDemo2 {RED(0, "red"), BLACK(1, "black"),YELLO(2, "yello"), BLUE(3, "blue");private int ordinal;private String color;EnumDemo2() {}private EnumDemo2(int ordinal, String color) {this.ordinal = ordinal;this.color = color;}
}

 如果将构造方法设置为除private外的其他类型的话就会报错

🍁2.3 枚举在反射中的特殊情况

还按照之前反射的方法获取构造方法并创建对象时,会发现报错了,并且给出的异常是没有找到这个构造方法,但是我们的EnumDemo2类中是存在这个方法的

自己定义的枚举类是默认继承Enum类的,会优先代用父类的构造方法,所以就需要加上父类的参数,此时就解决了第一个异常

此时通过查看newInstance的源码发现,只要是枚举类,就会报错,所以说枚举类是比较安全的

在这里插入图片描述


 

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 微服务网关
  • 基于python的自适应svm电影评价倾向性分析设计与实现
  • 全光谱日照模拟系统汽车整车光老化测试 太阳光照射模拟器
  • 【10.2 python中的类的定义和使用】
  • SQL进阶技巧:最近有效的缺失值填充问题【last_value实现版】
  • 基于WebSocket打造的一款SSH客户端
  • iLogtail 开源两周年:感恩遇见,畅想未来
  • 《中国档案》
  • 前端调用后端,出现跨域报错怎么办
  • GATK AlleleList接口介绍
  • SpringBoot3 简单集成 Spring AI 并使用
  • 图的应用
  • 使用 Python 绘制词云图的详细教程
  • SpringBoot的异常java.lang.ClassNotFoundException: io.r2dbc.spi.ValidationDepth
  • 【算法基础实验】图论-最小生成树-Prim的即时实现
  • [js高手之路]搞清楚面向对象,必须要理解对象在创建过程中的内存表示
  • [LeetCode] Wiggle Sort
  • 【MySQL经典案例分析】 Waiting for table metadata lock
  • Apache的基本使用
  • Go 语言编译器的 //go: 详解
  • iOS仿今日头条、壁纸应用、筛选分类、三方微博、颜色填充等源码
  • Java 11 发布计划来了,已确定 3个 新特性!!
  • java8-模拟hadoop
  • JavaScript的使用你知道几种?(上)
  • react-native 安卓真机环境搭建
  • SpiderData 2019年2月16日 DApp数据排行榜
  • spring cloud gateway 源码解析(4)跨域问题处理
  • 浅谈JavaScript的面向对象和它的封装、继承、多态
  • 手写双向链表LinkedList的几个常用功能
  • 教程:使用iPhone相机和openCV来完成3D重建(第一部分) ...
  • 树莓派用上kodexplorer也能玩成私有网盘
  • ​【C语言】长篇详解,字符系列篇3-----strstr,strtok,strerror字符串函数的使用【图文详解​】
  • # 职场生活之道:善于团结
  • $.ajax中的eval及dataType
  • (1)(1.8) MSP(MultiWii 串行协议)(4.1 版)
  • (39)STM32——FLASH闪存
  • (非本人原创)史记·柴静列传(r4笔记第65天)
  • (五)关系数据库标准语言SQL
  • (一)Mocha源码阅读: 项目结构及命令行启动
  • (轉貼) 蒼井そら挑戰筋肉擂台 (Misc)
  • (轉貼)《OOD启思录》:61条面向对象设计的经验原则 (OO)
  • .NET Core MongoDB数据仓储和工作单元模式封装
  • .NET Core 发展历程和版本迭代
  • .NET Core工程编译事件$(TargetDir)变量为空引发的思考
  • .netcore 如何获取系统中所有session_如何把百度推广中获取的线索(基木鱼,电话,百度商桥等)同步到企业微信或者企业CRM等企业营销系统中...
  • .NET的微型Web框架 Nancy
  • .net实现客户区延伸至至非客户区
  • .NET与java的MVC模式(2):struts2核心工作流程与原理
  • :=
  • @AliasFor注解
  • @kafkalistener消费不到消息_消息队列对战之RabbitMq 大战 kafka
  • @Transactional注解下,循环取序列的值,但得到的值都相同的问题
  • @基于大模型的旅游路线推荐方案
  • [ CTF ] WriteUp- 2022年第三届“网鼎杯”网络安全大赛(朱雀组)
  • [C# 开发技巧]实现属于自己的截图工具