Class类全面深入学习

笔记:Class类全面深入学习

本文动机:看各类框架源码时,发现很多反射逻辑看不懂,究其原因是很多Class对象的方法都是第一次见,也不知道其作用。原以为对反射还算比较了解了,看了框架源码才知道,对反射的了解,还局限于对构造函数,方法,Field的使用上。为了方便看源码,决定全面地撸一遍Class的方法的功能。

类声明

Class的声明:

public final class Class<T> implements java.io.Serializable,
                              GenericDeclaration,
                              Type,
                              AnnotatedElement 

Class的声明还是很简单的,没有显式继承其他类,实现的接口也不多。

看一下继承结构:
在这里插入图片描述

类型

在java中,类,枚举,接口,注解编译之后,都是以class文件存在的。而当这些被载入内存之后,又是都以Class对象形式存在的。另外,数组类型也是以Class。所以,Class对象中,既有如下的几个方法,可以确定Class对象,其对应的类型,到底是类,还是接口,还是枚举或者其他类型。

  • isAnnotation() : 判断Class是否是注解
  • isInterface() : 判断Class是否是接口
  • isArray() : 判断Class是否是数组
  • isEnum() : 判断Class是否是枚举

额,这里没有方法isClass, 因为其他类型,实际也都是Class。可以认为,他们是Class的特例,Class包括了这些类型。
或者说,这些方法本来就是在Class对象上调用的,也就是说,这些类型,都是Class。

看看他们的实现:

isAnnotation

public boolean isAnnotation() {
    return (getModifiers() & ANNOTATION) != 0;
}

ANNOTATION是常量:int ANNOTATION= 0x00002000;,而getModifiers()是native方法。返回Class对象所有的修饰符组成的int值,对于类来说,就是public,private,static,final等。因为类可能会有多个修饰符,所以这个方法返回来的int值,以表示该Class对象是否具有某个特点。

比如,bit0,就表示这个类是否是public,为1,就表示具有public特征,为0就表示没有public特征。bit1就表示是否具有private特征。而根据ANNOTATION常量的定义,我们就可以知道,注解特性在bit13.

public class ModfierTest {
    public static void main(String[] args) {
        byte[] bs = new byte[10];
        System.out.println(Integer.toHexString(Modifier.class.getModifiers()));
        System.out.println(Integer.toHexString(A.class.getModifiers()));
        System.out.println(Integer.toHexString(B.class.getModifiers()));
        System.out.println(Integer.toHexString(C.class.getModifiers()));
        System.out.println(Integer.toHexString(D.class.getModifiers()));
        System.out.println(Integer.toHexString(E.class.getModifiers()));
        System.out.println(Integer.toHexString(F.class.getModifiers()));
        System.out.println(Integer.toHexString(G.class.getModifiers()));
        System.out.println(Integer.toHexString(bs.getClass().getModifiers()));
        System.out.println(Integer.toHexString(int.class.getModifiers()));
    }

    public static final class A { }
    public class B { }
    public interface C {  }
    private interface D {  }
    public @interface E {  }
    public enum  F {  }
}

输出:

1
19
1
609
60a
2609
4019
0
411
411

Modifier.class.getModifiers()B.class.getModifiers()输出是一样的,所以从这里,是无法区分他们是不是内部类的。
A对应到19,因为bit0是public,bit3是static,而bit4是final。
C和D说明此时bit9和bit10是1,bit9是对应interface,bit1`0对应abstract,所以接口不仅仅是interface,还隐藏了关键字abstract,说明接口也是抽象类。

如果普通类,没有任何的修饰符,那么就会输出0.

对修饰符的总结:

15141312111098
32768163848192409620481024512256
enumannotationSYNTHETICSTRICTABSTRACTINTERFACENATIVE
76543210
1286432168421
TRANSIENTvolatilesynchronizedfinalstaticprotectedprivatepublic

可以看到数组的类型和int类型的修饰符,都是0x411,他们都是native,fina, public的。也就是说明他们都是虚拟机内部支持的。

isArray()

这是一个native方法,判断对象是否是数组。因为数组类型都是虚拟机内部支持的,那么这个方法是native就不奇怪了。

isEnum()

public boolean isEnum() {
    // An enum must both directly extend java.lang.Enum and have
    // the ENUM bit set; classes for specialized enum constants
    // don't do the former.
    return (this.getModifiers() & ENUM) != 0 &&
    this.getSuperclass() == java.lang.Enum.class;
}

枚举的判断除了对修饰符进行判断之外,还要求其直接继承了Enum类。

所以,如下情况的枚举,虽然从修饰符上看,还是枚举的实例,但是从这个isEnum方法的角度已经不是枚举了:

public class EnumTest {
    public static void main(String[] args) {
        System.out.println(ET.INST.getClass());
        System.out.println(ET.INST.getDeclaringClass());
        System.out.println(ET.INST.getClass().getSuperclass());
        System.out.println(ET.INST.getClass().isEnum());
        System.out.println(Integer.toHexString(ET.INST.getClass().getModifiers()));
    }
}

enum ET{
    INST{}
}
class com.woslx.java.temp.ET$1
class com.woslx.java.temp.ET
class com.woslx.java.temp.ET
false
4008

这里的INST不是ET这个枚举类型的实例,它是继承了ET这个枚举类型的静态内部类的对象。

isInterface()

这个方法也是native的:public native boolean isInterface();,判断一个Class对象是否是接口。

isAnonymousClass

判断是否是匿名内部类

public class AnonymousClass {
    public static void main(String[] args) {
        AnonymousClass anonymousClass = new AnonymousClass() {
        };

        System.out.println(AnonymousClass.class.getName());
        System.out.println(AnonymousClass.class.getSimpleName());
        System.out.println(anonymousClass.getClass().getName());
        System.out.println(anonymousClass.getClass().getSimpleName());
    }
}

输出:

com.woslx.java.temp.AnonymousClass
AnonymousClass
com.woslx.java.temp.AnonymousClass$1

最后anonymousClass.getClass().getSimpleName()输出了空字符串。

也就是说普通类,getSimpleName()输出的是不带包名的类名,而对于匿名内部类,会输出空字符串。再来看看isAnonymousClass()方法的实现:

public boolean isAnonymousClass() {
    return "".equals(getSimpleName());
}

如果getSimpleName()返回 “”,那么就是匿名内部类。

那么getSimpleName()对于匿名内部类为什么会返回""

public String getSimpleName() {
        if (isArray())
            return getComponentType().getSimpleName()+"[]";

        String simpleName = getSimpleBinaryName();
        if (simpleName == null) { // top level class
            simpleName = getName();
            return simpleName.substring(simpleName.lastIndexOf(".")+1); // strip the package name
        }
       
        // Remove leading "\$[0-9]*" from the name
        int length = simpleName.length();
        if (length < 1 || simpleName.charAt(0) != '$')
            throw new InternalError("Malformed class name");
        int index = 1;
        while (index < length && isAsciiDigit(simpleName.charAt(index)))
            index++;
        // Eventually, this is the empty string iff this is an anonymous class
        return simpleName.substring(index);
    }

方法中调用了getSimpleBinaryName()

private String getSimpleBinaryName() {
        Class<?> enclosingClass = getEnclosingClass();
        if (enclosingClass == null) // top level class
            return null;
        // Otherwise, strip the enclosing class' name
        try {
            return getName().substring(enclosingClass.getName().length());
        } catch (IndexOutOfBoundsException ex) {
            throw new InternalError("Malformed class name", ex);
        }
    }

getEnclosingClass()获取到包含这个类的外部类的Class对象。getName()方法获取到的是完整的类名。然后substring(enclosingClass.getName().length());获取到除了包名和外部类类名之外的部分。对于内部类来说,这里获取到字符串就是$这样的$+其他字符串的形式。$我们知道是内部类和外部类之间的分隔符,而只有匿名内部类,类名才会是纯数字,自定义的类,是无法以数字开头命名类的,因为在java中,标识符不能以数字开头。所以,如果一个类的类名如果以数字开头,那么一定是匿名内部类。

再回到getSimpleName方法中,下面代码就很好理解,知道为什么要过滤掉数字部分了:

 int index = 1;
while (index < length && isAsciiDigit(simpleName.charAt(index)))
    index++;
// Eventually, this is the empty string iff this is an anonymous class
return simpleName.substring(index);

我们要获取内部类的类名非数字开始的部分,而匿名内部类的类名是全数字的,那么simpleName.substring(index)就必然获取不到字符串。substring在这里返回的是new String(value, beginIndex, subLen),其中subLen=0.那么就返回了"".

疑问:为什么不能根据类名第一个字符是数字,就判断为匿名内部类?因为局部内部类和匿名内部类,都是以数字开始的。他们各有一个自己的序号空间。对于局部内部类,序号后面是自定义的类名,而对于匿名内部类,则只有序号。

总结:根据类名是否是全数字判断匿名内部类。

isLocalClass

判断一个类,是否是局部内部类。

public boolean isLocalClass() {
    return isLocalOrAnonymousClass() && !isAnonymousClass();
}

isLocalOrAnonymousClass()判断是否局部内部类或者是匿名类,然后通过!isAnonymousClass()排除匿名类。

isLocalOrAnonymousClass()的代码:

private boolean isLocalOrAnonymousClass() {
    // JVM Spec 4.8.6: A class must have an EnclosingMethod
    // attribute if and only if it is a local class or an
    // anonymous class.
    return getEnclosingMethodInfo() != null;
}

以如下的例子看一下:

public class AnonymousClass {
    public static void main(String[] args) {
        AnonymousClass anonymousClass = new AnonymousClass() {
        };

        class LocalClass{}

        System.out.println(LocalClass.class.isLocalClass());
        System.out.println(anonymousClass.getClass().isLocalClass());
    }
}

在执行到LocalClass.class.isLocalClass()时,看看getEnclosingMethodInfo返回了什么:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F2DLAETD-1590032423660)(_v_images/20191218161123147_2103535199.png)]

enclosingClass:表示定义在哪个类中。name表示在这个类的那个方法名中,因为方法可以重载,所以还需要方法的参数,才可以确定到底在哪个方法中。

getEnclosingMethodInfo返回包含这个类的方法的信息。也就是说,如果这个方法返回的不是null,那么可以确定这个类是在某个方法内部定义的。局部内部类,是在方法内部定义的。匿名内部类,也可以在方法内部定义。

也就是说,匿名内部类,不是局部内部类,虽然匿名内部类可以是局部类。

总结:定义在方法内部,且不是匿名类的类是局部内部类。也就是说,匿名内部类,不是局部内部类,即便是定义在方法内部的匿名内部类,也不是局部内部类。局部内部类一定是非匿名的。没有为什么,这是jdk定义的。

isMemberClass

判断一个类是否是成员内部类,这样的类是在一个类内部声明的类,且和方法,field在同一层级。

方法定义:

public boolean isMemberClass() {
    return getSimpleBinaryName() != null && !isLocalOrAnonymousClass();
}

!isLocalOrAnonymousClass()排除了定义在方法内部的类。
getSimpleBinaryName()获取一个类的二进制层面的类名,如果是顶层类,也就是如果类是直接定义在包中,那么会返回null。

如果一个类,定义在某个类的内部,又不是局部内部类,不是匿名内部类,那么就是成员类。我们是无法在一个类的成员层级,定义一个匿名内部类的。

解读一下getSimpleBinaryName()方法:

private String getSimpleBinaryName() {
    Class<?> enclosingClass = getEnclosingClass();
    if (enclosingClass == null) // top level class
        return null;
    // Otherwise, strip the enclosing class' name
    try {
        return getName().substring(enclosingClass.getName().length());
    } catch (IndexOutOfBoundsException ex) {
        throw new InternalError("Malformed class name", ex);
    }
}

getEnclosingClass()获取到包裹这个类的类,只有在类内部定义的类,这个方法返回才不是null,如果是顶层类,也就是在包中定义的类,这个方法返回的是null。

public class AnonymousClass {
    public static void main(String[] args) {
        class LocalClass{}

        System.out.println(LocalClass.class.isMemberClass());
        System.out.println(new AnonymousClass().anonymousClass2.getClass().isMemberClass());
        System.out.println(new AnonymousClass().anonymousClass2.getClass().isAnonymousClass());
    }

    public AnonymousClass2 anonymousClass2 = new AnonymousClass2(){};
}

class AnonymousClass2{

}

输出:

false
false
true

内部类和顶层类是根据是否在类中定义来区分的。
而内部类,由分为3种:

  • 匿名类部类 : 可以在类中成员层级定义,也可以在方法内部定义,但一定在类内部定义。匿名内部类和别的内部类不同,他是定义伴随实例化的。不能显式使用,因为无法在源码中写出类名。定义时不能使用class关键字,而需要使用new关键字。
  • 成员内部类: 在类成员层级定义。需要使用class关键字。
  • 局部内部类: 可以在方法内部任何地方定义。需要使用class关键字。

不要在一个类中定义本类的匿名内部子类,否则会出现堆栈溢出异常:

public class AnonymousClass {
    public static void main(String[] args) {
        System.out.println(new AnonymousClass().anonymousClass2.getClass());
    }

    public AnonymousClass anonymousClass2 = new AnonymousClass(){};
}

成员内部类,不区分静态和非静态,如果要知道一个类是否静态,需要根据其修饰符,自己判断。

在java8中,还有一个特殊的存在:lambda表达式。

public class TypeMain {
    public static void main(String[] args) {
        Runnable r2 = System.out::println;
        System.out.println(r2.getClass().getName());
        System.out.println(r2.getClass().isMemberClass());
        System.out.println(r2.getClass().isAnonymousClass());
        System.out.println(r2.getClass().isLocalClass());
        System.out.println(r2.getClass().getModifiers());
        System.out.println("--------->");
        System.out.println(r3.getClass().getName());
        System.out.println(r3.getClass().isMemberClass());
        System.out.println(r3.getClass().isAnonymousClass());
        System.out.println(r3.getClass().isLocalClass());
        System.out.println(r3.getClass().getModifiers());
    }

    public static Runnable r3 = System.out::println;

}

输出:

com.woslx.java.temp.TypeMain$$Lambda$2/1836643189
false
false
false
4112
--------->
com.woslx.java.temp.TypeMain$$Lambda$1/1837760739
false
false
false
4112

这里输出表示,lambda表达式不是匿名内部类,也不是局部内部类,不是成员内部类。她的修饰符是4112,这就比较迷了,因为它是fina的,enmu,private,native的。不过这几个特性,可能体现了lambda的实现。

isPrimitive

判断一个类是不是基本类型。以代码说明问题

public class TypeMain {
    public static void main(String[] args) {
        System.out.println(int.class.isPrimitive());
        System.out.println(Integer.class.isPrimitive());
        System.out.println(new Integer[10].getClass().isPrimitive());
        System.out.println(new int[10].getClass().isPrimitive());
        System.out.println(TypeMain.class.isPrimitive());
        System.out.println(void.class.isPrimitive());
    }
}

输出:

true
false
false
false
false
true

可以看出,只有基本类型,才会返回true。这里别忘了,void,也是基本类型。所以java的基本类型有:byte,bool,char,short,int,long,float,double,void 9种

虽然,我们无法声明一个void类型的变量,但是jdk层面来说,void是一种类型。

数组,普通的Class,都不是基本类型。

isSynthetic

Synthetic这个单词作为名词的含义是合成物,作为形容词的含义是合成的

也就是说,这个方法,判断一个类是否是合成的。如果一个类是合成的,会在修饰符中打上合成标记,特征就是修饰符的bit12为1.

它必须要求整个类都是编译器合成的,而不是某个方法是编译器添加的,也不能是动态生成的。

getComponentType

这个方法是native方法,返回一个Class对象。这个方法应该在数组类型的Class对象上调用,返回数组元素的Class对象,如果在非数组类型上调用,那么将返回null。

getSuperclass

这个方法可以获取到当前类的超类,也就是当前类继承的类。如果是Object类的Class对象上调用,将返回null。因为继承可以多个层次,这个方法返回的是直接继承的类。

getGenericSuperclass

这个方法也会返回类的直接超类。它和getSuperclass的区别是:如果父类型是范型类,因为编译时的类型擦除,getSuperclass返回的超类,是不包含范型类型的。而getGenericSuperclass会包含范型类型。

实例:

public class Test {

    public static void main(String[] args) {

        Class<? super Student> superclass = Student.class.getSuperclass();
        Type genericSuperclass = Student.class.getGenericSuperclass();

        System.out.println();
    }

}

class Person<T> {}

class Student extends Person<Test> {}

可以看到getSuperclass返回的是一个Class对象,而getGenericSuperclass返回的是Type类型的对象。Type是接口,实际返回的是ParameterizedTypeImpl类型的对象。这个ParameterizedTypeImpl实现了ParameterizedType接口,而ParameterizedType接口继承了Type接口。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fiAuAkfD-1590032423687)(_v_images/20191219141058614_203321411.png)]

这个类,用rawType记录了超类的原始对象,用actualTypeArguments这个Type类型的数组,记录了范型参数类型对象。这里之所以用Type数组,因为范型参数本身可能也有范型参数。因为可能会有多个范型参数,所以用了数组。

更多情形的测试:

public class Test {

    public static void main(String[] args) {
        System.out.println("Student.class.getSuperclass()\t"
                           + Student.class.getSuperclass());
        System.out.println("Student.class.getGenericSuperclass()\t"
                           + Student.class.getGenericSuperclass());

        System.out.println("Test.class.getSuperclass()\t"
                           + Test.class.getSuperclass());
        System.out.println("Test.class.getGenericSuperclass()\t"
                           + Test.class.getGenericSuperclass());

        System.out.println("Object.class.getGenericSuperclass()\t"
                           + Object.class.getGenericSuperclass());
        System.out.println("Object.class.getSuperclass()\t"
                           + Object.class.getSuperclass());

        System.out.println("void.class.getSuperclass()\t"
                           + void.class.getSuperclass());
        System.out.println("void.class.getGenericSuperclass()\t"
                           + void.class.getGenericSuperclass());

        System.out.println("int[].class.getSuperclass()\t"
                           + int[].class.getSuperclass());
        System.out.println("int[].class.getGenericSuperclass()\t"
                           + int[].class.getGenericSuperclass());
    }
}

class Person<T> {}

class Student extends Person<Test> {}

输出:

Student.class.getSuperclass()	class com.woslx.java.temp.Person
Student.class.getGenericSuperclass()	com.woslx.java.temp.Person<com.woslx.java.temp.Test>
Test.class.getSuperclass()	class java.lang.Object
Test.class.getGenericSuperclass()	class java.lang.Object
Object.class.getGenericSuperclass()	null
Object.class.getSuperclass()	null
void.class.getSuperclass()	null
void.class.getGenericSuperclass()	null
int[].class.getSuperclass()	class java.lang.Object
int[].class.getGenericSuperclass()	class java.lang.Object

对于超类没有范型参数的情况,这个两个方法的输出是一样的。

下面来看一下,这个方法的源码:

public Type getGenericSuperclass() {
        ClassRepository info = getGenericInfo();
        if (info == null) {
            return getSuperclass();
        }

        // Historical irregularity:
        // Generic signature marks interfaces with superclass = Object
        // but this API returns null for interfaces
        if (isInterface()) {
            return null;
        }

        return info.getSuperclass();
    }

getGenericInfo这个方法可以获取到范型信息,也就是说,虽然在编译期进行了类型擦除,但是java还是在Class文件中保存了范型信息的。

getClasses

看这个方法的官方解释之前,先搞懂一个单词的含义:represent

Returns an array containing Class objects representing all the public classes and interfaces that are members of the class represented by this Class object. This includes public class and interface members inherited from superclasses and public class and interface members declared by the class. This method returns an array of length 0 if this Class object has no public member classes or interfaces. This method also returns an array of length 0 if this Class object represents a primitive type, an array class, or void.

这个方法返回某个类中定义的public权限的成员类或者接口。

public class Test {

    public static void main(String[] args) {
        Class<?>[] classes = Student.class.getClasses();
        System.out.println();
    }
}

class Student {
    public class A{}
    public class B{}
    public interface C{}
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fmJxZa5Y-1590032423690)(_v_images/20191219150520524_1752671816.png)]

getInterfaces()

这个方法返回某个类直接实现的所有接口。也就是说,如果是这个类的父类实现的接口,这个方法获取不到。如果要拿到一个类继承链上所有实现的接口,那么必须遍历继承链,获取每一个父类实现的接口。还要对获取到的接口进行继承遍历,以便获取到接口继承的接口。

另外,如果Class对象表示的接口对象,那么这个方法表示的是该接口extends的接口。虽然extends用在类继承上,每次只能继承一个类,但是当用来表示接口之间的继承时,一个一次可以继承多个接口。

getGenericInterfaces()

这个方法返回某个类实现的所有接口。如果是范型接口,可以返回带范型参数的接口类型对象。

getDeclaringClass

getEnclosingClass

name相关

getName

这个方法可以获取类全名,包括包名。对于内部类来说,包括其外部类的类名。

public class TypeMain {
    public static void main(String[] args) {
        class ClA {
        }
        Runnable runnable = new Runnable() {
            @Override
            public void run() {

            }
        };
        Runnable r2 = System.out::println;

        System.out.println(int.class.getName());
        System.out.println(Integer.class.getName());
        System.out.println(new Integer[10].getClass().getName());
        System.out.println(new String[10].getClass().getName());
        System.out.println(new int[10].getClass().getName());
        System.out.println(new byte[10].getClass().getName());
        System.out.println(void.class.getName());
        System.out.println(ClA.class.getName());
        System.out.println(runnable.getClass().getName());
        System.out.println(r2.getClass().getName());
        System.out.println(MemberClass.class.getName());
        System.out.println(new TypeMain().m2.getClass().getName());
    }
    class MemberClass{}

    public MemberClass m2 = new MemberClass(){};
}

输出:

int
java.lang.Integer
[Ljava.lang.Integer;
[Ljava.lang.String;
[I
[B
void
com.woslx.java.temp.TypeMain$1ClA
com.woslx.java.temp.TypeMain$1
com.woslx.java.temp.TypeMain$$Lambda$1/1418428263
com.woslx.java.temp.TypeMain$MemberClass
com.woslx.java.temp.TypeMain$2

可以看到:

  • 对于基本类型,其Class name就是基本类型名。
  • 对于普通类,就是带包名的类名。
  • 对于普通类的数组,类名就是[L+数组元素类名。
  • 对于基本类型数组,类名就是[+基本类型首字母大写
  • 对于内部类,就是外部类+$+内部类序号+内部类名
  • 对于匿名内部类,就是外部类+$+匿名内部类的序号。
  • 对于Lambda表达式,则会有$Lambda$这一部分。

getSimpleName

public class TypeMain {
    public static void main(String[] args) {
        class ClA {
        }
        Runnable runnable = new Runnable() {
            @Override
            public void run() {

            }
        };
        Runnable r2 = System.out::println;

        System.out.println(int.class.getSimpleName());
        System.out.println(Integer.class.getSimpleName());
        System.out.println(new Integer[10].getClass().getSimpleName());
        System.out.println(new String[10].getClass().getSimpleName());
        System.out.println(new int[10].getClass().getSimpleName());
        System.out.println(new byte[10].getClass().getSimpleName());
        System.out.println(void.class.getSimpleName());
        System.out.println(ClA.class.getSimpleName());
        System.out.println(runnable.getClass().getSimpleName());
        System.out.println(r2.getClass().getSimpleName());
        System.out.println(MemberClass.class.getSimpleName());
        System.out.println(new TypeMain().m2.getClass().getSimpleName());
    }
    class MemberClass{}

    public MemberClass m2 = new MemberClass(){};

}
int
Integer
Integer[]
String[]
int[]
byte[]
void
ClA

TypeMain$$Lambda$1/1418428263
MemberClass

返回类型的简单名称,如果是匿名类,返回空字符串。如果是局部内部类,则返回其非数字开始的类名。如果是成员内部类,则返回其类名。如果是数组,则在返回的类名后,加[].

lambda表达式比较特殊,其调用getSimpleName()时,该方法内部的getSimpleBinaryName()返回null。这是因为getEnclosingClass返回了null。这说明,没有获取到lambda表达式的外部类。因为lamba表达式在java语言定义层面不算是内部类,所以就获取不到其外部类了。那这样,就只要调用了getName()方法,然后返回其最后一个.之后的部分,也就是当作了普通的类。

这个方法,就是对lambda表达式的时候,很特殊。

getTypeName

这个方法是1.8才加入的,这个方法返回类型的信息,和getName很像。究其原因就是:除了数组,实际上它就是直接调用了getName()方法。而对于数组,它会是输出数组会有多个维,比如int型的二维数组,会输出:int[][],对于Intger型二维数组,会输出:java.lang.Integer[][]。

getCanonicalName

canonical的意思是简洁的,经典的。这个类的输出和getTypeName比较像,因为凡是有输出,都是基本来自getName()方法。有以下特殊情况:
如果是数组,那么和getTypeName一样,会显示数组的维度。如果是局部内部类或者匿名内部类,那么会返回null。如果是成员内部类,则会把$换成.进行输出。

public class TypeMain {
    public static void main(String[] args) {
        class ClA {
        }
        Runnable runnable = new Runnable() {
            @Override
            public void run() {

            }
        };
        Runnable r2 = System.out::println;

        System.out.println(int.class.getCanonicalName());
        System.out.println(Integer.class.getCanonicalName());
        System.out.println(new Integer[10].getClass().getCanonicalName());
        System.out.println(new String[10].getClass().getCanonicalName());
        System.out.println(new int[10].getClass().getCanonicalName());
        System.out.println(new byte[10].getClass().getCanonicalName());
        System.out.println(void.class.getCanonicalName());
        System.out.println(ClA.class.getCanonicalName());
        System.out.println(runnable.getClass().getCanonicalName());
        System.out.println(r2.getClass().getCanonicalName());
        System.out.println(MemberClass.class.getCanonicalName());
        System.out.println(new TypeMain().m2.getClass().getCanonicalName());
    }
    class MemberClass{}

    public MemberClass m2 = new MemberClass(){};

}

输出:

int
java.lang.Integer
java.lang.Integer[]
java.lang.String[]
int[]
byte[]
void
null
null
com.woslx.java.temp.TypeMain$$Lambda$1/1418428263
com.woslx.java.temp.TypeMain.MemberClass
null

类加载

forName

forName方法可以把制定类加载到虚拟机中。

forName方法有2个:forName(String className)forName(String name, boolean initialize, ClassLoader loader).

这3个方法实际上最终都调用了forName0这个native方法。区别就是后面一个方法可以自己指定是否要初始化,以及可以自己指定类加载器。

先来看看 forName(String className)方法:

public static Class<?> forName(String className)
            throws ClassNotFoundException {
    Class<?> caller = Reflection.getCallerClass();
    return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}

先通过反射工具,获取到调用这个静态方法的类,然后获取到加载这个类的加载器。然后以要被加载的类路径字符串,获取到的类加载,调用了forName方法的类为参数,调用forName0方法。forName0方法的initialize参数设置为true。这个initialize是指在把类加载到jvm中之后,是发进行初始化。类的初始化是在jvm根据class文件生成了Class对象之后,执行类内部静态代码块的过程。这个过程如果类有父类,那么也会加载其父类,并对父类执行初始化。

public class Test {

    public static void main(String[] args) throws ClassNotFoundException {
        Class.forName("com.woslx.java.temp.B");
    }
}

class A{
    static {
        System.out.println("A的静态代码块");
    }
}

class B extends A{
    static{
        System.out.println("B的静态代码块");
    }
}

输出:

A的静态代码块
B的静态代码块

如果加载B的时候,不进行初始化:

public class Test {

    public static void main(String[] args) throws ClassNotFoundException {
        Class.forName("com.woslx.java.temp.B",false,Test.class.getClassLoader());
    }
}

class A{
    static {
        System.out.println("A的静态代码块");
    }
}

class B extends A{
    static{
        System.out.println("B的静态代码块");
    }
}

则不会有任何输出。

反射-构造函数

和构造函数相关的有5个方法,还有一个实例化对象的,也和构造函数有关。获取到构造函数对象之后,就可以实例化类

获取构造函数对象

用以下代码来说明此问题。

public class TypeMain {
    public static void main(String[] args) {
        Constructor<?>[] constructors = LDA.class.getConstructors();
        System.out.println();
    }
}

class P{
    public P(){ }

    protected  P(String str){ }

    private  P(int i){}
}

class LDA  extends P{
    LDA(String s,String s2){ }

    public LDA(){ }

    public LDA(String str,int i, Integer i2){ }

    protected  LDA(Double d){ }

    private LDA(String name){ }
}
Constructor<?>[] getConstructors()

这个方法可以获取到类中public声明的构造函数对象组成的数组。如果一个类没有public构造函数,或者这个类是数组,或者这个类是私有的,再或者这个类的类型是void。那么这个方法将返回一个长度为0的构造函数对象数组。

把断点下在System.out.println();这一行,开始调试程序,在程序暂停之后,查看constructors的值:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9I2ktuAo-1590032423692)(_v_images/20191219094607913_1029261817.png)]

我们共定义了4个LDA类的构造函数,其中public的2个。可以看到,这个方法只会获取到public声明的构造函数。

查看地一个构造函数对象的内容:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LYEqByQk-1590032423694)(_v_images/20191219094911043_1344989135.png)]

clazz记录了这个构造函数对象是哪个Class对象的构造函数。parameterTypes是构造函数的参数列表。exceptionTypes是构造函数可能抛出的异常类型数组。

注意,父类的构造函数,不管是私有的还是公有的,都获取不到。

Constructor<?>[] getDeclaredConstructors()

Declared是declare的过去式,表示声明的意思。
顾名思义,这个方法可以获取到类中定义的所有构造函数不管是私有的还是公有的还是保护的,还是默认的。只要是声明了的,都可以被获取到,包括隐式声明的。

把前面代码Constructor<?>[] constructors = LDA.class.getConstructors();换成Constructor<?>[] constructors = LDA.class.getDeclaredConstructors();,再调试,可以看到:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tvh2lybo-1590032423695)(_v_images/20191219095941139_183466103.png)]

Constructor<?> getConstructor(Class<?>… parameterTypes)

这个方法可以根据构造函数的参数列表的类型,获取对应的public类型的构造函数对象。因为参数类型列表的唯一性,所以这种方式,只会获取到一个构造函数对象。如果指定的参数类型列表不存在或者是私有的,那么这个方法将抛出NoSuchMethodException异常。

比如这里,要获取到LDA(String s,String s2){ }这个构造函数:

Constructor<?> constructors = LDA.class.getConstructor(String.class, String.class);

但是因为这个构造函数是默认访问权限,所以这里是获取不到的,所以就会抛出异常。

下面获取public LDA(String str,int i, Integer i2){ }这个构造函数:

Constructor<?> constructors = LDA.class.getConstructor(String.class, int.class, Integer.class);

可以看到:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1bHf2HzD-1590032423695)(_v_images/20191219101128135_826243780.png)]

如果要获取无参构造函数呢?

根据arrayContentsEq方法中比较参数的逻辑,我们可以传递null或者Class空数组

Constructor<?> constructors = LDA.class.getConstructor(null);

或者

Constructor<?> constructors = LDA.class.getConstructor(new Class[0]);

以参数类型列表获取构造函数对象的时候,对于顶层类或者静态内部类来说,参数类型列表,就是定义的时候列出来的那些。但是,对于非静态内部类来说,有些特殊。

在getConstructor的官方文档中有如下一句话:If this Class object represents an inner class declared in a non-static context, the formal parameter types include the explicit enclosing instance as the first parameter.
如果该类是在非静态上下文中声明的内部类,则类型参数列表将以该内部类所在的外部类的Class对象作为第一个参数。非静态内部类实例化的时候,总是和一个外部类的对象关联在一起的。因为非静态内部类,可以调用外部类的非静态方法。因为非静态方法必须在对象上调用,所以非静态内部类的对象必须保存外部类的对象。所以,非静态内部类的构造函数,编译器会默认添加一个外部类对象作为第一个参数。

把代码做如下更改:

public class TypeMain {
    public static void main(String[] args) throws NoSuchMethodException {
        Constructor<?> constructors = BBD.class.getConstructor(String.class);
        System.out.println();
    }
}

class P {
    public P() {
    }

    protected P(String str) {
    }

    private P(int i) {
    }
}

class LDA  extends P {
    LDA(String s, String s2) {
    }

    public LDA() {
    }

    public LDA(String str, int i, Integer i2) {
    }

    protected LDA(Double d) {
    }

    private LDA(String name) {
    }

    public class BBD{
        public BBD(String s){}
    }
}

运行时抛NoSuchMethodException异常,明明参数列表和定义的一样?先获取所有的构造函数看一下;

Constructor<?> []constructors = BBD.class.getConstructors();

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HVajvSZC-1590032423696)(_v_images/20191219103323985_692645921.png)]

可以看到参数列表多了一个。

现在以Constructor<?> constructors = BBD.class.getConstructor(LDA.class, String.class);获取,就可以获取到构造函数对象了。

用javap反编译看一下这个类中的信息;

javap -p LDA\$BBD.class 
public class com.woslx.java.temp.LDA$BBD {
  final com.woslx.java.temp.LDA this$0;
  public com.woslx.java.temp.LDA$BBD(com.woslx.java.temp.LDA, java.lang.String);
}

可以看到多了一个LDA类型的属性来接收传进来的LDA对象。

Constructor<T> getDeclaredConstructor(Class<?>… parameterTypes)

这个方法可以根据构造函数的参数类型,获取类声明的构造函数对象,无论是私有还是什么访问权限,都可以获取到。除了可以无视构造函数的访问权限这一点外,其他和Constructor<?> getConstructor(Class<?>... parameterTypes)无区别。

Constructor<?> getEnclosingConstructor()

如果一个类是定义在其他类的构造函数中的局部类或者匿名类,那么可以从这个类的Class对象上调用getEnclosingConstructor获取到这个类所在的构造函数的Constructor对象。这个表述很扭口。但是用代码很好说明:

public class TypeMain {
    public static void main(String[] args) throws NoSuchMethodException {
        LDA lda = new LDA();
    }
}


class LDA{
    public LDA() {
        class R2{

        }
        Constructor<?> enclosingConstructor = R2.class.getEnclosingConstructor();
        System.out.println(enclosingConstructor);
    }
}

enclosingClass的值是

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rgSljvB9-1590032423697)(_v_images/20191219111130770_836540300.png)]
这里获取到的构造函数对象,就是R2这个类定义所自爱的LDA()构造函数对象。

利用构造函数对象创建类对象

newInstance

这个方法可以调用类的无参构造函数创建出对象。这个方法实际上是简化了通过反射调用无参构造函数实例化对象,因为这个方法内部,就是通过反射调用了无参构造函数实例化出的对象。

看一下这个方法的源码是如何创建对象的:

Class<?>[] empty = {};
final Constructor<T> c = getConstructor0(empty, Member.DECLARED);
cachedConstructor = c;

这3行代码,拿到了无参构造函数对象,然后把无参构造函数对象保存到cachedConstructor,如果下次再调用这个方法时,就不用再次查找构造函数了。

然后下面的代码就是调用构造函数类Constructor的newInstance方法,后面是(Object[])null是参数数组,因为是无参构造函数,所以传递null:

return tmpConstructor.newInstance((Object[])null);

在这个方法中,会调用DelegatingConstructorAccessorImpl类的newInstance方法实例化对象,再往下就是虚拟机了。

利用构造函数对象手动创建对象

手动调用构造函数对象创建实例,也是先获取到构造函数对象,然后调用Constructor类的T newInstance(Object ... initargs)方法。只要把参数列表传进去即可。

public class TypeMain {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException,
                                                  InstantiationException, InvocationTargetException {
        Constructor<LDA> constructor = LDA.class.getConstructor(String.class);
        LDA lda = constructor.newInstance("123");
    }
}


class LDA{
    public LDA(String s) {

    }
}

如果是私有构造函数,即便通过getDeclaredConstructors或者getDeclaredConstructor方法获取到了构造函数对象,也是无法直接调用的,需要设置一下权限。

public class TypeMain {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException,
                                                  InstantiationException, InvocationTargetException {
        Constructor<LDA> constructor = LDA.class.getDeclaredConstructor(String.class);
        constructor.setAccessible(true);
        LDA lda = constructor.newInstance("123");
    }
}


class LDA{
    private LDA(String s) {

    }
}

上面的关键代码就是constructor.setAccessible(true);,设置为可访问。

反射-Field

前面构造函数比较特殊,通过那4个方法,只能获取到类本身的构造函数,而无法获取到父类的构造函数。但是Field和Method和构造函数不一样。

getFields()

这个方法可以获取到public访问权限的field的对象,不管是自己定义的,还是继承来的。
以代码说明:

public class TypeMain {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException,
                                                  InstantiationException, InvocationTargetException {
        Field[] fields = LDA.class.getFields();
        System.out.println();
    }
}

class P{
    private int data1;
    public  String str1;
}
class LDA extends P{
    private int data2;
    public  String str2;
}

fields的值:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ydWibWZS-1590032423697)(_v_images/20191219114158966_1325836576.png)]

可以看到str1,是从P继承来的,而str2是自己定义的。另外自己定义的但是权限为private的data1并没有获取到。

getDeclaredFields()

这个方法可以获取到本类中定义的所有域的对象,而不管是什么访问权限。但是继承来的,获取不到。

Field[] fields = LDA.class.getFields();换成Field[] fields = LDA.class.getDeclaredFields();再运行一遍,可以看到:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P9LMHKG1-1590032423698)(_v_images/20191219114505609_1547644991.png)]

getField(String name)

根据域的name,获取public权限的域对象。

public class TypeMain {
    public static void main(String[] args) throws NoSuchFieldException {
        Field field = LDA.class.getField("str1");
        System.out.println();
    }
}

class P {
    private int data1;
    public String str1;
}

class LDA extends P {
    private int data2;
    public String str2;
}

输出:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LJZmyY4T-1590032423699)(_v_images/20191219114819563_1442375214.png)]

可以获取到父类P中定义的public的域。

getDeclaredField(String name)

这个类可以获取到本类中定义的指定name的域。如果获取不到,将抛NoSuchFieldException异常。

现在用getDeclaredField获取父类的str1:Field field = LDA.class.getDeclaredField("str1");,结果抛异常了:java.lang.NoSuchFieldException: str1

再用getDeclaredField获取子类的data2:Field field = LDA.class.getDeclaredField("data2");

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lz2tvAsI-1590032423699)(_v_images/20191219115123403_1947323581.png)]

Filed和Class一样,也有getModifiers方法可以获取到Filed的修饰符信息。

利用反射,设置或者获取域的值

先来看看设置公有域:

LDA lda = new LDA();
Field field = LDA.class.getField("str1");
field.set(lda,"123");
System.out.println(field.get(lda));

输出:

123

更多的Field操作问题,在Field类分析时再详细研究。

反射-Method

方法对象的获取,和Filed对象的获取很类似。只是比Filed多了的getEnclosingMethod()方法:

  • getMethods()
  • getMethod(String name, Class<?>… parameterTypes)
  • getDeclaredMethods()
  • getDeclaredMethods(String name, Class<?>… parameterTypes)
  • getEnclosingMethod()

getMethods()

这个方法可以获取到类上可调用的所有public方法,包括继承来的和static方法。

public class TypeMain {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        LDA lda = new LDA();
        Method[] methods = LDA.class.getMethods();
        System.out.println();
    }
}

class P {
    public void pm1(String str){    }
    private void pm2(){    }
    public static void sm(){}
}

class LDA extends P {
    private void ldam1(Double d){    }
    public void ldam2(){    }
    public static void ldas(){}
}

输出:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rsezX4lf-1590032423700)(_v_images/20191219131317066_1393034261.png)]

可以看到,只要是public方法,不管是继承的还是自己声明的都可以被获取到。

getDeclaredMethods()

这个方法可以获取到当前类声明的方法,不管访问权限是什么,静态方法也可以被获取到。

更改代码

Method[] methods = LDA.class.getDeclaredMethods();

输出:

在这里插入图片描述

getMethod(String name, Class<?>… parameterTypes)

这个方法可以根据方法名,指定方法参数类型获取Class中public访问权限的方法对象。只要是public的,不管是不是继承的。如果获取不到,会抛异常。

Method method = LDA.class.getMethod("pm1", String.class);

查看method:

在这里插入图片描述

getDeclaredMethods(String name, Class<?>… parameterTypes)

这个方法可以根据方法名,指定方法参数类型获取到类中自己定义的方法对象,不管是什么访问权限的。

Method method = LDA.class.getDeclaredMethod("ldam1", Double.class);

查看method:
在这里插入图片描述

前面说Class有一个getModifiers方法可以获取到类的修饰符信息,实际上对于Method来说,也有这个方法,可以获取到方法的修饰符信息。

getEnclosingMethod()

假如一个Class是在方法中定义的局部内部类或者匿名内部类,那么在这个类的Class对象上调用getEnclosingMethod方法,那么可以获取到该方法的Method对象。

java文档中的解释:

If this Class object represents a local or anonymous class within a method, returns a Method object representing the immediately enclosing method of the underlying class. Returns null otherwise. In particular, this method returns null if the underlying class is a local or anonymous class immediately enclosed by a type declaration, instance initializer or static initializer.

这句话前面一部分很好理解,后面部分关于什么情况下返回null,英语不好的人有点难理解。

immediately enclosed是直接被包含。
instance initializer:是指类内部实例初始化代码块。
static initializer:是指类内部静态初始化代码块。

后面一句话的意思是:如果局部内部类或者匿名内部类是被类型声明,实例初始化代码块,静态初始化代码快直接包含的,那么这个方法将会范围null。总之一句话,只有在方法内部声明的类,在Class上调用这个方法的时候,才不会返回null。

写代码验证一下:

public class TypeMain {
    public static void main(String[] args) throws NoSuchMethodException {
        LDA lda = new LDA();
        lda.ldam2();
    }
}



class LDA {
    class MemberClass{}

    static{
        class StaticInner{}
        Method enclosingMethod = StaticInner.class.getEnclosingMethod();
        System.out.println();
    }

    {
        class InstInner{}
        Method enclosingMethod = InstInner.class.getEnclosingMethod();
        System.out.println();
    }
    private void ldam1(Double d) {
        class MehtodInner{}
        Class<?> enclosingClass = MehtodInner.class.getEnclosingClass();
        System.out.println();
    }

    public void ldam2() {
        ldam1(1.0);
        Method enclosingMethod = MemberClass.class.getEnclosingMethod();
        System.out.println();
    }
}

在每个输出语句那下断点,观察获取到Method对象。

只有Class<?> enclosingClass = MehtodInner.class.getEnclosingClass();这一行获取到了Method对象,其他地方获取到的都是null。

通过反射调用方法

获取到Method对象之后,就可以在对象上调用该方法了。

public class TypeMain {
    public static void main(String[] args)
            throws InvocationTargetException, IllegalAccessException {
        LDA lda = new LDA();
        Method[] declaredMethods = LDA.class.getDeclaredMethods();
        for(Method m: declaredMethods){
            if((m.getModifiers() & 0x02) == 0x02){
                m.setAccessible(true);
            }
            if(m.getParameterCount() == 0){
                m.invoke(lda, (Object[]) null);
            }
        }
    }
}

class LDA {
    public void ldam2() {
        System.out.println("ldam2被调用");
    }

    private void ldam21(){
        System.out.println("ldam21被调用");
    }

    public void ldam22(String str){
        System.out.println("ldam22被调用");
    }
}

这里通过反射的方式,把LDA自定义的无参方法给都调用一遍。Mehtod的getParameterCount方法可以获取到参数个数。如果是非private的方法,还设置了访问权限。这里没有protect方法,否则会抛异常。

注解相关

Class对象包含了一个类的所有信息,所以当然也包含了声明这个类时使用的注解。为了保存Class上声明的注解信息,Class类中定义了私有静态内部类AnnotationData。

除了isAnnotation()这个前面已经出现之外,注解相关的方法还有9个之多。

getAnnotations

官方文档说明:Returns annotations that are present on this element. If there are no annotations present on this element, the return value is an array of length 0. The caller of this method is free to modify the returned array; it will have no effect on the arrays returned to other callers.

看一下这个方法的声明:

public Annotation[] getAnnotations()

这个方法会返回当前类上所有的注解,以数组的形式返回。
实际上,每个Class对象中,都有个AnnotationData对象:

private volatile transient AnnotationData annotationData;

这个对象保存了修饰这个类上的注解的信息。注解信息保存在AnnotationData类的annotations和declaredAnnotations这两个域中:

final Map<Class<? extends Annotation>, Annotation> annotations;
final Map<Class<? extends Annotation>, Annotation> declaredAnnotations;

这是两个Map。

以代码来看看这个AnnotationData类内容

@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.TYPE})
@Documented
public @interface AnnoA {

}

@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.TYPE})
@Documented
public @interface AnnoB {
}

public class AnnoTest {
    public static void main(String[] args) {
        Class<A> aClass = A.class;
        Annotation[] annotations = aClass.getAnnotations();
        System.out.println();
    }
}

@AnnoA
@AnnoB
class A{}

在输出语句那行打断点,看一下Class对象中annotationData域:
在这里插入图片描述

注解是以接口的形式实现的。所以当使用注解对象的时候,实际上java是创建了这个注解的动态代理对象。annotations和declaredAnnotations这2个map是以注解本身的Class对象为key,以注解对应的代理对象为value保存的。

再来看一下Annotation[] annotations = aClass.getAnnotations();这一句的执行结果:

在这里插入图片描述

结合前面看到返回的是注解实例化之后的动态代理对象。这些动态代理对象,都是Annotation类型的,也就是说,创建注解实体的时候,实现了Annotation接口。Annotation接口。我们知道注解其实是通过接口实现的,可以把注解就看成是接口。当注解实例化的时候,它必定也是实现了注解本身的那些方法的。

我们知道前面annotations的第一个元素是AnnoA的实现,所以可以:AnnoA ann = (AnnoA) annotations[0];。如果这个注解有方法,那么就可以在ann上调用这个注解的方法来获取到配置的值了。

getDeclaredAnnotations()

因为注解是可以继承的。前面getAnnotations方法可以把继承来的注解一起返回,而这个方法只返回直接放置在这个类上的注解,如果是继承来的注解,那么就获取不到了。

可以用下面的代码验证:
先把注解改成可继承的,之需要用@Inherited修饰自定义注解即可。注解的继承不是值注解之前的继承,而是值使用了注解修饰的类,被另一个类继承时,这个子类是否被父类的注解修饰。

@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.TYPE})
@Documented
@Inherited
public @interface AnnoA {

}

@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.TYPE})
@Documented
@Inherited
public @interface AnnoB {
}

测试:

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

        Class<B> aClassB = B.class;
        Annotation[] annotationsB = aClassB.getAnnotations();
        Annotation[] declaredAnnotations = aClassB.getDeclaredAnnotations();
        System.out.println();
    }
}

@AnnoA
@AnnoB
class A{}

class B extends A{}

查看:
在这里插入图片描述

getAnnotation(Class<A> annotationClass)

这个方法获取类上指定类型的注解。

getDeclaredAnnotation(Class<A> annotationClass)

这个方法只获取类上直接标记的指定类型的注解,继承来的不算。

getAnnotationsByType(Class<A> annotationClass)

getAnnotationsByType和下面的getDeclaredAnnotationsByType是jdk1.8新增的,实际上他们的作用和getAnnotation以及getDeclaredAnnotation类似。只不过在jdk1.8之前,这2个方法是不对@Repeatable处理的,因为这个是jdk1.8新增的元注解。为了处理@Repeatable注解,引入了getAnnotationsByType和下面的getDeclaredAnnotationsByType方法。

getDeclaredAnnotationsByType(Class<A> annotationClass)

public <A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass)

isAnnotationPresent(Class<? extends Annotation> annotationClass)

如果指定类型的注解存在于此元素上,则返回 true,否则返回 false。

getAnnotatedInterfaces()

Java8新增方法,获取Class对象对应类实现的所有带注解的接口类型

查看这个方法的定义:

public AnnotatedType[] getAnnotatedInterfaces() 

getAnnotatedSuperclass()

返回父类的注解的AnnotatedType

public AnnotatedType getAnnotatedSuperclass()

java文档:

Returns an AnnotatedType object that represents the use of a type to specify the superclass of the entity represented by this Class object. (The use of type Foo to specify the superclass in ‘… extends Foo’ is distinct from the declaration of type Foo.)
If this Class object represents a type whose declaration does not explicitly indicate an annotated superclass, then the return value is an AnnotatedType object representing an element with no annotations.

If this Class represents either the Object class, an interface type, an array type, a primitive type, or void, the return value is null.

其它重要方法

isAssignableFrom(Class<?> cls)

这个方法很重要。 class1.isAssignableFrom(class2) 判定此 Class对象(class1)所表示的类或接口与指定的 Class参数(class2)所表示的类或接口是否相同,或是否是其超类或超接口。如果是则返回 true;否则返回 false。如果该Class对象(class1)表示一个基本类型,且指定的 Class参数(class2)正是该 Class 对象,则该方法返回 true;否则返回 false。

也可以这么理解,class2是不是可以看作class1. 比如有个类A,Object.class.isAssignableFrom(A.class),就表示,A是否可以看作Object类型。

assignable是可转换,class1.isAssignableFrom(class2)就是说:class1可否由class2转换而来。也就是说class2是否可以转换为class1.

  1. class2是不是class1的子类或者子接口
  2. Object是所有类的父类
package com.auuzee.test1;

class A {
}

class B extends A {
}

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

        A a = new A();
        B b = new B();
        A ba = new B();
        System.out.println("1-------------");
        System.out.println(A.class.isAssignableFrom(a.getClass()));
        System.out.println(B.class.isAssignableFrom(b.getClass()));
        System.out.println(A.class.isAssignableFrom(b.getClass()));
        System.out.println(B.class.isAssignableFrom(a.getClass()));
        System.out.println(A.class.isAssignableFrom(ba.getClass()));
        System.out.println(B.class.isAssignableFrom(ba.getClass()));
        System.out.println("2-------------");
        System.out.println(a.getClass().isAssignableFrom(A.class));
        System.out.println(b.getClass().isAssignableFrom(B.class));
        System.out.println(a.getClass().isAssignableFrom(B.class));
        System.out.println(b.getClass().isAssignableFrom(A.class));
        System.out.println(ba.getClass().isAssignableFrom(A.class));
        System.out.println(ba.getClass().isAssignableFrom(B.class));
        System.out.println("3-------------");
        System.out.println(Object.class.isAssignableFrom(b.getClass()));
        System.out.println(Object.class.isAssignableFrom("abc".getClass()));
        System.out.println("4-------------");
        System.out.println("a".getClass().isAssignableFrom(Object.class));
        System.out.println("abc".getClass().isAssignableFrom(Object.class));
    }
}

Determines if the class or interface represented by this Class object is either the same as, or is a superclass or superinterface of, the class or interface represented by the specified Class parameter. It returns true if so; otherwise it returns false. If this Class object represents a primitive type, this method returns true if the specified Class parameter is exactly this Class object; otherwise it returns false.

Specifically, this method tests whether the type represented by the specified Class parameter can be converted to the type represented by this Class object via an identity conversion or via a widening reference conversion. See The Java Language Specification, sections 5.1.1 and 5.1.4 , for details.

asSubclass

public <U> Class<? extends U> asSubclass(Class<U> clazz) 

cast

public T cast(Object obj)

desiredAssertionStatus

getEnumConstants

getModifiers

在isAnnotation一节,有关于这个方法的详细讲述。

getPackage

getProtectionDomain

getResource

getResourceAsStream

getSigners

返回类的前面,如果没有签名或者是一个基本类型或者是void则,返回null。
说了等于没说。

getTypeName

getTypeParameters

isInstance

方法声明:public native boolean isInstance(Object obj);

用来判断一个对象是否是该类型(这里说的是类型,而不是类)的实例。这是一种类型上的判断,而不是类层面的:

public class Test {

    public static void main(String[] args) {
        CB cb = new CB();
        System.out.println(CB.class.isInstance(cb));
        System.out.println(CA.class.isInstance(cb));
    }
}

class CA {}

class CB extends CA{}

输出:

true
true

toGenericString

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值