笔记:Java反射机制基础

来源:互联网 发布:postgresql mysql迁移 编辑:程序博客网 时间:2024/05/16 16:42

反射在百度的解释为“一种计算机处理方式,是程序可以访问、检测和修改它本身状态或行为的一种能力”,即在运行的时候可以分析代码的内容,包括注解的内容,得到需要的信息。就像在常用的IDE如Eclipse中利用了反射实现了获取类的信息。

1.Class

java.lang.Class没有公共的构造方法,因此单独声明一个Class对象是不允许的;每个类型都有一个Class对象,这个Class对象记录了改类型的interface,field,method,annotation,generics等。任何类型,包括基础数据类型(int, short, long, float, double, char, btye, boolean)以及void类型都有对应的Class对象。

package com.test.ReflectTest;/** * @author 王狗蛋  * @date : 2017年8月13日 上午11:42:42 */public class Reflect {    public static void main(String...args) throws Exception {        // The first method to get a class object        // This method will throws ClassNotFoundException        Class<String> clz = (Class<String>)Class.forName("java.lang.String");        // The second method to get a class object        clz = String.class;        // Now let check the attributes of clz        System.out.println("The name of clz is " + clz.getName());        System.out.println("Is clz a primitive " + clz.isPrimitive());        // Of course we can check whether an object is specific class                       System.out.println("Is string is a String " + clz.isInstance("string"));        // And there is another method to print info        System.out.println(clz.toGenericString());        // Of course we can create an instance        String instance = clz.newInstance();        System.out.println(instance instanceof String);    }}

输出结果
The name of clz is java.lang.String
Is clz a primitive fase
Is string is a String true
public final class java.lang.String
true

从结果可知,clz的类名为java.lang.String,并且clz不是一个基础类型,“string”字符串是clz的类型,Class这个类是一个public final 类,由于被final修饰,Class在创建之后不会被修改,保证了代理时的安全性

2.interface
接口不能实例化并且包含了所有继承类应包含的所有方法,但接口可以被访问,也可以调用里面的方法。调用方法会在介绍invoke方法的笔记中记录 。
我们都知道String类继承Serializable,Comparable;但String类到底都继承了那些接口呢,我们来下面代码:

        /** 这段代码接上述代码 */        Class<?>[] inters = clz.getInterfaces();        for (Class<?> inter: inters) {            System.out.println(inter.toGenericString());        }

结果如下:
public abstract interface java.io.Serializable
public abstract interface java.lang.Comparable
public abstract interface java.lang.CharSequence

这三个接口就是String类继承的接口,其中Serializable就是可序列化接口,使得该类型的对象可以通过IO输出;Comparable用于实现俩个对象之间的比较,并通过方法compareTo(T object)来制定比较规则 ;CharSequence是String和AbstractStringBuilder的接口,具体内容在String笔记中介绍(我也不知道这个笔记什么时候整理,一般String和StringBuilder就够用了)

3.Field
域就是类的属性,我们可以通过一个Class对象来获取field
首先我们先弄一个类用来演示:

package com.test.ReflectTest;/** * @author 王狗蛋  * @date : 2017年8月13日 下午12:21:06 */public class DemoClass {    public int num1 = 1;    protected int num2 = 2;    int num3 = 3;    private int num4 = 4;    public static int num5 = 5````}

然后我们继续在main方法中演示:

import java.lang.reflect.Field;import java.lang.reflect.Modifier;        Class<?> clzDemo = Class.forName("com.test.ReflectTest.DemoClass");        Object obj = clzDemo.newInstance();        Field field = clzDemo.getField("num1");        System.out.println(field.toGenericString());        System.out.println(Modifier.toString(field.getModifiers()));        System.out.println(field.getType().toGenericString());        field.set(obj, 10);        System.out.println(field.get(obj));

输出结果:
public int com.test.ReflectTest.DemoClass.num1
public
int
10

首先先用clzDemo.getFIeid(“field name”)获取了name为”num1”的域
然后field.getModifiers()获取field的修饰类型,但获得的是一个int型对象,需要通过Modifier的静态方法toString(int)来输出;field.getType()获取域的数据类型;通过set方法来修改值并通过get方法获取值;

在set(Object obj, Obejct value)方法中修改对象为obj的域,但num5是一个静态属性,即在内存中只存放一个num5对象,即使创建多个对象,每个对象的num5都是引用内存中那唯一的num5域的值。
所以在set()方法时,如果该域是一个静态的域,那么在传入第一个参数时可以传入空指针null,即set(null,20);这时所有对象的num5都被改为20。

可能你会想这样一个一个域取很麻烦,当然有简单的方法:

        Field[] fields = cls.getFields();        for (Field field: fields) {            System.out.println(field.toString() + "\t" + field.get(obj));        }

输出结果:
public int com.test.ReflectTest.FieldDemo.num1 1
public static int com.test.ReflectTest.FieldDemo.num5 10

诶,等等,明明有5个域,你怎么就把public的域输出出来了呢?这是因为getFields()方法只会返回public域,对于private protected 和默认类型不会输出,这时我们就需要用到另一个方法getDeclaredFields()

        fields = cls.getDeclaredFields();        for (Field field: fields) {            //field.setAccessible(true);            System.out.println(field.toGenericString() + "\t" + field.get(obj));        }

这时候一定会报一个java.lang.IllegalAccessException异常,这个异常就是说没有访问权限造成的异常,这时把注释掉那行代码加上就不会有问题了,哈哈神奇吧。
输出结果:
public int com.test.ReflectTest.FieldDemo.num1 1
0
protected int com.test.ReflectTest.FieldDemo.num2 2
int com.test.ReflectTest.FieldDemo.num3 3
private int com.test.ReflectTest.FieldDemo.num4 4
public static int com.test.ReflectTest.FieldDemo.num5 5

为什么会这样呢,Accessible = true 会忽略访问权限的限制,具体会在invoke方法介绍的笔记中介绍。

4.Method
方法就是定义在类中的所有方法,构造方法需要用构造调用

import java.lang.reflect.Method;import java.lang.reflect.Parameter;import java.lang.reflect.Modifier;        Method[] methods = clz.getMethods();        for (Method method: methods) {            System.out.println("-----------------------");            System.out.println("name:" + method.getName());            System.out.print("defaultValue:" + method.getDefaultValue());            System.out.print("generic return type:" + method.getGenericReturnType());            System.out.print("return type:" + method.getReturnType());            System.out.print("modifiers:" + Modifier.toString(method.getModifiers()));            // Parameters            Parameter[] parameters = method.getParameters();            System.out.print(parameters.length + "parameters:");            for (Parameter para: parameters) {                System.out.print("name:" + para.getName());                System.out.print("type:" + para.getType().toString());            }            Class<?>[] parameterTypes = method.getParameterTypes();            System.out.print(parameterTypes.length + "parameters:");            for (Class<?> paraType: parameterTypes){                print("parameter smiple name: " + paraType.getSimpleName());                System.out.print("parameter type name: " + paraType.getName());                System.out.print("parameter modifier:" + Modifier.toString(paraType.getModifiers()));                System.out.print(paraType.toGenericString());            }            Class<?>[] exceptionTypes = method.getExceptionTypes();            System.out.print(exceptionTypes.length + "exception types:");            for (Class<?> exceptionType: exceptionTypes) {                System.out.print("exception name: " + exceptionType.getName());                System.out.print("exception simple name: " + exceptionType.getSimpleName());                System.out.print(exceptionType.toGenericString());            }            System.out.print("is accessible: " + method.isAccessible());            System.out.print("is varArgs: " + method.isVarArgs());        }

输出结果太多了,就是String类的所有可见方法,只弄一部分结果:
name:equals
defaultValue:null
generic return type:boolean
return type:boolean
modifiers:public
1parameters:
name:arg0
type:class java.lang.Object
1parameters:
parameter smiple name: Object
parameter type name: java.lang.Object
parameter modifier:public
public class java.lang.Object
0exception types:
is accessible: false
is varArgs: false

当然如果你想看不可见的方法,可以调用getDeclaredMethods()方法,
当然在获取一个方法对象后可以通过invoke(Object rescourse, Object…argument)方法调用,具体在invoke笔记中介绍。

6.构造器
我们可以通过类对象来获取该类的构造器

import java.lang.reflect.Constructor;        Constructor<?>[] constructors = clz.getConstructors();        for (Constructor<?> constructor: constructors) {            System.out.println("constructor " + constructor.getName());            System.out.println("number of parameters: " + constructor.getParameterCount());            System.out.println("modifiers: " + Modifier.toString(constructor.getModifiers()));            System.out.println(constructor.toGenericString());            Annotation[] annotaitons = constructor.getAnnotations();            for (Annotation annotation: annotaitons) {                System.out.println(annotation.toString());            }            System.out.println();        }

输出结果:
constructor java.lang.String
number of parameters: 3
modifiers: public
public java.lang.String(byte[],int,int)

constructor java.lang.String
number of parameters: 2
modifiers: public
public java.lang.String(byte[],java.nio.charset.Charset)

constructor java.lang.String
number of parameters: 2
modifiers: public
public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException

constructor java.lang.String
number of parameters: 4
modifiers: public
public java.lang.String(byte[],int,int,java.nio.charset.Charset)

constructor java.lang.String
number of parameters: 4
modifiers: public
public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException

constructor java.lang.String
number of parameters: 1
modifiers: public
public java.lang.String(java.lang.StringBuilder)

constructor java.lang.String
number of parameters: 2
modifiers: public
public java.lang.String(byte[],int)
@java.lang.Deprecated()

constructor java.lang.String
number of parameters: 4
modifiers: public
public java.lang.String(byte[],int,int,int)
@java.lang.Deprecated()

我仅仅保留了一部分结果,在代码中我要求输出可见的构造器的名字,参数,修饰类型,构造器的toGenericString(),以及构造器的注解,对于注解的含义在注解介绍笔记详细介绍,从结果看有@Deprecated注解,表示该方法被遗弃,不建议使用。

java反射机制的基础就像介绍在这里,后续将继续通过笔记介绍java反射的invoke,proxy代理等。
第一篇博客如有不足还请见谅,如有原理问题欢迎提出,共同学习。

原创粉丝点击