Java基础知识:反射

  • 反射:能够分析类能力的程序。
  1. 反射机制用途:
    1. 运行时分析类的能力。
    2. 运行时查看对象。
    3. 实现通用的数组操作代码
    4. 利用Method对象(类似于C++的函数指针)
  2. Class类:
    1. 程序运行期间,Java运行时系统始终为所有对象维护一个被称为运行时的类型标识。跟踪着每个对象所属的类。保存这些信息的类成为 Class类
    2. Object类的 getClass()方法 会返回一个 Class类型 的实例。
    3. 获得 Class类对象的方法:
      1. Object类的 getClass()方法
      2. Class.forName(className) 静态方法
      3. 对于类型T → T.class
    4. Class类 实际上是一个泛型类,Employee.class 的类型是 Class
    5. 虚拟机为每个类型管理一个Class对象,故可以用 ==运算符 比较两个类对象。如:if(e.getClass() == Employee.class)... 。
    6. e.getClass().newInstance()。newInstance()方法 可以动态地创建一个类的实例,调用的是默认的构造器。如果需要提供参数,那么就使用 Constructor类中的newInstance()方法。
  3. 利用反射分析类的能力:
    1. 反射机制最重要的内容——检查类的结构
    2. java.lang.reflect 包中有三个类:FieldMethodConstructor 分别用于描述类的 域、方法 和 构造器。
  4. java.lang.Class:
    1. Field[] getFields()  //返回包含Field对象的数组,含 这个类或其超类的公有域
    2. Field[] getDeclaredFields()  //记录这个类的所有域。 
    3. Method[] getMethod() //返回所有的公有方法(含继承)
    4. Method[] getDeclaredFields() //返回这个类或接口的所有方法(不含继承)
    5. Constructor[] getConstructors() 
    6. Constructor[] getDeclaredConstructors()
  5. java.lang.reflect.Field/Method/Constructor
    1. Class getDeclaredClass()
    2. Class[] getExceptionType() // Method和Constructor
    3. int getModifiers()
    4. String getName()
    5. Class[] getParameterTypes() // Method和 Constructor
    6. Class[] getReturnType() // Method
  6. java.lang.reflect.Modifier
    1. 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() + ";" );
		}
	}
}

 

  • 在运行时使用反射分析对象:
  1. 进一步查看数据域的实际内容
  2. 查看对象域的关键方法是Field类中的 get方法
  3. java.lang.reflect.AccessibleObject
    1. void setAccessible( boolean flag )
    2. boolean isAccessible()
    3. static void setAccessible( AccessibleObject[] array, boolean flag )
  4. java.lang.Class
    1. Field getField( String name)
    2. Field getDeclaredField( String name )
  5. java.lang.reflect.Field
    1. Object get( Object obj )
    2. void set( Object obj, Object newValue )
  • 示例程序:( 泛型toString方法 )
package coreJava_5_13;

import java.util.*;

public class ObjectAnalyzerTest {
	public static void main( String args[] ) {
		ArrayList 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 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;
	}
}
 
  
  • 思路简述:
  1. 总体上,将对象分成三种情况讨论:
    1. String对象,直接返回。
    2. 数组对象,按照数组的格式返回
    3. 非String且非数组的对象,按格式将所有域返回
  2. 因为对象里面可能嵌套对象,故:需要调用自身toString方法。为避免无限递归的情况发生,增添一个域,记录已经处理过的对象。
  3. 关键的方法:java.lang.reflect.Array.get(obj,i) 以及 java.lang.reflect.Field.get(obj,i)。可以获得数组的元素对象或域对象。
  4. 访问私有域的值需要访问权限,AccessibleObject.setAccessible( fields, true ) 可以获得权限,否则会抛出 IllegalAccessException 异常。

 

  • 使用反射编写泛型数组代码:
  1. java.lang.reflect.Array:
    1. static Object get( Object array, int index )
    2. static xxx getXxx( Object array, int index ) //xxx是 int long double byte等基本类型
    3. static void set( Object array, int index, Object newValue )
    4. static void setXxx( Object array, int index, xxx newValue )
    5. static int getLength( Object array )
    6. static Object newInstance( Class componentType, int length )
    7. static Object newInstance( Class[] componentType, int[] length )
  2. Java数组会记住每个元素的类型,即创建数组时的元素类型。
  3. 将一个Employee[]数组临时转换成Object[]数组,再转换回来是没问题的。但是,一开始就是Object[]的数组,永远不能转换成Employee[]数组(抛出 ClassCastException)。
  4. 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 方法 则可以帮助我们完成拷贝。

 

 

  • 调用任意的方法:
  1. 方法指针很危险,常带来隐患,接口是一种更好的解决方案。然而,反射机制允许调用任意的方法
  2. Method类的invoke方法,允许调用包装在Method对象中的方法。签名:Object invoke( Object obj,  Object... args) 。第一个参数为隐式参数。对于静态方法,第一个参数可以被忽略,即可以将之设置为null。
  3. 如果返回类型是基本类型,那么invoke返回的是其包装器类型。
  4. 获得Method对象的方法: Method getMethod(String name, Class... parameterTypes)
Method m1 = Employee.class.getMethod("getName");
Method m2 = Employee.class.getMethod("raiseSalary", double.class);
  1. 可以使用method对象实现函数指针的所有操作。
  2. invoke方法易出错,导致抛出异常。
  3. invoke的参数和返回值都是Object类型的,意味着需要进行多次类型转换,会降低编译器检查错误的帮助。
  4. 反射获得的方法指针比直接调用
  5. 非必要不用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();
			}
		}
	}
}

 

你可能感兴趣的:(Java知识学习)