- 反射:能够分析类能力的程序。
- 反射机制用途:
- 运行时分析类的能力。
- 运行时查看对象。
- 实现通用的数组操作代码
- 利用Method对象(类似于C++的函数指针)
- Class类:
- 程序运行期间,Java运行时系统始终为所有对象维护一个被称为运行时的类型标识。跟踪着每个对象所属的类。保存这些信息的类成为 Class类。
- Object类的 getClass()方法 会返回一个 Class类型 的实例。
- 获得 Class类对象的方法:
- Object类的 getClass()方法
- Class.forName(className) 静态方法
- 对于类型T → T.class
- Class类 实际上是一个泛型类,Employee.class 的类型是 Class<Employee> 。
- 虚拟机为每个类型管理一个Class对象,故可以用 ==运算符 比较两个类对象。如:if(e.getClass() == Employee.class)... 。
- e.getClass().newInstance()。newInstance()方法 可以动态地创建一个类的实例,调用的是默认的构造器。如果需要提供参数,那么就使用 Constructor类中的newInstance()方法。
- 利用反射分析类的能力:
- 反射机制最重要的内容——检查类的结构
- java.lang.reflect 包中有三个类:Field、Method 和 Constructor 分别用于描述类的 域、方法 和 构造器。
- java.lang.Class:
- Field[] getFields() //返回包含Field对象的数组,含 这个类或其超类的公有域。
- Field[] getDeclaredFields() //记录这个类的所有域。
- Method[] getMethod() //返回所有的公有方法(含继承)
- Method[] getDeclaredFields() //返回这个类或接口的所有方法(不含继承)
- Constructor[] getConstructors()
- Constructor[] getDeclaredConstructors()
- java.lang.reflect.Field/Method/Constructor
- Class getDeclaredClass()
- Class[] getExceptionType() // Method和Constructor
- int getModifiers()
- String getName()
- Class[] getParameterTypes() // Method和 Constructor
- Class[] getReturnType() // Method
- java.lang.reflect.Modifier
- static String toString(int modifiers)
- 示例程序:(打印出一个类的所有 构造器的签名 、 方法的签名 和 域)
package coreJava_5_13;
import java.lang.reflect.*;
import java.util.*;
public class ReflectionTest {
public static void main( String args[] ) {
Scanner in = new Scanner(System.in);
System.out.println("Enter class name (e.g. java.lang.Double):");
String name = in.next();
try {
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 " + cl.getName() );
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( Exception e ) {
e.printStackTrace();
}
System.exit(0);
}
public static void printConstructors( Class cl ) {
Constructor[] constructors = cl.getDeclaredConstructors();
for( Constructor c : constructors ) {
System.out.print(" ");
String name = c.getName();
String modifiers = Modifier.toString(c.getModifiers());
if( modifiers.length() > 0 ) System.out.print( modifiers + " " );
System.out.print(name+"(");
Class[] params = c.getParameterTypes();
for( int i = 0; i < params.length; i++ ) {
if( i > 0 ) System.out.print(", ");
System.out.print(params[i].getName());
}
System.out.println(");");
}
}
public static void printMethods( Class cl ) {
Method[] methods = cl.getDeclaredMethods();
for( Method m : methods ) {
System.out.print(" ");
String modifiers = Modifier.toString(m.getModifiers());
if( modifiers.length() > 0 ) System.out.print( modifiers + " " );
String ret = m.getReturnType().getName();
System.out.print(ret + " ");
System.out.print( m.getName() + "(");
Class[] params = m.getParameterTypes();
for( int i = 0; i < params.length; i++ ) {
if( i > 0 ) System.out.print(", ");
System.out.print(params[i].getName());
}
System.out.println(");");
}
}
public static void printFields( Class cl ) {
Field[] fields = cl.getDeclaredFields();
for( Field f : fields ) {
System.out.print(" ");
String modifiers = Modifier.toString(f.getModifiers());
if( modifiers.length() > 0 ) System.out.print(modifiers + " ");
Class type = f.getType();
System.out.println( type.getName() + " " + f.getName() + ";" );
}
}
}
- 在运行时使用反射分析对象:
- 进一步查看数据域的实际内容。
- 查看对象域的关键方法是Field类中的 get方法。
- java.lang.reflect.AccessibleObject
- void setAccessible( boolean flag )
- boolean isAccessible()
- static void setAccessible( AccessibleObject[] array, boolean flag )
- java.lang.Class
- Field getField( String name)
- Field getDeclaredField( String name )
- java.lang.reflect.Field
- Object get( Object obj )
- void set( Object obj, Object newValue )
- 示例程序:( 泛型toString方法 )
package coreJava_5_13;
import java.util.*;
public class ObjectAnalyzerTest {
public static void main( String args[] ) {
ArrayList<Integer> squares = new ArrayList<>();
for( int i = 1; i <= 5; i++ )
squares.add(i*i);
int[] arrInt = new int[5];
String[] arrStr = new String[3];
arrStr[0] = "I";
arrStr[1] = "love";
arrStr[2] = "u";
for( int i = 0; i < arrInt.length; i++ )
arrInt[i] = i + 1;
System.out.println( new ObjectAnalyzer().toString(squares) );
System.out.println( new ObjectAnalyzer().toString( arrInt ) );
System.out.println( new ObjectAnalyzer().toString( arrStr ) );
}
}
package coreJava_5_13;
import java.util.*;
import java.lang.reflect.*;
public class ObjectAnalyzer {
private ArrayList<Object> visited = new ArrayList<>();
/**
* Converts an object to a string representation that lists all fields.
* @param obj an object
* @return a string with the object's class name and all field names and values
*/
public String toString( Object obj ) {
if( obj == null ) return "null";
if( visited.contains(obj) ) return "...";
visited.add(obj);
Class cl = obj.getClass();
if( cl == String.class ) return (String) obj;
if( cl.isArray() ) {
String r = cl.getComponentType() + "[]{";
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);
}
return r + "}";
}
String r = cl.getName();
do {
r += "[";
Field[] fields = cl.getDeclaredFields();
AccessibleObject.setAccessible(fields, true);
for( Field f : fields ) {
if( !Modifier.isStatic(f.getModifiers()) ) {
if( !r.endsWith("[") ) r += ",";
r += f.getName() + "=";
try {
Class t = f.getType();
Object val = f.get(obj);
if( t.isPrimitive() ) r += val;
else r += toString(val);
}catch( Exception e ) {
e.printStackTrace();
}
}
}
r += "]";
cl = cl.getSuperclass();
}while( cl != null );
return r;
}
}
- 思路简述:
- 总体上,将对象分成三种情况讨论:
- String对象,直接返回。
- 数组对象,按照数组的格式返回
- 非String且非数组的对象,按格式将所有域返回
- 因为对象里面可能嵌套对象,故:需要调用自身toString方法。为避免无限递归的情况发生,增添一个域,记录已经处理过的对象。
- 关键的方法:java.lang.reflect.Array.get(obj,i) 以及 java.lang.reflect.Field.get(obj,i)。可以获得数组的元素对象或域对象。
- 访问私有域的值需要访问权限,AccessibleObject.setAccessible( fields, true ) 可以获得权限,否则会抛出 IllegalAccessException 异常。
- 使用反射编写泛型数组代码:
- java.lang.reflect.Array:
- static Object get( Object array, int index )
- static xxx getXxx( Object array, int index ) //xxx是 int long double byte等基本类型
- static void set( Object array, int index, Object newValue )
- static void setXxx( Object array, int index, xxx newValue )
- static int getLength( Object array )
- static Object newInstance( Class componentType, int length )
- static Object newInstance( Class[] componentType, int[] length )
- Java数组会记住每个元素的类型,即创建数组时的元素类型。
- 将一个Employee[]数组临时转换成Object[]数组,再转换回来是没问题的。但是,一开始就是Object[]的数组,永远不能转换成Employee[]数组(抛出 ClassCastException)。
- copyOf方法的参数声明为 Object 类型 而不是 Object[] 类型。因为,比如 int[] 可以转换为 Object ,但是不能转换成对象数组。
- 示例程序:(通用的泛型数组拷贝程序)
package coreJava_5_13;
import java.lang.reflect.*;
import java.util.*;
public class CopyOfTest {
public static void main( String args[] ) {
int[] a = { 1, 2, 3 };
String[] b = { "I", "love", "u" };
a = (int[]) goodCopyOf( a, 10 );
b = ( String[] ) goodCopyOf( b, 10 );
System.out.println( Arrays.toString(a) );
System.out.println( Arrays.toString(b) );
//double[] c = { 1.0, 1.1, 1.2 };
//the following call will generate an exception
//b = (String[]) badCopyOf( b, 10 );
}
public static Object goodCopyOf( Object obj, int len ) {
Class cl = obj.getClass();
if( !cl.isArray() ) return null;
Class type = cl.getComponentType();
int length = Array.getLength(obj);
Object newArray = Array.newInstance(type, len);
System.arraycopy(obj, 0, newArray, 0, Math.min(length, len) );
return newArray;
}
public static Object[] badCopyOf( Object[] obj, int len ) {
Object[] newArray = new Object[len];
System.arraycopy(obj, 0, newArray, 0, Math.min(obj.length, len) );
return newArray;
}
}
- 总结:以上程序是一个实用的通用泛型数组拷贝程序。
- 拷贝传入的参数类型需是 Object 类型,而不是 Object[] 类型,原因:基本类型的数组不能转换为 Object[] 类型,只能转换成 Object 类型。
- 拷贝成功后返回的类型也应该是 Object 类型 而不是 Object[] 类型,因为:Object 类型能够通过强制转换转换成其他类型,但是 Object[] 类型无法转换成 其他对象数组类型。
- 借助的方法: Class类的 getComponentType()方法 可以获得 数组的类型信息。 Array类的 newInstance()方法 可以动态创建一个任意类型的对象数组。 System.arraycopy 方法 则可以帮助我们完成拷贝。
- 调用任意的方法:
- 方法指针很危险,常带来隐患,接口是一种更好的解决方案。然而,反射机制允许调用任意的方法。
- Method类的invoke方法,允许调用包装在Method对象中的方法。签名:Object invoke( Object obj, Object... args) 。第一个参数为隐式参数。对于静态方法,第一个参数可以被忽略,即可以将之设置为null。
- 如果返回类型是基本类型,那么invoke返回的是其包装器类型。
- 获得Method对象的方法: Method getMethod(String name, Class... parameterTypes)
Method m1 = Employee.class.getMethod("getName");
Method m2 = Employee.class.getMethod("raiseSalary", double.class);
- 可以使用method对象实现函数指针的所有操作。
- invoke方法易出错,导致抛出异常。
- invoke的参数和返回值都是Object类型的,意味着需要进行多次类型转换,会降低编译器检查错误的帮助。
- 反射获得的方法指针比直接调用慢。
- 非必要不用Method对象,优先考虑使用 接口 以及 lambda表达式 。
- 示例程序:(通用制表以及两个测试程序)
package coreJava_5_13;
import java.lang.reflect.*;
public class MethodTableTest {
public static void main( String args[] ) {
try {
Method sqrt = Math.class.getMethod("sqrt", double.class);
Method square = MethodTableTest.class.getMethod("square", double.class);
printTable( 1, 10, 10, square );
printTable( 1, 10, 10, sqrt );
}catch( Exception e ) {
e.printStackTrace();
}
}
public static double square( double x ) {
return x*x;
}
public static void printTable( double from, double to, int n, Method f ) {
System.out.println( f );
double dx = ( to - from ) / ( n - 1 );
for( double x = from; x <= to; x += dx ) {
try {
double y = (Double) f.invoke(null, x);
System.out.printf("%10.4f | %10.4f%n", x, y );
}catch( Exception e ) {
e.printStackTrace();
}
}
}
}