黑马程序员-----基础加强-反射

------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

1.反射的概念和用途总述

        反射首先是一种动态的思想,不再是硬编码。就是说在使用过程中,外部传入一个类,通过对这个类进行反射,再去按照反射定义者的意图使用这个类。传说中目标类和客户类的关系发生改变。

        做个比喻,饭店新请一个厨师,负责人就问他会做什么菜啊、调味品用什么量啊等等,等负责人了解之后就让厨师去做菜了,但是为了适应当地人口味,负责人还可以在下命令之前告诉厨师,少发花椒多放辣等。厨师的主要做菜功能不变,但是负责人可以要求他做一些功能上的调整。

反射的作用

反射的作用总结起来就一个:倒转了目标类和客户类的依赖关系。
以前我们设计程序,客户类要么依赖于目标类,要么依赖于目标类的接口。因为目标类是作为工具提供给客户类使用的,根据 java 基本语法规则,要使用某个类,必须知道该类提供的接口。有了反射之后,我们就可以方便是使用反射来实现框架,解除框架对于我们写的类——目标类,的依赖关系。
反射的概念和实现原理
Reflection 是Java被视为动态(或准动态)语言的一个关键性质。反射就是 把 JVM 通过符号引用动态解析 java 类的字节码的能力映射成为各种 Java 类的成分类的机制,通过这个机制,java 把 JVM 动态解析符号引用的功能封装为各种 API 类公开给我们使用,这个机制允许我们可以 于运行时加载、探知、使用,编译期间完全未知的classes程序在运行时通过 Reflection APIs 取得任何一个 class 的内部信息,包括其modifiers(诸如public, static 等等)、superclass(例如Object)、实现之interfaces(例如Serializable),也包括 fields 和 methods 的所有信息,并 于运 行时改变该类的对象的 fields 内容或调用该类或者该类对象的 methods。这种动态获取类的信息以及动态调用对象的方法的功能就是Java 语言的反射(Reflection)机制。

2.Java类反射中所必须的类:

Java的类反射所需要的类并不多,它们分别是:Class、Field、Constructor、Method、Object,下面我将对这些类做一个简单的说明。
Class类:类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。

Field类:提供有关类或接口的属性的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)属性或实例属性,简单的理解可以把它看成一个封装反射类的属性的类。
Constructor类:提供关于类的单个构造方法的信息以及对它的访问权限。这个类和Field类不同,Field类封装了反射类的属性,而Constructor类则封装了反射类的构造方法。
Method类:提供关于类或接口上单独某个方法的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。 这个类不难理解,它是用来封装反射类方法的一个类。
Object类:每个类都使用 Object 作为超类。所有对象(包括数组)都实现这个类的方法。

3.详述各个反射类及代码示例其用法

1、Class类

被称为反射的基石的Class类究竟何德何能获得这一殊荣呢?静听分解:

首先,这家伙长得像极了class,我们一直使用的class啊。难道有血缘关系?私生子?八卦之火熊熊燃烧。不过,做学问要严肃!好吧,严肃点

Classclass的区别

1classJava中的类用于描述一类事物的共性,该类事物有什么属性,没有什么属性,至于这个属性的值是什么,则由此类的实例对象确定,,不同的实例对象有不同的属性值。

2Class:指的是Java程序中的各个Java类是属于同一类事物,都是Java程序的类,这些类称为Class。例如人对应的是Person类,Java类对应的就是Class

Class获取对象的方法(借鉴toShareBeauty同学的图)

Class功能函数代码示例

[java]  view plain copy
  1. package cn.itcast.reflect;  
  2.   
  3. import java.lang.reflect.Constructor;  
  4. import java.lang.reflect.Field;  
  5. import java.lang.reflect.Method;  
  6. import java.lang.reflect.Type;  
  7.   
  8. /** 
  9.  * @class: ReflectionClassDemo 
  10.  * @package: cn.itcast.reflect 
  11.  * @description: TODO 
  12.  * @author: vivianZhao 
  13.  * @date: 2013-7-20 上午10:55:13 
  14.  * @version: 1.0 
  15.  */  
  16. public class ReflectionClassDemo {  
  17.   
  18.     public static void main(String args[]) throws Exception {  
  19.         ReflectionClassDemo ref = new ReflectionClassDemo();  
  20.         ref.getConstructor();  
  21.   
  22.     }  
  23.   
  24.     public void getConstructor() throws Exception {  
  25.         Class<?> c = Class.forName("java.lang.Long");  
  26.         Class<?> cs[] = { java.lang.String.class };  
  27.   
  28.         System.out.println("\n-------------------------------\n");  
  29.   
  30.         Constructor<?> cst1 = c.getConstructor(cs);  
  31.         System.out.println("1、通过参数获取指定Class对象的构造方法:");  
  32.         System.out.println(cst1.toString());  
  33.   
  34.         Constructor cst2 = c.getDeclaredConstructor(cs);  
  35.         System.out.println("2、通过参数获取指定Class对象所表示的类或接口的构造方法:");  
  36.         System.out.println(cst2.toString());  
  37.   
  38.         Constructor cst3 = c.getEnclosingConstructor();  
  39.         System.out.println("3、获取本地或匿名类Constructor 对象,它表示基础类的立即封闭构造方法。");  
  40.         if (cst3 != null)  
  41.             System.out.println(cst3.toString());  
  42.         else  
  43.             System.out.println("-- 没有获取到任何构造方法!");  
  44.   
  45.         Constructor[] csts = c.getConstructors();  
  46.         System.out.println("4、获取指定Class对象的所有构造方法:");  
  47.         for (int i = 0; i < csts.length; i++) {  
  48.             System.out.println(csts[i].toString());  
  49.         }  
  50.   
  51.         System.out.println("\n-------------------------------\n");  
  52.   
  53.         Type types1[] = c.getGenericInterfaces();  
  54.         System.out.println("1、返回直接实现的接口:");  
  55.         for (int i = 0; i < types1.length; i++) {  
  56.             System.out.println(types1[i].toString());  
  57.         }  
  58.   
  59.         Type type1 = c.getGenericSuperclass();  
  60.         System.out.println("2、返回直接超类:");  
  61.         System.out.println(type1.toString());  
  62.   
  63.         Class[] cis = c.getClasses();  
  64.         System.out.println("3、返回 Class 中使用的所有的类和所有的接口:");  
  65.         for (int i = 0; i < cis.length; i++) {  
  66.             System.out.println(cis[i].toString());  
  67.         }  
  68.   
  69.         Class cs1[] = c.getInterfaces();  
  70.         System.out.println("4、实现的接口");  
  71.         for (int i = 0; i < cs1.length; i++) {  
  72.             System.out.println(cs1[i].toString());  
  73.         }  
  74.   
  75.         System.out.println("\n-------------------------------\n");  
  76.   
  77.         Field fs1[] = c.getFields();  
  78.         System.out.println("1、类或接口的所有可访问公共字段:");  
  79.         for (int i = 0; i < fs1.length; i++) {  
  80.             System.out.println(fs1[i].toString());  
  81.         }  
  82.   
  83.         Field f1 = c.getField("MIN_VALUE");  
  84.         System.out.println("2、类或接口的指定已声明指定公共成员字段:");  
  85.         System.out.println(f1.toString());  
  86.   
  87.         Field fs2[] = c.getDeclaredFields();  
  88.         System.out.println("3、类或接口所声明的所有字段:");  
  89.         for (int i = 0; i < fs2.length; i++) {  
  90.             System.out.println(fs2[i].toString());  
  91.         }  
  92.   
  93.         Field f2 = c.getDeclaredField("serialVersionUID");  
  94.         System.out.println("4、类或接口的指定已声明指定字段:");  
  95.         System.out.println(f2.toString());  
  96.   
  97.         System.out.println("\n-------------------------------\n");  
  98.   
  99.         Method m1[] = c.getMethods();  
  100.         System.out.println("1、返回类所有的公共成员方法:");  
  101.         System.out.println(m1.length);  
  102.         for (int i = 0; i < m1.length; i++) {  
  103.             System.out.println(m1[i].toString());  
  104.         }  
  105.           
  106.         Method m3[] = c.getDeclaredMethods();  
  107.         System.out.println("2、返回类自己定义所有的成员方法:");  
  108.         System.out.println(m3.length);  
  109.         for (int i = 0; i < m3.length; i++) {  
  110.             System.out.println(m3[i].toString());  
  111.         }  
  112.   
  113.         Method m2 = c.getMethod("longValue"new Class[] {});  
  114.         System.out.println("3、返回指定公共成员方法:");  
  115.         System.out.println(m2.toString());  
  116.   
  117.     }  
  118. }  

2、构造方法的反射应用_Constructor 类

[java]  view plain copy
  1. package cn.itcast.day1;  
  2. import java.lang.reflect.Constructor;  
  3. public class ConstructorDemo {  
  4.   
  5.     public static void main(String[] args) throws Exception{  
  6.         //得到某个类所有的构造方法  
  7.         Constructor<?>[] constructors = Class.forName("java.lang.String").getConstructors();        
  8.         //取得指定类的构造方法  
  9.         Class classType = Class.forName("java.lang.String");  
  10.         Constructor constructor = classType.getDeclaredConstructor(StringBuffer.class);          
  11.         /*创建实例对象*/  
  12.         //通常方式:  
  13.         String str = new String(new StringBuffer("abc"));  
  14.         //反射方式  
  15.         String str1 = (String)constructor.newInstance(new StringBuffer("abc"));         
  16.         //Class.NewInstance()方法  
  17.         String str2 = (String)Class.forName("java.lang.String").newInstance();  
  18.         /*该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。该方法的内部用到了缓存机制来保存默认构造方法的实例对象*/          
  19.         //获得构造方法并创建实例对象  
  20.         Constructor constructor1 = String.class.getConstructor(StringBuffer.class);  
  21.         /*getConstructor()中用到是不定长度参数,1.4版本之前,则是通过传入数组来调节参数类型和数组不确定的情况*/  
  22.         String str3 = (String)constructor1.newInstance(new StringBuffer("aaa"));  
  23.     }  
  24. }  

3、成员变量的反射_Field 类

[java]  view plain copy
  1. //成员变量的反射  
  2. ReflectPoint pt1 = new ReflectPoint(36);  
  3. //成员变量时共有的可以正常反射  
  4. Field filedY = pt1.getClass().getField("y");   
  5. System.out.println(filedY.get(pt1));  
  6.   
  7.   
  8. //如果成员变量是私有的要强行反射getDeclaredField  
  9. Field fieldX = pt1.getClass().getDeclaredField("x");  
  10. //暴力反射修改字段的访问属性的方法方法 setAccessible(true); 这是继承自 java.lang.reflect.AccessibleObject 的方法  
  11. fieldX.setAccessible(true);  
  12. //获取  
  13. System.out.println(fieldX.get(pt1));  

练习:将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的"b"改成"a"

[java]  view plain copy
  1. <span style="font-size:14px;">import java.lang.reflect.Field;    
  2. public class Reflectest {    
  3.     public static void main(String[] args) throws Exception {    
  4.         ReflectPoint pt1=new ReflectPoint();    
  5.         changeStringValue(pt1);    
  6.         System.out.println(pt1);    
  7.     }      
  8.     private static void changeStringValue(Object obj) throws Exception{    
  9.         Field[] fields=obj.getClass().getFields();//获取所有的成员变量    
  10.         //遍历成员变量    
  11.         for(Field field:fields){    
  12.   //比较字节码用==    
  13.             if(field.getType()==String.class){    
  14.             String oldValue=(String)field.get(obj);//获取obj的String类型的成员变量    
  15.             String newValue=oldValue.replace('b''a');//将b换成a    
  16.             field.set(obj, newValue);//将此 Field表示的字段设置为指定的新值    
  17.              }    
  18.         }    
  19.     }    
  20. }    
  21. class ReflectPoint {    
  22.     public String str1="ball";    
  23.     public String str2="basketball";    
  24.     public String str3="itcast";    
  25.     //重写toString方法    
  26.     public String toString(){    
  27.         return str1+"  "+str2+"  "+str3+"  ";    
  28.     }    
  29. } </span>  

4.成员方法的反射_Method类

得到类中的某一个方法:
例子: Method charAt = Class.forName("java.lang.String").getMethod("charAt", int.class);
 调用方法:
通常方式:System.out.println(str.charAt(1));
反射方式: charAt.invoke(str, 1); 
如果传递给 Method 对象的 invoke() 方法的第一个参数为 null,说明该 Method 对象对应的是一个静态方法。
 jdk1.4和jdk1.5的invoke方法的区别:
jdk1.5:public Object invoke(Object obj,Object... args)
jdk1.4:public Object invoke(Object obj,Object[] args),按 jdk1.4的语法,需要将一个数组作为参数传递给 invoke 方法,数组中的每个元素分别对应被调用方法中的一个参数,所以,调用 charAt 方法的代码也可以用 jdk1.4 改写为 charAt.invoke(“str”, new Object[]{1}) 形式。

[java]  view plain copy
  1. package cn.itcast.day1;  
  2.   
  3. import java.lang.reflect.Method;  
  4.   
  5. public class MethoDemo {  
  6.   
  7.     public static void main(String[] args) throws Exception {         
  8.         Sring className = args[0];  
  9.         Method method = Class.forName(className).getMethod("main", String[].class);  
  10.           
  11.         method.invoke(null, (Object)new String[]{"aaa","bbb","ccc"});  
  12.         /* 
  13.          * 如果按照一般写法,传递参数应该是这样: 
  14.          * method.invoke(null, new String[]{"aaa","bbb","ccc"}); 
  15.          * 但是由于jvm自动拆包,会将String数组当作三个参数传入,这个main方法中只接受一个String[]不符,编译器会报错,所以有两种解决方案。 
  16.          * 其一:像上述程序中所写的那样,在前面加上强制类型转换,告诉编译器这是一个整体,不要拆包 
  17.          * 其二:可以这样写——method.invoke(null, new Object[]{new String[]{"aaa","bbb","ccc"}}); 
  18.          *          定义一个Object类型数组,并将String[]整体作为一个元素放入数组中,编译器拆包后得到的便是一个String[]类型参数。 
  19.          */  
  20.     }  
  21. }  
  22.   
  23. class Test{  
  24.       
  25.     public static void main(String[] args){  
  26.           
  27.         for (String str : args){  
  28.             System.out.println(str);  
  29.         }  
  30.     }  
  31. }  

5.数组与Object类的关系及其反射类型

[java]  view plain copy
  1. <span style="font-size:14px;"><strong>/** 
  2.  * 需求:演示数组 和 Object 的关系 
  3.  *  
  4.  * 思路: 
  5.  * 1.获取数组的  Class 对象,比较是否相等 
  6.  * 2.打印数组的  Class 对象的名字 
  7.  * 3.数组 和 Object 类型之间的类型转换 
  8.  *  
  9.  * 步骤: 
  10.  *  
  11.  * 总结: 
  12.  * 1.java 里面,相同元素类型和相同维度数的数组是同一个类型的数组,对应同一个 Class 对象 
  13.  * 2.数组类型的签名是" [ + 元素类型名签名 ",如果是多维数组,也是符合前面的规则,结果就成了几维数组会有几 
  14.  * 个" [ "符号 
  15.  * 3.数组类型可以向上转型为 Object 类型 
  16.  * 4.java 语言中没有多维数组,其实都是一维数组,所谓多维数组,是数组中的元素还是数组,只有最后一层是一个非 
  17.  * 数组类型 
  18.  */  
  19. package cn.itcast.reflect;  
  20. public class ArrayAndObject {  
  21.   
  22.     public static void main(String[] args) {  
  23.         // TODO Auto-generated method stub  
  24.         int [] a1 = new int[4];  
  25.         int [] a2 = new int[5];  
  26.         int [][] a3 = new int[2][3];  
  27.           
  28.         String [] a4 = new String[3];  
  29.           
  30.         // 返回 true,说明同类型同维度的数组是同一个 Class 对象  
  31.         System.out.println(a1.getClass() == a2.getClass());   
  32.         // 不可比较,说明同类型不同维度的数组不是同一个 Class 对象  
  33.         //System.out.println(a1.getClass() == a3.getClass());  
  34.         // 不可比较,说明不同类型同维度的数组不是同一个 Class 对象  
  35.         //System.out.println(a1.getClass() == a4.getClass());  
  36.           
  37.         // 数组类型的名称是 [ + 类型名签名,如果是多维数组,几维数组用几个 [  
  38.         System.out.println(a1.getClass().getName());  
  39.         System.out.println(a3.getClass().getName());  
  40.         System.out.println(a4.getClass().getName());  
  41.         // 数组类型的父类型都是 Object 类型  
  42.         System.out.println(a1.getClass().getSuperclass().getName());  
  43.         System.out.println(a3.getClass().getSuperclass().getName());  
  44.           
  45.         // 数组类型的父类都是 Object 类型,所以数组类型可以上转为 Object 类  
  46.         Object aObject1 = a1;  
  47.         Object aObject2 = a4;  
  48.           
  49.         // 数组中的元素有两种类型,一种是基本类型,一种是引用类型  
  50.         //Object[] aObjects3 = a1;  
  51.           
  52.         // 数组类型的类型匹配需要匹配两个地方,第一个是否是数组,第二个数组中的元素类型的匹配  
  53.         // Object [] aObject4 定义了一个 ,一维数组,其中数组中的元素是 Object 类型  
  54.         // a3 是定义了一个一维数组A,数组中的元素是 一维数组B,一维数组B中的元素是 int 类型,一维数组B可以  
  55.         // 向上转型为 Object 类型,所以可认为 Java 语言中没有多维数组,其实都是一维数组,所谓多维数组,是  
  56.         // 数组中的元素还是数组,只有最后一层是一个非数组类型  
  57.         Object[] aObject4 = a3;  
  58.         Object[] aObject5 = a4;  
  59.     }  
  60. }</strong></span>  

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

[java]  view plain copy
  1. <strong>  int [] a11 = new int[]{123};      
  2.     String [] a12 = new String[]{"a""b","c"};  
  3.     System.out.println(Arrays.asList(a11));  
  4.     // 这说明数组中的元素向上转型的时候不会进行自动装箱拆箱  
  5.     // 自动装箱拆箱只会在运算符表达式中进行  
  6.     //System.out.println(Arrays.asList((Integer [])a11));  
  7.     System.out.println(Arrays.asList(a12));</strong>  

打印结果为:
[[I@4706e02e]
[a, b, c]
这是因为 Arrays 类的 asList 方法在 jdk1.5 和 jdk1.4 中不同,
jdk1.5 :static <T> List<T> asList(T... a) 
jdk1.4:public static List asList(Object[] a)  
jdk1.5 为了兼容 jdk1.5 而首先按照 jdk1.4 的类型运行,所以把 int[] 当做一个 Object 对象,把 String[] 当做一个 Object[] 对象。

总结:反射部分的知识目前所了解,用途不是很广,也比较难以理解,但是我可以感觉到,反射的作用很大(作用大和用途不广不矛盾),当需要的时候,会非常省时省力,也非常有效率,非常有必要学透彻。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值