Class类是什么?

原文链接:https://blog.csdn.net/topdeveloperr/article/details/81066872

Class类

Class类是用来代表java的类的一个类。 就好比:人 是 小吕,小布实例的 类;Class 是 人的类的一个类。

那么为何java里面可以有Class这个类?,那么这个Class类对应的实例对象到底是一个什么样的对象,他和类的关系是什么,和对象的关系是什么?Class类也继承自Object类吗?带着这些问题我们继续往下看。

Java里面最出名得类可能是Object,众所周知,java里所有的类都继承自Object类,java体系里最顶上的类只有Object一个类。Class类肯定也是继承自Object类的。

并且在Object里面还提供了一个native方法getClass()用于返回调用这个方法的对象的运行时的class。英文原文注释如下:

/*
Returns the runtime class of this {@code Object}
*/
public final native Class<?> getClass();

在object里面定义了这个方法,意味着所有的对象都可以调用这个方法来获取自己运行时的类。

那么,为什么需要Class类?事实上Class类在java中是用来表示运行时类型信息的对应对象就是Class类对象。它包含了与类有关的信息。事实上,Class对象就是用来创建类的所有常规对象的。java使用Class对象来执行其RTTI。

每个类都有一个Class对象。换言之,每当编写并且编译了一个新类,就会产生一个Class对象,更恰当的说,是保存在一个同名的.class文件中。

为了生成这个类的对象,运行这个程序的JVM讲使用类加载器子系统。

所有的类都是在对其第一次使用时,动态的加载到JVM中的。当程序创建第一个对象类的静态成员引用时,就会加载这个类。因此java程序在它开始运行之前并非完全被加载,其各个部分是在必须是才加载的。这一点与传统语言不同。也很难在C++当中做到。类加载器首先检查这个类的Class对象是否已经加载。如果尚未加载,默认的类加载器根据类名查找.class文件。

一旦这个类的Class对象被载入内存,它就被用来创建这个类的所有对象。

因此,也说明了java的对象的创建原理。

class Candy{
   static {
       System.out.println("Loading Candy too");
   }

class Gum{
   static {
       System.out.println("Loading Gum");
   }
}

class Cookie{
   static {
       System.out.println("Loading Cookie");
   }
}

public class SweetShop {
   public static void main(String [] args){
       System.out.println("inside main");
       new Candy();
       System.out.println("After creating Candy");
       try {
           Class.forName("Gum");
       } catch (ClassNotFoundException e) {
           System.out.println("Couldn't find Gum");
       }
       System.out.println("After Class.forName(\"Gum\")");
       new Cookie();
       System.out.println("After creating Cookie");
   }
}

执行结果为

inside main
Loading Candy too
After creating Candy
Couldn't find Gum
After Class.forName("Gum")
Loading Cookie
After creating Cookie

这里的每个类Candy,Gum和Cookie,都有一个static子句,该子句在类第一次加载时候执行。这时会有相应的信息打印出来,告诉我们这个类什么时候被加载了。在main方法中,创建对象的代码被置于打印语句之间,帮助我们判断加载的时间点。

上面的代码的执行结果告诉我们,Class对象仅仅在需要的时候才被加载,static初始化是在类加载时进行的。

当虚拟机执行了new这个指令之后,(注意,不仅关键字new会使得虚拟机执行new指令,调用静态方法也会,jvm里面规定了五种情况必须初始化一个对象。)Java虚拟机(JVM)中的类加载器子系统会将对应Class对象加载到JVM中,然后JVM再根据这个类型信息相关的Class对象创建我们需要实例对象或者提供静态变量的引用值。实际上在Java中每个类都有一个Class对象,每当我们编写并且编译一个新创建的类就会产生一个对应Class对象并且这个Class对象会被保存在同名.class文件里(编译后的字节码文件保存的就是Class对象)。需要特别注意的是,手动编写的每个class类,无论创建多少个实例对象,在JVM中都只有一个Class对象,即在内存中每个类有且只有一个相对应的Class对象。因此,java里的class类就是存放一个对象对应的类的信息的类。

这里可以引申一下,为什么synchronized关键字在锁定的是Class的时候会对所有类的实例都生效?

因此总结一下:

手动编写的每一个类被编译后都会产生一个Class对象,其表示的是创建的类的类型信息,而且这个Class对象保存在同名.class的文件中(字节码文件).Class类的对象作用是运行时提供或获得某个对象的类型信息,当我们使用关键字new创建对象的时候再根据这个类型信息相关的Class对象创建我们需要实例对象或者提供静态变量的引用值,这点对于反射技术也很重要。

反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。一直以来反射技术都是Java中的闪亮点,这也是目前大部分框架(如Spring/Mybatis等)得以实现的支柱。在Java中,Class类与java.lang.reflect类库一起对反射技术进行了全力的支持。

三种获得Class对象引用的方式

通过继承自Object类的getClass方法,
Class类的静态方法forName
字面常量的方式”.class”。
其中实例类的getClass方法和Class类的静态方法forName都将会触发类的初始化阶段。字面常量的方式不仅更简单,而且更安全,因为省去了forName方法,效率更高。比较有趣的一点是,使用.class来创建对Class对象的引用时,不会自动初始化Class对象。初始化被延迟到了对静态方法或者非常数静态域进行首次引用时才执行

初始化是类加载的最后一个阶段,也就是说完成这个阶段后类也就加载到内存中(Class对象在加载阶段已被创建),此时可以对类进行各种必要的操作了(如new对象,调用静态成员等),注意在这个阶段,才真正开始执行类中定义的Java程序代码或者字节码。

关于这一部分的内容请移步至:https://blog.csdn.net/javazejian/article/details/70768369

综上来看,Class这个类是用来在一个比较通用的层面上描述一个对象信息的一个类,每一个对象会有一个原生类,同时每个原生类也有一个对应的Class类。每一个Class对象都对应着一个真实的手动编写的类,表示的是创建的类的类型信息,无论创建多少个对象,他们对应的Class对象只会有一个。且Class类只有私有构造函数,意味着它只能被虚拟机自己实例化。Class类的对象的的作用是在运行时,可以根据某个对象去获取这个对象对应的类的类型信息(即类的方法,字段等等)。

Class类提供的方法解析
在看了上面的内容之后,这里我们就不难理解class类为何要提供下面的这些方法了。因为无论我们定义一个怎样的类,他都由字段,方法,构造方法,父类,注解这些东西组成。

获取类的加载器
方法如下:

@CallerSensitive
public ClassLoader getClassLoader() {
    ClassLoader cl = getClassLoader0();
    if (cl == null)
        return null;
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        ClassLoader.checkClassLoaderPermission(cl, Reflection.getCallerClass());
    }
    return cl;
}

左右就是获取当前这个Class对象的类加载器的实例。

Class.forName()获取Class对象
在使用java的反射时,无论是要获取一个类的字段,还是构造方法,无论任何东西,第一件事首先是要获取一个这个类的一个实例。调用的方法便是Class类中的forName() 方法

在Class类中有两个重载的forName方法:

 @CallerSensitive
    public static Class<?> forName(String name, boolean initialize,
                                   ClassLoader loader)
        throws ClassNotFoundException
    {
        Class<?> caller = null;
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            // Reflective call to get caller class is only needed if a security manager
            // is present.  Avoid the overhead of making this call otherwise.
            caller = Reflection.getCallerClass();
            if (sun.misc.VM.isSystemDomainLoader(loader)) {
                ClassLoader ccl = ClassLoader.getClassLoader(caller);
                if (!sun.misc.VM.isSystemDomainLoader(ccl)) {
                    sm.checkPermission(
                        SecurityConstants.GET_CLASSLOADER_PERMISSION);
                }
            }
        }
        return forName0(name, initialize, loader, caller);
    }
    ```
```java
   @CallerSensitive
    public static Class<?> forName(String className)
                throws ClassNotFoundException {
        Class<?> caller = Reflection.getCallerClass();
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }
    ```
   
两个方法最后都是调用一个native方法实现的,该本地方法如下:

private static native Class<?> forName0(String name, boolean initialize,
ClassLoader loader,
Class<?> caller)
throws ClassNotFoundException;
本地方方法的参数当中有一个name参数,必然是类的全限定名,另外还有一个类ClassLoader。可以想到这个本地方法就是使用这个类加载器,然后根据类的全限定名去加载了这个类。然后我们才能获取到了这个类的实例,具体的forName0实现逻辑需要查看对应的实现源码,这里暂时不作深究。

再回到两个重载的forName方法,其中第二个方法在jdk library里面的注释如下

/**
* Returns the {@code Class} object associated with the class or
* interface with the given string name. Invoking this method is
* equivalent to:
*
*


* {@code Class.forName(className, true, currentLoader)}
*

*
* where {@code currentLoader} denotes the defining class loader of
* the current class.
*/
第一个方法的注释如下

/**
* Returns the {@code Class} object associated with the class or
* interface with the given string name, using the given class loader.
* Given the fully qualified name for a class or interface (in the same
* format returned by {@code getName}) this method attempts to
* locate, load, and link the class or interface. The specified class
* loader is used to load the class or interface. If the parameter
* {@code loader} is null, the class is loaded through the bootstrap
* class loader. The class is initialized only if the
* {@code initialize} parameter is {@code true} and if it has
* not been initialized earlier.
*
*/
直接看注释其实已经解释的非常清楚了,这两个类起到的作用,不作别的说明了

获取字段
通过字段名字获取一个字段,传入字段名字,获取字段

@CallerSensitive
public Field getField(String name)
throws NoSuchFieldException, SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
Field field = getField0(name);
if (field == null) {
throw new NoSuchFieldException(name);
}
return field;
}
获取所有共有字段:

@CallerSensitive
public Field[] getFields() throws SecurityException {
    checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
    return copyFields(privateGetPublicFields(null));
}

获取所有字段,包括公共的,私有的,受保护的

@CallerSensitive
public Field[] getDeclaredFields() throws SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
return copyFields(privateGetDeclaredFields(false));
}
获取普通方法
通过传入方法名字获得一个方法,注意,因为方法可能存在重载的情况,因此除了传入方法名之外,也需要传入方法参数,以此来唯一确定一个方法

@CallerSensitive
public Method getMethod(String name, Class<?>… parameterTypes)
throws NoSuchMethodException, SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
Method method = getMethod0(name, parameterTypes, true);
if (method == null) {
throw new NoSuchMethodException(getName() + “.” + name + argumentTypesToString(parameterTypes));
}
return method;
}
获取所有的共方法

@CallerSensitive
public Method[] getMethods() throws SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
return copyMethods(privateGetPublicMethods());
}
获得所有方法,包括公共的和私有的以及受保护的

@CallerSensitive
public Method[] getDeclaredMethods() throws SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
return copyMethods(privateGetDeclaredMethods(false));
}
构造方法
通过参数获取一个构造方法

@CallerSensitive
public Constructor<T> getConstructor(Class<?>... parameterTypes)
    throws NoSuchMethodException, SecurityException {
    checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
    return getConstructor0(parameterTypes, Member.PUBLIC);
}

获取所有的构造方法

@CallerSensitive
public Constructor<?>[] getConstructors() throws SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
return copyConstructors(privateGetDeclaredConstructors(true));
}
获取父类
public native Class<? super T> getSuperclass();
获取一个枚举类的所有枚举类型
public T[] getEnumConstants() {
T[] values = getEnumConstantsShared();
return (values != null) ? values.clone() : null;
}
获取一个类的注解
获取所有注解

public Annotation[] getAnnotations() {
return AnnotationParser.toArray(annotationData().annotations);
}
获取一个注解

/**
 * @throws NullPointerException {@inheritDoc}
 * @since 1.5
 */
@SuppressWarnings("unchecked")
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
    Objects.requireNonNull(annotationClass);

    return (A) annotationData().annotations.get(annotationClass);
}

————————————————
原文链接:https://blog.csdn.net/topdeveloperr/article/details/81066872

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值