Java 反射(Reflection)

反射提供描述jar包、模块和类型的对象(Class 类型)。 可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型,然后调用其方法或字段。 如果代码中使用了注解(Annotatation),可以利用反射来访问它们。本文主要介绍Java 反射(Reflection)。

1、Java 反射(Reflection)使用场景

反射在以下情况下很有用:

1)需要访问Jar包数据中的注解时。

2)检查和实例化jar包中的类型。

3)在运行时构建新类型。

4)编译时根本无法预知该对象的类型,程序只能依靠运行时来发现对象和类的具体信息

5)执行后期绑定,访问在运行时创建的类型上的方法。可以动态调用执行方法。

6)可以动态生成类框架,如Gson。

7)Tomcat服务器是典型的反射机制应用

2、Java 反射(Reflection)的使用

1)获取Class对象

通过Java代码获取Class对象有四种方法,具体如下,

import java.util.*;

public class Main {
  public static void main(String[] args) throws Exception  {
    Class<?> class1 = null;
    Class<?> class2 = null;
    Class<?> class3 = null;
    Class<?> class4 = null;
    //调用Class的静态方法:forName(全类名)    
    class1 = Class.forName("java.util.Date");
    //通过运行时类的对象的getClass方法
    class2 = new java.util.Date().getClass();
    //调用运行时类的属性
    class3 = java.util.Date.class;
    //ClassLoader中的loadClass(全类名)方式获取
    ClassLoader classLoader = Main.class.getClassLoader();
    class4 = classLoader.loadClass("java.util.Date");
        System.out.println(class4);

    System.out.println("类名称   " + class1.getName());
    System.out.println("类名称   " + class2.getName());
    System.out.println("类名称   " + class3.getName());
    System.out.println("类名称   " + class4.getName());

    System.exit(0); //success
  }
}

2)9个预定义Class对象

byte,short,int,long,char,float,double,booleanvoid关键字,都有class属性。例如,

int.class;
boolean.class;

int的包装类是IntegerIntegerint 不是同一种数据类型,8个基本数据类型的包装类中都有一个常量TYPETYPE表示的是该包装类对应的基本数据类型的Class对象,

Integer.TYPE == int.class;//true
Integer.TYPE != Integer.class;//true

3)获取类中的构造器

获取所有公有构造器:Class对象的getDeclaredConstructors();

获取所有构造器:Class对象的getDeclaredConstructors();

获取单个公有无参构造器:Class对象的getConstructor(null);

获取单个指定公有构造器:Class对象的getConstructor(String.class);

获取单个指定构造器:Class对象的getDeclaredConstructor(String.class);

4)获取成员变量的方法

方法

描述

Field[] getFields()

获取当前Class所表示类的所有public字段。

Field[] getDeclaredFields()

获取当前Class所表示类的所有字段,

包括:private、protected、默认、public。

public Field getField(String fieldName)

获取当前Class所表示类指定的public字段。

public Field getDeclaredField(String fieldName)

获取当前Class所表示类的指定字段

(可以为private)

public void set(Object obj,Object value) 

参数 obj:要设置的字段所在的对象,

value:要为字段设置的值

5)获取类中的成员方法

方法

描述

public Method[] getMethods()

获取当前Class所表示类和继承的所有public方法(包含了父类的方法也包含Object类)。

public Method[] getDeclaredMethods()

获取当前Class所表示类的所有成员方法,包括私有的(不包括继承的,和访问权限无关)。

public Method getMethod

(String name,

Class... parameterTypes)

获取当前Class所表示类的指定的public方法(包括继承的)。

public Method getDeclaredMethod

(String methodName,

Class... parameterTypes)

获取当前Class所表示类的指定的成员方法(不包括继承的)。

参数  methodName:方法名,

Class ... parameterTypes:形参的Class类型对象

6)调用执行方法

public Object invoke(Object obj,Object ... args)  :参数  obj:要调用方法的对象,args:调用方法时所传递的实参。

使用示例:

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;


public class Main{
  public static void main(String[] args) throws Exception {
        
        Sample s=new Sample();
        s.setProp1("cjavapy.com");
        s.setProp2(100);
        
        Class clazz=Sample.class;//获取Class对象      
                
        String clazzName=clazz.getName();//获取类名,含包名
        String clazzSimpleName=clazz.getSimpleName();//获取类名,不含包名
        System.out.println("getName:"+clazzName+"\tgetSimpleName:"+clazzSimpleName);
        
        int mod=clazz.getModifiers();//获取类修饰符
        System.out.println("Modifier.isPublic:"+Modifier.isPublic(mod));//判断类修饰符
        System.out.println("Modifier.isProtected:"+Modifier.isProtected(mod));//判断类修饰符
        
        Package p=clazz.getPackage();//获取包
        System.out.println("getPackage:"+p);
        
        Class superClass=clazz.getSuperclass();//获取父类
        System.out.println("getSuperclass:"+superClass);
        
        Class[] interfaces=clazz.getInterfaces();//获取实现接口
        System.out.println("getInterfaces:"+interfaces.length);
        
        Constructor[] cons=clazz.getConstructors();//构造方法
        System.out.println("getConstructors:"+cons.length);           
    Method[] methods=clazz.getMethods();//获取所有方法      
        System.out.println("getMethods:"+methods.length);     
        for(Method method:methods){        
            System.out.println("method.getName:"+method); 
        }
     Method method=clazz.getMethod("getProp1", new Class[]{});//获取指定方法    
       System.out.println("getMethod(,):"+method);     
        Object methodVlaue=method.invoke(s, new Object[]{});//调用方法
        System.out.println("method.invoke(,):"+methodVlaue);
    Method method3=clazz.getMethod("setProp3",Double.class);//获取指定方法
      System.out.println("getMethod(,):"+method3);
        //调用setter方法,该方法没有返回值,所以methodVlaue3为null;此处注意参数2.0 ,不能用null     
        Object methodVlaue3=method3.invoke(s, 2.0);     
        System.out.println("method.invoke(,):"+methodVlaue3);

        Field[] fields=clazz.getDeclaredFields();//获取变量
        System.out.println("getDeclaredFields:"+fields.length);
        for(Field field:fields){
            field.setAccessible(true);         
            field.set(s,null);//设置字段的值
            //获取实例属性名和值
            System.out.println("field.getAnnotations:"+field.getAnnotations().length+"\tfield.getName:"+field.getName()+"\tfield.get:"+field.get(s));
            
        }
        
        Annotation[] annos=clazz.getAnnotations();//获取类注解
        System.out.println("getAnnotations:"+annos.length);
        
    }
}

class Sample {
    
    private String prop1;
    private Integer prop2;  
    private Double prop3;
    
    public String getProp1() {
        return prop1;
    }
    public void setProp1(String prop1) {
        this.prop1 = prop1;
    }
    public Integer getProp2() {
        return prop2;
    }
    public void setProp2(Integer prop2) {
        this.prop2 = prop2;
    }
  public Double getProp3() {
       return prop3;
    }
    public void setProp3(Double prop3) { 
        this.prop3 = prop3;
    }
}

3、Java 反射使用实例

1)反射调用执行main方法

import java.lang.reflect.Method;
 
public class Main {
    public static void main(String[] args) {
        try {
            //获取Sample的字节码
            Class clazz = Class.forName("Sample");
            //获取main方法
             Method methodMain = clazz.getMethod("main", String[].class);//第一个参数:方法名称,第二个参数:方法形参的类型,
            //调用main方法
      
            //第一个参数,对象类型,
            //第二个参数是String数组,注意在jdk1.4时是数组,jdk1.5之后是可变参数,
            //将  new String[]{"a","b","c"} 拆成3个对象,所以需要将它强转。
            methodMain.invoke(null, (Object)new String[]{"p1","p2","p3"});//方法一
            // methodMain.invoke(null, new Object[]{new String[]{"p1","p2","p3"}});//方法二
        } catch (Exception e) {
            e.printStackTrace();
        }
        
    }
}
 
class Sample {    
   public static void main(String[] args) {        
    System.out.println("main方法执行"); 
   }
}

2)使用反射调用 ArrayList对象的add方法

import java.lang.reflect.Method;
import java.util.ArrayList;

public class Main {
    public static void main(String[] args) throws Exception{
        ArrayList list = new ArrayList<>();
        list.add("Java");
        list.add("CJavaPy");
  
        //获取ArrayList的Class对象,调用add()方法,添加数据
        Class listClass = list.getClass(); //得到 list对象的字节码对象
        //获取add()方法
        Method m = listClass.getMethod("add", Object.class);
        //调用add()方法
        m.invoke(list, "https://www.cjavapy.com");
        //可以通过反射越过泛型检查的,添加整型的值
        m.invoke(list,11);
        //遍历集合
        for(Object obj : list){
            System.out.println(obj);
        }
    }
}

3)使用反射为对象赋值

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class Main {
    
    private String prop1;
    private Integer prop2;
    private Double prop3;
    
    public String getProp1() {
        return prop1;
    }
    public void setProp1(String prop1) {
        this.prop1 = prop1;
    }
    public Integer getProp2() {
        return prop2;
    }
    public void setProp2(Integer prop2) {
        this.prop2 = prop2;
    }
  public Double getProp3() {
       return prop3;
    }   
    public void setProp3(Double prop3) {
        this.prop3 = prop3;
    }
     /**
     * 用于对类的字段赋值,无视private,project修饰符,无视set/get方法
     * @param c 要反射的类
     * @return
     */
    @SuppressWarnings("unchecked")
    public static Object setClassValue(Class c, Object source, Object object) {
        try {
            Class<?> obj = object.getClass();
            Class<?> s = source.getClass();
            Field[] fields = obj.getDeclaredFields();
            Field[] sfields = s.getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                fields[i].setAccessible(true);
                
                for (int j = 0; j < sfields.length; j++) {
                    if (sfields[j].getName().equals(fields[i].getName())) {
                        fields[i].set(object, sfields[j].get(source));
                        break;
                    }
                }
            }
            return object;
        } catch (Exception e) {
            e.printStackTrace();
        } 
        return null;
    }
   public static void main(String[] args) {
        try {
           Main s = new Main();
           s.setProp1("Java");
           s.setProp2(11);
           s.setProp3(22.00); 
           Main o = new Main();            
           setClassValue(o.getClass(), s, o);
           System.out.println(o.getProp1()+" "+o.getProp2()+" "+o.getProp3());
        } catch (Exception e) {
            e.printStackTrace();
        }
        
    }    
}

4)使用反射获取注解

import java.lang.annotation.*;
import java.lang.reflect.Method;


@SampleAnnotation(Value = "java", Name = "cjavapy")
public class Main {
    public static void main(String[] args) {
        Main s = new Main();

        Class clazz = s.getClass();
        Annotation[] annotations = clazz.getAnnotations();
        for (Annotation anno : annotations) {
            System.out.println("Annotation Type: " + anno.annotationType());
        }

        SampleAnnotation anno = (SampleAnnotation) clazz.getAnnotation(SampleAnnotation.class);
        System.out.println("Anno Value  : " + anno.Value());
        System.out.println("Anno Name: " + anno.Name());

        try {
            Method m = clazz.getMethod("sayHi");

            anno = m.getAnnotation(SampleAnnotation.class);
            System.out.println("Anno Value  : " + anno.Value());
            System.out.println("Anno Name: " + anno.Name());
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        s.sayHello();
    }

    @SampleAnnotation(Value = "Hi", Name = "Java")
    public void sayHi() {
    }

    @SampleAnnotation(Value = "Hello", Name = "CJavaPy")
    public void sayHello() {
        try {
            Method m = getClass().getMethod("sayHello");
            SampleAnnotation anno = m.getAnnotation(SampleAnnotation.class);

            System.out.println(anno.Value() + " " + anno.Name());
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface SampleAnnotation {
    String Name()default "Java";
    String Value()default "web";
}

推荐阅读
cjavapy编程之路首页