反射学习笔记整理

反射学习笔记整理 , 知识点整理自【韩顺平讲Java】Java反射专题

反射的使用场景 :

通过外部文件配置 , 在不修改源码的情况下 , 来控制程序 , 也符合设计模式OCP原则(即开闭原则 , 扩容内容)

反射的使用过程

  1. 使用Properties类 , 读取配置文件
Properties properties = new properties();
//通过文件输入流读取配置文件内的内容 , 将配置文件中的内容以key/value的形式存储到properties中
properties.load(new FileInputStream("/xx/xxx/xxx"))
//通过key得到配置文件中存储的类的全路径名
String classfullpath = properties.get("classfullapth").toString();
//得到配置文件中存储的方法
String methodName = properties.get("methodName").toString()
  1. 使用反射创建对象
    1. 加载类 , 返回Calss类型的对象class
    2. 通过class得到加载的类的实例对象
    3. 通过class得到加载的类中的方法名叫method的方法对象 , 即 : 在java中万物皆可对象 , 所以在反射中可以把方法视为对象
    4. 即通过方法对象来实现调用方法
//1. 加载类 , 返回Calss类型的对象class
//通过反射根据类的全路径名创建类的Calss对象
Class class = Class.forName(classfullpath);

//2. 通过class得到加载的类的实例对象
Object o=class.newInstance();

//3. 通过class得到加载的类的方法对象
Method method1 = class.getMethod(methodName);

//4. 通过method1调用方法 , 通过方法对象调用方法
//传统调用方法 : 对象.方法();
//反射调用方法 : 方法.invoke(对象);
method1.invok(o);

什么是反射机制

  1. 反射机制允许程序在执行期借助于ReflectionAPI取得任何类的内部信息(比如成员变量 构造器 成员方法等) , 并能操作对象的属性及方法 , 反射在设计模式和框架中都会用到
  2. 加载完类之后 , 在堆中产生了一个Class类型的对象(一个类只能有一个class对象) , 这个对象包含了类的完整结构信息 , 通过这个对象可以得到类的结构 , 这个Class对象就像一面镜子 , 透过这个镜子看到类的结构 , 所以称之为 : 反射

反射的原理

Java程序执行一共有3个过程 :

  1. 代码阶段/编译阶段 : 根据.java文件通过javac编译生成.class字节码文件 , 字节码文件中包含了.java文件中的所有信息(成员变量 构造器 成员方法 注解 泛型等)
  2. Class类阶段(加载阶段) : 通过类加载器(ClassLoader)将Class对象加载到堆中(这个过程体现反射) , 在堆中保存了类的所有信息(成员变量 构造器 成员方法 注解 泛型等) , 其中所有成员变量映射成Field[] fields(将所有成员变量当成对象来处理) , 构造器映射成Constructor[] constructor , 方法映射成Method[] method
  3. Runtime运行阶段 , 生成对象并存在堆中 , 并且生成的对象知道自己属于哪个Class的对象

Java程序在计算机中的过程图解

得到Class对象可以干什么?

  1. 在运行时判断任意一个对象所属的类
  2. 在运行时构造任意一个类的对象
  3. 在运行时得到任意一个类所具有的成员变量和方法
  4. 在运行时调用任意一个对象的成员变量和方法
  5. 生成动态代理

反射的相关类

反射相关的主要类 :

  1. java.lang.Class : 代表一个类 , Class对象表示某个类加载后在堆中的对象
  2. java.lang.reflect.Method : 代表类的方法 , Method对象代表某个类的方法
  3. java.lang.reflect.Filed : 代表类的成员变量 , Fieled对象代表某个类的成员变量
  4. java.lang.reflect.Constructor : 代表类的所有构造方法 , Constructor代表某个类的构造器

举例 :

//java.lang.reflect.Filed : 代表类的成员变量 , Fieled对象代表某个类的成员变量
//getFiled可以根据成员变量名得到成员变量值 , 前提是成员变量不能是私有的
Filed xxxFiled = cls.getFiled("xxx");//得到cls类中xxx方法的对象

//java.lang.reflect.Constructor : 代表类的所有构造方法 , Constructor代表某个类的构造器
Constructor constructor1 = cls.getConstructor();//getConstructor()方法可以指定构造器 , 根据的是()中传入的参数的class
Constructor constructor2 = cls.getConstructor(Integer.class);//此处传入的是Integer类的Class对象

这些类在java.lang.reflect包中

反射调用优化

  • 反射的优点和缺点 :

    • 优点 : 可以动态的创建和使用对象(也是框架底层核心) , 使用灵活 , 没有反射机制框架技术就失去底层支撑
    • 缺点 : 使用反射基本是解释执行 , 对执行速度有影响
  • 反射调用优化 -> 关闭访问检查 :

    1. Method 和 Filed Constructor都继承了AccessibleObject类 , 对象都有setAccessible()方法
    2. setAccessible()作用是启动和禁用访问安全检查开关
    3. 参数为true表示反射的对象在使用时取消访问检查 , 提高反射的效率 , 参数为false则表示反射的对象执行访问检查

Class类分析

Class类的继承图 :
Class类的继承图
上图中可以看出 : Class类就是一个普通的类 , 只是它的使用方法和功能有些特殊

Class类基本介绍 :

  1. Class也是类 , 因此也继承了Object类
  2. Class类对象不是new出来的 , 而是系统创建的(通过ClassLoader类)
  3. 对于某个类的Class对象 , 在内存中只有一份 , 因为类只加载一次(通过动态加载同一个类的多个Class对象都是同一个对象 , 因为类只加载一次)
类只加载一次演示

代码 :


    public static void main(String[] args) throws Exception{
        Class cls1 = Class.forName("com.mytest.test");
        Class cls2 = Class.forName("com.mytest.test");
        //通过动态加载同一个类的多个Class对象都是同一个对象 , 因为类只加载一次
        System.out.println("cls1 hash : " + cls1.hashCode());
        System.out.println("cls2 hash : " + cls2.hashCode());

        System.out.println("=======================");

        Object o1 = cls1.newInstance();
        Object o2 = cls1.newInstance();
        Object o3 = cls2.newInstance();
        Object o4 = cls2.newInstance();
        //通过反射创建的同一个类的多个对象 *不是* 同一个对象
        System.out.println("o1 hash :" + o1.hashCode());
        System.out.println("o2 hash :" + o2.hashCode());
        System.out.println("o3 hash :" + o3.hashCode());
        System.out.println("o4 hash :" + o4.hashCode());
    }

  1. 每个类的实例都会记得自己是由哪个Class实例所生成()
  2. 通过使用一系列API可以得到创建Class对象的类完整结构
  3. Class对象存放在堆内
  4. 类的字节码二进制数据存放于方法区 , 有的地方称为类的元数据(包括 方法代码,变量名,方法名,访问权限等)

Class类的常用方法

常用方法 :

方法名 功能说明
Class.forName("String name") 返回指定类名name的Class对象
Object.newInstance() 调用缺省构造函数(无参构造函数) , 返回该Class对象的一个实例
class.getClass() 获取class运行类型
class.getPackage().getName() 获取class的包名
class.getName 获取class的全路径
class.getField("FieldName") 通过属性名获取属性值
class.getField("FieldName").set("className","FieldValue") 设置对象的公开属性的属性值
class.getDeclaredFields(); 返回当前class类的所有属性(字段)
class.getSuperClass() 返回当前Class对象的父类的Class对象
class.getInterfaces(); 获取当前Class对象的接口
class.getClassLoader(); 返回该类的类加载器
class.getConstructor(); 返回一个包含某些Constructor对象的数组
class.getMethod(); 返回一个Method对象 ; 此对象的形参类型为paramType
常用方法演示

代码 :

public static void method2() throws Exception {


        //获取test的class对象
        Class cls1 = Class.forName("com.mytest.test");

        //通过class对象创建一个text对象
        //也可以写成 test o = (test)cls1.newInstance(); 但是没必要
        Object o = cls1.newInstance();

        //获取cls1的运行类型
        Class cls1Class = cls1.getClass();
        System.out.println("cls1运行类型 : " + cls1Class);
        System.out.println("----------");
        //获取cls1包名
        String cls1PackageName = cls1.getPackage().getName();
        System.out.println("cls1包名 : " + cls1PackageName);
        System.out.println("----------");
        //获得cls1的全路径
        String name = cls1.getName();
        System.out.println("cls1全路径 : " + name);
        System.out.println("----------");
        //通过属性名获取属性值 , 无法获取私有属性值
        Field cls1Field = cls1.getField("id");
        System.out.println("获取id属性值 : " + cls1Field.get(o));
        System.out.println("----------");
        //设置对象的公开属性的属性值
        cls1.getField("id").set(o, 12);
        System.out.println("修改后id属性值 : " + cls1Field.get(o));
        System.out.println("----------");
        //获取所有属性
        Field[] declaredFields = cls1.getDeclaredFields();
        System.out.println("遍历所有属性值 : ");
        for (Field field : declaredFields) {
            System.out.println(field.get(o));
        }
        System.out.println("----------");
        //获取cls1的父类class对象
        Class superclass = cls1.getSuperclass();
        System.out.println("cls1的父类class对象 : " + superclass);
        System.out.println("----------");
        //获取clas1的所有接口
        Class[] interfaces = cls1.getInterfaces();
        System.out.println("cls1的接口 : ");
        for (Class c : interfaces) {
            System.out.println(c.getName());
        }

        //获取cls1的类加载器
        ClassLoader classLoader = cls1.getClassLoader();

        System.out.println("----------");
        //通过参数获得cls1的有参构造器 , 并创建对象
        //获取无参构造器 : cls1.getConstructor();
        Constructor constructor = cls1.getConstructor(String.class);
        Object a = constructor.newInstance("a");
        System.out.println("----------");
        //获取cls1的方法的对象
        Method method = cls1.getMethod("testMethod");
        //选择调用方法的对象
        method.invoke(o);


    }

执行结果 :

获取Class对象的6中方式

  1. 前提 : 已知一个类的全类名 , 且该类在类路径下 , 可以通过Class类的静态方法Class.forName();获取
    注意 : 可能抛出ClassNotFoundException
    应用场景 : 多用于配置文件 , 读取类全路径 , 加载类

  2. 前提 : 若已知具体的类 , 通过类的class获取 , 该方法最为安全可靠 , 程序性能最高
    应用场景 : 多用于参数传递 , 比如通过反射得到对应构造器对象

  3. 前提 : 已知某个类的实例 , 调用该实例的getClass();方法获取class对象
    引用场景 : 通过创建好的对象 , 得到Class对象

  4. 其他方式 : 通过类加载器创建对象

  5. 基本数据类型可以通过基本数据类型.class得到Class类对象

  6. 基本数据类型对应的包装类 , 可以通过.TYPE得到Class类对象

Class对象6种创建方式演示

代码 :

        System.out.println("4中常用创建方式");

        //1. Class.forName()
        String classAllPath = "com.mytest.Test";
        Class class1 = Class.forName("com.mytest.Test");
        System.out.println("Class.forName创建 : " + class1);

        //2. 类名.class() 应用场景 : 参数传递
        Class class2 = Test.class;
        System.out.println(".class创建 : " + class2);

        //3. 对象.getClass() , 应用场景 : 有对象实例
        Test test = new Test();
        Class class3 = test.getClass();
        System.out.println(".getClass创建 : " + class3);

        //4. 类加载器得到Class对象
        //4.1 先得到类加载器
        ClassLoader testClassLoader = test.getClass().getClassLoader();
        //4.2 通过类加载器得到Class对象
        Class class4 = testClassLoader.loadClass(classAllPath);
        System.out.println("类加载器创建 : " + class4);
        System.out.println();
        //注意通过以上4中方法得到的Class对象是同一个Class对象
        System.out.println("4个class对象的hash值 :");
        System.out.println("class1 : " + class1.hashCode());
        System.out.println("class2 : " + class2.hashCode());
        System.out.println("class3 : " + class3.hashCode());
        System.out.println("class4 : " + class4.hashCode());


        System.out.println();
        System.out.println("基本数据类型与包装类获取Class类的方式 :");
        //5. 基本数据类型按如下方式得到Class对象
        Class<Integer> integerClass = int.class;
        Class<Character> characterClass = char.class;
        System.out.println("int.class : " + integerClass);
        System.out.println("char.class : " + characterClass);

        //6. 基本数据类型的包装了通过.TYPE得到Class类对象
        Class<Integer> type1 = Integer.TYPE;
        Class<Character> type2 = Character.TYPE;
        System.out.println("Integer.TYPE : " + type1);
        System.out.println("Character.TYPE : " + type2);

        //注意 : java会进行自动装箱和自动拆箱 , 基本数据类型与其对应的包装使用同一个class
        System.out.println("基本数据类型与其对应的包装使用同一个class :");
        System.out.println("integerClass.hashCode() : " + integerClass.hashCode());
        System.out.println("type1.hashCode() : " + type1.hashCode());
    

执行结果 :

哪些类型有Class对象

如下类型有Class对象 :

  1. 外部类 , 成员内部类 , 静态内部类 , 局部内部类 , 匿名内部类
  2. interface 接口
  3. 数组
  4. enum 枚举
  5. annotation 注解
  6. 基本数据类型
  7. void
具有Class对象的类型

代码 :

        Class<String> class1 = String.class;//外部类
        Class<Serializable> class2 = Serializable.class;//接口
        Class<Integer[]> class3 = Integer[].class;//数组
        Class<float[][]> class4 = float[][].class;//二维数组
        Class<Deprecated> class5 = Deprecated.class;//注解
        Class<Thread.State> class6 = Thread.State.class;//枚举
        Class<Long> class7 = long.class;//基本数据类型
        Class<Void> class8 = void.class;//void数据类型
        Class<Class> class9 = Class.class;//class也是外部类

        System.out.println("class1 : " + class1);
        System.out.println("class2 : " + class2);
        System.out.println("class3 : " + class3);
        System.out.println("class4 : " + class4);
        System.out.println("class5 : " + class5);
        System.out.println("class6 : " + class6);
        System.out.println("class7 : " + class7);
        System.out.println("class8 : " + class8);
        System.out.println("class9 : " + class9);

执行结果 :

动态和静态加载

  • 基本说明
    反射机制是java实现动态语言的关键 , 也就是通过反射实现类的动态加载
    • 静态加载 : 编译时加载相关的类 , 如果没有则报错 , 依赖性太强
    • 动态加载 : 运行时加载需要的类 , 如果运行时不用该类 , 即使不存在该类也不报错 , 降低了依赖性
静态加载与动态加载

静态加载代码 :

import java.util.*;
import java.lang.reflect.*;

public class ClassLoadTest{

	public static void main(String[] args){
	
	Scanner sc = new Scanner(System.in);
	System.out.println("请输入key");
	String key = sc.next();
	switch(key){
		case "1" : 
			Dog dog=new Dog();
			dog.cry();
			break;
		case "2" :
			Class testClass = Class.forName("Test");
			Object o = testClass.newInstance();
			Method m = testClass.getMethod("hi");
			m.invoke(o);
			break;
		default:
		System.out.println("Do nothing...");
		
		
		}
	}
}

静态加载编译结果 :

动态加载代码 :

import java.util.*;
import java.lang.reflect.*;

public class ClassLoadTest{

	public static void main(String[] args){
	
	Scanner sc = new Scanner(System.in);
	System.out.println("请输入key");
	String key = sc.next();
	switch(key){
		case "1" : 
			//Dog dog=new Dog();
			//dog.cry();
			break;
		case "2" :
			Class testClass = Class.forName("Test");
			Object o = testClass.newInstance();
			Method m = testClass.getMethod("hi");
			m.invoke(o);
			break;
		default:
		System.out.println("Do nothing...");
		
		
		}
	}
}

动态加载编译结果 :

总结 :

new Dog(); 是静态加载 , 因此必须编写Dog类
Test类是动态加载 , 所以没编写Test类也不会报错 , 只有当加载该类时才会报错
  • 类加载时机
    • 当创建对象时(new) //静态加载
    • 当子类被加载时 , 父类也加载 //静态加载
    • 调用类中的静态成员时 //静态加载
    • 通过反射 //动态加载

类加载流程

类加载流程 :

  1. java源代码(com.xxx.java)通过javac编译生成字节码文件(com.xxx.class)
  2. 字节码文件(com.xxx.class)通过java运行进行对字节码文件进行装载/加载
  3. 类加载3个阶段 :
    1. 加载 Loading

    2. 连接 Linking

      1. 验证 verification : 对文件进行安全校验 , 查看文件格式是否正确或元数据是否正确等
      2. 准备 Preparation : 对静态变量分配内存并进行默认初始化
      3. 解析 Resolution : 虚拟机会将常量池中的符号引用替换为直接引用
    3. 初始化 initialization

  4. 类加载后内存布局情况 :
    1. 方法区 : 将字节码文件以二进制的形式保存下来
    2. 堆区 : 生成类的Class对象 , 类的Class对象会对方法区中的字节码文件进行引用

类加载各阶段完成任务 :

  1. 加载 Loading : 将类的Class文件读入内存 , 并为之创建一个java.lang.Class对象 , 此过程由类加载器完成(此阶段由JVM控制)
  2. 连接 Linking : 将类的二进制数据合并到JRE中(此阶段由JVM控制)
  3. 初始化 initialization : JVM负责对类进行初始化 , 这里主要是指静态成员 (此阶段可由程序员控制)

类加载的5个阶段 :

  1. 加载阶段 :
    JVM在该阶段的主要目的是将字节码从不同的数据源(可能是class文件 也可能是jar包 甚至网络)转化为二进制字节流加载到内存中 , 并生成一个代表该类的java.lang.Class对象
  2. 连接阶段-验证 :
    1. 验证的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求 , 并且不会危害虚拟机自身的安全
    2. 包括 : 文件格式验证(是否以魔数oxcafebabe开头) 元数据验证 字节码验证 和符号引用验证
    3. 可以考虑使用-Xverify:none参数来关闭大部分的类验证措施 , 缩短虚拟机类加载的时间
  3. 连接阶段-准备 :
    1. JVM在该阶段对静态变量分配内存并初始化 , (对应数据类型的牧人初始值 , 如0 0L null false等) , 这些变量所使用的内存都在方法区进行分配
连接阶段-准备

代码 :

class A{
  //在连接阶段-准备 下面三个属性的值
  //1. n1是实例变量 , 不是静态变量 , 因此在准备阶段不会赋值
  //2. n2是静态变量 , 分配内存 , 并默认初始化为0 , n2=0 , 不是20
  //3. n3是static final修饰的常量 , 因为一旦赋值就不变 , 在此阶段直接赋值30 , n3=30
  public int n1 = 10;
  public static int n2 =20;
  public static final int n3 = 30;
}
  1. 连接阶段-解析
    此阶段虚拟机将常量池内的符号引用(用符号记录类之间的引用)替换为直接引用(直接使用地址来引用类)

  2. 初始化

    1. 到初始化阶段 , 才真正开始执行类中定义的Java程序代码 , 此阶段是执行<clinit>()方法的过程
    2. <clinit>()方法是由编译器按语句在源文件中出现的顺序 , 依次自动收集类中的所有静态方法的赋值动作和静态代码块中的语句 , 并合并执行
    3. 虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确的加锁 同步 , 如果多线程同时去初始化一个类 , 那么只会有一个线程去执行这个类的 <clinit>()方法 , 其他线程都需要阻塞等待 , 直到活动线程执行 <clinit>()方法完毕
类加载-初始化

代码 :

//类加载执行流程 :
//1. 加载B类 , 并生成B的class对象
//2. 链接 为num赋初值0
//3. 初始化 依次自动收集类中的所有静态变量的赋值动作和代码块中的语句 , 并合并
/*   此时会生成一个方法 :
     clinit(){
     System.out.println("B的静态代码块被执行");
     num=300;
     num=100;
     }
     合并后
     clinit(){
     System.out.println("B的静态代码块被执行");
     //num=300;
     num=100;
     }     
/

public static void main(String[] args){
   System.out.println(B.num);//注意 : 直接使用静态属性或静态代码块也会加载类

}

class B{
  static{
  System.out.println("B的静态代码块被执行");
  num=300;

  }
  static int num = 100;
  public B(){
  System.out.println("B构造器被执行");
  }
}

通过反射获取类的结构信息

java.lang.class类 :

  1. getName() : 获取全类名
  2. getSimpleName() : 获取简单类名
  3. getFields : 获取所有public修饰的属性 , 包含本类及父类
  4. getDeclaredFields : 获取本类中所有属性
  5. getMethods : 获取所有public修饰的方法 , 包含本类以及父类的
  6. getDeclaredMethods : 获取本类中所有的方法
  7. getConstructors : 获取本类中所有public修饰的构造器
  8. getDeclaredConstructor : 获取本类中所有的构造器
  9. getPackage : 以package形式返回包信息
  10. getSuperClass : 以Class形式返回父类信息
  11. getInterfaces : 以Class[]形式返回接口信息
  12. getAnnotations : 以Annotation[]形式返回注解信息
java.lang.class类 :

代码 :

 public void api_1() throws Exception {

        //得到class对象
        Class<?> innerClassCls = Class.forName("com.test.InnerClass");
        //1. getName() : 获取全类名
        System.out.println("获取全类名 : " + innerClassCls.getName());//com.test.InnerClass

        //2. getSimpleName() : 获取简单类名
        System.out.println("获取简单类名 : " + innerClassCls.getSimpleName());//InnerClass

        //3. getFields : 获取所有public修饰的属性 , 包含本类及父类
        Field[] fields = innerClassCls.getFields();
        for (Field field : fields) {
            System.out.println("本类及父类所有public属性 : " + field.getName());
        }

        //4. getDeclaredFields : 获取本类中所有属性
        Field[] declaredFields = innerClassCls.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println("本类所有属性 : " + declaredField.getName());
        }

        //5. getMethods : 获取所有public修饰的方法 , 包含本类以及父类的
        Method[] methods = innerClassCls.getMethods();
        for (Method method : methods) {
            System.out.println("本类以及父类的public方法 : " + method.getName());
        }

        //6. getDeclaredMethods : 获取本类中所有的方法
        Method[] declaredMethods = innerClassCls.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println("本类中所有的方法 : " + declaredMethod);
        }

        //7. getConstructors : 获取本类中所有public修饰的构造器
        Constructor<?>[] constructors = innerClassCls.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println("本类中所有public修饰的构造器 : " + constructor.getName());
        }
        //8. getDeclaredConstructors : 获取本类中所有的构造器
        Constructor<?>[] declaredConstructors = innerClassCls.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println("本类所有构造器 : " + declaredConstructor.getName());
        }
        //9. getPackage : 以package形式返回包信息
        System.out.println("class的包信息 : " + innerClassCls.getPackage());

        //10. getSuperClass : 以Class形式返回父类信息
        Class<?> superclass = innerClassCls.getSuperclass();
        System.out.println("Class的父类信息 : " + superclass);
        //11. getInterfaces : 以Class[]形式返回接口信息
        Class<?>[] interfaces = innerClassCls.getInterfaces();
        for (Class<?> anInterface : interfaces) {
            System.out.println("Class继承的接口 : " + anInterface.getName());
        }
        //12. getAnnotations : 以Annotation[]形式返回注解信息
        Annotation[] annotations = innerClassCls.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println("Class的注释信息"+annotation);
        }


    }


interface I1 {
}

interface I2 {
}


class A {
    public String A_attr1;

    public void A_m1() {
    }

    public A() {
    }
}

@Deprecated
@Addressing
class InnerClass extends A implements I1, I2 {

    //属性
    public String attr1;
    protected int attr2;
    String attr3;
    private double attr4;

    //构造器
    public InnerClass() {
    }

    public InnerClass(String string) {
    }

    private InnerClass(String string, int i) {
    }

    //方法
    public void m1() {
    }

    protected void m2() {
    }

    void m3() {
    }

    private void m4() {
    }
}

代码效果 :

java.lang.reflect.Field类 :

  1. getModifiers : 以int形式返回修饰符
    说明 : 默认修饰符是0 , public是1 , private是2 , protected是4 , static是8 , final是16组合修饰将数值相加 例如 : public(1)+static(8)=9
  2. getType : 以Class形式返回类型
  3. getName : 返回属性名
java.lang.reflect.Field类 :

代码 :

    public void api_2() throws Exception {
        //得到class对象
        Class<?> innerClassCls = Class.forName("com.test.InnerClass");
        //getDeclaredFields : 获取本类中所有属性
        //1. getModifiers : 以int形式返回修饰符
        //说明 : 默认修饰符是0 , public是1 , private是2 , protected是4 , static是8 , final是16组合修饰将数值相加 例如 : public(1)+static(8)=9
        //2. getType : 以Class形式返回类型
        //3. getName : 返回属性名
        Field[] declaredFields = innerClassCls.getDeclaredFields();
        System.out.println("本类的所有属性 :");
        System.out.println("---------------");
        for (Field declaredField : declaredFields) {
            System.out.println("属性名 : " + declaredField.getName());
            System.out.println("该属性的修饰符值 : " + declaredField.getModifiers());
            System.out.println("该属性的类型 : " + declaredField.getType());
            System.out.println("-------------------------");
        }

    }

结果 :

java.lang.reflect.Method类 :

  1. getModifiers以int形式返回修饰符
    说明 : 默认修饰符是0 , public是1 , private是2 , protected是4 , static是8 , final是16
  2. getReturnType : 以Class形式获取返回类型
  3. getName : 返回方法名
  4. getParameterType : 以Class[]返回参数类型数组
java.lang.reflect.Method类

代码 :

    public void api_3() throws Exception {
        //得到class对象
        Class<?> innerClassCls = Class.forName("com.test.InnerClass");
        //  getDeclaredMethods : 获取本类中所有的方法
        Method[] declaredMethods = innerClassCls.getDeclaredMethods();
        System.out.println("本类的所有方法 :");
        System.out.println("---------------");
        for (Method declaredMethod : declaredMethods) {
            System.out.println("方法名 : " + declaredMethod);
            System.out.println("该方法的访问修饰符值 : " + declaredMethod.getModifiers());
            System.out.println("该方法的返回类型: " + declaredMethod.getReturnType());
            Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
            for (Class<?> parameterType : parameterTypes) {
                System.out.println("该方法的形参类型 :" + parameterType);
            }
            System.out.println("-------------------------------");
        }
    }

演示 :

java.lang.reflect.Constructor类 :

  1. getModifiers : 以int形式返回修饰符
  2. getName : 返回构造器名(全类名)
  3. getParameterTypes : 以Class[]返回参数类型数组
java.lang.reflect.Constructor类

代码 :

    public void api_4() throws Exception {
        //得到class对象
        Class<?> innerClassCls = Class.forName("com.test.InnerClass");
        // getDeclaredConstructors : 获取本类中所有的构造器
        Constructor<?>[] declaredConstructors = innerClassCls.getDeclaredConstructors();
        System.out.println("本类所有构造器 :");
        System.out.println("----------------");
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println("构造器名 : " + declaredConstructor.getName());
            System.out.println("该构造器的访问修饰符值 : " + declaredConstructor.getModifiers());
            Class<?>[] parameterTypes = declaredConstructor.getParameterTypes();
            for (Class<?> parameterType : parameterTypes) {
                System.out.println("该构造器的形参类型 : " + parameterType);
            }
            System.out.println("----------------");
        }
    }

代码 :

通过反射创建对象

通过反射创建对象的方式 :

  1. 调用类中的public修饰的无参构造器
  2. 调用类中的指定构造器
  3. Class类相关 :
    newInstance : 调用类中的无参构造器 . 获取对应类的对象
    getConstructor(Class...class) : 根据参数列表 , 获取对应的构造器对象
    getDecalaredConstructor(Class...class) : 根据参数列表 , 获取对应的构造器对象
  4. Constructor类相关方法
    setAccessible : 爆破(临时改变访问权限 , 老师文件里写的是爆破 , 我觉得不好理解 , 所以接下来都会写成临时改变访问权限)
    newInstance(Object...obj) : 调用构造器
通过反射创建对象案例演示 :

代码 :

public class Test {
    public static void main(String[] args) throws Exception {

        //先获取Inner类的Class对象
        Class<?> innerClass = Class.forName("com.test.Inner");

        //1. 通过public的无参构造器创建实例
        Object inner1 = innerClass.newInstance();
        System.out.println(inner1);
        System.out.println("----------------------");

        //2. 通过public的有参构造器创建实例
        /*constructor是Inner的参数为String的构造器的对象
          也可以理解为 constructor对象就是 :
          有参 public
             public Inner(String string) {
                  this.attr1 = string;
             }
         */
        //2.1 先得到构造器
        Constructor<?> constructor1 = innerClass.getConstructor(String.class);
        //2.2 创建实例 , 并传入实参
        //newInstance的参数就是构造方法的参数
        Object inner2 = constructor1.newInstance("string1");
        System.out.println(inner2);
        System.out.println("----------------------");

        //3. 通过非public的有参构造器创建实例
        //getConstructor只能得到public修饰的构造器 , 因此会报错 :
        //java.lang.NoSuchMethodException: com.test.Inner.<init>(java.lang.String, int)
        //Constructor<?> constructor2 = innerClass.getConstructor(String.class, int.class);

        //3.1 通过getDeclaredConstructor方法得到所有的构造器包括private修饰的
        Constructor<?> constructor2 = innerClass.getDeclaredConstructor(String.class, int.class);
        //3.2 创建实例
        //直接创建实例会报错 : java.lang.IllegalAccessException 非法的访问异常
        //Object inner3 = constructor2.newInstance("string2", 1);
        //对于限制访问权限的方法/属性/构造器 :
        //使用constructor2.setAccessible(true);临时改变访问权限
        constructor2.setAccessible(true);
        Object inner3 = constructor2.newInstance("string2", 1);
        System.out.println(inner3);
        System.out.println("----------------------");


    }


}

class Inner {

    //属性
    public String attr1 = "attr1";
    private int attr2 = 2;

    //无参 public
    public Inner() {
    }

    //有参 public
    public Inner(String string) {
        this.attr1 = string;
    }

    //有参 private
    private Inner(String string, int i) {
        this.attr1 = string;
        this.attr2 = i;
    }

    //方法
    public String m1(int parameter1, String parameter2, double parameter3) {
        return null;
    }

    protected void m2() {
    }

    void m3() {
    }

    private void m4() {
    }

    @Override
    public String toString() {
        return "Inner{" +
                "attr1='" + attr1 + '\'' +
                ", attr2=" + attr2 +
                '}';
    }
}

执行结果 :

通过反射访问类中的变量

  1. 根据属性名获取Filed对象
    Filed f = class对象.getDeclaredFiled(属性名);
  2. 临时改变访问权限
    f.setAccessable(true);f是上面的Filed
  3. 访问
    f.set(o,值); //o表示对象
    syso(f.get(o)); //o表示对象
    

注意 : 如果是静态属性 , 则set和get中的参数o , 可以写成null

通过反射访问类中的变量案例演示 :

代码 :

public class Test {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {

        //得到Inner2的Class对象 , 并创建对象
        Class<?> inner2Class = Class.forName("com.test.Inner2");
        Object o = inner2Class.newInstance();//o的运行类型是student
        System.out.println("o的运行类型 : " + o.getClass());//class com.test.Inner2
        System.out.println("------------------");
        System.out.println("o的属性值 : " + o);
        System.out.println("------------------");

        //1. 用反射得到attr1的属性对象
        //getField() 得到共有属性
        //getDeclaredFiled() 得到所有属性 , 包括private修饰的
        Field attr1 = inner2Class.getField("attr1");
        attr1.set(o, 111);
        System.out.println("通过getField()得到对象并修改后 , o的属性值 : " + o);
        System.out.println("通过反射返回o的attr1的值 : " + attr1.get(o));
        System.out.println("------------------");

        //2. 使用反射操作attr2属性
        //inner2Class.getField("attr2"); 不会成功 , 因为attr2被private修饰 , 无法得到
        //通过getDeclaredField(); 可得到被private修饰的attr2属性
        Field attr2 = inner2Class.getDeclaredField("attr2");
        //通过attr2.setAccessible(true);临时改变访问权限
        attr2.setAccessible(true);
        //attr2.set(o,"老韩讲的真好!");
        //因为attr2是static属性 , static修饰的属性只有一个且所有inner2的对象共享
        //所以可以o可以写为null
        attr2.set(null, "老韩讲的真好!");
        System.out.println("通过getDeclaredField()得到对象并修改后 , o的属性值 : " + o);
        //获取属性值
        System.out.println("通过反射修改o的static属性修饰的attr2的值 :" + attr2.get(o));
        //System.out.println(attr2.get(null)); 要用null属性必须是static修饰的
    }
}

class Inner2 {

    //属性
    public int attr1;
    private static String attr2;

    //无参 public
    public Inner2() {
    }

    //有参 public
    public Inner2(int i) {
        this.attr1 = i;
    }

    //有参 private
    private Inner2(int i1, String i2) {
        this.attr1 = i1;
        this.attr2 = i2;
    }

    //方法
    public String m1(int parameter1, String parameter2, double parameter3) {
        return null;
    }

    protected void m2() {
    }

    void m3() {
    }

    private void m4() {
    }

    @Override
    public String toString() {
        return "Inner{" +
                "attr1='" + attr1 + '\'' +
                ", attr2=" + attr2 +
                '}';
    }
}

执行结果 :

通过反射访问类中的方法

  1. 根据方法名和参数列表获取Method方法对象
    Method m = Class.getDeclaredMethod(方法名 , xx.class);得到本类的所有方法
  2. 获取对象
    Object o = class.newInstance();
  3. 临时改变访问权限
    m.setAccessible(true);
  4. 访问
    Object returnValue = m.invoke(o,参数列表);o就是对象

注意 : 如果是静态方法 , 则invoke的参数o可以写成null

通过反射访问类中的方法案例演示 :

代码 :

public class Test_05_11_03 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        //得到Inner3的Class类 , 并创建对象
        Class<?> inner3Class = Class.forName("com.test.Inner3");
        Object o = inner3Class.newInstance();

        //1. 调用public的m2方法 , m2是public修饰 , 所以下面两种方法都可行
        //Method m2 = inner3Class.getMethod("m2" , String.class);
        //1.1 得到m2方法的对象
        Method m2 = inner3Class.getDeclaredMethod("m2", String.class);
        //1.2 调用
        System.out.println("调用inner3Class的m2方法 :");
        m2.invoke(o, "abc");

        //2. 调用private static修饰的m1方法
        //2.1 得到m2的方法对象
        //inner3Class.getMethod("m1", int.class, String.class, char.class); 无法执行 , 因为m1是私有的
        Method m1 = inner3Class.getDeclaredMethod("m1", int.class, String.class, char.class);
        //2.2获取临时访问权限
        m1.setAccessible(true);
        //以下两种方式均可调用
        System.out.println("m1第一个参数为null并调用 : " + m1.invoke(null, 1, "abc", 'a'));
        System.out.println("m1第一个参数为inner3Class并调用 : " + m1.invoke(inner3Class, 2, "abcd", 'b'));

        //注意 : 如果方法有返回值 , 编译时一律使用Object接收 , 运行类型和方法定义的放回类型一致
        Object invoke = m1.invoke(null, 123, "aaa", 'b');
        System.out.println("invoke的运行时类型 : " + invoke.getClass());
    }
}

class Inner3 {

    //属性
    public int attr1;
    private static String attr2;

    //无参 public
    public Inner3() {
    }

    //有参 public
    public Inner3(int i) {
        this.attr1 = i;
    }

    //有参 private
    private Inner3(int i1, String i2) {
        this.attr1 = i1;
        this.attr2 = i2;
    }

    //方法
    public static String m1(int i, String s, char c) {
        return i + "---" + s + "---" + c;
    }

    public void m2(String s) {
        System.out.println("inner3.m2() : " + s);
    }


    @Override
    public String toString() {
        return "Inner{" +
                "attr1='" + attr1 + '\'' +
                ", attr2=" + attr2 +
                '}';
    }
}

执行结果 :

posted @ 2021-05-08 20:44  小_Leo  阅读(137)  评论(0编辑  收藏  举报