Java Class对象详解

要怎样在java里来使用一个类,首先必须先把类的.class字节码文件加载进来,然后再进行连接对该类里的域分配内存,最后再调用构造器,如果该类有基类的话,会先去调用基类的构造器,总的来说,分为以下三个步骤。

1.根据环境变量找到并加载.class文件
2.为该类的非编译时常量分配内存
3.调用该类的构造器

java里的所有类都有一个Class对象,通过这个Class对象我们能够获取此类的各种信息。
当某个字节码文件被JVM加载的时候,Class对象就被创建。
Class类没有构造方法,是内部的一个defineClass方法来创建此对象的,此对象与被加载的字节码文件的类的类型相对应。

其实在java里包括基本数据类型(int short long byte float double boolean char),也包括了void

        System.out.println(int.class.getName());
		System.out.println(char.class.getName());
		System.out.println(short.class.getName());
		System.out.println(long.class.getName());
		System.out.println(byte.class.getName());
		System.out.println(float.class.getName());
		System.out.println(double.class.getName());
		System.out.println(boolean.class.getName());
		System.out.println(void.class.getName());

都有与之对应的class对象,同类型的类型也共享一个class对象。也包括了数组,所有同类型同维度的数组也共享一个class对象。

public class Main {
	public static void main(String[] args) {
		System.out.println(char[].class.getName());//[C
		System.out.println(char[][].class.getName());//[[C
	}
}

class的forName方法

同时class里有一个static的方法forName,可以让我们显示的来把一个类的.class文件加载至JVM虚拟机。

static Class< ? > forName(String className)

public class Main {
	public static void main(String[] args) throws Exception{
		Class a = Class.forName("A");
	}
}

class A{
	void print(){
		System.out.println("hello world");
	}
}

该方法返回的是一个Class对象,这个Class对象也可以添加泛型。
这样的话我们就获得一个与A类型对应的Class对象。
但是这时编译器会强制的让我们抛出或者捕获这个异常,所以我们需要将它捕获或者抛出。

接下来我们还能重载一个A类的private的构造器(注:默认的构造器是隐式的static,我们重载之后就不再是static),但是我们仍然能获取它的class对象,因为调用构造器是在最后一步,而我们这里只是加载.class文件。

public class Main {
	public static void main(String[] args) throws ClassNotFoundException {
		Class a = Class.forName("A");
	}
}


class A{
	private A(){
		
	}
	void print(){
		System.out.println("hello world");
	}
}

这段代码是毫无错误的

但是直到现在我们都还不能通过Class.forName来操作一个类,因为它只是简单的加载而已,但是没关系Class类还有一个newInstance方法,这个方法能帮助我们创建一个class类的实例,我们只需显示的转换一下类型即可操作A类.

public class Main {
	public static void main(String[] args) throws Exception {
		A a = (A)Class.forName("A").newInstance();
		a.print();
	}
}


class A{
	
	void print(){
		System.out.println("hello world");
	}
}

这时,其实我们得到的就是一个A类型的实例了。

但是如果我们这时把A的构造方法声明为private呢?

public class Main {
	public static void main(String[] args) throws Exception {
		A a = (A)Class.forName("A").newInstance();
		a.print();
	}
}


class A{
	private A(){
		
	}
	void print(){
		System.out.println("hello world");
	}
}

编译器是仍然不会报错的,但是如果我们执行这段代码就会发现会抛出一个异常,因为这是在运行时加载的,所以编译器是无法察觉的。这也是相当危险的,所以一般情况下我们都会遵守用new来创建对象。

既然class是运行时对象,那么对于final static 声明的域也是毫无作用的了,这么说的原因是用final static声明的域是不需要动态的来分配内存的,因为它是一个编译时常量。

到现在我们大概明白了class的含义和运用,那它和.class有什么联系呢。
其实每个类也有一个.class的常量,我们称为类字面常量,这个常量能够返回该类的class对象,也能通过newIstance创建实例来操作。

运用类字面常量的好处就在于不用去抛出或者捕获异常,所犯的错误在编译时就能查找出来。

public class Main {
	public static void main(String[] args) throws Exception {
		A a = (A)A.class.newInstance();
		a.print();
	}
}


class A{
	void print(){
		System.out.println("hello world");
	}
}

封装类的TYPE

这里以boolean类型来说明这个问题

System.out.println(boolean.class == Boolean.TYPE);//true

所以我们得出基本类型的.class 和 封装类的TYPE是等价的。

posted @ 2016-10-12 20:47  浩大王  阅读(4713)  评论(2编辑  收藏  举报