——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
反射
反射技术:其实就是动态加载一个指定的类,并获取该类中的所有的内容。而且将字节码文件封装成对象,并将字节码文件中的内容都封装成对象,这样便于操作这些成员。简单说:反射技术可以对一个类进行解剖。
反射的好处:大大的增强了程序的扩展性。
反射就是把java类中的各种成分映射成相应的java类(获取其中的变量,方法,构造方法,修饰符,包等信息)。用相应类的实例对象来表示,是Field、Method、Contructor、Package等等。
Field field;(字段)将字段封装成对象类型Field—–> getField();
Constructor cons;(构造函数)将构造函数封装成了对象类型Constructor——> getConstructor();
Method method;(其他方法)将其他方法封装成了对象类型 Method——–> getMethod();
一、Class类
Java程序中的各个java类属于同一类事物,描述这类事物的java类名就是class。
Class类代表java类,他的各个实例对象对应各个类在内存中的字节码。
字节码获得
1. Class cls1 = str1.getClass();
2. Class cls2 = String.class;
3. Class cls3 = Class.forName("java.lang.String");
九个预定义Class实例对象
1、参看Class.isPrimitive()方法的帮助,是否为基本类型
2、Int.class==Integer.TYPE
3、Class 类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。
数组类型的class实例对象
Class.isArray();
只有是在源程序中出现的类型,都有各自的Class实例对象 如:int[], void
二、Constructor类
得到某个类所有的构造方法:Constructor[] constructors = Class.forName(“java.lang.String”).getConStructors();
得到某个构造方法:Constructor[] constructors = Class.forName(“java.lang.String”).getConStructors(StringBuffer.class);
场景实例对象:
通常方式:String str = new String(new StringBuffer(“abc”));
反射方式:String str = (String)constructor.newInstance(new StringBuffer(“abc”));
Class.newInstance()方法:
该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象,用到了缓存机制来保存默认构造方法的实例对象.
三、Field类
Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。
Array 允许在执行 get 或 set 访问操作期间进行扩展转换,但如果将发生收缩转换,则抛出一个 IllegalArgumentException。
//把换源代码中的”a”换成”b”;
ReflectPoint pt1 = new ReflectPoint(3,5);
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().equals(String.class)){
if(field.getType() == String.class){
String oldValue = (String)field.get(obj);
String newValue = oldValue.replace('b', 'a');
field.set(obj, newValue);
}
}
}
public class ReflectPoint {
public String str1 = "ball";
public String str2 = "basketball";
public String str3 = "itcast";
}
四、Method类
Method 提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。
Method 允许在匹配要调用的实参与底层方法的形参时进行扩展转换;但如果要进行收缩转换,则会抛出 IllegalArgumentException。
Method charAt = Class.forNam(“java.lang.String”).getMethod(“charAt”, int.class);
调用方法:
通常方式:System.out.println(str.charAt());
反射方式:System.out.println(charAt.invoke(str, 1));
用反射的方式调用main
当不知道类的名字,就需要用反射的方式调用main。
String startingClassName = args[0];
Method mainMethod = Class.forName(startingClassName).getMethod("main", String[].class);
//mainMethod.invoke(null, new Object[]{new String[]{"111","222","333"}});//把数组打包成另一个数组,拆包后就是一个数组
mainMethod.invoke(null, (Object)new String[]{"111","222","333"});//告诉编译器不要拆包。
五、数组的反射
具有相同维数和元素类型的数组属于同一个类型,既具有相同的Class实例对象。
代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
基本类型的一维数组可以被当做Object类型使用,不能当做Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。
Arrays.asList()方法处理int[]和String[]时的差异。Array工具类用于完成对数组的反射操作。
六、反射的作用——实现框架功能
举例说明就是 一个人买的房子 这个房子就是框架,房子调用这个人买的东西(写的对象),这个人要装修房子,买锁……(人写的对象)也就是框架调用用户写的类,用户调用别人写的类(锁,门窗……)。
框架要解决的核心问题是要为各阶层客户提供服务,就需要:
Class.forName(classNameStr).getMethod(methodName).invoke(obj,Class)
因为在写程序时无法知道要被调用的类名,所以,在程序中无法直接new某个类的实例对象了,而要用反射方式来做
类加载器–ClassLoader
类加载器通常用来加载 *.class文件,
七、总结
反射的基本步骤:
1、获得Class对象,就是获取到指定的名称的字节码文件对象。
2、实例化对象,获得类的属性、方法或构造函数。
3、访问属性、调用方法、调用构造函数创建对象。
获取这个Class对象,有三种方式:
1:通过每个对象都具备的方法getClass来获取。弊端:必须要创建该类对象,才可以调用getClass方法。
//1. 根据给定的类名来获得 用于类加载
String classname = "cn.itcast.reflect.Person";// 来自配置文件
Class clazz = Class.forName(classname);// 此对象代表Person.class
2:每一个数据类型(基本数据类型和引用数据类型)都有一个静态的属性class。弊端:必须要先明确该类。
//2. 如果拿到了对象,不知道是什么类型 用于获得对象的类型
Object obj = new Person();
Class clazz1 = obj.getClass();// 获得对象具体的类型
前两种方式不利于程序的扩展,因为都需要在程序使用具体的类来完成。
3:使用的Class类中的方法,静态的forName方法。
指定什么类名,就获取什么类字节码文件对象,这种方式的扩展性最强,只要将类名的字符串传入即可。
//3. 如果是明确地获得某个类的Class对象 主要用于传参
Class clazz2 = Person.class;
反射的用法:
1)、需要获得java类的各个组成部分,首先需要获得类的Class对象,获得Class对象的三种方式:
Class.forName(classname) 用于做类加载
obj.getClass() 用于获得对象的类型
类名.class 用于获得指定的类型,传参用
2)、反射类的成员方法:
Class clazz = Person.class;
Method method = clazz.getMethod(methodName, new Class[]{paramClazz1, paramClazz2});
method.invoke();
3)、反射类的构造函数:
Constructor con = clazz.getConstructor(new Class[]{paramClazz1, paramClazz2,...})
con.newInstance(params...)
4)、反射类的属性:
Field field = clazz.getField(fieldName);
field.setAccessible(true);
field.setObject(value);
——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-