一 反射的概述
在JAVA中,当一段代码写完后会经历两个状态,编译状态和运行状态。而反射机制是存在于运行状态中,在运行状态下,对于任意一个类,我们可以知道它的所有属性和方法,对于任意一个对象,我们可以调用它的所有属性和方法;这种动态获取信息和动态调用对象的方法的功能称之为JAVA语言的反射机制。
一般来说,我们在使用某个类时,在编译器期时已经清楚了这个类的结构,可以用来干嘛,而反射就是在运行时才知道要操作的类是什么,可以得到该类的完整结构。
二 反射的作用
在java的反射机制中,我们把java类中的各个成分都映射成一个个java对象,例如一个java类中包含(成员变量 成员方法 构造方法 包 实现的接口 超类),利用反射技术可以对一个类进行解刨,可以到到该类的完整结构,把该类的各个组成部分映射成一个个对象。要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.
三 反射的使用
反射的本质是获得一个类的class对象,从而反向获取该类的所有信息。
先写一个Teacher类,下面用它做演示。
/**
* @author FengTianHao
* @version 1.0
* @since 2018/11/5 9:32
*/
public class Teacher {
//成员变量
private String name;
public int age;
protected char sex;
String PhoneNum;
//默认构造方法
public Teacher(){}
//有一个参数的构造方法
public Teacher(String name)
{
this.name=name;
}
//有多个参数的构造方法
public Teacher(String name,int age)
{
this.name=name;
this.age=age;
}
//受保护的构造方法
protected Teacher(String name,int age,char sex)
{
this.name=name;
this.age=age;
this.sex=sex;
}
//私有的构造方法
private Teacher(int age)
{
this.age=age;
}
//成员方法
public void show1()
{
System.out.println("公有的成员方法");
}
protected void show2()
{
System.out.println("受保护的成员方法");
}
void show3()
{
System.out.println("默认的成员方法");
}
private void show4()
{
System.out.println("私有的成员方法");
}
@Override
public String toString()
{
return "Teacger [name:"+name+" age:"+age+" sex:"+sex+" PhoneNum:"+PhoneNum+"]";
}
}
class对象的获取
/**
* @author FengTianHao
* @version 1.0
* @since 2018/11/12 19:05
*/
/** 获取class对象的三种方式
* 1. 通过Object类中的getclass()方法
* 2. 任何数据类型(包括基本类型)都有一个静态属性class
* 3. 通过class类的静态方法:forname(String classname)
* */
public class TestClass {
public static void main(String []args)
{
//通过getclass方法获得class对象
Teacher t1=new Teacher();
Class teaClass=t1.getClass();
System.out.println(teaClass.getName());
//通过静态属性来获取
Class teaClass2=Teacher.class;
System.out.println(teaClass==teaClass2);//判断第一种方式获得的class对象和第二种方式获得的是否为同一个
//通过froname(String classname)方法来获取
try {
Class teaClass3=Class.forName("Teacher");//该字符串必须是类的真实路径,有包的话格式为:包名.类名
System.out.println(teaClass3==teaClass2);
//判断第三种方式获得的对象是否和第二种获得的为同一个
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
运行结果:
注意:一个类在运行期间只有一个class对象产生;三种方式中通常使用第三种方式,因为第一种已经有啦对象则不需要反射,第二种需要导包。
构造方法的获取
Teacher类的构造方法
//默认构造方法
public Teacher(){}
//有一个参数的构造方法
public Teacher(String name)
{
this.name=name;
}
//有多个参数的构造方法
public Teacher(String name,int age)
{
this.name=name;
this.age=age;
}
//受保护的构造方法
protected Teacher(String name,int age,char sex)
{
this.name=name;
this.age=age;
this.sex=sex;
}
//私有的构造方法
private Teacher(int age)
{
this.age=age;
}
/**
* @author FengTianHao
* @version 1.0
* @since 2018/11/12 19:37
*/
import java.lang.reflect.Constructor;
/** 获取构造方法
* 1.批量获取
* public Constructor[] getConstructors() 获得所有公有的构造方法
* public Constructor[] getDeclaredConstructors() 获得所有的构造方法(公有的 受保护的 默认的 私有的)
* 2.单个获取
* public Constructor getConstructor(Class....parameterTypes) 获取 单个特定参数列表并且公有的构造方法
* public Constructor getDeclaredConstructor(Class...parameterTypes)获取 单个特定参数列表并的构造方法,可以是公有的 受保护的 默认的 私有的
* 调用构造方法
* Constructor中newInstance()方法
*/
public class Constructors {
public static void main(String[] args)throws Exception
{
//获取class对象
Class TeaClass=Class.forName("Teacher");
//获取所有公有的构造方法
Constructor[] conArray=TeaClass.getConstructors();
System.out.println("所有的构造方法:");
for(Constructor a:conArray)
{
System.out.println(a);
}
//获取所有的构造方法
Constructor[] conArray1=TeaClass.getDeclaredConstructors();
System.out.println("所有的构造方法:");
for(Constructor b:conArray1)
{
System.out.println(b);
}
//获得一个公有的无参数的构造方法
Constructor con=TeaClass.getConstructor();
System.out.println("一个公有的无参数的构造方法:"+con);
//获得一个带参数的公有的构造方法
Constructor con1=TeaClass.getConstructor(String.class);
System.out.println("带一个参数的公有的构造方法:"+con1);
//获得私有构造方法并访问(忽略private修饰)
Constructor con2=TeaClass.getDeclaredConstructor(int.class);
con2.setAccessible(true);
Object tea=con2.newInstance(20);
System.out.println(tea);
}
}
运行结果:
成员变量的获取和调用
/**
* @author FengTianHao
* @version 1.0
* @since 2018/11/12 20:21
*/
import java.lang.reflect.Field;
/** 获取成员变量并调用
* 1.批量获取
* public Field[] getFields() 获取所有公有属性
* public Field[] getDeclaredFields() 获取所有属性(公有的 受保护的 默认的 私有的)
* 2.单个获取
* public Field getField(String fieldname) 获取某个公有属性
* public Field getDeclaredField(String fieldname) 获取某个属性(可以是公有的 受保护的 默认的 私有的)
* 设置属性的值
* Field--->public void set(Object obj,Object value)
* obj是要设置值的属性所在的对象
* value是为属性设置的值
*/
public class Fields {
public static void main(String[] args)throws Exception
{
//class对象的获取
Class teacher=Class.forName("Teacher");
//所有公有属性的获取
Field[] fields=teacher.getFields();
System.out.println("所有的共有的属性:");
for(Field a:fields)
{
System.out.println(a);
}
//所有的属性的获取
Field[] fields2=teacher.getDeclaredFields();
System.out.println("所有的属性:");
for(Field b:fields2)
{
System.out.println(b);
}
//单个公有的属性的获取并调用
Field field=teacher.getDeclaredField("name");
field.setAccessible(true);
Object obj=teacher.getConstructor().newInstance();
field.set(obj,"白晓夏");
System.out.println(obj);
}
}
运行结果:
成员方法的获取及调用
成员方法:
//成员方法
public void show1(String name)
{
System.out.println("公有的成员方法"+name);
}
protected void show2()
{
System.out.println("受保护的成员方法");
}
void show3()
{
System.out.println("默认的成员方法");
}
private void show4(int age)
{
System.out.println("私有的成员方法"+age);
}
测试类:
/**
* @author FengTianHao
* @version 1.0
* @since 2018/11/12 20:51
*/
import java.lang.reflect.Method;
/** 成员方法的获取并调用
* 1.批量获取
* public Method[] getMethods() 获取所有的公有的方法
* public Method[] getDeclaredMethods() 获取所有的方法()
* 2.单个获取
* public Method getMethod(String name,Class...parameter)获取单个公有的方法
* name:方法名
* class:方法的形参的class对象
* public Method getDeclaredMethod(String name,class...parameter)获取单个任何方法
* name:方法名
* class:方法的形参的class对象
* 方法的调用
* Method---->Public Object invoke(Object obj,Object..args)
* obj:调用该方法的对象
* args:调用方法时所传递的实参
*/
public class MethodClass {
public static void main(String[]args)throws Exception
{
//获取class对象
Class teacher=Class.forName("Teacher");
//获取所有公共的方法
Method[] methods=teacher.getMethods();
System.out.println("所有的公共的方法:");
for(Method a:methods)
{
System.out.println(a);
}
//获取所有的方法(共有的 受保护的 默认的 私有的)
Method[] methods1=teacher.getDeclaredMethods();
System.out.println("所有的方法:");
for(Method b:methods)
{
System.out.println(b);
}
//获取单个方法并调用
Method m=teacher.getDeclaredMethod("show1", String.class);
System.out.println(m);
Object obj=teacher.getConstructor().newInstance();
m.invoke(obj,"白晓夏");
}
}
运行结果:
"C:\Program Files\Java\jdk1.8.0_73\bin\java" "-javaagent:D:\Software\IDEA\IntelliJ IDEA 2018.1\lib\idea_rt.jar=7382:D:\Software\IDEA\IntelliJ IDEA 2018.1\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_73\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_73\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_73\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_73\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_73\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_73\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_73\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_73\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_73\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_73\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_73\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_73\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_73\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_73\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_73\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_73\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_73\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_73\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_73\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_73\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_73\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_73\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_73\jre\lib\rt.jar;D:\ideproject\Test\out\production\Test" MethodClass
所有的公共的方法:
public java.lang.String Teacher.toString()
public void Teacher.show1(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 Teacher.toString()
public void Teacher.show1(java.lang.String)
void Teacher.show3()
private void Teacher.show4(int)
protected void Teacher.show2()
public void Teacher.show1(java.lang.String)
公有的成员方法白晓夏
Process finished with exit code 0
四反射的应用实例
通过反射运行配置文件内容
/**
* @author FengTianHao
* @version 1.0
* @since 2018/11/13 19:22
*/
import java.io.FileReader;
import java.lang.reflect.Method;
import java.util.Properties;
/**
* 我们利用反射和配置文件时,可以使应用程序无需修改源码
* 将新类发送给客户端,同时修改配置文件就好
*/
public class Demo {
public static void main(String[] args)throws Exception
{
//获取class对象
Class teacher=Class.forName(getValue("classname"));
//获取show1()方法
Method m=teacher.getMethod(getValue("methondname"),String.class);
//调用show1()方法
m.invoke(teacher.getConstructor().newInstance(),"冯天浩");
}
//此方法接受一个key在配置文件里面获取相应的value
public static String getValue(String key) throws Exception
{
Properties properties=new Properties();//获取配置文件对象
FileReader in=new FileReader("D:/ideproject/Test/src/pro.txt");//获取输入流
properties.load(in);//将输入流加入配置文件对象
in.close();//关闭输入流
return properties.getProperty(key); //返回根据key获取的value值
}
}
show1()方法
pro.txt文件内容
运行结果:
变动:当我们系统升级时需要用新类替换原来的类时,源代码不用改变,只需改变配置文件内容;
新类
修改配置文件内容:
运行结果:
通过反射越过泛型检查
测试类
/**
* @author FengTianHao
* @version 1.0
* @since 2018/11/13 19:22
*/
import java.io.FileReader;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Properties;
/**
* 通过反射越过泛型检查
* 如何给一个有Interger泛型的集合添加一个String类型的元素?
*/
public class Demo {
public static void main(String[] args)throws Exception {
ArrayList<Integer> array=new ArrayList<>();
array.add(111);
array.add(222);
Class arr=array.getClass();
Method m=arr.getMethod("add",Object.class);
m.invoke(array,"小机灵鬼");
for(Object a:array)
{
System.out.println(a);
}
}
}
运行结果: