Java反射学习

news/2024/11/8 16:59:11/

反射的概念

Reflection(反射)是Java被视为动态语言的关键
反射机制允许程序在执行期借助于Reflection API获得任何类的内部信息,
并能直接操作任意对象的内部属性及方法。
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象)。
这个对象就包含了整个的类的结构信息,我们可以通过这个对象看到类的结构。
这个对象就像一面镜子,透过这个镜子看到类的结构,所以我们形象的称之为:反射

正常方式:引入需要的“包类名称”–>通过new实例化–>取得实例化对象;

反射方式:实例化对象–>getClass()方法–》得到完整的“包类名称”;

反射的功能

在运行时判断任意一个对象所属的类

在运行时构造任意一个类的对象

在运行时构造任意一个类所具有的成员变量和方法

在运行时获取泛型信息

在运行时处理注解

生成动态代理

优点:可以实现动态类 创建对象和编译,体现出很大的灵活性

缺点:对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM我们希望做什么并且它满足我们的要求。这类操做总是慢于直接执行的相同操作

反射的使用

public class Reflect1 {public static void main(String[] args) throws ClassNotFoundException {//通过反射获取类的Class对象Class c1=Class.forName("Reflect.Reflect1");System.out.println(c1);Class c2=Class.forName("Reflect.Reflect1");Class c3=Class.forName("Reflect.Reflect1");Class c4=Class.forName("Reflect.Reflect1");//一个类在内存中只有一个Class对象// 一个类被加载后,类的整个结构都会被封装在Class对象中System.out.println(c2.hashCode());System.out.println(c3.hashCode());System.out.println(c4.hashCode());}//实体类: pojo,entityclass User {private String name;private int id;private int age;public  User() {}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getId() {return id;}public void setId(int id) {this.id = id;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public User(String name, int id, int age) {this.name = name;this.id = id;this.age = age;}}}

Class类详解

对象照镜子后可以得到的信息:某个类的属性、方法和构造器,某个类到底实现了哪些接口。对于每个类而言,JRE都为其保留一个不变的Class类型的对象。一个Class对象包含了特定的某个结构(class/interface/enum/annotation/primitive type/void[])的有关信息

·Class本身也是一个类

·Class对象只能由系统建立对象

·一个加载的类在JVM中只会有一个Class实例

·一个Class对象对应的是一个加载到JVM中的一个.class文件

·每个类的实例都会记得自己是由那个Class实例所生成

·通过Class可以完整地得到一个类中所有被加载 的结构

·Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象

Class类的创建方式

public class Reflect2 {public static void main(String[] args) throws ClassNotFoundException {Person person=new Student();System.out.println("这个人是"+ person.name);//方式一:通过对象获得Class c1=person.getClass();System.out.println(c1.hashCode());//方式二:forname 获得Class c2=Class.forName("Reflect.Reflect2");System.out.println(c2.hashCode());//方式三:通过类名.class获得Class c3=Student.class;System.out.println(c3.hashCode());//方式四: 基本内置类型的包装类都是有一个Type 属性Class c4=Integer.TYPE;System.out.println(c4);//获得父类类型Class c5=c1.getSuperclass();System.out.println(c5);}
}class Person{public String  name;public Person() {}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +'}';}}class Student extends Person {public Student() {this.name = "学生";}class Teacher extends Person {public Teacher() {this.name = "老师";}}}

在这里插入图片描述

三种创建方式的比较

一般使用Class.forName 方法去动态加载类 ,且使用forName就不需要导入其他类,可以加载我们任意的类。

而使用 .class属性,则需要导入类的包,依赖性强,且容易抛出编译错误。

而使用实例化对象的 getClass() 方法,需要本身创建一个对象,是静态的,就体现不出反射机制意义了。

所以我们在获取class对象中, 一般都会使用 Class.forName去获取

哪些类型可以有Class对象?

class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类

interface:接口

[ ]:数组

enum:枚举

annotation:注解@interfac

primitive type:基本数据类型

void

public class Reflect3 {public static void main(String[] args) {Class c1= Object.class;Class c2=Comparable.class;Class c3=String[].class;Class c4=int [][].class;Class c5=Override.class;Class c6= ElementType.class;Class c7=Integer.class;Class c8=void.class;Class c9=Class.class;System.out.println(c1);System.out.println(c2);System.out.println(c3);System.out.println(c4);System.out.println(c5);System.out.println(c6);System.out.println(c7);System.out.println(c8);System.out.println(c9);//只要元素类型一样,就是同一个Class.int []a=new int[10];int []b=new int[100];System.out.println(a.getClass().hashCode());System.out.println(b.getClass().hashCode());}
}

在这里插入图片描述

利用反射获取成员变量Field

//返回 字段对象,该对象反映此 类对象表示的类或接口的指定声明字段。
getDeclaredField(String name)//返回 字段对象的数组, 字段对象反映由此 类对象表示的类或接口声明的所有字段。
getDeclaredFields()//返回 字段对象,该对象反映此 类对象表示的类或接口的指定公共成员字段。
getField(String name)//返回一个包含 字段对象的数组, 字段对象反映此 类对象所表示的类或接口的所有可访问公共字段。
getFields()

例如:

Class<?> cls = Class.forName("com.person");
System.out.println(cls.getName());//只能获取public 的属性
Field[] field = cls.getFields();
for (Field field1 : field) {System.out.println(" 属性: "+field1.getName()); 
}System.out.println("========");//可以获取类全部属性
Field[] declaredFields = cls.getDeclaredFields();    
for (Field all : declaredFields) {System.out.println("属性" + all.getName());
}

利用反射获取方法Method

//返回 方法对象,该对象反映此 类对象表示的类或接口的指定声明方法。
getDeclaredMethod(String name,<?>... parameterTypes);
//返回一个包含 方法对象的数组, 方法对象反映此 类对象表示的类或接口的所有已声明方法,包括public,protected,default(package)访问和私有方法,但不包括继承的方法。
getDeclaredMethods();   //数组
//返回 方法对象,该对象反映此 类对象表示的类或接口的指定公共成员方法。
getMethod(String name,<?>... parameterTypes);
//返回一个包含 方法对象的数组, 方法对象反映此 类对象所表示的类或接口的所有公共方法,包括由类或接口声明的那些以及从超类和超接口继承的那些。
getMethods();

用法 跟Field 类似。

但是要注意后面的 String name, 类<?>… parameterTypes
例如

  Class clazz = Class.forName("java.lang.Runtime");clazz.getMethod("exec", String.class);

Runtime 类中的 exec 实现了方法的重载, 因此需要使用forMethod就要带上第二个参数,才能准确的找到指定方法的对象。

利用反射获取构造函数

//返回一个 构造器对象,该对象反映此 类对象所表示的类或接口的指定构造函数。
getDeclaredConstructor(<?>... parameterTypes)
//返回 构造器对象的数组, 构造器对象反映由此 类对象表示的类声明的所有构造函数。
getDeclaredConstructors()
//返回一个 构造器对象,该对象反映此 类对象所表示的类的指定公共构造函数。
getConstructor(<?>... parameterTypes)
//返回一个包含 构造器对象的数组, 构造器对象反映了此 类对象所表示的类的所有公共构造函数。
getConstructors()

用法几乎差不多,可以使用 getConstructors 再进行遍历取出

利用反射实例化对象

得到地址和类名后,使用newInstance 实例化这个类:

Class<?> cls = Class.forName("com.person");
Object o = cls.newInstance();
System.out.println(o);

也可以通过构造器实例化对象

 Constructor<?> cons = cls.getDeclaredConstructor(String.class, double.class);Object snowy = cons.newInstance("snowy", 1000000000);System.out.println(snowy);

利用反射中调用对象中的方法

反射中的调用,是用方法来调用对象,如:

方法.invoke(对象)

例子, 比如要调用 person 类中的 hi() 方法,首先要得到 hi() 方法的 Method对象,其次 还需要得到 person类的对象 作为invoke的参数

Class<?> cls = Class.forName("com.person");//加载类
Constructor<?> cons = 
cls.getDeclaredConstructor(String.class, double.class); //获取构造器Object snowy = cons.newInstance("snowy", 123);//实例化对象
Method m2 = cls.getMethod("m2");//得到Method 对象
Object name = m2.invoke(snowy); // 调用方法

如果调用的是普通方法,第一个参数就是类对象;

如果调用的这个方法是静态方法,第一个参数是类,或者可以护忽略,设置为null

特殊权限方法的反射访问方式

如果得到的Field 、Method、Constructor 是私有属性的话,受到权限影响,不能直接调用或者读取,需要设置 setAccessible(true) ;

setAccessible方法是AccessibleObejct类的一个方法,它是Field、Method和Constructor类的公共超类。这个特性是为调式、持久存储和类似机制提供的。

例如,Runtime类的构造函数是私有的,可以这样获取对象

Class<?> cls = Class.forName("java.lang.Runtime");
Constructor<?> m = cls.getDeclaredConstructor();
m.setAccessible(true);
cls.getMethod("exec", String.class).invoke(m.newInstance(),"calc.exe");

http://www.ppmy.cn/news/18472.html

相关文章

数据结构---set篇

第一次超时是因为用memsetmemsetmemset不得不超时&#xff0c;第二次超时是我用vectorvectorvector数组的时候&#xff0c;然后以O(n)O(n)O(n)复杂度查找元素之后使用eraseeraseerase方法进行删除&#xff0c;第三次超时是我把查找元素改成了O(logn)O(logn)O(logn)之后用vector…

【数据库】必须知道的MySQL优化

文章目录SQL语言有哪几部分组成为什么要进行MySQL优化&#xff1f;优化方法有哪些&#xff1f;SQL层面优化MySQL配置方面架构设计方面硬件和操作系统方面.SQL语言有哪几部分组成 数据定义语言&#xff0c;简称DDL&#xff1a;DROP,CREATE,ALTER等语句。数据操作语言&#xff0…

DPU网络开发SDK—DPDK(六)

rte_eal_init() 接上次内容继续对rte_eal_init()所做的工作进行分析。 20. 大页内存配置 internal_conf中的no_hugetlbfs指明是否禁用大页内存&#xff0c;通过命令行参数"--no-huge"设置禁用&#xff0c;默认情况下大页内存是开启的。DPDK根据进程是primary还是s…

【LeetCode】回溯算法总结

回溯法解决的问题 回溯法模板 返回值&#xff1a;一般为void参数&#xff1a;先写逻辑&#xff0c;用到啥参数&#xff0c;再填啥参数终止条件&#xff1a;到达叶子节点&#xff0c;保存当前结果&#xff0c;返回遍历过程&#xff1a;回溯法一般在集合中递归搜索&#xff0c;集…

基于蜣螂算法优化的BP神经网络(预测应用) - 附代码

基于蜣螂算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录基于蜣螂算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍3.蜣螂优化BP神经网络3.1 BP神经网络参数设置3.2 蜣螂算法应用4.测试结果&#xff1a;5.Matlab代码摘要&am…

实战打靶集锦-002-SolidState

**写在前面&#xff1a;**谨以此文纪念不完美的一次打靶经历。 目录1. 锁定主机与端口2. 服务枚举3. 服务探查3.1 Apache探查3.1.1 浏览器手工探查3.1.2 目录枚举3.2 JAMES探查3.2.1 搜索公共EXP3.2.2 EXP利用3.2.2.1 构建payload3.2.2.2 netcat构建反弹shell3.2.3 探查JAMES控…

c#检测网络连接信息

用手机全屏看B站视频时可以看到右上角标识有WIFI&#xff0c;比较好奇如何检测当前网络连接是wifi还是数据网络什么的。于是百度相关信息&#xff0c;找到参考文献1-2&#xff0c;其中介绍采用Xamarin.Essentials检测网络连接性&#xff0c;其中的Connectivity类可用于监视设备…

带你了解docker是什么----初始篇

docker容器docker简介docker、虚拟环境与虚拟机docker 的核心概念Docker 镜像Docker 仓库Docker容器镜像、容器、仓库&#xff0c;三者之间的联系容器 容器一词的英文是container&#xff0c;其实container还有集装箱的意思&#xff0c;集装箱绝对是商业史上了不起的一项发明&…