Java反射
一、什么是反射?
Java反射说的是在运行状态中,对于任何一个类,我们都能够知道这个类有哪些方法和属性。对于任何一个对象,我们都能够对它的方法和属性进行调用。我们把这种动态获取对象信息和调用对象方法的功能称之为反射机制。反射就是获取字节码(.class)文件,可以通过Class对象进行获取。java.lang.Class类是Java中的反射中心,Class类的一个对象表示运行时程序中的一个类,并且它是一个通用类。
二、反射的三种方式
1、第一种:使用Object下的getClass()方法,getClass()返回此Object的运行时类,Class继承了Object,我们可以直接使用。
Student stu=new Student(); Class clazz=stu.getClass();
2、第二种:使用类文字直接获取,耦合度极高。
Class clazz = Student.class;
3、第三种:使用Class类的forName()方法,Class类forName() static方法返回对Class对象的引用。
Class forName(String className)
Class clazz=Class.forName("com.vangen.bean.Student");
上面的forName()方法接受完全该类的限定名(例如:com.vangen.bean.Student)作为参数,并加载该类,并返回其对象的引用。
Class forName(String name, boolean initialize, ClassLoader loader)
上面的forName()方法可以控制是否初始化或不初始化该类在加载时。 我们也可以传入类加载器。
三、类反射
我们可以使用Java反射来获取关于类的信息,例如包名,访问修饰符等。
1、获取简单的类名,可以使用Class中的getSimpleName()方法:
String simpleName=clazz.getSimpleName();
2、获取类的修饰符(如:abstract,public),使用Class中的getModifiers()方法,返回类的所有修饰符。但是getModifiers()方法返回的是一个整数。我们需要使用java.lang.reflect.Modifier.toString(int modifiers)才能获得修饰符的文本形式。
3、获取超类的名称,使用Class中的getSuperclass()方法,可以获取到其超类。如果对Object使用getSuperclass()方法,它将返回null,因为Object没有超类。
4、获取类实现的所有接口的名称,使用getInterfaces();
Class[] interfaces=clazz.getInterfaces();
package com.vangen.test;
import java.io.Serializable;
import java.lang.reflect.Modifier;
import java.lang.reflect.TypeVariable;
class Test4 implements Serializable, Cloneable {
private int id = -1;
private String name = "Unknown";
public Test4(int id, String name) {
this.id = id;
this.name = name;
}
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e.getMessage());
}
}
public String toString() {
return "Test: id=" + this.id + ", name=" + this.name;
}
}
public class Main {
public static void main(String[] args) {
String classDesciption = getClassDescription(Test4.class);
System.out.println(classDesciption);
}
public static String getClassDescription(@SuppressWarnings("rawtypes") Class c) {
StringBuilder classDesc = new StringBuilder();
int modifierBits = 0;
String keyword = "";
if (c.isInterface()) {
modifierBits = c.getModifiers() & Modifier.interfaceModifiers();
if (c.isAnnotation()) {
keyword = "@interface";
} else {
keyword = "interface";
}
} else if (c.isEnum()) {
modifierBits = c.getModifiers() & Modifier.classModifiers();
keyword = "enum";
}
modifierBits = c.getModifiers() & Modifier.classModifiers();
keyword = "class";
String modifiers = Modifier.toString(modifierBits);
classDesc.append(modifiers);
classDesc.append(" " + keyword);
String simpleName = c.getSimpleName();
classDesc.append(" " + simpleName);
String genericParms = getGenericTypeParams(c);
classDesc.append(genericParms);
Class superClass = c.getSuperclass();
if (superClass != null) {
String superClassSimpleName = superClass.getSimpleName();
classDesc.append(" extends " + superClassSimpleName);
}
String interfaces = Main.getClassInterfaces(c);
if (interfaces != null) {
classDesc.append(" implements " + interfaces);
}
return classDesc.toString();
}
public static String getClassInterfaces(Class c) {
Class[] interfaces = c.getInterfaces();
String interfacesList = null;
if (interfaces.length > 0) {
String[] interfaceNames = new String[interfaces.length];
for (int i = 0; i < interfaces.length; i++) {
interfaceNames[i] = interfaces[i].getSimpleName();
}
interfacesList = String.join(", ", interfaceNames);
}
return interfacesList;
}
public static String getGenericTypeParams(Class c) {
StringBuilder sb = new StringBuilder();
TypeVariable<?>[] typeParms = c.getTypeParameters();
if (typeParms.length > 0) {
String[] paramNames = new String[typeParms.length];
for (int i = 0; i < typeParms.length; i++) {
paramNames[i] = typeParms[i].getTypeName();
}
sb.append("<");
String parmsList = String.join(",", paramNames);
sb.append(parmsList);
sb.append(">");
}
return sb.toString();
}
}
四、字段反射
获取类中字段的信息,我们可以使用java.lang.reflect.Field类来获取。
Field[] getFields()//返回所有可以访问的公共字段(在类中声明或者继承的)。 Field[] getDeclaredFields()//返回所有字段(只出现在类的声明中的)。 Field getField(String name)//通过字段名获取Field对象 Field getDeclaredField(String name)//通过字段名获取声明的Field对象
public static void main(String[] args) throws Exception {
// 获取字节码对象
Class clazz = Class.forName("com.vangen.bean.Student");
// 获取多个公有方法,成员变量
Field[] files = clazz.getFields();
for (Field f : files) {
System.out.println(f);
}
System.out.println("-----------------------");
// 获取所有成员变量
Field[] files1 = clazz.getDeclaredFields();
for (Field f : files1) {
System.out.println(f);
}
// 获取单个成员变量
System.out.println("-----------------------");
Field f1 = clazz.getField("name");
System.out.println(f1);
System.out.println("----------------");
Field f2 = clazz.getDeclaredField("money");
System.out.println(f2);
// 获取对象
Object obj = clazz.newInstance();
// 给对象属性设置值
f2.setAccessible(true);
f2.set(obj, 56);
System.out.println(obj);
}
五、方法反射
java.lang.reflect.Method类的实例表示一个方法,反射方法可以使用Class类中以下方法获取方法的信息:
Method[] getMethods() Method[] getDeclaredMethods() Method getMethod(String name, Class... parameterTypes) Method getDeclaredMethod(String name, Class... parameterTypes)
getMethods()方法返回该类的所有可以访问的公共方法(无论是从类中还是继承自超类)。
getDeclaredMethods()方法返回所有只在类中声明的方法(继承自超类的方法将不会返回)。
getMethod(String name,Class ... parameterTypes)和getDeclaredMethod(String name,Class ... parameterTypes)通过方法名和参数类型获取Method对象。
Method类中的getReturnType()方法返回包含有关返回类型信息的Class对象。
public static void main(String[] args) throws Exception {
// 获取Class对象
Class clazz = Class.forName("com.vangen.bean.Student");
// 获取方法
Method m = clazz.getDeclaredMethod("method3", String.class);
m.setAccessible(true);
System.out.println(m);
}
六、构造函数反射
构造函数反射可以使用以下方法:
Constructor[] getConstructors() Constructor[] getDeclaredConstructors() Constructor<T> getConstructor(Class... parameterTypes) Constructor<T> getDeclaredConstructor(Class... parameterTypes)
getConstructors()方法返回当前和超类的所有公共构造函数。
getDeclaredConstructors()方法返回当前类的所有声明的构造函数。
getConstructor(Class ... parameterTypes)和getDeclaredConstructor(Class ... parameterTypes)通过参数类型获取构造函数对象。
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
// 获取字节码对象
Class<?> clazz = Class.forName("com.vangen.bean.Student");
// 获取构造方法
// 获取多个(公有)构造方法
Constructor[] constructors = clazz.getConstructors();
for (Constructor c : constructors) {
System.out.println(c);
}
// 获取所有的构造方法
System.out.println("------------------------------");
Constructor[] constructors1 = clazz.getDeclaredConstructors();
for (Constructor c : constructors1) {
System.out.println(c);
}
// 获取单个构造方法
// 获取单个公有构造方法
System.out.println("------------------------------");
Constructor c = clazz.getConstructor();
System.out.println(c);
System.out.println("-------------------------------");
Constructor c1 = clazz.getDeclaredConstructor(String.class);
System.out.println(c1);
System.out.println("--------------------------------");
Constructor c2 = clazz.getDeclaredConstructor(int.class, String.class);
System.out.println(c2);
//调用构造方法
clazz.newInstance();
//取消构造方法的权限
c1.setAccessible(true);
c1.newInstance("张三");
}
七、反射对象的创建
1、使用无参构造函数
如果已经获取到一个Class对象的引用,可以创建一个对象类对Class使用newInstance()方法,此方法不使用参数。
package com.vangen.test;
class TestClass{
public TestClass() {
System.out.println("called");
}
}
public class Main {
public static void main(String[] args) throws InstantiationException{
Class<TestClass> personClass=TestClass.class;
try {
TestClass p = personClass.newInstance();
System.out.println(p);
} catch (InstantiationException | IllegalAccessException e) {
System.out.println(e.getMessage());
}
}
}
2、有参构造函数
可以通过调用特定的构造函数使用反射创建对象。它涉及两个步骤。
- 获取构造函数的实例
- 调用newInstance来调用它
package com.vangen.test;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
class MyClass {
public MyClass(int i, String s) {
System.out.println("called");
System.out.println(i);
System.out.println(s);
}
}
public class Main {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Class<MyClass> myClass = MyClass.class;
Constructor<MyClass> cons = myClass.getConstructor(int.class,
String.class);
MyClass vangen = cons.newInstance(1, "abc");
System.out.println(vangen);
}
}
八、反射字段访问
可以使用反射在两个步骤中获取或设置字段。
-
- 获取字段的引用。
- 要读取字段的值,请在字段上调用getXxx()方法,其中Xxx是字段的数据类型。
- 要设置字段的值,请调用相应的setXxx()方法。
以相同的方式访问静态和实例字段。
package com.vangen.test;
import java.lang.reflect.Field;
class TestClass {
public String name = "Unknown";
public TestClass() {
}
public String toString() {
return "name=" + this.name;
}
}
public class Main {
public static void main(String[] args) {
Class<TestClass> ppClass = TestClass.class;
try {
TestClass p = ppClass.newInstance();
Field name = ppClass.getField("name");
String nameValue = (String) name.get(p);
System.out.println("Current name is " + nameValue);
name.set(p, "abc");
nameValue = (String) name.get(p);
System.out.println("New name is " + nameValue);
} catch (InstantiationException | IllegalAccessException | NoSuchFieldException | SecurityException
| IllegalArgumentException e) {
System.out.println(e.getMessage());
}
}
}

浙公网安备 33010602011771号