java学习笔记58--反射(Reflection)

反射机制是在运行态中,对于任意一个类,都能知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
即:在“运行时”,通过反射机制可以动态获得和该类型相关的各种信息。

1)Class类型 java.lang.Class类
        Class是对java中所有类型的抽象。即一个Class类型对象可以表示出java中任意一种类型。
        每种类型在加载到内存后,内存中都会生产一个与之对应的Class类型对象(有且只有一个),用来表示该类型。每个类型都有且只有一个Class类型对象与之对应,通过这个Class类型对象就可以获得到该类型中的各种信息。
        Class类是Java反射的入口.
        1.表示基本类型
            Class c = int.class;
            System.out.println(c.isPrimitive());//true
            System.out.println(c.getName());//int
            
            注:其他基本类型的情况类似

        2.表示类类型
            注:s.getClass()方法返回的是变量s所指向对象的实现类型的Class对象。
            Student s = new Student();
            Class c1 = s.getClass();
            Class c2 = Student.class;
            System.out.println(c1 == c2);//true
            
            //p指向的对象实际类型是Student
            Person p = new Student();
            Class c1 = p.getClass();//c1表示Student类型
            Class c2 = Person.class;//c2表示Person类型
            System.out.println(c1 == c2);//false

        3.表示接口类型
            Action a = new Student();
            Class c1 = a.getClass();//c1表示Student类型
            Class c2 = Action.class;//c2表示Action类型
            System.out.println(c1 == c2);//false

            System.out.println(c2.isInterface());//true

        
        4.表示数组类型
            int[] a = new int[4];
            Class c1 = a.getClass();
            Class c2 = int[].class;
            System.out.println(c1 == c2);//true
            System.out.println(c1.isArray());//true

            Class c3 = c1.getComponentType();//c3表示该数组是使用什么类型声明出来的
            System.out.println(c3.getName());//int

            Student[] a = new Student[4];
            Class c1 = a.getClass();
            Class c2 = Student[].class;
            System.out.println(c1 == c2);//true
            System.out.println(c1.isArray());//true

            Class c3 = c1.getComponentType();//c3表示该数组是使用什么类型声明出来的
            System.out.println(c3.getName());//com.wsj.test.Student

2)获得一个类类型的Class对象的三种方式
        以上几种情况中,所以最多的还是类类型的Class对象
        1.使用对象调用getClass方法获得
            getClass是Object中的final修饰的方法,每个对象都可以调用而且不能重写
            Student s = new Student();
            Class c = s.getClass();

        2.使用类名获得
            Class c = Student.class;

        3.使用Class类中的forName方法获得
            //这种方法很灵活,只需一个String类型参数即可
            //而String类型的数据改变起来很容易
            //注意该方法是会抛出异常的
            Class c = Class.forName("com.wsj.test.Student");

3)反射机制中的常见类的含义
        java.lang包下:
        Class类        对java中所有类型抽象而得来的
        package类    对java中所有包抽象而得来的

        java.lang.reflect包下:
        Modifier类     对java中所有修饰符抽象而得来的
        Field类          对java中所有属性抽象而得来的
        Method类      对java中所有方法抽象而得来的
        Constructor 类    对java中所有构造器抽象而得来的
        Array类         提供了对数组对象的动态访问
        ParameterizedType接口  在反射中表示参数化类型
            例如:List<String> Point<Long,Long>等这种带泛型的类型

4)使用Class类型对象获得类中的信息
        1.获得该类所处的包的信息
            Student s = new Student();
            Class c = s.getClass();
            System.out.println(c.getPackage().getName());

        2.获得该类的修饰符信息
            //每个修饰符对应一个int值
            //如果有多个修饰符则int值相加
            Student s = new Student();
            Class c = s.getClass();
            System.out.println(c.getModifiers());

            System.out.println(Modifier.PUBLIC);
            System.out.println(Modifier.FINAL);

        3.获得该类的名字
            Student s = new Student();
            Class c = s.getClass();
            System.out.println(c.getName());
        
        4.获得该类的父类的Class对象
            Student s = new Student();
            Class c = s.getClass();
            //superclass表示其父类型的Class对象
            Class superclass = c.getSuperclass();
            System.out.println(superclass.getName());

            例如:
            Class c = Object.class;
            Class superclass = c.getSuperclass();
            System.out.println(superclass.getName());
            //运行报错,因为Object没有父类
            

            例如:
            //判断c1是不是c2的子类型
            //判断c3是不是c2的子类型
            Class c1 = Student.class;
            Class c2 = Person.class;
            Class c3 = String.class
            System.out.println(c2.isAssignableFrom(c1));//true
            System.out.println(c2.isAssignableFrom(c3));//false
        
        5.获得该类所实现的接口类型的Class对象
            Student s = new Student();
            Class c = s.getClass();
            Class[] interfaces = c.getInterfaces();
            for(Class clazz:interfaces){
                System.out.println(clazz.getName());
            }

            例如:
            //判断c1是不是c2的实现类
            //判断c3是不是c2的实现类
            Class c1 = Student.class;
            Class c2 = Action.class;
            Class c3 = String.class
            System.out.println(c2.isAssignableFrom(c1));//true
            System.out.println(c2.isAssignableFrom(c3));//false
    
        
        6.获得该类中所有的属性
            Student s = new Student();
            Class c = s.getClass();
            Field[] declaredFields = c.getDeclaredFields();
            
            for(Field f:declaredFields){
                System.out.println(f.getModifiers());
                System.out.println(f.getType().getName());
                System.out.println(f.getName());
            }
            注:
            getDeclaredFields()方法返回类中声明的属性,包括私有的
            getFields()方法只返回类中public修饰的属性,包括继承的
    
            
            例如:
            //获得某个指定的属性(也包括私有属性)
            Student s = new Student();
            Class c = s.getClass();
            Field f = c.getDeclaredField("score");
            System.out.println(f.getModifiers());
            System.out.println(f.getType().getName());
            System.out.println(f.getName());


        7.获得该类中所有的方法
            Student s = new Student();
            Class c = s.getClass();
            Method[] declaredMethods = c.getDeclaredMethods();
            for(Method m:declaredMethods){
                System.out.println(m.getModifiers());
                System.out.println(m.getReturnType().getName());
                System.out.println(m.getName());
                System.out.println(Arrays.toString(m.getParameterTypes()));
                System.out.println(Arrays.toString(m.getExceptionTypes()));
            }

            注:
            getDeclaredMethods()方法返回类中声明的方法,包括私有的
            getMethods()方法只返回类中public修饰的方法,包括继承的
            

            例如:
            //获得某个指定的方法(也包括私有方法)
            Student s = new Student();
            Class c = s.getClass();
            Method m = c.getDeclaredMethod("print");
            System.out.println(m.getModifiers());
            System.out.println(m.getReturnType().getName());
            System.out.println(m.getName());
            System.out.println(Arrays.toString(m.getParameterTypes()));
            System.out.println(Arrays.toString(m.getExceptionTypes()));


        8.获得该类中所有的构造器
            Student s = new Student();
            Class c = s.getClass();
            Constructor[] declaredConstructors = c.getDeclaredConstructors();
            for(Constructor con:declaredConstructors){
                System.out.println(con.getModifiers());
                System.out.println(con.getName());
                System.out.println(Arrays.toString(con.getParameterTypes()));
                System.out.println(Arrays.toString(con.getExceptionTypes()));
            }

            注:
            getDeclaredConstructors()方法返回类中所有构造器
            getConstructors()方法只返回类中public修饰的构造器
            
            
            例如:
            //获得某个指定的构造器(也包括私有构造器)
            Student s = new Student();
            Class c = s.getClass();
            Constructor con = c.getDeclaredConstructor(double.class);
            System.out.println(con.getModifiers());
            System.out.println(con.getName());
            System.out.println(Arrays.toString(con.getParameterTypes()));
            System.out.println(Arrays.toString(con.getExceptionTypes()));


        9.获得父类型中的泛型类型
            因为泛型类的泛型参数在编译期会被擦除,所以我们不能再运行期间直接拿到该泛型的实际类型,但是可以通过子类的Class对象来获取父类的泛型类型。

            例如: 不通过子类不能获得泛型实际类型
            public class GenericTest<T,S>{
                public T name;
                public S say(T t,S s){
                    return s;
                }
            }
            
            main:
                GenericTest<String,Integer> t = new GenericTest<String,Integer>();
                Class c = t.getClass();
                Field field = c.getDeclaredField("name");
                System.out.println(field.getType());
                System.out.println(field.getGenericType());
                //输出结果:
                    class java.lang.Object
                    T
                
                System.out.println("-------------------------");
                Method method = c.getMethod("say", Object.class,Object.class);
                System.out.println(method.getReturnType());
                System.out.println(method.getGenericReturnType());
                //输出结果:
                class java.lang.Object
                S

                System.out.println("-------------------------");
                System.out.println(Arrays.toString(method.getParameterTypes()));
                System.out.println(Arrays.toString(method.getGenericParameterTypes()));
                //输出结果:
                [class java.lang.Object, class java.lang.Object]
                [T, S]

            例如: 通过子类可以获得父类中泛型的实际类型
            public class GenericTest<T,S>{
                public T name;
                public S say(T t,S s){
                    return s;
                }
            }
            public class Sub entends GenericTest<String,Integer>{}
            
            main:
                Class c = Sub.class;

                //获得父类类型,包含泛型参数信息
                Type superType = c.getGenericSuperclass();

                //判断父类类型是不是属于ParameterizedType类型
                //ParameterizedType表示带泛型的类型
                if(superType instanceof ParameterizedType){
                    //强转,并调用方法获得泛型参数的实例类型
                    ParameterizedType pt = (ParameterizedType)superType;

                    Type[] actualTypeArguments = pt.getActualTypeArguments();

                    //循环遍历,并强转为Class类型,因为Type接口中没有任何方法
                    for(Type t:actualTypeArguments){
                        Class clazz = (Class)t;
                        System.out.println(clazz.getName());
                    }
                }           

            例如: 通过子类可以获得实现接口中泛型的实际类型        
                Class c = Sub.class;
                Type[] types = c.getGenericInterfaces();
                for(Type t:types){
                    if(t instanceof ParameterizedType){
                        ParameterizedType pt = (ParameterizedType)t;

                        Type[] actualTypeArguments = pt.getActualTypeArguments();

                        for(Type type:actualTypeArguments){
                            Class clazz = (Class)type;
                            System.out.println(clazz.getName());
                        }

                    }
                }


        10.获得类中的注解
            使用反射也可以获得类中的注解.(在之后的内容在来了解)

5)反射中的常用操作
        public class Student{
            private long id;
            private String name;

            private static int age;
            
            get/set

            public static void say(){
                System.out.println("say..");
            }

        }
        1.使用反射的方式调用构造器创建类的对象
            默认方式:必须调用无参构造器
            Class c = Student.class;
            Student s = (Student)c.newInstance();

            通用方式:获得构造器对象,并调用该构造器
            注:getConstructor方法和newInstance方法的参数都是可变参数
            例如:获得无参构造器并调用创建对象
            Class c = Student.class;
            Constructor constructor = c.getConstructor();
            Student o = (Student)constructor.newInstance();
            System.out.println(o);

            例如:获得有参构造器并调用创建对象
            Class c = Student.class;
            Constructor constructor = c.getConstructor(long.class,String.class);
            Student o = (Student)constructor.newInstance(1L,"tom");
            System.out.println(o);


        2.使用反射的方式访问对象中的属性
            例如:
            Student s = new Student();
            Class c = s.getClass();
            Field[] declaredFields = c.getDeclaredFields();
            for(Field f:declaredFields){

                //设置私有属性可以被访问
                f.setAccessible(true);

                //判断属性是否为static,静态属性的访问不需要对象
                if(Modifier.isStatic(f.getModifiers())){
                    System.out.println(f.getName() +" = "+f.get(null));
                }else{
                    System.out.println(f.getName() +" = "+f.get(s));
                }
            }

            注:Field类中的get方法可以获得属性值,set方法可以给属性设置值。
        

        3.使用反射的方式调用对象中的方法
            例如:
            Student s = new Student();
            Class c = s.getClass();
            
            Method m1 = c.getMethod("setName",String.class);
            //m1表示Student中的setName方法
            //调用对象s中的m1方法,并且传参"tom"
            //s.setName("tom");
            m1.invoke(s, "tom");
            
            Method m2 = c.getMethod("getName");
            String name = (String)m2.invoke(s);
            System.out.println("name = "+name);
            
            //调用静态方法 不需要对象
            Method m3 = c.getMethod("say");
            m3.invoke(null);

        4.使用反射的方式动态操作数组
            注:
            Object[] o1 = new int[1];//编译报错
            long[]      o2 = new int[1];//编译报错
            int[]     o3 = new int[1];//编译通过
            
            Object[] o1 = new 任意引用类型[1];//编译通过

            例如:
            //要求:传任意类型"数组",把数组长度扩大1倍并返回
            //注意这里不能收Object[],
            //因为Object[] o = new Integer[4];编译通过
            //但是Object[] o = new int[4];      编译报错
            //那么使用Object类中就可以接收任意类型的数组了
            public static Object arrayCopy(Object obj){
                //代码
            }

            实现:
            public static Object arrayCopy(Object obj){
                Class c = obj.getClass();
                Object newArray = null;
                if(c.isArray()){
                    int len = Array.getLength(obj);
                    Class<?> type = c.getComponentType();
                    newArray = Array.newInstance(type, len*2);
                    for(int i=0;i<len;i++){
                        Object value = Array.get(obj, i);
                        Array.set(newArray, i, value);
                    }
                }
                return newArray;
            }

猜你喜欢

转载自blog.csdn.net/onepiece_loves/article/details/88707104