0
点赞
收藏
分享

微信扫一扫

【Java】Java Class对象的理解

Raow1 2022-01-16 阅读 61

入门java不久,看代码经常会看到类名加.class,Class这样的用法,甚是不惑。查找资料学习,总结一下自己的理解。首先,要明确,在java的世界中,一切皆是对象,java中的对象可以分为两种对象:Class对象和实例对象。那么,我们围绕以下几方面来解读Class对象。
参考文献:https://blog.csdn.net/mcryeasy/article/details/52344729

Class对象是什么

带着“Class对象和实例对象是什么区别?”的问题,来看看怎么理解Class对象:
1、从对象的作用看,Class对象表示每个类型运行时的类型信息,在JVM中,一个类只对应一个Class对象。
2、Class对象是java.lang.Class类的对象,和其他对象一样,我们可以获取并操作它的引用。
3、每当JVM加载一个类就产生对应的Class对象,保存在堆区,类型和它的Class对象时一一对应的关系。一旦类被加载了到了内存中,那么不论通过哪种方式获得该类的Class对象,它们返回的都是指向同一个java堆地址上的Class对象引用。JVM不会创建两个相同类型的Class对象

废了不少力气,画了下面这张图作为总结:
在这里插入图片描述

Class对象的构造

需要明白两点:什么时候被构造的?是如何被构造的?
Class类没有公共的构造方法,Class对象是在类加载的时候由Java虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。

Class对象在类加载中的作用

一个类被加载到内存并供我们使用需要经历如下三个阶段:

所有的类都是在对其第一次使用时,动态加载到JVM中的(懒加载)。当程序创建第一个对类的静态成员的引用时,就会加载这个类。使用new创建类对象的时候也会被当作对类的静态成员的引用。因此java程序程序在它开始运行之前并非被完全加载,其各个类都是在必需时才加载的。
在类加载阶段,类加载器首先检查这个类的Class对象是否已经被加载。如果尚未加载,默认的类加载器就会根据类的全限定名查找.class文件。在这个类的字节码被加载时,它们会接受验证,以确保其没有被破坏,并且不包含不良java代码。一旦某个类的Class对象被载入内存,就可以它来创建这个类的所有对象。

获取Class对象的方法

有三种方式

  1. Class.forName(“类的全限定名”)
  2. 实例对象.getClass()
  3. 类名.class (类字面常量)

Class.forName(“类的全限定名”)

Class.forName方法是Class类的一个静态成员。forName在执行的过程中发现如果类还没有被加载,那么JVM就会调用类加载器去加载类,并返回加载后的Class对象。如果Class .forName找不到要加载的类,它会抛出ClassNotFoundException异常。

package com.feng;

class Book {
    static {
        System.out.println("Loading Book");
    }
}
public class Test {
    public static void main(String[] args){
        System.out.println("inside main");
        try {
            Class book=Class.forName("com.feng.Book");
        } catch (ClassNotFoundException e) {
            System.out.println("Couldn't find Book");
        }
        System.out.println("finish main");
    }
}
/* Output:
    inside main
    Loading Book
    finish main
 */

实例对象.getClass()

如果已经有了该类型的对象,那么就可以通过调用getClass()方法来获取Class对象引用了,getClass方法属于根类Object的一部分,它返回的是表示该对象的实际类型的Class对象引用。

类名.class

类名.class 这种方式就是类字面常量,例如:Test.class,这样操作更简单,而且更安全,因为它在编译时就会受到检查,类字面量不仅可以应用于普通的类,也可以应用于接口、数组及基本数据类型。

  • 基本数据类型的Class对象和包装类的Class对象是不一样的,但是在包装类中有个一个字段TYPE,TYPE字段是一个引用,指向对应的基本数据类型的Class对象,如下所示,左右两边相互等价:
    在这里插入图片描述
  • 用.class来创建对Class对象的引用时,不会自动地初始化该Class对象。类对象的初始化阶段被延迟到了对静态方法或者非常数静态域首次引用时才执行。

Class类的方法

  • forName():获取Class对象的一个引用,如果引用的类还没有被JVM加载,就立刻加载并初始化。
  • getName():取全限定的类名(包括包名),即类的完整名字。
  • getSimpleName():获取类名(不包括包名)
  • getCanonicalName():获取全限定的类名(包括包名),大多数情况下和getName一样,但是在内部类、数组等类型的表示形式就不同了。
  • isInterface():判断Class对象表示的是否是一个接口
  • getInterfaces():返回Class对象数组,对应Class对象所引用的类所实现的所有接口。
  • getSupercalss() :返回Class对象,表示Class对象所引用的类所继承的直接基类。
  • newInstance():返回一个Oject对象,是实现“虚拟构造器”的一种途径。使用该方法创建的类,必须带有无参的构造器。
  • getFields():获得某个类的所有的公共(public)的字段,包括继承自父类的所有公共字段。 类似的还有getMethods和getConstructors。
  • getDeclaredFields():获得某个类的自己声明的字段,即包括public、private和proteced,默认但是不包括父类声明的任何字段。类似的还有getDeclaredMethods和getDeclaredConstructors。

知道上面的方法,我们知道了获取到Class对象引用后可以用来做什么。

泛型Class引用

Class引用表示的就是它所指向的对象的类型,而该对象便是Class类的一个对象。在JavaSE5中,允许对Class引用所指向的Class对象的表示的类型进行限定,也就是说可以对Class对象使用泛型语法。通过泛型语法,可以让编译器强制指向额外的类型检查。
可以参考:https://blog.csdn.net/qq_35029061/article/details/100550584

1、简单举例

Class<Integer> c1 = int.class;
c1=Integer.class;
//c1=Double.class; 编译报错

int.class和Integer.class指向的Class对象对应的类是基本类型和包装类的关系,int可以自动包装为Integer,所以编译器可以编译通过。

2、使用通配符"?",可以表示任何类型

Class<?> c1 = int.class;
c1= float.class;

3、通配符?可以与extend结合

Class<? extends Number> c1 = Integer.class;
c1 = Number.class;
c1 = Double.class;
// c1=String.class; 报错,不属于Number类和其子类

4、通配符?与super关键字相结合,表示被限定为某种类型,或该类型的任何父类型

Class<? super Integer> c1 = Integer.class;
c1 = Number.class;
c1 = Object.class;
c1 = Integer.class.getSuperclass();

5、返回值泛型,用<T> T表示
参考文献:
https://blog.csdn.net/wangguidong520/article/details/87283373
https://www.cnblogs.com/jpfss/p/9929108.html

例子:

    /**
     * 这个<T> T 可以传入任何类型的List
     * 参数T
     *     第一个 表示是泛型
     *     第二个 表示返回的是T类型的数据
     *     第三个 限制参数类型为T
     * @param data
     * @return
     */
    private <T> T getListFisrt(List<T> data) {
        if (data == null || data.size() == 0) {
            return null;
        }
        return data.get(0);
    }

<T> T表示返回值是泛型的,传递什么类型,就返回什么类型的数据。
而单独的T就是表示限制你传递的参数类型为T类型,也就是对象实例化的时候T表示什么类型就已经定下来了。

泛型中,其他一些约定的符号,只是俗成的约定,其实用其他字母都是一样的

 E - Element (在集合中使用,因为集合中存放的是元素)
 T - TypeJava 类)
 K - Key(键)
 V - Value(值)
 N - Number(数值类型)
举报

相关推荐

0 条评论