理解java.lang.Class类

Java Class类理解:

首先,Class是一个java类,跟Java API中定义的诸如Thread、Integer类、我们自己定义的类是一样,也继承了Object(Class是Object的直接子类)。总之,必须明确一点,它其实只是个类,只不过名字比较特殊。更进一步说,Class是一个java中的泛型类型。

对于我们自己定义的类,我们用类来抽象现实中的某些事物,比如我们定义一个名称为Car的类来抽象现实生活中的车,然后可以实例化这个类,用这些实例来表示我的车、你的车、黄的车、红的车等等。

好了,现在回到Class 类上来,这个类它抽象什么了?它的实例又表示什么呢?

在一个运行的程序中,会有许多类和接口存在。我们就用Class这个来来表示对这些类和接口的抽象,而Class类的每个实例则代表运行中的一个类。例如,运行的程序有A、B、C三个类,那么Class类就是对A、B、C三个类的抽象。所谓抽象,就是提取这些类的一些共同特征,比如说这些类都有类名,都有对应的hashcode,可以判断类型属于class、interface、enum还是annotation。这些可以封装成Class类的域,另外可以定义一些方法,比如获取某个方法、获取类型名等等。这样就封装了一个表示类型(type)的类。


需要注意的是,这个特殊的Class类没有公开的构造函数,那怎么获取Class类的实例呢?有几个途径。

1.当Java虚拟机载入一个类的时候,它就会自动创建一个Class类的实例来表示这个类。例如,虚拟机载入Car这个来的时候,它就会创建一个Class类的实例。然后可以通过以下方法获得这个Class对象:

java.lang.Class classObj = ClassName.class;

2. 可以通过调用类加载器(ClassLoader)的defineClass()方法来得到一个实例。这个方法接受一个byte数组,载入这个byte数组否成的class类,同时实例化一个Class对象。

3. ClassName.class( ) ClassName.getClass( )

现在来分析一下Class类的源码(java.lang.Class):

public final

class Class<T>implementsjava.io.Serializable,

java.lang.reflect.GenericDeclaration,

java.lang.reflect.Type,

java.lang.reflect.AnnotatedElement {…

ClassClass<T>,前一个Class表示这是一个类的声明,第二个Class是类的名称,<T>表示这是一个泛型类,带有参数T.同时,Class类实现了许多接口。

紧接着定义了几个静态变量:

private static final int ANNOTATION= 0x00002000;

private static final int ENUM = 0x00004000;

private static final int SYNTHETIC = 0x00001000;

接着定义一个本地方法registerNatives(),并在静态块中调用:

private static native void registerNatives();

static {

registerNatives();

}

私有的构造函数:

private Class() {}

访问修饰符是private,程序员是无法直接调用这个构造函数,只能通过JVM来调用它,构造一个Class实例。

public String toString() {

return (isInterface() ?"interface " : (isPrimitive() ? "" : "class"))

+ getName();

}

这是Class对象实例的字符串表示方法,应该不陌生。那么,它返回什么东西呢?

>如果这个Class对象实例所表示的是一个Java类,则返回class full_classname.

例如java.lang.Math.java这个类,它所对应的Class实例的toString方法返回的就是class java.lang.Math

>如果是接口,将class改成interface。还有一种特殊情况,如果Class实例表示的是void类型,则发挥void。如果是基本类型,一样的返回基本类型的名称。

静态方法forName:

public static Class<?> forName(String className)

throws ClassNotFoundException{

returnforName0(className,true, ClassLoader.getCallerClassLoader());

}

根据给定的类名参数className,查找与className相对应的Class实例,然后加载、连接该实例对象,之后返回这个Class实例。其中例如以下代码段将输出:

class java.lang.Thread

public class ClassTest {

/**

* @param args

*/

public static void main(String[] args) {

try {

System.out.println( Class.forName("java.lang.Thread") );

} catch (ClassNotFoundException e) {

e.printStackTrace();

System.out.println("No ClassNamed java.lang.Thread");

}

}

}

forName方法重载:

public static Class<?> forName(String name, boolean initialize,

ClassLoaderloader)

throws ClassNotFoundException

{

if (loader ==null) {

SecurityManager sm = System.getSecurityManager();

if (sm !=null) {

ClassLoader ccl = ClassLoader.getCallerClassLoader();

if (ccl !=null) {

sm.checkPermission(

SecurityConstants.GET_CLASSLOADER_PERMISSION);

}

}

}

returnforName0(name, initialize,loader);

}

注意到这个forName重载方法中多了两个方法参数,其中initialize这个boolean类型指定是否要初始化对应的Class实例,loader指定加载Class实例的加载器。留意这个方法可能抛出的异常还是比较多的,比如连接失败、找不到对应的类等等。

forName0本地方法:

private static native Class<?> forName0(String name,boolean initialize,

ClassLoaderloader)

throws ClassNotFoundException;

这是一个本地方法,在前面的静态方法forName的两个版本中都调用了这个本地方法。

newInstance()方法:

public T newInstance()// T是个泛型参数,是Class实例所表示的Java类

throws InstantiationException,IllegalAccessException

{

if (System.getSecurityManager()!=null) {

checkMemberAccess(Member.PUBLIC, ClassLoader.getCallerClassLoader());

}

return newInstance0(); //这是一个本地方法,也在Class类中定义

}

注意这是一个实例方法,必须由Class类的实例对象调用。例如,有一个代表java.lang.Thread类的Class实例对象objec1,也就是说,泛型参数T此时就是Thread,object1这个实例代表Thread这个类。好了,现在调用object1的newInstance方法,即object1.newInstance(),此时这个调用将返回一个Thread类的对象。简单验证:

public class ForName {

/**

* @paramargs

* @throwsIllegalAccessException

* @throwsInstantiationException

*/

public static void main(String[] args) throws InstantiationException, IllegalAccessException {

Class<?> c = null ;

try {

c = Class.forName("java.lang.Thread");

} catch (ClassNotFoundException e) {

e.printStackTrace();

}

Thread thread = (Thread) c.newInstance(); //类型转化一下

System.out.println(thread.getId());

}

}

在我机子中,上述代码输出 8 。即c.newInstance产生一个ID为8的新线程。

getClassLoader:

public ClassLoader getClassLoader() {

ClassLoader cl = getClassLoader0();

if (cl ==null)

return null; // Bootstrap

SecurityManager sm = System.getSecurityManager();

if (sm !=null) {

ClassLoader ccl = ClassLoader.getCallerClassLoader();

if (ccl !=null && ccl != cl&& !cl.isAncestor(ccl)) {

sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);

}

}

return cl;

}

//Package-private to allow ClassLoader access

native ClassLoadergetClassLoader0(); // 本地方法

这个方法返回该Class对象代表的类的类加载器。如果类的加载器是Bootstrap,则返回null。下面的代码输出:The ClassLoader of Thread Class is Bootstrap

Class<?> classObj= Thread.class;

ClassLoader loader = classObj.getClassLoader();

if (loader ==null) {

System.out.println("TheClassLoader of Thread Class is Bootstrap");

} else {

System.out.println(loader);

}

获取父类方法:getSuperclass()

public native Class<? super T> getSuperclass();

这是一个本地方法,这里的逻辑有点饶,方法返回的是这个Class对象所代表的Java类的父类对应的的Class 对象。

例如: Thread.class.getSuperclass()将返回一个代表Thread类的Class对象,Thread.class.getSuperclass().toString()则输出这个Class对象的字符串表示:classjava.lang.Object。其实这里的关系无非就是说Thread的超类是Object。只是饶了Class对象这个弯子,至于这么绕有什么好处,还没有深刻体会。

(持续更新中)

### 回答1: 这个错误是因为在Java中,一个不能被强制转换为ParameterizedType型。通常是因为在使用反射时,尝试将一个Class对象转换为ParameterizedType对象,但是这个Class对象实际上不是一个ParameterizedType型。要解决这个问题,需要检查代码中的反射使用,确保正确地使用了ParameterizedType型。 ### 回答2: java.lang.Class无法强制转换为java.lang.reflect.ParameterizedType。 在Java中,java.lang.Class表示运行时的信息和属性,而java.lang.reflect.ParameterizedType接口表示带有参数的型(泛型类型)。 这个错误是因为在尝试将Class对象强制转换为ParameterizedType对象时,型转换发生了错误。 要解决这个问题,我们需要理解java.lang.reflect.ParameterizedType的使用情况。 ParameterizedType接口是Type接口的子接口,可以用于获取带有参数的型信息,例如泛型类或泛型接口的型参数信息。 如果我们想要获得一个的泛型参数型信息,我们可以使用java.lang.Class中的getGenericSuperclass()方法或getGenericInterfaces()方法。 但是要注意,这些方法返回的型是java.lang.reflect.Type,而不是java.lang.reflect.ParameterizedType。 如果我们想要将Type对象转换为ParameterizedType对象,我们需要进行型检查和型转换。 示例代码: import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; public class MyClass<T> { public MyClass() { Type type = getClass().getGenericSuperclass(); if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) type; // 在这里可以使用ParameterizedType对象来获取泛型参数型信息 } } } 总结来说,java.lang.Class不能直接强制转换为java.lang.reflect.ParameterizedType,我们需要根据具体的使用情况进行型检查和转换才能正确地获取泛型参数型信息。 ### 回答3: java.lang.Class不能被转换为java.lang.reflect.ParameterizedType的原因是两者表示的是不同的型信息。java.lang.Class用于表示的信息,而java.lang.reflect.ParameterizedType用于表示泛型类型的信息。 当使用强制型转换将一个对象转换为java.lang.reflect.ParameterizedType时,如果该对象实际上是java.lang.Class型的对象,就会抛出ClassCastException。这是因为java.lang.Classjava.lang.reflect.ParameterizedType是不兼容的型。 如果需要获取一个中的泛型类型信息,应该使用java.lang.Class对象的getGenericSuperclass()方法来获取其父泛型类型信息,再通过强制型转换获取到java.lang.reflect.ParameterizedType对象。 例如: ``` class MyGenericClass<T> { } class MyClass extends MyGenericClass<String> { } public static void main(String[] args) { MyClass myClass = new MyClass(); ParameterizedType parameterizedType = (ParameterizedType) myClass.getClass().getGenericSuperclass(); Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); Type actualTypeArgument = actualTypeArguments[0]; String genericType = actualTypeArgument.getTypeName(); System.out.println(genericType); } ``` 如果在强制型转换时抛出了java.lang.ClassCastException,可以检查代码中的型转换部分,并确保正确使用泛型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值