类的加载机制,反射,内省,类的加密和自定义加载器

来源:互联网 发布:会计报表软件 编辑:程序博客网 时间:2024/06/16 13:17

JVM允许提前加载类。

当程序要使用某个类的时候,如果该类还未被加载到内存中,系统会通过加载,连接,初始化三步实现对类的初始化

加载

将class文件读入内存,并为之创建一个Class对象

任何类被使用时都会建立一个Class对象

连接

验证 是否有正确的内部结构,并和其他类协调一致

准备 负责为类的静态成员分配内存,并设置默认初始化值

解析 将类的二进制数据中的符号引用替换为直接引用

类的初始化时机(可接触的层面)

创建类的实例

访问类的静态变量,或者是为类的静态变量赋值

调用类的静态方法

使用反射方式来强制创建某个类或者接口对应的java.lang.Class对象

初始化某个类的子类

直接使用java.exe来运行某个主类

类加载器的分类

启动类加载器:JDK提供的类lib下

拓展类加载器:lib下的ext内的

停用程序类加载器:加载ClassPath

这就是把class文件加载到内存中的加载器

反射:对于任意一个类都可以知道它的成员组成部分,当然,必须先获取字节码文件对象

获取字节码文件对象的三种方式

public static void main(String[] args) throws Exception {//先要获取Person类的字节码文件对象Class clazz=Person.class;Person p = new Person();Class clazz2 = p.getClass();Class clazz3 = Class.forName("com.practice921.Person");System.out.println(clazz==clazz2);System.out.println(clazz3==clazz2);}
类.class

对象.getClass

Class.forName();

第三种最常用,右键类名,copy qualified name


获取构造方法的几种方式

public static void main(String[] args) throws Exception {Class clazz = Class.forName("com.practice921.Person");//获取指定的公共构造方法对象Constructor c = clazz.getConstructor(null);System.out.println(c);//获取所有公共的构造方法Constructor[] constructors = clazz.getConstructors();//获取所有的构造方法//Constructor[] declaredConstructors = clazz.getDeclaredConstructors();showConstructor(constructors);}private static void showConstructor(Constructor[] constructors) {// TODO Auto-generated method stubfor (Constructor c : constructors) {System.out.println(c);}}
创建对象方法也很多。。。

简而言之。

Constructor c = clazz.getDeclaredConstructor(String.class,int.class);

Object obj=c.newInstance("张三",18);

System.out.println(obj);


不管怎样,先获取各种构造方法,再用构造方法创建对象

c.setAccessible(true);

暴力反射,为true取消java语法检查

标准解剖构造方法

Constructor c = clazz.getDeclaredConstructor(String.class,int.class);c.setAccessible(true);Object obj=c.newInstance("张三",18);System.out.println(obj);

获取成员方法

public class ReflectMethod {public static void main(String[] args) throws Exception {Class clazz = Class.forName("com.practice921.Person");// 获取本类及父类中的成员方法Method[] methods = clazz.getMethods();// for (Method m : methods) {// System.out.println(m);// }// 获取类或者接口指定的公共的成员方法对象Method method2 = clazz.getMethod("show", null);// 获取本类或者接口所有的成员方法对象Method method = clazz.getDeclaredMethod("test", String.class);// 执行一下方法// public object invoke(object obj,Object...args)// obj:从中调用底层方法的对象// args:用于方法调用的参数Person obj = (Person) clazz.newInstance();method.invoke(obj, "qaq");// 该method方法对象想在obj对象上面调用,参数为qaqSystem.out.println(method);}}

标准解剖成员方法

Method method = clazz.getDeclaredMethod("test", String.class);method.setAccessible(true);Object result = method.invoke(clazz.newInstance(), "qaq");// 该method方法对象想在obj对象上面调用,参数为qaqSystem.out.println(result);

获取成员变量

Class clazz = Class.forName("com.practice921.Person");//获取该类或者接口的所有公共字段//Field[] fields = clazz.getFields();//获取该类或者接口中所有的成员变量//Field[] fields = clazz.getDeclaredFields();//获取该类或者接口指定的公共修饰的字段对象//Field field = clazz.getField("age");//获取默认修饰符的字段对象Field field = clazz.getDeclaredField("sex");

标准解剖成员变量

Object obj = clazz.newInstance();Field field = clazz.getDeclaredField("sex");field.setAccessible(true);field.set(obj, '男');System.out.println(obj);



反射:内省的使用(避免一直使用暴力反射)

//通过内省技术:给一个字段的名字和字节码文件对象就可以得到关于该字段的设置和读取方法public class ReflectDemo {public static void main(String[] args) throws Exception {//new Test().setAge(-10);Class clazz = Class.forName("com.practice922.Test");Field field = clazz.getDeclaredField("age");Object obj = clazz.newInstance();field.setAccessible(true);field.set(obj, -1);System.out.println(obj);}}class Test{private int age;public int getAge() {return age;}public void setAge(int age) throws AgeException {if(age<0||age>260){throw new AgeException();}this.age = age;}@Overridepublic String toString() {return "Test [age=" + age + "]";}}
自定义的Exception
public class AgeException extends Exception {@Overridepublic String getMessage() {return "年龄输入错误";}}
此时暴力反射强行写入,不会报错,不能使用。



内省

public static void main(String[] args) throws Exception {//new Test().setAge(-10);Class clazz = Class.forName("com.practice922.Test");//Field field = clazz.getDeclaredField("age");Object obj = clazz.newInstance();//field.setAccessible(true);//field.set(obj, -1);//System.out.println(obj);PropertyDescriptor pd = new PropertyDescriptor("age", clazz);//获取用于写入属性值的方法Method writeMethod = pd.getWriteMethod();writeMethod.invoke(obj, 18);//获取用于读属性值的方法Method readMethod = pd.getReadMethod();Object age = readMethod.invoke(obj, null);System.out.println(age);System.out.println(obj);}}

PropertyDescriptor pd = new PropertyDescriptor("age", clazz, "getage", "setage");直接获取读取属性值的方法


自定义类加载器(类的加密和用自己的加载器进行加载)

public class MyClassLoaderDemo {public static void main(String[] args) throws Exception {Person p = new Person();System.out.println(p);// 一个数异或一个数值两次,值是本身// int a = 10;// System.out.println(a = a ^ 89);// System.out.println(a = a ^ 89);byte[] bytes = XORClass("/Users/yoofale/Documents/JAVASEworkspace/Practice/bin/com/practice922/Person.class");ByteArrayInputStream bais = new ByteArrayInputStream(bytes);int len;byte[] buf = new byte[1024];BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("/Users/yoofale/Documents/JAVASEworkspace/Practice/bin/com/practice922/Person.class"));while ((len = bais.read()) != -1) {bos.write(buf, 0, len);bos.flush();}bos.close();System.out.println("加密成功");}/** *  * 加密或者解密字节码文件 *  * @param classPath * @return 加密或者解密之后的文件数组 * @throws Exception */public static byte[] XORClass(String classPath) throws Exception {BufferedInputStream bis = new BufferedInputStream(new FileInputStream(classPath));byte[] buf = new byte[1024];int len;ByteArrayOutputStream baos = new ByteArrayOutputStream();while ((len = bis.read(buf)) != -1) {baos.write(buf, 0, len);baos.flush();}byte[] byteArray = baos.toByteArray();// 将所有字节都异或成一个值,实现加密for (int i = 0; i < byteArray.length; i++) {byte b = byteArray[i];byteArray[i] = (byte) (b ^ 32);}bis.close();return byteArray;}}

自定义的加载器

public class MyClassLoder extends ClassLoader {@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {try {// TODO Auto-generated method stubbyte[] bytes = ClassUtils.XORClass(name);return defineClass(null, bytes, 0, bytes.length);} catch (ClassFormatError | Exception e) {// TODO Auto-generated catch blocke.printStackTrace();return null;}}}

封装的加密

public class ClassUtils {/** *  * 加密或者解密字节码文件 *  * @param classPath * @return 加密或者解密之后的文件数组 * @throws Exception */public static byte[] XORClass(String classPath) throws Exception {BufferedInputStream bis = new BufferedInputStream(new FileInputStream(classPath));byte[] buf = new byte[1024];int len;ByteArrayOutputStream baos = new ByteArrayOutputStream();while ((len = bis.read(buf)) != -1) {baos.write(buf, 0, len);baos.flush();}byte[] byteArray = baos.toByteArray();// 将所有字节都异或成一个值,实现加密for (int i = 0; i < byteArray.length; i++) {byte b = byteArray[i];byteArray[i] = (byte) (b ^ 32);}bis.close();return byteArray;}}

主程序
public class MyClassLoaderDemo {public static void main(String[] args) throws Exception {// Person p = new Person();// System.out.println(p);MyClassLoder classLoder = new MyClassLoder();Class clazz = classLoder.loadClass("/Users/yoofale/Documents/JAVASEworkspace/Practice/bin/com/practice922/Person.class");System.out.println(clazz.newInstance());}// 一个数异或一个数值两次,值是本身// int a = 10;// System.out.println(a = a ^ 89);// System.out.println(a = a ^ 89);public static void test() throws Exception {byte[] bytes = ClassUtils.XORClass("/Users/yoofale/Documents/JAVASEworkspace/Practice/bin/com/practice922/Person.class");ByteArrayInputStream bais = new ByteArrayInputStream(bytes);int len;byte[] buf = new byte[1024];BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("/Users/yoofale/Documents/JAVASEworkspace/Practice/bin/com/practice922/Person.class"));while ((len = bais.read(buf)) != -1) {bos.write(buf, 0, len);bos.flush();}bos.close();System.out.println("加密成功");}}

原创粉丝点击