4.7 反射

news/2024/11/20 21:33:40/

文章目录

  • 1.概述
  • 2.为什么需要反射
  • 3.反射需要用到的API
    • 3.1 获取字节码对象
    • 3.2 常用方法
  • 4.反射的应用
    • 4.1 创建 : 测试物料类
    • 4.2 练习 : 获取类对象
    • 4.3 练习 : 类获取构造方法
    • 4.4 练习 : 获取成员方法
    • 4.5 练习:获取成员变量
    • 4.6 练习 : 创建对象
    • 4.7 熟悉API
      • 4.7.1 创建物科类
      • 4.7.2 物科类测试
  • 5.暴力反射
    • 5.1 创建 : 测试物料类
    • 5.2 练习 : 创建测试类


1.概述

首先我们要明白什么是反射;

通常,我们将拥有能够分析类的能力的程序称为反射;

Reflection(反射) 是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说“自审”,也有称作“自省”。

反射非常强大,它甚至能直接操作程序的私有属性。我们前面学习都有一个概念,被private封装的资源只能类内部访问,外部是不行的,但这个规定被反射赤裸裸的打破了。

反射就像一面镜子,它可以在运行时获取一个类的所有信息,可以获取到任何定义的信息(包括成员变量,成员方法,构造器等),并且可以操纵类的字段、方法、构造器等部分。

但同时它也十分复杂,一般的应用程序员(指开发应用程序的程序员)并不需要考虑反射机制,主要是开发工具的程序员会比较多的使用它;

2.为什么需要反射

在刚接触反射时,对于它存在的意义会比较难理解,首先普通的信息,我们可以通过创建对象等方式来进行调用,不需要反射的参与;其次,私有的属性与信息是比较重要,不暴露在外的,但是现在又存在反射,是不是自相矛盾呢?

其实不是的,反射在很多时候可以帮助我们获取相关信息,这有时候会比创建对象更容易;私有的信息虽然不暴露在外,但是你的同事,甚至于你自己有时候还是需要使用到这些内容,反射也会提供比较安全的获取信息的方式,这样会让你的信息不会变的不安全,同时有限制的去使用相关内容;

3.反射需要用到的API

3.1 获取字节码对象

Class.forName(“类的全路径”);
类名.class
对象.getClass();

3.2 常用方法

获取包名 类名

clazz.getPackage().getName()//包名
clazz.getSimpleName()//类名
clazz.getName()//完整类名

获取成员变量定义信息

getFields()//获取所有公开的成员变量,包括继承变量
getDeclaredFields()//获取本类定义的成员变量,包括私有,但不包括继承的变量
getField(变量名)
getDeclaredField(变量名)

获取构造方法定义信息

getConstructor(参数类型列表)//获取公开的构造方法
getConstructors()//获取所有的公开的构造方法
getDeclaredConstructors()//获取所有的构造方法,包括私有
getDeclaredConstructor(int.class,String.class)

获取方法定义信息

getMethods()//获取所有可见的方法,包括继承的方法
getMethod(方法名,参数类型列表)
getDeclaredMethods()//获取本类定义的的方法,包括私有,不包括继承的方法
getDeclaredMethod(方法名,int.class,String.class)

反射新建实例

clazz.newInstance();//执行无参构造创建对象
clazz.newInstance(666,”海绵宝宝”);//执行含参构造创建对象
clazz.getConstructor(int.class,String.class)//获取构造方法

反射调用成员变量

clazz.getDeclaredField(变量名);//获取变量
clazz.setAccessible(true);//使私有成员允许访问
f.set(实例,值);//为指定实例的变量赋值,静态变量,第一参数给null
f.get(实例);//访问指定实例变量的值,静态变量,第一参数给null

反射调用成员方法

Method m = Clazz.getDeclaredMethod(方法名,参数类型列表);
m.setAccessible(true);//使私有方法允许被调用
m.invoke(实例,参数数据);//让指定实例来执行该方法

4.反射的应用

4.1 创建 : 测试物料类

package partFour;
/*本类用作测试反射的物科类,假装这是别人写的代码* 反射的前提:获取字节码对象,因为字节码对象中有这个类所有的关键信息*/
public class Student {//1.定义本类的成员变量private String name;int age;//2.给私有属性name提供get与set方法public String getName() {return name;}public void setName(String name) {this.name = name;}//3.添加本类的无参与全参构造public Student(){ }public Student(String name, int age) {this.name = name;this.age = age;}//4.添加普通方法public void eat(int n){System.out.println("今天要吃"+n+"碗大米饭?");}
}

4.2 练习 : 获取类对象

package partFour;import org.junit.Test;/**本类用来测试反射*/
public class TestReflect {//1.创建入口函数main()--不用/**单元测试方法:是java测试的最小单位,使用灵活,推荐使用* 语法要求: @Test + void + 没有参数 + public* 注意使用时需要导包:Add JUnit 4 library to the build path:import org.junit.Test;* 单元测试方法执行方式:选中方法名-->右键运行(Run As-->JUnit Test)-->出现小绿条说明执行成功*///2.通过单元测试来测试如何获取类对象@Testpublic void getClazz() throws Exception {/**右键要获取字节码对象的类名,选择Copy Quailfied Name复制类的全路径名*/Class<?> student1 = Class.forName("partFour.Student");//此处的参数是类的全路径名[包名+类名]Class<?> student2 = Student.class;Class<?> student3 = new Student().getClass();//先创建匿名对象,匿名对象没有名字,然后对象的字节码对象System.out.println(student1);//反射得到的字节码Class对象System.out.println(student2.getName());//获取类的全路径名[包名+类名]System.out.println(student3.getSimpleName());//只获取类名System.out.println(student3.getPackage().getName());//获取包名}
}

4.3 练习 : 类获取构造方法

package partFour;import org.junit.Test;import java.lang.reflect.Constructor;
import java.util.Arrays;/*本类用于反射的测试*/
public class TestReflect2 {//通过单元测试方法获取Student类中的构造方法@Testpublic void getFunction() throws ClassNotFoundException {//1.获取字节码对象Class<?> Clazz = Class.forName("partFour.Student");//三种方式选择一种,注意forname需要抛出异常//2.通过字节码对象,获取目标类的成员方法Constructor<?>[] cs = Clazz.getConstructors();//3.查看每个方法信息//System.out.println(cs);//[Ljava.lang.reflect.Method;@1b9e1916System.out.println(Arrays.toString(cs));//已经获取到了方法,但是想遍历//4.把拿到的方法对象数组进行遍历for(Constructor c : cs){//通过每轮循环遍历到的方法对象,获取方法的各种信息System.out.println(c.getName());Class<?>[] types = c.getParameterTypes();System.out.println(Arrays.toString(types));//获取Student中的方法信息及其默认父类Object的方法信息}}
}

4.4 练习 : 获取成员方法

package partFour;import org.junit.Test;import java.lang.reflect.Method;
import java.util.Arrays;/**本类用来测试反射*/
public class TestReflect3 {//通过单元测试来测试获取成员方法@Testpublic void getFunction() throws Exception {//1.获取Class字节码对象Class<?> clazz = Class.forName("partFour.Student");//2.获取所有成员方法Method[] ms = clazz.getMethods();//3.遍历数组,获取每个方法的信息for (Method m : ms) {System.out.println(m.getName());//获取方法名Class<?>[] pt = m.getParameterTypes();//获取方法参数类型System.out.println(Arrays.toString(pt));}}
}

4.5 练习:获取成员变量

package partFour;import org.junit.Test;import java.lang.reflect.Field;public class TestReflect4 {//通过单元测试来测试获取成员变量@Testpublic void getFields(){//1.获取Class字节码对象/** Class<?>中的"?"是泛型约束的通配符,类似于"*" */Class<?> clazz = Student.class;//2.获取所有的成员变量,公共的!!!/**!!!注意目前成员变量的修饰符必须是public才能获取到,采用默认修饰符就反射不到*/Field[] fs = clazz.getFields();//3.遍历数组,获取每个成员变量的信息for (Field f: fs) {System.out.println(f.getName());//获取变量名System.out.println(f.getType().getName());//获取变量类型}}
}

4.6 练习 : 创建对象

package partFour;import org.junit.Test;import java.lang.reflect.Constructor;public class TestReflect5 {/*本方法用于练习通过反射创建指定类Student的对象*//*方式一:通过字节码对象之间调用newInstance(),触发目标类的无参构造来创建对象* 方式二:先获取指定参数类型的构造函数对象*       再通过获取到的这个构造函数对象调用newInsatnce(参数列表)*       来创建Student类对象*/@Testpublic void getObject() throws Exception {//1.获取字节码对象Class<?> clazz = Student.class;//2.通过反射创建对象Object o = clazz.newInstance();//这样已经创建对象了System.out.println(o);/*以上我们newInstance()触发的是Student类中的无参构造创建对象* 所以仅仅能创建对象,但不能给对象的属性赋值* 所以,如果需要触发其他的构造函数来创建对象的话* 需要先获取指定的构造函数对象*///3.想尝试通过其他的构造函数来创建对象//3.1想用其他构造,先得获取,怎么获取?指定参数列表来获取/*本方法用于获取指定参数列表的构造函数,获取的是一个构造函数对象* 注意,这个方法的参数是目标类Student中对应构造函数的参数类型* 而且参入的是字节码对象,不是普通的类型*/Constructor<?> c = clazz.getConstructor(String.class,int.class);//3.2通过刚刚获取到的构造函数对象来帮我们创建Student类的对象Object o2 = c.newInstance("海绵宝宝", 18);/*向下转型:之前转成父类类型的子类对象* 如果想要使用子类的特有功能,需要重新转回成子类类型* 因为父类对象无法使用子类的特有功能*///4.将多态对象转回子类对象--向下转型Student s = (Student) o2;System.out.println(s.getName());System.out.println(s.age);s.eat(999);}
}

4.7 熟悉API

自己创建类练习,获取类中的所有资源,熟悉反射中涉及的API

4.7.1 创建物科类

package partFour;
/* 本类用于复习反射的物科类*/
public class StudentEnd {//1.定义成员变量private String name;public int age;//2.public String getName() {return name;}public void setName(String name) {this.name = name;}//3.生成本类的无参构造和全参构造public StudentEnd(){}public StudentEnd(String name,int age){this.name=name;this.age=age;}//4.提供本类的普通方法/成员方法public void play(){//无参的普通方法System.out.println("今天大结局");}public void sunDay(int n){System.out.println("国庆一共放"+n+"天");}//5.为了查看学生对象的具体属性和属性值,重写toString()@Overridepublic String toString() {return "StudentEnd{" +"name='" + name + '\'' +", age=" + age +'}';}
}

4.7.2 物科类测试

package partFour;import org.junit.Test;import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;/*本类用于反射的测试类*/
public class TestReflectionEnd {//单元测试方法:public + void + 没有参数 + @Test//1.通过单元测试方法,获取目标类Student对应的字节码对象@Testpublic void getClazz() throws ClassNotFoundException {//练习获取字节码对象的3中方式Class<?> clazz1 = Class.forName("partFour.StudentEnd");Class<?> clazz2 = StudentEnd.class;Class<?> clazz3 = new StudentEnd().getClass();System.out.println(clazz1);//class cn.review.Student  Student类对应的字节码对象System.out.println(clazz1.getName());//cn.review.Student 通过字节码对象获取到的Student的全路径名:包名+类名System.out.println(clazz1.getSimpleName());//Student 通过字节码对象获取到的Student的类名System.out.println(clazz2.getPackage());//package cn.review 通过字节码对象获取到的Student的包对象System.out.println(clazz2.getPackage().getName());//cn.review 通过包对象获取到的包名}//2.通过单元测试方法练习引用类型数组的定义与遍历@Testpublic void getStu(){//1.创建Student类的三个对象StudentEnd s1 = new StudentEnd("张三",3);StudentEnd s2 = new StudentEnd("李四",4);StudentEnd s3 = new StudentEnd("王麻子", 5);//2.创建数组将刚刚的3个对象存入数组中StudentEnd[] s = {s1,s2,s3};//3.直接打印数组,查看数组中的元素System.out.println(Arrays.toString(s));//4.遍历学生数组,拿到每一个学生对象,做进一步操作for(StudentEnd stu:s){System.out.println(stu);stu.play();//通过遍历到的对象执行play方法System.out.println(stu.age);//通过遍历到的对象,打印age属性}}//3.通过单元测试方法,获取Student类中的成员方法@Testpublic void getFunction() {//1.获取字节码对象Class<?> clazz = Student.class;//2.通过字节码对象,获取目标类中的成员方法们Method[] ms = clazz.getMethods();//3.通过高效for循环,拿到每一个方法对象for (Method m : ms) {System.out.println(m);//直接打印遍历到的方法对象System.out.println(m.getName());//通过方法对象获取方法名Class<?>[] pt = m.getParameterTypes();//通过方法对象获取方法所有参数的数组System.out.println(Arrays.toString(pt));//打印方法参数的数组}}//4.通过单元测试方法,获取Student类中的构造方法@Testpublic void getCons(){//1.获取字节码对象Class<?> clazz = new StudentEnd().getClass();//2.通过字节码对象获取目标类Student的构造方法们Constructor<?>[] cs = clazz.getConstructors();//3.通过高效for循环遍历数组for(Constructor c: cs){System.out.println(c.getParameterTypes());//打印本轮遍历到的构造方法的名字Class[] pt = c.getParameterTypes();//通过本轮遍历到的构造函数对象获取构造函数的参数类型System.out.println(Arrays.toString(pt));}}//5.通过单元测试方法,获取Student类中的成员变量@Testpublic void getFie() throws ClassNotFoundException{//1.获取字节码对象Class<?> clazz = Class.forName("partFour.StudentEnd");//2.通过字节码对象获取成员变量们Field[] fs = clazz.getFields();//3.遍历数组,获取每个成员变量的具体信息/*注意:目前成员变量的修饰符必须是public的才能获取到,默认修饰符也是获取不到的*/for(Field f : fs){System.out.println(f.getName());//通过本轮循环到的字段对象获取字段名System.out.println(f.getType());//通过本轮循环到的字段对象获取字段的类型}}//6.通过单元测试方法,创建Student目标类的对象@Testpublic void getObject() throws Exception {//1.获取字节码对象Class<?> clazz = StudentEnd.class;//2.通过反射技术,创建目标类的对象/*反射创建对象方案1:通过触发目标类的无参构造创建对象*/Object o = clazz.newInstance();//会抛出异常,需要throwsSystem.out.println(o);//这一步已经获取大了对象Student{name='null', age=0}/*反射创建对象方案2:通过触发全参构造创建对象* 思路:1.先获取指定的构造函数对象,需要指定构造函数的参数,传入的是.class字节码对象*      2.通过刚刚获取到的获取到的构造函数对象,创建Student目标类的对象,并且给对象的属性赋值*/Constructor<?> c = clazz.getConstructor(String.class,int.class);//System.out.println(c);//public cn.review.Student(java.lang.String,int)Object o2 = c.newInstance("赵六", 6);System.out.println(o2);//Student{name='赵六', age=6}}
}

5.暴力反射

指可以将程序中的私有的属性或者方法通过反射技术,暴力的获取到资源。需要使用的常见方法如下:
在这里插入图片描述

5.1 创建 : 测试物料类

package partFour;
/*本类用作暴力反射测试物料类*/
public class Person {//1.提供私有属性private String name;private int age;//2.提供私有方法private void save(int n,String s){System.out.println("save()..."+n+s);}private void update(){System.out.println("update()...");}
}

5.2 练习 : 创建测试类

package partFour;import org.junit.Test;import java.lang.reflect.Field;
import java.lang.reflect.Method;/*本类用于练习暴力反射*/
public class TestViolentReflection {@Testpublic void getFields() throws Exception {//1.获取字节码对象Class<?> clazz = Person.class;//2.获取指定的私有属性,传入的是属性名,注意抛出异常clazz.getDeclaredField("name");//3.根据刚刚获取到的属性对象,查看属性的信息Field field = clazz.getDeclaredField("name");System.out.println(field);//private java.lang.String cn.review.Person.name// 直接打印获取到的字段对象System.out.println(field.getType().getName());//java.lang.StringSystem.out.println(field.getType());//class java.lang.String//4.设置属性的值//4.1需要指定到底是哪个对象的name属性设置值,没有对象就创建对象Object obj = clazz.newInstance(); //触发无参构造利用反射创建对象//4.2暴力反射需要设置私有可见权限field.setAccessible(true);//4.3通过字段对象给刚刚创建好的对象obj设置属性值为海绵宝宝//field就是我们刚刚捕获的name属性//set(m,n)--m是给哪个对象的name属性设置值,n是设置的值是什么field.set(obj,"海绵宝宝");//4.4打印查看刚刚设置的属性值//field.get(m)--field代表的就是Person类的name属性,m是查看哪个对象的这个属性值System.out.println(field.get(obj));//海绵宝宝}@Testpublic void getFie3() throws Exception {//1.获取字节码对象Class<?> clazz = Person.class;//2.根据获取到的属性对象,查看相关信息,比如属性的类型Field f = clazz.getDeclaredField("age");//3.根据获取到的属性对象,查看相关信息,比如属性的类型System.out.println(f.getType().getName());//int//4.操作:设置属性的值:一共需要三个元素:给哪个对象【1】的哪个属性【2】设置一个什么值【3】//4.1需要先指定给哪个对象的这个age属性设置值Object obj = clazz.newInstance();//4.2在给属性设置值之前,需要设置权限私有可见,否则报错f.setAccessible(true);//4.3通过刚刚获取到的age属性对象,给obj对象设置值f.set(obj,17);//4.4打印查看刚刚的属性值是否设置成功System.out.println(f.get(obj));//17}//3.创建单元测试方法:通过暴力反射获取与执行Person类的私有方法@Testpublic void getFunction2() throws Exception {//1.获取字节码对象Class<?> clazz = Person.class;//2.可以通过字节码对象获取某一个指定的私有方法对象//如何确定要找哪一个方法?方法名+参数列表Method method = clazz.getDeclaredMethod("save", int.class, String.class);/*在执行私有的方法之前,需要设置私有可见的权限*/method.setAccessible(true);//3.在执行获取到的方法前,需要先指定给哪个对象做这个save()操作//3.1没有对象就创建对象Object obj = clazz.newInstance();//3.2通过刚刚获取到的方法对象method给指定的对象obj做操作,注意传参method.invoke(obj,666,"哈哈哈");System.out.println(method);//save()...666哈哈哈}
}

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

相关文章

Flutter(一)介绍、Dart语言简介

Flutter介绍 纯原生开发主要面临动态化更新和开发成本两个问题&#xff0c;而针对这两个问题&#xff0c;诞生了一些跨平台的动态化框架 跨平台技术简介 Flutter 是 Google 推出并开源的移动应用开发框架&#xff0c;主打跨平台、高保真、高性能。开发者可以通过 Dart 语言开…

零基础学MySQL(五)-- 详细讲解数据库中的常用函数

目录&#x1f387;一、聚合函数1️⃣count 函数&#xff08;1&#xff09;基本语法&#xff08;2&#xff09;基本练习&#xff08;3&#xff09;注意细节2️⃣sum 函数&#xff08;1&#xff09;基本语法&#xff08;2&#xff09;基本练习&#xff08;3&#xff09;注意细节3…

vue-router路由配置

介绍&#xff1a;路由配置主要是用来确定网站访问路径对应哪个文件代码显示的&#xff0c;这里主要描述路由的配置、子路由、动态路由&#xff08;运行中添加删除路由&#xff09; 1、npm添加 npm install vue-router // 执行完后会自动在package.json中添加 "vue-router…

Hive手册

文章目录1 Hive基本概念1.1 Hive简介1.1.1 什么是Hive1.1.2 为什么使用Hive1.1.3 Hive特点1.2 Hive的体系架构1.3 Hive和RDBMS的对比1.4 Hive的数据存储2 Hive基本使用2.1 Hive存储格式2.2 Hive中的数据模型3 Hive应用3.1 Hive内置函数3.2 SQL介绍与Hive应用场景3.2.1 数据库操…

数模美赛如何找数据 | 2023年美赛数学建模必备数据库

2023美赛资料分享/思路答疑群&#xff1a;322297051 欧美相关统计数据&#xff08;一般美赛这里比较多&#xff09; 1、http://www.census.gov/ 美国统计局&#xff08;统计调查局或普查局&#xff09;官方网站 The Census Bureau Web Site provides on-line access to our …

leetcode练习一:数组(二分查找、双指针、滑动窗口)

文章目录一、 数组理论基础二、 二分查找2.1 解题思路2.2 练习题2.2.1 二分查找(题704)2.2.2 搜索插入位置&#xff08;题35&#xff09;2.2.3 查找排序数组元素起止位置&#xff08;题34&#xff09;2.2.4 有效的完全平方数&#xff08;题367&#xff09;2.2.5 x 的平方根&…

“华为杯”研究生数学建模竞赛2006年-【华为杯】C题:维修线性流量阀时的内筒设计问题(附获奖论文及matlab代码)

赛题描述 油田采油用的油井都是先用钻机钻几千米深的孔后,再利用固井机向四周的孔壁喷射水泥砂浆得到水泥井管后形成的。固井机上用来控制砂浆流量的阀是影响水泥井管质量的关键部件,但也会因磨损而损坏。目前我国还不能生产完整的阀体,固井机仍依赖进口。由于损坏的内筒已…

两种特征提取方法与深度学习方法对比的小型金属物体分类分析研究

本文讨论了用于对包括螺丝、螺母、钥匙和硬币在内的小型金属物体进行分类的两种特征提取方法的效率&#xff1a;定向梯度直方图 (HOG) 和局部二进制模式 (LBP)。首先提取标记图像的所需特征并以特征矩阵的形式保存。使用三种不同的分类方法&#xff08;非参数 K 最近邻算法、支…