参考博文:
https://blog.csdn.net/u011679785/article/details/98853403
一般情况下,我们使用某个类时必定知道它是什么类,是用来做什么的。于是我们可以直接对这个类进行实例化,之后使用这个类对象进行操作。
Student s1 = new Student();
s1.setAge(20);
1、反射定义
Java反射机制是在运行状态中,
对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意方法和属性;
这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
2、类加载机制
.java源文件通过javac编译成.class字节码文件之后,结构不变,还是存储在硬盘中,字节码文件通过一个叫类加载器的东西,将类加载到内存当中去。类加载器中有一个class对象,封装了类的基本信息,包括属性、构造方法、普通方法。
3)、运行时阶段
在源代码阶段,我们需要创建对象时直接new User()就可以了。
3、但是如果在运行时想要创建对象或者操作字段,应该如何做?
---引出反射
(1)、作用:
在运行时判断任意一个对象所属的类型。
在运行时构造任意一个类的对象。
在运行时判断任意以一个类所具有的成员变量和方法。
在运行时调用任意一个对象的方法,甚至可以调用private方法。
(2)、用途:
数据库加载驱动。
Spring中通过xml配置文件加载不同的对象或类。
(3)、优点:
可以在程序运行的过程中去操作对象、字节码文件,不需要重新编译。
提高程序扩展性、复用性。
(4)、缺点:
反射操作的效率比非反射操作低得多。我们应该避免在经常被执行的代码或性能要求很高得程序中使用反射。
反射机制主要应用在对灵活性和扩展性要求很高的系统框架上,普通程序不建议使用。
一切反射相关的代码都从获得java.lang.Class类对象开始。
4、反射应用场景
5、反射的使用
(1)、获取Class对象的三种方式、获取类属性、类方法、构造方法
package com.guo.demo;
public class User {
private String id;
private String name;
private int age;
public String publicName;
protected String protectedName;
//构造方法、setter、getter、toString
}
package com.guo.demo;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
public class Test {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
User user = new User();
// Class clazz = new Class(); //报错 私有的构造方法
Class clazz = null;
try {
clazz = Class.forName("com.guo.demo.User");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println("***********1)、获取类对象的方式(3种)***********");
//1、通过Class.forName(类名(类的完整名字) String className)
System.out.println(clazz); //结果:class com.guo.demo.User
//2、通过类名.class
clazz = User.class;
System.out.println(clazz); //结果:class com.guo.demo.User
//3、通过对象名.getClass()来获取类对象
//clazz 类对象, user实例对象
clazz = user.getClass();
System.out.println(clazz); //结果:class com.guo.demo.User
System.out.println("************2)、获取类属性****************");
Field[] fields = clazz.getDeclaredFields(); //获取类当中声明(Declared)的所有属性
for(Field f:fields) {
System.out.println(f.getName()+":"+f.getType());
}
System.out.println("\n*****getFields()只能输出public修饰的属性**********");
fields = clazz.getFields();
for(Field f:fields) {
System.out.println(f.getName()+":"+f.getType());
}
System.out.println("\n************3)、获取类方法****************");
Method[] methods = clazz.getMethods();
for(Method m:methods) {
System.out.println(m.getName()+":"+m.getReturnType()+":"+
Arrays.toString(m.getParameterTypes()));
}
System.out.println("\n************getDeclaredMethods()获取本类的方法,不包括父类的方法***********");
methods = clazz.getDeclaredMethods();
for(Method m:methods) {
System.out.println(m.getName()+":"+m.getReturnType()+":"+
Arrays.toString(m.getParameterTypes()));
}
System.out.println("\n*****************4)、获取构造方法********************");
Constructor[] cs = clazz.getConstructors();
for(Constructor c:cs) {
System.out.println(c.getName()+":"+Arrays.toString(c.getParameterTypes()));
if(c.getParameterCount()==0) { //无参构造
User user1 = (User) c.newInstance();
System.out.println(user1);
}else {
Object[] param = new Object[c.getParameterCount()];
int i=0;
for(Class c1:c.getParameterTypes()) {
if(c1==int.class) {
param[i]=28;
}else if(c1==String.class) {
param[i]="tom";
}
i++;
}
User user2 = (User) c.newInstance(param);
System.out.println(user2);
}
}
}
}
控制台输出
(2)、通过反射运行配置文件内容
写一个框架,在不改变任意类的前提下,可以帮我们创建任意类的对象,并且执行其中任意的方法。
package com.guo.reflectionDemo;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;
/*
写一个框架,在不改变任意类的前提下,可以帮我们创建任意类的对象,并且执行其中任意的方法。
步骤:
1、将需要创建的对象的全类名和需要执行的方法定义在配置文件中
2、在程序中加载读取配置文件
3、使用反射技术来加载文件到内存
4、创建对象
5、执行方法
*/
public class ReflectUtil {
public static void main(String[] args) throws Exception{
//可以创建任意类,可以执行任意方法
//1、加载配置文件
//1.1创建properties文件
Properties pro = new Properties();
//1.2加载配置文件
//1.2.1获取class目录下的配置文件
ClassLoader classLoader = ReflectUtil.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("pro.properties");
pro.load(is);
//2、获取配置文件中定义的数据
String className = pro.getProperty("className");
String methodName = pro.getProperty("methodName");
//3、加载该类到内存
Class> cls = Class.forName(className);
//4、创建对象
Object o = cls.newInstance();
//5、获取方法对象
Method method = cls.getMethod(methodName);
//6、执行方法
method.invoke(o);
}
/*
这样就在不改变原有代码的条件下,仅通过修改properties的文件就执行了相应的方法,
可以原来的方法只需要两行就能调用run方法,为什么要写一大堆反射的东西还要弄properties呢?
因为将来在开发大型项目的时候,如果修改源代码中的一行代码,是需要重新测试的,
通过反射加载配置文件,仅仅修改物理文件properties是不会对源码产生影响的,可以节约大量的时间。
*/
}
pro.perties
className=com.guo.reflectionDemo.User
methodName=run
User.java
package com.guo.reflectionDemo;
public class User {
private String id;
private String name;
private int age;
public String publicName;
protected String protectedName;
//构造方法、setter、getter
public void run() {
System.out.println("我是User类的run()...");
}
}