Java 反射


部分引用:Java基础之反射

反射

反射是框架设计的灵魂

反射的概述

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

反射的特点:动态获取动态创建动态调用

要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法。所以先要获取到每一个字节码文件对应的Class类型的对象。

所以反射的原理在于class类对象。它把java类中的各种成分映射成一个个的Java对象。

例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。

回顾一下类的加载过程
反射原理

其中这个Class对象很特殊。我们先了解一下这个Class类

查看Class类在java中的api详解(1.7的API)

class api

Class 类的实例表示正在运行的 Java 应用程序中的类和接口。也就是jvm中有N多的实例每个类都有该Class对象。(包括基本数据类型)

Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass 方法自动构造的。也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了。

Class没有公共的构造方法,方法共有64个,以下是个人总结归纳的一些常用方法。

以下代码为简洁起见,约定:c = Class对象的变量名

注意:要读取私有的属性和方法,需要先取消访问控制,设置可见性

c.setAccessible(true);

获得Class对象

  1. 通过Object类中的getClass() 方法
Person p = new Person();
Class c = p.getClass();
  1. 通过 类名.class 获取到字节码文件对象
Class c = Person.class;
  1. 通过Class类中的静态方法(推荐)
Class c = Class.forName(全类名);

获得Class类名

  1. 获取全类名
Class c = p.getClass();
String name = c.getName();
  1. 获取类名
Class c = P.getclass();
String name = c.getSimpleName();

获取指定类的构造方法

  1. 获取public修饰的指定参数的构造方法
Constructor con = c.getConstructor(String.class);
  1. 获取任意修饰的指定参数的构造方法
Constructor con = c.getDeclaredConstructor(String.class);
  1. 获取public修饰的所有构造方法
Constructor[] cons = c.getConstructors();
  1. 获取任意修饰的所有构造方法
Constructor[] cons = c.getDeclaredConstructors();

创建实例

  1. 使用Class对象的newInstance()方法
Class<?> c = String.class;
Object str = c.newInstance();
  1. 通过获取构造器对象,调用newInstance()方法
//获取String所对应的Class对象
Class<?> c = String.class;

//获取String类带一个String参数的构造器
Constructor constructor = c.getConstructor(String.class);

//根据构造器创建实例
Object obj = constructor.newInstance("23333");
System.out.println(obj);

获取变量并赋值

  1. 获取public修饰的指定变量(包括继承变量)
Field f = c.getField("age");
  1. 获取任意修饰的指定变量(不包括继承变量)
Field privateField = c.getDeclaredField("privateAge");
  1. 获取public修饰的所有变量
Field[] f = c.getFields();
  1. 获取任意修饰的所有变量
Field[] f = c.getDeclaredFields();
  1. 为属性赋值(方法一)
Person p = new Person();
Field f = p.getClass().getDeclaredField("privateAge");
//访问私有属性要设置可见性
f.setAccessible(true);
f.set( p , value );
  1. 为属性赋值(方法二)
Method setPrivateAge = p.getClass().getMethod("setPrivateAge", String.class);
setPrivateAge.invoke( p , "2333" );

以上两种设置属性的方法区别在于:

  • 方法一是直接访问属性,设置属性
  • 方法二是调用了公开的set属性方法,设置属性,优点是可以同时执行该set方法里面的代码 (前提是有该方法,下面会继续说获取方法的代码)

获取方法并调用方法

  1. 获取public修饰的指定名字指定参数的方法
//获取methodClass类的add方法
Method method = c.getMethod("add", int.class, int.class);
  1. 获取public修饰的所有方法(包括继承类的公用方法)
Method[] methods = c.getMethods();
  1. 获取任意修饰的所有方法(不包括继承的方法)
Method[] declaredMethods = c.getDeclaredMethods();
  1. 使用指定方法(private多加步设置可见性)
//获取methodClass类的add方法
Method method = c.getMethod("add",int.class,int.class);
//调用method对应的方法 => add(1,4)
Object result = method.invoke(obj,1,4);

反射使用和操作

泛型擦除

使用反射,向有泛型约束的集合中添加任意类型的元素

    public static void main(String[] args) throws Exception {
        ArrayList<Integer> list = new ArrayList<Integer>();
        //添加元素到集合
        list.add(new Integer(30));
        list.add(new Integer("12345"));
        list.add(123);
        //list.add("哈哈");//因为有泛型类型的约束,会报错
        System.out.println(list);
    
        //通过反射技术,实现添加任意类型的元素
        //1, 获取字节码文件对象
        Class c = Class.forName("java.util.ArrayList");
    		
        //2, 找到add()方法
        Method addMethod = c.getMethod("add", Object.class);
    
        //3,执行add()方法
        addMethod.invoke(list, "哈哈");// list.add("哈哈");
        System.out.println(list);
    }

调用Main方法

假设一个对象类里存在一个main方法

//1、获取 Student 对象的字节码
Class clazz = Class.forName("fanshe.main.Student");

//2、获取main方法
 Method methodMain = clazz.getMethod("main", String[].class);//第一个参数:方法名称,第二个参数:方法形参的类型,
//3、调用main方法
// methodMain.invoke(null, new String[]{"a","b","c"});
 //第一个参数,对象类型,因为方法是static静态的,所以为null可以,第二个参数是String数组,这里要注意在jdk1.4时是数组,jdk1.5之后是可变参数
 //这里拆的时候将  new String[]{"a","b","c"} 拆成3个对象。。。所以需要将它强转。
 methodMain.invoke(null, (Object)new String[]{"a","b","c"});//方式一
// methodMain.invoke(null, new Object[]{new String[]{"a","b","c"}});//方式二

反射获取配置文件

有一个配置文件名为 config.properties

className  = com.zohn.Person
methodName = demo
public static void main(String[] args) throws Exception {
	// 通过Properties集合从文件中读取数据
	Properties prop = new Properties();
	// 读取文件中的数据到集合中
	prop.load(new FileInputStream("config.properties"));
	// 获取键所对应的值
	String className = prop.getProperty("className");
	System.out.println(className);

	// 1,获取Person.class 字节码文件对象
	Class c = Class.forName(className);
	// 2,获取构造方法
	// public Person(String name, int age)
	Constructor con = c.getConstructor(String.class, int.class);

	// 3,创建对象
	Object obj = con.newInstance("小明", 20);
	System.out.println(obj);

	// 4,获取指定的方法
	// private void method5(){}
	String methodName = prop.getProperty("methodName");
	Method m5 = c.getDeclaredMethod(methodName, null);
	// 5,开启暴力访问
	m5.setAccessible(true);
	// 6,执行找到的方法
	m5.invoke(obj, null);
}

关于反射的实现原理,请跳转 深入分析Java方法反射的实现原理


posted @ 2020-05-11 21:47  鹿友  阅读(94)  评论(0编辑  收藏  举报