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

这篇具有很好参考价值的文章主要介绍了APT 系列 (一):APT 筑基之反射。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

什么是反射?

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

一般情况下,根据面向对象封装原则,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();
    }
}

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

反射方式

首先准备一个配置文件,如下图:
APT 系列 (一):APT 筑基之反射读取配置文件,反射创建实例并调用方法

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 业务功能的切换了文章来源地址https://www.toymoban.com/news/detail-502456.html

到了这里,关于APT 系列 (一):APT 筑基之反射的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【测试开发】python系列教程:python反射

    这次我们分享python的反射 在java中大家都知道,java的反射的机制,其实在python中也是有反射机制的,我们看下如何 来用? 一、什么是反射? 程序可以访问、检测和修改\\\'本身状态\\\'或者行为的一种能力。大白话:其实就是通过字符串操作对象的数据和方法 二、反射的作用 正常

    2024年02月05日
    浏览(47)
  • linux之Ubuntu系列 find 、 ln 、 tar、apt 指令 软链接和硬链接 snap

    查找文件 find 命令 功能非常强大,通常用来在 特定的目录下 搜索 符合条件的文件 find [path] -name “.txt” 记得要加 “ ” 支持通配符 ,正则表达式 包括子目录 ls 不包括 子目录 如果省略路径,表示 在当前路径下,搜索 find 按大小查找文件 软链接 软链接 跟windows 系统中的

    2024年02月16日
    浏览(37)
  • String、反射、枚举、lambda表达式以及泛型进阶(数据结构系列16)

    目录 前言: 1. String 1.1 字符串常量池 1.1.1 创建对象的思考 1.1.2 字符串常量池(StringTable) 1.1.3 再谈String对象创建 1.1.4 intern方法 2. 反射 2.1 反射的定义 2.2 反射的用途 2.3 反射的基本信息 2.4 反射相关的类 2.4.1 Class类(反射机制的起源) 2.4.1.1 Class类中的相关方法 2.5 反

    2024年02月11日
    浏览(55)
  • 算法修炼之筑基篇——筑基二层后期(初步理解解决贪心算法)

    ✨ 博主: 命运之光 🦄 专栏: 算法修炼之练气篇 🍓 专栏: 算法修炼之筑基篇 ✨ 博主的其他文章: 点击进入博主的主页 前言: 学习了算法修炼之练气篇想必各位蒟蒻们的基础已经非常的扎实了,下来我们进阶到算法修炼之筑基篇的学习。筑基期和练气期难度可谓是天差

    2024年02月11日
    浏览(35)
  • 算法修炼之筑基篇——筑基一层中期(解决01背包,完全背包,多重背包)

    ✨ 博主: 命运之光​​​​​​ 🦄 专栏: 算法修炼之练气篇​​​​​ 🍓 专栏: 算法修炼之筑基篇 ✨ 博主的其他文章: 点击进入博主的主页​​​​​​ 前言: 学习了算法修炼之练气篇想必各位蒟蒻们的基础已经非常的扎实了,下来我们进阶到算法修炼之筑基篇的

    2024年02月08日
    浏览(32)
  • 筑基九层 —— 指针详解

    目录 前言: 指针详解 1.CSDN由于我的排版不怎么好看,我的有道云笔记比较美观,请移步 有道云笔记 2.修炼必备   1)入门必备:VS2019社区版,下载地址:Visual Studio 较旧的下载 - 2019、2017、2015 和以前的版本 (microsoft.com)   2)趁手武器:印象笔记/有道云笔记   3)修炼秘籍:

    2023年04月08日
    浏览(24)
  • Kotlin筑基

    本文链接 核心思路:每个知识点都要和源码结合起来讲。 1、const val PI = 45 编译时常量不可以用于局部变量,为什么? 函数之内必须在运行时赋值,不符合编译时常量 2、编译时常量(compile-time constants)有什么用? 提高性能:编译时进行常量折叠(constant folding),避免在运行

    2024年02月15日
    浏览(16)
  • 【蓝桥杯-筑基篇】排序算法

    🍓系列专栏:蓝桥杯 🍉个人主页:个人主页 目录 前言: 一、冒泡排序 二、选择排序 三、插入排序 四、图书推荐 算法工具推荐:  还在为数据结构发愁吗?这款可视化工具,帮助你更好的了解其数据结构数据结构和算法动态可视化 (Chinese) - VisuAlgo ​ 1.什么是冒泡排序? 冒

    2024年02月02日
    浏览(42)
  • 音频筑基:算法时延分析

    音频算法中,经常遇到时延分析的问题,刚开始接触大多都比较迷惑,这里将自己对时延的学习思考梳理总结于此。 音频领域中,时延(delay/latency)主要指声音从源端发出,经链路传输,再到对端接收到声音,所经过的总时间延迟。一般人耳无法感知的蓝牙段链路时延是25-30

    2024年01月17日
    浏览(35)
  • 【蓝桥杯-筑基篇】贪心

    🍓系列专栏:蓝桥杯 🍉个人主页:个人主页 目录 1.找零问题 ①暴力枚举 ②贪心 2.人性总是贪婪的 3.堆果子 4.图书推荐 有币种 1 、 2 、 4 、 5 、 10 若干张,找零 n 元,输出找零方案。 ①暴力枚举 这是一个找零问题,我们需要找到一种方案,使得用给定的硬币找零时,所需的

    2024年01月18日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包