Java 基础进阶篇(十七):反射概述及获取对象的方式

news2024/11/23 18:38:20

文章目录

  • 一、反射概述
  • 二、反射获取类对象
  • 三、反射获取构造器对象
  • 四、反射获取成员变量对象
  • 五、反射获取方法对象
  • 六、 反射的作用
    • 6.1 绕过编译阶段为集合添加数据
    • 6.2 通用框架的底层原理


一、反射概述

反射是指对于任何一个Class类,在 “运行的时候”,不用创建对象,就可以直接得到这个类全部成分。

  • 在运行时,可以直接得到这个类的构造器对象:Constructor
  • 在运行时,可以直接得到这个类的成员变量对象:Field
  • 在运行时,可以直接得到这个类的成员方法对象:Method

这种运行时动态获取类信息以及动态调用类中成分的能力称为 Java 语言的反射机制。

反射的作用:反射是在运行时获取类的字节码文件对象,然后可以解析类中的全部成分。
反射的关键:反射的第一步都是先得到编译后的 Class 类对象,然后就可以得到 Class 的全部成分。

在这里插入图片描述

注意:“类对象”和“类的对象”之间的区别,“类的对象”是 new 出来对象,而“类对象”是其本身,即 class 文件,是“类”类型。


二、反射获取类对象

反射的第一步:获取 Class 类的对象。

在这里插入图片描述

在这里插入图片描述

三种方式:

Class c1 = Class.forName(“全类名");
Class c2 = 类名.class;
Class c3 = 对象.getClass();

举例:

public class Test {
    public static void main(String[] args) throws Exception {
        // 1、Class类中的一个静态方法:forName(全限名:包名 + 类名)
        Class c = Class.forName("com.itheima.d2_reflect_class.Student");
        System.out.println(c); // Student.class

        // 2、类名.class
        Class c1 = Student.class;
        System.out.println(c1);

        // 3、对象.getClass() 获取对象对应类的Class对象。
        Student s = new Student();
        Class c2 = s.getClass();
        System.out.println(c2);
    }
}

三、反射获取构造器对象

步骤:

在这里插入图片描述
获取构造器的方法:
在这里插入图片描述

Constructor类中用于创建对象的方法:
在这里插入图片描述

注:反射可以破坏封装性,私有的也可以执行了。

举例:

public class Student {
	private String name;
	private int age;
	
	// 构造器私有化
	private Student(){
	    System.out.println("无参数构造器执行!");
	}
	
	public Student(String name, int age) {
	    System.out.println("有参数构造器执行!");
	    this.name = name;
	    this.age = age;
	}
	...
}
@Test
public void getDeclaredConstructor() throws NoSuchMethodException {
	// 1. 第一步:获取类对象
	Class c = Student.class;
	
	// 2. 定位单个构造器对象 (按照参数定位到无参构造器)
	Constructor con = c.getDeclaredConstructor();
	System.out.println(con.getName() + " ==> " + con.getParameterCount()); // Student.class ==> 0
	
	// 3. 定位单个构造器对象 (按照参数定位到有参构造器)
	Constructor con1 = c.getDeclaredConstructor(String.class, int.class);
	System.out.println(con1.getName() + " ==> " + con1.getParameterCount()); // Student.class ==> 2
}

@Test
public void getDeclaredConstructors(){
    // 1. 第一步:获取类对象
    Class c = Student.class;
    
    // 2. 提取类中的全部构造方法
    Constructor[] constructors = c.getDeclaredConstructors();
    
    // 3. 遍历构造器
    // 拿到所有的构造器
    for (Constructor con : constructors) {
        System.out.println(con.getName() + " ==> " + con.getParameterCount());
    }
}

暴力反射举例:

@Test
public void getDeclaredConstructor() throws Exception {
	// 1. 第一步:获取类对象
	Class c = Student.class;
	
	// 2. 定位单个构造器对象 (按照参数定位到无参构造器)
	Constructor con = c.getDeclaredConstructor();
	
	// 遇到私有的构造器对象,可以暴力反射
	con.setAccessible(true);
	Student student = (Student) con.newInstance();
	System.out.println(student); // Student{name='null', age=0}
}

四、反射获取成员变量对象

步骤:
在这里插入图片描述

反射的第一步是先得到类对象,然后从类对象中获取类的成分对象。

Class类中用于获取成员变量的方法:
在这里插入图片描述

Field类中用于取值、赋值的方法:
在这里插入图片描述
注:某成员变量是非public的,需要打开权限(暴力反射),然后再取值、赋值。

举例:

public class Student {
    private String name;
    private int age;
    public static String schoolName;
    public static final String  COUNTTRY = "中国";
	....
}
@Test
public void getDeclaredField() throws Exception {
	// 1. 第一步:获取类对象
	Class c = Student.class;
	
	// 2. 第二步:根据名称定位某个成员变量
	Field f = c.getDeclaredField("age");
	System.out.println(f.getName() +" ===> " + f.getType()); // age ===> int
}
 
@Test
public void getDeclaredFields(){
	// 1. 第一步:获取类对象
	Class c = Student.class;
	
	// 2. 第二步:根据名称定位某个成员变量
	Field[] fields = c.getDeclaredFields();
	
	// 3. 第三步:遍历
	for (Field field : fields) {
		System.out.println(field.getName() + " ==> " + field.getType());
		// name ==> class java.lang.String ...
	}
}

暴力反射举例:

@Test
public void setField() throws Exception {
	// 1. 第一步:反射第一步,获取类对象
	Class c = Student.class;
	
	// 2. 第二步:提取某个成员变量
	Field ageF = c.getDeclaredField("age");
	ageF.setAccessible(true); // 暴力打开权限
	
	// 3. 第三步:赋值
	Student s = new Student();
	ageF.set(s, 18);  // 相当于 s.setAge(18);
	System.out.println(s);
	
	// 4. 第四步:取值
	int age = (int) ageF.get(s);
	System.out.println(age);
}

五、反射获取方法对象

步骤:
在这里插入图片描述

获取成员方法的方法:
在这里插入图片描述

Method类中用于触发执行的方法:
在这里插入图片描述
注:某成员方法是非 public 的,需要打开权限(暴力反射),然后再取值、赋值。

举例:

public class Dog {
    public void run(){
        System.out.println("狗跑的贼快~~");
    }
    private void eat(){
        System.out.println("狗吃骨头");
    }
    private String eat(String name){
        System.out.println("狗吃" + name);
        return "吃的很开心!";
    }
    public static void sleep(){
        System.out.println("狗在睡觉!");
    }
}
@Test
public void getDeclaredMethods(){
	// 1. 第一步:获取类对象
	Class c = Dog.class;
	
	// 2. 第二步:提取全部方法;包括私有的
	Method[] methods = c.getDeclaredMethods();
	
	// 3. 第三步:遍历全部方法
	for (Method method : methods) {
	    System.out.println(method.getName() +" 返回值类型:" 
	            + method.getReturnType() + " 参数个数:" + method.getParameterCount());
	}
	// getName 返回值类型:String 参数个数:0 ...
}

暴力反射举例:

@Test
public void getDeclaredMethod() throws Exception {
	// 1. 第一步:获取类对象
	Class c = Dog.class;
	
	// 2. 第二步:提取单个方法对象
	Method eat = c.getDeclaredMethod("eat");
	Method eat2 = c.getDeclaredMethod("eat", String.class);
	
	// 暴力打开权限了
	eat.setAccessible(true);
	eat2.setAccessible(true);
	
	// 3. 第三步: 触发方法的执行
	Dog d = new Dog();
	// 注意:方法如果是没有返回值,那么返回的是null。
	Object result = eat.invoke(d); // 狗吃骨头
	System.out.println(result); // null
	
	Object result2 = eat2.invoke(d, "骨头"); // 狗吃骨头
	System.out.println(result2); // 吃的很开心!
}

六、 反射的作用

6.1 绕过编译阶段为集合添加数据

绕过泛型约束:泛型只是在编译阶段可以约束集合只能操作某种数据类型,编译成Class文件后,进入运行阶段的时候,泛型会自动擦除。

反射是作用在运行时的技术,此时集合的泛型将不能产生约束了,此时是可以为集合存入其他任意类型的元素。

举例:

public class Test {
	public static void main(String[] args) throws Exception {
        // 需求:反射实现泛型擦除后,加入其他类型的元素
        ArrayList<String> lists1 = new ArrayList<>();
        ArrayList<Integer> lists2 = new ArrayList<>();
        System.out.println(lists1.getClass() ==  lists2.getClass());  // true  ArrayList.class

        ArrayList<Integer> lists3 = new ArrayList<>();
        lists3.add(23);
        lists3.add(22);
        // lists3.add("黑马"); // 报错

        Class c = lists3.getClass(); // ArrayList.class  ===> public boolean add(E e)
        // 定位类对象 c 中的 add 方法
        Method add = c.getDeclaredMethod("add", Object.class); // 第二个参数代表此时 add方法中参数是任意类型
        boolean rs = (boolean) add.invoke(lists3, "黑马");
        System.out.println(rs); // true
        System.out.println(lists3); // [23, 22, 黑马]
	}
}

举例2:变量被赋值后,泛型也会被擦除

public class Test2 {
    public static void main(String[] args) {
        // 需求:反射实现泛型擦除后,加入其他类型的元素
        ArrayList<Integer> list1 = new ArrayList<>();
        list1.add(23);
        list1.add(22);
        // list.add("黑马"); // 报错

        ArrayList list2 = list1;
        list2.add("白马");
        list2.add(false);
        System.out.println(list2);  // [23, 22, 白马, false]
    }
}

6.2 通用框架的底层原理

需求:给定任意一个对象,在不清楚对象字段的情况可以,可以把对象的字段名称和对应值存储到文件中去。

public class MyBatisUtil {
    /**
     * 保存任意类型的对象
     */
    public static void save(Object obj){
        try (
                PrintStream ps = new PrintStream(new FileOutputStream("day11-oop/src/data.txt", true));
                ){
            // 提取到这个对象的全部成员变量,只有反射可以解决
            Class c = obj.getClass();  //   c.getSimpleName() 获取当前类名   c.getName() 获取全限名:包名+类名
            ps.println("================" + c.getSimpleName() + "================");

            // 提取到它全部成员变量
            Field[] fields = c.getDeclaredFields();

            // 获取成员变量的信息
            for (Field field : fields) {
                field.setAccessible(true);
                String name = field.getName();
                String value = field.get(obj) + "";
                ps.println(name + " ==> " + value);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
public class Test {
    public static void main(String[] args) {
        Student s = new Student();
        s.setName("猪八戒");
        s.setClassName("西天跑路1班");
        s.setAge(1000);
        s.setHobby("吃,睡");
        s.setSex('男');
        MyBatisUtil.save(s);

        Teacher t = new Teacher();
        t.setName("波仔");
        t.setSex('男');
        t.setSalary(6000);
        MyBatisUtil.save(t);
    }
}

测试结果:

在这里插入图片描述


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

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

相关文章

PHP实战开发23-PHP结合Nginx获取用户真实IP地址

文章目录 一、前言二、关于用户IP的背景知识2.1 HTTP请求2.2 HTTP代理服务器2.3 X-Forwarded-For头部 三、代码实现3.1 Nginx配置3.2 PHP代码处理 总结 一、前言 本文已收录于PHP全栈系列专栏&#xff1a;PHP快速入门与实战 在Web应用程序中&#xff0c;IP地址是常见的数据项…

Flink 学习四 Flink 基础架构

Flink 学习四 Flink 基础架构&算子链&槽位 文章大部分数据来源 : https://nightlies.apache.org/flink/flink-docs-release-1.14/docs/concepts/flink-architecture/ Flink 是一个分布式系统,需要有效的分配和管理计算资源才可以执行流式程序; 集成了常见的资源管理…

chatgpt赋能python:Python简介

Python简介 Python是一种高级编程语言&#xff0c;具有易读性和简洁性的特点。它被广泛使用于Web开发、数据科学、人工智能、机器学习和自动化测试等领域。Python也是一种非常适合新手学习编程的语言。 在本篇文章中&#xff0c;我们将讨论如何使用Python提取指定内容以进行S…

【BMS】电池包硬件方案选型指南

🔋电池包硬件方案选型指南🔋 BMS硬件系统需求主要包括:测温模块、测流模块、测压模块、系统电源、保护电路、故障检测电路,本文阐述各个功能模块在不同场景下的电池包硬件系统方案选择。 一、测温 NTC(热敏电阻) 电池包测温一般包括表皮温度、内部温度、PCB温度(极片布…

[自定义组件]微信小程序自定义组件实现缩略图和原图分离及可缩放效果

目录 目标及基础环境背景 实现原理左右滑动缩放图片菜单 开发实现自定义组件wxml组件结构wxss 样式控制js定义属性及回调json声明为组件 使用添加组件声明及地址声明为全局组件(也可声明为局部)声明为全局组件&#xff08;也可以声明为全局组件&#xff09;使用组件 效果展示 附…

pycharm安装, 汉化 , 使用教程

目录 1.下载安装包 2.汉化 3.使用 1.下载安装包 访问Pycharm官网 根据自己的操作系统下载对应版本的Pycharm Community或Professional Edition。 2.汉化 点击“file”选项&#xff0c;然后点击“setting”&#xff0c;再点击“plugins”选项&#xff1b; 输入“Chinese”找…

使用Frp进行反向代理实现远程桌面控制[teamviewer/nomachine]

.使用Frp进行反向代理实现远程桌面控制 V1.0.0 – by Holden Date : 2023-06-20 文章目录 .使用Frp进行反向代理实现远程桌面控制1. 简介2. 工具准备3. 服务器端搭建4. 受控端配置&&运行teamviewer5. 控制机端运行teamviewer6. 切换成nomachine 1. 简介 ​ frp 是一…

winform多语言资源管理

SailingEase WinForm Framework WinForm开发框架开发手册&#xff1a;http://docs.shengxunwei.com/Home/Browser/sewinformfw/ 这是我2010年左右&#xff0c;写 Winform IDE &#xff08;使用 .NET WinForm 开发所见即所得的 IDE 开发环境&#xff0c;实现不写代码直接生成应用…

什么是算法

有人说程序算法数据结构&#xff0c;虽说这样的认为有失偏颇&#xff0c;一个程序决定的东西实在太多&#xff0c;但某些方面也说明了算法是很重要的&#xff08;数据结构承上启下&#xff0c;最终也是要为算法服务&#xff09;。 算法是用来解决问题的&#xff0c;要理解什么是…

AI Image Codec技术落地实践

AI Codec自2016年首次提出以来&#xff0c;众多海内外高校、企业研究院等机构对此展开了广泛研究。6年时间里&#xff0c;AI Codec 的SOTA方案的压缩性能已经超越了H.266(最新的传统Codec标准)&#xff0c;展现了强大的技术潜力。但受限于计算复杂度、非标等原因&#xff0c;AI…

Vue中的JSX的特性

JSX简介 JSX是一种Javascript的语法扩展&#xff0c;即具备了Javascript的全部功能&#xff0c;同时又兼具html的语义化和直观性。它可以让我们在JS中写模板语法&#xff1a; const el <div>Vue 2</div>; 复制代码上面这段代码既不是 HTML 也不是字符串&#xf…

java阿里云sls基于LoghubAppender自定义日志上传

1、背景&#xff1a;阿里sls日志提供快捷日志平台&#xff0c;平替elk公司使用这个日志服务&#xff0c;需要对接写入日志 目前日志集成有3种 1&#xff09;基于封装manager手动写日志手动send 弊端&#xff1a;本地日志和阿里云日志共用日志代码很臃肿 2&#xff09;基于云服…

开启数字时代,分享电脑监控和录制工具

近年来&#xff0c;随着网络技术的快速发展和普及&#xff0c;电脑屏幕录制和监控越来越成为企业、学校、家庭等不可或缺的工具。无论是在线教学、远程工作&#xff0c;还是家长对孩子上网行为的关注&#xff0c;电脑屏幕录制和监控都具有极大的帮助和重要性。今天就给大家推荐…

【Visual Studio】使用 C++ 语言,配合 Qt,开发了一个串口通信界面

知识不是单独的&#xff0c;一定是成体系的。更多我的个人总结和相关经验可查阅这个专栏&#xff1a;Visual Studio。 文章目录 1. 获取串口名字1.1 文件 GUI.ui1.2 文件 GUI.h1.3 文件 GUI.cpp 2. 配置串口连接2.1 文件 GUI.ui2.2 文件 GUI.h2.3 文件 GUI.cpp 3. 配置串口连接…

chatgpt赋能python:Python排错大全:10年经验总结,快速定位并解决问题!

Python排错大全&#xff1a;10年经验总结&#xff0c;快速定位并解决问题&#xff01; 作为一名有着10年Python编程经验的工程师&#xff0c;在这篇文章中&#xff0c;我将详细介绍常见的Python排错技巧&#xff0c;以及我在实际工作中使用的一些技巧和最佳实践。我们将学习如…

《网络安全0-100》安全策略制定

安全策略制定 安全策略制定是指制定一系列的规范、标准和 流程&#xff0c;以保护企业或组织的信息资源和业务活 动&#xff0c;确保其安全性和可靠性。安全策略制定通 常包括以下几个步骤&#xff1a; 风险评估&#xff1a;对企业或组织的信息系统进行全面 评估&#xff…

Electron 和 Angular 项目升级

Electron 和 Angular 项目升级: Angular4Electron1.7.8 升级到 Angular13Electron2 原项目 Angular 和 Electron 版本: angular/cli: 1.4.9angular/core: 4.4.6Electron: 1.7.8 升级后 Angular 和 Electron 版本: Angular: 13.3.1Electron: 21.2.1 流程: angular-electro…

一次服务器被入侵的处理过程分享

一、服务器入侵现象 近期有一个朋友的服务器(自己做了网站)好像遭遇了入侵&#xff0c;具体现象是&#xff1a; 服务器 CPU 资源长期 100%&#xff0c;负载较高。 服务器上面的服务不能正常提供服务。 ​ 朋友处理了一会没有解决&#xff0c;我开始想说我不是搞安全的&#xf…

【Visual Studio】报错 LNK2019,使用 C++ 语言,配合 Qt 开发串口通信界面

知识不是单独的&#xff0c;一定是成体系的。更多我的个人总结和相关经验可查阅这个专栏&#xff1a;Visual Studio。 文章目录 问题解决方案Ref. 问题 使用 C 语言&#xff0c;配合 Qt 开发串口通信界面时&#xff0c;报错代码 LNK2019。 复制以下错误信息&#xff0c;方便别…

15、SQL注入之Oracel,MongoDB等注入

这里写目录标题 引言补充上篇文章Json注入案例分析 简要学习各种数据库的注入特点Access数据库Mssql数据库PostgreSQL数据库Oracle数据库MongoDB数据库 简要学习各种注入工具的使用指南 引言 mysql的注入方法跟其它的数据库注入方法是差不多的&#xff0c;是可以举一反三的&am…