Java 反射

Java 反射

类加载器

当程序要使用某个类时,如果该类还未被加载到内存中,则系统(虚拟机)会使用类加载器进行加载,连接,初始化三步来实现对这个类进行初始化。

  • 加载

    • 就是指将class文件读入内存,并为之创建一个编译后的class文件对象(字节码对象),任何类被使用时系统都会建立一个class文件对象
  • 连接

    • 验证:是否有正确的内部结构(构造器、变量、方法、代码块等),并和其他类协调一致

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

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

  • 初始化

    • 就是类的初始化步骤,使用关键字new进行初始化

注:加载和连接是由虚拟机的类的加载器来完成的,初始化是由程序员来做的

类的加载时机

  • 创建类的实例

  • 调用类的静态变量,或者为静态变量赋值

  • 调用类的静态方法

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

  • 初始化某个类的子类,会先加载父类

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

三种类的加载器

  • Bootstrap ClassLoader 根类加载器

    也被称为引导类加载器,负责Java核心类的加载。比如System,String等。在JDK中JRE的lib目录下rt.jar文件中

  • Extension ClassLoader 扩展类加载器

    负责JRE的扩展目录中jar包的加载。在JDK中JRE的lib目录下ext目录

  • System ClassLoader 系统类加载器

    负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径

反射

概念:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

java.lang.Class 类是描述.class文件对象的类

获取class文件对象的三种方式

  1. 对象获取

    Person person=new Person();
    Class c=person.getClass();
    System.out.println(c);
    
  2. 类名获取

    每个类型,包括基本类型和引用类型,都会赋予这个类型一个静态的属性,属性名为class

    Class c=Person.class;
    System.out.println(c);
    
  3. Class类的静态方法forName()获取

    Class c=Class.forName("Person");
    System.out.println(c);
    

    注:这种方式中forName()的参数需要类的全名(报名.类型)形式,另需要抛出ClassNotFoundException异常

    以上3中方式中获取的对象是唯一性的,也就是说,三种方式获取同一个类的对象都是一样的(class文件对象)

获取构造方法并运行

  • 获取构造函数,以数组形式返回

    • 获取所有public修饰的指定参数类型的构造函数:getConstructors()

      Class c=Person.class;
      Constructor[] constructors = c.getConstructors();
      for (Constructor con:constructors){
          System.out.println(con);
      }
      
    • 获取所有指定参数类型的构造函数,包括私有:getDeclaredConstructors()

      Class c=Person.class;
      Constructor[] constructors = c.getDeclaredConstructors();
      for (Constructor con:constructors){
          System.out.println(con);
      }
      
  • 获取无参的构造函数并运行

    Class c=Person.class;
    Constructor constructor = c.getConstructor();
    Object obj = constructors.newInstance();
    System.out.println(obj);
    
  • 获取有参的构造函数并运行

    Class c=Person.class;
    Constructor constructor = c.getConstructor(String.class,int.class);
    Object obj = constructors.newInstance("tony",21);
    System.out.println(obj);
    
  • 快捷方式获取构造函数并运行

    需要满足以下两个条件

    1. 该类必须有无参的构造函数
    2. 该类无参的构造函数必须是public
    Class c=Person.class;
    Object obj = c.newInstance();
    System.out.println(obj);
    
  • 获取私有的构造方法并运行(暴力反射);constructors.setAccessible(true);

    Class c=Person.class;
    Constructor constructor = c.getConstructor(String.class,int.class);
    constructors.setAccessible(true);
    Object obj = constructors.newInstance("tony",21);
    System.out.println(obj);
    

获取成员变量并改值

Class c=Person.class;
Object obj = c.newInstance();
Field field = c.getField("name");
field.set(obj,"张三");
System.out.println(field.get(obj));

说明:

  • 可获取成员变量的数组
  • 可获取私有private的成员变量

获取成员方法

  • 获取空参的成员方法并运行

    Class c=Person.class;
    Object obj = c.newInstance();
    Method method=c.getMethod("eat");
    Object invoke = method.invoke(obj);
    System.out.println(invoke);
    
  • 获取带参的成员方法并运行

    Class c=Person.class;
    Object obj = c.newInstance();
    Method method=c.getMethod("sleep",String.class,int.class,double.class);
    Object invoke = method.invoke(obj,"休眠",888,99.9);
    System.out.println(invoke);
    

反射泛型擦除

将已存在的ArrayList集合中添加一个字符串数据,如何实现呢?其实程序编译后产生的.class文件中是没有泛型约束的,这种现象我们称为泛型的擦除。那么,我们可以通过反射技术,来完成向有泛型约束的集合中,添加任意类型的元素

 ArrayList<Integer> list = new ArrayList<Integer>();
 list.add(new Integer(30));
 list.add(new Integer("12345"));
 list.add(123);
 System.out.println(list);

//通过反射技术,实现添加任意类型的元素
Class c = list.getClass();
Method addMethod = c.getMethod("add", Object.class);
addMethod.invoke(list, "哈哈");// list.add("哈哈");
System.out.println(list);

反射配置文件

配置文件内容:

该配置文件制定类名和方法名,后可更改该配置文件来执行不同的类和方法

className=cn.itcast_01_Reflect.Person
methodName=method5

实现代码:


Properties prop = new Properties();
prop.load(new FileInputStream("properties.txt"));
String className = prop.getProperty("className");
System.out.println(className);
Class c = Class.forName(className);
Constructor con = c.getConstructor(String.class, int.class, String.class);
Object obj = con.newInstance("小明", 20, "中国");
System.out.println(obj);
String methodName = prop.getProperty("methodName");
Method m = c.getDeclaredMethod(methodName, null);
// 开启暴力访问
m.setAccessible(true);
m.invoke(obj, null);
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值