• 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方法反射的实现原理


  • 相关阅读:
    J2EE第四周
    J2EE 第三周
    jsf简单入门
    Applrt和Ajax
    hello.java分析
    filter用户例子
    分析LogFilter
    理解session
    关于XML
    企业级应用和互联网应用的区别
  • 原文地址:https://www.cnblogs.com/zohnn/p/12872432.html
Copyright © 2020-2023  润新知