【读码JDK】-带你详细了解lang.Class类(一)

简介: 给你详细讲解Class类近80个方法

在这里插入图片描述
【读码JDK】java.lang包目录

该文章根据JDK中的API进行逐个讲解,自己的理解、使用案例和总结,同时为了避免不可抗拒因素产生的误导,我会带上JDK原有的注释,便于读者自行判断。
Class类里面方法实在太多了,分两篇文章,我自己整理都整理了很久。

java.lang.Class类Api介绍及测试

测试实体

首先这是本篇文章用到的几个测试类,Animal是顶级父类,Person继承了Animal,ChinesePeople 继承自Person

//顶级父类
public interface Animal {
}

//人类
public class Person implements Animal {
    public String name;
    public int age;
    private String address;
    Status status;

    public Status getStatus() {
        return status;
    }

    public void setStatus(Status status) {
        this.status = status;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public <F> String getName() {
        return name;
    }

    public void setName(String name) {
        System.out.println("name=" + name);
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", address='" + address + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                Objects.equals(name, person.name) &&
                Objects.equals(address, person.address);
    }

    @Override
    public int hashCode() {
        return 111;
    }
}

//中国人继承人类
public class ChinesePeople extends Person {

    static {
        System.out.println("加载类");
    }

    public ChinesePeople() {
        System.out.println("实例化类,我是中国人");
    }

    public ChinesePeople(String name) {
        System.out.println(name + ",我是中国人");
    }

    public class ZjPeople {

    }
}

toGenericString

返回描述Class的字符串,包括修饰符,和类型参数的信息

System.out.println(this.getClass().toGenericString()); 
//public class lang.ClassTest

forName

该方法我们经常在反射的时候用到,用于将类文件加载到ClassLoader中

 /**
  * 返回与给定字符串名称相关联的类或接口的Class对象
  *  此方法等效于:
  *  Class.forName(className, true, currentLoader)
  * 其中currentLoader表示当前类的定义类加载器。
  * 例如,以下代码片段返回名为java.lang.Thread的类的运行时类描述符:
  *  Class t = Class.forName("java.lang.Thread")
  * 调用forName("X")会导致名为X的类被初始化。
  *
  * 参数:
  * className - 类的全限定名
  */
 Class chinesePeople = Class.forName("lang.ChinesePeople");

 /**
  * 使用指定的类加载器,加载给定字符串名称相关联的Class对象
  *
  * 参数:
  * name - 类的全限定名
  * initialize - true 代表加载类,false代表不加载类
  * loader - 指定的类加载器
  */
 Class chinessPeople1 = Class.forName("lang.ChinesePeople", true, this.getClass().getClassLoader());

 /**
  * 返回指定模块中,与给定字符串相关的Class对象
  * 不会初始化类
  *
  * 参数:
  *  module - 模块
  *  name - 类的全限定名
  */
 //todo
 //Class.forName();

newInstance

创建Class对象的实例 会执行无参构造器

在JDK9中过期,使用clazz.getDeclaredConstructor().newInstance()替

Class chinesePeople = Class.forName("lang.ChinesePeople");
ChinesePeople chinesePeople2 = (ChinesePeople) chinesePeople.newInstance();
ChinesePeople chinesePeople3 = (ChinesePeople) chinesePeople.getDeclaredConstructor().newInstance();
/**
 * 加载类
 * 实例化类,我是中国人
 * 实例化类,我是中国人
 */

isInstance

确定指定的Object是否与该Class表示的对象赋值兼容。

/**
 * 确定此Class对象表示的类或接口是否与指定的Class参数表示的类或接口相同,或者是其超类或超接口。如果是,则返回true ;否则返回false 。如果此Class对象表示原始类型,则如果指定的Class参数正是此Class对象,则此方法返回true ;否则返回false 。
 * 具体来说,该方法测试指定的Class参数表示的类型是否可以通过恒等转换或扩展引用转换转换为该Class对象表示的类型。有关详细信息,请参阅Java 语言规范的第 5.1.1 和 5.1.4 节。
 * 参形:
 * cls – 要检查的Class对象
 * 返回值:
 * boolean值,指示是否可以将cls类型的对象分配给此类的对象
 * 抛出:
 * NullPointerException – 如果指定的 Class 参数为 null。
 * 自:
 * 1.1
 */
//指定的对象实例是否与该类对象兼容,是否可以转换
ChinesePeople chinesePeople = new ChinesePeople();
Person person = new Person();
//父类不能转换为子类
System.out.println(chinesePeople.getClass().isInstance(person)); //false
//子类可以转换为父类
System.out.println(person.getClass().isInstance(chinesePeople)); //true
System.out.println(Person.class.isInstance(chinesePeople)); //true

//对象是本身类的一个实例
System.out.println(ChinesePeople.class.isInstance(chinesePeople)); //true
//不是一个对象
System.out.println(ChinesePeople.class.isInstance(ChinesePeople.class)); //false

//如果object是数组对象
Integer[] integers = new Integer[0];
System.out.println(Number.class.isInstance(integers)); //false
//除了这种情况,还有其他等于true的情况吗??
System.out.println(new Integer[0].getClass().isInstance(integers)); //true

Person[] persons = new Person[0];
ChinesePeople[] chinesePeoples = new ChinesePeople[0];
Person[] chinesePeople2 = new ChinesePeople[0];
//chinesePeople是Person的子类,兼容
System.out.println(persons.getClass().isInstance(chinesePeoples));//true
//chinesePeople2 是ChinesePeople对象数组,与chinesePeoples兼容
System.out.println(chinesePeoples.getClass().isInstance(chinesePeople2));//true


//Object是所有类的超类
System.out.println(Object.class.isInstance(chinesePeople)); //true

//------------instanceof-------------------
System.out.println(chinesePeoples instanceof Person[]);
这个isInstance 与 instanceof 用法是类似的,但是isInstance表示右边的入参可以与左边的类对象兼容,跟人类从左往右的习惯是反的,这个方法是以前Jdk1.1老版本的方法,后期建议使用instanceof

isAssignableFrom

/**
 * 确定此Class对象表示的类或接口是否与指定的Class参数表示的类或接口相同,或者是其超类或超接口。如果是,则返回true ;否则返回false 。如果此Class对象表示原始类型,则如果指定的Class参数正是此Class对象,则此方法返回true ;否则返回false 。
 * 具体来说,该方法测试指定的Class参数表示的类型是否可以通过恒等转换或扩展引用转换转换为该Class对象表示的类型。有关详细信息,请参阅Java 语言规范的第 5.1.1 和 5.1.4 节。
 * 参形:
 * cls – 要检查的Class对象
 * 返回值:
 * boolean值,指示是否可以将cls类型的对象分配给此类的对象
 * 抛出:
 * NullPointerException – 如果指定的 Class 参数为 null。
 * 自:
 * 1.1
 */
@Test
public void isAssignableFrom() throws ClassNotFoundException {

    Class chinesePeople = Class.forName("lang.ChinesePeople");
    //同类
    assert chinesePeople.isAssignableFrom(ChinesePeople.class);
    //Person 是ChinesePeople 的超类
    assert Person.class.isAssignableFrom(chinesePeople);
    // ChinesePeople 不是Person的超类
    assert !chinesePeople.isAssignableFrom(Person.class);
}
这个也是表示类对象时候相同,或者兼容(超类或接口),但该方法表示左边的对象是否右边参数的超类、接口或同对象,是顺序的,符合人的思维习惯

isInterface

/**
 * 确定指定的类对象是否表示接口类型
 */
@Test
public void isInterface() {

    assert Animal.class.isInterface();
    assert !ChinesePeople.class.isInterface();
    //注解也是接口
    assert Name.class.isInterface();
}

isArray

/**
 * 确定指定的类对象是否表示数组类型
 */
    @Test
    public void isArray() {

        ChinesePeople[] chinesePeopleAry = new ChinesePeople[10];
        //确定此类对象是否表示数组类
        assert chinesePeopleAry.getClass().isArray();
    }

isPrimitive

/**
 * 确定指定的类对象是否表示原型类型
 * 
 * 只有一下几种才返回真
 Boolean.TYPE , 
 Character.TYPE , 
 Byte.TYPE , 
 Short.TYPE , 
 Integer.TYPE , 
 Long.TYPE , 
 Float.TYPE ,
 Double.TYPE , 
 Void.TYPE
 */
    @Test
    public void isPrimitive() {
        System.out.println(Boolean.TYPE.isPrimitive()); //true
        System.out.println(Person.class.isPrimitive()); //false

        Integer[] integers = new Integer[0];
        System.out.println(integers.getClass().isPrimitive()); //false
        Integer a = 1;
        System.out.println(a.getClass().isPrimitive());//false
    }
这个方法也是JDK1.1时代的,感觉很鸡肋,没什么用

isAnnotation

确定此类对象是否表示注解类型

        System.out.println(Test.class.isAnnotation()); //true
        System.out.println(Test.class.isInterface()); //true

isSynthetic

确定此类是否表示合成类

System.out.println(ChinesePeople.class.isSynthetic()); //false

扩展:什么是合成类

官方说明:
According to the JVM Spec: "A class member that does not appear in the source code must be marked using a Synthetic attribute." Also, "The Synthetic attribute was introduced in JDK release 1.1 to support nested classes and interfaces."
根据JVM规范:“源代码中未出现的类成员必须使用合成属性进行标记。”此外,“合成属性在JDK 1.1版中引入,以支持嵌套类和接口。”

就是说Synthetic是JVM自动生成的,私有内部类编译后会生成一个"mainClass$1.class"的类,该类带有synthetic class,表示合成类。

为了在该文,不引入太多内容,我另外起了一篇文章。
synthetic 的详细介绍见:【读码JDK】Java synthetic的介绍

getName

/**
 * 返回此类对象表示的实体名称(类,接口,数组类,基本类型或void)
 * 如果此类对象表示不是数组类型的引用类型,则返回该类的二进制名称,如The Java™ Language Specification所指定。
 * <p>
 * 如果此类对象表示基本类型或void,则返回的名称是String等于与基本类型或void对应的Java语言关键字。
 * <p>
 * 如果此类对象表示一个数组类,则该名称的内部形式由元素类型的名称组成,前面是一个或多个表示数组嵌套深度的“ [ ”字符。
 */
       System.out.println(ChinesePeople.class.getName());
       System.out.println(Animal.class.getName());
       System.out.println(String.class.getName());
       System.out.println(byte.class.getName());
       System.out.println((new Object[3]).getClass().getName());
       System.out.println((new Object[1][2]).getClass().getName());
       /**
        * lang.ChinesePeople
        * lang.Animal
        * java.lang.String
        * byte
        * [Ljava.lang.Object;
        * [[Ljava.lang.Object;
        */

getClassLoader

/**
 * 返回该类的类加载器。 某些实现可能使用null来表示引导类加载器。 如果此类由引导类加载器加载,则此方法将在此类实现中返回null。
 * 如果此对象表示基本类型或void,则返回null。
 */
        System.out.println(ChinesePeople.class.getClassLoader());
        System.out.println(String.class.getClassLoader());

        /**
         * jdk.internal.loader.ClassLoaders$AppClassLoader@47f37ef1
         * null
         */

getModule

首先ChinesePeople是定义在lang包中,并且定义了一个model类,暴露出lang包

module java.test {
    requires java.desktop;
    exports io;
    exports lang;
}

看看官方注释:

/**
 * 返回此类或接口所属的模块。
 * 如果此类表示数组类型,则此方法返回元素类型的模块 。
 * 如果此类表示基本类型或void,则返回java.base模块的模块对象。
 * 如果此类位于未命名的模块中,则返回 模块的类加载器的unnamed 模块 。
 */
        System.out.println(List.class.getModule());
        System.out.println(ChinesePeople.class.getModule());
        System.out.println(new Integer[0].getClass().getModule());
        System.out.println(String.class.getModule());

        /**
         * module java.base
         * module java.test
         * module java.base
         * module java.base
         */

getTypeParameters

/**
 * 返回泛型的变量名称。
 * 如果基础泛型声明未声明类型变量,则返回长度为0的数组。
 */
        TypeVariable[] typeVariables = List.class.getTypeParameters();
        System.out.println(Arrays.toString(typeVariables));
        TypeVariable[] typeVariables1 = Animal.class.getTypeParameters();
        System.out.println(typeVariables1.length);
        TypeVariable[] typeVariables2 = TreeMap.class.getTypeParameters();
        System.out.println(Arrays.toString(typeVariables2));

        /**
         * [E]
         * 0
         * [K, V]
         */

返回结果即为类的泛型名称

getSuperclass

/**
 * 返回表示此类表示的实体的直接超类(类,接口,基本类型或void)的类 。
 * 如果此类表示Object类,接口,基本类型或void,则返回null。
 * 如果此对象表示数组类,则返回表示Object类的类对象。
 */
        //ChinesePeople 继承Person,返回class lang.Person
       System.out.println(ChinesePeople.class.getSuperclass());
       //Person实现了Animal,Person的超类是class java.lang.Object
       System.out.println(Person.class.getSuperclass());
       //Bird实现了Animal,同理,所以该方法说的超类是指继承的父类
       System.out.println(Bird.class.getSuperclass());
       //Object、接口、原始类型、void返回null
       System.out.println(Animal.class.getSuperclass());
       System.out.println(Object.class.getSuperclass());
       System.out.println(byte.class.getSuperclass());
       System.out.println(void.class.getSuperclass());
       //数组类型返回Object
       System.out.println(new Object[0].getClass().getSuperclass());
       //枚举的超类
       System.out.println(Status.class.getSuperclass());

       /**
        * class lang.Person
        * class java.lang.Object
        * class java.lang.Object
        * null
        * null
        * null
        * null
        * class java.lang.Object
        * class java.lang.Enum
        */

getGenericSuperclass

/**
 * 返回Type表示此所表示的实体(类,接口,基本类型或void)的直接超类类 。
 * 如果超类是参数化类型,则返回的Type对象必须准确反映源代码中使用的实际类型参数。
 * 如果之前尚未创建超类,则创建表示超类的参数化类型。
 * 有关参数化类型的创建过程的语义,请参阅ParameterizedType的声明。
 * 如果此类表示Object类,接口,基本类型或void,则返回null。
 * 如果此对象表示数组类,则返回表示Object类的类对象。
 */
        Type type = ChinesePeople.class.getGenericSuperclass();
        System.out.println(type.getTypeName());
        Type type1 = Person.class.getGenericSuperclass();
        System.out.println(type1.getTypeName());
        //返回null
        Type type2 = Object.class.getGenericSuperclass();
        System.out.println(type2);
        Type type3 = Animal.class.getGenericSuperclass();
        System.out.println(type3);

        /**
         * lang.Person
         * java.lang.Object
         * null
         * null
         */

跟getSuperclass用法很类似,只不过getSuperclass返回的是Class, getGenericSuperclass返回的是Type,而Class类实现了Type接口

getPackageName

/**
 * 返回完全限定的包名称。
 * 如果此类是顶级类,则此方法返回该类所属的包的完全限定名称,如果该类位于未命名的包中,则返回空字符串。
 * 如果此类是成员类,则此方法等效于在封闭类上调用getPackageName() 。
 * 如果此类是本地类或匿名类,则此方法等效于在封闭方法或封闭构造函数的声明类上调用getPackageName() 。
 * 如果此类表示数组类型,则此方法返回元素类型的包名称。如果此类表示原始类型或 void,则返回包名称“ java.lang ”。
 * 返回值:
 * 完全限定的包名
 * 自:9
 */
        System.out.println(ChinesePeople.class.getPackageName());
        System.out.println(ChinesePeople.ZjPeople.class.getPackageName());
        System.out.println(Object.class.getPackageName());
        System.out.println(new Object[0].getClass().getPackageName());

        Integer[] integers = new Integer[0];
        System.out.println(integers.getClass().getPackageName());
        Dog[] dogs = new Dog[0];
        System.out.println(dogs.getClass().getPackageName());

        /**
         * lang
         * lang
         * java.lang
         * java.lang
         * java.lang
         * lang.test
         */

getInterfaces

/**
 * 返回此类的接口
 * 如果此对象表示类,则返回值是一个数组,其中包含表示该类直接实现的所有接口的对象。
 * 数组中接口对象的顺序对应于此对象所表示的类的声明的implements子句中的接口名称的顺序。 例如,鉴于声明:
 * class Shimmer implements FloorWax, DessertTopping { ... }
 * 假设的值s是实例Shimmer ; 表达式的值:
 * s.getClass().getInterfaces()[0]
 * 是类对象,表示接口FloorWax ; 和价值:
 * s.getClass().getInterfaces()[1]
 * 是类对象,表示接口DessertTopping 。
 * 如果此对象表示接口,则该数组包含表示由接口直接扩展的所有接口的对象。 数组中接口对象的顺序对应于此对象表示的接口声明的extends子句中接口名称的顺序。
 * 如果此对象表示不实现接口的类或接口,则该方法返回长度为0的数组。
 * 如果此对象表示基本类型或void,则该方法返回长度为0的数组。
 * 如果此类对象表示阵列类型, Cloneable顺序返回接口Cloneable和java.io.Serializable 。
 */
 
        //ChinesePeople继承的Person,没有实现接口
        Class[] interfaces = ChinesePeople.class.getInterfaces();
        System.out.println(Arrays.toString(interfaces)); //0
        //Person实现了接口Animal
        Class[] interfaces2 = Person.class.getInterfaces();
        System.out.println(Arrays.toString(interfaces2)); //[interface lang.Animal]
        //Collection接口返回继承的接口
        System.out.println(Arrays.toString(Collection.class.getInterfaces())); //[interface java.lang.Iterable]
        //ArrayList类按顺序返回其实现的接口,不返回继承的类
        //[interface java.util.List, interface java.util.RandomAccess, interface java.lang.Cloneable, interface java.io.Serializable]
        System.out.println(Arrays.toString(ArrayList.class.getInterfaces()));

getGenericInterfaces

/**
 * 同getInterfaces,只是返回结果为Type类型
 * 如接口是泛型,则返回带泛型名的接口
 */
       System.out.println(Arrays.toString(Collection.class.getGenericInterfaces()));
       //[java.lang.Iterable<E>]

getComponentType

/**
 * 返回表示数组的组件类型的类 。
 * <p>
 * 如果此类不表示数组类,则此方法返回null
 */
        Class componentType = new Object[1].getClass().getComponentType();
        System.out.println(componentType); //class java.lang.Object

        System.out.println(new Integer[0].getClass().getComponentType());//class java.lang.Integer

        //ChinesePeople不是数组
        Class componentType2 = ChinesePeople.class.getComponentType();
        System.out.println(componentType2); //null

getModifiers

/**
 * 返回此类或接口的Java语言修饰符,以整数编码。
 * 修饰符由Java虚拟机的常数为public , protected , private , final , static , abstract和interface ;
 * 它们应该使用Modifier类的方法解码。
 */
        System.out.println(ChinesePeople.class.getModifiers()); //1
        //Modifier提供了各种方法判断获取的整型代表public,还是protected
        System.out.println(Modifier.isPublic(ChinesePeople.class.getModifiers()));

getSigners

/**
 * 获取此类的签名者
 * 此类的签名者,如果没有签名者则为null。 特别是,如果此对象表示基本类型或void,则此方法返回null。
 * //todo 具体怎么给类签名还不清楚,不过这个方法在Java内部经常看到
 */
        assert ChinesePeople.class.getSigners() == null;
        assert int.class.getSigners() == null;
        assert Void.class.getSigners() == null;

getEnclosingMethod

/**
 * 如果此类对象表示方法中的本地或匿名类,则返回方法对象,该对象表示基础类的直接封闭方法。
 * 否则返回null 。
 * 特别是,如果基础类是由类型声明,实例初始化程序或静态初始化程序直接包含的本地或匿名类,则此方法返回null 。
 */
 
    public void getEnclosingMethod() {

        Runnable runnable = () -> {
        };
        Method method = runnable.getClass().getEnclosingMethod();
        System.out.println(method); //null

        Method method1 = getPerson().getClass().getEnclosingMethod();
        System.out.println(method1); //private lang.Person lang.ClassTest.getPerson()
    }

    private Person getPerson() {
        class BjPerson extends Person {

        }

        return new BjPerson();
    }

getEnclosingConstructor

/**
 * 如果此类对象表示构造函数中的本地或匿名类,则返回构造器对象,该对象表示基础类的直接封闭构造函数。
 * 否则返回null 。
 * 特别是,如果基础类是由类型声明,实例初始化程序或静态初始化程序直接包含的本地或匿名类,则此方法返回null 。
 */
        class OuterClass {
            public Object test;

            public OuterClass() {
                class InnerClass {
                }
                test = new InnerClass();
            }
        }
        Constructor constructor = new OuterClass().test.getClass().getEnclosingConstructor();
        assert constructor.getName().equals("lang.ClassTest$1OuterClass");
相关文章
|
7月前
|
安全 Java API
JDK 11中的动态类文件常量:探索Java字节码的灵活性与动态性
在JDK 11中,Java语言引入了一个新的特性,允许在运行时动态地修改类文件常量。这一特性为Java开发者提供了更大的灵活性,使他们能够根据需要在运行时更改类文件中的常量值。本文将深入探讨动态类文件常量的工作原理、优点、限制以及在实际项目中的应用。
156 11
|
7月前
|
安全 Java API
14.JDK底层Unsafe类是个啥东西?
14.JDK底层Unsafe类是个啥东西?
82 0
14.JDK底层Unsafe类是个啥东西?
|
7月前
|
Java
Integer类【JDK源码分析】
Integer类【JDK源码分析】
49 0
|
3月前
|
Java Linux
java基础(3)安装好JDK后使用javac.exe编译java文件、java.exe运行编译好的类
本文介绍了如何在安装JDK后使用`javac.exe`编译Java文件,以及使用`java.exe`运行编译好的类文件。涵盖了JDK的安装、环境变量配置、编写Java程序、使用命令行编译和运行程序的步骤,并提供了解决中文乱码的方法。
72 2
|
Java
Java之JDK8时间相关类的详细解析
Java之JDK8时间相关类的详细解析
80 0
|
3月前
|
Java API 开发者
【Java字节码操控新篇章】JDK 22类文件API预览:解锁Java底层的无限可能!
【9月更文挑战第6天】JDK 22的类文件API为Java开发者们打开了一扇通往Java底层世界的大门。通过这个API,我们可以更加深入地理解Java程序的工作原理,实现更加灵活和强大的功能。虽然目前它还处于预览版阶段,但我们已经可以预见其在未来Java开发中的重要地位。让我们共同期待Java字节码操控新篇章的到来!
|
3月前
|
Java API 开发者
【Java字节码的掌控者】JDK 22类文件API:解锁Java深层次的奥秘,赋能开发者无限可能!
【9月更文挑战第8天】JDK 22类文件API的引入,为Java开发者们打开了一扇通往Java字节码操控新世界的大门。通过这个API,我们可以更加深入地理解Java程序的底层行为,实现更加高效、可靠和创新的Java应用。虽然目前它还处于预览版阶段,但我们已经可以预见其在未来Java开发中的重要地位。让我们共同期待Java字节码操控新篇章的到来,并积极探索类文件API带来的无限可能!
|
4月前
|
存储 Java
【Java集合类面试七】、 JDK7和JDK8中的HashMap有什么区别?
JDK7中的HashMap使用数组加链表解决冲突,而JDK8增加了红黑树结构以优化链表过长时的性能,提高查找效率。
|
4月前
|
Oracle 安全 Java
JDK8到JDK28版本升级的新特性问题之在Java 15及以后的版本中,密封类和密封接口是怎么工作的
JDK8到JDK28版本升级的新特性问题之在Java 15及以后的版本中,密封类和密封接口是怎么工作的
|
4月前
|
Java 编译器 API
JDK版本特性问题之在 JDK 17 中,想声明一个密封类,如何解决
JDK版本特性问题之在 JDK 17 中,想声明一个密封类,如何解决