JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
如果正常的情况下,如果要使用一个类,则必须按照如下的步骤操作:
1)使用import导入类所在的包(类:java.lang.Class)。
2)明确的使用类名称或接口名称定义对象。
3)通过关键字new进行类对象实例化(构造方法:java.lang.reflect.Constructor).
4)产生对象可以使用”对象.属性”进行类中属性的调用(属性:java.lang.reflect.Field);
而反射的过程呢?不需要有明确类型的对象,所有的对象使用Object表示:
可以直接利用Object与反射机制的混合调用类中的方法。
下面我们来看一下反射中类的关系图:
下面就简单说一下图中每个类的功能:
AnnotatedElement:注解元素操作类
Member:提供类和接口,以获取关于类和对象的反射信息。
GenericDeclaration:声明类型变量的所有实体的公共接口
Type:返回这个类的名称(e.g. java.lang.Class)
AccessibleObject:Field,Contrutor,Method类的基本类,用于控制可访问.
Executable:Constructor,Method共享通用功能的超类
Filed:类中的属性
Contrutor:类中的构造器
Method:类中的方法
Annotation:类中的注解
Class:Class代表类的实例(类类型)
如何取得Class对象:
1. Object类提供了一个返回Class类对象的方法:public Class getClass();
2. 利用”类.class”取得,日后见得最多的就是在Hibernate上
3. 利用Class类的static方法取得:ClassforName(String className);
如果是程序设计人员,使用最多的方法一定是forName()方法,但是如果是使用者,肯定会使用”类.class”通过之前的分析可以知道,工厂设计模式最好利用反射机制来解决耦合问题
我们都知道在Java的世界中一切皆对象。那么我们就有必要了解一下Object类。那么我们就来了解一下Object类中的所有方法以及每一个方法使用的注解事项:
1. 对象clone:Object clone()
clone对象所在的类一定要实现java.lang.Cloneable接口,而且子类只需要调用Object.clone就可以成功
实现clone操作
2. 对象输出 : String toString();
直接输出对象时会默认调用toString()方法。
3. 对象比较:boolean equals(Object obj)
当保存set集合时,会依靠hashCode()和equals()判断是否为重复对象
4. 取得对象的hash码:int hashCode();
可以理解为每一个类对象的唯一编码,比较时会先判断编码是否相同,而后再调用equals()
5. 取得class类对象:Class getClass();
通过一个已经实例化好的对象进行对象的反射操作
6. 线程等待void wait() InterceptorException
执行到此代码时线程要等待执行,一直到执行notify(),notifyAll()方法
7. 唤醒第一个等待线程:void notify();
8. 唤醒全部等待线程:void notifyAll();
9. public void finalize() throws Throwable;
当使用gc回收无用垃圾空间时默认调用
Class类如果使用了forName()方法之后,就可以使用Class类定义的newInstance方法默认去调用类之中的无参构造方法进行操作 T newInstance.一定要记住,此泛型使用不到。
1)Class Demo类
/**
* Created by Carl on 2016/8/13.
*/
class Person{
public Person(){
System.out.println("Peson类的构造方法...");
}
@Override
public String toString() {
return "Peson类的toString()方法...";
}
}
public class ReflectDemo {
public static void main(String[] args) throws Exception {
Class> clazz = Class.forName("com.weimob.carl.user.reflect.Person");
Object obj = clazz.newInstance();
System.out.println(obj);
}
}
2)运行结果
这个时候就可以非常清楚的发现,在整个程序编写之中,即使不完全知道类的结构,即使不导入包.class,可以进行对象的实现
但是如果使用反射实例化类对象,必须要求类中存在无参构造方法。因为默认使用Class类的newInstance()
方法只能够找到无参。这个时候只能够取得类之中的构造方法,传递所需要的参数后才可以执行。
在Class类里面定义了可以取得一个类之中方法的操作:
1、取得类之中的全部构造:Constructor[?> getConstructors()
2、取得类之中指定参数的构造:Constructor[T>getConstructor(Class[?>… parameterTypes)
1)范例:取得String类之中的全部构造方法:
public class ReflectDemo {
public static void main(String[] args) throws Exception {
Class> clazz = Class.forName("java.lang.String");
Constructor>[] constructors = clazz.getConstructors();
for (int i = 0; i < constructors.length; i++) {
System.out.println(constructors[i]);
}
}
}
运行结果:
所以如果现在要想进行指定构造方法调用,就必须将关注点放在Constructor类之中。在此类中定义了一个实例化对象方法public T newInstance(Object.. initargs)。
2)范例:实例化不含有无参的Person对象
class Person{
private String name;
private Integer age;
public Person(String name, Integer age){
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "姓名: " + this.name + ",年龄: " + this.age;
}
}
public class ReflectDemo {
public static void main(String[] args) throws Exception {
Class> clazz = Class.forName("com.weimob.carl.user.reflect.Person");
Constructor> constructor = clazz.getConstructor(String.class, Integer.class);
Object object = constructor.newInstance("carl", 26);
System.out.println(object);
}
}
运行结果:
正是因为如果是通过构造方法实例化对象规格不统一。所以在进行简单Java类操作的时候就明确给出了,必须有无参构造方法。
取得了一个类的实例化对象之后,下面主要的任务就是要调用类之中可以使用的操作方法,对于一个类之中的方法实际上由两类:
1)取得父类继承而来的方法和本类方法:
取得全部方法 Methods[] getMethods();
取得指定方法 Method(String name, Class[?>… parameterType)
2)取得本类定义的方法
取得全部方法 Method[] getDeclaredMethods()
取得指定方法 Method getDeclaredMethod(String name, Class[?>… parameterType)
但是以上的操作在方法定义上区别不大,因为方法大部分都是public ,所有两种方式取得的结果是没有区别。
1)范例:取得本类中的方法
class Person{
private String name;
private Integer age;
public Person(String name, Integer age){
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "姓名: " + this.name + ",年龄: " + this.age;
}
public void sayHello(){
System.out.println("hello world...");
}
}
public class ReflectDemo {
public static void main(String[] args) throws Exception {
Class> clazz = Class.forName("com.weimob.carl.user.reflect.Person");
Method[] methods = clazz.getDeclaredMethods();
for(int i = 0; i < methods.length; i++){
System.out.println(methods[i]);
}
}
}
运行结果:
现在的程序是直接调用Method类之中的toString()方法实现的输出。如果用户有需要也可以自己去整理方法的输出,需要使用到Method类的如下方法.
2)自己去整理方法的输出
1.取得方法修饰符:public int modityiers()
程序之中找的不是public、static等关键字,而是关键字所代表的数字,但是在程序之中必须将其更换为
读懂的信息,则可以借助于Modifyer类完成,此类中可以直接利用方法将数字变为修饰字符串
转换方法:public static String toString(int mod)。
2)取得方法的返回类型:public Class[?> getReturnType();
3)取得方法的参数:public Class[?> getParameterTypes();
4)取得所有抛出的异常:public Class[?> getExceptionTypes();
代码如下:
class Person{
private String name;
private Integer age;
public Person(String name, Integer age){
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "姓名: " + this.name + ",年龄: " + this.age;
}
public void sayHello(){
System.out.println("hello world...");
}
}
public class ReflectDemo {
public static void main(String[] args) throws Exception {
Class> clazz = Class.forName("com.weimob.carl.user.reflect.Person");
Method[] methods = clazz.getMethods();
for(int i = 0; i < methods.length; i++){
// 方法修饰符
System.out.print(Modifier.toString(methods[i].getModifiers()));
// 方法返回参数
System.out.print(" " + methods[i].getReturnType().getSimpleName() + " ");
// 方法名
System.out.print(methods[i].getName() + "(");
// 方法参数
Class> params[] = methods[i].getParameterTypes();
if(params.length > 0){// 有方法参数
for(int j = 0; j < params.length; j++){
System.out.print(params[j].getSimpleName() + " ");
if(j < params.length - 1){
System.out.print(", ");
}
}
}
System.out.print(") ");
// 方法异常
Class> exceptions[] = methods[i].getExceptionTypes();
if (exceptions.length > 0){
System.out.print(" throws ");
for(int j = 0; j < exceptions.length; j++){
System.out.print(exceptions[j].getSimpleName());
if(j < exceptions.length - 1){
System.out.print(", ");
}
}
}
System.out.println();// 换行
}
}
}
运行结果:
此类代码一般只会在编写开发工具的时候出现,而所谓的随笔提示功能就是依据以上的代码实现的。
开发者密切关联最紧密的一定是利用Method调用类中的方法。而且在Method调用类中的方法。
范例:反射调用类中的方法。
调用方法 – public Object invoke(Object obj, Object… args)
class Person{
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public class ReflectDemo {
public static void main(String[] args) throws Exception {
Class> clazz = Class.forName("com.weimob.carl.user.reflect.Person");
Object object = clazz.newInstance();
Method setNameMethod = clazz.getMethod("setName", String.class);
Method getNameMethod = clazz.getMethod("getName");
setNameMethod.invoke(object, "carl");
System.out.println(getNameMethod.invoke(object));
}
}
关于类中的属性也可以直接利用反射进行操作,而支持的方法有两类:
1)取得所有继承而来的属性
取得全部属性 public Field[] getFields()
取得指定属性 public Field getField(String name)
2)取得本类定义的属性
取得本类全部属性public Field getDeclareFields()
取得本类全部属性public Field getDeclareField(String name)
范例:取得一个类之中的属性
interface Message{
String INFO = "hello world.";
}
class Person{
private String name;
private Integer age;
}
class Student extends Person implements Message{
private String school;
private Double price;
}
public class ReflectDemo {
public static void main(String[] args) throws Exception {
Class> clazz = Class.forName("com.weimob.carl.user.reflect.Student");
{ // 取得继承而来的全部属性
System.out.println("取得继承而来的全部属性");
Field[] fields = clazz.getFields();
for(int i = 0; i < fields.length; i++){
System.out.println(fields[i]);
}
}
{ // 取得本类定义的全部属性
System.out.println("取得本类定义的全部属性");
Field[] fields = clazz.getDeclaredFields();
for(int i = 0; i < fields.length; i++){
System.out.println(fields[i]);
}
}
{ // 取得父类属性
System.out.println("取得父类属性");
Field[] fields = clazz.getSuperclass().getDeclaredFields();
for(int i = 0; i < fields.length; i++){
System.out.println(fields[i]);
}
}
}
}
运行结果:
2)在Field类里面还定义有进行属性调用的方法:
1)设置属性内容:public void set(Object obj, Object value)
2)取得属性内容:public Object get(Object obj);
在Constructor、Method、Field三个类上有一个共同的父类 AccessibleOblect,在这个类中定义了可以取消封装操作:public void setAccessible(boolean flag)
class Person{
private String name;
}
public class ReflectDemo {
public static void main(String[] args) throws Exception {
Class> clazz = Class.forName("com.weimob.carl.user.reflect.Person");
Object object = clazz.newInstance();
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(object, "carl");
System.out.println(nameField.get(object));
}
}
运行结果:
在开发之中,只需要灵活使用Class、Constructor、Method、Field就可以使用反射进行一系列操作
的代码实现了。