Java中的反射技术是一种非常重要的技术,能够动态操作Java代码程序,在目前大量的框架中都有应用,所以掌握反射是十分必要,本博客主要整理反射技术的使用。
在《Java核心技术》定义反射:
能够分析类能力的程序称为反射(reflective)。反射机制的功能及其强大,在下面可以看到,反射机制可以用来:在运行中分析类的能力。在运行中查看对象,例如,编写一个toString方法供所有类使用。实现通用的数组操作代码。利用Method对象,这个对象很像C++中的函数指针。
总而言之,反射就是可以在代码运行时剖析类中的信息。
在反射中总是伴随着Class
类,可以说Class
类是反射的基石。在Java中我们编写一个类就会产生一个.class
文件,其中就会产生一个Class
对象,主要用于保存该类的类型信息。而我们也是从Class
对象中获取到我们需要的信息。
测试People类,下面的所有关于反射的测试均使用该测试类
package com.refleat;
@Controller("peopleController")
public class People {
@BaseValue("NAME")
private String name;
private Integer age;
public String sex;
public People() {
super();
}
public People(String name, Integer age,String sex) {
super();
this.name = name;
this.age = age;
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "People [name=" + name + ", age=" + age + ", sex=" + sex + "]";
}
@BaseMethod("hi")
public void say(){
System.out.println("say hi");
}
public static void sayHello(){
System.out.println("say Hello");
}
private void sayOK(){
System.out.println("say OK");
}
public void saySome(String str){
sayOK();
System.out.println("say"+str);
}
}
反射最重要的就是Class
对象,而且我们首先也是需要获取到Class
对象,才能接下来的操作。这里主要有三种方式:
Class> clazz=Class.forName("类名")
根据给定的类名获得,主要用于类加载Class> clazz=类名.calss
如果是明确地获得某个类的Class对象,主要用于传参Class> clazz=对象.getClass()
如果得到了对象,不知道是什么类型,主要用于获得对象的具体类型测试
try {
Class> class1 = Class.forName("com.refleat.People");
System.out.println(class1);
Class> class2 = People.class;
System.out.println(class2);
People people = new People();
Class> class3=people.getClass();
System.out.println(class3);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
结果
class com.refleat.People
class com.refleat.People
class com.refleat.People
额外点
//利用==运算符可以实现两个类对象比较操作
System.out.println(class3==class1);//true
//class类对象还有newInstance方法快速创建一个类的实例,
//主要调用无参构造器来初始化新创建的对象。当然没有无参构造器就会抛异常。
People people=Class.forName("com.refleat.People").newInstance();
测试获取所有构造方法
//获取所有构造方法
Constructor>[] constructors=People.class.getConstructors();
for (Constructor> constructor : constructors) {
System.out.println(constructor);
}
结果
public com.refleat.People()
public com.refleat.People(java.lang.String,java.lang.Integer,java.lang.String)
测试获取特定的构造方法
//获取特定的构造方法,主要用参数来确定
try {
//单个构造方法的反射
Constructor constructor = People.class
.getConstructor(String.class,Integer.class,String.class);
System.out.println(constructor);
//使用构造函数实例化对象
People people = constructor.newInstance("xxa",12,"女");
System.out.println(people);
} catch (NoSuchMethodException | SecurityException e) {
e.printStackTrace();
}
}
结果
public com.refleat.People(java.lang.String,java.lang.Integer,java.lang.String)
People [name=xxa, age=12, sex=女]
测试获取所有的类属性
//该方法只能获取公有属性,不能获取私有属性
Field[] fields = People.class.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("=========");
//获取所有属性
Field[] fields1 = People.class.getDeclaredFields();
for (Field field : fields1) {
//表明屏蔽Java语言的访问检查
field.setAccessible(true);
System.out.println(field);
}
结果
public java.lang.String com.refleat.People.sex
=========
private java.lang.String com.refleat.People.name
private java.lang.Integer com.refleat.People.age
public java.lang.String com.refleat.People.sex
测试获取类中的属性值
try {
People people = new People("xxa",15,"nv");
System.out.println("原值:"+people);
//获取公有属性
System.out.println("============");
Field fieldSex=people.getClass().getField("sex");
System.out.println(fieldSex.get(people));
//获取私有属性名
System.out.println("============");
Field fieldAge=people.getClass().getDeclaredField("age");
fieldAge.setAccessible(true);
Integer oldValue = (Integer)fieldAge.get(people) ;
System.out.println(oldValue);
//修改属性中的值
System.out.println("============");
Integer newValue = oldValue + 15;
fieldAge.set(people, newValue);
System.out.println("修改后值:"+people);
} catch (IllegalArgumentException | IllegalAccessException
| NoSuchFieldException | SecurityException e) {
e.printStackTrace();
}
结果
原值:People [name=xxa, age=15, sex=nv]
============
nv
============
15
============
修改后值:People [name=xxa, age=30, sex=nv]
测试获取所有的类方法
//返回所有的公有方法,包括从超类继承来的公有方法
Method[] methods = People.class.getMethods();
for (Method method : methods) {
System.out.println(method);
}
//返回这个类或接口的全部方法,但不包括由超类继承了的方法
System.out.println("=========");
Method[] methods1 = People.class.getDeclaredMethods();
for (Method method : methods1) {
System.out.println(method);
}
结果
public java.lang.String com.refleat.People.toString()
public java.lang.String com.refleat.People.getName()
public void com.refleat.People.setName(java.lang.String)
public void com.refleat.People.say()
public static void com.refleat.People.sayHello()
public void com.refleat.People.setAge(java.lang.Integer)
public java.lang.Integer com.refleat.People.getAge()
public void com.refleat.People.saySome(java.lang.String)
public java.lang.String com.refleat.People.getSex()
public void com.refleat.People.setSex(java.lang.String)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
=========
public java.lang.String com.refleat.People.toString()
public java.lang.String com.refleat.People.getName()
public void com.refleat.People.setName(java.lang.String)
public void com.refleat.People.say()
public static void com.refleat.People.sayHello()
private void com.refleat.People.sayOK()
public void com.refleat.People.setAge(java.lang.Integer)
public java.lang.Integer com.refleat.People.getAge()
public void com.refleat.People.saySome(java.lang.String)
public java.lang.String com.refleat.People.getSex()
public void com.refleat.People.setSex(java.lang.String)
测试调用方法
try {
//无参方法
Method sayMethod = People.class.getMethod("say");
People people = new People();
sayMethod.invoke(people);
System.out.println("============");
//有参方法
Method saySome = People.class.getMethod("saySome",String.class);
saySome.invoke(people,"kkk");
System.out.println("============");
//调用私有方法
Method sayOK= People.class.getDeclaredMethod("sayOK");
sayOK.setAccessible(true);
sayOK.invoke(people);
System.out.println("============");
//如果invoke第一个方法的参数是null,那么该Method对象对应的是一个静态方法
Method sayHello = People.class.getMethod("sayHello");
sayHello.invoke(null);
} catch (NoSuchMethodException | SecurityException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
结果
say hi
============
say OK
saykkk
============
say OK
============
say Hello
首先自定义注解:
Controller.java
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE})//注解在类上
@Retention(RetentionPolicy.RUNTIME)//生命周期是在运行时有效
@Documented
public @interface Controller {
String value() default "";
}
BaseMethod.java
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD})//注解在方法
@Retention(RetentionPolicy.RUNTIME)//生命周期是在运行时有效
@Documented
public @interface BaseMethod {
String value() default "";
}
BaseValue.java
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD})//注解在参数上
@Retention(RetentionPolicy.RUNTIME)//生命周期是在运行时有效
@Documented
public @interface BaseValue {
String value() default "";
}
其中的几个元注解的意义
@Documented
表示将此注解包含在javadoc
中 ,它代表着此注解会被javadoc
工具提取成文档。@Inherited
表示允许子类继承父类中的注解。@Target
表示该注解用于什么地方。ElemenetType.CONSTRUCTOR
构造器声明 、ElemenetType.FIELD
域声明(包括 enum 实例)、ElemenetType.LOCAL_VARIABLE
局部变量声明 、ElemenetType.METHOD
方法声明 、ElemenetType.PACKAG
包声明 、ElemenetType.PARAMETER
参数声明 、ElemenetType.TYPE
类,接口(包括注解类型)或enum声明。@Target
表示在什么级别保存该注解信息。RetentionPolicy.SOURCE
在源文件中有效,注解将被编译器丢弃、RetentionPolicy.CLASS
在class文件中有效,但会被VM丢弃、RetentionPolicy.RUNTIME
在运行时有效,因此可以通过反射机制读取注解的信息。测试获取注解
Class> clazz = People.class;
//获取该类上的所有注解
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) {
if(annotation.annotationType()==Controller.class){
Controller an = clazz.getAnnotation(Controller.class);
System.out.println("Controller==value:"+an.value());
}
System.out.println(annotation);
}
System.out.println("=========");
//判断该类上是否有controller注解
if(clazz.isAnnotationPresent(Controller.class)){
Controller an = clazz.getAnnotation(Controller.class);
System.out.println(an.value());
}
System.out.println("=========");
//获取属性上的注解
Field[] fields =People.class.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
if(field.isAnnotationPresent(BaseValue.class)){
BaseValue bv = field.getAnnotation(BaseValue.class);
System.out.println("BaseValue==value:"+bv.value());
}
}
System.out.println("=========");
//获取方法上的注解
Method[] methods = People.class.getDeclaredMethods();
for (Method method : methods) {
method.setAccessible(true);
if(method.isAnnotationPresent(BaseMethod.class)){
BaseMethod bm= method.getAnnotation(BaseMethod.class);
System.out.println("BaseMethod==value:"+bm.value());
}
}
结果
Controller==value:peopleController
@com.refleat.Controller(value=peopleController)
=========
peopleController
=========
BaseValue==value:NAME
=========
BaseMethod==value:hi
java.lang.reflect
包中的Array
类允许动态地创建数组。这个有个经典用法可用于扩展已经填满的数组。
我去翻了翻JDK8源码,:
Array.java
public static T[] copyOf(U[] original, int newLength, Class extends T[]> newType) {
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
其中的Array.newInstance(componentType,newLength)
是关键能够构造新数组,提供两个参数一个数组的元素类型,数组的长度。
《Java核心技术》的一段比较好的代码,实现扩展任意类型的数组
public static Object goodCopyOf(Object a,int newLength){
Class cl = a.getClass();
if(!cl.isArray()) return null;
Class componentType = cl.getComponentType();
int length = Array.getLength(a);
Object newArray = Array.newInstance(componentType,newLength);
System.arraycopy(a,0,newArray,0,Math.min(length,newLength));
return newArray;
}