Java反射

获取Class对象

有三种方式获取Class对象。

  • 类名.class
    如果JVM里面没有该类,则将类装入内存里,但不会初始化代码,返回Class的对象
  • Class.forName(“包名+类名”)
    同上,但会初始化静态代码。
  • 实例对象.getClass()
    此时对象已经新建了,新建对象会把静态和非静态的方法属性都初始化了。此时getClass()返回引用真正所属类的Class对象。动态绑定中,子类对象引用赋给父类对象的引用变量需要注意一下这点。
    这三个方法其实都是同一个Class对象,在生成Class对象时候,他们共同都会先判断内存里是否已经加载了。
public class Test {
	public static void main(String[] args) {

		try {		

			// 测试.class
			Class testTypeClass = TestClassType.class;
			System.out.println("测试.class---" + testTypeClass);

			// 测试Class.forName()
			Class testTypeForName = Class.forName("TestClassType");
			System.out.println("测试Class.forName()---" + testTypeForName);
			
			// 测试Object.getClass()
			TestClassType testTypeGetClass = new TestClassType();
			System.out.println("测试Object.getClass()---" + testTypeGetClass.getClass());


			System.out.println("\n------------测试是否相等------------");
	        //the same class object, one achieved statically and one dynamically.
	        System.out.println(testTypeGetClass.getClass() == TestClassType.class);
	        //the same class object using forName() to get class name dynamically 
	        System.out.println(Class.forName("TestClassType") == TestClassType.class);
	        //different class. B is not A!
	        TestClassType B = new TestClassB();
	        System.out.println(B.getClass() == TestClassType.class);

		} catch (ClassNotFoundException e) {

			// TODO Auto-generated catch block

			e.printStackTrace();

		}
	}
}

 class TestClassType {

	// 构造函数

	public TestClassType() {

		System.out.println("----构造函数---");

	}

	// 静态的参数初始化

	static {

		System.out.println("---静态的参数初始化---");

	}

	// 非静态的参数初始化

	{

		System.out.println("----非静态的参数初始化---");

	}

}

class TestClassB extends TestClassType {
	
}

运行结果

测试.class---class TestClassType
---静态的参数初始化---
测试Class.forName()---class TestClassType
----非静态的参数初始化---
----构造函数---
测试Object.getClass()---class TestClassType

------------测试是否相等------------
true
true
----非静态的参数初始化---
----构造函数---
false

可以看出,.class不初始化代码,Class.forName()初始化了静态属性,Object.getClass()是在队形的静态、非静态方法属性都建立好了再调用,并不会重复新建。
false是父类和子类的class对象是不同的,即使是子类把引用赋予了父类引用(动态绑定)。

getCalss

该方法出自Object.class里面。

	public final native Class<?> getClass()

获取Class对象时候用<类名>.class获取,接口同样。
Class<Date> cls = Date.class;

基本类型没有getClass方法, 但有对应的Class 对象, 类型参数为对应的包装类型。

	Class<Integer> intCls = int.class; 
	Class<Byte> byteCls = byte.class;
	Class<Character> charCls = char.class; 
	Class<Double> doubleCls = double.class;

void
Class<Vode> vodeCls = vode.class;
数组
每个维度一个类型

	String[] strArr = new String[10]; 
	int[][] twoDimArr = new int[3][2]; 
	int[] oneDimArr = new int[10]; 
	Class<? extends String[]> strArrCls = strArr.getClass(); 
	Class<? extends int[][]> twoDimArrCls = twoDimArr.getClass(); 
	Class<? extends int[]> oneDimArrCls = oneDimArr.getClass();

forName

静态方法,可以根据类名加载Class,获取Class对象。

		try {
			Class<?> cls = Class.forName("java.util.HashMap");
			System.out.println(cls.getName());
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}

此时费劲心思获取Class对象是为下一步对Class里面的属性进行操作。

Class类

每个已加载的类在内存里面都有一份类信息,每个对象都指向它所属类信息的引用。信息对应的类是java.lang.Class类。

1.获取名称信息

	public String getName()
	public String getSimpleName() 
	public String getCanonicalName() 
	public Package getPackage()
  • getName()
    返回的是java内部真正的名字。
      String.class.getName()
          returns "java.lang.String"
      byte.class.getName()
          returns "byte"
      (new Object[3]).getClass().getName()
          returns "[Ljava.lang.Object;"
      (new int[3][4][5][6][7][8][9]).getClass().getName()
          returns "[[[[[[[I"

如果遇到数组会返回“[”,一维就返回一个“[”,多少维就返回多少个“[”。数组类型是用符号表示,如下图。注意引用型的数据是带分号的“;”。

Element Type     Encoding
boolean     Z
byte     B
char     C
class or interface     Lclassname;
double     D
float     F
int     I
long     J
short     S
  • getSimpleName()
    返回不带包名的名字
int.class --> int
int[].class --> int[]
String[].class --> String[]
HashMap.class-->HashMap
  • getCanonicalName()
    返回包名和类名
int.class --> int
int[].class --> int[]
String[].class --> java.lang.String[]
HashMap.class-->java.util.HashMap
  • getPackage()
    获取包名。基础类的包名为null,数组也是。
int.class --> null
int[].class --> null
String[].class -->null
HashMap.class-->java.util

2.字段信息

类中静态变量和实例变量都是字段信息,用类Field表示,位于包java.lang.reflect下。
Class里面获取字段信息

	//返回所有的public字段,包括父类,如果没有则返回空数组
	public Field[] getFields() 
	//返回所有本类声明的字段,包括非public的,不包括父类
	public Field[] getDeclaredFields() 
	//返回本类或父类中指定名称的public字段,找不到抛出异常NoSuchFieldException
	public Field getField(String name) 
	//返回本类中声明的指定名称的字段,找不到抛出异常NoSuchFieldException
	public Field getDeclaredField( String name)

获取到Field对象就可以根据Field里面的一些方法属性来操作了。
Field里面的一些方法属性。

	//获取字段的名称 
	public String getName() 
	
	//字段权限
	//判断当前程序是否有该字段的访问权限
	public boolean isAccessible()
	//flag为true表示忽略java的访问检查机制,允许读写非public的字段 
	public void setAccessible(boolean flag) 
	
	//字段内容
	//获取指定对象obj中该字段的值
	public Object get(Object obj)
	//将指定对象终端该字段的值设置设为value
	value public void set(Object obj, Object value)

案例

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;

class Zlass {
	static public void getFieldMes() throws IllegalArgumentException, IllegalAccessException {
		List<String> obj = Arrays.asList(new String[] { "Hello,", "world!" });
		Class<?> cls = obj.getClass();
		for (Field f : cls.getDeclaredFields()) {
			f.setAccessible(true);
			System.out.println(f.getName() + " - " + f.get(obj));
		}
	}
}

public class Test {
 	public static void main(String[] args) {
		try {
			Zlass.getFieldMes();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

输出

serialVersionUID - -2764017481108945198
a - [Ljava.lang.String;@70dea4e

set(Object obj, Object value),如果是基本类型的数据,会自动与对应包装类型之间相互转化,静态对象可以为null。
在Field里面还有一堆get/set方法,用来获取基本类型。

	//获取返回字段的修饰符
	public int getModifiers()
	//返回字段的类型
	public Class<?> getType()
	public void setBoolean(Object obj, boolean z) 
	public boolean getBoolean(Object obj) 
	public void setDouble(Object obj, double d) 
	public double getDouble(Object obj) 
	//查询字段的注解信息
	public < T extends Annotation> T getAnnotation( Class< T> annotationClass) 
	public Annotation[] getDeclaredAnnotations()

下面是展示修饰符的一个例子


public class Student {
	public static final int MAX_NAME_LEN = 255;
}

public class Test {	
		public static void main(String[] args) {
			try {
				Field f = Student.class.getField("MAX_NAME_LEN");
				int mod = f.getModifiers();
				System.out.println(Modifier.toString(mod));
				System.out.println("isPublic: " + Modifier.isPublic(mod));
				System.out.println("isStatic: " + Modifier.isStatic(mod));
				System.out.println("isFinal: " + Modifier.isFinal(mod));
				System.out.println("isVolatile: " + Modifier.isVolatile(mod));
			} catch (NoSuchFieldException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (SecurityException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
}

Modifier类里面有各种修饰符判断。

运行结果

public static final
isPublic: true
isStatic: true
isFinal: true
isVolatile: false

3.方法信息

Class类里面获取方法信息的方式如下。

	//多个处理
	//返回所有的public方法,包括其父类的,如果没有方法,返回空数组
 	public Method[] getMethods() 
 	//返回本类声明的所有方法,包括非public的,但不包括父类的 
 	public Method[] getDeclaredMethods() 
	//单个处理 
	//返回本类或父类中指定名称和参数类型的public方法,找不到抛出异常NoSuchMethodException
	public Method getMethod(String name, Class<?>... parameterTypes) 
	//返回奔雷中声明的指定名称和参数类型的方法,找不到抛出异常NoSuchMethodException
 	public Method getDeclaredMethod(String name, Class<?>... parameterTypes)

Method类里面基本方法。

	//获取方法名称
 	public String getName()
  	//flag为true时候,忽略java访问检查机制,允许调用非public的方法
  	public void setAccessible( boolean flag)
   	//指定调用方法。在指定的obj上调用Method代表的方法,传递的参数列表为args
    public Object invoke(Object obj, Object... args) throws IllegalAccessException, Illegal- ArgumentException, InvocationTargetException

invoke方法上,如果Method是静态方法,可以为null,args可以为null,也可以为空的数组,调用返回值被包裹为Object输出。

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Test {

	public static void main(String[] args) {
		Class<?> cls = Integer.class;
		try {
			Method method = cls.getMethod("parseInt", new Class[] {String.class });
			//静态方法,这里可以为null
			System.out.println(method.invoke(null, "123"));
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

输出

123

Method里面还有获取修修饰符,参数,范沪指,注解等信息,这里不一一列举了。

4.创建对象和构造方法

Class类里面

	//创建对象
    public T newInstance()
        throws InstantiationException, IllegalAccessException
    //获取所有public构造方法,返回值可能为长度为0的空数组
    public Constructor<?>[] getConstructors() 
    //获取所有的构造方法,包括非public
    public Constructor<?>[] getDeclaredConstructors() 
    //获取指定参数类型的public构造方法,没有找到抛出异常NoSuchMethodException 
    public Constructor< T> getConstructor(Class<?>... parameterTypes)
    //获取指定参数类型的go欧洲方法,包括非public的,没找打抛出异常 NoSuchMethodException 
    public Constructor< T> getDeclaredConstructor( Class<?>... parameterTypes)
    

newInstance只能用在无参数的默认构造方法。如果想要建有参数方法,用java.lang.reflect.Constructor#newInstance(java.lang.Object...) Constructor.newInstance调用。

实例

		try {
			Map<String, Integer> map = HashMap.class.newInstance();
			map.put(" hello", 123);
		} catch (InstantiationException | IllegalAccessException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}

		try {
			Constructor<StringBuilder> contructor = StringBuilder.class.getConstructor(new Class[] { int.class });
			StringBuilder sb = contructor.newInstance(100);
		} catch (NoSuchMethodException | SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

5.类型检查和转换

Class类里面

	//判断变量指向的实际对象类型
 	public native boolean isInstance(Object obj);
 	//强制转型
 	public T cast(Object obj)
 	//检查参数类型cls能否赋给当前Class类型的变量
 	public native boolean isAssignableFrom(Class<?> cls);

范例

	if(list instanceof ArrayList){ 
		System.out.println("array list"); 
	}
	//对等下面代码
	Class cls = Class.forName("java.util.ArrayList"); 
	if(cls.isInstance(list)){ 
		System.out.println("array list"); 
	}

	//转型
	public static <T> T toType(Object obj, Class< T> cls){ 
		return cls.cast(obj); 
	}
	//检查参数类型cls能否赋给当前Class类型的变量,或者说是否父类或其本身关系
	Object.class.isAssignableFrom(String.class);
	String.class.isAssignableFrom(String.class);
	List.class.isAssignableFrom(ArrayList.class);

6.Class的类型信息检查

判断普通类,内部类,数组等。

 //判断是否数组
 public native boolean isArray() 
 //判断是否基本类型
 public native boolean isPrimitive()
 //判断是否接口
 public native boolean isInterface() 
 //判断是否枚举
 public boolean isEnum() 
 //注解判断
 public boolean isAnnotation() 
 //匿名内部类
 public boolean isAnonymousClass() 
 //方法外的成员类,不是匿名类
 public boolean isMemberClass() 
 //方法内的本地类,不是匿名类
 public boolean isLocalClass()

7.类的声明信息

修饰符,父类,接口,注解等。

//获取修饰符,返回值可通过Modifier类进行解读 
public native int getModifiers() 
//获取父类, 如果为Object,父类为null 
public native Class<? super T> getSuperclass() 
//对于类,为自己声明实现的所有接口,对于接口,为直接扩展的接口,不包括通过父类继承的 
public native Class<?>[] getInterfaces(); 
//自己声明的注解 
public Annotation[] getDeclaredAnnotations() 
//所有的注解,包括继承得到的 
public Annotation[] getAnnotations() 
//获取或检查指定类型的注解,包括继承得到的 
public < A extends Annotation> A getAnnotation(Class< A> annotationClass) 
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)

8.类加载

public static Class<?> forName(String className) 
public static Class<?> forName(String name, boolean initialize, ClassLoader loader)

initialize表示加载后,是否初始化代码(比如static代码)。第一个方法等同于Class. forName( className, true, currentLoader)
基本数据类型不能用forName。要该结果要自己处理。

public static Class<?> forName( String className) throws ClassNotFoundException{
	 if(" int". equals( className)){ 
	 	return int. class;
	 } 
	 return Class.forName(className);
}

java9还有另外一个forName

public static Class<?> forName(Module module, String name)

找不到类返回null

9.数组

Class类里面。

public native Class<?> getComponentType();

例子

		String[] arr = new String[] {};
		System.out.println(arr.getClass().getComponentType());

输出

class java.lang.String

更加细致的操作可以看看到java.lang.reflect.Array

newInstance(Class<?>, int)
newInstance(Class<?>, int...)
getLength(Object)
get(Object, int)
getBoolean(Object, int)
getByte(Object, int)
getChar(Object, int)
getShort(Object, int)
getInt(Object, int)
getLong(Object, int)
getFloat(Object, int)
getDouble(Object, int)
set(Object, int, Object)
setBoolean(Object, int, boolean)
setByte(Object, int, byte)
setChar(Object, int, char)
setShort(Object, int, short)
setInt(Object, int, int)
setLong(Object, int, long)
setFloat(Object, int, float)
setDouble(Object, int, double)
newArray(Class<?>, int)
multiNewArray(Class<?>, int[])

详细点

//创建指定元素类型、指定长度的数组。返回Object是为了方便不同类型的数组互换。 
public static Object newInstance(Class<?> componentType, int length) 
//创建多维数组 
public static Object newInstance(Class<?> componentType, int... dimensions) 
//获取数组array指定的索引位置index处的值 
public static native Object get(Object array, int index) 
//修改数组array指定的索引位置index处的值为value
public static native void set(Object array, int index, Object value) 
//返回数组的长度
public static native int getLength(Object array)
//其中一些参数设置
public static native double getDouble( Object array, int index) 
public static native void setDouble( Object array, int index, double d)
public static native void setLong( Object array, int index, long l)
public static native long getLong( Object array, int index)

例子

		int[] intArr = (int[]) Array.newInstance(int.class, 20);
		String[] strArr = (String[]) Array.newInstance(String.class, 20);

10.枚举

public T[] getEnumConstants()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值