Java基础 --- 反射机制 Reflection
什么是反射
- 一般情况下使用new 关键字实例化一个类,创建初始化一个对象
- 也就是在写代码时我们就知道类的类别, 类的属性, 类的所有方法等
Apple apple = new Apple(); //直接初始化
apple.setPrice(4);
- 而反射则和正常情况相反, 在写代码时我们不知道类的类别, 类的属性, 类的方法等
- 这时候,我们使用 JDK 提供的反射 API 进行反射调用创建对象
Class clz = Class.forName("com.chenshuyi.reflect.Apple");
Method method = clz.getMethod("setPrice", int.class);
Constructor constructor = clz.getConstructor();
Object object = constructor.newInstance();
method.invoke(object, 4);
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制. 这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制
反射的实现原理
Class 类
- 当我们写完一个Java项目之后,每个java文件都会被编译成一个 .class文件
- 这些 .class文件在程序运行时会被ClassLoader加载到JVM中,当一个类被加载以后,JVM就会在内存中自动产生一个Class对象
- Class类是一个特殊的类, 包括了大量的方法可以获得一个类的详细信息, 下面是常用的方法
Class.forName();动态加载类。
newInstance() :根据对象的class新建一个对象
getSuperclass() 获取继承的父类
getInterfaces() 获取继承的接口
getDeclaredFields() 获取所有属性名
getDeclaredMethods();获取当前类的所有方法。
getConstructors() :获得所有的构造函数。
getModifiers() : 反射中获得修饰符
getPackage() :反射中获得package
getField(String name):反射中获得属性成员。
getFields() :获得域数组成员。
isAnnotation() :判断是否为注解类型。
isPrimitive() :判断是否为基本类型。
isArray() :判断是否为数组类型。
isEnum() :判断是否为枚举类型。
getClassLoader() :获得类的类加载器
getMethods() 获得公共的方法
获取Class对象的三个方法
Class.forName(“全类名”)
注意是全类名,也就是类的完整路径
类名.class
通过类名的class属性获取
对象名.getClass()
getClass()方法定义于Object类中
使用 java.lang.reflect和Class类
反射的本质其实就是用java.lang.reflect类库API操作Class对象的结果
- java.lang.reflect中最主要的类: Field, Method, and Constructor , 分别描述一个类的成员属性, 所有方法以及构造函数
package reflection;
import java.util.*;
import java.lang.reflect.*;
/**
* This program uses reflection to print all features of a class.
* @version 1.1 2004-02-21
* @author Cay Horstmann
*/
public class ReflectionTest {
public static void main(String[] args) {
// read class name from command line args or user input
String name;
if (args.length > 0) name = args[0];
else {
Scanner in = new Scanner(System.in);
System.out.println("Enter class name (e.g. java.util.Date): ");
name = in.next();
}
try {
// print class name and superclass name (if != Object)
Class cl = Class.forName(name);
Class supercl = cl.getSuperclass();
String modifiers = Modifier.toString(cl.getModifiers());
if (modifiers.length() > 0) System.out.print(modifiers + " ");
System.out.print("class " + name);
if (supercl != null && supercl != Object.class) System.out.print(" extends "+ supercl.getName());
System.out.print("\n{\n");
printConstructors(cl);
System.out.println();
printMethods(cl);
System.out.println();
printFields(cl);
System.out.println("}");
}
catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.exit(0);
}
/**
* Prints all constructors of a class
* @param cl a class
*/
public static void printConstructors(Class cl) {
Constructor[] constructors = cl.getDeclaredConstructors();
for (Constructor c : constructors) {
String name = c.getName();
System.out.print(" ");
String modifiers = Modifier.toString(c.getModifiers());
if (modifiers.length() > 0) System.out.print(modifiers + " ");
System.out.print(name + "(");
// print parameter types
Class[] paramTypes = c.getParameterTypes();
for (int j = 0; j < paramTypes.length; j++) {
if (j > 0) System.out.print(", ");
System.out.print(paramTypes[j].getName());
}
System.out.println(");");
}
}
/**
* Prints all methods of a class
* @param cl a class
*/
public static void printMethods(Class cl) {
Method[] methods = cl.getDeclaredMethods();
for (Method m : methods) {
Class retType = m.getReturnType();
String name = m.getName();
System.out.print(" ");
// print modifiers, return type and method name
String modifiers = Modifier.toString(m.getModifiers());
if (modifiers.length() > 0) System.out.print(modifiers + " ");
System.out.print(retType.getName() + " " + name + "(");
// print parameter types
Class[] paramTypes = m.getParameterTypes();
for (int j = 0; j < paramTypes.length; j++) {
if (j > 0) System.out.print(", ");
System.out.print(paramTypes[j].getName());
}
System.out.println(");");
}
}
/**
* Prints all fields of a class
* @param cl a class
*/
public static void printFields(Class cl) {
Field[] fields = cl.getDeclaredFields();
for (Field f : fields) {
Class type = f.getType();
String name = f.getName();
System.out.print(" ");
String modifiers = Modifier.toString(f.getModifiers());
if (modifiers.length() > 0) System.out.print(modifiers + " ");
System.out.println(type.getName() + " " + name + ";");
}
}
}
input: java.lang.Double
下面是输出结果:
public class java.lang.Double extends java.lang.Number
{
//Constructor
public java.lang.Double(java.lang.String);
public java.lang.Double(double);
//All methods
public int hashCode();
public int compareTo(java.lang.Object);
public int compareTo(java.lang.Double);
public boolean equals(java.lang.Object);
public java.lang.String toString();
public static java.lang.String toString(double);
public static java.lang.Double valueOf(java.lang.String);
public static boolean isNaN(double);
public boolean isNaN();
public static boolean isInfinite(double);
public boolean isInfinite();
public byte byteValue();
public short shortValue();
public int intValue();
public long longValue();
public float floatValue();
public double doubleValue();
public static double parseDouble(java.lang.String);
public static native long doubleToLongBits(double);
public static native long doubleToRawLongBits(double);
public static native double longBitsToDouble(long);
//All fields
public static final double POSITIVE_INFINITY;
public static final double NEGATIVE_INFINITY;
public static final double NaN;
public static final double MAX_VALUE;
public static final double MIN_VALUE;
public static final java.lang.Class TYPE;
private double value;
private static final long serialVersionUID;
}
反射的优势和劣势
优势
- 使用反射的时候,可以只传入类名参数,就可以生成对象,降低了耦合性,使得程序更具灵活性
劣势
- 因为是运行时的操作, 所以性能方面要慢
- 反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题
反射的实际使用场景
以下是一些反射的使用场景
IDE的使用
- 当我们在使用 IDE(如 Eclipse,IDEA)时,当我们输入一个对象或类并想调用它的属性或方法时,一按点号,编译器就会自动列出它的属性或方法,这里就会用到反射
JDBC连接数据库
- 数据库的连接驱动类就是编译的时候不知道你到底是用的mysql,oracle还是其他数据库,而是由运行期动态加载的
// 1.加载驱动程序
Class.forName("com.mysql.jdbc.Driver");
// 2.获得数据库的连接
Connection conn = DriverManager.getConnection(URL, NAME, PASSWORD);
.......
配置文件
- 反射最重要的用途就是开发各种通用框架。很多框架(比如 Spring)都是配置化的(比如通过 XML 文件配置 Bean),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射,运行时动态加载需要加载的对象。
举一个例子,在运用 Struts 2 框架的开发中我们一般会在 struts.xml 里去配置 Action,比如:
<action name="login"
class="org.ScZyhSoft.test.action.SimpleLoginAction"
method="execute">
<result>/shop/shop-index.jsp</result>
<result name="error">login.jsp</result>
</action>
配置文件与 Action 建立了一种映射关系,当 View 层发出请求时,请求会被
StrutsPrepareAndExecuteFilter
拦截,然后StrutsPrepareAndExecuteFilter
会去动态地创建 Action 实例。比如我们请求login.action
,那么StrutsPrepareAndExecuteFilter
就会去解析struts.xml
文件,检索action中name为login的Action,并根据class属性创建SimpleLoginAction实例,并用invoke方法来调用execute方法,这个过程离不开反射。
对与框架开发人员来说,反射虽小但作用非常大,它是各种容器实现的核心。而对于一般的开发者来说,不深入框架开发则用反射用的就会少一点,不过了解一下框架的底层机制有助于丰富自己的编程思想,也是很有益的。
Example
利用反射写一个Generic Array
package arrays;
import java.lang.reflect.*;
import java.util.*;
/**
* This program demonstrates the use of reflection for manipulating arrays.
* @version 1.2 2012-05-04
* @author Cay Horstmann
*/
public class CopyOfTest
{
public static void main(String[] args)
{
int[] a = {
1, 2, 3 };
a = (int[]) goodCopyOf(a, 10);
System.out.println(Arrays.toString(a));
String[] b = {
"Tom", "Dick", "Harry" };
b = (String[]) goodCopyOf(b, 10);
System.out.println(Arrays.toString(b));
System.out.println("The following call will generate an exception.");
b = (String[]) badCopyOf(b, 10);
}
/**
* This method attempts to grow an array by allocating a new array and copying all elements.
* @param a the array to grow
* @param newLength the new length
* @return a larger array that contains all elements of a. However, the returned array has
* type Object[], not the same type as a
*/
public static Object[] badCopyOf(Object[] a, int newLength) // not useful
{
Object[] newArray = new Object[newLength];
System.arraycopy(a, 0, newArray, 0, Math.min(a.length, newLength));
return newArray;
}
/**
* This method grows an array by allocating a new array of the same type and
* copying all elements.
* @param a the array to grow. This can be an object array or a primitive
* type array
* @return a larger array that contains all elements of a.
*/
public static Object goodCopyOf(Object a, int newLength)
{
Class cl = a.getClass();
if (!cl.isArray()) return null;
Class componentType = cl.getComponentType();
int length = Array.getLength(a);
Object newArray = Array.newInstance(componentType, newLength);
System.arraycopy(a, 0, newArray, 0, Math.min(length, newLength));
return newArray;
}
}
自定义配置文件
原文链接:https://blog.csdn.net/weixin_45333509/article/details/115360094
游戏中有两名英雄角色供玩家选择
角色1:
姓名:安琪拉
职业:中单
一技能:程序中会使用public修饰,代表角色行为的方法
被动:程序中会使用private修饰,代表角色行为的方法
角色2
姓名:凯
职业:上单
一技能:程序中会使用public修饰,代表角色行为的方法
被动:程序中会使用private修饰,代表角色行为的方法
此时我们需要一个配置文件供玩家配置想要选择的英雄角色以及要释放的技能 people.properties
定义角色1 AnQiLa
package com.demo.roles;
public class AnQiLa {
private String name="安琪拉";
private String job="中单";
//私有化角色触发被动技能的方法
private String getPassiveSkill(){
return name+": [触发了被动技能!]";
}
//角色一技能
public String getFirstSkill(){
return name+": [在中路使用了一技能]";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
}
定义角色2 Kai
package com.demo.roles;
public class Kai {
private String name="凯";
private String job="上单";
//私有化角色触发被动技能的方法
private String getPassiveSkill(){
return name+": [触发了被动技能!]";
}
//角色一技能
public String getFirstSkill(){
return name+": [在上路使用了一技能]";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
}
开发业务场景工具类
package com.demo.controller;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;
public class ReflectUtil {
public static void main(String[] args) throws Exception {
//加载玩家配置信息
Properties properties = new Properties();
ClassLoader classLoader = ReflectUtil.class.getClassLoader();
InputStream resourceAsStream = classLoader.getResourceAsStream("people.properties");
properties.load(resourceAsStream);
//从配置文件中读取玩家选择的英雄角色
String role = properties.getProperty("object.role");
//从配置文件中读取玩家使用的技能
String actions = properties.getProperty("method.skills");
String[] skills = actions.split(",");
//通过反射实例化玩家选择的角色对象
Class aClass = Class.forName(role);
Object instance = aClass.newInstance();
//遍历玩家选择使用的技能
for (String skill : skills) {
//获取玩家使用角色的所有技能
Method declaredSkill = aClass.getDeclaredMethod(skill);
//判断获得的技能是否私有
if (declaredSkill.toString().contains("private")) {
//如果是私有的被动技能,则需设置私有技能可使用
declaredSkill.setAccessible(true);
//使用被动
Object invoke = declaredSkill.invoke(instance, null);
System.out.println(invoke.toString());
} else {
//如果是普通技能 则可以直接访问
Object invoke = declaredSkill.invoke(instance, null);
System.out.println(invoke.toString());
}
}
}
}
接下来玩家只需要通过更改people.properties配置文件选择需要使用的角色和需要使用的技能,工具类就可以帮我们完成角色的创建以及技能的释放
#玩家配置需要选择的英雄角色
#选择AnQiLa[安琪拉]
object.role=com.demo.roles.AnQiLa
#玩家配置需要使用的技能,多个技能之间用英文逗号隔开
#选择使用一技能并触发私有的被动技能
method.skills=getPassiveSkill,getFirstSkill
F:\software\Java\jdk1.8.0_121\bin\java.exe "-javaagent:D:\IntelliJ IDEA 2019.1.4\lib\idea_rt.jar=63738:D:\IntelliJ IDEA 2019.1.4\bin" -Dfile.encoding=UTF-8 -classpath F:\software\Java\jdk1.8.0_121\jre\lib\charsets.jar;F:\software\Java\jdk1.8.0_121\jre\lib\deploy.jar;F:\software\Java\jdk1.8.0_121\jre\lib\ext\access-bridge-64.jar;F:\software\Java\jdk1.8.0_121\jre\lib\ext\cldrdata.jar;F:\software\Java\jdk1.8.0_121\jre\lib\ext\dnsns.jar;F:\software\Java\jdk1.8.0_121\jre\lib\ext\jaccess.jar;F:\software\Java\jdk1.8.0_121\jre\lib\ext\jfxrt.jar;F:\software\Java\jdk1.8.0_121\jre\lib\ext\localedata.jar;F:\software\Java\jdk1.8.0_121\jre\lib\ext\nashorn.jar;F:\software\Java\jdk1.8.0_121\jre\lib\ext\sunec.jar;F:\software\Java\jdk1.8.0_121\jre\lib\ext\sunjce_provider.jar;F:\software\Java\jdk1.8.0_121\jre\lib\ext\sunmscapi.jar;F:\software\Java\jdk1.8.0_121\jre\lib\ext\sunpkcs11.jar;F:\software\Java\jdk1.8.0_121\jre\lib\ext\zipfs.jar;F:\software\Java\jdk1.8.0_121\jre\lib\javaws.jar;F:\software\Java\jdk1.8.0_121\jre\lib\jce.jar;F:\software\Java\jdk1.8.0_121\jre\lib\jfr.jar;F:\software\Java\jdk1.8.0_121\jre\lib\jfxswt.jar;F:\software\Java\jdk1.8.0_121\jre\lib\jsse.jar;F:\software\Java\jdk1.8.0_121\jre\lib\management-agent.jar;F:\software\Java\jdk1.8.0_121\jre\lib\plugin.jar;F:\software\Java\jdk1.8.0_121\jre\lib\resources.jar;F:\software\Java\jdk1.8.0_121\jre\lib\rt.jar;D:\ideastudyspace\spring_study\reflect-demo\target\classes com.demo.controller.ReflectUtil
安琪拉: [触发了被动技能!]
安琪拉: [在中路使用了一技能]
Process finished with exit code 0
假如玩家此时想玩上单使用凯,直接在people.properties配置文件里选择即可
#玩家配置需要选择的英雄角色
#选择Kai[凯]
object.role=com.demo.roles.Kai
#玩家配置需要使用的技能,多个技能之间用英文逗号隔开
#选择使用一技能并触发私有的被动技能
method.skills=getPassiveSkill,getFirstSkill
F:\software\Java\jdk1.8.0_121\bin\java.exe "-javaagent:D:\IntelliJ IDEA 2019.1.4\lib\idea_rt.jar=63947:D:\IntelliJ IDEA 2019.1.4\bin" -Dfile.encoding=UTF-8 -classpath F:\software\Java\jdk1.8.0_121\jre\lib\charsets.jar;F:\software\Java\jdk1.8.0_121\jre\lib\deploy.jar;F:\software\Java\jdk1.8.0_121\jre\lib\ext\access-bridge-64.jar;F:\software\Java\jdk1.8.0_121\jre\lib\ext\cldrdata.jar;F:\software\Java\jdk1.8.0_121\jre\lib\ext\dnsns.jar;F:\software\Java\jdk1.8.0_121\jre\lib\ext\jaccess.jar;F:\software\Java\jdk1.8.0_121\jre\lib\ext\jfxrt.jar;F:\software\Java\jdk1.8.0_121\jre\lib\ext\localedata.jar;F:\software\Java\jdk1.8.0_121\jre\lib\ext\nashorn.jar;F:\software\Java\jdk1.8.0_121\jre\lib\ext\sunec.jar;F:\software\Java\jdk1.8.0_121\jre\lib\ext\sunjce_provider.jar;F:\software\Java\jdk1.8.0_121\jre\lib\ext\sunmscapi.jar;F:\software\Java\jdk1.8.0_121\jre\lib\ext\sunpkcs11.jar;F:\software\Java\jdk1.8.0_121\jre\lib\ext\zipfs.jar;F:\software\Java\jdk1.8.0_121\jre\lib\javaws.jar;F:\software\Java\jdk1.8.0_121\jre\lib\jce.jar;F:\software\Java\jdk1.8.0_121\jre\lib\jfr.jar;F:\software\Java\jdk1.8.0_121\jre\lib\jfxswt.jar;F:\software\Java\jdk1.8.0_121\jre\lib\jsse.jar;F:\software\Java\jdk1.8.0_121\jre\lib\management-agent.jar;F:\software\Java\jdk1.8.0_121\jre\lib\plugin.jar;F:\software\Java\jdk1.8.0_121\jre\lib\resources.jar;F:\software\Java\jdk1.8.0_121\jre\lib\rt.jar;D:\ideastudyspace\spring_study\reflect-demo\target\classes com.demo.controller.ReflectUtil
凯: [触发了被动技能!]
凯: [在上路使用了一技能]
Process finished with exit code 0