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

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

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

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

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

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

import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier; public class ReflectionDemo {
/**
* 三种获取Class对象的的方式:
* 1、对象.getClass()
* 2、类名.class
* 3、Class.forName("完整的类路径")
*/
@Test
public void getClassTest() { //第一种:对象.getClass()
Dog dog = new Dog("wangwang", 2, "yellow");
Class<? extends Dog> aClass = dog.getClass(); //第二种:类名.class
Class<Dog> dogClass = Dog.class; //第三种:Class.forName("完整的类路径")
try {
Class<?> aClass1 = Class.forName("com.vince.Dog"); } catch (ClassNotFoundException e) {
e.printStackTrace();
} }
}

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

代码如下:

import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier; public class ReflectionDemo {
/**
* 通过反射来实例化目标类对象
*/
@Test
public void test1(){
Class<Dog> dogClass = Dog.class;
try {
//通过Class对象(dogClass)来实例化类对象,调用了该类默认的无参的构造方法(所以创建Dog类的时候无参的构造方法要保留呢);此时可以正常使用dog对象了
Dog dog = ((Dog) dogClass.newInstance()); //所以这里是没有参数的newInstance()
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} } @Test
public void test2(){
Class<Dog> dogClass = ( Dog.class);
//获取目标类的所有构造方法
Constructor<?>[] constructors = dogClass.getConstructors(); //通过Class对象(dogClass),得到该类Dog的所有构造方法。返回的是一个数组
for (int i = 0; i <constructors.length ; i++) {
System.out.println(constructors[i].getName()); //循环遍历,获取每个构造方法的名称
System.out.println(constructors[i].getParameterCount()); //获取每个构造方法的参数数量
System.out.println(constructors[i].getParameterTypes()); //获取构造方法的参数类型
} //获取指定的构造器
try {
//获取指定的构造器;因为参数要传入原类的属性,所以用String.class 也是一种“类名.class”的方式
Constructor<Dog> constructor = dogClass.getConstructor(String.class, int.class, String.class);
//调用带参数的构造器来实例化对象,因为有参所以这里newInstance()需要传入具体的参数
Dog dog = constructor.newInstance("haha", 3, "white"); } catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} } //获取属性
@Test
public void test3(){
Class<Dog> dogClass = Dog.class;
//获取目标类的所有属性的的一个抽象对象,返回的是一个数组;且这种只能获取公有的属性
Field[] fields = dogClass.getFields();
//System.out.println(fields.length); //获取私有的以及公有的属性;即所有的属性
Field[] declaredFields = dogClass.getDeclaredFields();
//System.out.println(declaredFields.length); int len = declaredFields.length;
for (int i = 0; i <len ; i++) {
int modifiers = declaredFields[i].getModifiers(); //获取每个属性的修饰符,但是这么获取的是修饰符的整数值(JVM自动给转换了)
String modifilesName = Modifier.toString(modifiers); //因此可用修饰符的一个类Modifier.toString()方法再转换成字符串
System.out.println(modifilesName+" "+declaredFields[i].getType()+" "+declaredFields[i].getName()); } } }

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

目录结构如下:

 
 

有2种方式:

第一种,直接配置:

signingConfigs {
debug {
storeFile file("app/keystore.properties")
storePassword "111111"
keyAlias "key"
keyPassword "111111"
}
release {
storeFile file("app/keystore.properties")
storePassword "111111"
keyAlias "key"
keyPassword "111111"
}
}
buildTypes {
debug {
signingConfig signingConfigs.debug
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
}

第二种,通过读取文件

新建keystore.properties文件

storeFile=keyStore.jks
storePassword=123456
keyAlias=encrypt
keyPassword=123456

build.gradle配置
signingConfigs {
// 从keystore.properties文件中读取信息
def keystorePropertiesFile = rootProject.file("app/keystore.properties")
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
debug {
println("======== debug mode: set key ========")
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
}
release {
println("======== release mode: set key ========")
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
}
}
buildTypes {
debug {
signingConfig signingConfigs.debug
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
}

四、Hook动态注入代码

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

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

public class Hero {
private Weapon weaponMain; public Hero(Weapon weaponMain) {
this.weaponMain = weaponMain;
} public void attack(){
weaponMain.attack();
}
} public class Weapon {
int damage = 10; public void attack(){
System.out.println(String.format("对目标造成 %d 点伤害",damage));
}
} public class Game{
public static void main(String[] args){
Hero hero = new Hero(new Weapon());
hero.attack();
}
}
//对于上面这段程序,游戏对我们隐藏了Weapon的伤害值,但现在我们想要在每次攻击的时候知道这个伤害值是多少。
//下面看看使用Hook机制如何来实现。 //首先我们通过观察,发现切入点就是weaponMain,我们要对它下手。
//创建一个Weapon的复制品WeaponHook,我们需要用自己的人WeaponHook打入内部。
//WeaponHook一切看起来都和Weapon那么相似,但是我们给它留了一个后门,使得我们可以进行监控。
public class WeaponHook extends Weapon{
private OnUseWeaponAttackListener onUseWeaponAttackListener; @Override
public void attack(){
super.attack();
if (onUseWeaponAttackListener != null){
onUseWeaponAttackListener.onUseWeaponAttack(damage);
}
} public void setOnUseWeaponAttackListener(OnUseWeaponAttackListener onUseWeaponAttackListener) {
this.onUseWeaponAttackListener = onUseWeaponAttackListener;
} //这就是我们的后门
public static interface OnUseWeaponAttackListener {
int onUseWeaponAttack(int damage);
}
} //下面看看如何来进行“偷天换日”
public class Game{
public static void main(String[] args){
Hero hero = new Hero(new Weapon());
try {
Field weapon = ReflectUtils.getVariable(hero.getClass(), "weaponMain");
weapon.setAccessible(true);
Weapon weaponHook = new WeaponHook();
((WeaponHook) weaponHook).setOnUseWeaponAttackListener(damage -> {
//通过后门进行操作,这其实就是我们注入的代码
System.out.println("damage = " + damage);
return damage;
});
weapon.set(hero, weaponHook); //tou tian偷天换日
hero.attack();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}Hero hero = new Hero(new Weapon());
hero.attack();
}
}
//看输出
对目标造成 10 点伤害
damage = 10 //我们获得了Weapon的伤害值

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

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

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

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. 解决(Oracle)ORA-12528: TNS: 监听程序: 所有适用例程都无法建立新连接 问题

    解决(Oracle)ORA-12528: TNS: 监听程序: 所有适用例程都无法建立新连接 问题通过在CMD下用lsnrctl status 查看出的问题:发现BLOCKEDORACLE启动步骤:s ...

  2. Linux下安装JDK(小白教程)

    一.      选择与下载jdk 1. 官网上按照自己的系统版本下载相应jdk,因为我的LINUX(testbest)是32位的,所以我下载32位的jdk. 2. 官网下载地址:http://www. ...

  3. css3水平垂直居中(不知道宽高同样适用)

    css水平垂直居中 第一种方法: 在父div里加: display: table-cell; vertical-align: middle; text-align: center; 内部div设置: ...

  4. docker对容器进行资源限制

    对内存进行资源限制 docker run --memory=200m soymilk/stress 对cpu进行资源限制 终端1 docker run --name=test1 --cpu-share ...

  5. HTTP、HTTP1.0、HTTP1.1、HTTP2.0、HTTPS

      一.HTTP HTTP(超文本传输协议,HyperText Transfer Protocol)是应用层的协议,目前在互联网中应用广泛. 它被设计用于Web浏览器和Web服务器之间的通信,但它也可 ...

  6. npm install 时 提示err code EINTEGRITY报错

    1.报错截图如图: 2.报错缘由:因npm 版本问题导致 3.解决方法:升级npm版本(npm i -g npm),再重新npm install即可.

  7. OpenGL学习——搭建OpenGL运行环境——生成一个空白视口——基于GLFW&GLEW

    最近学习OpenGL,读OpenGL宝典一头蒙,各种gl函数不知所云.逐决定先搭OpenGL运行环境,详细如下. 1.首先OpenGL是什么?是一个标准规范,是一个巨大的状态机,并无具体实现,大多数实 ...

  8. Spring Cloud服务保护

    微服务虽然解决了传统单体式应用各个模块之间强耦合的缺点,但同时也引出了新问题,由于微服务各个服务之间是独立部署的,并且一般情况下一个服务往往会依赖多个其他服务,并且服务之间的调用更多的是依赖不稳定的网 ...

  9. InnoDB global status

    常见参数 Innodb_buffer_pool_pages_free 发现 Innodb_buffer_pool_pages_free 为0 ,则说明buffer_pool 已经被用光,需要增大 in ...

  10. python_django_类视图的第一次忐忑碰触!!!

    我们Django学到这里,基础知识阶段是已经完成了!! 在我们前面接触到的视图都是基于函数的视图函数我们可以称它为FBV,而今天我们新接触的就是视图函数的另一类CBV:基于类的视图函数,我们这里拓展这 ...