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

来源:互联网 发布:国家电网数据运维招聘 编辑:程序博客网 时间:2024/05/21 08:05

------<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功能函数代码示例

package cn.itcast.reflect;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.lang.reflect.Type;/** * @class: ReflectionClassDemo * @package: cn.itcast.reflect * @description: TODO * @author: vivianZhao * @date: 2013-7-20 上午10:55:13 * @version: 1.0 */public class ReflectionClassDemo {public static void main(String args[]) throws Exception {ReflectionClassDemo ref = new ReflectionClassDemo();ref.getConstructor();}public void getConstructor() throws Exception {Class<?> c = Class.forName("java.lang.Long");Class<?> cs[] = { java.lang.String.class };System.out.println("\n-------------------------------\n");Constructor<?> cst1 = c.getConstructor(cs);System.out.println("1、通过参数获取指定Class对象的构造方法:");System.out.println(cst1.toString());Constructor cst2 = c.getDeclaredConstructor(cs);System.out.println("2、通过参数获取指定Class对象所表示的类或接口的构造方法:");System.out.println(cst2.toString());Constructor cst3 = c.getEnclosingConstructor();System.out.println("3、获取本地或匿名类Constructor 对象,它表示基础类的立即封闭构造方法。");if (cst3 != null)System.out.println(cst3.toString());elseSystem.out.println("-- 没有获取到任何构造方法!");Constructor[] csts = c.getConstructors();System.out.println("4、获取指定Class对象的所有构造方法:");for (int i = 0; i < csts.length; i++) {System.out.println(csts[i].toString());}System.out.println("\n-------------------------------\n");Type types1[] = c.getGenericInterfaces();System.out.println("1、返回直接实现的接口:");for (int i = 0; i < types1.length; i++) {System.out.println(types1[i].toString());}Type type1 = c.getGenericSuperclass();System.out.println("2、返回直接超类:");System.out.println(type1.toString());Class[] cis = c.getClasses();System.out.println("3、返回 Class 中使用的所有的类和所有的接口:");for (int i = 0; i < cis.length; i++) {System.out.println(cis[i].toString());}Class cs1[] = c.getInterfaces();System.out.println("4、实现的接口");for (int i = 0; i < cs1.length; i++) {System.out.println(cs1[i].toString());}System.out.println("\n-------------------------------\n");Field fs1[] = c.getFields();System.out.println("1、类或接口的所有可访问公共字段:");for (int i = 0; i < fs1.length; i++) {System.out.println(fs1[i].toString());}Field f1 = c.getField("MIN_VALUE");System.out.println("2、类或接口的指定已声明指定公共成员字段:");System.out.println(f1.toString());Field fs2[] = c.getDeclaredFields();System.out.println("3、类或接口所声明的所有字段:");for (int i = 0; i < fs2.length; i++) {System.out.println(fs2[i].toString());}Field f2 = c.getDeclaredField("serialVersionUID");System.out.println("4、类或接口的指定已声明指定字段:");System.out.println(f2.toString());System.out.println("\n-------------------------------\n");Method m1[] = c.getMethods();System.out.println("1、返回类所有的公共成员方法:");System.out.println(m1.length);for (int i = 0; i < m1.length; i++) {System.out.println(m1[i].toString());}Method m3[] = c.getDeclaredMethods();System.out.println("2、返回类自己定义所有的成员方法:");System.out.println(m3.length);for (int i = 0; i < m3.length; i++) {System.out.println(m3[i].toString());}Method m2 = c.getMethod("longValue", new Class[] {});System.out.println("3、返回指定公共成员方法:");System.out.println(m2.toString());}}

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

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

3、成员变量的反射_Field 类

  //成员变量的反射  ReflectPoint pt1 = new ReflectPoint(3, 6);  //成员变量时共有的可以正常反射  Field filedY = pt1.getClass().getField("y");   System.out.println(filedY.get(pt1));    //如果成员变量是私有的要强行反射getDeclaredField  Field fieldX = pt1.getClass().getDeclaredField("x");  //暴力反射修改字段的访问属性的方法方法 setAccessible(true); 这是继承自 java.lang.reflect.AccessibleObject 的方法  fieldX.setAccessible(true);  //获取  System.out.println(fieldX.get(pt1));

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

<span style="font-size:14px;">import java.lang.reflect.Field;  public class Reflectest {      public static void main(String[] args) throws Exception {          ReflectPoint pt1=new ReflectPoint();          changeStringValue(pt1);          System.out.println(pt1);      }        private static void changeStringValue(Object obj) throws Exception{          Field[] fields=obj.getClass().getFields();//获取所有的成员变量          //遍历成员变量          for(Field field:fields){    //比较字节码用==              if(field.getType()==String.class){              String oldValue=(String)field.get(obj);//获取obj的String类型的成员变量              String newValue=oldValue.replace('b', 'a');//将b换成a              field.set(obj, newValue);//将此 Field表示的字段设置为指定的新值               }          }      }  }  class ReflectPoint {      public String str1="ball";      public String str2="basketball";      public String str3="itcast";      //重写toString方法      public String toString(){          return str1+"  "+str2+"  "+str3+"  ";      }  } </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}) 形式。

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

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

<span style="font-size:14px;"><strong>/** * 需求:演示数组 和 Object 的关系 *  * 思路: * 1.获取数组的  Class 对象,比较是否相等 * 2.打印数组的  Class 对象的名字 * 3.数组 和 Object 类型之间的类型转换 *  * 步骤: *  * 总结: * 1.java 里面,相同元素类型和相同维度数的数组是同一个类型的数组,对应同一个 Class 对象 * 2.数组类型的签名是" [ + 元素类型名签名 ",如果是多维数组,也是符合前面的规则,结果就成了几维数组会有几 * 个" [ "符号 * 3.数组类型可以向上转型为 Object 类型 * 4.java 语言中没有多维数组,其实都是一维数组,所谓多维数组,是数组中的元素还是数组,只有最后一层是一个非 * 数组类型 */package cn.itcast.reflect;public class ArrayAndObject {public static void main(String[] args) {// TODO Auto-generated method stubint [] a1 = new int[4];int [] a2 = new int[5];int [][] a3 = new int[2][3];String [] a4 = new String[3];// 返回 true,说明同类型同维度的数组是同一个 Class 对象System.out.println(a1.getClass() == a2.getClass()); // 不可比较,说明同类型不同维度的数组不是同一个 Class 对象//System.out.println(a1.getClass() == a3.getClass());// 不可比较,说明不同类型同维度的数组不是同一个 Class 对象//System.out.println(a1.getClass() == a4.getClass());// 数组类型的名称是 [ + 类型名签名,如果是多维数组,几维数组用几个 [System.out.println(a1.getClass().getName());System.out.println(a3.getClass().getName());System.out.println(a4.getClass().getName());// 数组类型的父类型都是 Object 类型System.out.println(a1.getClass().getSuperclass().getName());System.out.println(a3.getClass().getSuperclass().getName());// 数组类型的父类都是 Object 类型,所以数组类型可以上转为 Object 类Object aObject1 = a1;Object aObject2 = a4;// 数组中的元素有两种类型,一种是基本类型,一种是引用类型//Object[] aObjects3 = a1;// 数组类型的类型匹配需要匹配两个地方,第一个是否是数组,第二个数组中的元素类型的匹配// Object [] aObject4 定义了一个 ,一维数组,其中数组中的元素是 Object 类型// a3 是定义了一个一维数组A,数组中的元素是 一维数组B,一维数组B中的元素是 int 类型,一维数组B可以// 向上转型为 Object 类型,所以可认为 Java 语言中没有多维数组,其实都是一维数组,所谓多维数组,是// 数组中的元素还是数组,只有最后一层是一个非数组类型Object[] aObject4 = a3;Object[] aObject5 = a4;}}</strong></span>

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

<strong>int [] a11 = new int[]{1, 2, 3};String [] a12 = new String[]{"a", "b","c"};System.out.println(Arrays.asList(a11));// 这说明数组中的元素向上转型的时候不会进行自动装箱拆箱// 自动装箱拆箱只会在运算符表达式中进行//System.out.println(Arrays.asList((Integer [])a11));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
原创粉丝点击