Java 深入学习(24) —— Class 对象

1 简介


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

所有的类都是在对其第一次使用时,动态加载到 JVM 中的。

当程序创建第一个对类的静态成员的引用时,就会加载这个类。

使用 new 操作符创建类的新对象也会被当做对类的静态成员的引用。

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


2 Class 类的基本应用

package com.test.class_obj;

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

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");
        System.out.println("------------------");
        try {
            Class.forName("Gum");
        } catch (ClassNotFoundException e) {
            System.out.println("Couldn't find Gum");
        }
        System.out.println("------------------");

        System.out.println("After Class.forName(\"Gum\")");
        new Cookie();
        System.out.println("After creating Cookie");
    }
}

这里写图片描述

Class 对象仅在需要的时候才被加载,static 初始化使在类加载时进行的。

Class.forName() 返回的是一个 class 对象的引用,作用的要求 JVM 查找并加载指定的类

这里写图片描述


package com.test.class_obj;

interface HasBatteries {
}

interface Waterproof {
}

interface Shoots {
}

class Toy {
    Toy() {
    }

    Toy(int i) {
    }
}

class FancyToy extends Toy
        implements HasBatteries, Waterproof, Shoots {
    FancyToy() {
        super(1);
    }
}

public class ToyTest {
    static void printInfo(Class cc) {
        System.out.println("Class name: " + cc.getName() +
                " is interface? [" + cc.isInterface() + "]");
        System.out.println("Simple name: " + cc.getSimpleName());
        System.out.println("Canonical name : " + cc.getCanonicalName());
        System.out.println("-----------------------------------");
    }

    public static void main(String[] args) {
        Class c = null;
        try {
            c = Class.forName("com.test.class_obj.FancyToy");
        } catch (ClassNotFoundException e) {
            System.out.println("Can't find FancyToy");
            System.exit(1);
        }
        printInfo(c);
        for (Class face : c.getInterfaces())
            printInfo(face);
        Class up = c.getSuperclass();
        Object obj = null;
        try {
            // Requires default constructor:
            obj = up.newInstance();
        } catch (InstantiationException e) {
            System.out.println("Cannot instantiate");
            System.exit(1);
        } catch (IllegalAccessException e) {
            System.out.println("Cannot access");
            System.exit(1);
        }
        printInfo(obj.getClass());
    }
} 
Class name: com.test.class_obj.FancyToy is interface? [false]
Simple name: FancyToy
Canonical name : com.test.class_obj.FancyToy
-----------------------------------
Class name: com.test.class_obj.HasBatteries is interface? [true]
Simple name: HasBatteries
Canonical name : com.test.class_obj.HasBatteries
-----------------------------------
Class name: com.test.class_obj.Waterproof is interface? [true]
Simple name: Waterproof
Canonical name : com.test.class_obj.Waterproof
-----------------------------------
Class name: com.test.class_obj.Shoots is interface? [true]
Simple name: Shoots
Canonical name : com.test.class_obj.Shoots
-----------------------------------
Class name: com.test.class_obj.Toy is interface? [false]
Simple name: Toy
Canonical name : com.test.class_obj.Toy
-----------------------------------

Process finished with exit code 0

Class.forName() 查找并加载指定的 class,返回 class 对象的引用

注意:在传递给 forName() 的字符串中,必须使用全限定名(包名)

  • classObj.getName() 产生全限定的类名
  • classObj.getSimpleName() 产生不包含包名的类名
  • classObj.getCanonicalName() 产生全限定的类名
  • classObj.isInterface() 判断这个 class 对象是否是接口
  • classObj.getSuperclass() 查询其直接基类
  • classObj.newInstance() 是实现“虚拟构造器”的一种途径,允许声明:“我不知道你的确切类型,但是无路如何你要正确创建你自己的实例。”

注意:使用 newInstance() 来创建的类,必须带有默认的构造器。否则会得到一个异常


2 类字面常量


使用类字面常量生成对 Class 对象的引用

FancyToy.class

在编译时就会受到检查(不需要置于 try 块中)


类字面常量不仅可以用于普通的类,也可以应用于接口、数组、以及基本数据类型。

注意:当使用 .class 来创建对 Class 对象的引用时,不会自动地初始化该 Class 对象

package com.test.class_obj;

import java.util.*;

class Initable {
    static final int staticFinal = 47;
    static final int staticFinal2 =
            ClassInitialization.rand.nextInt(1000);
    static {
        System.out.println("Initializing Initable");
    }
}

class Initable2 {
    static int staticNonFinal = 147;
    static {
        System.out.println("Initializing Initable2");
    }
}

class Initable3 {
    static int staticNonFinal = 74;
    static {
        System.out.println("Initializing Initable3");
    }
}

public class ClassInitialization {
    public static Random rand = new Random(47);
    public static void main(String[] args) throws Exception {
        Class initable = Initable.class;
        System.out.println("After creating Initable ref");
        System.out.println("---------------------------");
        // staticFinal 是编译期常量,不需要对 Initable 类进行初始化就可以被读取
        System.out.println(Initable.staticFinal);
        System.out.println("---------------------------");
        // staticFinal2 是非编译期常量,访问它将会强制进行类的初始化
        System.out.println(Initable.staticFinal2);
        System.out.println("---------------------------");
        // staticNonFinal 只是 static 域,在被读取之前,要先进行链接和初始化
        System.out.println(Initable2.staticNonFinal);
        System.out.println("---------------------------");
        // Class.forName 会对类进行初始化
        Class.forName("com.test.class_obj.Initable3");
        System.out.println("After creating Initable3 ref");
        System.out.println(Initable3.staticNonFinal);
        System.out.println("---------------------------");
    }
}

这里写图片描述

  • 仅使用 .class 来获得对类的引用不会引发初始化;而 Class.forName() 立即就进行了初始化(Initable3)
  • static final 是“编译期常量”,不需要对类进行初始化就可以读取(Initable.staticFinal)
  • 如果将一个域设置为 static 和 final 的,则可能会强制进行类的初始化(Initable.staticFinal2)
  • 如果一个 static 域不是 final 的,那么在对他访问之前,要先进行链接(为这个域分配存储空间)和初始化(初始化该存储空间)(Initable2.staticNonFinal)

3 泛化的 Class 引用

Class 引用指向某个 Class 对象,可以制造类的实例,并包含课作用域这些实例的所有方法代码,包含该类的静态成员。

通过使用泛型语法,可以让编译器强制执行额外的类型检查。

package com.test.class_obj;

public class GenericClassReferences {
    public static void main(String[] args) {
        Class intClass = int.class;
        System.out.println(intClass.getSimpleName());
        intClass = double.class;
        System.out.println(intClass.getSimpleName());
        System.out.println("----------------------");

        Class<Integer> genericIntClass = int.class;
        System.out.println(genericIntClass.getSimpleName());

        genericIntClass = Integer.class; // Same thing
        System.out.println(genericIntClass.getSimpleName());

        System.out.println(int.class.equals(Integer.class));
        System.out.println("----------------------");

        genericIntClass = double.class; // Illegal
        Class<Number> genericNumberClass = int.class; // Illegal

        Class<? extends Number> classExtendNumber = int.class;
        System.out.println(classExtendNumber.getSimpleName());

    }
}

这里写图片描述

这里写图片描述

通配符可以使泛化的 Class 引用放松限制,通配符 表示“任何事物”。

Class<?> 优于 平凡的ClassClass<?>的好处是,它表示使用了一个非具体的类引用,选择了非具体的版本。


将通配符与 extends 关键字结合,可以创建一个范围,它被先定位某种类型,或该类型的任何子类型。

Class<? extends Number> classExtendNumber = int.class;

向 Class 引用添加泛型语法的原因仅仅是为了提供编译期类型检查

package com.test.class_obj;

public class GenericToyTest {
    public static void main(String[] args) throws Exception {
        Class<FancyToy> ftClass = FancyToy.class;
        // newInstance() 返回确切类型
        FancyToy fancyToy = ftClass.newInstance();
        System.out.println(ftClass.newInstance().getClass());
        System.out.println("----------------------------------");

        // 某个类,它是 FancyToy 的超类
        Class<? super FancyToy> up = ftClass.getSuperclass();

        Class<Toy> up2 = ftClass.getSuperclass(); // This won't compile:

        // newInstance() 无法返回精确的类型
        Toy obj_1 = up.newInstance();
        Object obj = up.newInstance();
        System.out.println(up.newInstance().getClass());
    }
}

这里写图片描述

这里写图片描述

这里写图片描述


4 参考资料

1、《Java编程思想》

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值