• 黑马程序员:Java基础总结----反射


    黑马程序员:Java基础总结



    反射

      ASP.Net+Android+IO开发 .Net培训 、期待与您交流!




    反射

    反射的基石:Class类

    Class类代表Java类,它的各个实例对象又分别对应什么呢?
    对应各个类在内存中的字节码,例如,Person类的字节码,ArrayList类的字节码,等等。
    一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以它们在内存中的内容是不同的,这一个个的空间可分别用一个个的对象来表示,这些对象显然具有相同的类型,这个类型是什么呢?
    如何得到各个字节码对应的实例对象( Class类型)
    类名.class,例如,System.class
    对象.getClass(),例如,new Date().getClass()
    Class<?> getClass()
              返回此 Object 的运行时类。
    Class.forName("类名"),例如,Class.forName("java.util.Date");
    static Class<?> forName(String className)
              返回与带有给定字符串名的类或接口相关联的 Class 对象。

    九个预定义Class实例对象:
    参看Class.isPrimitive方法的帮助
    Int.class == Integer.TYPE
     boolean isPrimitive()
              判定指定的 Class 对象是否表示一个基本类型。

    数组类型的Class实例对象
    Class.isArray()
     boolean isArray()
              判定此 Class 对象是否表示一个数组类。
    总之,只要是在源程序中出现的类型,都有各自的Class实例对象,例如,int[],void…


    反射概念

    反射就是把Java类中的各种成分映射成相应的java类。

    表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们是Field、Method、Contructor、Package等等。 


    Constructor类:代表某个类中的一个构造方法

    Class类中的方法
     Constructor<T> getConstructor(Class<?>... parameterTypes)
              返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。
     Constructor<?>[] getConstructors()
              返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。
    得到某个类所有的构造方法:
    例子:Constructor [] constructors= Class.forName("java.lang.String").getConstructors();
    得到某一个构造方法:
    例子: Constructor constructor = Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);  //获得方法时要用到类型
                 Constructor con = Class.forName("java.lang.String").getConstructor(
                             int.class );

    java.lang.reflect.Constructor类中的方法

    创建实例对象:
    通常方式:String str = new String(new StringBuffer("abc"));
    反射方式: String str = (String)constructor.newInstance(new StringBuffer("abc"));  //调用获得的方法时要用到上面相同类型的实例对象
    T newInstance(Object... initargs)
              使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
                String str = (String) con.newInstance( new StringBuffer("qwrt" ));

    Class.newInstance()方法:
     T newInstance()
              创建此 Class 对象所表示的类的一个新实例。
    例子:String obj = (String)Class.forName("java.lang.String").newInstance();
    该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
    该方法内部的具体代码是怎样写的呢?用到了缓存机制来保存默认构造方法的实例对象。



    Field类:代表某个类中的一个成员变量

    Class类中的方法
     Field getField(String name)
              返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定[公共public]成员字段。
     Field[] getFields()
              返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。
    得到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量?类只有一个,而该类的实例对象有多个,如果是与对象关联,哪关联的是哪个对象呢?所以字段fieldX 代表的是x的定义,而不是具体的x变量。
    Field y = fi.getClass().getField( "a" );
    java.lang.reflect.Field中的方法
    Object get(Object obj)
              返回指定对象上此 Field 表示的字段的值。
    System. out .println(fa.get(fi));

    访问非公有成员
     Field getDeclaredField(String name)
              返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。
     Field[] getDeclaredFields()
              返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。

     void setAccessible(boolean flag)
              将此对象的 accessible 标志设置为指示的布尔值。

                Fie fi =  new  Fie(2, 3);
                Field fa = fi.getClass().getDeclaredField(  "a" );
                fa.setAccessible(  true );
                System.  out .println(fa.get(fi));

    高级用法:问题:将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的"b"改成"a"。
     void set(Object obj, Object value)
              将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
    public  class  Ts0 {

            public  static  void  main(String[] args)  throws  Exception {
                Text text =  new  Text();
                Field[] fields = text.getClass().getFields();
                  for  (Field field : fields) {
                        // if(field.getType().equals(String.class)){
                        if  (field.getType() == String. class ) {
                            String oldVar = (String) field.get( text);
                            String newVar = oldVar.replace(  'a' ,  '1'  );
                            field.set( text, newVar);
                      }
                }
                System.  out .println(text);
          }

    }

    class  Text {
            public  String  a  =  "jfaiweonv" ;
            public  String  b  =  "fewiojavo" ;
            public  String  c  =  "fejwaofkpwel" ;

            @Override
            public  String toString() {
                  return  a  +  ":"  +  b  +  ":"  +  c  ;
          }
    }


    Method类代表某个类中的一个成员方法

    Class类中的方法
     Method getMethod(String name, Class<?>... parameterTypes)
              返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
     Method[] getMethods()
              返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。
    Method method = Class. forName(  "java.lang.String"  ).getMethod(  "substring" int  .  class int  .  class );
    java.lang.reflect.Method类中的方法
    Object invoke(Object obj, Object... args)
              对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
    参数:
    obj - 从中调用底层方法的对象     args - 用于方法调用的参数
    如果底层方法是静态的,那么可以忽略指定的 obj 参数。该参数可以为 null。
    如果底层方法 所需的形参数为 0,则所提供的  args 数组长度可以为 0 或 null。
                Method method = Class.forName(  "java.lang.String" ).getMethod(  "substring"   int  .  class int  . class  );
                String a =(String) method.invoke(  new  String( "abcdefg"  ), 1,3);
                System.  out  .println(a);

    用反射方式执行某个类中的main方法
    写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。用普通方式调完后,大家要明白为什么要用反射方式去调啊?
                 Class clazz = Class.forName(args[0]);
                Method method = clazz.getMethod( "main" , String[].  class );
                method.invoke(  null , (Object)  new  String[] {  "123" ,  "456" ,  "789"  });

    数组的反射

    具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象(此处比较与值无关)。
    如:int [][] a = new int [1][3];   int [][] b = new int [1][3];   a.getclass()==b.getclass
    代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
    基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。
    Arrays.asList()方法处理int[]和String[]时的差异。
                  int [] a = { 1, 2, 3, 4, 5 };
                String[] b = {  "a" ,  "b"  ,  "c"  ,  "d"  ,  "e"  };
                System.  out .println(Arrays.asList(a));  //[[I@34780af5]
                System.  out .println(Arrays.asList(b));  //[a, b, c, d, e]

    java.lang.reflect.Array:工具类用于完成对数组的反射操作。
    static Object get(Object array, int index)
              返回指定数组对象中索引组件的值。
    static int getLength(Object array)
              以 int 形式返回指定数组对象的长度。
    static void set(Object array, int index, Object value)
              将指定数组对象中索引组件的值设置为指定的新值。

                String[] b = {  "a" ,  "b" ,  "c"  ,  "d"  ,  "e"  };
                  if  (b.getClass().isArray()) {
                      System.  out .println(Array.getLength( b));
                      Array. set(b , 1,  "1"  );
                      System.  out .println(Arrays.asList( b));
                }

    反射的作用:实现框架功能

    什么是框架,例如,我们要写程序扫描.java文件中的注解,要解决哪些问题:读取每一样,在每一个中查找@,找到的@再去查询一个列表,如果@后的内容出现在了列表中,就说明这是一个我能处理和想处理的注解,否则,就说明它不是一个注解或者说至少不是一个我感兴趣和能处理的注解。接着就编写处理这个注解的相关代码。现在sun提供了一个apt框架,它会完成所有前期工作,只需要我们提供能够处理的注解列表,以及处理这些注解的代码。Apt框找到我们感兴趣的注解后通知或调用我们的处理代码去处理。
    你做的门调用锁,锁是工具,你做的门被房子调用,房子是框架,房子和锁都是别人提供的。


    import  java.io.InputStream;
    import  java.util.Collection;
    import  java.util.Properties;

    public  class  Ts {
            public  static  void  main(String[] args)  throws  Exception {
                  // 应该先直接用ArrayList和HashSet,然后才引入从配置文件读,这样便于学员学习。
                Properties props =  new  Properties();
                 // 先演示相对路径的问题
                    完整路径:Jsp中
                                  /*getRealPath();//金山词霸/内部          !一定要记住用完整的路径,但完整的路径不是硬编码,而是运算出来的。*/

                    相对路径:
    Class类也提供getResourceAsStream
    ClassLoader getClassLoader()
              返回该类的类加载器。
     InputStream getResourceAsStream(String name)
              查找具有给定名称的资源。
                  // InputStream ips = new FileInputStream("config.properties");
                  /*
                 * 一个类加载器能加载.class文件,那它当然也能加载 classpath环境下的其他文件,既然它有如此能力,它没有理由不顺带提供这样一个方法。
                 * 它也只能加载 classpath环境下的那些文件。注意:直接使用类加载器时,不能以/打头。
                 */
                  // InputStream ips =
                  // ReflectTest2.class.getClassLoader().getResourceAsStream("cn/ itcast/javaenhance /config.properties");
                   IDE会把src中所有的非.java的文件自动搬到bin中,所以相对路径没问题,推荐使用
                  // Class提供了一个便利方法,用加载当前类的那个类加载器去加载相同包目录下的文件
                  // InputStream ips =
                  // ReflectTest2.class.getResourceAsStream("config.properties");
                InputStream ips = Ts. class .getResourceAsStream( "temp.properties"  );
                props.load(ips);
                ips.close();

                String className = props.getProperty(  "className" );
                 Class clazz = Class.forName(className);

                 Collection collection = (Collection) clazz.newInstance();
                  // Collection collection = new ArrayList();
                 collection.add(1);
                System.  out .println(collection.size());
          }
    }








      ASP.Net+Android+IO开发 .Net培训 、期待与您交流!

  • 相关阅读:
    Spring-----<context:annotation-config/>
    Spring-----代码中使用注入的Properties配置属性
    Spring-----事务配置的五种方式
    读书汇总贴
    读书_2019年
    有道词典_每日一句_2019/08
    微信小程序 报错Failed to load image
    有道词典_每日一句_2019/07
    微信小程序 base64格式图片的显示及保存
    Mac版微信无法安装之始末
  • 原文地址:https://www.cnblogs.com/jiangu66/p/3184833.html
Copyright © 2020-2023  润新知