zoukankan      html  css  js  c++  java
  • 反射

    第一讲:透彻分析反射的基础_Class类

    一,反射的基石——Class类的了解:

      1. 此类的由来:Java程序中java类属于同一类事物,描述这一类同一事物的类就是Class类。
      2. 类的定义:public final class Class<T>extends Object implements Serializable, GenericDeclaration, Type, AnnotatedElement    ==在包java.lang中==
      3. Class类的作用:通过Class类可以得到一个类的方方面面的信息,例如:父类,方法,成员,实现的接口。
      4. 当使用一个类的时候,java虚拟机将类的字节码文件加载到内存中,在内存中的字节码文件,即是这个类的Class对象的内容。

    二,得到字节码的三种方式:

      1. 类名.class          例:System.class                 ==编译阶段识别==
      2. 对象.getClass()  例: new Date().getClass();       ====此方法在Object类中定义===
      3. Class.forName() 例:Class.forName("java.util.Date");           ===注意:此处必须明确指出类名,路径名===

    三,八个基本数据类型都有对应的Class对象,并且与其包装类所对应的Class对象不同。void 也有对应的Class对象。

    四,代码练习:

     

     1 public class ReflectTest {
     2                 public static void main(String[] args) throws ClassNotFoundException {
     3                     
     4                             //创建一个String对象
     5                             String str = "abc";
     6                             
     7                             //通过三个方式获得String的Class对象。
     8                             Class c1 = str.getClass();
     9                             Class c2 = String.class;
    10                             Class c3 = Class.forName("java.lang.String");
    11                             
    12                             //判断是否是同一份字节码。
    13                             System.out.println(c1==c2);
    14                             System.out.println(c2==c3);
    15                             
    16                             //是否是基本类型
    17                             System.out.println(c1.isPrimitive());
    18                             
    19                             //基本类型和包装类型的字节码文件是不同的
    20                             System.out.println(int.class==Integer.class);
    21                             
    22                             //可以通过包装类型获得其包装的基本类型的字节码
    23                             System.out.println(int.class==Integer.TYPE);
    24                             
    25                             //判断数字是否是一个原始类型
    26                             System.out.println(int[].class.isPrimitive());
    27                             
    28                             //判断一个字节码是否是数组类型
    29                             System.out.println(int[].class.isArray());
    30                             
    31                 }
    32 }

     

    第二讲:理解反射的概念

     

    一,反射:把java类中的各种成分映射成响应的java类。

      1. 概念:Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类中的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
      2. 作用:反射技术可以对类进行解剖。大大提高了程序的扩展性。

    二,Class类中获取信息的方法:

      1. public ClassLoader  getClassLoader()    返回该类的类加载器。
      2. public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException,   SecurityException   返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。
      3. public Constructor <?>[] getConstructors() throws SecurityException    返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class
        对象所表示的类的所有公共构造方法。
      4. public Constructor<?>[] getDeclaredConstructors() throws SecurityException  返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法。
      5. public Field getField(String name)  throws NoSuchFieldException,   SecurityException    返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。
      6. public Method[] getMethods()  throws SecurityException       返回一个包含某些 Method 对象的数组

    第三讲:构造方法的反射应用

    一,Constructor类的了解:

      1. public final class Constructor<T>extends AccessibleObject  implements GenericDeclaration, Member   Constructor 提供关于类的单个构造方法的信息以及对它的访问权限。
      2. public Class<T> getDeclaringClass()     返回 Class 对象,该对象表示声明由此 Constructor 对象表示的构造方法的类。
      3. public int getModifiers()          以整数形式返回此 Constructor 对象所表示构造方法的 Java 语言修饰符。
      4. public String  getName()         以字符串形式返回此构造方法的名称。它总是与构造方法的声明类的简单名称相同。
      5. public Class <?>[] getParameterTypes()       按照声明顺序返回一组 Class 对象,这些对象表示此 Constructor对象所表示构造方法的形参类型。如果底层构造方法不带任何参数,则返回一个长度为 0 的数组。
      6. public boolean isVarArgs()           如果声明此构造方法可以带可变数量的参数,则返回 true;否则返回 false
      7. public T newInstance(Object... initargs)  throws InstantiationException,   IllegalAccessException,  IllegalArgumentException,  InvocationTargetException         使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。

    二,明确程序的,编译时和运行时:

      1. 编译时:在程序的编译期间起作用,简单的作一些翻译工作。检查有没有粗心写错关键字,词法分析,语法分析之类的过程。
      2. 运行时:就是代码跑起来了.被装载到内存中去了。在内存中做些操作,做些判断。

    三,创建一个对象的三种方式:

      1. 通过new 关键字和构造方法进行实例化。例:  String s = new String("abc");
      2. 通过Class.newInstance ,进行实例化。此方法调用类中的无参构造方法。        例:  String s = String.class.newInstance();        ==注:这种方式只会使用无参构造方法===
      3. 通过类的构造方法的 Constructor 对象的 newInstance(Object... initargs)   方法进行实例化       例: Constructor c = String.class.getConstructro(null);     String s = c.newInstance(null);          ==注:此种方式可以使用特定构造方法===

    四,代码练习:

     

     1 import java.lang.reflect.*;
     2 
     3 
     4 public class Reflect_Method {
     5                 public static void main(String[] args) throws Exception {
     6                             
     7                             //获取一个Class对象的指定构造方法
     8                             Constructor m = String.class.getConstructor(StringBuffer.class);
     9                             
    10                             // 通过方法对象,创建类的对象
    11                             String s = (String) m.newInstance(new StringBuffer("abc"));
    12                             
    13                             //通过Class对象创建类对象
    14                             String s2 = String.class.newInstance();
    15                             
    16                             s2="a";
    17                             
    18                             //直接实例化
    19                             String s3 = new String("abc");
    20                             
    21                             
    22                             System.out.println(s.charAt(2));
    23                             
    24                             System.out.println(s2);
    25                             
    26                             System.out.println(s3);
    27                 }
    28 }

     

    第四讲,第五讲:成员变量的反射,成员变量反射的综合案例

    一,Field 类的了解:

      1. public final class Fieldextends AccessibleObject  implements Member      Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。
      2. 方法:public Object get(Object obj) throws IllegalArgumentException, IllegalAccessException   返回指定对象上此 Field 表示的字段的值。如果该值是一个基本类型值,则自动将其包装在一个对象中。
      3. 方法:public boolean getBoolean(Object obj) throws IllegalArgumentException, IllegalAccessException    获取一个静态或实例 boolean 字段的值。====此方法根据多种数据类型有多个重载方法。====
      4. 方法:public int getModifiers()        以整数形式返回由此 Field 对象表示的字段的 Java 语言修饰符。应该使用 Modifier 类对这些修饰符进行解码。
      5. 方法:public String getName()         返回此 Field 对象表示的字段的名称。
      6. 方法:public void setBoolean(Object obj,boolean z) throws IllegalArgumentException, IllegalAccessException    将字段的值设置为指定对象上的一个 boolean 值。。====此方法根据多种数据类型有多个重载方法。====

    =========Field对象表示的不是对象的成员,而是类的成员==========

    二,代码练习:

     

     1 import java.lang.reflect.*;
     2 
     3 
     4 //定义用来反射操作的类
     5 class ReflectPoint {
     6             private int x;
     7             public int y;
     8             
     9             public String str1 = "ball";
    10             public String str2 = "basketball";
    11             public String str3 = "itcase";
    12             
    13             public ReflectPoint(int x, int y) {
    14                 this.x = x;
    15                 this.y = y;
    16             }
    17             
    18             public String toString(){
    19                 return str1+"::"+str2+"::"+str3;
    20             }
    21 }
     1 import java.util.Arrays;
     2 
     3 
     4 public class Reflect_Array {
     5             public static void main(String[] args) {
     6                         
     7                         //分别定义不同维数,不同类型的数组
     8                         int[] a1 = new int[]{1,2,3};
     9                         int[] a2 = new int[5];
    10                         int[][] a3 = new int[2][4];
    11                         String[] a4 = new String[]{"a","b","c"};
    12                         
    13                         //具有相同维数相同类型的数组是共享一个字节码对象的
    14                         System.out.println(a1.getClass() == a2.getClass());
    15                         
    16                         //下面这句话编译期出错
    17                         //System.out.println(a1.getClass() == a4.getClass());
    18                         
    19                         //获取数组的字节码名字即类名
    20                         System.out.println(a1.getClass().getName());
    21                         System.out.println(a4.getClass().getName());
    22                         
    23                         //获取数组的父类名字
    24                         System.out.println(a4.getClass().getSuperclass().getName());
    25                         
    26                         
    27                         //数组是Object的子类,向上转型
    28                         Object obj = a1;
    29                         Object obj2 = a3;
    30                         
    31                         //Object[] objs = a1;
    32                         
    33                         //引用类型数组,可以向父类型数组转型
    34                         Object[] objs2 = a4;
    35                         Object[] objs3 = a3;
    36                         
    37                         System.out.println(obj.getClass().getName());
    38                         
    39                         System.out.println(obj2.getClass().getName());
    40                         
    41                         System.out.println(objs2.getClass().getName());
    42                         
    43                         //使用Arrays操作数组
    44                         System.out.println(Arrays.asList(a4));
    45             }
    46 }

    第六讲:成员方法的反射

    一,Method类的了解:

      1. public final class Methodextends AccessibleObject implements GenericDeclaration, Member   Method 提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。
      2. 方法:public int getModifiers()      以整数形式返回此 Method 对象所表示方法的 Java 语言修饰符。应该使用 Modifier 类对修饰符进行解码。
      3. 方法:public String getName()       以 String 形式返回此 Method 对象表示的方法名称。
      4. 方法:public Class<?>[] getParameterTypes()      按照声明顺序返回 Class 对象的数组,这些对象描述了此 Method 对象所表示的方法的形参类型。如果底层方法不带参数,则返回长度为 0 的数组。
      5. 方法:public Class<?> getReturnType()          返回一个 Class 对象,该对象描述了此 Method 对象所表示的方法的正式返回类型。
      6. 方法:public boolean isBridge()          如果此方法是 bridge 方法,则返回 true;否则,返回 false
      7. 方法:public boolean isVarArgs()       如果将此方法声明为带有可变数量的参数,则返回 true;否则,返回 false
      8. 方法:public Object invoke(Object obj,Object... args) throws IllegalAccessException,IllegalArgumentException, InvocationTargetException   对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。个别参数被自动解包,以便与基本形参相匹配,基本参数和引用参数都随需服从方法调用转换。

     二,jdk1.4与jdk1.5 invoke 方法的区别:

                                        jdk1.4  public Object invoke(Object obj,Object[] args)

                                        jdk1.5  public Object invoke(Object obj,Object... args)   ====注:这是jdk1.5的新特性。可变参数====

    三,专家设计模式:

            专家模式:谁调用这个数据,就是谁在调用它的专家。

              如人关门:

                      调用者:是门调用关的动作,对象是门,因为门知道如何执行关的动作,通过门轴之类的细节实现。

                      指挥者:是人在指挥门做关的动作,只是给门发出了关的信号,让门执行。

                      总结:变量使用方法,是方法本身知道如何实现执行的过程,也就是“方法对象”调用方法,才执行了方法的每个细节的。

    四,代码练习:

     1 import java.lang.reflect.*;
     2 
     3 public class Reflect_Method {
     4             public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
     5                 
     6                         //定义Method对象,表示String中的charAt方法
     7                         Method m = String.class.getMethod("charAt", int.class);
     8                         
     9                         //实例化String对象
    10                         String str = "abcd";
    11                         
    12                         //通过Method对象调用方法
    13                         System.out.println(m.invoke(str, 1));
    14                         
    15                         //jdk1.4 方法调用
    16                         System.out.println(m.invoke(str, new Object[]{2}));
    17             }
    18 }

    第七讲:对接收数组参数的成员方法进行反射

    一,练习目标:

            写一个程序实现根据用户提供的类名去动态的调用它的main() 方法。

    二,为什么要使用反射:

                在写源程序时,并不知道使用者传入的类名是什么,但是虽然传入的类名不知道,而知道的是这个类中的方法有main这个方法。所以可以通过反射的方式,通过使用者传入的类名(可定义字符串型变量作为传入类名的入口,通过这个变量代表类名),内部通过传入的类名获取其main方法,然后执行相应的内容。

    三,遇到的问题:

             通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中的每个元素对应一个      参数,当把一个字符串数组作为参数传递给invoke方法时,javac会到底按照哪种语法进行处理呢?jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。所以,在给main方法传递参数时,不能使用代码mainMethod.invoke(null,new String[]{“xxx”}),javac只把它当作jdk1.4的语法进行理解,而不把它当作jdk1.5的语法解释,因此会出现参数类型不对的问题。

    四,解决办法:

          mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});

                           mainMethod.invoke(null,(Object)new String[]{"xxx"});

    五,代码练习:

     1 import java.lang.reflect.Method;
     2 
     3 
     4 //定义一个测试类
     5 class Test{
     6     public static void main(String[] args){
     7         for(String arg : args){
     8             System.out.println(arg);
     9         }
    10     }
    11 }
    12 
    13 
    14 //用反射方式根据用户提供的类名,去执行该类中的main方法。
    15 public class PerformedMain{
    16 
    17     public static void main(String[] args) throws Exception {
    18         //普通方式
    19         Test.main(new String[]{"123","456","789"});
    20         System.out.println("-----------------------------");
    21                 
    22         //反射方式
    23         String className=args[0];
    24         Class clazz=Class.forName(className);
    25                 
    26         Method method_Main=clazz.getMethod("main",String[].class);
    27         //方式一:强制转换为超类Object,不用拆包
    28         method_Main.invoke(null, (Object)new String[]{"123","456","789"});
    29         //方式二:将数组打包,编译器拆包后就是一个String[]类型的整体 
    30         method_Main.invoke(null, new Object[]{new String[]{"123","456","789"}});
    31     }
    32 }

    ============此示例用eclipse运行时,需要在Run As——>RunConfigurations——>Arguments——>Program arguments中添加要执行的类名,如:Reflect.Test。========

     

    第八讲:数组与Object的关系及其反射类型

     一,具有相同Class 文件的数组判定  :具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。数组字节码的名字:有[和数组对应类型的缩写,如int[]数组的名称为:[I

    二、Object[]与String[]没有父子关系,Object与String有父子关系,所以new Object[]{“aaa”,”bb”}不能强制转换成new String[]{“aaa”,”bb”}; Object x =“abc”能强制转换成String x =“abc”。

            =======注:所有数组的父类是Object 所以数组类可以转换为Object对象:Object obj = new int[3];   所有引用类型都是Object 的子类。所以引用类型的数组可以转换为Object的数组: Object  obj[] = new String[3];。==========

    三,如何得到某个数组中的某个元素的类型,

            例:

                  int a = new int[3];Object[] obj=new Object[]{”ABC”,1};

            无法得到某个数组的具体类型,只能得到其中某个元素的类型,

            如:

                   Obj[0].getClass().getName()得到的是java.lang.String。

    四,Array工具类用于完成对数组的反射操作。

            Array.getLength(Object obj);//获取数组的长度

            Array.get(Object obj,int x);//获取数组中的元素

    五,代码练习:

     1 import java.util.Arrays;
     2 
     3 
     4 public class Reflect_Array {
     5             public static void main(String[] args) {
     6                         
     7                         //分别定义不同维数,不同类型的数组
     8                         int[] a1 = new int[]{1,2,3};
     9                         int[] a2 = new int[5];
    10                         int[][] a3 = new int[2][4];
    11                         String[] a4 = new String[]{"a","b","c"};
    12                         
    13                         //具有相同维数相同类型的数组是共享一个字节码对象的
    14                         System.out.println(a1.getClass() == a2.getClass());
    15                         
    16                         //下面这句话编译期出错
    17                         //System.out.println(a1.getClass() == a4.getClass());
    18                         
    19                         //获取数组的字节码名字即类名
    20                         System.out.println(a1.getClass().getName());
    21                         System.out.println(a4.getClass().getName());
    22                         
    23                         //获取数组的父类名字
    24                         System.out.println(a4.getClass().getSuperclass().getName());
    25                         
    26                         
    27                         //数组是Object的子类,向上转型
    28                         Object obj = a1;
    29                         Object obj2 = a3;
    30                         
    31                         //下面这个表达式不成立编译期出错
    32                         //Object[] objs = a1;
    33                         
    34                         //引用类型数组,可以向父类型数组转型
    35                         Object[] objs2 = a4;
    36                         Object[] objs3 = a3;
    37                         
    38                         System.out.println(obj.getClass().getName());
    39                         
    40                         System.out.println(obj2.getClass().getName());
    41                         
    42                         System.out.println(objs2.getClass().getName());
    43                         
    44                         //使用Arrays操作数组
    45                         System.out.println(Arrays.asList(a4));
    46             }
    47 }

    第九讲:数组的反射应用

    一,学习目标:

          通过Array 类实现对数组的反射操作。

    二,Arrays.asList()  方法处理 int[] 和 String[] 的差异:

          因为该方法参数定义为public static <T> List<T> asList(T... a)  ,因为String[] 可以转换为Object[] 所以处理它是按照jdk1.5新特性,可变参数处理的,它作为一个参数传入。而int[] 由于不能转换为Object[] 所以他的处理是按照jdk1.4的规则作为多个参数进行处理,即把数组展开了。

    三,代码练习:

     1 import java.lang.reflect.Array;
     2 
     3 
     4 
     5 public class Reflect_Array2 {
     6                 public static void main(String[] args) {
     7                     
     8                     //创建一个数组作为参数传入
     9                     String[] s = new String[]{"abc","d","feg"};
    10                     
    11                     //普通对象
    12                     String str = "lisi";
    13                     printObject(s);
    14                     printObject(str);
    15                 }
    16                 
    17                 public static void printObject(Object obj){
    18                     if(obj.getClass().isArray()){
    19                         int len = Array.getLength(obj);
    20                         for(int i = 0 ; i<len; i++){
    21                                 System.out.println(Array.get(obj,i));
    22                         }
    23                     }else{
    24                         System.out.println(obj);
    25                     }
    26                 }
    27 }

    第十讲:ArrayList_HashSet的比较及Hashcode分析

    一、哈希算法的由来:

            若在一个集合中查找是否含有某个对象,通常是一个个的去比较,找到后还要进行equals的比较,对象特别多时,效率很低。有这么一种HashCode算法,有一个集合,把这个集合分成若干个区域,每个存进来的对象,可以算出一个hashCode值,根据算出来的值,就放到相应的区域中去。当要查找某一个对象,只要算出这个对象的hashCode值,看属于第几个区域,然后到相应的区域中去寻找,看是否有与此对象相等的对象。这样查找的性能就提高了。

    二、要想HashCode方法有价值的话,前提是对象存入的是hash算法这种类型的集合当中才有价值。如果不存入是hashCode算法的集合中,则不用复写此方法。

    三、如果没有复写hashCode方法,对象的hashCode值是按照内存地址进行计算的。这样即使两个对象的内容是想等的,但是存入集合中的内存地址值不同,导致hashCode值也不同,被存入的区域也不同。所以两个内容相等的对象,就可以存入集合中。

            所以就有这样的说法:如果两个对象equals相等的话,你应该让他们的hashCode也相等。如果对象存入的不是根据hash算法的集合中,就不需要复写hashCode方法。

    四、当一个对象存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则对象修改后的哈希值与最初存储进HashSet集合中的哈希值就不同了。在这种情况下,调用contains方法或者remove方法来寻找或者删除这个对象的引用,就会找不到这个对象。从而导致无法从HashSet集合中单独删除当前对象,从而造成内存泄露。

    五,代码练习:

     1 import java.util.*;
     2 
     3 public class HashCodeDemo {
     4             public static void main(String[] args) {
     5                 
     6                     HashCodeTest h1 = new HashCodeTest(4,4);
     7                     HashCodeTest h2 = new HashCodeTest(4,4);
     8                     HashCodeTest h3 = new HashCodeTest(5,5);
     9                 
    10                     Collection  collections = new HashSet();
    11                     
    12                     collections.add(h1);
    13                     collections.add(h2);
    14                     collections.add(h3);
    15                     System.out.println(collections.size());
    16             } 
    17 }
    18 
    19 class HashCodeTest{
    20         private int x;
    21         private int y;
    22         
    23         
    24         public HashCodeTest(int x, int y) {
    25             this.x = x;
    26             this.y = y;
    27         }
    28 
    29 
    30         @Override
    31         public int hashCode() {
    32             final int prime = 31;
    33             int result = 1;
    34             result = prime * result + x;
    35             result = prime * result + y;
    36             return result;
    37         }
    38 
    39 
    40         @Override
    41         public boolean equals(Object obj) {
    42             if (this == obj)
    43                 return true;
    44             if (obj == null)
    45                 return false;
    46             if (getClass() != obj.getClass())
    47                 return false;
    48             HashCodeTest other = (HashCodeTest) obj;
    49             if (x != other.x)
    50                 return false;
    51             if (y != other.y)
    52                 return false;
    53             return true;
    54         }
    55         
    56         
    57 }

     

     
  • 相关阅读:
    [Wix] 搞了这么久才知道Wix怎么装
    [Wix] Wix Library Tool : lit.exe
    [Wix] Wix代码生成器:tallow
    [Wix] 不同的产品用了一同ID
    [Wix] 安装时建Services, 用户, 虚拟目录....
    [Wix] 添加自定义Action
    [Wix] 有IIS虚拟目录的程序安装(XP or Win2003)
    [wix]ExampleCPlusPlusCustomAction
    几个OpenSource的源代码管理软件
    Bug管理
  • 原文地址:https://www.cnblogs.com/xiaochongbojue/p/4067697.html
Copyright © 2011-2022 走看看