JAVA反射之Class类型解析

Class 类中有众多方法用来判断当前 Class 的类型

方法名描述
isPrimitive()是否基本数据类型,如:int、long、double
isInterface()是否是接口
isEnum()是否是枚举
isAnnotation()是否是注解
isArray()是否是数组
isAnonymousClass()是否匿名类
isSynthetic()是否合成类,由 JVM 编译生成
isMemberClass()是否内部类,包括静态内部类
isLocalClass()是否局部类,方法内部定义的类
isAssignableFrom(Class)ClassA.isAssignableFrom(ClassB),A 是否 B 的父类或者相同类
isInstance(Object)ClassA.isAssignableFrom(obj),相当于 obj instanceof ClassA

基本数据类型 & 数组

JAVA 提供了八种基本数据类型:byteshortintlongfloatdoublebooleanchar

// 基本数据类型
System.out.println(long.class.isPrimitive());
// 数组
System.out.println(String[].class.isArray());

接口 & 枚举 & 注解

// 接口
System.out.println(Cloneable.class.isInterface());
// 枚举
System.out.println(ElementType.class.isEnum());
// 注解
System.out.println(Override.class.isAnnotation());

匿名类 & 内部类 & 局部类

  • 匿名类:直接实现接口,没有类名的类
  • 内部类:定义在其它类内部的类
  • 局部类:在方法中定义的类,生命周期就是方法的生命周期
public class Test {

    public static void main(String[] args) {
        Cloneable anonymous = new Cloneable() {
        };
        print(anonymous.getClass());
        print(Member.class);
        print(StaticMember.class);

        class LocalClass {
        }
        print(LocalClass.class);

        Object proxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Cloneable.class}, (p, m, a) -> null);
        print(proxy.getClass());
    }

    public static void print(Class<?> clazz) {
        System.out.println("----------------------------------------------------------------");
        System.out.println(clazz.getName() + " isAnonymousClass:" + clazz.isAnonymousClass());
        System.out.println(clazz.getName() + " isMemberClass:" + clazz.isMemberClass());
        System.out.println(clazz.getName() + " isLocalClass:" + clazz.isLocalClass());
        System.out.println(clazz.getName() + " isSynthetic:" + clazz.isSynthetic());
    }

    public class Member {
    }

    public static class StaticMember {
    }

}

输出结果

-----------------------------------------------------------
com.Test$1 isAnonymousClass:true
com.Test$1 isMemberClass:false
com.Test$1 isLocalClass:false
com.Test$1 isSynthetic:false
-----------------------------------------------------------
com.Test$Member isAnonymousClass:false
com.Test$Member isMemberClass:true
com.Test$Member isLocalClass:false
com.Test$Member isSynthetic:false
-----------------------------------------------------------
com.Test$StaticMember isAnonymousClass:false
com.Test$StaticMember isMemberClass:true
com.Test$StaticMember isLocalClass:false
com.Test$StaticMember isSynthetic:false
-----------------------------------------------------------
com.Test$1LocalClass isAnonymousClass:false
com.Test$1LocalClass isMemberClass:false
com.Test$1LocalClass isLocalClass:true
com.Test$1LocalClass isSynthetic:false
-----------------------------------------------------------
com.sun.proxy.$Proxy0 isAnonymousClass:false
com.sun.proxy.$Proxy0 isMemberClass:false
com.sun.proxy.$Proxy0 isLocalClass:false
com.sun.proxy.$Proxy0 isSynthetic:false

isSynthetic

是否合成,其实在 MethodField 等反射类中也有这个方法,表示这个类、方法、字段是否是由 JVM 编译期额外生成。

桥接方法

桥接方法用于处理引入泛型带来的方法继承问题。

声明一个泛型接口

public interface A<T> {
    void test(T t);
}

使用 String 类型实现一个具体的实现类

public class B implements A<String> {
    public void test(String s) {
        System.out.println(s);
    }
}

泛型经过编译会被擦除,所以接口 A 在编译后,test 方法的入参会被转换为 Object

public interface A {
    void test(Object t);
}

而实现类中的入参却是具体的 String 类型,并没有实现 void test(Object t) 方法。但是这个代码是正确的,可以正常运行,因为 JVM 在编译过程中对我们的实现类做了一点手脚

public class B implements A {
    public void test(String s) {
        System.out.println(s);
    }
    public void test(Object s) {
        this.test((String) s);
    }
}

编译后的类 B 也擦除了泛型,但是增加了一个方法同名的 test 方法,这个方法由 JVM 在编译期生成,解决了泛型带来的继承问题,这个方法就是桥接方法,同时也是合成方法。

通过反射验证一下

public class Test {
    public static void main(String[] args) {
        Class<B> classB = B.class;
        Method[] methods = classB.getDeclaredMethods();

        System.out.println("方法总数量:" + methods.length);
        for (Method method : methods) {
            System.out.println(method.getReturnType().getSimpleName() + " "
                    + method.getName() +
                    "(" + method.getParameters()[0].getType().getSimpleName() + ")"
                    + " isBridge" + method.isBridge()
                    + " isSynthetic" + method.isSynthetic()
            );
        }
    }
}

输出如下

方法总数量:2
void test(String) isBridge false isSynthetic false
void test(Object) isBridge true isSynthetic true

内部类

内部类之所以能够直接访问外部类中的属性,是因为编译器自动为内部类添加一个成员变量,这个成员变量指向外部类对象的引用,同时 JVM 在编译时会为内部类的构造方法添加一个外部类的入参,在创建内部类实例时,会传入外部类实例并设置到 JVM 生成的成员变量。

但是静态内部类就没有,因为静态内部与外部类是同等地位,而普通的内部类依赖于外部类

public class Test {

    public static void main(String[] args) {
        printField(Member.class);
        System.out.println("------------------------------------");
        printField(StaticMember.class);
    }

    public static void printField(Class<?> clazz) {
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field.getType() + " " + field.getName() + " isSynthetic:" + field.isSynthetic());
        }
    }

    public class Member {
    }

    public static class StaticMember {
    }

}

输出如下

class com.Test this$0 isSynthetic:true
-------------------------------------------

继承关系判断

instanceof

如果你有一个对象实例,需要判断该实例是否属于某个类型,就可以使用 instanceof 进行判断。instanceofJAVA 的关键字。

String s = "Hello World";
System.out.println(s instanceof String);  // true

isInstance

Class 类中提供了一个 isInstance(Object) 方法,该方法与 instanceof 动态等价。

String s = "Hello World";
System.out.println(String.class.isInstance(s)); // true

isAssignableFrom

如果没有对象实例,单纯判断两个类的继承关系,则可以使用 父类.class.isAssignableFrom(子类.class) 来进行判断。

System.out.println(ArrayList.class.isAssignableFrom(List.class));  //false
System.out.println(List.class.isAssignableFrom(ArrayList.class));  //true

如果是同一个类,isAssignableFrom 也返回 true

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值