该文章根据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");