反射是框架设计的灵魂
使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码))
一、反射的概述
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.
Java中编译类型有两种:
静态编译:在编译时确定类型,绑定对象即通过。
动态编译:运行时确定类型,绑定对象。动态编译最大限度地发挥了Java的灵活性,体现了多态的应用,可以减低类之间的耦合性。
Java反射是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public、static等)、superclass(例如Object)、实现之interfaces(例如Cloneable),也包括fields和methods的所有信息,并可于运行时改变fields内容或唤起methods。
Reflection可以在运行时加载、探知、使用编译期间完全未知的classes。即Java程序可以加载一个运行时才得知名称的class,获取其完整构造,并生成其对象实体、或对其fields设值、或唤起其methods。
反射(reflection)允许静态语言在运行时(runtime)检查、修改程序的结构与行为。
在静态语言中,使用一个变量时,必须知道它的类型。在Java中,变量的类型信息在编译时都保存到了class文件中,这样在运行时才能保证准确无误;换句话说,程序在运行时的行为都是固定的。如果想在运行时改变,就需要反射这东西了。
实现Java反射机制的类都位于java.lang.reflect包中:
Class类:代表一个类
Field类:代表类的成员变量(类的属性)
Method类:代表类的方法
Constructor类:代表类的构造方法
Array类:提供了动态创建数组,以及访问数组的元素的静态方法
一句话概括就是使用反射可以赋予jvm动态编译的能力,否则类的元数据信息只能用静态编译的方式实现,例如热加载,Tomcat的classloader等等都没法支持。
反射就是把java类中各种信息反射成一个个java对象
例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。
(其实:一个类中这些成员方法、构造方法、在加入类中都有一个类来描述)
如图是类的正常加载过程:反射的原理在与class对象。
熟悉一下加载的时候:Class对象的由来是将class文件读入内存,并为之创建一个Class对象。
二、查看Class类在java中的api详解(1.7的API)
Class 类的实例表示正在运行的 Java 应用程序中的类和接口。也就是jvm中有N多的实例每个类都有该Class对象。(包括基本数据类型)
Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass 方法自动构造的。也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了。
没有公共的构造方法,方法共有64个
三、反射的使用(这里使用Student类做演示)
1、获取Class对象的三种方式
1.1 Object ——> getClass();
1.2 任何数据类型(包括基本数据类型)都有一个“静态”的class属性
1.3 通过Class类的静态方法:forName(String className)(常用)
其中1.1是因为Object类中的getClass方法、因为所有类都继承Object类。从而调用Object类来获取**
常使用第三种获取Class对象,第一种已经new出对象了,第二种需要导入对象的jar包,依赖太强,一般都第三种,一个字符串可以传入也可写在配置文件中等多种方法。
//第一种getClass()
Student student = new Student();
Class class1 = student.getClass();
System.out.println(class1.getName());
//第二种任何对象都有一个静态的class属性
Class class2 = Student.class;
System.out.println(class1 == class1);
//第三种通过类的真实路径获取
try {
Class class3 = Class.forName("com.itcast.demo.reflect.Student");
System.out.println(class2 == class3);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
2、获取构造方法
public class Student {//---------------构造方法-------------------
//(默认的构造方法)
Student(String str){
System.out.println("(默认)的构造方法 s = " + str);
}
//无参构造方法
public Student(){
System.out.println("调用了公有、无参构造方法执行了。。。");
}
//有一个参数的构造方法
public Student(char name){
System.out.println("姓名:" + name);
}
//有多个参数的构造方法
public Student(String name ,int age){
System.out.println("姓名:"+name+"年龄:"+ age);//这的执行效率有问题,以后解决。
}
//受保护的构造方法
protected Student(boolean n){
System.out.println("受保护的构造方法 n = " + n);
}
//私有构造方法
private Student(int age) {
System.out.println("私有的构造方法 年龄:" + age);
}
}
//获取Class对象
Class classStudent = Class.forName("com.itcast.demo.reflect.Student");
//1.获取所有公有构造方法
Constructor[] constructors = classStudent.getConstructors();
System.out.println("**********************所有公有构造方法*********************************");
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
///2.获取所有构造方法(公有、私有、默认、受保护)
Constructor[] declaredConstructors = classStudent.getDeclaredConstructors();
System.out.println("**********************所有的构造方法*********************************");
for (Constructor declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
//3.获取公有无参构造方法,因为是无参的构造方法所以类型是一个null,不写也可以:这里需要的是一个参数的类型,切记是类型
Constructor constructor = classStudent.getConstructor();
Student student = (Student) constructor.newInstance();
System.out.println("student对象"+student);
//4.获取私有构造方法
Constructor declaredConstructor = classStudent.getDeclaredConstructor();
Student studentDeclared = (Student) declaredConstructor.newInstance();
System.out.println("私有构造方法获取对象"+studentDeclared);
System.out.println("私有构造方法"+declaredConstructor);
3、获取成员变量
@Test
public void testVariable()
throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException,
InvocationTargetException, InstantiationException {
//获取Class对象
Class personClass = Class.forName("com.itcast.demo.reflect.Person");
Person person = (Person) personClass.getConstructor().newInstance();
//1获取所有的公有字段
Field[] fields = personClass.getFields();
System.out.println("**********************获取所有的公有字段*********************************");
for (Field field : fields) {
System.out.println(field);
}
//2获取所有字段(私有,受保护,默认,公有)
Field[] declaredFields = personClass.getDeclaredFields();
System.out.println("**********************获取所有的字段*********************************");
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
//3获取某个特定的公有字段
Field name = personClass.getField("name");
System.out.println("**********************获取某个特定的公有字段*********************************");
System.out.println(name);
//4获取某个特定的(公有或者私有)字段
Field name1 = personClass.getDeclaredField("name");
Field age = personClass.getDeclaredField("age");
//对象中添加数据
age.set(person,20);
name.set(person,"北京");
System.out.println(name1);
System.out.println(age);
System.out.println(person);
}
由此可见
调用字段时:需要传递两个参数:
Object obj = stuClass.getConstructor().newInstance();//产生Student对象–》Student stu = new Student();
//为字段设置值
f.set(obj, “刘德华”);//为Student对象中的name属性赋值–》stu.name = “刘德华”
第一个参数:要传入设置的对象,第二个参数:要传入实参
public class Student {
public Student(){
}
//**********字段*************//
public String name;
protected int age;
char sex;
private String phoneNum;
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ", sex=" + sex
+ ", phoneNum=" + phoneNum + "]";
}
@Test
public void testVariable()
throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException,
InvocationTargetException, InstantiationException {
//获取Class对象
Class personClass = Class.forName("com.itcast.demo.reflect.Person");
//创建person
Person person = (Person) personClass.getConstructor().newInstance();
//1获取所有的公有字段
Field[] fields = personClass.getFields();
System.out.println("**********************获取所有的公有字段*********************************");
for (Field field : fields) {
System.out.println(field);
}
//2获取所有字段(私有,受保护,默认,公有)
Field[] declaredFields = personClass.getDeclaredFields();
System.out.println("**********************获取所有的字段*********************************");
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
//3获取某个特定的公有字段
Field name = personClass.getField("name");
System.out.println("**********************获取某个特定的公有字段*********************************");
System.out.println(name);
//4获取某个特定的(公有或者私有)字段
Field name1 = personClass.getDeclaredField("name");
Field age = personClass.getDeclaredField("age");
//对象中添加数据
age.set(person,20);
name.set(person,"上海");
System.out.println(name1);
System.out.println(age);
System.out.println(person);
}
4、获取成员变量
public class Student {
//**************成员方法***************//
public void show1(String s){
System.out.println("调用了:公有的,String参数的show1(): s = " + s);
}
protected void show2(){
System.out.println("调用了:受保护的,无参的show2()");
}
void show3(){
System.out.println("调用了:默认的,无参的show3()");
}
private String show4(int age){
System.out.println("调用了,私有的,并且有返回值的,int参数的show4(): age = " + age);
return "abcd";
}
public static void main(String[] args) throws Exception {
//1.获取Class对象
Class stuClass = Class.forName("fanshe.method.Student");
//2.获取所有公有方法
System.out.println("***************获取所有的”公有“方法*******************");
stuClass.getMethods();
Method[] methodArray = stuClass.getMethods();
for(Method m : methodArray){
System.out.println(m);
}
System.out.println("***************获取所有的方法,包括私有的*******************");
methodArray = stuClass.getDeclaredMethods();
for(Method m : methodArray){
System.out.println(m);
}
System.out.println("***************获取公有的show1()方法*******************");
Method m = stuClass.getMethod("show1", String.class);
System.out.println(m);
//实例化一个Student对象
Object obj = stuClass.getConstructor().newInstance();
m.invoke(obj, "刘德华");
System.out.println("***************获取私有的show4()方法******************");
m = stuClass.getDeclaredMethod("show4", int.class);
System.out.println(m);
m.setAccessible(true);//解除私有限定
Object result = m.invoke(obj, 20);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参
System.out.println("返回值:" + result);
}
由此可见:
m = stuClass.getDeclaredMethod(“show4”, int.class);//调用制定方法(所有包括私有的),需要传入两个参数,第一个是调用的方法名称,第二个是方法的形参类型,切记是类型。
System.out.println(m);
m.setAccessible(true);//解除私有限定
Object result = m.invoke(obj, 20);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参
System.out.println(“返回值:” + result);//
5、获取main方法
public class TestMain {
public static void main(String[] args) {
System.out.println("通过反射获取main方法");
}
}
@Test
public void testMain()
throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException,
InstantiationException {
//获取main方法
Class<?> mainClass = Class.forName("com.itcast.demo.reflect.TestMain");
Method main = mainClass.getMethod("main", String[].class);
//第一个参数,对象类型,因为方法是static静态的,所以为null可以,第二个参数是String数组,这里要注意在jdk1.4时是数组,jdk1.5之后是可变参数
//这里拆的时候将 new String[]{"a","b","c"} 拆成3个对象。。。所以需要将它强转。
TestMain testMain = (TestMain) mainClass.getConstructor().newInstance();
main.invoke(null, (Object)new String[]{"1","2","3"});//方式一
main.invoke(testMain, new Object[]{new String[]{"a","b","c"}});//方式二
System.out.println(main);
}
6、反射方法的其它使用之—通过反射运行配置文件内容
student类:
public class Student {
public void show(){
System.out.println("is show()");
}
}
配置文件以txt文件为例子(pro.txt):
className = cn.fanshe.Student
methodName = show
测试类:
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Properties;
/*
* 我们利用反射和配置文件,可以使:应用程序更新时,对源码无需进行任何修改
* 我们只需要将新类发送给客户端,并修改配置文件即可
*/
public class Demo {
public static void main(String[] args) throws Exception {
//通过反射获取Class对象
Class stuClass = Class.forName(getValue("className"));//"cn.fanshe.Student"
//2获取show()方法
Method m = stuClass.getMethod(getValue("methodName"));//show
//3.调用show()方法
m.invoke(stuClass.getConstructor().newInstance());
}
//此方法接收一个key,在配置文件中获取相应的value
public static String getValue(String key) throws IOException{
Properties pro = new Properties();//获取配置文件的对象
FileReader in = new FileReader("pro.txt");//获取输入流
pro.load(in);//将流加载到配置文件对象中
in.close();
return pro.getProperty(key);//返回根据key获取的value值
}
}
控制台输出:
is show()
6、反射方法的其它使用之—通过反射越过泛型检查
public static void main(String[] args) throws Exception{
ArrayList<String> strList = new ArrayList<>();
strList.add("aaa");
strList.add("bbb");
// strList.add(100);
//获取ArrayList的Class对象,反向的调用add()方法,添加数据
Class listClass = strList.getClass(); //得到 strList 对象的字节码 对象
//获取add()方法
Method m = listClass.getMethod("add", Object.class);
//调用add()方法
m.invoke(strList, 100);
//遍历集合
for(Object obj : strList){
System.out.println(obj);
}
}