Java 根底巩固-反射机制(四)

1 、反射(经过镜子看到类的结构)

  • 反射能够在程序运转期间凭借 RefectionApi取得任何类的内部信息(比方成员变量,结构器,成员办法等),而且能操作目标的特点和办法。反射在规划模式和框架底层都会用到
  • 加载完类之后,在堆中产生了一个Class类型的一个目标,这个目标包括了类的完好结构信息。经过这个目标得到类的结构。

1.1 反射原理图

Java 基础巩固-反射机制(四)

1.2 反射快速入门

// classfullpath=com.lyq.Cat
// method=cry
Properties properties = new Properties();
properties.load(new FileInputStream("src\re.properties"));
String classfullpath = properties.get("classfullpath").toString();
String methodName = properties.get("method").toString();
System.out.println(classfullpath);
System.out.println(methodName);
// 反射快速入门
// 1. 加载类
Class<?> aClass = Class.forName(classfullpath);
// 2. 得到 cls 得到加载类 com.lyq.Cat 的目标实例
Object cat = aClass.newInstance();
// 3. 经过 cls 得到 Cat 里边的 hi 目标
//    即:在反射中,万物皆目标
Method method = aClass.getMethod(methodName);
// 4. 经过 method 来唤醒 cat 的对应办法,
method.invoke(cat); // 传统是 目标.办法, 反射是: 办法.invoke(目标)

1.3 反射相关类

import java.lang.Class;  // 类目标
import java.lang.reflect.Method; // 类的办法
import java.lang.reflect.Field;  // 类的成员变量
import java.lang.reflect.Constructor; // 代表类的结构办法
Properties properties = new Properties();
properties.load(new FileInputStream("src\re.properties"));
String classfullpath = properties.getProperty("classfullpath");
String methodName = properties.getProperty("method");
Class<?> aClass = Class.forName(classfullpath);
Object o = aClass.getDeclaredConstructor().newInstance();
Method method = aClass.getMethod(methodName);
method.invoke(o);
// getField 不能得到私有特点
Field ageField = aClass.getField("age");
System.out.println(ageField.get(o));
// String.class 代表形参类型
Constructor<?> constructor = aClass.getConstructor(String.class);
System.out.println(constructor);

1.4 反射长处和缺陷

  • 长处:能够动态创立和运用目标,灵敏,没有反射机制,框架技能短少底层支撑
  • 缺陷:运用反射根本是解说履行,对履行速度又影响

1.5 反射优化

  • Method 和 Field 和 Constructor 都有 setAccssible()办法
  • 默以为false,查看拜访安全,能够设置为true,撤销查看

1.6 Class 类

  • Class类也是类,继承 Object
  • Class 不是 new 出来的类,而是类加载器经过 loadClass()办法创立
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }
    
  • 类只会被加载一次
  • Class 目标存放在
  • 类的二进制字节码数据存放在办法区

1.6.1 Class 类常用办法

String s = "com.lyq.Cat";
Class<?> cls = Class.forName(s);
System.out.println(cls.getName());    // 获取全类名  com.lyq.Cat
System.out.println(cls.getClass());   // 获取类名    class java.lang.Class
System.out.println(cls.getPackage()); // 获取包名    package com.lyq
Cat o = (Cat) cls.getConstructor().newInstance();
System.out.println(o);                // Cat{name='小黄', age=15}
// 得到 Cat 类的 age 特点
Field age = cls.getField("age");
System.out.println(age.get(o));       // 15
// 经过反射给特点赋值
age.set(o, 123123123);
System.out.println(age.get(o));
// 获取 Cat 目标的一切特点
Field[] fields = cls.getFields();
for (Field o : fields) {
    System.out.println(o);
}

1.7 获取 Class 目标的六种 方式

  • Class.forName() :多用于配置文件
  • 类.class :多用于参数传递。比方经过反射得到类目标
  • 目标.getClass()
  • 类加载器 getClassLoader() 得到 Class
  • 包装类.TYPE
// 1. Class.forName
Class<?> aClass = Class.forName("com.lyq.Cat");
System.out.println(aClass);
// 2. 类名.class
System.out.println(Cat.class);
// 3. getClass()
Cat cat = new Cat();
System.out.println(cat.getClass());
// 4. 类加载器 Classloader
ClassLoader classLoader = cat.getClass().getClassLoader();
Class<?> aClass1 = classLoader.loadClass("com.lyq.Cat");
System.out.println(aClass1);
// 5. 根本数据类型
Class<Integer> integerClass = int.class;
Class<Character> characterClass = char.class;
// 6. 包装类
Class<Integer> type = Integer.TYPE;

1.8 那些类型有Class目标

  • 外部类、成员内部类、静态内部类、局部内部类、匿名内部类
  • 接口
  • 枚举
  • 数组
  • 注解
  • 根本数据类型
  • void
Class<String> stringClass = String.class; // 外部类
Class<Serializable> serializableClass = Serializable.class; // 接口
Class<Integer[]> aClass = Integer[].class; // 数组
Class<Enum> enumClass = Enum.class; // enum 枚举
System.out.println(Deprecated.class); // 注解
Class<Long> longClass = long.class; // 根本数据类型
Class<Void> voidClass = void.class; // void

1.9 静态加载和动态加载

  • 静态加载:不运用反射,在编译期间就会加载的类,假如没有则报错,依赖性强
  • 动态加载:运用反射,依赖性不强,可是时间久

1.10 类加载进程

Java 基础巩固-反射机制(四)

Java 基础巩固-反射机制(四)

1.10.1 类加载进程详解

  • 加载阶段:jvm在该阶段的首要意图是将字节码从不同的数据源(class、jar包等)转化为二进制字节省加载到内存中,而且生成代表该类的 java.lang.Class目标
  • 连接阶段
    • 验证:

      • 意图是为了确保Class文件的字节省中包括的信息符号JVM机的要求,而且不会危害到JVM机
      • 包括:文件格式验证(是否以魔数 oxcafebabe最初)、元数据验证、字节码严重和符号引证验证
      • 能够考虑运用 -Xverify:none参数关闭大部分的验证办法,缩短虚拟机类加载时间
    • 准备:

      • 在该阶段会对静态变量分配内存并默许初始化(0,null,false等)。这些变量所运用的内存都将在办法区中进行分配
    • 解析:

      • 虚拟机将常量池内的符号引证替换成直接引证的进程
    • 初始化:

      1. 到该阶段,才真正开始履行类中界说的 Java 程序代码,此阶段是履行<clinit>()办法的进程
      2. <clinit>()办法是由编译器句子在源文件中呈现的次序,顺次自动搜集类中的一切静态变量的赋值动作和静态代码块中的句子,并进行合并
      3. 虚拟时机保证一个类的<clinit>()办法在多线程环境下被正确加锁、同步,假如多个线程一起去初始化一个类,那么只有一个类会初始化成功,其他将被堵塞,直到活动线程履行<clinit>()办法结束

1.11 经过反射获取类的结构信息

  1. java.lang.Class 类

    • getName :获取全类名
    • getSimpleName :获取简略类名
    • getFields :获取一切public修饰的特点,包括本类以及父类
    • getMethods :获取一切public修饰的办法,包括本类以及父类
    • getConstructors : 获取一切public修饰的结构器
    • getDeclaredConstructors :获取本类中一切结构器
    • getPackage :以Package方式回来包信息
    • getSuperClass :以Class方式回来父类信息
    • getlnterfaces :以Class方式回来接口信息
    • getAnnotations :以Annotation方式回来注解信息
  2. java.lang.reflect.Method 类

    • getModifiers:以 int 方式回来修饰符
      1. 默许修饰符是 0
      2. public 是 1
      3. private 是 2
      4. protected 是 4
      5. static 是 8
      6. final 是 16
    • getReturnType:以 class 方式获取回来类型
    • getName :回来办法名
    • getParameterTypes:以 Class[]回来参数类型数组
  3. java.lang.reflect.Method 类

    • getModifiers:以 int 方式回来修饰符
      1. 默许修饰符是 0
      2. public 是 1
      3. private 是 2
      4. protected 是 4
      5. static 是 8
      6. final 是 16
    • getType:以 Class 方式回来类型
    • getName : 回来特点名

1.12 反射爆炸创立实例

  1. 经过调用类中的 public 修饰的无参结构器

  2. 调用类中的指定结构器

  3. Class 类相关办法

    • newInstance :调用类中的无参结构器,获取对应类的目标
    • getCOnstructor(Class … clazz) :依据参数列表,获取对应的 public 结构器目标
    • getDecalaredConstructor(Class … class) :依据参数列表,获取全部的结构器目标
  4. Constructor 类相关办法

    • setAccessible :爆炸
    • newInstance(Object…obj) :调用结构器

1.12.1 经过反射拜访结构器

String path = "com.lyq.reflection_.User";
Class<?> cls = Class.forName(path);
// 拜访公有结构器,而且调用
Object publicConstructor = cls.getConstructor().newInstance(); // User{age=10, name='lyq'}
Constructor<?> declaredConstructor = cls.getDeclaredConstructor(int.class, String.class);
// 经过 setAccessible 能够拜访私有结构器,而且调用私有结构器
declaredConstructor.setAccessible(true);
Object lyq = declaredConstructor.newInstance(1, "lyq222");
System.out.println(lyq);  // User{age=1, name='lyq222'}

1.12.2 经过反射操作特点

Class<?> cls = Class.forName("com.lyq.reflection_.Student");
Constructor<?> constructor = cls.getConstructor();
Object o = constructor.newInstance();
Field age = cls.getField("age");
age.set(o, 20202020);
System.out.println(age.get(o));  // 反射设置特点值 20202020
Field name = cls.getDeclaredField("name");
// 对 name 进行暴力破解
name.setAccessible(true);
name.set(o, "zzz");
System.out.println(o); // Student{age=20202020, name='zzz'}

1.12.3 经过反射操作办法

Class<?> cls = Class.forName("com.lyq.reflection_.Boss");
Object o = cls.getConstructor().newInstance();
// 调用公有办法
Method method = cls.getMethod("hi", String.class);
method.invoke(o, "dasda"); // hi dasda
// 调用私有办法
Method say = cls.getDeclaredMethod("say", int.class, String.class, char.class);
// 暴力破解
say.setAccessible(true);
Object invoke = say.invoke(o, 100, "ccccc", 'k');
System.out.println(invoke); // 100  ccccc  k

事例:运用反射创立文件

Class<File> fileClass = File.class;
Constructor<File> declaredConstructor = fileClass.getDeclaredConstructor(String.class);
File file = declaredConstructor.newInstance("d:\aa.txt");
file.createNewFile();
System.out.println(file);