Java反射
Java反射
反射库提供了一个丰富且精巧的工具集,可以用来编写能够动态操纵Java代码的程序。能够分析类能力的程序称为反射(reflective)。反射机制的功能极为强大,反射机制可以用来:
- 在运行时分析类的能力
- 在运行时检查对象,例如,编写一个适用于所有类的toString方法
- 实现泛型数组操作代码
- 利用Method对象
1、Class类
在程序运行期间,Java运行时系统始终为所有对象维护一个运行时类型标识。这个信息会跟踪每个对象所属的类。虚拟机利用运行时类型信息选择要执行的正确的方法。
可以使用一个特殊的Java类访问这些信息。保存这些信息的类名为Class。
三种获得Class类对象的方法:
- class.getClass()
- Class.forName(className)
- class.class
虚拟机为每个类型管理一个唯一的Class对象。因此,可以使用==运算符实现两个类对象的比较。例如,
if(Employee.getClass()==Employee.class)//返回结果为true
如果有一个Class类型的对象,可以用它来构造类的实例。调用getConstructor方法将得到一个Constructor类型的对象,然后使用newInstance方法来构造一个实例。例如:
Class cl = Class.forName("java.util.Random");
Object obj = cl.getConstructor().newInstance;
如果这个类没有无参数的构造器,getConstructor方法会抛出一个异常。
API
java.lang.Class
- static Class forName(String className)
返回一个Class对象,表示名为className的类- Constructor getConstructor(Class… paramterTypes)
生成一个对象,描述有指定参数类型的构造器
java.lang.reflect.Constructor
- Object newInstance(Object… params)
将params传递到构造器,来构造这个构造器声明类的一个新实例
2、资源
类通常有一些关联的数据文件,例如
- 图像和声音文件
- 包含消息字符串和按钮标签的文本文件。
Class类提供了一个很有用的服务可以查找资源文件。下面给出必要的步骤:
- 获得拥有资源的类的Class对象,例如,ResourceTest.class
- 有些方法,如ImageIcon类的getImage方法,接受描述资源位置URL。则要调用
URL url = class.getResource("about.gif")
- 否则,使用getResourceAsStream方法得到一个输入流来读取文件中的数据
ResourceTest.java
/**
* 使用反射读取资源
*/
public class ResourceTest {
public static void main(String[] args) throws IOException {
Class cl = ResourceTest.class;
URL url = cl.getResource("about.jpg");
ImageIcon icon = new ImageIcon(url);
InputStream stream = cl.getResourceAsStream("about.txt");//相对路径
byte[] bytes = new byte[1024];
stream.read(bytes);
String about = new String(bytes, "UTF-8");
InputStream stream2 = cl.getResourceAsStream("/com/company/reflection/about.txt");//绝对路径
stream2.read(bytes);
String title = new String(bytes, StandardCharsets.UTF_8).trim();
JOptionPane.showMessageDialog(null,about,title,JOptionPane.INFORMATION_MESSAGE,icon);
}
}
API
java.lang.Class
- URL getResource(String name)
- InputStream getResourceAsStream(String name)
找到与类位于同一位置的资源,返回一个可以用来加载资源的URL或者输入流。如果没有找到资源,则返回null,所以不会抛出异常或者I/O错误
3、利用反射分析类的能力
在java.lang.reflect包中有三个类Field、Method、Constructor分别用来描述类的字段、方法和构造器。这三个类都有一个叫做getName的方法,用来返回字段、方法或构造器的名称。
Field类有一个getType方法,用来返回描述字段类型的一个对象,这个对象的类型同样是Class。Method和Constructor类有报告参数类型的方法,Method类还有一个报告返回类型的方法。这三个类都有一个名为getModifiers的方法,它将返回一个整数,用不同的0/1位描述所使用的修饰符,如public和static。另外,还可以利用java.lang.reflect包中的Modifier类的静态方法分析getModifiers返回的这个整数。例如,可以使用Modifier类中的isPublic、isPrivate或isFinal判断方法或构造器是public、private还是final。我们需要做的就是在getModifiers返回的整数上调用Modifier类中适当的方法,另外,还可以利用Modifier.toString方法将修饰符打印出来。
ReflectionTest.java
/**
* 使用反射分析类的能力
*/
public class ReflectionTest {
public static void main(String[] args) throws ClassNotFoundException {
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();
}
Class cl = Class.forName(name);//使用name获取Class
Class supercl = cl.getSuperclass();//获取父类的class
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("}");
}
/**
* prints all constructors of a class
*/
public static void printConstructors(Class cl){
Constructor[] constructors = cl.getDeclaredConstructors();//获取全部构造器的数组
for(Constructor c : constructors){//遍历构造器数组
String name = c.getName();//获取构造器的name
System.out.print(" ");
String modifiers = Modifier.toString(c.getModifiers());//获取构造器的修饰符
if(modifiers.length()>0) System.out.print(modifiers+" ");
System.out.print(name+"(");
Class[] parameterTypes = c.getParameterTypes();//获取构造器的参数数组
for(int j = 0; j < parameterTypes.length; j++){//遍历参数数组
if(j>0) System.out.print(", ");//逗号分隔开
System.out.print(parameterTypes[j].getName());//打印参数name
}
System.out.println(");");
}
}
/**
* prints all methods of a class
*/
public static void printMethods(Class cl){
Method[] methods = cl.getDeclaredMethods();
for(Method m : methods){
Class<?> returnType = 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(returnType.getName() + " " + name + "(");
//print parameter types
Class[] parameterTypes = m.getParameterTypes();
for(int j = 0; j<parameterTypes.length; j++){
if(j>0) System.out.print(", ");
System.out.print(parameterTypes[j].getName());
}
System.out.println(");");
}
}
/**
* prints all fields of a class
*/
public static void printFields(Class cl){
Field[] fields = cl.getDeclaredFields();
for(Field field : fields){
Class type = field.getType();
String name = field.getName();
System.out.print(" ");
String modifiers = Modifier.toString(field.getModifiers());
if(modifiers.length()>0) System.out.print(modifiers + " ");
System.out.println(type.getName() + " "+ name + ";");
}
}
}
输入
java.lang.Double
输出
public final class java.lang.Double extends java.lang.Number
{
public java.lang.Double(double);
public java.lang.Double(java.lang.String);
public boolean equals(java.lang.Object);
public static java.lang.String toString(double);
public java.lang.String toString();
public int hashCode();
public static int hashCode(double);
public static double min(double, double);
public static double max(double, double);
public static native long doubleToRawLongBits(double);
public static long doubleToLongBits(double);
public static native double longBitsToDouble(long);
public volatile int compareTo(java.lang.Object);
public int compareTo(java.lang.Double);
public byte byteValue();
public short shortValue();
public int intValue();
public long longValue();
public float floatValue();
public double doubleValue();
public static java.lang.Double valueOf(java.lang.String);
public static java.lang.Double valueOf(double);
public static java.lang.String toHexString(double);
public static int compare(double, double);
public static boolean isNaN(double);
public boolean isNaN();
public static boolean isFinite(double);
public static boolean isInfinite(double);
public boolean isInfinite();
public static double sum(double, double);
public static double parseDouble(java.lang.String);
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_NORMAL;
public static final double MIN_VALUE;
public static final int MAX_EXPONENT;
public static final int MIN_EXPONENT;
public static final int SIZE;
public static final int BYTES;
public static final java.lang.Class TYPE;
private final double value;
private static final long serialVersionUID;
}
API
java.lang.Class
- Field[] getFields()
返回这个类支持的公共字段- Field[] getDeclaredFields()
返回类中声明的全部字段- Method[] getMethods()
返回这个类支持的公共方法- Field[] getDeclaredMethods()
返回类中声明的全部方法- Constructor[] getConstructors()
返回这个类支持的公共构造器- Constructor[] getDeclaredConstructors()
返回类中声明的全部构造器
java.lang.reflect.Field
java.lang.reflect.Method
java.lang.reflect.Constructor
- int getModifiers()
返回一个整数,描述这个构造器、方法或字段的修饰符。使用Modifier类中的方法来分析这个返回值- String getName()
返回一个表示构造器名、方法名或字段名的字符串- Class[] getParamterTypes() (在Constructor 和 Method classes 类中)
返回一个Class对象数组,其中各个对象表示参数的类型。- Class getReturnType() (在 Method 类中)
返回一个用于表示返回类型的Class对象
java.lang.reflect.Modifier
- static String toString(int modifiers)
返回一个字符串,包含对应modifiers中位设置的修饰符
4、使用反射在运行时分析对象
在编写程序时,如果知道想要查看的字段名和类型,查看对象中指定字段的内容是一件很容易的事情。而利用反射机制可以查看在编译时还不知道的对象字段。
要做到这一点,关键方法时Field类中的get方法。如果f是一个Field类型的对象,obj是某个包含f字段的类的对象,f.get(obj)将返回一个对象,其值为obj的当前字段值。
ObjectAnalyzerTest.java
public class ObjectAnalyzerTest {
public static void main(String[] args) throws IllegalAccessException {
ArrayList<Integer> squares = new ArrayList<>();
for (int i = 1; i <= 5; i++)
squares.add(i*i);
System.out.println(new ObjectAnalyzer().toString(squares));
}
}
ObjectAnalyzer.java
/**
* 使用反射在运行时分析对象
*/
public class ObjectAnalyzer {
private ArrayList<Object> visted = new ArrayList<>();
/**
* 将对象转换为列出所有字段的字符串表示形式
*/
public String toString(Object obj) throws IllegalAccessException {
if (obj == null) return null;
if (visted.contains(obj)) return "...";
visted.add(obj);
Class cl = obj.getClass();
if (cl == String.class) return (String)obj;
if (cl.isArray()){
String r = cl.getComponentType() + "[]{";// cl.getComponentType:返回表示数组的元素类型的Class
for (int i = 0; i< Array.getLength(obj); i++){
if (i > 0) r += ",";
Object val = Array.get(obj, i);
if (cl.getComponentType().isPrimitive()) r += val;// 如果是基本类型直接追加
else r += toString(val);// 如果不是基本类型递归调用toString方法
}
return r + "}";
}
String r = cl.getName();
// 检查此类和所有超类的字段
do{
r += "[";
Field[] fields = cl.getDeclaredFields();
AccessibleObject.setAccessible(fields,true);// 设置一个对象数组的可访问标志
// 获取所有字段的名称和值
for (Field field : fields){
if (!Modifier.isStatic(field.getModifiers())){
if (!r.endsWith("[")) r += ",";
r += field.getName() + "=";
Class type = field.getType();
Object val = field.get(obj);// 返回obj对象中用这个Field对象描述的字段的值
if (type.isPrimitive()) r += val;// 如果是基本类型直接追加
else r += toString(val);// 如果不是基本类型递归调用toString方法
}
}
r += "]";
cl = cl.getSuperclass();
}
while (cl != null);
return r;
}
}
输出
java.util.ArrayList[elementData=class java.lang.Object[]{java.lang.Integer[value=1][][],java.lang.Integer[value=4][][],java.lang.Integer[value=9][][],java.lang.Integer[value=16][][],java.lang.Integer[value=25][][],null,null,null,null,null},size=5][modCount=5][][]
API
java.lang.reflect.AccessibleObject
- void setAccessible(boolean flag)
设置或取消这个可访问对象的可访问标志,如果拒绝访问则抛出一个IllegalAccessException异常- static void setAccessible(AccessibleObject[] array, boolean flag)
这是一个便利方法,用于设置一个对象数组的可访问标志。
5、使用反射编写泛型数组代码
CopyOfTest.java
/**
* 使用反射编写泛型数组代码
*/
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));
//以下调用将生成异常
b = (String[]) badCopyOf(b,10);
}
public static Object[] badCopyOf(Object[] a, int newLength){
Object[] newArray = new Object[newLength];
System.arraycopy(a,0,newArray,0,Math.min(a.length,newLength));
return newArray;
}
public static Object goodCopyOf(Object a, int newLength){
Class cl = a.getClass();
if(!cl.isArray()) return null;
Class componentType = cl.getComponentType();// 返回表示数组的元素类型的Class
int length = Array.getLength(a);
Object newArray = Array.newInstance(componentType, newLength);// 使用反射包里的Array构造一个新数组,提供两个参数,一个是数组的元素类型,一个是数组的长度
System.arraycopy(a,0,newArray,0,Math.min(length,newLength));
return newArray;
}
}
输出
[1, 2, 3, 0, 0, 0, 0, 0, 0, 0]
[Tom, Dick, Harry, null, null, null, null, null, null, null]
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
at com.company.reflection.CopyOfTest.main(CopyOfTest.java:20)
API
java.lang.reflect.Array
- static Object get(Object array,int index)
- static void set(Object array,int index,Object newValue)
- static int getLength(Object array)
返回给定数组的长度- static Object newInstance(Class componentType,int Length)
返回一个有给定类型、给定大小的新数组
6、调用任意方法和构造器
Method类中有一个invoke方法,允许调用包装在Method对象中的方法。invoke方法的签名是:
Object invoke(Object obj,Object... args)
第一个参数是隐式参数,其余的对象提供了显示参数。
对于静态方法,第一个参数可以忽略,即可以将它设置为null。
MethodTableTest.java
/**
* 使用反射调用任意方法和构造器
*/
public class MethodTableTest {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
// 获取指向square和sqrt方法的方法指针
Method square = MethodTableTest.class.getMethod("square", double.class);// 平方
Method sqrt = Math.class.getMethod("sqrt", double.class);// 平方根
// 打印x和y值表
printTable(1,10,10,square);
printTable(1,10,10,sqrt);
}
public static double square(double x){
return x * x;
}
public static void printTable(double from, double to, int n, Method f) throws InvocationTargetException, IllegalAccessException {
// 打印该方法作为表头
System.out.println(f);
double dx = (to - from) / (n - 1);
for (double x = from; x <= 10; x += dx){
double y = (Double) f.invoke(null,x);
System.out.printf("%10.4f | %10.4f%n",x,y);
}
}
}
输出
public static double com.company.reflection.MethodTableTest.square(double)
1.0000 | 1.0000
2.0000 | 4.0000
3.0000 | 9.0000
4.0000 | 16.0000
5.0000 | 25.0000
6.0000 | 36.0000
7.0000 | 49.0000
8.0000 | 64.0000
9.0000 | 81.0000
10.0000 | 100.0000
public static double java.lang.Math.sqrt(double)
1.0000 | 1.0000
2.0000 | 1.4142
3.0000 | 1.7321
4.0000 | 2.0000
5.0000 | 2.2361
6.0000 | 2.4495
7.0000 | 2.6458
8.0000 | 2.8284
9.0000 | 3.0000
10.0000 | 3.1623
API
java.lang.reflect.Method
- public Object invoke(Object implicitParamter, Object[] explicitParamters)
调用这个对象描述的方法,传入给定参数,并返回方法的返回值。对于静态方法,传入null作为隐式参数。
本文地址:https://blog.csdn.net/kzmzzz/article/details/107878591
下一篇: 吃面食好吗,来自南方人的灵魂拷问