13.java反射机制

反射(reflection)

可以在运行时加载、探知、使用编译期间完全未知的类
程序在运行状态中,可以动态加载一个只有名称的类,对于任意一个已加载的类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;
Class c = Class.forName("com.company.User");
加载完类后,在堆内存中就产生了一个Class类型对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为"反射"

Class类

要点

1.java.lang.Class类十分特殊,用来表示java中类型(class/interface/enum/annotation/primitive type/void)本身
2.Class类的对象中包含了某个被加载的类的结构,一个被加载的类对应一个Class对象
3.当一个class被加载,或当加载器(class loader)的defineClass()被jvm调用,JVM便自动产生一个Class对象
4.Class类是Reflection的根源 针对任何想动态加载、运行的类唯有先获得相应的Class对象

     获得Class对象的三种方法:
     1.Class.forName("类路径")  (最常用)
     2.类路径.getClass()
     3.类.class;

/**
 *测试Class类
 */
public class Test01 {
    public static void main(String[] args){
        try {
            //1.Class.forName() 最常用
            Class clazz = Class.forName("com.company.User");
            Class clazz2 = Class.forName("com.company.User");
            System.out.println(clazz.hashCode()==clazz2.hashCode());  //true 说明一个类只对应一个Class对象

            //2. getClass方法
            Class strclazz1 = "hahh".getClass();
            //3. .class方法
            Class strclazz2 = String.class;
            System.out.println(strclazz1.hashCode()==strclazz2.hashCode());  //true 同一对象

            int[] arr01 = new int[10];
            int[] arr02 = new int[20];
            int[][] arr03 = new int[10][3];
            System.out.println(arr01.getClass().hashCode()==arr02.getClass().hashCode()); //true  数组Class对象和长度无关
            System.out.println(arr01.getClass().hashCode()==arr03.getClass().hashCode()); //false  和维度有关

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

反射机制的常见作用

  • 动态加载类、动态获取类的信息(属性、方法、构造器)
  • 动态构造对象
  • 动态调用类和对象的任意方法、构造器
  • 动态调用和处理属性
  • 获取泛型信息
  • 处理注解

 通过反射API动态获取类的信息

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

/**
 *通过反射API获取类的相关信息
 */
public class Test01 {
    public static void main(String[] args){
        try {
            Class clazz = Class.forName("com.company.User");

            //获取类名
            System.out.println(clazz.getName());  //包名+类名 com.company.User
            System.out.println(clazz.getSimpleName());  //类名 User

            //获取属性信息
            //Field name = clazz.getField("name");  //获取单个属性 NoSuchFieldException 只能返回public修饰的属性
            //Field[] fields = clazz.getFields();  //获取所有属性 []  也是只能回public修饰的属性
            //System.out.println(Arrays.toString(fields));
            Field name = clazz.getDeclaredField("name");
            System.out.println(name);  //private java.lang.String com.company.User.name
            Field[] fields = clazz.getDeclaredFields();
            for(Field temp:fields){
                System.out.println(temp);
                //private int com.company.User.id
                //private java.lang.String com.company.User.name
                //private int com.company.User.age
            }

            //获取方法信息
            //和获得属性信息基本一致,注意方法有参数时,要加上方法参数类型的Class对象
            Method method = clazz.getMethod("setName", String.class); //public修饰的单个方法 String.class表示setName的参数形式
            Method[] methods = clazz.getMethods();  //获取所有方法
            Method method1 = clazz.getDeclaredMethod("setName", String.class);  //获取单个方法 private修饰的也能获取
            Method[] methods1 = clazz.getDeclaredMethods();  //获取所有方法  private修饰的也能获取

            //获得构造器信息 和上面一样  注意获得某个特有的构造器和方法一样,要加上构造器的参数类型的Class对象
            clazz.getConstructor();
            clazz.getConstructors();
            clazz.getDeclaredConstructor();
            clazz.getDeclaredConstructors();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

通过反射API动态操作类的属性、方法、构造器

import com.company.User;

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

/**
 * 通过反射API动态操作类的属性、方法、构造器
 */
public class Test02 {
    public static void main(String[] args) {
        try {
            Class clazz = Class.forName("com.company.User");

            //通过反射API调用构造器,创建对象
            User user1 = (User) clazz.newInstance();  //实际是调用了User无参构造方法

            Constructor<User> c = clazz.getDeclaredConstructor(int.class,String.class,int.class);
            User user2 = c.newInstance(101,"陈一",20);
            System.out.println(user2.getName());  //陈一

            //通过反射API调用普通方法
            User user3 = (User) clazz.newInstance();
            //当然有对象了可以直接操作对象,user3.setName("陈二");但是下面的方法也有优点
            Method setNameMethod = clazz.getMethod("setName", String.class);
            setNameMethod.invoke(user3,"陈二");  //和上面user3.setName("陈二")效果相同
            //优点:方法名称和参数都是变量,可以动态调用
            System.out.println(user3.getName());  //陈二

            //通过反射API调用属性
            User user4 = (User) clazz.newInstance();
            Field nameField = clazz.getDeclaredField("name");
            nameField.setAccessible(true);  //这个属性不需要作安全检查,可以直接访问  方法也是(如果是私有方法也需要设置)
            nameField.set(user4,"陈四");  //通过反射设置属性的值
            System.out.println(user4.getName());  //陈四
            System.out.println(nameField.get(user4));  //陈四 通过反射读属性的值
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

}

通过反射操作泛型

 java采用泛型擦除机制来引入泛型。java中的泛型仅仅是给编译器javac使用的确保数据的安全性和免去强制类型转换的麻烦。但是,一旦编译完成,所有的和泛型有关的类型全部擦除

 为了通过反射操作这些类型以迎合实际开发的需要,java新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType几种类型来代表不能被归一到Class类中的类型但是和原始数据类型齐名的类型。

 ParameterizedType:表示一种参数化的类型,比如Collection<String>
 GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型
 TypeVariable:各种类型变量的公共父接口
 WildcardType:代表一种通配符类型表达式,比如?,?extends Number,? super Integer

demo

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;


/**
 * 通过反射获取泛型信息
 */
public class Demo{  
    //定义两个带泛型的方法
    public void test01(Map<String,Person> map,List<Person> list){
        System.out.println("Demo.test01()");
    }   
    public Map<Integer,Person> test02(){
        System.out.println("Demo.test02()");
        return null;
    }   


    public static void main(String[] args) {
        try {           
            //获得指定方法参数泛型信息
            Method m = Demo.class.getMethod("test01", Map.class,List.class);
            Type[] t = m.getGenericParameterTypes();  //获得带泛型参数类型

            for (Type paramType : t) {
                System.out.println("#"+paramType);
                if(paramType instanceof ParameterizedType){
                    //获取泛型中的具体信息
                    Type[] genericTypes = ((ParameterizedType) paramType).getActualTypeArguments();
                    for (Type genericType : genericTypes) {
                        System.out.println("泛型类型:"+genericType);
                    }
                }
            }   

            //获得指定方法返回值泛型信息
            Method m2 = Demo.class.getMethod("test02", null);
            Type returnType = m2.getGenericReturnType();
            if(returnType instanceof ParameterizedType){
                    Type[] genericTypes = ((ParameterizedType) returnType).getActualTypeArguments();

                    for (Type genericType : genericTypes) {
                        System.out.println("返回值,泛型类型:"+genericType);
                    }                   
            }       

        } catch (Exception e) {
            e.printStackTrace();
        }   
    }
}

通过反射操作注解

见上一篇文章https://mp.csdn.net/postedit/84291266

猜你喜欢

转载自blog.csdn.net/weixin_39722922/article/details/88845296