Java学习(一):反射

jkouu 83 0

反射是Java提供的另外一种访问类的途径——让我们再简洁一点,反射是一种访问类的途径。无论网上把反射说得多么重要,它仍然只是一种途径,一个工具。作为一种工具,它的使用并不复杂,复杂的地方在于在什么情况下决定使用它。所以,在这篇文章中,我只会讲反射相关方法,并不会讲在什么时候用它——这要由你们的灵感来决定,而且我保证即使在这里我没有讲到反射的应用场景,你们也会在别的文章中了解到。

反射作用的对象

反射将它能涉及到的对象分为了四大类:类和接口、成员变量、方法以及构造器。事实上,这四大类就是Java程序的全部了。下面我们依次来看一看反射是怎么作用到这四类的。

类和接口

类和接口部分是反射一定会作用到的地方,无论你最终的目的是想访问一个成员变量还是一个方法,首先你要先获取到它属于的类。这就是类和接口部分存在的意义。这部分相关的方法如下:

asSubclass(Class<U> clazz) //把传递的类的对象转换成代表其子类的对象
getClassLoader() //获得类的加载器
getClasses() //返回一个数组,数组中包含该类中所有公共类和接口类的对象
getDeclaredClasses() //返回一个数组,数组中包含该类中所有类和接口类的对象
forName(String className) //根据类名(有时候是完整名)返回类的对象
getName() //获得类的完整路径名字
getPackage() //获得类的包
getSimpleName() //获得类的名字
getSuperclass() //获得当前类继承的父类的名字
getInterfaces() //获得当前类实现的类或是接口

除了上述方法之外,你还可以直接通过类名.class或者类实例.getClass()来获取类对象。无论你调用了怎样的方法,在部分你总可以得到一个或者多个Class类的实例,这些实例就是类的对象(还记得JVM的类加载机制吗?那里有提到类对象,忘了的再去看)。拥有了类对象,你就可以去访问类的注解(以后会介绍)、成员变量、方法或者构造器了。

当然,除了获取属于它的元素,类对象也有针对自己的方法:

isAnnotation() //如果是注解类型则返回true 
isAnnotationPresent(Class<? extends Annotation> annotationClass) //如果是指定类型注解类型则返回true
isAnonymousClass() //如果是匿名类则返回true 
isArray() //如果是一个数组类则返回true 
isEnum() //如果是枚举类则返回true 
isInstance(Object obj) //如果obj是该类的实例则返回true 
isInterface() //如果是接口类则返回true 
isLocalClass() //如果是局部类则返回true 
isMemberClass() //如果是内部类则返回true

构造器

构造器其实就是构造方法,只不过反射专门把它拿了出来作为了Constructor这个单独的类。通过类对象获得构造器的方法有以下四种:

getConstructor(Class...<?> parameterTypes) //获得该类中与参数类型匹配的公有构造方法 
getConstructors() //获得该类的所有公有构造方法 
getDeclaredConstructor(Class...<?> parameterTypes) //获得该类中与参数类型匹配的构造方法 
getDeclaredConstructors() //获得该类所有构造方法

在这里要说一句,如果你使用的是getDeclaredConstructor()来获得一个构造器,那么这个构造器有可能是私有的。在这种情况下,你不能直接使用它,还需要获得它的访问权限。下面一个例子展示了这个过程:

class MyClass{
    public String name;
    private int value;
    
    private Myclass(){
        
    }
    
    public void setName(String name){
        this.name = name;
    }
    
    private void setValue(int value){
        this.value = value;
    }
}

class Main{
    public static void main(String[] args){
        Class mclass = Class.forName("MyClass);
        Constructor constructor = mclass.getDeclaredConstructor();
        constructor.setAccessible(true);
        Myclass instance = constructor.newInstance();
    }
}

在这段代码中,我们先获取到了MyClass的类对象,然后再获取到它的构造器。因为它的构造器是私有的,所以我们要获得它的访问权限,然后调用构造器的newInstance()方法创建一个实例。当然,如果构造器的方法是公共的,那就不用获得访问权限了。

成员变量

通过类对象获取成员变量对象(Field)的方法如下:

getField(String name) //获得某个公有的属性对象 
getFields() //获得所有公有的属性对象 
getDeclaredField(String name) //获得某个属性对象 
getDeclaredFields() //获得所有属性对象

方法都大同小异,所以我直接上例子了:

public void accessField(Myclass object){
    Field name = object.getClass().getField("name");
    Field value = object.getClass().getField("value");
    
    name.set(object, "haha");
    
    value.setAccessible(true);
    value.set(object, 5);
}

在这段代码中,我们先通过MyClass的一个实例获得了MyClass的类对象,再通过类对象获得了类的两个成员变量,最后我们通过Field的set()方法为这个实例的两个变量赋了值。除了set()方法,Field还有获取变量值的get()方法和比较变量值的equals()方法。

方法

通过类对象获取方法对象(Method)的方法如下:

public void accessMethod(MyClass object){
    Method setName = object.getClass().getMethod("setName", String);
    Method setValue = object.getClass().getMethod("setValue", Integer);
    
    setName.invoke(object, "haha");
    
    setValue.setAccessible(true);
    setValue.invoke(object, 5);
}

代码逻辑和前面的大同小异,这里就不多说了。要说明的就两点:getMethod()方法的参数除了方法名外还要指明参数的类;Method的invoke()方法用来调用一个实例的对应方法。

 

发表评论 取消回复
您必须 [登录] 才能发表评论!
分享