文章目录
1、反射机制
1.1什么是反射机制
反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
1.2反射机制能做什么
在运行时判断任意一个对象所属的类;
在运行时构造任意一个类的对象;
在运行时判断任意一个类所具有的成员变量和方法;
在运行时调用任意一个对象的方法;
生成动态代理。
1.3反射机制的相关API
通过一个对象获得完整的包名和类名
实例化一个类对象(使用构造函数,默认的和带参数的)
返回一个类实现的接口
取得一个类的父类
获得一个类的全部构造函数
可以通过反射调用一个类的方法
调用一个类的set和get方法
通过反射操作属性
通过反射取得并修改数组的信息
通过反射修改数组大小
2、获取一个Class对象(反射)的三种方式
java.lang.Class 类是Java中的反射中心,所有反射都是获取他的一个对象,这个对象表示运行时程序中的一个类,然后再用这个对象去做其他的操作,我们有三种方法可以获取到这个对象的引用。
2.1类文字
类文字是类名称后跟一个点和class。
Class<?> c1 = MyClass.class;
还可以获取原始数据类型的类对象,每个包装器原始数据类型类具有名为 TYPE 的静态字段,它具有对它表示的基本数据类型的类对象的引用。
Class c = boolean.class;
c = Boolean.TYPE;
c = void.class;
c = Void.TYPE;
2.2使用Object类的getClass()方法
Object类有一个 getClass()方法,它返回引用类对象的Class对象。
Class<?> c2 = new MyClass().getClass();
2.3使用Class类的forName()方法
Class类 forName() static方法返回对Class对象的引用,他有两个重载方法:
//接受一个类的限定名作为参数加载该类,并返回其对象引用。
//如果类已经加载,它将返回对Class对象的引用。
Class<?> forName(String className)
//可以在加载时控制是否初始化该类,我们也可以传入类加载器。
Class<?> forName(String name, boolean initialize, ClassLoader loader)
Class<?> c3 = null;
try {
// 第一种方式
c3 = Class.forName("reflect.MyClass");
// 第二种方式
ClassLoader cLoader = Object.class.getClassLoader();
c3 = Class.forName("reflect.MyClass", false, cLoader);
} catch (ClassNotFoundExceptione) {
e.getMessage();
}
完整样例如下:
package reflect;
class MyClass {
static {
System.out.println("加载这个类。。。");
}
}
public class Main {
public static void main(String[] args) {
Class<?> c1 = MyClass.class;
Class<?> c2 = new MyClass().getClass();
Class<?> c3 = null;
try {
// 第一种方式
c3 = Class.forName("reflect.MyClass");
// 第二种方式
ClassLoader cLoader = Object.class.getClassLoader();
c3 = Class.forName("reflect.MyClass", false, cLoader);
} catch (ClassNotFoundException e) {
e.getMessage();
}
}
}
3、通过Class对象获取一个类的信息(类名、包名、修饰符等)
-
Class
中的getSimpleName()
方法,获取简单类名;getName()
获取完整的类名,包括包名和类名; -
Class
中的getModifiers()
方法返回类的所有修饰符。getModifiers()
方法返回一个整数。我们必须调用java.lang.reflect.Modifier.toString(int modifiers)
以获得修饰符的文本形式。 -
要获取超类的名称,使用
Class
中的getSuperclass()
方法。如果对Object
类调用getSuperclass()
方法,它将返回null
,因为它没有超类。 -
要获取类实现的所有接口的名称,使用
getInterfaces()
。
package reflect;
import java.io.Serializable;
public abstract class MyClass implements Cloneable, Serializable {
private static final long serialVersionUID = 1L;
}
public class Main {
public static void main(String[] args) {
Class<?> c = null;
try {
// 第一种方式
c = Class.forName("reflect.MyClass");
} catch (ClassNotFoundException e) {
e.getMessage();
}
System.out.println("简单类名:" + c.getSimpleName());
System.out.println("完整类名:" + c.getName());
System.out.println("类型名:" + c.getTypeName());
Package p1 = c.getPackage();
System.out.println("\n包名:"+p1.getName());
int modifiers = c.getModifiers();
String mod = Modifier.toString(modifiers);
System.out.println("\n修饰符:" + mod);
// 获取所有接口
Class[] interfaces = c.getInterfaces();
System.out.println("\n实现的接口:");
for (Class in : interfaces) {
System.out.println(in.toString());
}
Class superClass = c.getSuperclass();
System.out.println("\n父类:"+superClass.getName());
}
}
输出结果如下:
4、通过Class对象获取一个类的字段
可以使用java.lang.reflect.Field
类来获取关于类中的字段的信息,以下四种方法在Class类可以返回关于字段的 Field 对象:
Field[] getFields()//返回该类中声明的或继承父类的所有公共字段
Field[] getDeclaredFields()//返回只在该类中声明的所有字段
//下面两个是通过字段名来返回Filed对象
Field getField(String name)
Field getDeclaredField(String name)
样例如下:
package reflect;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
class MySuperClass {
public int super_id = -1;
public String super_name = "Unknown";
protected int super_age = 18;
}
class MyClass extends MySuperClass {
private String sex = "男";
protected int age = 18;
public int id = -1;
public String name = "Unknown";
}
public class Main {
public static void main(String[] args) {
Class<?> c = null;
try {
c = Class.forName("reflect.MyClass");
} catch (ClassNotFoundException e) {
e.getMessage();
}
// 获取只在该类中声明的字段
System.out.println("只在该类中声明的所有字段:");
Field[] fields1 = c.getDeclaredFields();
for (Field f : fields1) {
// 获取字段修饰符
int mod = f.getModifiers();
String modifiers = Modifier.toString(mod);
// 获取字段类型
Class<?> type = f.getType();
String typeName = type.getSimpleName();
// 获取字段名
String fieldName = f.getName();
System.out.println(modifiers + " " + typeName + " " + fieldName);
}
// 获取所有字段
System.out.println("\n在该类中声明的和继承父类的公共字段:");
Field[] fields2 = c.getFields();
for (Field f : fields2) {
// 获取字段修饰符
int mod = f.getModifiers();
String modifiers = Modifier.toString(mod);
// 获取字段类型
Class<?> type = f.getType();
String typeName = type.getSimpleName();
// 获取字段名
String fieldName = f.getName();
System.out.println(modifiers + " " + typeName + " " + fieldName);
}
// 获取所有字段
System.out.println("\n通过字段名获取一个字段,并获取字段的属性:");
try {
MyClass p = (MyClass) c.newInstance();
Field name = c.getField("name");
String nameValue = (String) name.get(p);
System.out.println("name = " + nameValue);
name.set(p, "abc");
nameValue = (String) name.get(p);
System.out.println("name = " + nameValue);
} catch (InstantiationException | IllegalAccessException
| NoSuchFieldException | SecurityException
| IllegalArgumentException e) {
System.out.println(e.getMessage());
}
}
}
输出结果:
5、通过Class对象获取一个类的构造函数
java.lang.reflect.Constructor
类的实例表示一个构造方法。方法和构造方法继承自一个通用的抽象超类java.lang.reflect.Executable
。以下四种方法来自 Class
类中获取Constructor
对象:
Constructor[] getConstructors()//返回当前类和超类的所有公共构造函数。
Constructor[] getDeclaredConstructors()//返回当前类的所有构造函数
//通过参数类型获取构造函数函数
Constructor<T> getConstructor(Class... parameterTypes)
Constructor<T> getDeclaredConstructor(Class... parameterTypes)
样例如下:
package reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
class MyClass<T> {
public MyClass(int i, int j, String s) {
}
public MyClass(T t) {
}
public int getInt(String a) {
return 0;
}
}
public class Main {
public static void main(String[] args) {
Class<MyClass> c = MyClass.class;
// 返回当前类和父类的所有公共构造函数
Constructor[] constructors = c.getConstructors();
ArrayList<String> constructDescList = getConstructorsDesciption(
constructors);
for (String desc : constructDescList) {
System.out.println(desc);
}
}
public static ArrayList<String> getConstructorsDesciption(
Constructor[] constructors) {
ArrayList<String> constructorList = new ArrayList<>();
for (Constructor constructor : constructors) {
String modifiers = getModifiers(constructor);
String constructorName = constructor.getName();
constructorList.add(modifiers + " " + constructorName + "("
+ getParameters(constructor) + ") "
+ getExceptionList(constructor));
}
return constructorList;
}
public static ArrayList<String> getParameters(Executable exec) {
Parameter[] parms = exec.getParameters();
ArrayList<String> parmList = new ArrayList<>();
for (int i = 0; i < parms.length; i++) {
int mod = parms[i].getModifiers() & Modifier.parameterModifiers();
String modifiers = Modifier.toString(mod);
String parmType = parms[i].getType().getSimpleName();
String parmName = parms[i].getName();
String temp = modifiers + " " + parmType + " " + parmName;
if (temp.trim().length() == 0) {
continue;
}
parmList.add(temp.trim());
}
return parmList;
}
public static ArrayList<String> getExceptionList(Executable exec) {
ArrayList<String> exceptionList = new ArrayList<>();
for (Class<?> c : exec.getExceptionTypes()) {
exceptionList.add(c.getSimpleName());
}
return exceptionList;
}
public static String getModifiers(Executable exec) {
int mod = exec.getModifiers();
if (exec instanceof Method) {
mod = mod & Modifier.methodModifiers();
} else if (exec instanceof Constructor) {
mod = mod & Modifier.constructorModifiers();
}
return Modifier.toString(mod);
}
}
输出结果:
6、通过Class对象获取一个类的实例方法
java.lang.reflect.Method
类的实例表示一个方法。方法和构造方法继承自一个通用的抽象超类java.lang.reflect.Executable
。有以下四种方法获取一个Class
类中Method
实例:
Method[] getMethods()//返回当前类和父类的所有公共方法
Method[] getDeclaredMethods()//返回当前类的所有方法
//通过方法名和方法参数获取Method对象
Method getMethod(String name, Class<?>... parameterTypes)
Method getDeclaredMethod(String name, Class<?>... parameterTypes)
方法中的参数由 Parameter
类的对象表示,Executable
类中的getParameters()
方法获取所有参数作为 Parameter
的数组。默认情况下,参数名称不存储在类文件中。参数类的名称将类似于arg0,arg1等。我们可以通过编译source来保存类文件中的实际参数名代码使用 -parameters 选项与 javac 编译器。
Executable
类的getExceptionTypes()
方法类返回一个由Executable抛出的异常数组。
Executable
类的getModifiers()
方法将修饰符作为int返回。
Executable
类的getTypeParameters()
方法返回一个TypeVariable数组,该数组表示通用方法或构造函数的类型参数。
package reflect;
import java.beans.Expression;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
class MyClass<T> {
public MyClass(int i, int j, String s) {
}
public MyClass(T t) {
}
public String getString(String a) throws Exception {
if (a == null) {
throw new Exception();
}
return a;
}
public int getInt(int b, int c) throws ArithmeticException {
if (c == 0) {
throw new ArithmeticException();
}
return b / c;
}
}
public class Main {
public static void main(String[] argv) {
Class<MyClass> cls = MyClass.class;
Method[] methods = cls.getDeclaredMethods();
for (Method m : methods) {
System.out.println("方法名:" + m.getName());
System.out.println("修饰符:" + getModifiers(m));
System.out.println("参数列表:" + getParameters(m));
System.out.println("返回类型:" + m.getReturnType().getSimpleName());
System.out.println("异常列表:" + getExceptionList(m));
System.out.println();
}
}
public static ArrayList<String> getParameters(Executable exec) {
Parameter[] parms = exec.getParameters();
ArrayList<String> parmList = new ArrayList<>();
for (int i = 0; i < parms.length; i++) {
int mod = parms[i].getModifiers() & Modifier.parameterModifiers();
String modifiers = Modifier.toString(mod);
String parmType = parms[i].getType().getName();
String parmName = parms[i].getName();
String temp = modifiers + " " + parmType + " " + parmName;
if (temp.trim().length() == 0) {
continue;
}
parmList.add(temp.trim());
}
return parmList;
}
public static ArrayList<String> getExceptionList(Executable exec) {
ArrayList<String> exceptionList = new ArrayList<>();
for (Class<?> c : exec.getExceptionTypes()) {
exceptionList.add(c.getSimpleName());
}
return exceptionList;
}
public static String getModifiers(Executable exec) {
int mod = exec.getModifiers();
if (exec instanceof Method) {
mod = mod & Modifier.methodModifiers();
} else if (exec instanceof Constructor) {
mod = mod & Modifier.constructorModifiers();
}
return Modifier.toString(mod);
}
}
输出结果:
7、通过Class对象创建一个类的对象
我们可以使用反射动态创建类的对象,通过调用其中一个构造函数。然后我们可以访问对象的字段的值,设置它们的值,并调用它们的方法。
有两种方法来创建对象:
- 使用no-args构造函数
- 使用带参数的构造函数
6.1无参构造函数
如果有一个Class
对象的引用,则可以利用Class
类的newInstance()
方法创建一个类的实例,此方法不使用参数,并且等效的使用new
运算符利用无参构造函数创建出来的实例对象。
package reflect;
class MyClass {
public int age = 18;
public String name = "huangqiqi";
public MyClass() {
System.out.println("构造函数。。。");
}
}
public class Main {
public static void main(String[] args) {
Class<MyClass> personClass = MyClass.class;
MyClass p = null;
try {
p = personClass.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
System.out.println(e.getMessage());
}
System.out.println(p);
System.out.println("age = " + p.age);
System.out.println("name = " + p.name);
}
}
6.2有参构造函数
还可以通过调用特定的构造函数使用反射创建对象。它涉及两个步骤。
-
获取构造函数的实例
Constructor<MyClass> cons = myClass.getConstructor(int.class, String.class);
-
调用带有参数的newInstance来创建一个对象
MyClass chris = cons.newInstance(1, "abc");
样例如下:
package reflect;
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) {
Class<MyClass> myClass = MyClass.class;
try {
// 获取构造函数的实例
Constructor<MyClass> cons = myClass.getConstructor(int.class,
String.class);
// 通过newInstance来调用它
MyClass chris = cons.newInstance(1, "abc");
System.out.println(chris);
} catch (NoSuchMethodException | SecurityException
| InstantiationException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException e) {
System.out.println(e.getMessage());
}
}
}
输出结果:
6.3通过构造的实例调用方法
调用一个方法,基本上分为三个步骤:
- 通过
Class
对象获取一个类的实例对象obj
- 通过
Class
对象的getMethod(String name, Class<?>... parameterTypes)
方法获取一个Method
的对象,name
为方法名,后面参数为参数类型的Class对象 - 通过
Method
的对象调用invoke(Object obj, Object... args)
方法,其中obj
就是第一步中获得的对象,后面的参数就是要传入的实参,如果方法没有参数就不写
样例如下:
package reflect;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
class MyClass {
private String name;
public MyClass() {
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public class Main {
public static void main(String[] args) {
Class<MyClass> myClass = MyClass.class;
try {
MyClass p = myClass.newInstance();
// 调用setName方法
Method setName = myClass.getMethod("setName", String.class);
setName.invoke(p, "huangqiqi");
// 调用getName方法
Method getName = myClass.getMethod("getName");
String name = (String) getName.invoke(p);
System.out.println(name);
} catch (InstantiationException | IllegalAccessException
| NoSuchMethodException | SecurityException
| IllegalArgumentException | InvocationTargetException e) {
System.out.println(e.getMessage());
}
}
}
输出结果:
8、通过反射创建、获取、修改数组信息
这个其实挺简单的,主要用的就是java.lang.reflect.Array
这个类,话不多说,一切都在代码里面。
样例如下:
package reflect;
import java.lang.reflect.Array;
public class Main {
public static void main(String[] args) {
// 修改数组值-------------------------------------------------
int[] array = { 1, 2, 3, 4, 5 };
// 得到数组类型
Class<?> demo = array.getClass().getComponentType();
System.out.println("数组类型: " + demo.getName());
System.out.println("数组长度 " + Array.getLength(array));
System.out.println("数组的第一个元素: " + Array.get(array, 0));
Array.set(array, 0, 100);
System.out.println("修改之后数组第一个元素为: " + Array.get(array, 0));
System.out.println();
// 修改数组大小----------------------------------------------
int[] temp = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
print(temp);
int[] newTemp = (int[]) arrayInc(temp, 15);
print(newTemp);
String[] atr = { "a", "b", "c" };
String[] str1 = (String[]) arrayInc(atr, 8);
print(str1);
}
/**
* 修改数组大小
*/
public static Object arrayInc(Object obj, int len) {
/*
* 创建一个长度为len的数组,getComponentType()是Class中的方法, 返回数组的元素类型的Class对象
* 如果这个类型不是数组类型,则返回null
*/
Class<?> arr = obj.getClass().getComponentType();
Object newArr = Array.newInstance(arr, len);
// 获取数组长度
int co = Array.getLength(obj);
// 复制一个数组,从指定的下表开始
System.arraycopy(obj, 0, newArr, 0, co);
return newArr;
}
/**
* 打印
*/
public static void print(Object obj) {
Class<?> c = obj.getClass();
if (!c.isArray()) {// 判断是否为数组类型
return;
}
System.out.println("数组长度为: " + Array.getLength(obj));
for (int i = 0; i < Array.getLength(obj); i++) {
// 得到数组指定下标的元素
System.out.print(Array.get(obj, i) + " ");
}
System.out.println();
}
}
输出结果: