zoukankan      html  css  js  c++  java
  • 反射(基础知识)

    一、反射的概念

    反射是java语言提供的一项非常吸引人的特性,利用反射可以在运行时对程序进行动态的控制。开发使用反射特性的程序需要使用一套专门的工具类,这些类大部分都位于java.lang.reflect包中。

    反射的操作都是编译之后的操作。

    二、Class类

    Class类属于java.lang包,不需要使用import语句引入特殊的包就可以使用,其对象代表一个类,携带类的相应信息,主要包括构造器、方法、成员变量等。其实,java程序运行过程中,每个类被加载都在内存中产生一个对应的Class类对象,在普通应用程序中这些对象是由系统自动维护的,开发人员不需要关心。

    1、对类和对象的理解

    (1)在面向对象的世界里,万事万物皆对象

    (2)java语言中静态的成员、普通数据类型类不是对象。

    (3)类是对象,类是java.lang.Class类的实例对象。(任何一个类都是Class的实例对象)

    2、任何类都是Class的实例对象,这个实例对象有三种表示方式(可以通过三种方式获取Class类对象

    类类型不可以使用Class类的构造器来创建Class类对象,只能使用其提供的静态工厂方法来获取对象。

    (1)实际上,在告诉我们任何类都有一个隐含的静态成员变量Class,所以可以通过下面方式获取Class类对象 

    Class c1=FOO.class;                         //   FOO是一个类

    (2)已知一个类的实例对象,通过getClass方法获取Class类对象

     1 FOO foo=new FOO();

    2  Class c1=foo.getClass(); 

    注:c1,c2都代表了FOO类的Class类对象,但是一个类只可能是Class类的一个实例对象。

     1 package Reflect;
     2 
     3 public class Test {
     4 
     5     public static void main(String[] args) {
     6         FOO foo=new FOO();
     7         Class c=foo.getClass();
     8         Class c1=FOO.class;
     9         System.out.println(c1==c);
    10     }
    11 }

    运行结果:

    true

    (3)通过Class.forName(String className),className指出类的全称名。,如果找不到,就会抛出ClassNotFoundException异常

    1     try {
    2             Class c3=Class.forName("Reflect.FOO");
    3         } catch (ClassNotFoundException e) {
    4             // TODO Auto-generated catch block
    5             e.printStackTrace();
    6         }
    7     }

    3、Class类的常用方法

    Class类没有构造方法,所以很自然创建Class类对象,不能通过构造器来创建,而只能通过其提供的静态工厂方法来获取对象。Filed、Method、Constructor为java.lang.reflect包的类,它们分别代表了成员变量、方法、构造器,分别携带对应成员变量、方法、构造器信息。package类在java.lang包中,其对象代表一个包,携带相应包的信息。

    Class类的常用方法表

    方法关键字

    含义

    public Method[] getDeclaredMethods()

    获取声明的所有的方法

    public Method getDeclaredMethod("方法名",参数类型.class,……)

    获得特定的方法

    public Constructor[] getDeclaredConstructors()

    获取所有声明的构造方法

    public Constructor[] getConstructors()

    获取所有的构造方法

    public Constructor getDeclaredConstructor(参数类型.class,……)

    获取特定的构造方法
    public Field[] getFields()

    获取所有成员变量

    public  Field[] getDeclaredFields()

    获取所有声明的成员变量

    public  Field getField(String name)

    获取指定的成员变量

     public Package getPackage()

    返回Class对象所对应类所在的包 

    public String getName()

    返回Class对象对应类的名称

    public static Class forName(String className)

    通过此方法获取指定名称的类对应的Class对象,className指出类的全名称

    Public Object newInstance()

    调用默认的无参构造器新建Class对象对应类的一个对象。

    注:标红的两个方法是Class类实现动态加载类的核心方法。

    通过newInstance()方法创建的对象与普通方式创建的对象在使用上完全相同(区别:用于动态加载类后的实例化)。

    代码示例:

     1 package Reflect;
     2 
     3 public interface OfficeAble {
     4 
     5     public void start();
     6 }
     7 
     8 
     9 package Reflect;
    10 
    11 public class Word implements OfficeAble {
    12 
    13     private String name;
    14     private String gender;
    15     public Word() {
    16         System.out.println("word的构造方法1~~");
    17     }
    18     
    19     public Word(String name,String gender) {
    20         this.name=name;
    21         this.gender=gender;
    22         System.out.println("word的构造方法1~~"+name+" "+gender);
    23     }
    24     
    25     @Override
    26     public void start() {
    27         System.out.println("word..start.......");
    28     }
    29     
    30     public void start1() {
    31         System.out.println("word..start1.......");
    32     }
    33 
    34 }
    35 
    36 package Reflect;
    37 
    38 import java.lang.reflect.Constructor;
    39 import java.lang.reflect.Field;
    40 import java.lang.reflect.Method;
    41 
    42 public class OfficeBetter {
    43 
    44     public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
    45         //动态加载类,在运行时刻加载
    46         Class c=Class.forName("Reflect.Word");
    47         //通过类类型,创建该类对象
    48         OfficeAble oa=(OfficeAble) c.newInstance();
    49         oa.start();
    50         
    51         //获取Word类中所有构造函数对象,所有成员变量对象,所有方法对象
    52         Constructor []cs=c.getConstructors();
    53         Field []fd=c.getFields();
    54         Method []md=c.getMethods();
    55         
    56         //遍历构造函数对象,成员变量对象,方法对象,打印他们的信息
    57         for (Method method : md) {
    58             System.out.println(method.getName()+" "+method.getParameterCount());
    59         }
    60 
    61         for (Field field : fd) {
    62             System.out.print(field.getName()+" ");
    63         }
    64         System.out.println(fd.length);
    65         
    66         for (Constructor constructor : cs) {
    67             System.out.println(constructor.getName()+" "+constructor.getParameterCount());
    68         }
    69     }
    70 
    71 }

    运行结果:

     1 word的构造方法1~~
     2 word..start.......
     3 start 0
     4 start1 0
     5 wait 0
     6 wait 2
     7 wait 1
     8 equals 1
     9 toString 0
    10 hashCode 0
    11 getClass 0
    12 notify 0
    13 notifyAll 0
    14 0
    15 Reflect.Word 0
    16 Reflect.Word 2

    分析:仔细观察会发现,Field返回的对象的名称没有打印出来,当成员变量的属性为private时,通过正常的反射获取其成员变量将会失败,但是不会报错,当成员变量的属性改成public时,反射获取其成员变量将会成功。也就是说没有取消访问限制反射机制不能打破面向对象编程中,私有的成员变量封装在类中,不能通过外部直接访问,需要通过该类的方法才能访问。没有取消访问限制反射不能获取的是private修饰的任何信息。

    4、取消访问限制 

    当用Class对象的getDeclaredXXXs()方法获得Field、Method或Constructor时由于访问限制的作用可能某些字段、方法或构造器是不能访问的。如果需要通过反射访问这些不允许访问的元素,则需要首先去除访问限制,然后在访问。

    若希望取出访问限制,则需要使用java.lang.reflect.AccessibleObject类,其中提供了一些用来去除访问限制的方法。

    AccessibleObject类中与访问限制有关的方法

    方法签名 功能说明
    public void setAccessible(boolean flag) 设置AccessibleObject类对象对应的Field、Method或Constructor的访问限制,可以访问为true,不可访问为false
    public void isAccessible() 返回AccessibleObject类对象对应Field、Method或Constructor的访问限制,可以访问为true。不可访问为false
    public static void setAccessible(AccessibleObject[] array,boolean flag) 设置一个数组中所有AccessibleObject对象对应的Field、Method或Constructor的访问标志,可以访问为true,不可访问为false

    代码演示:

     1 package Reflect;
     2 
     3 public interface OfficeAble {
     4 
     5     public void start(String name);
     6 }
     7 
     8 package Reflect;
     9 
    10 public class Word implements OfficeAble {
    11 
    12     private String name;
    13     
    14     @Override
    15     public void start(String name) {
    16         System.out.println("word..start......."+name);
    17     }
    18     private void start1(String name) {
    19         System.out.println("word..start......."+name);
    20     }
    21     private void start2(String name){
    22         System.out.println("word..start......."+name);
    23     }
    24 }
    25 
    26 package Reflect;
    27 
    28 import java.lang.reflect.AccessibleObject;
    29 import java.lang.reflect.Constructor;
    30 import java.lang.reflect.Field;
    31 import java.lang.reflect.InvocationTargetException;
    32 import java.lang.reflect.Method;
    33 import java.lang.reflect.Modifier;
    34 
    35 public class OfficeBetter {
    36 
    37     public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
    38         //动态加载类,在运行时刻加载
    39         Class c=Class.forName("Reflect.Word");
    40         //通过类类型,创建该类对象
    41         OfficeAble oa=(OfficeAble) c.newInstance();
    42         oa.start("女神");
    43         
    44         //获取Word类中所有构造函数对象,所有成员变量对象,所有方法对象
    45    
    46         Field []fd=c.getDeclaredFields();
    47         Method []md=c.getDeclaredMethods();
    48         
    49         //遍历构造函数对象,成员变量对象,方法对象,打印他们的信息
    50       //设置单个成员变量对象访问限制为允许(设置所有方法对象可访问)
    51         AccessibleObject.setAccessible(md, true);
    52        for (Method method : md) {
    53            StringBuffer sb=new StringBuffer();
    54            if((method.getModifiers()&Modifier.PRIVATE)!=0){
    55                sb.append(" private ");
    56            }
    57             System.out.println("修饰符:"+sb);            
    58         }
    59 
    60        //设置单个成员变量对象访问限制为允许(设置单个成员变量对象可访问)
    61        fd[0].setAccessible(true);
    62         System.out.println(fd.length);
    63         for (Field field : fd) {
    64         
    65             field.set(oa, "女神");
    66             System.out.print(field.getName()+" "+field.get(oa));
    67         }                         
    68     }
    69 }

    运行结果:

    1 word的构造方法~~
    2 word..start.......女神
    3 修饰符:
    4 修饰符: private 
    5 修饰符: private 
    6 1
    7 name 女神

    5、数组与Class类

    (1)基本数据类型对应的代号表

    基本类型 代   号 基本类型 代号
    boolean Z int I
    byte B long J
    short S double D
    char C float F

    代码示例:

     1 package Reflect;
     2 
     3 public class Test {
     4 
     5     public static void main(String[] args) {
     6     //创建数组对象
     7         String [] stringArray=new String[4];
     8         int[][] intArray=new int[9][9];
     9         byte []btArray=new byte[2];
    10         //获取数组对象对应的Class类
    11         Class sc=stringArray.getClass();
    12         Class sc1=intArray.getClass();
    13         Class sc2=btArray.getClass();
    14         //打印两个数组对应的类名
    15         System.out.println("一维String数组对应的类名为:"+sc.getName());
    16         System.out.println("二维int数组对应的类名为:"+sc1.getName());
    17         System.out.println("一维byte数组对应的类名为:"+sc2.getName());
    18     }

    运行结果:

     1 一维String数组对应的类名为:[Ljava.lang.String; 2 二维int数组对应的类名为:[[I 3 一维byte数组对应的类名为:[B 

    注:要特别注意的是数组对应的类比较特殊,没有提供可用的构造器,因此无法直接使用Class类提供的静态工厂方法newInstance()来创建。

    (2)利用反射动态的创建数组对象

    由于数组类型比较特殊,没有提供任何可用的构造器,因此不能使用Class类或Constructor类的newInstance方法来创建数组对象。但是这并不意味着不可以使用反射技术动态创建数组,java.lang.reflect.Array类中专门提供了动态创建数组的newInstance()方法。

    Arrays类中的newInstance方法表

    方法签名 功能说明
    pubic static Object newInstance(Class componentType, int length) 生成指定类型的一维数组,compinenType指出数组元素的类型,length指出数组的长度
    public static Object newInstance(Class componentType,int[] dimensions) 生成指定类型的多维数组,componentType指出数组元素的类型,dimensions指出数组每一维的长度

    1)如果数组元素为基本数据类型,则使用"<封装类类名>.TYPE"来指出数组元素的类型。

    2)两个方法返回的都是Object类型,需要进行恰当的强制类型转换以便以后使用

    代码示例:

     1 package Reflect;
     2 
     3 import java.lang.reflect.Array;
     4 
     5 public class Test {
     6 
     7     public static void main(String[] args) {
     8         //使用反射的方式动态的创建一维int行数组
     9         int[] intArray=(int[])Array.newInstance(Integer.TYPE, 5);
    10         //使用反射的方式动态创建二维String型数组
    11         String [][]stringArray=(String[][])Array.newInstance(String.class, new int[]{4,5});
    12        //打印两个数组的长度信息
    13         System.out.println("intArray长度为:"+intArray.length);
    14         System.out.println("stringArray第一维长度为:"+stringArray.length+",第二维长度:"+stringArray[0].length);
    15     }
    16 }

    运行结果:

     1 intArray长度为:5 2 stringArray第一维长度为:4,第二维长度:5 

    三、精确判断对象类型

    1、instanceof无法用来判断对象的精确程度。如果需要判断对象的精确程度就需要使用反射技术。

     1 package Reflect;
     2 
     3 class MyFather{
     4     
     5 }
     6 
     7 class MyClass extends MyFather{
     8     
     9 }
    10 public class Test {
    11 
    12     public static void main(String[] args) throws ClassNotFoundException {
    13         //创建MyClass类对象
    14         MyClass mc=new MyClass();
    15         //用instanceof判断类型
    16         System.out.println("instanceof的判断结果:");
    17         if(mc instanceof MyFather){
    18             System.out.println("对象是MyFather类型的!!");
    19         }else{
    20             System.out.println("对象不是MyFather类型的!");
    21         }
    22         
    23         //用反射精确判断类型
    24         System.out.println("反射判断的结果:");
    25         if(mc.getClass()==Class.forName("Reflect.MyFather")){
    26             System.out.println("对象是MyFather类型的!!");
    27         }else{
    28             System.out.println("对象不是MyFather类型的!");
    29         }
    30     
    31     }
    32 }

    运行结果:

     1 instanceof的判断结果: 2 对象是MyFather类型的!! 3 反射判断的结果: 4 对象不是MyFather类型的! 

    2、Field类

    Field类的对象代表成员变量,携带成员变量的信息。要注意的是,与Class类类似,一样不可以通过构造器创建Field类的对象,其对象都是通过Class类对象提供的get()系列方法获得的。

    Field类的常用发方法表

    方法签名 功能说明
    public String getName() 返回Field对象对应成员变量的名称
    public Class getType() 返回Field对象所对应成员变量的类型
    public Object get(Object obj) 返回指定对象此成员变量的值,obj为指定对象的引用,不管是何种类型的值都将成为Object类型返回
    public xxx getXxx(Object obj)

    返回指定对象此成员变量的值,obj为指定的对象的引用,xxx代表8中基本数据类型当中的一种,如getBoolean返回boolean型值

    public void set(Object obj,Object value) 设置指定对象此成员变量的值,obj为指定对象的引用,value为指定的值,若为基本数据类型的值,则使用对应封装类对象。
    代码示例:
    package Reflect;
    
    public interface OfficeAble {
    
        public void start();
    }
    
    package Reflect;
    
    public class Word implements OfficeAble {
    
        public String name;
        public Word() {
            System.out.println("word的构造方法1~~");
        }
        
        public Word(String name) {
            this.name=name;
            System.out.println("word的构造方法1~~"+name);
        }
        
        @Override
        public void start() {
            System.out.println("word..start.......");
        }
        
        public void start1() {
            System.out.println("word..start1.......");
        }
    
    }
    
    package Reflect;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    
    public class OfficeBetter {
    
        public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
            //动态加载类,在运行时刻加载
            Class c=Class.forName("Reflect.Word");
            //通过类类型,创建该类对象
            OfficeAble oa=(OfficeAble) c.newInstance();
            oa.start();
            
            //获取Word类中所有构造函数对象,所有成员变量对象,所有方法对象
            Field []fd=c.getFields();
            
            //遍历构造函数对象,成员变量对象,方法对象,打印他们的信息
    
            System.out.println(fd.length);
            for (Field field : fd) {
                field.set(oa, "女神");
                System.out.print(field.getName()+" "+field.get(oa));
            }
        }
    
    }

    运行结果:

     1 word的构造方法1~~ 2 word..start....... 3 1 4 name 女神 

    分析:之前代码中就有讲过反射不能获取类中的private修饰的信息,所以Field的set()方法只能对非praivate的对象赋值。

    3、Method类

    Method类的对象代表一个方法,携带方法的相关信息与Field类类似,其对象也不能通过构造器创建,而是要使用Class对象提供的get()系列方法获得。

    Method类的一些常用方法

    方法签名 功能说明

    public Object invoke(Object obj,Object []args)

    此方法用来调用Method对象的方法,返回值为调用方法的返回值
    public String getName() 返回此方法对应的名称
    public Class []getParameterTypes() 返回此方法的参数序列
    public Class getReturnType() 返回此方法的返回类型

    对于invoke()方法有如下几点需要注意

    (1)不管实际对应方法的返回值是什么类型,都作为Object类型返回。若返回值为基本的数据类型,则返回对应封装类的对象。

    (2)obj参数指出要被调用方法所属的对象,若调用静态的方法用null值。

    (3)args指出要被调用方法的参数序列,若方法没有参数则传递空数组--new Object[0],若方法有基本数据类型的参数则使用基本参数类型的封装类对象。

    代码示例:

    package Reflect;
    
    public interface OfficeAble {
    
        public void start(String name);
    }
    
    package Reflect;
    
    public class Word implements OfficeAble {
    
        public String name;
        public Word() {
            System.out.println("word的构造方法1~~");
        }
        
        public Word(String name) {
            this.name=name;
            System.out.println("word的构造方法1~~"+name);
        }
        
        @Override
        public void start(String name) {
            System.out.println("word..start......."+name);
        }
    
    }
    
    package Reflect;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    public class OfficeBetter {
    
        public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
            //动态加载类,在运行时刻加载
            Class c=Class.forName("Reflect.Word");
            //通过类类型,创建该类对象
            OfficeAble oa=(OfficeAble) c.newInstance();
            oa.start("女神");
            
            //获取Word类中所有构造函数对象,所有成员变量对象,所有方法对象
            Method []md=c.getDeclaredMethods();
            
            //遍历构造函数对象,成员变量对象,方法对象,打印他们的信息
            for (Method method : md) {
                System.out.println(method.getName()+" 参数个数:"+method.getParameterCount());
                System.out.println("*************下面是通过invoke方法调用的Method********");
                method.invoke(oa, "小帅");
            }
    
        }
    
    }

    运行结果:

     1 word的构造方法1~~ 2 word..start.......女神 3 start 参数个数:1 4 *************下面是通过invoke方法调用的Method******** 5 word..start.......小帅 

    4、Constructor类

    Constructor类的对象代表了一个构造器,携带构造器的相关信息,与Field、Method类型类似,其对象也不能通过构造器创建,而是要使用Class对象提供的get()系列方法获得。

    Constuctor类的一些常用方法

    方法签名 功能说明
    public Object newInstance(Object []initargs) 此方法用啦调用Constructor对象代表的构造器,创建构造器所属类的对象。initargs为构造器参数数组。注意无论创建的对象是何种类型,此方法的返回值都为Object类型,要适当进行强制类型转换。
    public String getName() 返回构造器的名称
    public Class getParameterTypes() 返回构造器的参数序列

    代码示例:

    package Reflect;
    
    public interface OfficeAble {
    
        public void start(String name);
    }
    
    package Reflect;
    
    public class Word implements OfficeAble {
    
        public String name;
        public Word() {
            System.out.println("word的构造方法~~");
        }
        
        public Word(String name) {
            this.name=name;
            System.out.println("word的构造方法1~~"+name);
        }
        
        @Override
        public void start(String name) {
            System.out.println("word..start......."+name);
        }
    
    }
    
    package Reflect;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    public class OfficeBetter {
    
        public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
            //动态加载类,在运行时刻加载
            Class c=Class.forName("Reflect.Word");
            //通过类类型,创建该类对象
            OfficeAble oa=(OfficeAble) c.newInstance();
            oa.start("女神");
            
            //获取Word类中所有构造函数对象,所有成员变量对象,所有方法对象
            Constructor []cs=c.getConstructors();
            //Field []fd=c.getFields();
            //Method []md=c.getDeclaredMethods();
            
            //遍历构造函数对象,成员变量对象,方法对象,打印他们的信息
          
            //System.out.println();      
            for (Constructor constructor : cs) {
                System.out.println(constructor.getName()+" "+constructor.getParameterCount());
                if(constructor.getParameterCount()!=0){
                    constructor.newInstance("男神");
                }
            }
        }
    }

    运行结果:

     1 word的构造方法1~~ 2 word..start.......女神 3 Reflect.Word 0 4 Reflect.Word 1 5 word的构造方法1~~男神 

    5、反射与修饰符

    有关修饰符的信息,也可以通过反射获得,Class、Fileld、Method、Constructor类都提供了用于获取各自表示的类、成员变量、方法、构造器对应修饰符的方法getModifiers,下面给出了该方法的签名

    public int getModifiers()

    (1)Class、Field、Method、Constructor四个类提供的getModifiers()方法签名功能完全相同

    (2)getModifiers方法返回值为整数,不同的整数代表不同的修饰符组合。(表示不同修饰符的整数都定义在java.lang.reflect.Modifier类中,作为Modifier类的静态常量)

    Modifier类中表示修饰符静态常量

    ABSTRACT
              修饰符 abstract
    FINAL
              修饰符 final
    NATIVE
              修饰符 native
    PRIVATE
              修饰符 private
    PROTECTED
              修饰符 protected
    PUBLIC
              修饰符 public
    STATIC
              修饰符 static
    STRICTFP
              修饰符 strictfp
    SYNCHRONIZED
              修饰符 synchronized
    TRANSIENT
              修饰符 transient
    VOLATILE
              修饰符 volatile

     注:如果同时具有多个修饰符,则getModifiers()方法返回值是几个修饰符的常量和。

    代码示例:(只演示Method类或的Modifier静态常量的解析)

    package Reflect;
    
    public interface OfficeAble {
    
        public void start(String name);
    }
    
    package Reflect;
    
    public class Word implements OfficeAble {
        
        
        @Override
        public void start(String name) {
            System.out.println("word..start......."+name);
        }
        public static void start1(String name) {
            System.out.println("word..start......."+name);
        }
    }
    
    package Reflect;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.lang.reflect.Modifier;
    
    public class OfficeBetter {
    
        public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
            //动态加载类,在运行时刻加载
            Class c=Class.forName("Reflect.Word");
            
            //获取Word类中所有构造函数对象,所有成员变量对象,所有方法对象
           ;
            Method []md=c.getDeclaredMethods();
            
            //遍历构造函数对象,成员变量对象,方法对象,打印他们的信息
           for (Method method : md) {
               StringBuffer sb=new StringBuffer();
               if((method.getModifiers()&Modifier.PUBLIC)!=0){
                   sb.append(" public ");
               }
               if((method.getModifiers()&Modifier.STATIC)!=0){
                   sb.append(" static ");
               }
                System.out.println("修饰符:"+sb);     
            }
        }
    }

    运行结果:

     1 修饰符: public 2 修饰符: public static 

     四、通过Class,Method来认识泛型的本质

    反射的操作都是编译之后的操作:程序员写的java应用程序,在运行之前需要被编译成.class文件,反射的操作就是在程序在编译成.class文件之后进行的。

     1 package Reflect;
     2 
     3 import java.lang.reflect.Array;
     4 import java.lang.reflect.InvocationTargetException;
     5 import java.lang.reflect.Method;
     6 import java.util.ArrayList;
     7 
     8 public class Test {
     9 
    10     public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
    11     //实例化两个ArrayList集合对象
    12     ArrayList<String> list=new ArrayList<String>();
    13     ArrayList list1=new ArrayList();
    14     //同过实例的两个ArrayList集合对象各自的获取Class类对象
    15     Class c=list.getClass();
    16     Class c1=list1.getClass();
    17     //判断两个集合的Class类对象是否相等,若打印true则相等,false则不相等
    18     System.out.println(c==c1);
    19     /*
    20      * c==c1结果返回的为true说明编译之后集合的泛型是去泛型化的
    21      * java中集合的泛型,是为了防止错误输入,只在编译阶段有效,绕过编译就无效了。
    22      * 验证:我们通过方法的反射操作,绕过编译
    23      * (反射操作都是编译后的,所以Method的invoke函数调用ArrayList类对象的add和get方法就可以绕过编译过程了)
    24      */
    25     Method method=c.getMethod("add", Object.class);
    26     method.invoke(list, 1);
    27     Method method1=c.getMethod("get", Integer.TYPE);
    28     System.out.println("list集合中添加数字:"+method1.invoke(list, 0)+" 成功");
    29     }
    30     
    31 }

    运行结果:

     1 true 2 list集合中添加数字:1 成功 

    分析:反射操作都是编译后的,所以Method的invoke函数调用ArrayList类对象的add和get方法就可以绕过编译,所以不报错

     

  • 相关阅读:
    ThinkPHP---框架介绍
    (独孤九剑)--cURL
    浅谈Session与Cookie的区别与联系
    (独孤九剑)--会话管理和控制
    PHP数据乱码
    (独孤九剑)---PHP操作MySQL数据库
    错误宝典
    变量、常量、注释、PEP8开发规范、数据类型、Python2和Python3的区别、用户输入、流程控制语句
    什么是编程语言
    CSS
  • 原文地址:https://www.cnblogs.com/xiaotiaosi/p/6400740.html
Copyright © 2011-2022 走看看