类加载反射
Posted cmm123
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了类加载反射相关的知识,希望对你有一定的参考价值。
类的加载
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
加载
就是指将class文件读入内存,并为之创建一个Class对象。
任何类被使用时系统都会建立一个Class对象
连接
验证 是否有正确的内部结构,并和其他类协调一致
准备 负责为类的静态成员分配内存,并设置默认初始化值
解析 将类的二进制数据中的符号引用替换为直接引用
初始化
初始化阶段,虚拟机负责对类的变量进行初始化
- 声明类变量是指定初始值
- 使用静态初始化块为类变量指定初始值
Class类的初始化时机
Java虚拟机只有在程序首次主动使用一个类或接口的时候才会初始化它。只有6种活动被看作是程序对类和接口的主动使用:
- 创建类的实例
- 调用类的静态方法
- 访问类或接口的静态变量,或者为静态变量赋值
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
- 初始化某个类的子类
- 直接使用java.exe命令来运行某个主类
反射
JAVA反射机制是在运行状态中:
对于任意一个类,都能够知道这个类的所有属性和方法
对于任意一个对象,都能够调用它的任意一个方法和属性
这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象。
类也是一种类型
获取Class对象三种方式
方式一:调用Object类的getClass():任何类都会继承此方法
Person p = new Person();
Class c = p.getClass()
l 方式二:调用某个类的class属性来获取该类对应的Class对象
Class s = Student.class
l 方式三:调用Class类的静态方法forName(String clazzName),参数的值是某个类的全限定类名(必须添加完整包名)
Class c3 = Class.forName("类的全限定类名");
目的:通过这个Class对象,可以获取Student类内部的成员属性、构造方法、成员方法的一些信息,并能够调用它们
学生类
class Student { // 成员变量 public String name; public int age; private String address; // 构造方法 public Student() { System.out.println("空参数构造方法"); } public Student(String name) { this.name = name; System.out.println("带有String的构造方法"); } // 私有的构造方法 private Student(String name, int age) { this.name = name; this.age = age; System.out.println("私有-------带有String,int的构造方法"); } public Student(String name, int age, String address) { this.name = name; this.age = age; this.address = address; System.out.println("带有String, int, String的构造方法"); } // 成员方法 // 没有返回值没有参数的方法 public void m1() { System.out.println("没有返回值没有参数的方法"); } // 没有返回值,有参数的方法 public void m2(String name) { System.out.println("没有返回值,有参数的方法 name= " + name); } // 有返回值,没有参数 public int m3() { System.out.println("有返回值,没有参数的方法"); return 123; } // 有返回值,有参数的方法 public String m4(String name) { System.out.println("有返回值,有参数的方法"); return "哈哈" + name; } // 私有方法 private void m5() { System.out.println("私有方法"); } @Override public String toString() { return "Student [name=" + name + ", age=" + age + ", address=" + address + "]"; } }
通过反射获取构造方法
在反射机制中,把类中的成员(构造方法、成员方法、成员变量)都封装成了对应的类进行表示。其中,构造方法使用类Constructor表示。可通过Class类中提供的方法获取构造方法:
l 获取单个
Constructor getConstructor(Class ... parameterTypes):获取指定的"公有构造方法"
Constructor getDeclaredConstructor(Class ... parameterTypes):获取指定的构造方法(包括私有的)
l 批量获取构造方法
Constructor[] getConstructors() :获取所有的"公有构造方法"
Constructor[] getDeclaredConstructors() :获取全部(包括私有)的构造方法
l 暴力访问:如果想要访问私有成员,需要设置暴力访问
Constructor的setAccessible(true):不进行权限检查
通过反射方式创建对象(公有构造)
步骤如下:
1. 获取到Class对象
2. 获取指定的构造方法
3. 通过构造方法类Constructor中的方法,创建对象
public class Demo { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { // 1. 获取到Class对象 Class clazz = Class.forName("Student的全限定名");// 包名.类名 // 2. 获取指定的构造方法 Constructor c1 = clazz.getConstructor(); // 相当于调用无参构造方法创建对象,返回Object类型的对象 Object o1 = c1.newInstance(); System.out.println("------------------------------------------"); // 获取三个参数的构造方法 Constructor c2 = clazz.getConstructor(String.class, int.class, String.class); // 调用三个参数的构造方法 Object obj = c2.newInstance("小红", 22, "南京"); // Student [name=小红, age=22, address=南京] System.out.println(obj); }
在反射机制中,把类中的成员方法使用类Method表示。可通过Class类中提供的方法获取成员
l 获取多个
Method[] getMethods():获取所有"公有方法"(包括继承的)
Method[] getDeclaredMethods() :获取所有成员方法(包括私有)
l 获取单个成员方法
Method getMethod(String name, Class... parameterTypes) :获取指定的"公有方法"
Method getDeclaredMethod(String name, Class... parameterTypes) :获取指定的方法,包括私有的
l 调用方法:(注意:1.要先创建此类的对象;2.调用私有方法前,要先设置暴力访问)
Method
Object invoke(Object obj, Object... args) :调用Method所代表的方法
返回值:此Method对象调用所代表的方法,所获取的返回值;
形参:
obj:方法所属对象;
args:Method所代表的那个方法的实参列表
以上是关于类加载反射的主要内容,如果未能解决你的问题,请参考以下文章
Android 逆向类加载器 ClassLoader ( 类加载器源码简介 | BaseDexClassLoader | DexClassLoader | PathClassLoader )(代码片段