反射就是从内存当中找类的相关信息,从而构建一个对象,然后可以执行一系列的操作
简单说一下,类的字节码文件被加载到内存之后,就会构建一个Class对象,来保存相关类的信息与接口,我们反射的目的就是拿到这个Class对象进行操作。
既然是对象,那么内部就会有很多功能,有什么功能推也能推测出来,对象嘛,就有什么涉及成员变量的,成员方法的,还有构造方法的一系列操作
下面列举出这些方法:
话不多说,直接上代码:
Reflect1.java
package reflect;
import domain.Person;
import java.lang.reflect.Field;
public class Reflect1 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
//获取Person的class对象
Class personClass = Person.class;
//获取所有public修饰的变量
Field[] fields = personClass.getFields();
//把这些打出来
for(Field data : fields) {
System.out.println(data);//这里只能找到public String a,因为只有它是public的
}
System.out.println("-------------");
//根据反射,来获取对象的值
Field a = personClass.getField("a");
Person person = new Person();
//给a成员属性赋一个值
person.a = "张三风";
Object o1= a.get(person);
System.out.println(o1);//打印张三风
System.out.println("-------------");
//通过反射来操作对象,给person对象赋值
a.set(person,"张三");
System.out.println(person);
System.out.println("-----------------");
//获取所有变量(不管啥属性)
Field[] fields2 = personClass.getDeclaredFields();
for(Field data : fields2) {
System.out.println(data);
}
System.out.println("------------");
//获取私有变量来破解赋值
personClass.getDeclaredField("d");
//获取
Field fields3 = personClass.getDeclaredField("age");
fields3.setAccessible(true);//暴力破解,才可以为私有属性赋值
fields3.set(person,20);
Object value = fields3.get(person);
System.out.println(person);
System.out.println("------------------------");
}
}
运行结果:
上面也就很明显,Field对象中get与set方法,都要带上已经声明的对象,否则我们不知道是得到或者设置哪一个对象的中属性的值。
下面还需要来说明一下,这个传入的是啥:
可变函数参数,是一个Class类型,所以,参数也是传入该参数在内存中的class对象,别忘了,数据类型就是一个对象。
代码实例:
首先看一下我们放在domain下面的一个类:
package domain;
public class Person {
private String name;
private int age;
public String a;
protected String b;
String c;
private String d;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", a='" + a + '\'' +
", b='" + b + '\'' +
", c='" + c + '\'' +
", d='" + d + '\'' +
'}';
}
public void eat(){
System.out.println("eat...");
}
public void eat(String food){
System.out.println("eat..."+food);
}
}
然后利用反射来构造一个对象
package reflect;
import domain.Person;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Reflect2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//获得class类对象,类文件
Class person = Class.forName("domain.Person");
//根据反射创建一个对象
Constructor[] cons = person.getConstructors();
for(Constructor data : cons) {
System.out.println(data);
}
System.out.println("-----------");
//利用反射构造一个对象
Constructor cons1 = person.getConstructor(String.class,int.class);
Object obj = cons1.newInstance("王五",20);
Person p1 = (Person)obj;
System.out.println(p1.getName());
}
}
运行结果:
上面可以看到传入getConstructor(类名.class...)这个就是传入了参数的Class对象,然后调用newInstance(传入参数值),这样就构造了一个class对象。
这里在多说一下,对于Class类型饿对象,他还有如下的一个方法
他会默认去调用无参构造创建一个相应的T类型的对象出来。
利用反射来执行某个对象的方法:
package reflect;
import domain.Person;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Reflect3 {
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
//得到Class类对象
Class obj = Person.class;//得到对象
Method[] method = obj.getDeclaredMethods();
for(Method data : method) {
System.out.println(data);
data.setAccessible(true);
}
System.out.println("-------");
//得到具体某一个方法,然后执行
Method method_eat = obj.getMethod("eat");//没有参数
Person p = new Person();
// method_eat.setAccessible(true);暴力破解对方法无效
method_eat.invoke(p);
}
}
运行结果:
在来看一个实例:
根据配置文件来找构建一个对象,然后执行一个方法:
什么是配置文件:以.properties结尾的文件,我们需要把这个配置文件以文件流的形式加载到内存中,传给Properties对象来获取相关信息。
上面包含了加载的对象与需要执行的方法,下面看整体代码:
package reflect;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
public class ReflectTest {
public static void main(String[] args) throws IOException, NoSuchMethodException, ClassNotFoundException, IllegalAccessException, InvocationTargetException, InstantiationException {
//加载Properties对象
Properties pro = new Properties();
//获得一个类加载器,去寻找配置文件,你都能找到类文件还找不到配置文件
ClassLoader classLoader = ReflectTest.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("pro.properties");
//加载配置文件
pro.load(is);
//在配置文件中定义数据
String className= pro.getProperty("className");
String methodName = pro.getProperty("methodName");
//加载该类进内存,找到这个类的字节码对象
Class cls = Class.forName(className);
Constructor cons = cls.getConstructor();
//获得一个obj对象
Object obj = cons.newInstance();
//获取方法对象
Method method = cls.getMethod(methodName);
//再来执行方法
method.invoke(obj);
}
}
运行结果:
简单说一下注解对象的获取
对于Java中使用@
标明的注解,它们的底层实现都隐式地继承java.lang.annotation.Annotation
接口。
我们可以通过上面两个方法获取注解对象
比如,我们判断某一个类上面有没有这个注解
传入注解的Class对象,注意这个对象是一个注解对象
其实这个函数接收的时候,里面泛型是会用一个Class<? extends Annotation> annotationClass
为什么要加这样一个泛型<? extends Annotation>
我们先来看一下上面的源码
然后继续追进去
里面就是调用了一个getAnnotation()方法,只要这个对象不等于null,就返回真,反正就是只要返回这个对象
继续追进去
他会给我们返回一个A类型的对象,这个对象必须是Annotation的对象
说一下强制类型转换如果类型不相关联,就会抛出一个ClassCastException异常,所以这里返回的一定与你传入的Class注解对象相互关联的对象。