Java--反射机制

前言:

反射与之前的知识的区别

1.面向对象中创建对象,调用指定结构(属性、方法)等功能,可以不使用反射,也可以使用反射。请问有什么区别?

  • 不使用反射,我们需要考虑封装性。比如:出了自定义类之后,就不能调用自定义类中私有的结构
  • 使用反射,我们可以调用运行时类中任意的构造器、属性、方法。包括了私有的属性、方法、构造器

2.以前创建对象并调用方法的方式,与现在通过反射创建对象并调用方法的方式对比的话,哪种用的多?场景是什么?

  • 从开发者的角度来讲,开发中主要是完成业务代码,对于相关的对象、方法的调用都是确定的。所以,使用非反射的方式多一些。
  • 因为反射体现了动态性(可以在运行时动态的获取对象所属的类,动态的调用相关的方法),所以我们在设计框架的时候会大量的使用反射。意味着,如果大家需要学习框架源码,那么就需要学习反射。
  • 框架 = 注解 +反射 +设计模式

3,通过反射,可以调用类中私有的结构,是否与面向对象的封装性有冲突?是不是Java语言设计存在Bug?

  • 首先不存在bug!
  • 封装性:体现的是是否建议我们调用内部api的问题。比如,private声明的结构,意味着不建议调用。
  • 反射:体现的是我们能否调用的问题。因为类的完整结构都加载到了内存中,所有我们就有能力进行调用。

一、反射(Reflection)

1.概念

概念:

  • Reflection(反射)是被视为动态语言 的关键,反射机制允许程序在 运行期间 借助于Refection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

加载完类之后,在堆内存的方法区中就产生了一个class类型的对象(一个类只有一个class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。

  • 这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。

图示:

1.1反射的优缺点

优点:

  • 提高了Java程序的灵活性和扩展性,降低了耦合性,提高 自适应 能力
  • 允许程序创建和控制任何类的对象,无需提前 硬编码 目标类

缺点:

  • 反射的性能较低
  • 反射机制主要应用在对灵活性和扩展性要求很高的系统框架上
  • 反射会 模糊 程序内部逻辑,可读性较差

2.Class类

2.1 Class类的理解(掌握)

(如下以Java类的加载为例说明)
针对于编写好的.java源文件进行编译(使用javac.exe),会生成一个或多个.class字节码文件。接着,我们使用java.exe命令对指定的.class文件进行解释运行。这个解释运行的过程中,我们需要将.class字节码文件加载(使用类的加载器)到内存中(存放在方法区)。加载到内存中的.class文件对应的结构即为 Class 的一个实例。

如:加载到内存中的Person类或String类或User类,都作为Class的一个一个的实例

//运行时类

  • Class clazz1 = Person.class;
  • Class clazz2 = String.class;
  • Class clazz3 = User.class;

Class clazz4 = Comparable.class;(接口)

2.2 Class的实例化

1.Class类实例化的三种方式:

package exer1;import org.junit.Test;public class ClassTest {/*获取Class实例的几种方式(掌握前三种)*/@Testpublic void test4() throws ClassNotFoundException {//1.调用运行时类的静态属性:classClass clazz1 = User.class;System.out.println(clazz1);//2. 调用运行时类的对象的getclass()User u1 = new User();Class clazz2 = u1.getClass();//3.调用Class的静态方法forName(String className)String className ="exer1.User";//全类名Class clazz3 = null;try {clazz3 = Class.forName(className);} catch (ClassNotFoundException e) {e.printStackTrace();}System.out.println(clazz1 == clazz2);//trueSystem.out.println(clazz1 == clazz3);//true}
}

2.Class的实例都可以指向哪些结构呢?(熟悉)
即所有Java类型!

  1. class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
  2. interface:接口
  3. []:数组
  4. enum:枚举
  5. annotation:注解@interface
  6. primitive type:基本数据类型
  7. void

3.类的加载过程(了解)
过程1:类的装载(loading)

  • 将类的class文件读入内存,并为之创建一个java.lang.class对象。此过程由类加载器完成

过程2:链接(linking)

  • 验证(Verify):确保加载的类信息符合JVM规范,例如:以cafebabe开头,没有安全方面的问题。
  • 准备(Prepare):正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。
  • 解析(Resolve):虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。

过程3:初始化(initialization)

  • 执行类构造器<clinit>()方法的过程。
  • 类构造器<clinit>()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。

4.使用类的加载器获取流,并配置文件

需求:通过ClassLoader加载指定的配置文件

public class ClassLoaderTest {//使用ClassLoader加载配置文件@Testpublic void test(){try {Properties pros = new Properties();InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("info1.properties");//默认当前读取的文件是module下的src中的文件pros.load(is);String name = pros.getProperty("name");String pwd = pros.getProperty("password");System.out.println(name+":"+pwd);} catch (Exception e) {throw new RuntimeException(e);}}//Properties配置文件@Testpublic void test2(){try {Properties pros = new Properties();FileInputStream fis = new FileInputStream(new File("info.properties"));//默认当前读取的饿文件是module下的pros.load(fis);String name = pros.getProperty("name");String pwd = pros.getProperty("password");System.out.println(name + ":" + pwd);} catch (IOException e) {e.printStackTrace();}}
}

2.3 创建运行时类的对象,以及获取运行时类的完整结构

(掌握)反射的应用:调用指定的结构:指定的属性、方法、构造器
1. 调用指定的属性(步骤)

  • 步骤1.通过Class实例调用getDeclaredField(string fieldName),获取运行时类指定名的属性
  • 步骤2.setAccessible(true):确保此属性是可以访问的
  • 步骤3.通过Filed类的实例调用qet(0bject obj)(获取的操作)或 set(0bject obj,0bject value)(设置的操作)进行操作。

2. 调用指定的方法(步骤)

  • 步骤1.通过Class的实例调用getDeclaredMethod(String methodName,Class ... args),获取指定的方法
  • 步骤2.setAccessible(true):确保此方法是可访问的
  • 步骤3.通过Method实例调用invoke(0bject obj ,0bject ... objs),即为对Method对应的方法的调用。invoke()的返回值即为Method对应的方法的返回值                                                                    //特别的:如果Method对应的方法的返回值类型为void,则invoke()返回值为null

3. 调用指定的构造器(步骤)

  • 步骤1.通过class的实例调用getDeclaredConstructor(class...args),获取指定参数类型的构造器
  • 步骤2.setAccessible(true):确保此构造器是可以访问的
  • 步骤3.通过Constructor实例调用newInstance(0bject... objs),返回一个运行时类的实例。

1.2.3部分的代码实现:

package exec2;import org.junit.Test;import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;public class ClassTest {@Testpublic void test() throws Exception {//1.调用指定的属性Class clazz = Person.class;Person per = (Person) clazz.newInstance();Field nameField = clazz.getDeclaredField("name");//属性权限打开nameField.setAccessible(true);//通过Field类的实例化调用get(Object obj)(获取的属性)//或 set(Object obj, Object value)(设置的操作)进行操作nameField.set(per,"Alice");System.out.println(nameField.get(per));}@Testpublic void test2(){//2.调用指定的方法try {Class clazz = Person.class;Person per = (Person) clazz.newInstance();Method showMethod = clazz.getDeclaredMethod("show",String.class,int.class);showMethod.setAccessible(true);//通过Method实例调用invoke(Object obj, Object...obj),即为Method对应的方法的调用//invoke()的返回类型为Method对应的方法的返回值Object returnValue = showMethod.invoke(per,"China",18);System.out.println(returnValue);} catch (Exception e) {e.printStackTrace();}}@Testpublic void test3(){//3.调用指定的构造器try {Class clazz = Person.class;Constructor constructor = clazz.getDeclaredConstructor(String.class,int.class);constructor.setAccessible(true);Person per = (Person) constructor.newInstance("Tom",12);System.out.println(per);} catch (Exception e) {e.printStackTrace();}}}

4.反射的动态性

    @Testpublic void test4(){// Main.javatry {// 动态加载Person类Class personClass = Person.class;// 获取构造方法并创建实例Constructor<?> constructor = personClass.getConstructor(String.class, int.class);Object personInstance = constructor.newInstance("Alice", 30);// 调用introduce方法Method introduceMethod = personClass.getMethod("introduce");introduceMethod.invoke(personInstance);// 访问私有字段namejava.lang.reflect.Field nameField = personClass.getDeclaredField("name");nameField.setAccessible(true); // 允许访问私有字段String nameValue = (String) nameField.get(personInstance);System.out.println("Name from field: " + nameValue);// 修改私有字段namenameField.set(personInstance, "Bob");introduceMethod.invoke(personInstance);} catch (Exception e) {e.printStackTrace();}}//Person类
package exec2;public class Person {private String name;public int age;private String nation;public Person() {}public Person(String name, int age) {this.name = name;this.age = age;}public Person(String name, int age, String nation) {this.name = name;this.age = age;this.nation = nation;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getNation() {return nation;}public void setNation(String nation) {this.nation = nation;}public static String show(String nation , int age){return "国籍:"+nation+"  "+"年龄:"+age;}public void introduce() {System.out.println("Hello, my name is " + name + " and I am " + age + " years old.");}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age + '}';}
}

5.笔记:(重要) 

3.案例练习

案例:榨汁机榨水果汁,水果分别有苹果(Apple)、香蕉(Banana)、桔子(0range)等。
提示:
1、声明(Fruit)水果接口,包含榨汁抽象方法:void squeeze();
2、声明榨汁机(Juicer),包含运行方法:public void run(Fruit f),方法体中,调用f的榨汁方法squeeze()
3、声明各种水果类,实现水果接口,并重写squeeze();
4、在src下,建立配置文件:config.properties,并在配置文件中配上fruitName=xxx(其中xx为某种水果的全类名)
5、在FruitTest测试类中,
(1)读取配置文件,获取水果类名,并用反射创建水果对象,
(2)创建榨汁机对象,并调用run()方法

代码实现:

配置的文件信息:

fruitName=reflectionTest1.Apple//可修改成想要使用的其他类的路径

三个类

package reflectionTest1;public class Apple implements Fruit{@Overridepublic void squeeze() {System.out.println("榨一杯苹果汁!");}
}package reflectionTest1;public class Banana implements Fruit{@Overridepublic void squeeze() {System.out.println("弄一碗香蕉泥!");}
}package reflectionTest1;public class Orange implements Fruit{@Overridepublic void squeeze() {System.out.println("榨一杯橙汁!");}
}

Fruit接口

package reflectionTest1;public interface Fruit {//榨汁的方法void squeeze();
}

 榨汁机类

package reflectionTest1;public class Juicer {public void run(Fruit f){f.squeeze();}
}

测试方法:

package reflectionTest1;import org.junit.Test;import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Constructor;
import java.util.Properties;public class FruitTest {@Testpublic void test(){try {//1.读取配置文件中的信息,获取全类名Properties pros = new Properties();File file = new File("src/config.properties");FileInputStream fis = new FileInputStream(file);pros.load(fis);String fruitName = pros.getProperty("fruitName");//2.通过反射,创建指定全类名对应的类的实例Class clazz = Class.forName(fruitName);Constructor con = clazz.getDeclaredConstructor();//权限设置为允许con.setAccessible(true);Fruit fruit = (Fruit) con.newInstance();//3.通过对榨汁机的对象调用run()方法Juicer juicer = new Juicer();juicer.run(fruit);} catch (Exception e) {e.printStackTrace();}}
}

结果:

结果的修改只需要修改配置文件中的调用的类 

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

WPF+MVVM案例实战(六)- 自定义分页控件实现

文章目录 1、项目准备2、功能实现1、分页控件 DataPager 实现2、分页控件数据模型与查询行为3、数据界面实现 3、运行效果4、源代码获取 1、项目准备 打开项目 Wpf_Examples&#xff0c;新建 PageBarWindow.xaml 界面、PageBarViewModel.cs ,在用户控件库 UserControlLib中创建…

电池的主被动均衡

只有串联的电池需要进行电压均衡&#xff0c;并联的电池由于电压一致&#xff0c;所以并不需要进行均衡&#xff1a; 被动均衡有一个很明显的特征就是会看到很多大电阻&#xff0c;串联在MOS和电池之间&#xff1a;下图中的保护板就是被动均衡板子以及它的原理图&#xff1a; …

软硬件开发面试问题大汇总篇——针对非常规八股问题的提问与应答

软硬件开发&#xff0c;从微控制器编程到复杂的嵌入式系统开发&#xff0c;离不开下位机、操作系统、上位机等&#xff0c;涵盖范围很广。 如何快速一行代码操作硬件寄存器 直接操作硬件寄存器的代码通常依赖于特定平台和编程语言。在 C 或 C 中&#xff0c;常见的方法是使用指…

WORFBENCH:一个创新的评估基准,目的是全面测试大型语言模型在生成复杂工作流 方面的性能。

2024-10-10,由浙江大学和阿里巴巴集团联合创建的WORFBENCH&#xff0c;一个用于评估大型语言模型&#xff08;LLMs&#xff09;生成工作流能力的基准测试。它包含了一系列的测试和评估协议&#xff0c;用于量化和分析LLMs在处理复杂任务时分解问题和规划执行步骤的能力。WORFBE…

智慧停车场导航系统架构及反向寻车系统解决方案

一、系统概述&#xff1a; 随着当前室内定位导航技术在大型公共场所如政务中心、商业综合体、车站中的应用越来越多&#xff0c;人们对智慧停车场的需求也日益凸显出来&#xff0c;并且智慧停车场对大型公共场所智慧化的整体建设起到重要作用。如何更有效提高停车效率&#xf…

如何加密电脑磁盘?电脑本地磁盘加密方法介绍

随着信息技术的不断发展&#xff0c;电脑磁盘加密已经成为保护个人隐私和数据安全的重要手段。本文将介绍几种常见的电脑本地磁盘加密方法&#xff0c;帮助用户保护自己的数据安全。 文件夹只读加密专家 文件夹只读加密专家不仅可以加密电脑中的文件夹&#xff0c;还可以加密保…

Android 13 SystemUI 隐藏下拉快捷面板部分模块(wifi,bt,nfc等)入口

frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java createTileInternal(tileSpec)方法注释想隐藏的模块即可。

【C++进阶篇】——STL的简介

【C进阶篇】——STL的简介 1.什么是STL STL(standard template libaray-标准模板库)&#xff1a;是C标准库的重要组成部分&#xff0c;不仅是一个可复用的组件库&#xff0c;而且是一个包罗数据结构与算法的软件框架。 2.STL的版本 原始版本 Alexander Stepanov、Meng Lee 在…

redis集群配置

一、Redis集群的三种方式 Redis集群提供了三种分布式方案&#xff1a;主从模式&#xff1a;一个主节点和一个或多个从节点&#xff0c;主节点负责写操作&#xff0c;从节点负责读操作&#xff0c;实现读写分离&#xff0c;分担主节点的压力。哨兵模式&#xff1a;哨兵系统用于监…

【每日一题】LeetCode - 盛最多水的容器

给定一个长度为 n 的整数数组 height。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i])。要求找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水。 输入示例&#xff1a; height [1,8,6,2,5,4,8,3,7]输出&#xff1a; 4…

Python依赖库的几种离线安装方法

Python依赖库的几种安装方法 python经常需要安装一些依赖库&#xff0c;但是有时候环境可以连通python源&#xff0c;有时不能连通需要离线安装&#xff08;安装单个库包或者整个库环境&#xff09;&#xff0c;使用pip的如下方法可以相对简单解决问题。 一、如何copy一个pyt…

Linux 端口占用 kill被占用的端口 杀掉端口

1、yum install lsof 2、输入netstat -tln,查看系统当前所有被占用端口 3、根据端口查询进程,输入lsof -i :9555,切记不要忘了添加冒号 4、 既然知道进程号了,那杀死当前进程就简单多了,直接 kill -9 PID 回车

如何通过企业架构蓝图引导企业实现数字化转型:构建与实施的全方位指南

在当今迅速变化的商业环境中&#xff0c;企业进行数字化转型已成为提升竞争力、优化运营的必要手段。企业架构蓝图&#xff08;EA Blueprint&#xff09;作为指导企业数字化转型的战略工具&#xff0c;不仅提供了系统化的设计和规划路径&#xff0c;还帮助企业在技术与业务目标…

【读书笔记·VLSI电路设计方法解密】问题26:什么是漏电流问题

功耗现已成为半导体行业面临的主要技术难题。在当前基于CMOS的VLSI电路中,有两种主要的功耗来源:动态功耗和静态功耗。动态功耗来源于晶体管的切换以及芯片上数百万逻辑门输出端的电容反复充电和放电,是芯片为产生有效输出所消耗的能量。静态功耗则指即使在晶体管关闭时也会…

法治在沃刷积分-刷文章浏览数

最近有一个任务&#xff0c;需要通过浏览文章来获取积分&#xff0c;一个个手点文章太麻烦&#xff0c;专业的事情还得专业的来。 法1&#xff1a;模拟发包 抓包发现&#xff0c;是通过接口来使积分增长&#xff0c;那直接模拟发包即可。 至于info_id的获取&#xff0c;可以通…

2024年全球 MoonBit 编程创新赛-零基础早鸟教程-使用wasm4八小时开发井子棋小游戏

前言 本篇文章主要分享 “2024年全球 MoonBit 编程创新赛 游戏赛道”参赛过程中九宫棋游戏的开发技巧和心得。以此抛砖引玉。首先介绍下 MoonBit。 月兔语言 MoonBit 是一个用于云计算和边缘计算的 WebAssembly 端到端的编程语言工具链。 您可以访问 https://try.moonbitlang.…

文本预处理操作简述

自然语言处理 (NLP) 是数据科学的一个分支&#xff0c;主要处理文本数据。除了数值数据外&#xff0c;文本数据也广泛可用&#xff0c;用于分析和解决业务问题。然而&#xff0c;在使用数据进行分析或预测之前&#xff0c;处理数据非常重要。 我们执行文本预处理来准备用于模型…

mysql的卸载与安装

一、mysql的卸载 1、用管理员模式的打开cmd&#xff0c;我的服务名是mysql。 net stop 【你的服务名】 sc delete 【你的服务名】 2、将下图中有包含‘bin’目录&#xff0c;‘data’目录等等的这个总目录删掉 如图我的目录是&#xff1a;mysql-5.7.28-winx64 3、删除mysql的隐…

代码随想录算法训练营Day39 | 卡玛网-46.携带研究材料、416. 分割等和子集

目录 卡玛网-46.携带研究材料 416. 分割等和子集 卡玛网-46.携带研究材料 题目 卡玛网46. 携带研究材料&#xff08;第六期模拟笔试&#xff09; 题目描述&#xff1a; 小明是一位科学家&#xff0c;他需要参加一场重要的国际科学大会&#xff0c;以展示自己的最新研究成…

day3:管道,解压缩,vim

一&#xff0c;管道&#xff08;|&#xff09; 引入 当我们要将本次命令结果作为下次命令参数时就可以用到&#xff0c;极大的简化了操作。 比如&#xff1a;head -5 文件| tail -1&#xff1a;表示显示第五行这就是管道的魅力 概述 管道符&#xff1a;| 作用&#xff1a…