java类型信息——RTTI概念与Class类

1、RTTI(运行时类型识别)

    是什么?

    RTTI即运行时类型识别,意思跟名字一样,在运行时识别某个引用所指向对象的确切类型。

    为什么需要?

  《编程思想》中的例子:有一个Shape类,它有几个子类Circle、Square和Triangle。假如创建一个Shape类型的ArrayList数组,那么这个数组是可以用Shape的子类来填充的,但是当你取出数组中的元素时,你只知道这是一个Shape的引用,它到底指向的是Square类还是Circle类你不知道,而使用RTTI则可以查询Shape引用指向的对象的确切类型。

    怎么实现?

    java怎么让我们在运行时识别类和对象的信息:一是编译是就知道的类型信息,二是通过反射机制在运行时发现和使用类的信息

2、Class对象

    是什么?

    Class对象是保存了与类有关信息的特殊对象,它用来创建类的对象。每当编译了一个新类就会产生一个保存在一个同名的.class文件中的Class对象。(感觉要去看看虚拟机的知识)

    用来干什么?

    比如要生成Shape类的对象,那么类加载器首先检查Shape类的Class对象是否加载,若没有加载就会根据类名(Shape)查找.class文件,并将Class对象载入内存,然后Class对象就用来创建Shape类对象。(自己的理解,欢迎大佬指正)

3、Class类中一些方法的使用:

(1)Class.forName(),使用Class.forName()获得Class对象引用时会导致类的加载

package rtti;
class A{
    static{
        System.out.println("A");
    }
}
class Test {
    public static void main(String[] args){
        try{
		    Class.forName("rtti.A");//此时类A被加载了
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
    }
}
//output:
//A

(2)getName()、getSimepleName()、getCanonicalName()和isInterface()

    getName():返回虚拟机里面class的表示

    getSimepleName():返回简单的类名表示

    getCanonicalName():返回比getName()简洁的类名表示,大部分情况下与getName()返回的值相同

package psl.rtti.study;

class A{
	class InnerA{
	}
}
class B{
}
public class Test3 {
	public static void main(String[] args) {
		Class a = A.class;
		Class innera = A.InnerA.class;
		Class b = B.class;
		System.out.println("a.getName():"+a.getName());
		System.out.println("a.getSimpleName():"+a.getSimpleName());
		System.out.println("a.getCanonicalName():"+a.getCanonicalName());

		System.out.println("innera.getName():"+innera.getName());
		System.out.println("innera.getSimpleName():"+innera.getSimpleName());
		System.out.println("innera.getCanonicalName():"+innera.getCanonicalName());

		System.out.println("b.getName():"+b.getName());
		System.out.println("b.getSimpleName():"+b.getSimpleName());
		System.out.println("b.getCanonicalName():"+b.getCanonicalName());
	}
}
/*output:
 *a.getName():psl.rtti.study.A
 *a.getSimpleName():A
 *a.getCanonicalName():psl.rtti.study.A
 *innera.getName():psl.rtti.study.A$InnerA
 *innera.getSimpleName():InnerA
 *innera.getCanonicalName():psl.rtti.study.A.InnerA
 *b.getName():psl.rtti.study.B
 *b.getSimpleName():B
 *b.getCanonicalName():psl.rtti.study.B
 */

(3)getSuperClass()以及newInstance()

    使用非泛化的Class的newInstance()方法返回的是Object对象,但是该引用指向的是对应的类对象。使用newInstance()来创建类时必须要有默认的构造器。

    使用泛化的Class时:

       Class<? extends A> 此时可以确定newInstance()产生的类为A的子类,所以可以将其返回值赋值给A的引用

       Class<? super A>此时使用newIntance()依然返回Object的引用

       Class<A>此时确切的知道类型为A,newInstance()返回的为A类型

class A1 {
}
class B1 extends A1{
}

public class Test {
	public static void main(String[] args) throws Exception{
		Class<B1> cb1= B1.class;
		B1 b1 = cb1.newInstance();//返回的为B1类型
		System.out.println(b1);
		
		Class c = cb1.getSuperclass();
		//A1 a = c.newInstance();	//Error 
		Object obj1 = c.newInstance(); //这里实际返回Object引用,但指向A1对象
		System.out.println(obj1);
		
		Class<? super B1> csb1 = cb1.getSuperclass();//这儿只知道是B1的超类,但具体不知道
		Object obj2 =csb1.newInstance();	//所以返回Object引用,指向的A1对象
		Class<? extends A1> cea1 = B1.class;//这里保证是A1的导出类
		A1 a2 = cea1.newInstance();//所以可以用A1
	}
}
//output:
//psl.rtti.study.B1@15db9742
//psl.rtti.study.A1@6d06d69c

4、类字面常量(A.class)

    当使用类字面常量来创建对Class对象的引用时,并不会初始化类。

    为了使用类而做的准备包含三步(以后看虚拟机时再深入理解)

      (1)加载。由类加载器执行,通常在classpath所指定的路径中查找字节码,并从这些字节码中创建一个Class对象

      (2)链接。验证类中的字节码,为静态域分配存储空间,如果需要的话,解析这个类创建的对其他类的引用

      (3)初始化。如果该类具有超类,则对其初始化,执行静态初始化器和静态初始化块

    使用类的“编译期常量”也不需要对类进行初始化

class A{
	static final int sfa = 10;
	static final double sa =Math.random();
	static {
		System.out.println("A初始化了");
	}
}
class B{
	static int b = 20;
	static {
		System.out.println("B初始化了");
	}
}
public class Test{
    public static void main(String[] args){
        Class a = A.class;
        System.out.println("取得A的Class对象引用之后");//没有引起类的初始化
        System.out.println("A.sfa="+A.sfa);//由于sfa为编译期常量,也没有引起类的初始化
        System.out.println("A.sa="+A.sa);//虽然sa是static与final的但是依然引起类的初始化
        System.out.println("B.b="+B.b);
    }
}
//output:
//取得A的class对象引用之后
//A.sfa=10
//A初始化了
//A.sa=0.6363871958687979
//B初始化了
//B.b=20

5、泛化的Class引用

    java SE5后Class可以使用泛型,从而可以限制Class引用所指向的Class对象的类型

    通配符“?”可以取消这一限制,也可以使用通配符限定某一范围

Class Test{
    public static void main(String[] args){
        Class c1 = int.class;
        Class<Integer> c2 = Integer.class;//或者 = int.class
        Class<?> c3 = int.class;
        Class<? extends Number> c4 =int.class;
        
        c1 = Double.class;//c1没有做限定,所以可以
      //c2 = Double.class;  c2限制为Integer的Class对象,所以编译错误
        c3 = Double.class;//c3使用的通配符,所以也可以
        c4 = Double.class;//Double是Number的子类,所以也可以
    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值