APT 系列 (一):APT 筑基之反射

news2024/9/29 9:34:03

什么是反射?

简单来讲,反射就是:已知一个类,可以获取这个类的所有信息

一般情况下,根据面向对象封装原则,Java实体类的属性都是私有的,我们不能获取类中的属性。但我们可以根据反射,获取私有变量、方法、构造方法,注解,泛型等等,非常的强大

注意:Google在 Android 9.0及之后对反射做了限制,被使用 @hide标记的属性和方法通过反射拿不到

反射使用

//包路径
package com.dream.aptdemo;

//自定义注解1
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface CustomAnnotation1{
  
}

//自定义注解2
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface CustomAnnotation2{
  
}

//自定义注解3
@Target(ElementType.TYPE)
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface CustomAnnotation3{

}

//接口
interface ICar {
    void combine();
}

//车
@CustomAnnotation3
class Car<K,V> {
    private String carDesign = "设计稿";
    public String engine = "发动机";

    public void run(long kilometer) {
        System.out.println("Car run " + kilometer + " km");
    }
}
//==============================上面这些都是为下面这台奔驰服务的😂===========================
//奔驰
@CustomAnnotation1
@CustomAnnotation2
class Benz extends Car<String,Integer> implements ICar {
  
    private String carName = "奔驰";
    public String carColor = "白色";
  
    public Benz() {
    }

    private Benz(String carName) {
        this.carName = carName;
    }

    public Benz(String carName, String carColor) {
        this.carName = carName;
        this.carColor = carColor;
    }

    @Override
    public void combine() {
        System.out.println("组装一台奔驰");
    }

    private void privateMethod(String params){
        System.out.println("我是私有方法: " + params);
    }
}

获取类

3 种方式去获取类对象
1)Benz.class:类获取
2)benz.getClass:对象获取
3)Class.forName:静态获取

 Benz benz = new Benz();
 Class benzClass = Benz.class;
 Class benzClass1 = benz.getClass();
 Class benzClass2 = Class.forName("com.dream.aptdemo.Benz");

注意:
1、在一个JVM中,一种类,只会有一个类对象存在。所以以上三种方式取出来的类对象,都是一样的。
2、无论哪种途径获取类对象,都会导致静态属性被初始化,而且只会执行一次。(除了直接使用Benz.class类获取这种方式,这种方式不会导致静态属性被初始化)

获取类名

String className = benzClass.getSimpleName();
System.out.println(className);

//打印结果
Benz

获取类路径

String classPath1 = benzClass.getName();
String classPath2 = benzClass.getCanonicalName();
System.out.println(classPath1);
System.out.println(classPath2);

//打印结果
com.pf.reflect.Benz
com.pf.reflect.Benz

这里可能大家会有个疑问:benzClass.getName()和 benzClass.getCanonicalName()有啥区别吗?
从上面打印结果来看,没啥区别,但是如果我们在Benz这个里面加个内部类,然后获取内部类的路径,你就会看到区别了:

//...
class Benz extends Car implements ICar {
    //...
    class InnerClass{
        
    }
}

Class<Benz.InnerClass> innerClass = Benz.InnerClass.class;
System.out.println(innerClass.getName());
System.out.println(innerClass.getCanonicalName());
//打印结果
com.pf.reflect.Benz$InnerClass
com.pf.reflect.Benz.InnerClass

获取父类名

String fatherClassName = benzClass.getSuperclass().getSimpleName();
System.out.println(fatherClassName);

//打印结果
Car

获取接口

Class[] interfaces = benzClass.getInterfaces();
for (Class anInterface : interfaces) {
    System.out.println(anInterface.getName());
}

//打印结果
com.pf.reflect.inter.ICar

创建实例对象

//获取构造方法
Constructor constructor = benzClass.getDeclaredConstructor();
//创建实例
Benz myBenz = (Benz) constructor.newInstance();
//修改属性
myBenz.carColor = "黑色";
myBenz.combine();
System.out.println(myBenz.carColor);

//打印结果
组装一台奔驰
黑色

注意:下面要讲的关于带Declare 的属性和方法和不带Declare 区别:
1、带Declare 的属性和方法获取的是本类所有的属性和方法,不包含继承得来的
2、不带Declare的属性和方法获取的是所有public修饰的属性和方法,包含继承得来的
3、访问private 修饰的属性和方法,需调用 setAccessible设置为true,表示允许我们访问私有变量

属性

获取单个属性

Field carName = benzClass.getDeclaredField("carName");

获取多个属性

//获取本类全部属性
Field[] declaredFields = benzClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
    System.out.println("属性: " + declaredField.getName());
}

//打印结果
属性: carName
属性: carColor

//获取本类及父类全部 public 修饰的属性
Field[] fields = benzClass.getFields();
for (Field field : fields) {
    System.out.println("属性: " + field.getName());
}

//打印结果
属性: carColor
属性: engine

设置允许访问私有变量

carName.setAccessible(true);

获取属性名

System.out.println(carName.getName());

//打印结果
carName

获取变量类型

System.out.println(carName.getType().getName());

//打印结果
java.lang.String

获取对象中该属性的值

System.out.println(carName.get(benz));

//打印结果
奔驰

方法

获取单个方法

//获取 public 方法
Method publicMethod = benzClass.getMethod("combine");

//获取 private 方法
Method privateMethod = benzClass.getDeclaredMethod("privateMethod",String.class);

获取多个方法

//获取本类全部方法
Method[] declaredMethods = benzClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
    System.out.println("方法名: " + declaredMethod.getName());
}

//打印结果
方法名: privateMethod
方法名: combine


//获取本类及父类全部 public 修饰的方法
Method[] methods = benzClass.getMethods();
for (Method method : methods) {
    System.out.println("方法名: " + method.getName());
}

//打印结果 因为所有类默认继承 Object , 所以打印了 Object 的一些方法
方法名: combine
方法名: run
方法名: wait
方法名: wait
方法名: wait
方法名: equals
方法名: toString
方法名: hashCode
方法名: getClass
方法名: notify
方法名: notifyAll

方法调用

Method privateMethod = benzClass.getDeclaredMethod("privateMethod",String.class);
privateMethod.setAccessible(true);
privateMethod.invoke(benz,"接收传入的参数");

//打印结果
我是私有方法: 接收传入的参数

构造方法

获取单个构造方法

//获取本类单个构造方法
Constructor declaredConstructor = benzClass.getDeclaredConstructor(String.class);

//获取本类单个 public 修饰的构造方法
Constructor singleConstructor = benzClass.getConstructor(String.class,String.class);

获取多个构造方法

//获取本类全部构造方法
Constructor[] declaredConstructors = benzClass.getDeclaredConstructors();
for (Constructor declaredConstructor1 : declaredConstructors) {
    System.out.println("构造方法: " + declaredConstructor1);
}
//打印结果
构造方法: public com.dream.aptdemo.Benz()
构造方法: public com.dream.aptdemo.Benz(java.lang.String,java.lang.String)
构造方法: private com.dream.aptdemo.Benz(java.lang.String)


//获取全部 public 构造方法, 不包含父类的构造方法
Constructor[] constructors = benzClass.getConstructors();
for (Constructor constructor1 : constructors) {
    System.out.println("构造方法: " + constructor1);
}
//打印结果
构造方法: public com.dream.aptdemo.Benz()
构造方法: public com.dream.aptdemo.Benz(java.lang.String,java.lang.String)

构造方法实例化对象

//以上面 declaredConstructor 为例
declaredConstructor.setAccessible(true);
Benz declareBenz = (Benz) declaredConstructor.newInstance("");
System.out.println(declareBenz.carColor);
//打印结果
白色

//以上面 singleConstructor 为例
Benz singleBenz = (Benz) singleConstructor.newInstance("奔驰 S ","香槟金");
System.out.println(singleBenz.carColor);
//打印结果
香槟金

泛型

获取父类的泛型

Type genericType = benzClass.getGenericSuperclass();
if (genericType instanceof ParameterizedType) {
   Type[] actualType = ((ParameterizedType) genericType).getActualTypeArguments();
   for (Type type : actualType) {
       System.out.println(type.getTypeName());
   }
}
//打印结果
java.lang.String
java.lang.Integer

注解

获取单个注解

//获取单个本类或父类注解
Annotation annotation1 = benzClass.getAnnotation(CustomAnnotation1.class);
System.out.println(annotation1.annotationType().getSimpleName());
Annotation annotation3 = benzClass.getAnnotation(CustomAnnotation3.class);
System.out.println(annotation3.annotationType().getSimpleName());
//打印结果
CustomAnnotation1
CustomAnnotation3

//获取单个本类注解
Annotation declaredAnnotation1 = benzClass.getDeclaredAnnotation(CustomAnnotation2.class);
System.out.println(declaredAnnotation1.annotationType().getSimpleName());
//打印结果
CustomAnnotation2

获取全部注解

//获取本类和父类的注解(父类的注解需用 @Inherited 表示可被继承)
Annotation[] annotations = benzClass.getAnnotations();
for (Annotation annotation : annotations) {
    System.out.println("注解名称: " + annotation.annotationType().getSimpleName());
}
//打印结果
注解名称: CustomAnnotation3
注解名称: CustomAnnotation1
注解名称: CustomAnnotation2

//获取本类的注解
Annotation[] declaredAnnotations = benzClass.getDeclaredAnnotations();
for (Annotation declaredAnnotation : declaredAnnotations) {
    System.out.println("注解名称: " + declaredAnnotation.annotationType().getSimpleName());
}
//打印结果
注解名称: CustomAnnotation1
注解名称: CustomAnnotation2

反射实践

需求大概就是:通过后台配置下发,完成 App 业务功能的切换。因为只是模拟,我们这里就以通过读取本地配置文件完成 App 业务功能的切换:
首先准备两个业务类,假设他们的功能都很复杂

//包名
package com.dream.aptdemo;

//业务1
class Business1 {
 
    public void doBusiness1Function(){
        System.out.println("复杂业务功能1");
    }
}

//业务2
class Business2 {
 
    public void doBusiness2Function(){
        System.out.println("复杂业务功能2");
    }
}

非反射方式

public class Client {
  
    @Test
    public void test() {
        //业务功能1
        new Business1().doBusiness1Function();
    }
}

假设这个时候需要从第一个业务功能切换到第二个业务功能,使用非反射方式,必须修改代码,并且重新编译运行,才可以达到效果。那么我们可以通过反射去通过读取配置从而完成功能的切换,这样我们就不需要修改代码且代码变得更加通用

反射方式

首先准备一个配置文件,如下图:
在这里插入图片描述读取配置文件,反射创建实例并调用方法

public class Client {
  
    @Test
    public void test() throws Exception {
        try {
            //获取文件
            File springConfigFile = new File("/Users/zhouying/AndroidStudioProjects/AptDemo/config.txt");
            //读取配置
            Properties config= new Properties();
            config.load(new FileInputStream(springConfigFile));
            //获取类路径
            String classPath = (String) config.get("class");
            //获取方法名
            String methodName = (String) config.get("method");
                        
            //反射创建实例并调用方法
            Class aClass = Class.forName(classPath);
            Constructor declaredConstructor = aClass.getDeclaredConstructor();
            Object o = declaredConstructor.newInstance();
            Method declaredMethod = aClass.getDeclaredMethod(methodName);
            declaredMethod.invoke(o);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

完成上面两步后,后续我们就只需要修改配置文件就能完成 App 业务功能的切换了

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

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

相关文章

问题解决 |关于CUDA的代码错误总结以及解决方法

本博客主要关于常见的CUDA的代码错误总结以及解决方法~ 1.RuntimeError运行错误 1.1.RuntimeError: CUDA error: out of memory CUDA kernel errors might be asynchronously reported at some other API call,so the stacktrace below might be incorrect. For debugging cons…

Day09 Python面向对象和异常详解

文章目录 第六章 Python面向对象6.1. 面向对象基础6.1.1. 面向对象与面向过程6.1.2. 类与对象6.1.3. 类的设计与对象的实例化6.1.4. 构造方法6.1.5. 魔术方法6.1.6. 类与类的关系使用到另一个类的对象完成需求使用到另一个类的对象作为属性 6.2. 面向对象进阶6.2.1. 封装6.2.1.…

开启AI原型设计新时代:数字创意的崭新前景

随着人工智能生成内容&#xff08;AIGC&#xff09;相关研究的突破&#xff0c;人类社会正面临一个全新的转折点。诸如多模态、可控扩散模型和大型语言模型等技术正在直接改变创意设计领域的生产过程。 在AIGC领域中&#xff0c;根据输入内容和输出形式的差异&#xff0c;我们…

【MySQl】索引及其B+树

目录 一、索引初识和测试数据的构建 二、磁盘 三、MySQL、OS、磁盘的交互方式&#xff08;InnoDB 存储引擎&#xff09; 四、MySQL中索引和page的理解 1、为什么MySQL和磁盘进行IO交互的时候&#xff0c;要采用page的方案进行交互&#xff0c;而不是采用用多少&#xff0c…

O2O跑腿快递可以解决哪些问题?

各大电商巨头都已经布局了O2O快递&#xff0c;就目前国内的快递环境而言&#xff0c;基本已经形成了四通一达局面&#xff0c;那么O2O同城配送目前有何痛点呢?下面小编就来为大家分析分析&#xff0c;感兴趣的朋友快来一起了解了解吧! 一、O2O快递目前存在哪些痛点? 我们国…

力扣题库刷题笔记14--最长公共前缀

1、题目如下&#xff1a; 2、个人Python代码实现 首先讲一下思路&#xff0c;通俗的来讲&#xff0c;就是依次比较字符串里面所有的字符&#xff0c;如果相同就是公共前缀&#xff0c;如果不同&#xff0c;后面就不用比较了&#xff0c;所以主要就是以下几点&#xff1a; 1、外…

[Leetcode] 0026. 删除有序数组中的重复项

26. 删除有序数组中的重复项 点击上方&#xff0c;跳转至Leetcode 题目描述 给你一个 升序排列 的数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。 由于在某些语…

好用到爆的数据库软件,还能兼容Excel,可以抛弃“VF”和Access

现在大部人已经不再用VF “VF”这个缩写&#xff0c;也只是停留在那个时代里&#xff0c;很多人已经不知道原来的样子&#xff0c;但有的人却还感慨万千。 懂得人自然都懂&#xff01; 微软的两款数据库软件&#xff1a;一个是office的Access&#xff0c;另一个就是“VF” VF…

自定义数据类型:结构体,枚举,联合

之前我们已经了解过结构体&#xff0c;这篇文章再来深入学习的一下&#xff0c;然后再学习其他自定义数据类型&#xff0c;枚举和联合 目录 1.结构体 1.1 结构体类型的声明 1.2 结构体的自引用 1.3 结构体变量的定义和初始化 1.4 结构体内存对齐 1.5 结构体传参 1.6 结…

【Shermo学习】使用shermo批量读入ORCA频率计算结果文件,并批量输出热力学校正数据

使用shermo批量读入ORCA频率计算结果文件&#xff0c;并批量输出热力学校正数据 安装与运行简单任务示例批量输出热力学校正数据 Shermo是北京科音自然科学研究中心卢天老师开发的一个程序&#xff0c;可以用来处理量子化学计算过程中的热力学数据。本文基于Shermo程序&#xf…

数据库设计篇-范式与反范式

概述 一般地&#xff0c;在进行数据库设计时&#xff0c;应遵循三大原则&#xff0c;也就是我们通常说的三大范式&#xff0c;即第一范式要求确保表中每列的原子性&#xff0c;也就是不可拆分&#xff1b;第二范式要求确保表中每列与主键相关&#xff0c;而不能只与主键的某部…

抖音林客生活服务商平台

抖音林客生活服务服务商平台是为了方便服务商管理自己的服务和订单而设计的平台。以下是其主要功能&#xff1a; 服务管理&#xff1a;服务商可以在平台上添加自己提供的服务&#xff0c;并设置服务的价格、规格等信息&#xff1b; 订单管理&#xff1a;服务商可以查看…

【Git总结】

第三章Git常用命令 Git注意首次 安装必须设置一下用户签名&#xff0c;否则无法提交代码。 vim 文件名&#xff08;hellow.txt&#xff09;//进入编辑模式 cat 文件名&#xff08;hellow.txt&#xff09;//查看文件内容 i进入编辑模式&#xff0c;(Esc):wq保存退出 &#…

掌握imgproc组件:opencv-图像处理

图像处理 1.线性滤波&#xff1a;方框滤波、均值滤波、高斯滤波1.1 平滑处理1.2 图像滤波与滤波器1.3 线性滤波器的简介1.4 滤波和模糊1.5 邻域算子与线性邻域滤波1.6 方框滤波1.7 均值滤波1.8 高斯滤波1.9 线性滤波综合案例 2.非线性滤波&#xff1a;中值滤波、双边滤波2.1 中…

python自定义序列类深入学习

一&#xff1a;自定义序列类 1、序列类型的分类 容器序列&#xff1a; list 、 tuple、deque 扁平序列&#xff1a; str 、bytes、bytearray、arry.array 可变序列&#xff1a; list 、deque、bytearray、array 不可变&#xff1a; str、tuple、bytes 容器序列表示可以放置任意…

计算机中CPU、内存、缓存的关系

CPU&#xff08;Central Processing Unit&#xff0c;中央处理器&#xff09; 内存&#xff08;Random Access Memory&#xff0c;随机存取存储器&#xff09; 缓存&#xff08;Cache&#xff09; CPU、内存和缓存之间有着密切的关系&#xff0c;它们共同构成了计算机系统的核…

软考高级系统架构设计师(八) 基于中间件的开发实际项目

目录 中间件的用途 中间件技术 中间件的特点 中间件的十大优越性 企业应用集成 轻量级架构 Struts框架 spring Hibernate 实际项目举例 产品逻辑大图 gRPC的接口规范 关键中间件交互 整体架构设计 大数据素材底层处理 业务交互大图 底层数据素材加工大图 中间…

这些年你走了多少弯路?接口性能测试你真的懂了?进阶测试...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 性能测试关注 响…

java + opencv对比图片不同

1&#xff0c;去官网下载opencv&#xff0c;下载的时候需要注册一个 Oracle 账户&#xff0c;分分钟就能注册。然后安装。我下的是4.7的。 2&#xff0c;找到jar包放进项目里 3&#xff0c;项目结构&#xff0c;比较简单 4&#xff0c;把下载的文件放进C盘 5&#xff0c;主类代…

未来独角兽!安全狗入选2023年福建省数字经济核心产业领域创新企业名单

近日&#xff0c;福建省数字福建建设领导小组办公室公布了入选2023年全省数字经济核心产业领域创新企业名单。 作为国内云原生安全领导厂商&#xff0c;安全狗凭借综合安全能力入选名单&#xff0c;荣膺“未来独角兽”称号。 厦门服云信息科技有限公司&#xff08;品牌名&#…