Class详解

转自:https://www.jianshu.com/p/bcb5970e4014

在运行中的Java应用中,Class类的实例代表类或者接口。
enum、数组是一种类,annotation是一种接口。
Java原始类型(boolean,byte,char,short,int,log,float,double)以及void关键字也是类。

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

AnnotatedElement是注解相关接口,可以用来获取Class的注解。
GenericDeclaration是泛型声明接口。

toString

    public String toString() {
        return (isInterface() ? "interface " : (isPrimitive() ? "" : "class "))
            + getName();
    }

toString方法对于原始类型(int.class等)只打印类名。对于接口,类名前带有“interface”前缀;对于普通类则带有“class”前缀。

toGenericString

    /*与`toString`的不同在于:
        1. 带有可见性修饰符。
        2. 对不同类型区分的更细。如annotation、interface、enum、class、原始类型。
        3. 若是泛型,会带有泛型类型名。
    */
    public String toGenericString() {
        if (isPrimitive()) {
            return toString();
        } else {
            StringBuilder sb = new StringBuilder();

            // Class modifiers are a superset of interface modifiers
            int modifiers = getModifiers() & Modifier.classModifiers();
            if (modifiers != 0) {
                sb.append(Modifier.toString(modifiers));
                sb.append(' ');
            }

            if (isAnnotation()) {
                sb.append('@');
            }
            if (isInterface()) { // Note: all annotation types are interfaces
                sb.append("interface");
            } else {
                if (isEnum())
                    sb.append("enum");
                else
                    sb.append("class");
            }
            sb.append(' ');
            sb.append(getName());

            TypeVariable<?>[] typeparms = getTypeParameters();//获取泛型参数列表
            if (typeparms.length > 0) {
                boolean first = true;
                sb.append('<');
                for(TypeVariable<?> typeparm: typeparms) {
                    if (!first)
                        sb.append(',');
                    sb.append(typeparm.getTypeName());
                    first = false;
                }
                sb.append('>');
            }

            return sb.toString();
        }
    }

public class JavaDemo<T extends Number, K> {
    public static void main(String[] args) {
        System.out.println(JavaDemo.class.toString());
        System.out.println(JavaDemo.class.toGenericString());
    }
}

输出如下:

class demo.sollian.com.demo.JavaDemo
public class demo.sollian.com.demo.JavaDemo<T,K>

forName

该函数与ClassLoader.loadClass相似,也是用来加载类。有两个重载方法:

    public static Class<?> forName(String className)
                throws ClassNotFoundException {
        return forName(className, true, VMStack.getCallingClassLoader());
    }

    public static Class<?> forName(String name, boolean initialize,
                                   ClassLoader loader)
        throws ClassNotFoundException
    {
        if (loader == null) {
            loader = BootClassLoader.getInstance();
        }
        Class<?> result;
        try {
            result = classForName(name, initialize, loader);
        } catch (ClassNotFoundException e) {
            Throwable cause = e.getCause();
            if (cause instanceof LinkageError) {
                throw (LinkageError) cause;
            }
            throw e;
        }
        return result;
    }

第二个方法需要三个参数:类名称、是否初始化以及ClassLoader。第一个函数默认使用当前类的ClassLoader。

initialize为true时,会对类进行初始化,即执行类的静态代码块,初始化静态成员等。否则在对加载的类对象调用newInstance方法时进行类初始化。ClassLoader.load方法仅仅加载类,不会初始化。这是forNameloadClass的一点区别。

该方法不能用于获取原始类型或者void。比如int.class.getName()返回int,但若使用Class.forName("int")则会抛出ClassNotFoundException

如果name表示一个数组类,那么数组的元素类对象会自动加载,但不会初始化。比如:

public class Data {
    static {
        System.out.println("我来了");
    }
}

上面是Data类。如下代码加载Data类,会执行静态代码块:

public static void main(String[] args) {
        try {
            Class<?> clazz = Class.forName("demo.sollian.com.demo.clazz.Data", true, ClassLoader.getSystemClassLoader());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

通过System.out.println(Car[].class.getName())打印其数组类如下:

[Ldemo.sollian.com.demo.Car;

加载其数组类:

    public static void main(String[] args) {
        try {
            Class<?> clazz = Class.forName("[Ldemo.sollian.com.demo.clazz.Data;", true, getSystemClassLoader());
//            if (clazz != null) {
//                Object obj = clazz.getComponentType().newInstance();
//            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

不会执行Data类的静态代码块,去掉注释,则会执行Data类的静态代码块。

需要注意的是,该方法并不会检查要加载的类对调用者是否可见。

public native T newInstance() throws InstantiationException, IllegalAccessException

创建一个类的实例,与调用该类的无参构造函数相似。如果该类未初始化,将进行初始化。
该方法会传递无参构造函数抛出的任何异常。

isInstance

    public boolean isInstance(Object obj) {
        if (obj == null) {
            return false;
        }
        return isAssignableFrom(obj.getClass());
    }

instanceof运算符等价。判断obj是否是调用类或其子类的实例。
若obj为null,则始终返回false。

若调用类是原始类型,则始终返回false,如:

        System.out.println(int.class.isInstance(2));
        System.out.println(Integer.class.isInstance(2));

返回false true

若调用类是数组类型,如:

        System.out.println(List[].class.isInstance(new List[2]));
        System.out.println(List[].class.isInstance(new ArrayList[2]));
        System.out.println(int[].class.isInstance(new Integer[2]));
        System.out.println(Integer[].class.isInstance(new int[2]));

返回true true false false

isAssignableFrom

isInstance最终调用了该方法来实现,从该方法可以学习其判断逻辑。

public boolean isAssignableFrom(Class<?> cls) {
        if (this == cls) {//同一个类类型
            return true;  // Can always assign to things of the same type.
        } else if (this == Object.class) {//调用类为Object类型,则当cls为原始类型时返回false。
            return !cls.isPrimitive();  // Can assign any reference to java.lang.Object.
        } else if (isArray()) {//同是数组时,比较其元素的类型
            return cls.isArray() && componentType.isAssignableFrom(cls.componentType);
        } else if (isInterface()) {//若调用类是接口,则当cls是该接口的实现类时,返回true。
            // Search iftable which has a flattened and uniqued list of interfaces.
            Object[] iftable = cls.ifTable;
            if (iftable != null) {
                for (int i = 0; i < iftable.length; i += 2) {
                    if (iftable[i] == this) {
                        return true;
                    }
                }
            }
            return false;
        } else {//否则判断调用类是否是cls的父类。
            if (!cls.isInterface()) {
                for (cls = cls.superClass; cls != null; cls = cls.superClass) {
                    if (cls == this) {
                        return true;
                    }
                }
            }
            return false;
        }
    }

isInterface

判断是否是接口类型。

isArray

    public boolean isArray() {
        return getComponentType() != null;
    }

判断是否是数组。

isPrimitive

判断是否是原始类型。除boolean byte char short int long float double外,以下值也表示原始类型:

java.lang.Boolean#TYPE
java.lang.Character#TYPE
java.lang.Byte#TYPE
java.lang.Short#TYPE
java.lang.Integer#TYPE
java.lang.Long#TYPE
java.lang.Float#TYPE
java.lang.Double#TYPE
java.lang.Void#TYPE

isAnnotation

判断是否是注解类型。若该方法返回true,则isInterface也返回true。

isSynthetic

判断是否是合成类。可以参考synthetic Java合成类型

getName

返回名称,包括类/接口/数组类型/原始类型/void。

若是数组类型,则“[”的个数表示数组的维度,元素类型表示如下:

元素类型 表示符号
boolean Z
byte B
char C
double D
float F
int I
long J
short S
class or interface Lclassname

如:

        System.out.println(void.class.getName());
        System.out.println(int[].class.getName());
        System.out.println(boolean[][][].class.getName());
        System.out.println(TestAnnotation[].class.getName());//注解
        System.out.println(Car[].class.getName());

结果如下:

void
[I
[[[Z
[Ldemo.sollian.com.demo.TestAnnotation;
[Ldemo.sollian.com.demo.Car;

getClassLoader

返回加载这个类的ClassLoader。返回null来表示bootstrap class loader。如果该类表示原始类型或者void,则返回null。

public synchronized TypeVariable<Class<T>>[] getTypeParameters()

该方法是GenericDeclaration接口的方法,返回类型的泛型参数。如:

public class JavaDemo<T extends Number, K> {
    public static void main(String[] args) {
        for (TypeVariable<Class<JavaDemo>> var : JavaDemo.class.getTypeParameters()) {
            System.out.print(var.getName() + ",");
            for (Type type : var.getBounds()) {
                System.out.print(type);
            }
            System.out.println();
        }
    }
}

输出如下:

T,class java.lang.Number
K,class java.lang.Object

getSuperclass

    public Class<? super T> getSuperclass() {
        // For interfaces superClass is Object (which agrees with the JNI spec)
        // but not with the expected behavior here.
        if (isInterface()) {
            return null;
        } else {
            return superClass;
        }
    }

返回当前类的父类。

注:

如果当前类是Object、接口、原始类型或者void,则返回null。
如果当前类是数组类型,则返回Object。

getGenericSuperclass

    public Type getGenericSuperclass() {
        Type genericSuperclass = getSuperclass();
        // This method is specified to return null for all cases where getSuperclass
        // returns null, i.e, for primitives, interfaces, void and java.lang.Object.
        if (genericSuperclass == null) {
            return null;
        }

        String annotationSignature = getSignatureAttribute();
        if (annotationSignature != null) {
            GenericSignatureParser parser = new GenericSignatureParser(getClassLoader());
            parser.parseForClass(this, annotationSignature);
            genericSuperclass = parser.superclassType;
        }
        return Types.getType(genericSuperclass);
    }

返回带有参数类型的父类。getSuperclass的“注”同样适用于该方法。另外两个方法的返回值是不同的。

public class JavaDemo {
    public static void main(String[] args) {
        System.out.println(Child.class.getSuperclass());
        System.out.println(Child.class.getGenericSuperclass());
    }

    private static class Parent<T> {
    }

    private static class Child extends Parent<String> {
    }
}

结果为:

//前者返回值为Class类型,后者返回值为ParameterizedType类型
class demo.sollian.com.demo.JavaDemo$Parent
demo.sollian.com.demo.JavaDemo$Parent<java.lang.String>

public Package getPackage

返回类所在的包的信息。

getInterfaces

    public Class<?>[] getInterfaces() {
        if (isArray()) {
            return new Class<?>[] { Cloneable.class, Serializable.class };
        }

        final Class<?>[] ifaces = getInterfacesInternal();
        if (ifaces == null) {
            return EmptyArray.CLASS;
        }

        return ifaces;
    }

若该类是

  1. 普通类:返回其实现的所有接口,若没有则返回空数组。
  2. 接口:返回其扩展的所有接口,若没有则返回空数组。
  3. 数组:返回new Class<?>[] { Cloneable.class, Serializable.class }
  4. 原始类型或者void:返回空数组。

数组中接口的顺序,与类或接口文件中实现或扩展的接口顺序相同。

getGenericInterfaces

参考getGenericSuperclass

public Class<?> getComponentType

若该类是数组类型,则返回该数组的元素类型;否则返回null。

getModifiers

返回类的修饰符,包括public, protected, private, final, static, abstract和interface,用一个int值表示。
如果是一个数组类型,它的访问修饰符(public, protected, private)和其元素一样。
如果是原始类型或者void,则访问修饰符是public(返回的int值中代表public的位等于1)
如果是数组类型或者原始类型或者void,则final位为1,interface位为0。

public native Class<?> getDeclaringClass()

如果该类是一个内部类(匿名内部类除外),则返回它的外部类;否则返回null。
对于枚举,可以参见java 枚举类 getClass和getDeclaringClass的区别

getSimpleName

public String getSimpleName() {
        if (isArray())//数组类型,如int数组,返回“int[]”
            return getComponentType().getSimpleName()+"[]";

        if (isAnonymousClass()) {//匿名类
            return "";
        }

        if (isMemberClass() || isLocalClass()) {//内部类或者局部类
            // Note that we obtain this information from getInnerClassName(), which uses
            // dex system annotations to obtain the name. It is possible for this information
            // to disagree with the actual enclosing class name. For example, if dex
            // manipulation tools have renamed the enclosing class without adjusting
            // the system annotation to match. See http://b/28800927.
            return getInnerClassName();
        }

        String simpleName = getName();
        final int dot = simpleName.lastIndexOf(".");
        if (dot > 0) {
            return simpleName.substring(simpleName.lastIndexOf(".")+1); // strip the package name
        }

        return simpleName;
    }

public native boolean isAnonymousClass()

是否是匿名类。如:

System.out.println(new ArrayList(){}.getClass().isAnonymousClass());//true

isLocalClass

判断是否是局部类。如:

public class JavaDemo implements Cloneable {

    public static void main(String[] args) {
        new JavaDemo().test();
    }

    public void test() {
        class B extends ArrayList{
        }
 
        System.out.println(B.class.isLocalClass());
    }
}

结果为true。

isMemberClass

判断是否是内部类。如:

public class JavaDemo implements Cloneable {
    public static void main(String[] args) {
        System.out.println(A.class.isMemberClass());
    }

    private static class A{
    }
}

结果为true。

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 isLocalClass() || isAnonymousClass();
    }

getClasses

    public Class<?>[] getClasses() {
        List<Class<?>> result = new ArrayList<Class<?>>();
        for (Class<?> c = this; c != null; c = c.superClass) {
            for (Class<?> member : c.getDeclaredClasses()) {
                if (Modifier.isPublic(member.getModifiers())) {
                    result.add(member);
                }
            }
        }
        return result.toArray(new Class[result.size()]);
    }

返回该类及其所有父类中所有的访问修饰符为public的内部类和接口。如果该类型是原始类型、数组或者void,则返回一个空数组。

public native Class<?>[] getDeclaredClasses()

返回当前类的所有内部类和接口。


public Field[] getFields() throws SecurityException

返回该类及其所有父类的所有访问修饰符为public的字段。如果该类型是原始类型、数组或者void,则返回一个空数组。

public native Field[] getDeclaredFields()

返回当前类的所有字段。


public Method[] getMethods() throws SecurityException

返回该类及其所有父类及实现的接口中所有的访问修饰符为public的方法。如果该类型是原始类型或者void,则返回一个空数组。
不包括构造函数。
若有方法覆写的情况,则返回子类的方法。

public Method[] getDeclaredMethods() throws SecurityException

返回当前类的所有方法,不含构造方法。


public Constructor<?>[] getConstructors() throws SecurityException

返回该类所有的访问修饰符为public的构造方法。如果该类型是原始类型、数组或者void,则返回一个空数组。

public Constructor<?>[] getDeclaredConstructors() throws SecurityException

返回当前类的所有构造方法。


public Field getField(String name) throws NoSuchFieldException

返回该类或其所有父类、父接口中名称为name且访问修饰符为public的字段。找不到则抛出NoSuchFieldException异常。

public native Field getDeclaredField(String name) throws NoSuchFieldException

返回当前类名称为name的字段。找不到则抛出NoSuchFieldException异常。

不能用来获取数组的length字段。


public Method getMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException

返回该类或其所有父类、父接口中名称为name且访问修饰符为public的方法。找不到则抛出NoSuchMethodException异常。
parameterTypes为所要查找的方法的参数列表,顺序一致。需要注意的是,若方法的参数为int,则需要传入int.class;若是Integer,则需要传入Integer.class。其他原始类型及其包装类型以此类推。
该方法无法获取到构造方法。

public Method getDeclaredMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException

返回当前类的名称为name,参数列表为parameterTypes的方法。


public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException

返回该类访问修饰符为public,且参数类型为parameterTypes的构造方法。

若该类是一个非静态内部类,需要将其外部类作为第一个参数。如下:

public class JavaDemo extends JavaDemo2 implements Cloneable {
    public static void main(String[] args) {
        try {
            Constructor method = A.class.getConstructor(JavaDemo.class, //传入外部类
                    int.class);
            if (method != null) {
                System.out.println(method);
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    private class A {
        public A(int a) {
        }
    }
}

输出为:

public demo.sollian.com.demo.JavaDemo$A(demo.sollian.com.demo.JavaDemo,int)

public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException

返回该类参数类型为parameterTypes的构造方法。


getResourceAsStream

     public InputStream getResourceAsStream(String name) {
        name = resolveName(name);
        ClassLoader cl = getClassLoader();
        if (cl==null) {
            // A system class.
            return ClassLoader.getSystemResourceAsStream(name);
        }
        return cl.getResourceAsStream(name);
    }

    private String resolveName(String name) {
        if (name == null) {
            return name;
        }
        if (!name.startsWith("/")) {
            Class<?> c = this;
            while (c.isArray()) {
                c = c.getComponentType();
            }
            String baseName = c.getName();
            int index = baseName.lastIndexOf('.');
            if (index != -1) {
                name = baseName.substring(0, index).replace('.', '/')
                    +"/"+name;
            }
        } else {
            name = name.substring(1);
        }
        return name;
    }

getResource

    public java.net.URL getResource(String name) {
        name = resolveName(name);
        ClassLoader cl = getClassLoader();
        if (cl==null) {
            // A system class.
            return ClassLoader.getSystemResource(name);
        }
        return cl.getResource(name);
    }

isEnum

判断该类是否是枚举类型

getEnumConstants

如果该类是枚举类型,则返回所有元素的数组,否则返回null。

cast

    public T cast(Object obj) {
        if (obj != null && !isInstance(obj))
            throw new ClassCastException(cannotCastMsg(obj));
        return (T) obj;
    }

将obj强转成该类的类型。

getAnnotation

    public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
        Objects.requireNonNull(annotationClass);

        A annotation = getDeclaredAnnotation(annotationClass);
        if (annotation != null) {
            return annotation;
        }

        if (annotationClass.isDeclaredAnnotationPresent(Inherited.class)) {
            for (Class<?> sup = getSuperclass(); sup != null; sup = sup.getSuperclass()) {
                annotation = sup.getDeclaredAnnotation(annotationClass);
                if (annotation != null) {
                    return annotation;
                }
            }
        }

        return null;
    }

返回该类或所有父类中带Inherited注解的类型为annotationClass的注解,如果没有则返回null。

有时候拿不到,是因为注解的RetentionPolicy的问题。

  • SOURCE:仅在源码中保留,编译时会被丢弃。
  • CLASS:编译时保留,运行时被丢弃。
  • RUNTIME:一直保留。

public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)

判断该类或所有父类中带Inherited注解的注解中,是否包含指定类型的注解。

getAnnotations

    public Annotation[] getAnnotations() {
        /*
         * We need to get the annotations declared on this class, plus the
         * annotations from superclasses that have the "@Inherited" annotation
         * set.  We create a temporary map to use while we accumulate the
         * annotations and convert it to an array at the end.
         *
         * It's possible to have duplicates when annotations are inherited.
         * We use a Map to filter those out.
         *
         * HashMap might be overkill here.
         */
        HashMap<Class<?>, Annotation> map = new HashMap<Class<?>, Annotation>();
        for (Annotation declaredAnnotation : getDeclaredAnnotations()) {
            map.put(declaredAnnotation.annotationType(), declaredAnnotation);
        }
        for (Class<?> sup = getSuperclass(); sup != null; sup = sup.getSuperclass()) {
            for (Annotation declaredAnnotation : sup.getDeclaredAnnotations()) {
                Class<? extends Annotation> clazz = declaredAnnotation.annotationType();
                if (!map.containsKey(clazz) && clazz.isDeclaredAnnotationPresent(Inherited.class)) {
                    map.put(clazz, declaredAnnotation);
                }
            }
        }

        /* Convert annotation values from HashMap to array. */
        Collection<Annotation> coll = map.values();
        return coll.toArray(new Annotation[coll.size()]);
    }

返回该类或所有父类中带Inherited注解的所有注解。

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

返回当前类中类型为annotationClass的注解。

public native Annotation[] getDeclaredAnnotations()

返回当前类的所有注解。



作者:sollian
链接:https://www.jianshu.com/p/bcb5970e4014
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

猜你喜欢

转载自blog.csdn.net/blueangle17/article/details/83057798