------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------
1.反射的概念和用途总述
反射首先是一种动态的思想,不再是硬编码。就是说在使用过程中,外部传入一个类,通过对这个类进行反射,再去按照反射定义者的意图使用这个类。传说中目标类和客户类的关系发生改变。
做个比喻,饭店新请一个厨师,负责人就问他会做什么菜啊、调味品用什么量啊等等,等负责人了解之后就让厨师去做菜了,但是为了适应当地人口味,负责人还可以在下命令之前告诉厨师,少发花椒多放辣等。厨师的主要做菜功能不变,但是负责人可以要求他做一些功能上的调整。
反射的作用
反射的概念和实现原理
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啊。难道有血缘关系?私生子?八卦之火熊熊燃烧。不过,做学问要严肃!好吧,严肃点
Class和class的区别
1)class:Java中的类用于描述一类事物的共性,该类事物有什么属性,没有什么属性,至于这个属性的值是什么,则由此类的实例对象确定,,不同的实例对象有不同的属性值。
2)Class:指的是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());
- else
- System.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 stub
- int [] 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>
[a, b, c]
总结: 反射部分的知识目前所了解,用途不是很广,也比较难以理解,但是我可以感觉到,反射的作用很大(作用大和用途不广不矛盾),当需要的时候,会非常省时省力,也非常有效率,非常有必要学透彻。