码迷,mamicode.com
首页 > 编程语言 > 详细

黑马程序员------Java反射学习总结(一)

时间:2015-02-26 16:21:53      阅读:171      评论:0      收藏:0      [点我收藏+]

标签:

-------------------------Java培训、Android培训,期待与您交流!-----------------------------

一、反射的概念

  1) Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类中的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

  2)一句话来讲,反射就是将Java类中的各个成分映射成相应的Java类。

  3)即在Java中,描述事物的各种类同样也是一种事物,也可以用面向对象的方法来描述,即也有一个类来描述众多的Java类。

  4)反射也称为对类的解剖。例如一个类有成员变量、构造方法、成员方法,包等信息,通过反射,就可以拿到这个类的各种信息。

二:Class类

  1)所有的类文件(.class文件)都有共同属性,所以可以向上抽取,把这些共性内容封装成一个类,这个类就叫Class(描述字节码文件的对象)。

  2)Class类描述的信息:类的名字,类的访问属性,类所属于的包名,字段名称列表,方法名称列表等。每一个字节码就是class的实例对象。如:Class clazz = String.class.

  3)Class类是Java中反射的基石。

1、获取Class对象的三种方式:

  1)通过所有数据类型都有的静态属性.class来获取

    类名.class  例如:String.class

  2)通过对象的getClass()方法获取

    对象.getClass()  例如:new String("123").getClass();

  3)通过Class类的静态方法forName()和要获得Class对象的类名来获取

    Class.forName(包名.类名);  例如:Class.forName("java.lang.String");

  代码示例: 

 1 public class ClassTest{
 2   public static void main(String...args)throws Exception{ 
 3     String str = "黑马程序员"; 
 4     Class cls1 =String.class; 
 5     Class cls2 = str.getClass(); 
 6     Class cls3 = Class.forName("java.lang.String"); 
 7     System.out.println(cls1 == cls2);//true 
 8     System.out.println(cls2 == cls3);//true 
 9   } 
10 }

  注:同一个类的字节码文件是唯一的,所以无论怎样获取,都是同一份字节码文件,即同一个Class实例对象

2、Java中预定义的九个Class对象:

  1)  包括八种基本类型(byte、short、int、long、float、double、char、boolean)的字节码对象和一种返回值为void类型的void.class。

  2)  Integer.TYPE是Integer类的一个常量,它代表此包装类型包装的基本类型的字节码,所以和int.class是相等的。基本数据类型的字节码都可以用与之对应的包装类中的TYPE常量表示。

  注意:具有相同数据类型和维数的数组在Java中被映射为同一个Class对象。

3、Class类中的常用方法:

   1)  static Class forName(String className)  返回与给定字符串名的类或接口的相关联的Class对象。

        2)  Class getClass()  返回的是Object运行时的类,即返回Class对象即字节码对象

        3)  Constructor getConstructor()   返回Constructor对象,它反映此Class对象所表示的类的指定公共构造方法。

        4)  Field getField(String name)   返回一个Field对象,它表示此Class对象所代表的类或接口的指定公共成员字段。

        5)  Field[] getFields()   返回包含某些Field对象的数组,表示所代表类中的成员字段。

        6)  Method getMethod(String name,Class… parameterTypes)  返回一个Method对象,它表示的是此Class对象所代表的类的指定公共成员方法。

        7)  Method[] getMehtods()  返回一个包含某些Method对象的数组,是所代表的的类中的公共成员方法。

        8)  String getName()  以String形式返回此Class对象所表示的实体名称。

        9)  String getSuperclass()  返回此Class所表示的类的超类的名称

        10) boolean isArray()  判定此Class对象是否表示一个数组

        11) boolean isPrimitive()  判断指定的Class对象是否是一个基本类型。

        12) T newInstance()  创建此Class对象所表示的类的一个新实例。注意:此方法会使用该的空参数构造函数进行初始化实例对象。 

三:Constructor类

1、概述:Constructor类的实例对象代表类的一个构造方法。

2、获取构造方法:

        1)得到这个类的所有构造方法:如得到String类的所有构造方法

              Constructor[] cons = Class.forName(“java.lang.String”).getConstructors();

        2)获取某一个构造方法:

              Constructor con=String.class.getConstructor(StringBuffer.class);

3、创建实例对象:

        1)通常方式:String str = new String("123");

         2)反射方式:String str = (String)con.newInstance("123");

注:

        1、创建实例时newInstance方法中的参数列表必须与获取Constructor的方法getConstructor方法中的参数列表一致。

        2、newInstance():构造出一个实例对象,每调用一次就构造一个对象。

        3、利用Constructor类来创建类实例的好处是可以指定构造函数,而Class类只能利用无参构造函数创建类实例对象。

代码示例:

 1 package com.itheima.day01; 
 2 import java.lang.reflect.Constructor; 
 3 public class ReflectTest { 
 4     public static void main(String[] args) throws Exception { 
 5         //获取String类的Class对象
 6         Class clszz = Class.forName("java.lang.String"); 
 7         //获取String类Class对象的构造方法
 8         Constructor constructor =  clszz.getConstructor(StringBuffer.class); 
 9         //通过此构造方法获取String类的实例对象
10         String str = (String) constructor.newInstance(new StringBuffer("黑马程序员")); 
11     } 
12 } 

四、Field类

1、Field类代表反射某个类中的成员变量。

2、方法

  1)  Field getField(String s);//只能获取公有和父类中公有

  2)  Field getDeclaredField(String s);//获取该类中任意成员变量,包括私有

  3)  setAccessible(ture);//如果是私有字段,要先将该私有字段进行取消权限检查的能力。也称暴力访问。

  4)  set(Object obj, Object value);//将指定对象变量上此Field对象表示的字段设置为指定的新值。

  5)  Object get(Object obj);//返回指定对象上Field表示的字段的值。

示例:

 1 package cn.itheima;
 2   public class Person {
 3     private String name;
 4     public int age;
 5 
 6     Person(){}
 7 
 8     Person(String name,int age){
 9       this.name = name;
10       this.age = age;
11     }
12 
13     public String toString(){
14       return name+"::"+age;
15     }
16   }
17 
18 //获取Person对象的成员变量
19 public static void getPersonField() throws Exception{    
20 //如果想要给该变量赋值,必须先要有对象。
21     Class clazz=Class.forName("cn.itheima.Person");
22     Person p=(Person)clazz.newInstance();
23         
24     //获取所有的成员变量
25     Field[] fs=clazz.getFields();
26     for(Field f:fs){
27         System.out.println(f);
28     }
29         
30     //获取指定的成员变量
31     Field fage=clazz.getField("age");
32     Field fname=clazz.getDeclaredField("name");
33         
34     //显示改变后的值
35     fage.set(p, 20);
36     System.out.println(fage.get(p));
37         
38     //暴力访问私有变量
39     fname.setAccessible(true);
40     fname.set(p, "zhangsan");
41     System.out.println(fname.get(p));
42 }

  注意:暴力反射的方式,也就是使用setAccessible(true)使private类型的成员变量也可以被获取值。

五:Method类

1、概念:

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

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

如人画圆:

        调用者:是圆调用画的动作,对象是圆,因为圆知道如何执行画的动作,通过圆心和半径等之类的细节实现。

        指挥者:是人在指挥圆做画的动作,只是给圆发出了画自己的信号,让圆去执行。

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

   一种程序设计思想,即设计类时,应把此类有关改变自身成员变量的动作设计为自己的成员方法,而不是外置到另一个类中。

3、方法

        Method[] getMethods();//只获取公共和父类中的方法。

        Method[] getDeclaredMethods();//获取本类中包含私有。

        Method   getMethod("方法名",参数.class(如果是空参可以写null));

        Object invoke(Object obj ,参数);//调用方法  注:如果方法是静态,invoke方法中的对象参数可以为null。

  例如:

    获取某个类中的某个方法:String str = "abcd";

     Method methodCharAt =Class.forName("java.lang.String").getMethod("charAt",int.class);

    调用这个方法:

    1)通常方式:

      str.charAt(2);

    2)反射方式

      methodCharAt.invoke(str,2);

   注:如果传递给Method对象的invoke()方法的第一个参数为null,说明Method对象对应的是一个静态方法

4、JDK1.4和JDK1.5的invoke方法的区别:

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

  JDK1.5:public Object invoke(Object obj,Object... args)

  即按JDK1.4的语法,需要将一个数组作为参数传递给invoke方法时,这时它会把一个数组作为一个元素。

  这时如果我们要取出其中的元素,需要将数组中的元素通过数组一个个的取出。

  所以,调用charAt方法的代码也可以用JDK 1.4改写为 charAt.invoke("str", new Object[]{1})形式。

 代码示例:

 1 package com.itheima.day1;
 2 import java.lang.reflect.Method;
 3 public class ReflectTests {
 4     public static void main(String[] args) throws Exception {
 5         String str = "黑马程序员";
 6         Method method = str.getClass().getMethod("charAt", int.class);
 7         // 1.4写法。
 8         System.out.println(method.invoke(str, new Object[] { 1 }));
 9         // 1.5写法。
10         System.out.println(method.invoke(str, 0));
11     }
12 }

5、对接受数组参数的成员方法进行反射

  需求:用反射的方法运行某个类的mian函数

  代码:

 1  package com.itheima.day1;
 2   import java.lang.reflect.Method;
 3    public class MassTests {
 4          Class clszz = Class.forName("com.itheima.day1.TestArguments");
 5 
 6           Method main = clszz.getMethod("main", String[].class);
 7 
 8           main.invoke(null, new String[]{"aaa", "bbb", "ccc"});
 9        }
10   }
11  class TestArguments{
12   public static void main(String[] args){
13     for(String s: args){
14       System.out.println(s);
15     }
16   }
17 }

  这段代码会发生异常:非法参数异常:Exception in thread "main" java.lang.IllegalArgumentException: wrong number of arguments

分析原因:

  1)启动Java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?

  2)按JDK1.5的语法,整个数组是一个参数,而按JDK1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,Javac会到底按照哪种语法进行处理呢?

  3)JDK1.5肯定要兼容JDK1.4的语法,会按JDK1.4的语法进行处理,即把数组打散成若干个单独的参数。

  4)所以,在给main方法传递参数时,不能使用代码main.invoke(null,"aaa","bbb","ccc"),Javac只把它当作JDK1.4的语法进行理解,而不把它当作JDK1.5的语法解释,因此会出现参数类型不对的问题。

解决方法:

  1)main.invoke(null,new Object[]{new String[]{"aaa","bbb","ccc"}});

  2)main.invoke(null,(Object)new String[]{"aaa","bbb","ccc"});编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数组打散成若干个参数了。

六、数组的反射

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

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

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

        例:

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

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

        如:

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

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

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

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

5、基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。

示例:

 1 package cn.itheima.Demo;
 2 
 3 import java.lang.reflect.Array;
 4 import java.util.Arrays;
 5 
 6 public class ArrayReflect {
 7     public static void main(String[] args) {
 8         int [] a1 = new int[]{1,2,3};
 9         int [] a2 = new int[4];
10         int[][] a3 = new int[2][3];
11         String [] a4 = new String[]{"a","b","c"};
12         System.out.println(a1.getClass().equals(a2.getClass()));//true
13     System.out.println(a1.getClass().equals(a3.getClass()));//false
14       System.out.println(a1.getClass().equals(a4.getClass()));//false
15       System.out.println(a1.getClass().getName());//[I
16       System.out.println(a4.getClass().getName());//[Ljava.lang.String;
17       System.out.println(a1.getClass().getSuperclass());//class java.lang.Object
18       System.out.println(a4.getClass().getSuperclass());//class java.lang.Object
19         
20         Object obj1=a1;
21         Object obj2=a3;
22         Object obj3=a4;
23         
24 //        Object[] obj11=a1;//编译失败,因为a1中的元素是int类型,基本数据类型不是Object
25         Object[] obj13=a3;
26         Object[] obj14=a4;//编译成功,因为String数组中的元素属于Object
27         
28         System.out.println(a1);//[I@4caaf64e
29         System.out.println(a4);//[Ljava.lang.String;@6c10a234
30         System.out.println(Arrays.asList(a1));//[I@4caaf64e
31         System.out.println(Arrays.asList(a4));//[a, b, c]32 }
33   

6、Arrays.asList()方法处理int[]和String[]时的差异。

  代码示例:

 1 public classReflectTest { 
 2   public static void main(String[] args) throwsException { 
 3     int[] arr =new int[] { 1, 2, 3 }; 
 4     String[] str =newString[] {"aaa", "bbb", "ccc" }; 
 5 
 6     // 直接使用System.out.println无法打印出数组的内容 
 7     System.out.println(arr); // 结果:[I@62bc184 
 8     System.out.println(str); // 结果:[Ljava.lang.String;@22adc446 
 9 
10     // 通过Arrays.asList方法打印出集合的内容 
11     System.out.println(Arrays.asList(arr)); // 结果:[[I@62bc184] 
12     System.out.println(Arrays.asLsit(str)); // 结果:[a,b,c]
13 
14     /* 
15      * 原因是因为JDK1.4中为Arrays.asList(Object[] a),JDK1.5中为Arrays.asList(T... a)。 
16      * arr是int[]类型,JDK1.4中的asList方法处理不了,JDK1.5可以处理。但是JDK1.5将 int数组整体作为一个参数进行处理。 
17      * 因此最终结果就是将 int[]进行了封装,结果类型也就成了[[I。 
18      */ 
19   } 
20 }

 练习:利用反射原理写一个打印任意数值的方法。如果是数组,则遍历打印。

  代码:

 1 package com.itheima.day1;
 2 import java.lang.reflect.Array;
 3 public class ReflectTest4 {
 4     
 5     public static void main(String[] args) {
 6         // TODO Auto-generated method stub
 7         int[] a1 = new int[]{1,2,3,4,5};
 8         printObject(a1);
 9         String str = "adc";
10         printObject(str);
11     }
12      //打印任意数值
13     private static void printObject(Object obj) {
14         Class clazz=obj.getClass();
15        //如果传入的是数组,则遍历
16         if(clazz.isArray()){
17            int len =Array.getLength(obj);//Array工具类获取数组长度方法
18             for(int x=0;x<len;x++){
19                 System.out.println(Array.get(obj, x));//Array工具获取数组元素
20             }
21         }
22         else
23             System.out.println(obj);
24     }
25 }

 

 

  

黑马程序员------Java反射学习总结(一)

标签:

原文地址:http://www.cnblogs.com/alvis2015/p/4301396.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!