反射

反射

反射是一种机制/功能,
利用该机制/功能可以在程序运行过程中对类进行解剖并操作类中的构造方法,成员方法,成员属性。

反射的应用场景

1. 开发工具中写代码时的提示
   开发工具之所能够把该对象的方法和属性展示出来就使用利用了反射机制对该对象所有类
   进行了解剖获取到了类中的所有方法和属性信息,这是反射在 IDE 中的一个使用场景。
   
2. 各种框架的设计
   Java 的三大框架,简称 SSH,SSM。
   【SSH = Struts2 + Spring + Hibernate】
   【SSM = Springmvc + Spring + MyBatis】
   这三大框架的内部实现也大量使用到了反射机制,所以要想学好这些框架,则必须要
   求对反射机制熟练了。

使用反射机制解剖类的前提

必须先要获取字节码对象,即 Class 类型对象。

说明:
Java 中使用 Class 类表示 class 文件。任何一个 class 文件都是 Class 类的一个对象。

获取 Class 对象的三种方式

通过类名.class 获取

通过 Object 类的成员方法 getClass()方法获取

通过 Class.forName("全限定类名")方法获取
public class Demo01 {
    public static void main(String[] args) throws Exception{
        //通过类名.class获取Class 对象
        Class c1 = java.lang.String.class;
        
        //通过Object类的成员方法getClass()方法获取Class 对象
        Class c2 = new String().getClass();
        
        //通过Class.forName("全限定类名")方法获取Class 对象
        Class c3 = Class.forName("java.lang.String");
        
        //因为java.lang.String字节码文件只有一份
        //所有三种方式获取的java.lang.String字节码对象是相同的
        System.out.println(c1.hashCode());
        System.out.println(c2.hashCode());
        System.out.println(c3.hashCode());
    }
}

获取 Class 对象的信息

获取简单类名
String getSimpleName(); 获取简单类名,只是类名,没有包

获取完整类名
String getName(); 获取完整类名,包含包名 + 类名

创建对象
T newInstance() ; 创建此 Class 对象所表示的类的一个新实例。
要求:类必须有public 的无参数构造方法
public class Demo02 {
    public static void main(String[] args) throws Exception {
        //通过类名.class获取Class 对象
        Class c = String.class;
        
        //获取简单类名
        System.out.println(c.getSimpleName());
        
        //获取完整类名
        System.out.println(c.getName());
        
        //使用Class 对象创建对象
        Object obj = c.newInstance();
        
        //判断obj是否是String类型的对象
        if(obj instanceof String){
            String s = (String) obj;
            System.out.println(s.length());
        }
    }
}

获取 Class 对象的 Constructor 信息(构造方法信息)

利用反射可以在程序运行过程中对类进行解剖并操作里面的成员。
而一般常操作的成员:构造方法,成员方法,成员属性,

怎么利用反射来操作这些成员以及操作这些成员能干什么,先来看看怎么操作构造方法。

要通过反射操作类的构造方法,我们需要先知道一个Constructor 类。

Constructor

Constructor 是构造方法类,

类中的每一个构造方法都是 Constructor 的对象,

通过Constructor 对象可以实例化对象。

Class 类中与 Constructor 相关方法

1. Constructor[] getConstructors()
   获取所有的 public 修饰的构造方法
   
2. Constructor[] getDeclaredConstructors()
   获取所有构造方法,包括 privat 修饰的
   
3. Constructor getConstructor(Class... parameterTypes)
   根据参数类型获取构造方法对象,只能获得 public 修饰的构造方法
   
4. Constructor getDeclaredConstructor(Class... parameterTypes)
   根据参数类型获取构造方法对象,包括 private 修饰的构造方法

Constructor类中常用方法

1. T newInstance(Object... initargs)
   根据指定参数创建对象
   
2. void setAccessible(true)
   暴力反射,设置为可以直接访问私有类型的构造方法

/**
 * 获取Class对象的Constructor信息
 */
public class Demo03 {

    /**
     * 根据参数类型获取构造方法对象,只能获得public修饰的构造方法
     * Constructor getConstructor(Class... parameterTypes)
     *
     * 根据指定参数创建对象
     * T newInstance(Object... initargs)
     */
    
    @Test
    public void test1() throws Exception {
         //通过类名.class获取Class 对象
        Class clazz = Student.class;
        
        //根据参数类型获取Constructor对象(构造方法对象)
        Constructor c = clazz.getConstructor(int.class);
        
        //Constructor对象根据指定参数创建对象
        Object obj = c.newInstance(22);
    }

    /**
     * 根据参数类型获取构造方法对象,包括private修饰的构造方法
     * Constructor getDeclaredConstructor(Class... parameterTypes)
     *
     * 暴力反射,设置为可以直接访问私有类型的构造方法
     * void setAccessible(true)
     *
     * 根据指定参数创建对象
     * T newInstance(Object... initargs)
     */
    @Test
    public void test2() throws Exception{
        //通过类名.class获取Class 对象
        Class clazz = Student.class;
        
        //根据参数类型获取构造方法对象
        Constructor c = clazz.getDeclaredConstructor(int.class,String.class);
        
        //设置为可以直接访问私有类型的构造方法//不设定将抛出异常
        c.setAccessible(true);
        
        //Constructor对象根据指定参数创建对象
        Object obj = c.newInstance(22,"jack");
    }

}


/**
 * 学生
*/
public class Student {

    /**
     * 成员属性
     */
    private int id;
    private String name;

    /**
     * 构造方法
     */


    public Student(){
        System.out.println("public Student()");
    }
    public Student(int id){
        System.out.println("public Student(int id)");
    }
    private Student(int id,String name) {
        System.out.println("private Student(int id,String name)");
    }

    /**
     * 成员方法
     */
    public void run(){
        System.out.println("public void run()");
    }
    public void jump(int id){
        System.out.println("public void jump(int id)");
    }
    private void sleep(int id,String name){
        System.out.println("private void sleep(int "+id+",String "+name+")");
    }
    public static void close(){
        System.out.println("public static void close()");
    }
    public static void main(String[] args){
        System.out.println("public static void main(String[] args)");
    }
}

获取 Class 对象的 Method 信息(成员方法信息)

操作完构造方法之后,就来看看反射怎么操作成员方法了。

同样在操作成员方法之前我们需要学习一个类:Method 类。

Method 类

Method 是方法类,

类中的每一个方法都是 Method 的对象,

通过 Method 对象可以调用方法。

Class类中与Method相关方法

1. Method[] getMethods()
获取所有的public修饰的成员方法,//包括父类中

2. Method[] getDeclaredMethods()
获取当前类中所有的方法,包含私有的,//不包括父类中

3. Method getMethod("方法名", 方法的参数类型... 类型)
根据方法名和参数类型获得一个方法对象,只能是获取public修饰的

4. Method getDeclaredMethod("方法名", 方法的参数类型... 类型)
根据方法名和参数类型获得一个方法对象,包括private修饰的

Method类中常用方法

1. Object invoke(Object obj, Object... args)
根据参数args调用对象obj的该成员方法,如果obj=null,则表示该方法是静态方法

2. void setAccessible(true)
暴力反射,设置为可以直接调用私有修饰的成员方法

public class Demo04 {
    /**
     * 根据方法名和参数类型获得一个方法对象,只能是获取public修饰的
     * Method getMethod("方法名", 方法的参数类型... 类型)
     *
     * 根据参数args调用对象obj的该成员方法
     * Object invoke(Object obj, Object... args)
     */
    @Test
    public void test1() throws Exception{
        //通过类名.class获取Class 对象
       Class clazz = Student.class;
        
        //根据方法名和参数类型获得一个方法对象
       Method m = clazz.getMethod("jump",int.class);
        
        //clazz.newInstance() //使用Class 对象创建对象
        //根据参数args调用对象obj的该成员方法
       m.invoke(clazz.newInstance(),22);
    } 

    /**
     * 根据方法名和参数类型获得一个方法对象,包括private修饰的
     * Method getDeclaredMethod("方法名", 方法的参数类型... 类型)
     *
     * 暴力反射,设置为可以直接调用私有修饰的成员方法
     * void setAccessible(true)
     *
     * 根据参数args调用对象obj的该成员方法
     * Object invoke(Object obj, Object... args)
     */
    @Test
    public void test2() throws Exception{
         //通过类名.class获取Class 对象
        Class clazz = Student.class;
        
        //根据方法名和参数类型获得一个方法对象
        Method m = clazz.getDeclaredMethod("sleep",int.class,String.class);
        
        //设置为可以直接调用私有修饰的成员方法
        m.setAccessible(true);
        
        //clazz.newInstance() //使用Class 对象创建对象
        //根据参数args调用对象obj的该成员方法
        m.invoke(clazz.newInstance(),22,"jack");
    }

    /**
     * 反射static非main方法
     */
    @Test
    public void test3() throws Exception{
         //通过类名.class获取Class 对象
        Class clazz = Student.class;
        
        //根据方法名和参数类型获得一个方法对象
        Method m = clazz.getDeclaredMethod("close",null);
        
        //调用static方法
        m.invoke(null);
    }

    /**
     * 反射static的main方法
     * 注意:要将String[]放在另一个Object[]中
     * 很少反射main方法
     */
    @Test
    public void test4() throws Exception{
        //通过类名.class获取Class 对象
        Class clazz = Student.class;
        
        //根据方法名和参数类型获得一个方法对象
        Method m = clazz.getDeclaredMethod("main",String[].class);
        String[] args = {"a","b","c","d"};
        Object[] obj = {args};
        //Object obj = args;
        //main方法特点。。只获取obj数组中的第一个元素,且必须是String[]类型的
        m.invoke(null,obj);
    }
}

使用反射传入对象类型的数组时,默认情况会先对数组进行拆除.

/*
 */

class Person{
    
    
    public void eat(String[] foods){
        for(String food : foods){
            System.out.println("吃"+food);
        }
    }
    
    public void sum(int[] nums){
        int sum = 0;
        for(int num : nums){
            sum+=num;
        }
        System.out.println("结果是:"+ sum);
    }
    
}

public class Demo1 {
    
    public static void main(String[] args) throws Exception {
        //获取到该类的字节码对象
        Class clazz = Class.forName("com.itheima.reflect.Person") ;
        //创建对象
        Person p = (Person) clazz.newInstance();
        
        //通过Class对象得倒Method对象
        Method method = clazz.getMethod("eat", String[].class);
        //执行该方法
/*
 使用反射传入对象类型的数组时,默认情况会先对数组进行拆除。 然后
 把数组中的元素数据作为方法的方法的参数传入。
 */ 
        //String[] foods = {"鸡","鸭","狗","蛇"};
        
        //解决的方案一: 使用Object变量去引用一个数组对象
        //Object foods = new String[]{"鸡","鸭","狗","蛇"};
        //解决方案二: 可以利用Object的数组进行包装
        /*Object[] foods = new Object[]{new String[]{"鸡","鸭","狗","蛇"}};
        
        method.invoke(p, foods);*/
        
        Method method2 = clazz.getMethod("sum", int[].class);
        method2.invoke(p, new int[]{1,2,3});
    }
}

获取Class对象的Field信息

Field是属性类,类中的每一个属性都是Field的对象,通过Field对象可以

给对应的属性赋值和取值。

Class类中与Field相关方法

1. Field[] getFields()
获取所有的public修饰的属性对象,返回数组

2. Field[] getDeclaredFields()
获取所有的属性对象,包括private修饰的,返回数组
 
3. Field getField(String name)
根据属性名获得属性对象,只能获取public修饰的
 
4. Field getDeclaredField(String name)
根据属性名获得属性对象,包括private修饰的

Field类中常用方法

String getName()
获取字段名

Type getType()
获取字段名对应的类型
 
基本类型:setXxx方法和getXxx方法
引用类型:set方法和get方法

void setAccessible(true)
暴力反射,设置为可以直接访问私有类型的属性

public class Demo05 {
    /**
     * 获取所有的属性对象,包括private修饰的,返回数组
     * Field[] getDeclaredFields()
     */
    @Test
    public void test1() throws Exception{
         //通过类名.class获取Class 对象
        Class clazz = Student.class;
        
        //获取所有的属性对象,包括private修饰的,返回数组
        Field[] fs = clazz.getDeclaredFields();
        
        for (Field f : fs) {
            //输出文件名和文件类型
            System.out.println(f.getName()+"#"+f.getType());
        }
    }

    /**
     * 根据属性名获得属性对象,包括private修饰的
     * Field getDeclaredField(String name)
     *
     * 暴力反射,设置为可以直接访问私有类型的属性
     * void setAccessible(true);
     *
     * set通用方法都是给对象obj的属性设置使用
     * get通用方法是获取对象obj对应的属性值的
     */
    @Test
    public void test2() throws Exception {
        //通过类名.class获取Class 对象
        Class clazz = Student.class;
        
        //使用Class 对象创建对象
        Object obj = clazz.newInstance();
        
        //根据属性名获得属性对象,
        Field nameField = clazz.getDeclaredField("name");
        
        //设置为可以直接访问私有类型的属性
        nameField.setAccessible(true);
        
        if(nameField.getType()==String.class){
            
            //set通用方法都是给对象obj的属性设置使用
            nameField.set(obj,"jack");
            
            //get通用方法是获取对象obj对应的属性值的
            System.out.println(nameField.get(obj));
        }
    }
}

编写一个方法可以根据配置文件产【任意类型】的对象,并读取属性文件中的值,【设置】到这个类型的对象中。

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页