运行时类型信息使得你能够在程序运行时发现和使用类型信息;
java在运行中识别类型信息主要有两类,一是从传统的RTTI
,另一种是反射机制;
1、Class对象
要理解RTTI
是在java中的工作原理,必须要知道,类型信息在运行中是如何表示的,这项工作是由Class类来完成的,它包含了与类有关的信息;
类是程序的一部分,每一个类都有一个Class对象,换言之,每当编写并编译一个新类,就会产生一个Class对象(保存在对应的同名.class文件中),为了生成这个类的对象,java虚拟机将使用成为类加载器的子系统;
所有的类都是在其第一次使用时,动态的加载到JVM中,当程序创建的第一个对类的静态成员的引用时,就会加载此类。这证明构造器也是一种静态方法,即使构造器之前并没有使用关键词static
。因此,使用new关键字创造的新对象也会被当作对类的静态成员的引用;
因此,java程序在其开始运行之前并非全部加载,其各个部分只有需要的时候才被加载。
package com.innerclass;
class Candy{
static {
System.out.println("Candy has been load...");
}
}
class Gum{
static {
System.out.println("Gum has been load...");
}
}
class Cookie{
static {
System.out.println("Cookie has been load...");
}
}
public class classform {
public static void main(String[] args){
System.out.println("inside main");
new Candy();
System.out.println("After creating Candy");
try{
Class.forName("com.innerclass.Gum");
} catch (ClassNotFoundException e) {
//e.printStackTrace();
System.out.println("Gum not found");
}
/*
try {
Class<?> g=Gum.class;
}catch (Exception e){
System.out.println("未加载成功...");
}
*/
System.out.println("After Class.forName(\"Gum\") ");
new Cookie();
System.out.println("After creating Cookie");
}
}
inside main
Candy has been load...
After creating Candy
Gum has been load...
After Class.forName("Gum")
Cookie has been load...
After creating Cookie
上面一个例子证明,类仅在其需要的时候加载,static初始化,是在类加载的时候进行的。
实现class对象的引用方法,可以通过Class.forName()
方法,根类的.getClass()
方法和.Class
方法进行使用;
Class类还有一些别的方法:
package com.innerclass;
interface Web{}
interface Applaction{}
interface Net{}
class Candy{
static {
System.out.println("Candy has been load...");
}
}
class Gum extends Candy implements Web,Applaction,Net{
static {
System.out.println("Gum has been load...");
}
}
public class classform {
public static void main(String[] args){
Class c=null;
try{
c=Class.forName("com.innerclass.Gum");
} catch (ClassNotFoundException e) {
System.out.println("cannt find Gum");
System.exit(1);
}
System.out.println(c);
for(Class face:c.getInterfaces()){
System.out.println("c.getInterfaces"+face);
Class up=c.getSuperclass();
Object object=null;
try {
object=up.newInstance();
} catch (IllegalAccessException e) {
System.out.println("cannt access");
System.exit(1);
} catch (InstantiationException e) {
System.out.println("cannt instantiate");
System.exit(1);
}
System.out.println("c.getSuperclass"+object.getClass());
}
}
}
注意上例的newInstance()
方法,是实现虚拟构造器的一种途径,并且由该方法创建的类,必须含有默认无参构造器;
2、类字面常量
java生成引用的另外一个方法,.class
方法,即类字面常量;这样做不仅简单,而且安全,因为他在编译的时候已经检查,故无需采用try语句;它不仅可以应用到普通的类,还可以应用到接口、数组以及基本数据类型;对于包装类型,还有一个标准的字段TYPE;
即对于包装类型:
boolean.class
等价于Boolean.TYPE
当使用.class
来创建Class对象时,不会自动的初始化该Class对象,为了使用类而做的准备实际上包括三个步骤:
1、加载,有类加载器执行,该步骤查找字节码,并从这些字节码中创建一个Class对象;
2、链接:在链接阶段将验证码类中的字节码,为静态域分配存储空间,同时解析该类创建的对其他类的引用;
3、初始化:如果该类有超类的话,先对其超类进行初始化,执行其静态初始化构造器或静态代码块,而初始化延迟到,对静态方法或者常数静态域进行首次引用;
package com;
class FatheCLss{
private static FatheCLss fatheCLss=new FatheCLss();
private final static String str="hello world";
private static int age=1;
private String name="lihua";
public FatheCLss(){
System.out.println("执行FatheClss的构造方法"+" age"+age+" name"+name+ str);
}
static {
System.out.println("执行FatherClss的静态代码块"+age+str);
age=38;
System.out.println("执行FatherClss的静态代码块"+age+str);
}
{
System.out.println("执行"+this.getClass()+"的非静态代码块"+" age:"+age+" name:"+name+str);
age=12;
System.out.println("执行"+this.getClass()+"的非静态代码块"+" age:"+age+" name:"+name+str);
}
}
public class CLassLoad extends FatheCLss {
private static double height;
public CLassLoad(){
super();
System.out.println("执行CLassLoad的构造方法"+height);
}
static {
System.out.println("执行CLassLoad 的静态代码块"+height);
height=12;
System.out.println("执行CLassLoad 的静态代码块"+height);
}
public static void main(String args[]){
new CLassLoad();
}
}
执行class com.FatheCLss的非静态代码块 age:0 name:lihuahello world
执行class com.FatheCLss的非静态代码块 age:12 name:lihuahello world
执行FatheClss的构造方法 age12 namelihuahello world
执行FatherClss的静态代码块1hello world
执行FatherClss的静态代码块38hello world
执行CLassLoad 的静态代码块0.0
执行CLassLoad 的静态代码块12.0
执行class com.CLassLoad的非静态代码块 age:38 name:lihuahello world
执行class com.CLassLoad的非静态代码块 age:12 name:lihuahello world
执行FatheClss的构造方法 age12 namelihuahello world
执行CLassLoad的构造方法12.0
package com;
class FatheCLss{
// private static FatheCLss fatheCLss=new FatheCLss();
static final String STR="hello world";
static int age=1;
int width=12;
final int height=1;
String name="lihua";
public FatheCLss(){
System.out.println("执行FatheClss的构造方法"+" age"+age+" name"+name+ STR);
}
static {
System.out.println("执行FatherClss的静态代码块"+age+STR);
age=38;
System.out.println("执行FatherClss的静态代码块"+age+STR);
}
{
System.out.println("执行"+this.getClass()+"的非静态代码块"+" age:"+age+" name:"+name+STR);
age=12;
System.out.println("执行"+this.getClass()+"的非静态代码块"+" age:"+age+" name:"+name+STR);
}
}
public class CLassLoad {
public static void main(String args[]){
Class father=FatheCLss.class;
System.out.println("before fatherClass 实例化");
System.out.println(FatheCLss.STR);
System.out.println("fatheclass 的 str");
System.out.println(FatheCLss.age);
}
}
可以看出对于static final
修饰的值,代表为编译器常量,不需要对其进行初始化即可得到;
而,一个static
域而不是final
的,那么对其访问时,总是要求在它被读之前进行链接和初始化;