天天看点

java基础之反射1 RTTI和Class2 反射概念3获取类相关信息

工作中有活干会抱怨,如若没活干呢?

1 RTTI和Class

运行时类型识别(RTTI, Run-Time Type Identification),RTTI是能在运行时能够自动识别每个编译时已知的类型。java多态就是依赖RTTI实现的。

不太懂,简单解释就是子类继承父类,函数参数传入父类类型,如果传入的是子类,运行过程中就可以强制转成子类而不会报错。

java中利用Class对象描述类的相关信息,JVM利用类加载器把class字节码文件加载到内存中就会对应一个Class对象,可以利用生成的Class创建对应类的对象。

如何获取Class(以com.ldx.Person类为例子,Person p = new Person()):

  • 调用对象的Class<?> c = p.getClass()
  • 方法接收一个字符串作为参数,该字符串是类的名字。这将返回相应的Class类对象。

    Class<?> c = Class.forName(“com.ldx.Person”)

  • 方法是直接调用类的class成员。这将返回相应的Class类对象.

    Class<?> c = Person.class

    Class是支持泛型的,所以获取Class时可以指定类型,但为了放松限制可以使用泛型的通配符,在javaSE5 中,Class<?> 优于平凡的Class,即使它们是等价的,平凡的Class不会产生警告信息,但Class<?>的好处是它表示你并非是碰巧或者疏忽而是使用了一种非具体的引用,其实两者是一样的。

Class 方法:

//生成Class

java.lang.Class.forName(String)

//利用Class 生成对象

java.lang.Class.newInstance()

//是否有注解

java.lang.Class.isAnnotation()

//获取名字

java.lang.Class.getName()

//获取类加载器

java.lang.Class.getClassLoader()

//获取类自身相关信息

java.lang.Class.getTypeParameters()

java.lang.Class.getSuperclass()

java.lang.Class.getGenericSuperclass()

java.lang.Class.getPackage()

java.lang.Class.getInterfaces()

java.lang.Class.getGenericInterfaces()

java.lang.Class.getComponentType()

java.lang.Class.getModifiers()

java.lang.Class.getSimpleName()

//获取类属性方法的信息

java.lang.Class.getFields()

java.lang.Class.getMethods()

java.lang.Class.getConstructors()

java.lang.Class.getField(String)

java.lang.Class.getMethod(String, Class<?>…)

java.lang.Class.getConstructor(Class<?>…)

java.lang.Class.getDeclaredClasses()

java.lang.Class.getDeclaredFields()

java.lang.Class.getDeclaredMethods()

java.lang.Class.getDeclaredConstructors()

java.lang.Class.getDeclaredField(String)

java.lang.Class.getDeclaredMethod(String, Class<?>…)

java.lang.Class.getDeclaredConstructor(Class<?>…)

//读取资源

java.lang.Class.getResourceAsStream(String)

//获取注解

java.lang.Class.getAnnotation(Class)

java.lang.Class.isAnnotationPresent(Class<? extends Annotation>)

java.lang.Class.getAnnotationsByType(Class)

java.lang.Class.getAnnotations()

java.lang.Class.getDeclaredAnnotation(Class)

java.lang.Class.getDeclaredAnnotationsByType(Class)

java.lang.Class.getDeclaredAnnotations()

2 反射概念

RTTI和Class是实现反射的基础,有些时候我们编码拿不到某些类的源码,但是又想实例化对象或者修改对象,就需要用到反射。

反射:反射能够在程序运行构成中修改程序的行为,深点说就是利用Class生成对象之后,还可以在内存中修改对象的某些行为。

我们写的反射代码其实在编译时是不确定哪个类会被加载的,程序运行后才会真正的获取对象,所以可以使用编译期并不知道的类。知道这个特点就可以使用一些非当前项目存在的类,然后实例化修改。

3获取类相关信息

从上面的分析指导反射最终会操作Class对象,那反射后可以获取什么呢,又如何修改呢?

猜测能获取的信息,能获取的信息包括属性,方法,构造函数访问修饰符,注解,接口等

3.1 获取类的访问修饰符

java获取访问修饰符包括:public,protected,private,abstract ,static

public static  void getModifier() {
		try {
			Class<?> clazz = Class.forName("com.ldx.ref.Demo");
			int data = clazz.getModifiers();
			System.out.println(data);
			System.out.println(Modifier.toString(data));
			System.out.println(Modifier.isPublic(data));
			System.out.println(Modifier.isAbstract(data));
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
           

result:

1025

public abstract

true

true

getModifiers()函数获取到的是数字,Modifier.toString把数字转成修饰符,查看源码知道java工程师利用了位运算。

public class Modifier {
//判断是不是某种修饰符
    public static boolean isPublic(int mod) {
        return (mod & PUBLIC) != 0;
    }
    public static boolean isPrivate(int mod) {
        return (mod & PRIVATE) != 0;
    }
    public static boolean isStatic(int mod) {
        return (mod & STATIC) != 0;
    }
    public static boolean isFinal(int mod) {
        return (mod & FINAL) != 0;
    }
    public static boolean isSynchronized(int mod) {
        return (mod & SYNCHRONIZED) != 0;
    }
    public static boolean isVolatile(int mod) {
        return (mod & VOLATILE) != 0;
    }
    public static boolean isTransient(int mod) {
        return (mod & TRANSIENT) != 0;
    }
    public static boolean isNative(int mod) {
        return (mod & NATIVE) != 0;
    }
    public static boolean isInterface(int mod) {
        return (mod & INTERFACE) != 0;
    }
    public static boolean isAbstract(int mod) {
        return (mod & ABSTRACT) != 0;
    }
    public static boolean isStrict(int mod) {
        return (mod & STRICT) != 0;
    }
    //如何利用mod输出修饰符的标识
    public static String toString(int mod) {
        StringBuilder sb = new StringBuilder();
        int len;

        if ((mod & PUBLIC) != 0)        sb.append("public ");
        if ((mod & PROTECTED) != 0)     sb.append("protected ");
        if ((mod & PRIVATE) != 0)       sb.append("private ");

        /* Canonical order */
        if ((mod & ABSTRACT) != 0)      sb.append("abstract ");
        if ((mod & STATIC) != 0)        sb.append("static ");
        if ((mod & FINAL) != 0)         sb.append("final ");
        if ((mod & TRANSIENT) != 0)     sb.append("transient ");
        if ((mod & VOLATILE) != 0)      sb.append("volatile ");
        if ((mod & SYNCHRONIZED) != 0)  sb.append("synchronized ");
        if ((mod & NATIVE) != 0)        sb.append("native ");
        if ((mod & STRICT) != 0)        sb.append("strictfp ");
        if ((mod & INTERFACE) != 0)     sb.append("interface ");

        if ((len = sb.length()) > 0)    /* trim trailing space */
            return sb.toString().substring(0, len-1);
        return "";
    }
    //修饰符的标识
    public static final int PUBLIC           = 0x00000001;
    public static final int PRIVATE          = 0x00000002;
    public static final int PROTECTED        = 0x00000004;
    public static final int STATIC           = 0x00000008;
    public static final int FINAL            = 0x00000010;
    public static final int SYNCHRONIZED     = 0x00000020;
    public static final int VOLATILE         = 0x00000040;
    public static final int TRANSIENT        = 0x00000080;
    public static final int NATIVE           = 0x00000100;
    public static final int INTERFACE        = 0x00000200;
    public static final int ABSTRACT         = 0x00000400;
    public static final int STRICT           = 0x00000800;
    static final int BRIDGE    = 0x00000040;
    static final int VARARGS   = 0x00000080;
    static final int SYNTHETIC = 0x00001000;
    static final int ANNOTATION  = 0x00002000;
    static final int ENUM      = 0x00004000;
    static final int MANDATED  = 0x00008000;
    static boolean isSynthetic(int mod) {
      return (mod & SYNTHETIC) != 0;
    }

    static boolean isMandated(int mod) {
      return (mod & MANDATED) != 0;
    }
//下面的几个变量是把修饰符进行或操作,最终生成一个mod
    private static final int CLASS_MODIFIERS =
        Modifier.PUBLIC         | Modifier.PROTECTED    | Modifier.PRIVATE |
        Modifier.ABSTRACT       | Modifier.STATIC       | Modifier.FINAL   |
        Modifier.STRICT;
    private static final int INTERFACE_MODIFIERS =
        Modifier.PUBLIC         | Modifier.PROTECTED    | Modifier.PRIVATE |
        Modifier.ABSTRACT       | Modifier.STATIC       | Modifier.STRICT;

    private static final int CONSTRUCTOR_MODIFIERS =
        Modifier.PUBLIC         | Modifier.PROTECTED    | Modifier.PRIVATE;

    private static final int METHOD_MODIFIERS =
        Modifier.PUBLIC         | Modifier.PROTECTED    | Modifier.PRIVATE |
        Modifier.ABSTRACT       | Modifier.STATIC       | Modifier.FINAL   |
        Modifier.SYNCHRONIZED   | Modifier.NATIVE       | Modifier.STRICT;

    private static final int FIELD_MODIFIERS =
        Modifier.PUBLIC         | Modifier.PROTECTED    | Modifier.PRIVATE |
        Modifier.STATIC         | Modifier.FINAL        | Modifier.TRANSIENT |
        Modifier.VOLATILE;
    private static final int PARAMETER_MODIFIERS =
        Modifier.FINAL;
    static final int ACCESS_MODIFIERS =
        Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE;
    public static int classModifiers() {
        return CLASS_MODIFIERS;
    }

    public static int interfaceModifiers() {
        return INTERFACE_MODIFIERS;
    }
    public static int constructorModifiers() {
        return CONSTRUCTOR_MODIFIERS;
    }

    public static int methodModifiers() {
        return METHOD_MODIFIERS;
    }
    public static int fieldModifiers() {
        return FIELD_MODIFIERS;
    }
    public static int parameterModifiers() {
        return PARAMETER_MODIFIERS;
    }
}
           

获取Class的成员

获取Field

Class中关于获取字段的函数有四个,getFields(),getField(String name),Field[] getDeclaredFields(),Field getDeclaredField(String name),前两个获取类及其父类的public修饰的方法,后两个可以获取包括private修饰的所有方法。

public abstract class Demo {
	
	private String str1;
	public String str2;
	protected String str3;
	String str4;

	public void getData() {
		
	}
	
	public abstract void getData2();
}

public static  void getField() {
		try {
			Class<?> clazz = Class.forName("com.ldx.ref.Demo");
			try {
				//正常获取
				Field str2f1 = clazz.getField("str2");
				System.out.println(str2f1.getName());
			} catch (NoSuchFieldException e) {
				e.printStackTrace();
			} catch (SecurityException e) {
				e.printStackTrace();
			}
			try {
				//正常获取
				Field str2f2 = clazz.getDeclaredField("str2");
				System.out.println(str2f2.getName());
			} catch (NoSuchFieldException e) {
				e.printStackTrace();
			} catch (SecurityException e) {
				e.printStackTrace();
			}
			
			try {
				//可以获取
				Field str1f1 = clazz.getDeclaredField("str1");
				Field str3f1= clazz.getDeclaredField("str3");
				Field str4f1 = clazz.getDeclaredField("str4");
				System.out.println(str1f1.getName());
				System.out.println(str3f1.getName());
				System.out.println(str4f1.getName());
			} catch (NoSuchFieldException e) {
				e.printStackTrace();
			} catch (SecurityException e) {
				e.printStackTrace();
			}
			
			try {
				//都没有成功
				Field str3f2 = clazz.getField("str3");
				Field str4f2= clazz.getField("str4");
				Field str1f2 = clazz.getField("str1");
				//System.out.println(str3f2.getName());
				//System.out.println(str4f2.getName());
				//System.out.println(str1f2.getName());
			} catch (NoSuchFieldException e) {
				e.printStackTrace();
			} catch (SecurityException e) {
				e.printStackTrace();
			}
			
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
           

//获取所有的属性,但不包括从父类继承下来的属性

public Field[] getDeclaredFields()

//获取自身的所有的 public 属性,包括从父类继承下来的。

public Field[] getFields()

获取方法(普通方法&构造方法)

获取普通方法:

同样有四个方法,并且用法类似:

public Method getDeclaredMethod(String name, Class<?>… parameterTypes)

public Method getMethod(String name, Class<?>… parameterTypes)

public Method[] getDeclaredMethods() throws SecurityException

public Method getMethod(String name, Class<?>… parameterTypes)

public static void getMethod() {
		try {
			Class<?> clazz = Class.forName("com.ldx.ref.Demo");
			try {
				Method metho1 = clazz.getMethod("getData1", String.class);
			} catch (NoSuchMethodException e) {
				e.printStackTrace();
			} catch (SecurityException e) {
				e.printStackTrace();
			}
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
           

获取构造函数:

public Constructor getDeclaredConstructor(Class<?>… parameterTypes)

public Constructor getConstructor(Class<?>… parameterTypes)

public Constructor<?>[] getDeclaredConstructors() throws SecurityException

public Constructor<?>[] getConstructors() throws SecurityException

构造函数无法继承所以getConstructors无法获取父类构造函数。

public static void getMethod() {
		try {
			Class<?> clazz = Class.forName("com.ldx.ref.Demo");
			try {
				Method metho1 = clazz.getMethod("getData1", String.class);
				Constructor<?>[] conmethods = clazz.getConstructors();
			} catch (NoSuchMethodException e) {
				e.printStackTrace();
			} catch (SecurityException e) {
				e.printStackTrace();
			}
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
           

修改使用获取到类的成员

利用反射获取运行时class的信息,是为了读取或者修改,下面逐一对获取到的信息进行讲解。

修饰符(类,Field,Method,ConstructorMethod等)

修饰符只能获取无法修改,下面对类成员的操作都不会再涉及修饰符。

Field

获取属性之后,可以读取属性的值,修改属性的值,都比较简单,有一点需要注意如果要使用反射获取到的private类型的属性,则需要调用fieldb.setAccessible(true),否则会报IllegalAccessException错误。

try {
			Demo demo = new Demo();
			demo.setStr1("aaa");
			demo.setStr2("aaa");
			demo.setStr3("aaa");
			demo.setStr4("aaa");
			
			Class<?> clazz = Class.forName("com.ldx.ref.Demo");
			try {
				//正常获取
				Field str2f1 = clazz.getField("str2");
				
				String str2str;
				try {
					//获取值
					str2str = (String) str2f1.get(demo);
					//int num = str2f1.getInt(demo);
					System.out.println(str2str);
					
					//设置值
					str2f1.set(demo, "sdfsdfsf");
					//str2f1.setInt(demo, 2);
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}
				
           
//private 属性
try {
				//正常获取
				Field str1f2 = clazz.getDeclaredField("str1");
				String str1f2str;
				try {
					str1f2.setAccessible(true);
					str1f2str = (String) str1f2.get(demo);
					System.out.println(str1f2str);
					
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}
				try {
					//str1f2.setAccessible(true);
					str1f2.set(demo, "bbb");
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}
				
			} catch (NoSuchFieldException e) {
				e.printStackTrace();
			} catch (SecurityException e) {
				e.printStackTrace();
			}
           

Method

getName():获取方法名

Parameter[] getParameters():获取方法参数

Class<?>[] getParameterTypes():获取所有参数类型

Type[] getGenericParameterTypes():获取所有参数类型包括泛型

Class<?> getReturnType():获取返回值类型,

Type getGenericReturnType():获取返回值类型包括泛型

Class<?>[] getExceptionTypes():获取异常类型

Type[] getGenericExceptionTypes():获取异常类型带参数

上面的方法都比较简单,暂时不写例子后续会补上。

方法的执行

获取方法最终的目的是为了执行方法,Object invoke(Object obj, Object… args)

Method 调用 invoke() 的时候,存在许多细节:

  • invoke() 方法中第一个参数 Object 是真正的对象实例,如果这个方法是一个静态方法,那么 ojb 为 null,后面的可变参数 Object 对应的自然就是参数,如果没有参数为null。
  • invoke() 返回值是 Object对象,要进行强制转换。
  • Method 调用 invoke() 的时候,如果存在异常,由 Method 统一抛出 InvocationTargetException。而通过 InvocationTargetException.getCause() 可以获取真正的异常。
  • private类型的方法和filed一样需要特殊处理
public static void getMethod() {
		try {
			Demo demo = new Demo();
			demo.setStr1("aaaa");
			demo.setStr2("aaaa");
			demo.setStr3("aaaa");
			demo.setStr4("aaaa");
			Class<?> clazz = Class.forName("com.ldx.ref.Demo");
			try {
				Method metho1 = clazz.getMethod("setStr1", String.class);
				try {
					metho1.invoke(demo, "bbbbb");
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (InvocationTargetException e) {
					e.printStackTrace();
				}
				
			} catch (NoSuchMethodException e) {
				e.printStackTrace();
			} catch (SecurityException e) {
				e.printStackTrace();
			}
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
           

构造函数

构造函数不同于普通函数,反射中构造函数可以创建对象实例,class也可以创建对象实例:

Class.newInstance() 和 Constructor.newInstance()

  • Class.newInstance() 只能调用无参的构造方法,而 Constructor.newInstance() 则可以调用任意的构造方法。
  • Class.newInstance() 通过构造方法直接抛出异常,而 Constructor.newInstance() 会把抛出来的异常包装到 InvocationTargetException 里面去,这个和 Method 行为一致。
  • Class.newInstance() 要求构造方法能够被访问,而 Constructor.newInstance() 却能够访问 private 修饰的构造器。

还有反射中的数组,枚举,会在后面另外开文章进行讲解。