引言
初学Java反射机制的时候,只是感觉很神奇,但是不知道学了该怎么用,所以过了一段时间就忘得差不多了;最近接触到了框架,在学习中遇到了反射,深深体会到了反射机制的神奇,回来复习了一下反射机制,写一个小总结;
概念
反射机制是什么?百度百科很好的解释了:
Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。
这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。
在这里我也就不细说概念了,在使用中你就可以慢慢明白了;
下面我来展示一下Java反射机制的基本操作;
注:为了使代码简明易看,所有的异常处理直接抛出Exception(实际应用不要这样!!)
获取类型
在Java中有一种类型叫做Class,这是一种类的类型,和String、Object一个意思,所以不要和类弄混了;
我们可以通过反射机制获取一个类的类型,返回值类型就是Class
获取类型有三种方法:
- Class静态方法forName()
- 对象.getClass
- 类名.class
下面是代码演示:
// 获取不同类型(Class)的三种方式:Class静态方法forName、对象.getClass()、类名.class
// Class也是一个类类型,和String、Object一样
// java.lang.Class:Class代表整个字节码,代表一个类型,代表整个类
public class ReflectTest01 {
public static void main(String[] args) throws Exception {
// 第一种:通过Class的静态方法:forName获取Class类型
Class c1 = Class.forName("java.lang.String"); // String类型
Class c2 = Class.forName("java.lang.Object"); // Object类型
System.out.println(c1 == c2); // String类型和Object类型不同, false
//==============================================================
// 第二种:(对象.getClass())java中任何一个对象都有这个方法:getClass(),返回值类型是Class类型
Class c3 = "444".getClass(); // c5是String类型
Class c4 = new Object().getClass(); // c6是Object类型
System.out.println(c1 == c3); // true
System.out.println(c2 == c4); // true
//=========================================================
// 第三种:(类名.class)java中任何一种类型,包括基本数据类型,都有一个.class属性
Class c5 = String.class; // String类型
Class c6 = Object.class; // Object类型
System.out.println(c1 == c5); // true
System.out.println(c2 == c6); // true
}
}
这三种方法中主要留意的就是第一种,forName()方法的使用规则是:
这里需要记住forName()方法可以使对应的类加载,类加载就会执行静态代码块;
所以如果只想执行静态代码块,那么可以使用forName()方法;
为什么说这一点呢?因为JDBC注册驱动中将会用到这一点;
下面来展示一下这个特性:
forName()传入的类:
public class MyClass {
static {
System.out.println("静态代码块执行了!!");
}
}
// 结论:如果只想要一个类的静态代码块执行,其余代码都不执行,可以使用:
// Class.forName("完整类名")
// 这个方法的执行会导致类加载,类加载时静态代码快执行
// *****这一点会在后面JDBC中用到*****
public class ReflectTest03 {
public static void main(String[] args) throws Exception{
// Class.forName()导致类加载,静态代码快执行
Class.forName("reflect.test03.MyClass");
}
}
输出:
当然这里只是简单模拟了一下,但是原理都是一样的;
结论:如果只想要一个类的静态代码块执行,其余代码都不执行,可以使用:Class.forName(“完整类名”)
实例化对象
当获取到对象类型后就可以通过该类型实例化对应的对象,用到newInstance()
方法
在框架一般都是需要通过反射机制获取配置文件内容并进行操作的,通过配置文件内容可以获取不同信息,下面演示一下如何通过配置文件实例化对象;
注:这里使用.properties文件,简单易懂
创建一个.properties文件:
这个配置文件需要放到该项目的目录下,不能放到类路径(src)或者其他目录下;
import java.io.FileInputStream;
import java.util.Properties;
// 通过反射机制获取配置文件内容并实例化对象
// 反射机制让程序更加灵活,符合OPC开闭原则
// 高级框架(Spring等)底层原理都是反射机制
public class ReflectTest02 {
public static void main(String[] args) throws Exception{
FileInputStream fis = new FileInputStream("classname.properties"); // 创建输入流
Properties properties = new Properties(); // 底层实现是Map,继承了HashTable,key和Value都是String类型
properties.load(fis); // 加载Java的配置文件classname.properties到内存中
fis.close(); // 关闭输入流
String classname = properties.getProperty("first"); // 通过key得到Value
Class<?> clazz = Class.forName(classname); // 获取classname(Value值)的类型
Object obj = clazz.newInstance(); // 创建对象实例
System.out.println(obj);
}
}
因为这里的key传入的是first,所以输出为:
可以看出实例化了一个Object对象
还有另外一种方法,是比较常用简单的方法:
java.util包下提供了一个资源绑定器,便于获取属性配置文件中的内容;
需要使用该类的两个方法:
创建一个配置文件:
使用这种方式的时候,配置文件必须放到类路径(src)下
import java.util.ResourceBundle;
// 使用以下这种方式的时候,属性配置文件xxx.properties必须放到类路径下。
// 实现和ReflectTest02一样的功能,但是又通过新的获取绝对路径的方法,而且以后常用这个方法
public class ReflectTest05 {
public static void main(String[] args) throws Exception {
// 资源绑定器,只能绑定xxx.properties文件。并且这个文件必须在类路径(src文件夹)下。文件扩展名也必须是properties
// 并且在写路径的时候,路径后面的扩展名.properties不能写。
ResourceBundle bundle = ResourceBundle.getBundle("absolute_path"); // 绑定配置文件
String classname = bundle.getString("second"); // 通过key得到Value
Class c = Class.forName(classname);
Object obj = c.newInstance();
System.out.println(obj);
}
}
这次key传入的是second,输出为:
反射机制的一些操作
下面展现反射机制的一些操作,其实就是各种方法的调用,会查文档就好;
这里先定义一个类,下面所有的操作都基于这个类;
public class TestClass01 {
int age;
public String name;
private final static int num = 4;
static protected boolean flag;
public void firstMethod(String s) {
System.out.println("第一个方法的调用!");
}
public void secondMethod(String s, int i) {
System.out.println("第二个方法的调用!");
}
}
反射TestClass类中所有Field(属性)
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class ReflectTest06 {
public static void main(String[] args) throws Exception {
// 获取整个类
Class testClass = Class.forName("reflect.test06.TestClass01");
// 获取类名
String classname = testClass.getName(); // 完整类名
System.out.println(classname);
System.out.println(testClass.getSimpleName()); // 简单类名
// 获取TestClass类的属性
// getFields()只会获取所有public修饰的属性
Field[] fields01 = testClass.getFields();
Field f = fields01[0];
String filename = f.getName(); // 获取属性名
System.out.println(filename);
System.out.println("=====================");
// 获取所有属性
Field[] fields02 = testClass.getDeclaredFields();
for (Field i : fields02) {
System.out.println(i.getName()); // 获取属性名
// 获取属性的类型
System.out.println(i.getType().getName()); // 完整类型
System.out.println(i.getType().getSimpleName()); // 简单类型
// 获取修饰符(用到了Modifier类的toString方法)
System.out.println(Modifier.toString(i.getModifiers()));
System.out.println("------------");
}
}
}
输出:
reflect.test06.TestClass01
TestClass01
name
=====================
age
int
int
------------
name
java.lang.String
String
public
------------
num
int
int
private static final
------------
flag
boolean
boolean
protected static
------------
获取对象属性中的值
import java.lang.reflect.Field;
public class ReflectTest07 {
public static void main(String[] args) throws Exception {
Class className = Class.forName("reflect.test06.TestClass01"); // 获取类
Object obj = className.newInstance(); // 通过类创建对象
Field ageFiled = className.getDeclaredField("age"); // 获取age属性
// 给属性赋值需要有实例化的对象obj
ageFiled.set(obj, 111); // 给obj对象的age属性复制111
System.out.println(ageFiled.get(obj)); // 获取obj对象的age属性的值
}
}
输出:
获取方法并调用(重要)
这个相对重要,获取方法用到了Class类的getMethod()方法或者getMethods()
返回类型Method:
同样获取方法后需要调用,使用Method类的一个方法:invoke()
下面是具体使用方法:
import java.lang.reflect.Method;
// 通过反射机制调用方法
public class ReflectTest08 {
public static void main(String[] args) throws Exception{
// 先获取类
Class c = Class.forName("reflect.test06.TestClass01");
// 通过类实例化对象
Object obj = c.newInstance();
// 通过类获取方法
Method method01 = c.getMethod("firstMethod", String.class);
Method method02 = c.getMethod("secondMethod", String.class, int.class);
// 通过获取的方法来调用(通过invoke方法)
// 需要:1,该方法; 2,一个对象; 3,对应参数
Object retValue01 = method01.invoke(obj, "这是一个没有什么用的参数");
Object retValue02 = method02.invoke(obj, "这也是一个没有什么用的参数,后面1也是", 1);
}
}
输出:
总结
这是反射机制一些基本常用操作,但是实际应用并不会这么简单,所以基础的一定要掌握好,这样在以后学习spring等框架时就能看懂学习底层源码了;
欢迎大家的点评!