zoukankan      html  css  js  c++  java
  • Java高新技术2(反射)

    1.Class类概述:

    /*
    Java程序中的各个Java类属于同一类事物,描述这类事物的Java
    类名就是Class
     对比提问,众多的人用一个什么类表示?众多的Java类用一个什么类表示?
       人->Person
       .class->Class
    Class类代表Java类,它的各个实例对象又分别对应什么呢?
     1.对应各个类在内存中的字节码,例如:Person类的字节码,ArrayList类的字节码等
     2.一个类被加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码
       不同的类,字节码是不同的,这一个个的空间可以用一个个的对象来表示,这些对象显然具有相同的类型
       这个类型就是Class
     */

    2.获取字节码对象:

    /*
     如何得到各个字节码对应的实例对象(Class类型)
       1.类名.class,例如:System.class
       2.对象.getClass,例如new Date().getClass();
          返回:表示此 对象运行时 的Class对象
       3.Class.forName("java.lang.String"):
         运行时,可以接收字符串变量
         作用:
         返回字节码,如果JVM已加载该字节码,直接返回
         如果没有,则用类加载器将硬盘中的字节码加载到虚拟机中
    
    基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)
    和关键字 void 也表示为 Class 对象(Class cls=void.class)。 
    */
    package com.itheima.day1;
    
    public class ReflectTest {
    
        /**
         * @param args
         */
        public static void main(String[] args)throws Exception {
            // TODO 自动生成的方法存根
        /*验证通过三种方式获取到的字节码对象是否是同一个*/ 
        Class cls1=String.class;
        Class cls2="abc".getClass();
        Class cls3=Class.forName("java.lang.String");//此方法上声明有异常
        System.out.println("①"+(cls1==cls2));//true
        System.out.println("②"+(cls2==cls3));//true
        /*
         以上结果说明JVM中只会保存一份同样的字节码
         以后再用到还是该字节码
         */
    //     isPrimitive():
    //        判定指定的 Class 对象是否表示一个基本类型。 
    //    有九种预定义的 Class 对象,表示八个基本类型和 void。
    //   这些类对象由 Java 虚拟机创建,与其表示的基本类型同名
    //   
        System.out.println("③"+cls1.isPrimitive());//false
        System.out.println("④"+int.class.isPrimitive());//ture
        System.out.println("⑤"+Integer.TYPE.isPrimitive());//ture
        System.out.println("⑥"+Integer.class.isPrimitive());//false
        
        //public boolean isArray()
       //判定此 Class 对象是否表示一个数组类。
        System.out.println("⑦"+int[].class.isArray());//true
        /*
         总之,只要是在源程序中出现的类型,都有各自的Class实例对象:例如:int[],void..
         */
       
      }
    
    }

    3.构造方法反射:

    package com.itheima.day1;
    /*
     反射就是把 Java类中的各种成分 映射成  相应的Java类.
     Java类中有什么?(类所属的包,类中的字段,类中的方法)
      Package getPackage() 
              获取此类的包。 
      Method getMethod(String name, Class<?>... parameterTypes) 
              返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。 
      Field getField(String name) 
              返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。
      每个返回值都是一个对象,都映射成相应的类(相当于对java中所有类中的成分向上抽取描述),对上面那句话的理解 
     学习反射的目的:
       一个类中的 每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些
       实例对象后,如何用?怎么用?这才是学习和应用反射的关键
     */
    import java.lang.reflect.*;
    public class ReflectConstructor {
    
        /**
         * @param args
         */
        public static void main(String[] args)throws Exception {
            // TODO 自动生成的方法存根
         /*
          public Constructor<T> getConstructor(Class<?>... parameterTypes)
                                  throws NoSuchMethodException,
                                         SecurityException
                                         返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。
                    parameterTypes 参数是 Class 对象的一个数组,
                                         这些 Class 对象按声明顺序标识构造方法的形参类型。 
          */
          //new String(new StringBuffer("abc"));
          Constructor strCons=String.class.getConstructor(StringBuffer.class);//对应String类中形参类型为StringBuffer的构造器对象
          Constructor strBufCon=StringBuffer.class.getConstructor(String.class);//对应StringBuffered类中形参类型为String的构造器对象
          /*
           public T newInstance(Object... initargs)//如果传入基本类型->自动装箱
                  throws InstantiationException,
                         IllegalAccessException,
                         IllegalArgumentException,
                         InvocationTargetException
                         使用此 Constructor 对象表示的构造方法来   创建该构造方法的声明类 的新实例,
                         并用指定的初始化参数初始化该实例。个别参数会自动解包,以匹配基本形参,
                         必要时,基本参数和引用参数都要进行方法调用转换。 
           */
          String str=(String)strCons.newInstance(strBufCon.newInstance("abc"));//这里没有使用泛型,编译时期不能确定返回类型,返回Object                                                      //如果传入类型非StirngBuffer类型->IllegalArgumentException
         
          System.out.println(str+" "+str.length());
          System.out.println(String.class.newInstance());//
    使用缓存机制来保存默认构造方法创建的实例对象
                                                      //Class类中的newInstance相当于调用该字节码对象所表示的类的无参构造器.
                                                         //该语句相当于new String();
        }
    
    }

    ReflectConstructor

    4.成员字段反射:

    获取该类中的字段:

    package com.itheima.day1;
    //测试反射用
    public class ReflectPoint {
      private int x;
      int y;
      public int z;
      String str;
        public ReflectPoint(int x, int y, int z) {
            super();
            this.x = x;
            this.y = y;
            this.z = z;
        }
        public ReflectPoint(String str) {
            super();
            this.str = str;
        }
        @Override
        public String toString(){
            return str;
        }
    }
    package com.itheima.day1;
    import java.lang.reflect.Field;
    public class ReflectField {
    
        /**
         * @param args
         */
        public static void main(String[] args)throws Exception{
            // TODO 自动生成的方法存根
          ReflectPoint rp=new ReflectPoint(4,5,6);
         
          
          
          
          /*
           public Field getField(String name)
                   throws NoSuchFieldException,
                          SecurityException返回一个 Field 对象,
                       它反映此 Class 对象所表示的类或接口的指定公共成员字段.(default访问权限也不能,必须public)
            name 参数是一个 String,用于指定所需字段的简称。
                      返回: 由 name 指定的该类的 Field 对象 
           */
          //获取公共字段的值
          Field fieldZ=rp.getClass().getField("z");//ReflectPoint类中的字段z被封装成Field对象
                                                    //注意Field对象中z并没有值
                                                  //仅仅把ReflectPoint的字段z封装了,而字段的值需要对象去初始化的
                                                  //一个类可以有成千上万个对象,每个对象的中同一个字段的值都可能不同
                                                   //因此下面要获取z的值,指出ReflectPoint哪个对象
          System.out.println("z="+fieldZ.get(rp));//6
          
          
          
          //获取私有/默认字段的值
          /*
          public Field getDeclaredField(String name)
                           throws NoSuchFieldException,
                                  SecurityException
                    返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。
          name 参数是一个 String,它指定所需字段的简称。注意,此方法不反映数组类的 length 字段。 
          */
          Field fieldX=rp.getClass().getDeclaredField("x");//注意该方法只要是类中声明过的一定能拿到
                                                           //只要你有,管你暴露不暴露,藏起来我也要弄出来
         
          /*
           即使拿到了该fieldX对象,但是你获取不到字段值,依然是权限问题,这时需要用到一个类: AccessibleObject
         AccessibleObject 类是 Field、Method 和 Constructor 对象的基类。
         它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。
         对于公共成员、默认(打包)访问成员、受保护成员和私有成员,
         
         在分别使用 Field、Method 或 Constructor 对象来设置或获取字段、调用方法,
         或者创建和初始化类的新实例的时候,会执行访问检查。   
         
         public void setAccessible(boolean flag)
                       throws SecurityException
                  将此对象的 accessible 标志设置为指示的布尔值。值为 true 
                  则指示反射的对象在使用时应该取消 Java 语言访问检查。
                  值为 false 则指示反射的对象应该实施 Java 语言访问检查。   
         */
          fieldX.setAccessible(true);//暴力反射,抢~~ - -!
          System.out.println("x="+fieldX.get(rp));//4
          
          Field fieldY=rp.getClass().getDeclaredField("y");
          //fieldY.setAccessible(true);//经测试,default权限可以不用进行标志设置也可以获取到,因为default权限的字段类外也可访问
          System.out.println("y="+fieldY.get(rp)+"
    ");//5
          getField(rp);
          
        }
        
        
        //尝试使用数组的方式获取
        public static void getField(ReflectPoint rp)throws Exception{
             
             //Field[] fieldArr=rp.getClass().getFields();//该数组只存入public字段
             Field[] fieldArr=rp.getClass().getDeclaredFields();
             for(Field f : fieldArr){
                 System.out.println(f);
                 f.setAccessible(true);
                 System.out.println(f.get(rp));
             }
            
        } 
    
    }
    /*
     注意几个异常:
      1.如果对private/default字段使用getField(String)-->Exception in thread "main" java.lang.NoSuchFieldException: x/y
      2.如果未设置此Field对象的accessible(可存取)标志为true,而调用get
        -->Exception in thread "main" java.lang.IllegalAccessException: Class ReflectField can not access a member of class ReflectPoint with modifiers "private"
     */

    ReflectField

    字段反射练习:

    package com.itheima.day1;
    import java.lang.reflect.Field;
    //将任意一个对象中的所有String类型的成员变量所对应的字符串内容中含有"b"改成"a"
    public class ReflectFieldTest {
    
        public static void main(String[] args)throws Exception {
            // TODO Auto-generated method stub
            ReflectPoint rf=new ReflectPoint("bbca");
            setStrValue(rf);
            System.out.println(rf);//aaca
        }
        public static void setStrValue(Object obj)throws Exception{
             
        Field[] fieldArr=obj.getClass().getDeclaredFields();
        
        for(Field f : fieldArr){
          Class type=f.getType();
           
    if(type==String.class){//判断下类型是否为String类型
               f.setAccessible(true);//为了修改所有满足条件的字段
               String value=(String)f.get(obj);
               if(value.contains("b"))
                   f.set(obj, value.replaceAll("b","a"));//会将该字符串中所有是"b"的子串替换为"a"
            }
        }
      }
    }

    5.成员方法的反射:

    package com.itheima.day1;
    import java.lang.reflect.Method;
    public class ReflectMethod {
    
        public static void main(String[] args)throws Exception{
            // TODO 自动生成的方法存根
    //实现通过String对象调用charAt(1); 
        
        /*public Method getMethod(String name,Class<?>... parameterTypes) throws NoSuchMethodException
           SecurityException返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法*/
           Method strMethod=String.class.getMethod("charAt", int.class);
           
           /*
            public Object invoke(Object obj,Object... args)
         throws IllegalAccessException,
                IllegalArgumentException,
                InvocationTargetException
           对带有 指定参数 的 指定对象 调用由此 Method 对象表示的底层方法。
           个别参数被自动解包,以便与基本形参相匹配,基本参数和引用参数都随需服从方法调用转换。 
             如果底层方法是静态的,那么可以忽略指定的 obj 参数,该参数可以为 null。   
            返回: 使用参数 args 在 obj 上指派该对象所表示方法的结果          
            
         */
        System.out.println(strMethod.invoke("abc", 1));//该方法被哪个对象调用,以及传入相应实参
        System.out.println(strMethod.invoke("abc", new Object[]{1}));//在JDK 1.5之前,传入一个数组对象
                                                                      //所以为Object,因为数组中元素类型是不确定的
                                                                      //可以new Object[]{"abc",1}
        /*                                                             
         这里非常别扭:invoke方法是通过Method对象调用
         通俗例子:
           列车司机把列车刹车:列车司机给列车发信号(刹车动作),哥们停车吧,真正让列车停车的是列车自己(内部一系列动作)
           画圆方法定义在圆的内部(Circl.draw(圆心,半径)),关门(Door.close())
         */
      }
    
    }

    ReflectMethod

    练习:通过反射调用某个类的main方法:

    package com.itheima.day1;
    import java.lang.reflect.Method;
    import java.util.Arrays;
    //需求:写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法,由于不知道是哪个类,通过反射解决.
    //该例子的难点:给main方法传入的实参!
    //例如调用ReflectMethod中的main方法
    
    public class ReflectMethodTest {
      public static void main(String[] args)throws Exception{
       invokeMainMethod("com.itheima.day1.TestInvoke");//不要忘了包名(完整类名) 
     }
      public static void invokeMainMethod(String className)throws Exception{
          Class  cls=Class.forName(className);
          Method method=cls.getMethod("main", String[].class);
          
          //第一种解决方案
          method.invoke(null,new Object[]{new String[]{"123","456"}});//以打包/拆包理解
                                                                  //main方法的形参类型为String[]
                                                         //这里如果传入new String[]{"123"}等价于"123"->类型为String
                                                         //这里需要传入一个数组对象:new String[]{"123"},需要再封装为数组
              //new Object[]{new String[]{"123"}},Object[0]指向new String[]{"123"}-->相当于传入了一个实参->new String[]{"123"}
                    //说明当传入new String[]{"123"},可变参数并不会再次封装成一个数组,而是把它当成String数组对待->兼容JDK 1.5以前版本
         
    //第二种解决方案
          method.invoke(null,(Object)new String[]{"123","abc"});//(Object)强转动作告诉编译器->这是一个对象(字符串数组对象),别拆开
      }
    }
    
    
    //调用该类的main方法
    class TestInvoke{
        public static void main(String[] args){
            System.out.println(Arrays.toString(args));
        }
        
    }

    ReflectMain

    6.数组,Object,Object[]之间关系探讨:

    package com.itheima.day1;
    
    import java.util.Arrays;
    
    //数组反射
    /*每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。*/
    public class ReflectArray {
    
        public static void main(String[] args)throws Exception {
            // TODO 自动生成的方法存根
         int[] arr1=new int[3];
         int[] arr2=new int[4];
         int[][] arr3=new int[2][3];
         String[] str=new String[2];
        
         System.out.println(arr1.getClass());//class [I
         System.out.println(arr3.getClass());//class [[I
         System.out.println(str.getClass());//class [java.lang.String
         System.out.println(arr1==arr2);//false
         System.out.println((arr1.getClass()==arr2.getClass())+"
    ");//同一个字节码对象:class [I
         
         //通过forName获取字节码对象
         System.out.println(Class.forName("[[I")+"
    "); 
         
         //查看数组字节码文件对象所表示的类的 超类的字节码对象
         System.out.println(arr1.getClass().getSuperclass());
         System.out.println(arr3.getClass().getSuperclass()); 
         System.out.println(str.getClass().getSuperclass()+"
    ");//均为class java.lang.Object
        
         //Object,Object[],数组之间的转化
         convert(arr1,arr3,str);
        
        }
        
        public static void convert(int[] arr1,int[][] arr3,String[] str){
             Object obj=null;
             Object[] objArr=null;
             
             //一维数组
             obj=arr1;//obj和arr1均指向new int[3]
             System.out.println("arr1->"+arr1+"  obj->"+obj);//但是你不能使用obj[0]取出元素->因为obj类型为Object(指向一个对象)
            
    //objArr=arr1;//
    int[]与Object[] 类型不匹配(arr1数组中元素为int而不是Object或其子类)
             
             //数组的数组
             obj=arr3;//obj和arr3均指向new int[2][3];
             System.out.println("obj->"+obj+"  arr3->"+arr3);
             objArr=arr3;//Object[] objArr=new int[2][3];-->objArr数组中每个元素指向了一个一维数组对象
             System.out.println("objArr->"+objArr+"  arr3->"+arr3+
                                 "  arr3[0]->"+arr3[0]+"  objArr[0]->"+objArr[0]+
                                 "  arr3[1]->"+arr3[1]+"  objArr[1]->"+objArr[1]);
            
             //元素的类型为String
             obj=str;
             System.out.println("obj->"+obj+"  str->"+str);
             objArr=str;//区分上面数组元素为基本类型的objArr=arr1,Object[] objArr=new String[2];
                         //objArr中的每个元素指向一个String对象
             System.out.println("objArr->"+objArr+"  str->"+str+
                                "  str[0]->"+str[0]+"  objArr[0]->"+objArr[0]+
                                "  str[1]->"+str[1]+"  objArr[0]->"+objArr[1]);//str[0]/objArr[0]获取到数组的String类型元素的值(默认值null)
             str[0]="abc";
             System.out.println(str[0].length()+" "+((String)objArr[0]).charAt(1)+"
    ");//Object[] objArr=new String[2],元素类型为Object
            
            
             //最后关于Arrays.asList方法:
             /*
              JDK 1.4: public static List asList(Object[] a)//不能接受收基本类型一维数组(int[])
              JDK 1.5: public public static <T> List<T> asList(T... a)
             */
             arr1[0]=1;
             arr1[1]=2;
             System.out.println(Arrays.asList(arr1)+" "+Arrays.asList(str));//第一个使用:asList(T... a)->传入了一个参数(一个 一维数组对象)->存入集合 
    //第二个使用:asList(Object[] a)->传入new String[2],将a数组中每一个元素(字符串对象)存入集合
                                                                             
        }
    
    }

    数组与Object

    7.数组反射:

    package com.itheima.day1;
    import static java.lang.reflect.Array.getLength;
    import static java.lang.reflect.Array.get;
    
    import java.util.Arrays;
    //数组反射获取数组属性length,数组中元素值
    /*
     public boolean isArray() :判定此 Class 对象是否表示一个数组类。 
     
     public static int getLength(Object array)
                         throws IllegalArgumentException
        以 int 形式返回指定数组对象的长度。 
     
     public static Object get(Object array,
                             int index)
      throws IllegalArgumentException
       ArrayIndexOutOfBoundsException
     返回指定数组对象中索引组件的值。
     如果该值是一个基本类型值,则自动将其包装在一个对象中。  
     */
    public class ReflectArray2 {
       
        public static void main(String[] args){
            // TODO 自动生成的方法存根
            Object obj=null;
            obj=new int[]{3,5};
            //System.out.println(Arrays.toString(obj.getClass().getDeclaredMethods()));
        
            printObj(obj);
            obj="cdef";
            printObj(obj);
            
    
    }
       //一.根据传入对象不同采取不同方式打印
       //传入数组对象->打印数组中的元素,传入非数组对象->打印该对象
        private static void printObj(Object obj) {
            // TODO 自动生成的方法存根
            Class cls=obj.getClass();
            if(cls.isArray()){//true->实参为一个数组对象
              for(int i=0;i<getLength(obj);++i)
                  System.out.println(get(obj,i));
            }
            else
             System.out.println(obj);
        }
      
    }
    /*
     思考:如何得到数组元素的类型?
     回到是:这是不可能的
     int[] arr=new int[3];//这样的数组还可能想办法获取数组元素类型
     Object[] obj=new Object[]{"abc",1,1.3};//数组元素的类型是什么? 不同统一而论
     obj[0].getClass().getName();//也就是说只能获取到某个元素的具体类型
     */

    ReflectArr

  • 相关阅读:
    fork 函数 和vfork 函数的区别
    进程时间
    输出子进程状态
    二维字符串数组字典排序
    括号匹配
    队列实现二叉树层序遍历
    二叉查找树
    分块查找
    JS中的className含义
    Java打印温度转换表
  • 原文地址:https://www.cnblogs.com/yiqiu2324/p/3201602.html
Copyright © 2011-2022 走看看