当你看到这篇文章时请您慢慢的将它看完,相信对您一定会有帮助的!
技术目录
前言
提起反射我想大家第一印象就是牛批!牛批。确实反射技术的确是Java中非常强大的技术了。大多数的框架都是通过反射技术完成的,因此你要想学习一些框架的原理那么反射是必学技术,不多说一起来看看吧!
一、反射的引入
学习反射之前可以首先了解一下反射机制中相关的类java.lang.Class<T> 类的字节码文件对象
java.lang.reflect.Constructor<T> 字节码中的构造方法字节码
java.lang.reflect.Field 字节码中的属性字节码
java.lang.reflect.Method 字节码中的方法字节码
二、获取Class对象
使用反射技术的前提是获取到某个类的字节码文件,而获取这个Class对象的话有三种方式,依次介绍。
特别提醒:使用反射技术的第一步一定是获取该类的字节码对象。获取不到的话你玩个寂寞?
1.Class.forName()
Class.forNam(“完整类名”) :这里要注意的是一定是完整类名,就是包名加类名
代码如下(示例):
Class aClass = Class.forName("java.lang.String");
System.out.println(aClass.getName());
结果:
补充:如果类中有静态代码块的话,Class.forName()会执行静态代码块中的内容
举例:
public class Person{
//定义一个静态代码块
static{
System.out.println("静态代码块执行了");
}
}
//测试
@Test
public void show(){
Class.forName("cn.offcn.constructor.Person");
}
//执行结果:
//静态代码块执行了
2.类名.class
类名.class也获取该类的字节码文件对象,注意使用这种方式一定要留意不同包同类名的情况,还有记得有返回值Class对象
代码如下(示例):
Class aClass = Person.class;
System.out.println(aClass);
执行结果:
3.对象.getClass()
对象.getClass() 要先创建对象后调用getClass方法
Person p = new Person();
Class aClass = p.getClass();
System.out.println(aClass.getName());
这种方法使用的较少,但是需要有了解
下边将是反射技术的重点
三、通过反射实例化对象
我们知道实例化一个对象是通过构造方法进行的,因此我们可以首先要获取到类的构造器。api很多但是技巧性很大容易记住勿偷懒
//我们知道一个类可以是有多个构造方法的
Constructor[] getConstructors() 获取所有的public修饰的构造方法
Constructor[] getDeclaredConstructors() 获取所有的构造方法包括private修饰的
Constructor getConstructor(Class<?> ...parameterType)获取public修饰的指定构造器
Constructor getDeclaredConstructor(Class<?>...parameterType) 获取指定的构造器(可以是private修饰)
上述api我们可以得出不仅可以获取public修饰的构造方法同时也可以获取到private修饰的构造方法。
获取到Constructor对象后使用newInstance(…)创建对象
newInstance(Object... initargs)
使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
实例化对象演示:
数据准备:四个构造方法
package cn.offcn.constructor;
public class Person {
private String name;
private int age;
private String gender;
//为了方便查看都输出一句话
public Person(){
System.out.println("无参数构造方法执行");
}
public Person(String name){
this.name = name;
System.out.println("public修饰的一个参数的构造方法执行了");
}
private Person(String name,int age){
this.name=name;
this.age=age;
System.out.println("private修饰的两个参数的构造方法执行了");
}
public Person(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
System.out.println("public修饰的三个参数的构造方法执行了");
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
'}';
}
}
代码演示:
首先演示通过参数类型指定构造方法
@Test
public void reflect()throws Exception{
//获取Person类的字节码对象
Class<?> aClass = Class.forName("cn.offcn.constructor.Person");
//获取public修饰的指定的构造方法
Constructor<?> constructor = aClass.getConstructor(String.class);
//返回类型是Object需要强转,newInstance("初始化参数")
Person person1 = (Person) constructor.newInstance("aaa");
System.out.println(person1);
//获取private修饰的构造方法
Constructor constructor2 = aClass.getDeclaredConstructor(String.class, int.class);
//因为此时的构造方法时private修饰的需要暴力破解
constructor2.setAccessible(true);
Person person2 =(Person) constructor2.newInstance("张三", 23);
System.out.println(person2);
}
私有方法、构造方法等在初始化时需要暴力反射:setAccessible(true)
来个花样?
案例要求:一次性通过四个构造方法完成四个对象的创建并完成初始化。
import java.lang.reflect.Constructor;
public class Test {
public static void main(String[] args) throws Exception{
Class<?> aClass = Class.forName("cn.offcn.constructor.Person");
//获取所有的构造方法
Constructor[] constructors= aClass.getDeclaredConstructors();
//遍历所有的构造方法
for (Constructor constructor : constructors) {
//设置暴力破解
constructor.setAccessible(true);
switch (constructor.getParameterCount()){
case 0:
System.out.println(constructor.newInstance());
break;
case 1:
System.out.println(constructor.newInstance("lisi"));
break;
case 2:
System.out.println(constructor.newInstance("wangwu",22));
break;
case 3:
System.out.println(constructor.newInstance("haha",23,"男"));
break;
}
}
}
}
结果:
至于这个顺序我的猜想是:它会使用贪婪模式,就是尽可能的先找到能提供多个参数的构造方法,就像是spring容器使用自动注入一样。好吧这只是我的猜想
四、通过反射设置对象属性
要想设置对象的属性,那么就需要首先获取到Field对象
看一下常用api
Field getDeclaredField(String name); //通过属性名获取Field对象
Field getField(String name); //通过属性名获取Field对象只能获取public修饰的
Field[] getDeclaredFields(); //获取所有的属性对象
Field[] getFields(); //获取所有public修饰的对象
获取到Field对象后就可以通过反射为对象设置属性使用:获取到Field对象后使用set(object o,object value)为对象设置属性
此时看这个反射这个词是多么的形象
代码演示:
package cn.offcn.field;
public class Person {
private String name;
private int age;
public double money;
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;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", money=" + money +
'}';
}
}
测试类
//创建一个Person对象,使用getClass获取类的字节码对象
Person p = new Person();
Class aClass = p.getClass();
//获取各个属性Field对象
Field money = aClass.getField("money");
Field name = aClass.getDeclaredField("name");
Field age = aClass.getDeclaredField("age");
//为对象设置值
money.set(p,12.5);
//private修饰的属性值设置值需要暴力破解
age.setAccessible(true);
age.set(p,12);
name.setAccessible(true);
name.set(p,"李四");
System.out.println(p);
结果显示:
注意事项:
①在设置属性值时要确保类型一致。就是该属性的类型时int类型就不要传递一个String类型的字符串
②私有属性设置值时一定要加上setAccessible(true)
不然会报
五、通过反射调用方法
回想一下我们以往是怎么调用方法的。–>创建对象调用对象的方法
接下来看一下反射是怎么做的
首先获取Method对象
Method[] getDeclaredMethods();//获取所有的Method对象不包括父类的
Mothod[] getMethods(); //获取public修饰的对象,包括父类的
Method getDeclaredMethod(String name,Class<T> ...parameterType);//通过方法名和参数类型获取指定的Method对象
Methos getMethos(String name,Class<T>...parameterType)//通过参数名和参数类型获取指定的public修饰的Method对象
//api中需要留意:
//getMethods()是可以获取父类中public修饰的Mothod对象的而getDeclaredMethods()可以获取非public修饰的Mothod对象但是获取不到父类的
获取到Method对象后使用invoke(Object o,Object…args)来调用方法
代码演示:
package cn.offcn.field;
public class Person {
//分别定一个不同权限修饰符修饰的方法
public void show1(){
System.out.println("public修饰的无参数方法");
}
public void show2(String name){
System.out.println("public修饰的一个参数的方法"+name);
}
private void show3(){
System.out.println("private修饰的无参数方法");
}
private void show4(String name,int age){
System.out.println("private修饰的两个参数的方法,姓名:"+name+"年龄:"+age);
}
}
调用方法:
首先创建一个对象->获取Method对象->执行invoke方法
代码演示
public class Test {
public static void main(String[] args) throws Exception {
//获取Person类的Class对象
Class aClass = Class.forName("cn.offcn.field.Person");
//通过无参数构造方法创建对象
Constructor constructor = aClass.getConstructor();
Person person = (Person)constructor.newInstance();
//通过方法名获取Method对象并执行
Method show1 = aClass.getMethod("show1");
show1.invoke(person);
//通过方法名和参数获取指定的Method对象并执行
Method show4 = aClass.getDeclaredMethod("show4", String.class, int.class);
//私有方法需要开启暴力反射
show4.setAccessible(true);
show4.invoke(person,"张三",23);
}
}
运行结果:
六、反射获取注解
反射获取注解个人使用起来用的不太多了解一下吧
Annotation[] getAnnotations();//返回所有注解包括父类的
Annotation getAnnotation(Class<T> Annotation.class);//返回指定的类型的注解,没有返回null
Annotation[] getDeclaredAnnotations();//返回所有注解不包括父类
了解内容,简单举个栗子
//首先自定义一个注解
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String[] value();
}
//定义一个类添加注解
@MyAnnotation(value ={"值1","值2","值3"})
public class Person {
}
//测试
public class Test {
public static void main(String[] args) throws Exception {
Class aClass = Class.forName("cn.offcn.field.Person");
Annotation annotation = aClass.getAnnotation(MyAnnotation.class);
System.out.println(annotation.toString());
}
}
//打印结果:
//@cn.offcn.annotation.MyAnnotation(value=[值1, 值2, 值3])
七、注解获取类的接口
了解内容
Class[] getInterfaces(); //获取所有接口的Class对象
这个知道就行,后边学习的动态代理中会使用到
//创建第一个接口
public interface A{}
//创建第二个接口
public interface B{}
//创建一个类去实现这两个接口
public class C implements A,B{}
//测试
@org.junit.Test
public void test(){
Class c = C.class;
Class[] interfaces = c.getInterfaces();
for (Class anInterface : interfaces) {
System.out.println(c.getSimpleName()+"实现了"+anInterface.getSimpleName());
}
}
//打印结果:
//C实现了A
//C实现了B
八、通过反射完成反编译一个类
接下来我们完成一个综合练习反编译一个类
反编译之前我们要了解Class类中的一些常用方法
----------Class对象中方法--------------------
String getSimpleName(); //获取简单名称
String getName(); //获取全类名
int getModifiers(); //获取修饰符
String getTypeName(); //获取类型名
------------Method对象中的方法-----------
Parameter[] getParameters(); //获取所有的参数
Class getReturnType(); //返回值类型
Modifier.toString(int i) //将权限修饰符转换为字符串
了解一下简单方法我们直接开始定义一个方法用来反编译类
package cn.offcn.field;
import java.lang.reflect.*;
public class Test {
public static void main(String[] args) throws Exception {
//测试ArrayList类的代码
String s = "java.util.ArrayList";
reflect(s);
}
//定义一个反编译的方法
public static void reflect(String name) {
try {
//获取类的Class对象
Class c = Class.forName(name);
//开始打印:
System.out.print(Modifier.toString(c.getModifiers()) +" class " + c.getSimpleName()+" ");
//判断是否继承有父类
Class superclass = c.getSuperclass();
if(!superclass.getSimpleName().equals("Object")){
System.out.print("extends "+superclass.getSimpleName());
}
//判断是否有实现接口
Class[] interfaces = c.getInterfaces();
if (interfaces.length >0){
System.out.print(" implements ");
int k = 0;
for (Class anInterface : interfaces) {
if(++k < interfaces.length){
System.out.print(anInterface.getSimpleName()+",");
}else{
System.out.print(anInterface.getSimpleName());
}
}
k = 0;
}
System.out.println("{");
//获取所有的属性
Field[] Fields = c.getDeclaredFields();
for (Field field : Fields) {
System.out.println(" "+Modifier.toString(field.getModifiers())+" "+field.getType().getSimpleName()+" "+field.getName()+";");
}
System.out.println();
//打印所有的构造方法
Constructor[] constructors = c.getDeclaredConstructors();
int j = 0; //用来判断是否需要加逗号
for (Constructor constructor : constructors) {
System.out.print(" "+Modifier.toString(constructor.getModifiers())+" "+c.getSimpleName()+"(");
Parameter[] parameters = constructor.getParameters();
int num = constructor.getParameterCount();
for (Parameter parameter : parameters) {
if(++j < num){
System.out.print(parameter.getType().getSimpleName()+" "+parameter.getName()+",");
}else{
System.out.print(parameter.getType().getSimpleName()+" "+parameter.getName());
}
}
j = 0; //恢复变量值
System.out.println(");");
}
System.out.println();
//获取所有方法
Method[] methods = c.getDeclaredMethods();
int i =0; //定义一个变量用来判断是否需要加逗号
for (Method method : methods) {
System.out.print(" "+Modifier.toString(method.getModifiers())+" "+method.getReturnType().getSimpleName()+" "+method.getName()
+"(");
int nums = method.getParameterCount();
for (Parameter parameter : method.getParameters()) {
if(++i < nums)
System.out.print(parameter.getType().getSimpleName()+" "+parameter.getName()+",");
else{
System.out.print(parameter.getType().getSimpleName()+" "+parameter.getName());
}
}
i = 0;
System.out.println("){...}");
}
System.out.println();
System.out.println(" }");
} catch (Exception e) {
e.toString();
}
}
}
结果如下:
太多了,只截取部分截图
把这个反编译联系一下,反射技术就掌握一大部分了。。。