zoukankan      html  css  js  c++  java
  • JavaSE(十)之反射

    开始接触的时候可能大家都会很模糊到底什么是反射,大家都以为这个东西不重要,其实很重要的,几乎所有的框架都要用到反射,增加灵活度。到了后面几乎动不动就要用到反射。

    首先我们先来认识一下对象

    学生----->抽象----->Student

    表示学生   Student = .......

    那我们的反射中的Class呢?

    类型----->抽象----->Class(反射的入口破)

    java.lang.class    表示java中的类型

      Class c = Student.Class

      Class c = int.Class

      Class c =int[].Class

    一、反射(Reflection)的概述

    1.1、定义

      反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法 对于任意一个对象,都能够调用它的任意一个方法和属性, 这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

      即:在"运行时",通过反射机制可以动态获得到和该类型相关的各种信息。    

      java通常是先有类后有对象,有对象就可以调用方法或属性。反射其实是通过Class对象来调用类里面的方法。
      通过反射可以调用私有方法和私有属性。大部分框架都是运用反射原理。

    1.2、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.briup.test.Student

    1.3、获取一个类类型的Class对象的三种方式

      想要使用反射机制,就必须要先获取到该类的字节码文件对象(.class),通过字节码文件对象,就能够通过该类中的方法获取到

      我们想要的所有信息(方法,属性,类名,父类名,实现的所有接口等等),每一个类对应着一个字节码文件也就对应着一个Class类型的对象,也就是字节码文件对象

        1)使用Class类中的forName方法获得

              Class clazz1 = Class.forName("全限定类名");  //通过Class类中的静态方法forName,直接获取到一个类的字节码文件对象,此时该类还是源文件阶段,并没有变为字节码文件。

              这种方法很灵活,只需一个String类型参数即可,而String类型的数据改变起来很容易,注意该方法是会抛出异常。

        2)使用类名获得

            Class clazz2  = Person.class;    //当类被加载成.class文件时,此时Person类变成了.class,在获取该字节码文件对象,也就是获取自己, 该类处于字节码阶段。

        3)使用对象调用getClass方法获得    

            Class clazz3 = p.getClass();    //通过类的实例获取该类的字节码文件对象,该类处于创建对象阶段。

              getClass是Object中的final修饰的方法,每个对象都可以调用而且不能重写 

        注:以上三种方法获得的同一个对象(==比较),因为每个类型内存都有且只有一个Class类型对象。

    1.4、反射机制中的常见类的含义

      java.lang包下:
        Class  类        对java中所有类型抽象而得来的
        Package类        对java中所有包抽象而得来的

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

    二、反射机制获取类中的信息

    2.1、使用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.获得类中的注解
                使用反射也可以获得类中的注解.(在之后的内容在来了解)
    Class类型获取对象类中信息

    2.2、反射的常用操作

     1 public class Student{
     2             private long id;
     3             private String name;
     4 
     5             private static int age;
     6             
     7             get/set
     8 
     9             public static void say(){
    10                 System.out.println("say..");
    11             }

      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;
                    } 
    数组长度扩大一倍

    三、Class的API详解

    3.1、通过字节码对象创建实例对象

             

    3.2、获取指定构造器方法

      constructor 如果没有无参构造,只有有参构造如何创建实例呢?看下面

             

      总结上面创建实例对象:Class类的newInstance()方法是使用该类无参的构造函数创建对象, 如果一个类没有无参的构造函数, 就不能这样创建了,可以调用Class类的getConstructor(String.class,int.class)方法

        取一个指定的构造函数然后再调用Constructor类的newInstance("张三",20)方法创建对象

      获取全部构造方法  

                  

    3.3、获取成员变量并使用 

      Field对象

        获取指定成员变量

              

             Class.getField(String)方法可以获取类中的指定字段(可见的), 如果是私有的可以用getDeclaedField("name")方法获取,通过set(obj, "李四")方法可以设置指定对象上该字段的值,

              如果是私有的需要先调用setAccessible(true)设置访问权限,用获取的指定的字段调用get(obj)可以获取指定对象中该字段的值

        获取全部成员变量

            

    3.4、获得方法并使用 

               Method

              

            Class.getMethod(String, Class...) 和 Class.getDeclaredMethod(String, Class...)方法可以获取类中的指定方法,    

              如果为私有方法,则需要打开一个权限。setAccessible(true);

            用invoke(Object, Object...)可以调用该方法,

            跟上面同理,也能一次性获得所有的方法

                          

    3.5、获得该类的所有接口

      Class[] getInterfaces():确定此对象所表示的类或接口实现的接口

      返回值:接口的字节码文件对象的数组

    3.6、获取指定资源的输入流

      InputStream getResourceAsStream(String name)  

      return:一个 InputStream 对象;如果找不到带有该名称的资源,则返回 null

      参数:所需资源的名称,如果以"/"开始,则绝对资源名为"/"后面的一部分。

    四、两个实例来全面了解反射

    实例1:

    package corejava.test;
    
    import java.lang.reflect.Array;
    import java.lang.reflect.Constructor;
    
    class A{
        private int id;
        private String name="Tom";
        private  A(int a,String name){
            id=a;
        }
        @Override
        public String toString() {
            return "A [id=" + id + ", name=" + name + "]";
        }
    }
    
    public class Test1 {
        
        public static Object createObject(String classname) throws Exception {
            Class<?> name = Class.forName(classname);
            return createObject(name);
        }
        public static Object createObject(Class<?>c) throws Exception{
            Object object=null;
            try {
                object=c.newInstance();
            } catch (InstantiationException e) {
    //            如果此 Class 表示一个抽象类、接口、数组类、基本类型或 void; 
    //            或者该类没有 null 构造方法; 或者由于其他某种原因导致实例化失败。
                
    //            2、数组类、基本类型  能够创建对象
                
                if(c.isArray()){
                    object=Array.newInstance(c.getComponentType(),5);
                }
                else if(c.isPrimitive()){
                    
                    if(c == boolean.class){
                        object=false;
                    }else if(c==void.class){
                        throw new Exception(c.getName()+"不能创建对象!");
                    }else {
                        object=0;
                    }
                }else {
    //                3、没有 null 构造方法
                    
                    Constructor<?>[] constructors = c.getConstructors();
                    
                    if(constructors.length==0) throw new Exception(c.getName()+"没有Public构造器!");
                    
                    Class<?>[] parameterTypes = constructors[0].getParameterTypes();
                    Object[] parameters=new Object[parameterTypes.length];
                    for(int i=0;i<parameters.length;i++){
                        parameters[i]=createObject(parameterTypes[i]);
                    }
                    object=constructors[0].newInstance(parameters);
                }
    //            1、其他某种原因、void、抽象类、接口时抛出异常
                if(object==null)throw new Exception(c.getName()+"不能创建对象!");
            }
            return object;
        }
        public static void main(String[] args) throws Exception {
            
            System.out.println(createObject(A.class));
            System.out.println("ssss");
    //        System.out.println(new String());
    //        System.out.println("ssss");
        }
    
    }
    实例1

    实例2:

    package corejava.test;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.lang.reflect.Modifier;
    import java.lang.reflect.Parameter;
    import java.nio.file.spi.FileSystemProvider;
    
    import static java.lang.System.out;
    
    final class B{
        private int id;
        private String name="Tom";
        private static String mm="Tom";
        private  B(int a,String name){
            id=a;
        }
        @Override
        public String toString() {
            return "B [id=" + id + ", name=" + name + "]";
        }
        public int f(String name,A a){
            return id;}
    }
    public class Test2 {
    
        public static void printClass(Class<?> c) throws Exception{
            //1、包名;
            out.println(c.getPackage()+";");
            //2、修饰符 +class+类名{
            out.println(Modifier.toString(c.getModifiers())+
                    " class "+c.getSimpleName()+" {");
            
            //3、修饰符 +属性类型+属性名;
            for(Field f:c.getDeclaredFields()){
                out.print("	"+Modifier.toString(f.getModifiers())+
                        " "+f.getType().getSimpleName()+" "+f.getName());
                f.setAccessible(true);
                if(Modifier.isStatic(f.getModifiers()))
                    out.print(" = "+f.get(null));
                out.println(";");
            }
            
            //4、修饰符 +类名+( 参数类型+参数名字){  }
            for(Constructor<?> constructor:c.getDeclaredConstructors()){
                out.print("	"+Modifier.toString(constructor.getModifiers())+" "+
                        c.getSimpleName()+"(");    
                
                Parameter[] ps = constructor.getParameters();
                for(int i=0;i<ps.length;i++){
                    out.print(ps[i].getType().getSimpleName()+" "
                        +ps[i].getName());
                    if(i<ps.length-1)out.print(",");
                }
                out.println("){}");
            }
            out.println();
            //5、修饰符 +返回类型+方法名++( 参数类型+参数名字){  }
            for(Method m:c.getDeclaredMethods()){
                out.print("	"+Modifier.toString(m.getModifiers())+" "+
                        m.getReturnType().getSimpleName()+" "+
                        m.getName()+"(");    
                
                Parameter[] ps = m.getParameters();
                for(int i=0;i<ps.length;i++){
                    out.print(ps[i].getType().getSimpleName()+" "
                        +ps[i].getName());
                    if(i<ps.length-1)out.print(",");
                }
                out.println("){}");
                out.println();
            }
            //6、}
            out.println("}");
            
        }
        public static void main(String[] args) throws Exception {
            printClass(Object.class);
        }
    
    }
    实例2

    觉得不错的“点个推荐”哦!

  • 相关阅读:
    VS2019 离线安装方法详解
    VS2019 实用操作
    WIN7 X64位系统安装SQL SERVER2008失败总结
    给reportview传参数的操作过程
    山寨dell mini 3i的问题
    sql backup
    基于wince.net的环境,使用pocketBuilder调用webservice所需安装环境和步骤
    写了一个通用的用户选择页面,记录一下调用方法
    回顾这几年开发医药CRM的历程
    Cumulative Update package 3 for SQL Server 2008 R2三个补丁下载地址,官网下载不直接给地址,不知为什么
  • 原文地址:https://www.cnblogs.com/zhangyinhua/p/7266178.html
Copyright © 2011-2022 走看看