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