【Java】反射

Java反射

通过Class实例获取class信息的方法称为反射(Reflection)

Class类

获取一个classClass实例有三种方法:

  • 直接通过一个class的静态变量class获取
Class cls = String.class;
  • 如已有一个实例变量,可通过该实例变量的getClass()方法获取
String s = "Hello";
Class cls = s.getClass();
  • 如已知道一个class的完整类名,可通过静态方法Class.forName()获取
Class cls = Class.forName("java.lang.String");

反射的目的是为了获得某个实例的信息。因此,当我们拿到某个Object实例时,我们可以通过反射获取该Objectclass信息:

public class Main {
    public static void main(String[] args) {

        printClassInfo(String[].class);
    }

    static void printClassInfo(Class cls) {
        System.out.println("Class name: " + cls.getName());
        //Class name: [Ljava.lang.String;
        System.out.println("Simple name: " +cls.getSimpleName()); //Simple name: String[]
        if (cls.getPackage() != null) {
            System.out.println("Package name: " + cls.getPackage().getName());
        }
        System.out.println("is interface: " + cls.isInterface()); // is interface: false
        System.out.println("is enum: " + cls.isEnum()); //is enum: false
        System.out.println("is array: " + cls.isArray()); //is array: true
        System.out.println("is primitive: " + cls.isPrimitive()); //is primitive: false
    }
}

也可以根据一个已知的Class实例来创建对应类型的新实例:

// 获取String的Class实例:
Class cls = String.class;
// 创建一个String实例:
String s = (String) cls.newInstance();

上述代码相当于new String()。通过Class.newInstance()可以创建类实例,它的局限是:只能调用public的无参数构造方法。带参数的构造方法,或者非public的构造方法都无法通过Class.newInstance()被调用。


访问字段

通过Class实例,获取所有Field对象。

Class类提供了以下几个方法来获取Field字段:

  • Field getField(name):根据字段名获取某个publicfield(包括父类)
  • Field getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类)
  • Field[] getFields():获取所有publicfield(包括父类)
  • Field[] getDeclaredFields():获取当前类的所有field(不包括父类)

一个Field对象包含了一个字段的所有信息:

  • getName():返回字段名称,例如,"name"
  • getType():返回字段类型,也是一个Class实例,例如,String.class
  • getModifiers():返回字段的修饰符,它是一个int,不同的bit表示不同的含义。
public static void main(String[] args) throws NoSuchFieldException {

        Field f = People.class.getDeclaredField("name");
        f.getName(); // "name"
        f.getType(); // class java.lang.String
        int m = f.getModifiers();
        Modifier.isFinal(m); // false
        Modifier.isPublic(m); // false
        Modifier.isProtected(m); // false
        Modifier.isPrivate(m); // true
        Modifier.isStatic(m); // false
    }

    public final class People{
        private String name;

        public People(String name) {
            this.name = name;
        }
    }

获取字段值

对于一个People实例,我们可以先拿到name字段对应的Field,再获取这个实例的name字段的值:

public class Main {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {

        Object p = new People("ASHER");
        Field f = p.getClass().getDeclaredField("name");
        f.setAccessible(true); //别管这个字段是public还是private,一律允许访问!暴力!
        System.out.println(f.get(p));
    }
}

class People{
    private String name;

    public People(String name) {
        this.name = name;
    }
}

注意:上述代码People类的name字段是private,如果不加**f.setAccessible(true);**,由于Main类无法访问Person类的private字段,就会得到一个IllegalAccessException

设置字段值

通过Field实例不仅可以获取字段值,也可以设置字段值,通过Field.set(Object,Object)实现,其中第一个Object参数是指定的实例,第二个Object参数是待修改的值:

public class Main {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {

        People p = new People("ASHER");
        Field f = p.getClass().getDeclaredField("name");
        f.setAccessible(true);
        System.out.println("原名:" + f.get(p)); //原名:ASHER
        f.set(p,"asher");
        System.out.println("现名:" + p.getName()); //现名:asher
    }
}

class People{
    private String name;

    public People(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

调用方法

通过Class实例,获取所有Method信息。

Class类提供了以下几个方法来获取Method

  • Method getMethod(name, Class...):获取某个publicMethod(包括父类)
  • Method getDeclaredMethod(name, Class...):获取当前类的某个Method(不包括父类)
  • Method[] getMethods():获取所有publicMethod(包括父类)
  • Method[] getDeclaredMethods():获取当前类的所有Method(不包括父类)

调用方法

  • getMethod("substring", int.class, int.class)就是获取这个class类的"substring"方法,然后传入该方法所需的两个参数的类
  • invoke()就是调用m这个方法,第一个参数是指定在哪个实例上是使用该方法
String s1 = "Hello world";
Method m = s1.getClass().getMethod("substring", int.class, int.class);
String s2 = (String)m.invoke(s1,6,8); //wo

调用静态方法

调用静态方法时,由于无需指定实例对象,所以invoke方法传入的第一个参数永远为null

下面就是调用IntegergetMethod()方法,将String型的字符串转为Integer型:

Method m = Integer.class.getMethod("parseInt", String.class);
Integer s = (Integer)m.invoke(null,"123"); //123

调用非public方法

对于非public的方法,首先要对该方法设置Method.setAccessible(true),然后再使用Class.getDeclaredMethod()获取。

public class Main {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {

        People p = new People();
        Method m = p.getClass().getDeclaredMethod("getName",null);
        m.setAccessible(true);
        String s = (String)m.invoke(p,null); //asher
    }
}

class People{
    private String name = "asher";

    private String getName() {
        return name;
    }
}

获取继承关系

获取父类的Class

Integer的父类类型是NumberNumber的父类是ObjectObject的父类是null

Object外,其他任何非interfaceClass都必定存在一个父类类型。

Class i = Integer.class;
Class n = i.getSuperclass(); //class java.lang.Number
Class o = n.getSuperclass(); //class java.lang.Object
System.out.println(o.getSuperclass()); //null

获取interface接口

  • Class[] getInterfaces():获取当前类直接实现的所有接口(不包括父类实现的接口)
  • Class getSuperclass():获取父类类型

查询String实现的接口:

Class cla = String.class;
Class[] clas = cla.getInterfaces();
System.out.println("=====接口为:=====");
for(Class c : clas){
    System.out.println(c.getName());
}
System.out.println("=====父类为:=====");
System.out.println(cla.getSuperclass().getName());

=接口为:=
java.io.Serializable
java.lang.Comparable
java.lang.CharSequence
=父类为:=
java.lang.Object

继承关系

  • instanceof:判断某个实例是否是某个类型
  • isAssignableFrom():判断一个Class向上转型是否是另一个Class
// Integer i = ?
Integer.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Integer
// Number n = ?
Number.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Number
// Object o = ?
Object.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Object
// Integer i = ?
Integer.class.isAssignableFrom(Number.class); // false,因为Number不能赋值给Integer
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值