更多Android高级架构进阶视频学习请点击:https://space.bilibili.com/474380680
本篇文章将从以下几个内容来阐述反射与类加载:

  • [三种获取Class对象的方式]
  • [获取构造器实例化对象与属性信息]
  • [Android 配置打包签名信息的两种方法]
  • [Hook动态注入代码]

一、反射基本概念与三种获取Class对象的方式

Class类是一切的反射根源。
Class类表示什么?
很多的人--可以定义一个Person类(有年龄,性别,姓名等)
很多的车--可以定义一个Car类(有发动机,颜色,车轮等)
很多的类--Class类(类名,构造方法,属性,方法)

得到Class类的对象有三种方式:
第一种:Object类中的getClass()方法
第二种:类.class
第三种:通过Class类的forName()方法

为什么要学习反射?
反射可以通过一个Class类的对象反过来获取目标类的类信息(私有的公有的属性,方法等);
javaEE框架源码大多是采用反射的方式实现,学习此可为学习javaEE框架做铺垫
代码如下:

  1. import org.junit.Test;
  2. import java.lang.reflect.Constructor;
  3. import java.lang.reflect.Field;
  4. import java.lang.reflect.InvocationTargetException;
  5. import java.lang.reflect.Modifier;
  6. public class ReflectionDemo {
  7. /**
  8. * 三种获取Class对象的的方式:
  9. * 1、对象.getClass()
  10. * 2、类名.class
  11. * 3、Class.forName("完整的类路径")
  12. */
  13. @Test
  14. public void getClassTest() {
  15. //第一种:对象.getClass()
  16. Dog dog = new Dog("wangwang", 2, "yellow");
  17. Class<? extends Dog> aClass = dog.getClass();
  18. //第二种:类名.class
  19. Class<Dog> dogClass = Dog.class;
  20. //第三种:Class.forName("完整的类路径")
  21. try {
  22. Class<?> aClass1 = Class.forName("com.vince.Dog");
  23. } catch (ClassNotFoundException e) {
  24. e.printStackTrace();
  25. }
  26. }
  27. }

二、获取构造器实例化对象与属性信息

代码如下:

  1. import org.junit.Test;
  2. import java.lang.reflect.Constructor;
  3. import java.lang.reflect.Field;
  4. import java.lang.reflect.InvocationTargetException;
  5. import java.lang.reflect.Modifier;
  6. public class ReflectionDemo {
  7. /**
  8. * 通过反射来实例化目标类对象
  9. */
  10. @Test
  11. public void test1(){
  12. Class<Dog> dogClass = Dog.class;
  13. try {
  14. //通过Class对象(dogClass)来实例化类对象,调用了该类默认的无参的构造方法(所以创建Dog类的时候无参的构造方法要保留呢);此时可以正常使用dog对象了
  15. Dog dog = ((Dog) dogClass.newInstance()); //所以这里是没有参数的newInstance()
  16. } catch (InstantiationException e) {
  17. e.printStackTrace();
  18. } catch (IllegalAccessException e) {
  19. e.printStackTrace();
  20. }
  21. }
  22. @Test
  23. public void test2(){
  24. Class<Dog> dogClass = ( Dog.class);
  25. //获取目标类的所有构造方法
  26. Constructor<?>[] constructors = dogClass.getConstructors(); //通过Class对象(dogClass),得到该类Dog的所有构造方法。返回的是一个数组
  27. for (int i = 0; i <constructors.length ; i++) {
  28. System.out.println(constructors[i].getName()); //循环遍历,获取每个构造方法的名称
  29. System.out.println(constructors[i].getParameterCount()); //获取每个构造方法的参数数量
  30. System.out.println(constructors[i].getParameterTypes()); //获取构造方法的参数类型
  31. }
  32. //获取指定的构造器
  33. try {
  34. //获取指定的构造器;因为参数要传入原类的属性,所以用String.class 也是一种“类名.class”的方式
  35. Constructor<Dog> constructor = dogClass.getConstructor(String.class, int.class, String.class);
  36. //调用带参数的构造器来实例化对象,因为有参所以这里newInstance()需要传入具体的参数
  37. Dog dog = constructor.newInstance("haha", 3, "white");
  38. } catch (NoSuchMethodException e) {
  39. e.printStackTrace();
  40. } catch (IllegalAccessException e) {
  41. e.printStackTrace();
  42. } catch (InstantiationException e) {
  43. e.printStackTrace();
  44. } catch (InvocationTargetException e) {
  45. e.printStackTrace();
  46. }
  47. }
  48. //获取属性
  49. @Test
  50. public void test3(){
  51. Class<Dog> dogClass = Dog.class;
  52. //获取目标类的所有属性的的一个抽象对象,返回的是一个数组;且这种只能获取公有的属性
  53. Field[] fields = dogClass.getFields();
  54. //System.out.println(fields.length);
  55. //获取私有的以及公有的属性;即所有的属性
  56. Field[] declaredFields = dogClass.getDeclaredFields();
  57. //System.out.println(declaredFields.length);
  58. int len = declaredFields.length;
  59. for (int i = 0; i <len ; i++) {
  60. int modifiers = declaredFields[i].getModifiers(); //获取每个属性的修饰符,但是这么获取的是修饰符的整数值(JVM自动给转换了)
  61. String modifilesName = Modifier.toString(modifiers); //因此可用修饰符的一个类Modifier.toString()方法再转换成字符串
  62. System.out.println(modifilesName+" "+declaredFields[i].getType()+" "+declaredFields[i].getName());
  63. }
  64. }
  65. }

三、Android 配置打包签名信息的两种方法

目录结构如下:

 
 

有2种方式:

第一种,直接配置:

  1. signingConfigs {
  2. debug {
  3. storeFile file("app/keystore.properties")
  4. storePassword "111111"
  5. keyAlias "key"
  6. keyPassword "111111"
  7. }
  8. release {
  9. storeFile file("app/keystore.properties")
  10. storePassword "111111"
  11. keyAlias "key"
  12. keyPassword "111111"
  13. }
  14. }
  15. buildTypes {
  16. debug {
  17. signingConfig signingConfigs.debug
  18. }
  19. release {
  20. minifyEnabled false
  21. proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
  22. signingConfig signingConfigs.release
  23. }
  24. }

第二种,通过读取文件

新建keystore.properties文件

  1. storeFile=keyStore.jks
  2. storePassword=123456
  3. keyAlias=encrypt
  4. keyPassword=123456

  1. build.gradle配置
  2. signingConfigs {
  3. // 从keystore.properties文件中读取信息
  4. def keystorePropertiesFile = rootProject.file("app/keystore.properties")
  5. def keystoreProperties = new Properties()
  6. keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
  7. debug {
  8. println("======== debug mode: set key ========")
  9. storeFile file(keystoreProperties['storeFile'])
  10. storePassword keystoreProperties['storePassword']
  11. keyAlias keystoreProperties['keyAlias']
  12. keyPassword keystoreProperties['keyPassword']
  13. }
  14. release {
  15. println("======== release mode: set key ========")
  16. storeFile file(keystoreProperties['storeFile'])
  17. storePassword keystoreProperties['storePassword']
  18. keyAlias keystoreProperties['keyAlias']
  19. keyPassword keystoreProperties['keyPassword']
  20. }
  21. }
  22. buildTypes {
  23. debug {
  24. signingConfig signingConfigs.debug
  25. }
  26. release {
  27. minifyEnabled false
  28. proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
  29. signingConfig signingConfigs.release
  30. }
  31. }

四、Hook动态注入代码

Hook机制是回调机制的一种,普通的回调是静态的,我们必须提前写好回调接口;而Hook机制在Java中则可以利用反射,针对切入点(通常是一个成员变量),采用替换的手段,使代码在运行时改变,听起来有些抽象,下面简单介绍下,然后我看代码。

寻找适合Hook点,它应该是一个成员变量,并且应该在我们需要注入的方法中调用过它的方法,或者使用了它的的值;
创建继承自Hook点的对象的子类,根据需求修改其相应的方法;
使用反射将我们自己创建的对象替换对象实例中的对象,达到偷梁换柱的目的。

  1. public class Hero {
  2. private Weapon weaponMain;
  3. public Hero(Weapon weaponMain) {
  4. this.weaponMain = weaponMain;
  5. }
  6. public void attack(){
  7. weaponMain.attack();
  8. }
  9. }
  10. public class Weapon {
  11. int damage = 10;
  12. public void attack(){
  13. System.out.println(String.format("对目标造成 %d 点伤害",damage));
  14. }
  15. }
  16. public class Game{
  17. public static void main(String[] args){
  18. Hero hero = new Hero(new Weapon());
  19. hero.attack();
  20. }
  21. }
  22. //对于上面这段程序,游戏对我们隐藏了Weapon的伤害值,但现在我们想要在每次攻击的时候知道这个伤害值是多少。
  23. //下面看看使用Hook机制如何来实现。
  24. //首先我们通过观察,发现切入点就是weaponMain,我们要对它下手。
  25. //创建一个Weapon的复制品WeaponHook,我们需要用自己的人WeaponHook打入内部。
  26. //WeaponHook一切看起来都和Weapon那么相似,但是我们给它留了一个后门,使得我们可以进行监控。
  27. public class WeaponHook extends Weapon{
  28. private OnUseWeaponAttackListener onUseWeaponAttackListener;
  29. @Override
  30. public void attack(){
  31. super.attack();
  32. if (onUseWeaponAttackListener != null){
  33. onUseWeaponAttackListener.onUseWeaponAttack(damage);
  34. }
  35. }
  36. public void setOnUseWeaponAttackListener(OnUseWeaponAttackListener onUseWeaponAttackListener) {
  37. this.onUseWeaponAttackListener = onUseWeaponAttackListener;
  38. }
  39. //这就是我们的后门
  40. public static interface OnUseWeaponAttackListener {
  41. int onUseWeaponAttack(int damage);
  42. }
  43. }
  44. //下面看看如何来进行“偷天换日”
  45. public class Game{
  46. public static void main(String[] args){
  47. Hero hero = new Hero(new Weapon());
  48. try {
  49. Field weapon = ReflectUtils.getVariable(hero.getClass(), "weaponMain");
  50. weapon.setAccessible(true);
  51. Weapon weaponHook = new WeaponHook();
  52. ((WeaponHook) weaponHook).setOnUseWeaponAttackListener(damage -> {
  53. //通过后门进行操作,这其实就是我们注入的代码
  54. System.out.println("damage = " + damage);
  55. return damage;
  56. });
  57. weapon.set(hero, weaponHook); //tou tian偷天换日
  58. hero.attack();
  59. } catch (NoSuchFieldException e) {
  60. e.printStackTrace();
  61. } catch (IllegalAccessException e) {
  62. e.printStackTrace();
  63. }Hero hero = new Hero(new Weapon());
  64. hero.attack();
  65. }
  66. }
  67. //看输出
  68. 对目标造成 10 点伤害
  69. damage = 10 //我们获得了Weapon的伤害值

总结
由于内容不多,总结我就不回顾前面了,我们来看看一种防止Hook入侵的一种思路。
我们在Hero类中加入一个检查机制。

  1. public class Hero {
  2. private Weapon weaponMain;
  3. private final int weaponMainId;
  4. public Hero(Weapon weaponMain) {
  5. this.weaponMain = weaponMain;
  6. weaponMainId = this.weaponMain.hashCode();//记录原始Weapon对象的Id,hashCode对于每个对象而言都是唯一的。
  7. }
  8. public void attack() {
  9. if (this.weaponMain.hashCode() != weaponMainId) { //关键位置检查是否遭到替换
  10. throw new IllegalAccessError(String.format("警告!遭到入侵!入侵者身份:%d", this.weaponMain.hashCode()));
  11. }
  12. weaponMain.attack();
  13. }
  14. }

现在再次运行程序,输出如下:

  1. java.lang.IllegalAccessError: 警告!遭到入侵!入侵者身份:1288141870

更多Android高级架构进阶视频学习请点击:https://space.bilibili.com/474380680
参考:https://www.jianshu.com/p/8bf24de75a7a
https://blog.csdn.net/qq_31370269/article/details/85780165
https://www.cnblogs.com/danew/p/11511952.html
https://www.jianshu.com/p/1a0c368da1b8

反射与类加载之反射基本概念与Class(一)的更多相关文章

  1. java JDK8 学习笔记——第17章 反射与类加载器

    第十七章 反射与类加载器 17.1 运用反射 反射:.class文档反映了类基本信息,从Class等API取得类信息的方式称为反射. 17.1.1 Class与.class文档 1.java.lang ...

  2. 处理异常、常用类、反射、类加载与垃圾回收、java集合框架

    异常处理概述 检查异常:检查异常通常是用户错误或者不能被程序员所预见的问题.(cheched) 运行时异常:运行时异常是一个程序在运行过程中可能发生的.可以被程序员避免的异常类型.(Unchecked ...

  3. 【java】解析java类加载与反射机制

    目录结构: contents structure [+] 类的加载.连接和初始化 类的加载 类的连接 类的初始化 类加载器 类加载器机制 自定义类加载器 URLClassLoader类 反射的常规操作 ...

  4. 反射与类加载之ClassLoader与类加载器(二)

    更多Android高级架构进阶视频学习请点击:https://space.bilibili.com/474380680本篇文章将从以下几个内容来阐述反射与类加载: [动态代理模式] [Android ...

  5. [C#反射]C#中的反射解析及使用.

    1.对C#反射机制的理解2.概念理解后,必须找到方法去完成,给出管理的主要语法3.最终给出实用的例子,反射出来dll中的方法 参考: C#反射,MSDN编程指南 反射是一个程序集发现及运行的过程,通过 ...

  6. C#工具:反射帮助类 泛型反射帮助类

    反射帮助类 using System; using System.Reflection; using System.Data; using System.Drawing; using System.R ...

  7. .NET/C# 反射的的性能数据,以及高性能开发建议(反射获取 Attribute 和反射调用方法)——转载

    原文链接:https://blog.walterlv.com/post/dotnet-high-performance-reflection-suggestions.html ***** 大家都说反射 ...

  8. 反射入门-浅谈反射用途_根据Ado游标对象创建list集合

    本人大二菜鸟一只,今天在上课期间有个同学看着C#反射的内容说反射没什么用,一时之间也想不到什么更好的例子,就写了个根据泛型类型和游标反射创建List集合的Demo. 首先创建一个用于封装对应数据的en ...

  9. [Java反射基础四]通过反射了解集合泛型的本质

    本文接上文"方法反射的基本操作",利用反射了解下java集合中泛型的本质 1.初始化两个集合,一个使用泛型,一个不使用 ArrayList list1 = new ArrayLis ...

随机推荐

  1. python学习笔记:操作Excle

    import xlwt #写excel import xlrd #读excel import xlutils #修改excel 一.写操作 1.写Excel import xlwt #写excel,导 ...

  2. vue.js实现点击后动态添加class及删除同级class

    最近使用vue需要实现一个点餐选择商品规格的页面,需要通过vue动态的给被点击的元素添加class名字,使其变色,其他的删除class.如图: 开始在网上找了许多办法发现不是太好用,最后找到一个发现还 ...

  3. c# SqlBulkCopy实现批量从数据集中把数据导入到数据库中

    今天遇到了一个导入类第一次见 SqlBulkCopy 可以实现从一个数据集导入到数据库中的表中 本来想从数据集中一条条遍历insert到库中 有了这个后发现: 只在把表与数据集的列做一下对应关系,再走 ...

  4. byte为什么要与0xff

    面对带正负号的数,会采用符号扩展,如果原值是正数,则高位补上0:如果原值是负数,高位补1.二进制是计算技术中广泛采用的一种数制.二进制数据是用0和1两个数码来表示的数.当前的计算机系统使用的基本上是二 ...

  5. Activiti学习笔记3 — 流程定义

    一.创建流程引擎对象 private ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); 二.发布一个流程 ...

  6. junit单元测试报错Failed to load ApplicationContext,但是项目发布到tomcat浏览器访问没问题

    junit单元测试报错Failed to load ApplicationContext,但是项目发布到tomcat浏览器访问没问题,说明代码是没问题的,配置也没问题.开始时怀疑是我使用junit版本 ...

  7. DoubleCache

    DoubleCache 指的是本地+redis两份缓存模式 本地缓存过期之后从redis读取新数据 redis缓存过期时,从业务里读取新数据. 设计原理: 利用 loadingCache的过期刷新来实 ...

  8. shell 脚本文件类型.sh ,变量

    1. shell脚本编程的基本过程 (1)建立shell文件,以 .sh 结尾的文件 (2)赋予shell文件执行权限,chmod 0777 文件名 (3)执行shell文件, ./ 文件名 或者ba ...

  9. ubuntu安装及破解WebStorm11

    ubuntu安装及破解WebStorm11 下载地址:http://www.jetbrains.com/webstorm/download/#section=linux-version 1.qingy ...

  10. Docker 部署 nginx 前端项目

    docker pull nginx docker run -itd -p 82:80 -v /opt/soft/nginx/conf/nginx.conf:/etc/nginx/nginx.conf ...