Java反射

25 篇文章 0 订阅

反射

能够分析类能力的程序称为反射(reflective)。反射机制的功能极其强大,反射机制可以用来:(1)在运行时分析类的能力。 (2)在运行时查看对象, 例如, 编写一个 toString方法供所有类使用。s(3)实现通用的数组操作代码。 (3)利用 Method 对象, 这个对象很像cpp中的函数指针。

通过代码分析展示反射的作用。

首先创建com.reflectTest包下Test.javaFa.javaSon.java三个类文件。代码如下:Test.java根据具体样例给出。
Fa.java

package com.reflectTest;

public class Fa {
    public final int a = 1;
}

Son.java

package com.reflectTest;

public class Son extends Fa{
    private double b = 2.0;
    public static int maxn = 1000;

    public Son() {}

    public Son(double b) {
        this.b = b;
    }

    public void sonFunc(String s) {
        System.out.println(s);
    }
}

Class类

在程序运行期间,Java运行时系统始终为所有的对象维护一个被称为运行时的类型标识。 这个信息跟踪着每个对象所属的类。虚拟机利用运行时类型信息选择相应的方法执行。
然而, 可以通过专门的 Java 类访问这些信息。保存这些信息的类被称为 Class, 这个名 字很容易让人混淆。Object 类中的 getClass( ) 方法将会返回一个 Class 类型的实例。

基本方法

getClass方法与getName方法

  • 通过ObjectgetClass方法可以获得一个对象的类,这是一个本地方法。
  • 通过ClassgetName方法,可以获得类的名字。

Test.java代码如下:

package com.reflectTest;

public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        Class c = new Son().getClass(); //通过Object类的getClass方法获得一个Class对象
        System.out.println(c);
        System.out.println(c.getName());    //getName()方法返回类的名字
    }
}

运行结果:
在这里插入图片描述

forName方法、newInstance方法和getSuperclass方法

  • Class的静态方法forName,通过输入的字符串获得一个Class对象,该方法可能抛出一个ClassNotFoundException异常。
  • ClassnewInstance方法,调用类的无参构造方法构造一个对象,该方法不能调用有参数构造方法。(ConstructornewInstance方法可以调用有参构造方法)方法可能抛出IllegalAccessExceptionInstantiationException异常。
  • ClassgetSuperclass方法,返回该Class对象的父类(不是返回Class类的父类)

Test.java代码如下:

package com.reflectTest;

public class Test {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        String className = "com.reflectTest.Son";    //使用forName构建一个Class对象表示Son类
        Class c = Class.forName(className); //可能抛出ClassNotFoundException
        double[] b = new double[0];
        Son s = (Son) c.newInstance();
        System.out.println(c.getName());    //输出Son类的类名
        System.out.println(c.getSuperclass().getName());    //输出Son类父类的类名
    }
}

在这里插入图片描述

虚拟机为每个类型管理一个 Class 对象

通过以下代码的输出结果为true可以验证。(怎么实现的。为什么?)

package com.reflectTest;

public class Test {
    public static void main(String[] args) {
        Class c1 = Fa.class;
        Class c2 = new Fa().getClass();
        Class c3 = new Fa().getClass();
        System.out.println(c1 == c2 && c2 == c3);
    }
}

在这里插入图片描述

利用反射分析类的能力

java.lang.reflect包中有三个类FieldMethodConstructor分别用于描述类的域、方法和构造器。下面一步一步分析这三个类的作用。

getModifiers方法

  • ClassFieldMethodConstructor这几个类均有getModifiers方法返回一个整数(表示修饰符),利用java.lang.reflett包中的Modifier类的静态方法toString分析getModifiers返回的整型数值。如下代码:

Test.java

package com.reflectTest;

import java.lang.reflect.Modifier;

public final class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        String className = "com.reflectTest.Son";
        Class c = Class.forName(className);
        String modifier = Modifier.toString(c.getModifiers());  //分析类
        System.out.println(modifier);   //Son类的修饰符是public
        System.out.println(Modifier.toString(Class.forName("com.reflectTest.Test").getModifiers()));
    }
}

在这里插入图片描述

获取类的域(Field)、构造器(Constructor)、方法(Method)

  • 使用Class类的getFieldsgetConstructorsgetMethods方法可以获得FieldConstructorMethod对象,但是只能获得public的。
  • 使用Class类的getDeclaredFieldsgetDeclaredConstructorsgetDeclaredMethods方法可以获得FieldConstructorMethod对象,但是不能获得父类的。

Test.java

package com.reflectTest;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public final class Test {
    public int a = 1;
    private int b = 1;

    public Test() {}
    private Test(int a) {}

    public void func1() {}
    private void func2() {}

    public static void main(String[] args) throws ClassNotFoundException {
        Class c = Class.forName("com.reflectTest.Test");
        Constructor[] constructors = c.getDeclaredConstructors();
        for (Constructor constructor : constructors)
            System.out.println(constructor.getName());
        Field[] fields = c.getDeclaredFields();
        for (Field field : fields)
            System.out.println(field.getName());
        Method[] methods = c.getDeclaredMethods();
        for (Method method : methods)
            System.out.println(method.getName());
    }
}

在这里插入图片描述

获得方法的返回值和参数的类型

  • 通过MethodgetReturnType方法可以获得返回值Class对象。
  • 通过MethodgetParameterTypes方法可以获得函数的参数Class对象列表。
package com.reflectTest;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public final class Test {
    public int func1(int a,double b) {return 0;}
    private boolean func2(boolean c) {return true;}

    public static void main(String[] args) throws ClassNotFoundException {
        Class c = Class.forName("com.reflectTest.Test");
        Method[] methods = c.getDeclaredMethods();
        for (Method method : methods) {
            String modifiers = Modifier.toString(method.getModifiers());
            System.out.print(modifiers + " " + method.getReturnType().getName() + " ");
            System.out.print(method.getName() + "(");
            Class[] parameters = method.getParameterTypes();
            for (int i = 0; i < parameters.length; i++) {
                if (i == 0) System.out.print(parameters[i].getName());
                else System.out.print(", " + parameters[i].getName());
            }
            System.out.println(")");
        }
    }
}

在这里插入图片描述

在运行时使用反射分析对象

  • 在运行时,可以通过之前提到的方法获得Field对象,然后通过get获得对象的域中的值,或者通过set方法修改域中的值。
  • 注意通过Class对象的getDefaulted方法才能获得所有修饰符中的对象(如private),而获取private域的值时,需要调用FieldMethodConstructorsetAccess方法设置访问控制权限。

Test.java

package com.reflectTest;

import java.lang.reflect.Field;

public class Test {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        GetTest gt = new GetTest();
        Class c = gt.getClass();
        //获得s1域
        Field field1 = c.getField("s1");
        System.out.println(field1.get(gt));
        //获得s2域(因为不是public,所以需要用getDeclaredField而不是get)
        Field field2 = c.getDeclaredField("s2");
        //设置访问方法,否则(private)域不能访问。
        field2.setAccessible(true);
        System.out.println(field2.get(gt));
        //设置gt的field2域的值为"ccccc"
        field2.set(gt, "ccccc");
        System.out.println(gt.getS2());
    }
}

class GetTest {
    public String s1 = "aaaaa";
    private String s2 = "bbbbb";

    public String getS2() {
        return s2;
    }
}

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值