反射

第一讲:透彻分析反射的基础_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 }

 

 
原文地址:https://www.cnblogs.com/xiaochongbojue/p/4067697.html