Java类型信息

package RTTI;

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("RTTI.Gum");
        }catch (ClassNotFoundException e){
            System.out.println(e);
        }
        System.out.println("After Class.forName(\"Gum\")");
        new CooKie();
        System.out.println("After creating Cookie");
    }
}
class Candy{
    static {
        System.out.println("Loading Candy");
    }
}
class CooKie{
    static {
        System.out.println("Loading Cookie");
    }
}
class Gum {
    static {
        System.out.println("Loading Gum");
    }
}

static子句,static初始化是在类加载时进行的。

Class.forName();取得Class对象的引用的一种方法,它是用一个包含目标类的文本名的String作输入参数,返回一个Class对象的引用。如果类还没有被加载就加载它。在传递给forNam()的字符串中,你必须使用权限定名(包含包名)

Class对象使用getName()来产生全限定的类名,并分别使用getSimpleName()和getCanonicalName()来产生不含包名的类名和全限定的类名。isInterface()方法如同其名,可以告诉你这个Class对象是否表示某个接口。因此,通过Class对象,你可以发现你想要了解的类型的所有信息。

在main()中调用的Class.getInterfaces()方法返回的是Class对象,它们表示在感兴趣的Class对象中所包含的接口。

如果你有一个Class对象,还可以使用getSuperclass()方法查询其直接基类,这将返回你可以用来进一步查询的Class对象。因此,你可以在运行时发现一个对象完整的类继承结构。

Class的newInstance()方法是实现“虚拟构造器”的一种途径,虚拟构造器允许你声明:“我不知道你的确切类型,但是无论如何要正确地创建你自己。”在前面的示例中,up仅仅是一个Class引用,在编译期不具备任何更近一步的类型信息,当你创建新实例时,会得到Object引用,但是这个引用指向的是Toy对象。当然,在你可以发送Object能够接收的消息之外的任何消息之前,你必须更多地了解它,并执行某种转型。另外,使用newInstance()来创建的类,必须带有默认的构造器。

 


 

package RTTI;
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());
    }

    public static void main(String[] args) {
        Class c=null;
        try{
            c=Class.forName("RTTI.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{
            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());
    }
}

在ToyTest.java中,将Toy的默认构造器注释掉,并解释发生的现象

up.newInstance()发生异常,异常类型InstantiationException,输出:Cannot instantiate

将新的interface加到ToyTest.java中,看看这个程序是够能够正确检测出来并加以显示

 

 不能,不知道为啥

将Rhomboid加入Shapes.java中。创建一个Rhomboid,将其向上转型为Shape,然后向下转型回Rhomboid。试着将其向下转型成Circle,看看会发生什么。

 


 

 

 

package RTTI;

import java.util.Arrays;
import java.util.List;

abstract class Shape {
    void draw(){
        System.out.println(this+".draw()");
    }
    abstract public String toString();
}
class Circle extends Shape{
    public String toString(){
        return "Circle";
    }
}
class Square extends Shape{
    public String toString(){
        return "Square";
    }
}
class Triangle extends Shape{
    public String toString(){
        return "Triangle";
    }
}
public class Shapes{
    public static void main(String[] args) {
        List<Shape> shapeList= Arrays.asList(
                new Circle(),new Square(),new Triangle()
        );
        for(Shape shape:shapeList){
            shape.draw();
        }
    }
}

 

如果某个对象出现在字符串表达式中(涉及“+”和字符串对象的表达式),toString()方法就会被自动调用,以生成表示该对象的String。

 

如果不重写toString()的话,就会输出对象的地址

 

toString()被声明为abstract,以此强制继承者覆写该方法,并可以防止对无格式的SHape的实例化。

 

在本例中,当把Shape对象放入List<Shape>的数组时会向上转型。但在向上转型为Shape的时候也丢失了Shape对象的具体类型。对于数组而言,他们知识Shape类的对象。

 

当从数组中取出元素时,这种容器--实际上他将所有的事物都当做Object持有-----会自动将结果转型回Shape。这是TRRI最基本的使用形式,因为在java中,所有的类型转换都是在运行时进行正确性检查的。这也是RTTI名字的含义:在运行时,识别一个对象的类型。

 

我们希望尽可能少地了解对象的具体类型,而是只与对象家族中的一个通用表示打交道。

 

posted @ 2021-09-24 10:20  北征愚人  阅读(30)  评论(0编辑  收藏  举报