您的位置:首页 > 编程语言 > Java开发

java基础-反射1(类型信息,Class对象简介,Class对象初始化)

2017-11-30 15:18 1041 查看

Java基础-反射1(反射基础,运行时类型信息,Class对象简介)

在学习反射之前,先来了解一下类型信息,类型信息就是JAVA类的一些信息,包括(包,方法,属性等),运行时类型信息可以让你在程序运行时了解类的信息并且使用类,那么如何在运行时获取类的信息?在JAVA中有两种方式:

传统的RTTI

反射

RTTI (Run-Time Type identification)

传统的RTTI的使用是假定我们在编译时就已经知道了类的信息。先来看一个例子:有一个People的抽象类,然后American类和Chinese类继承至People如下:

public abstract  class  People {
public abstract void say();
}


public class American extends People {
@Override
public void say() {
System.out.println("Americaen say: hello");
}
}


public class Chinese extends People {
@Override
public void say() {
System.out.println("中国人 说: 你好");
}
}


public class Test {
public static void main(String agrs[]){
List<People> peoples= new ArrayList<>();
peoples.add(new American());
peoples.add(new Chinese());

for (int i = 0 ;i <peoples.size();i++){
peoples.get(i).say();
}
}
}




这是一个简单的类的继承例子,People是父类,American 和Chinese 是子类,在Test中我们创建一个List,放入American和Chinese,然后我们取出,调用say()方法.

在这里我们List中定义需要放的类型是父类People,而不是子类,面向对象的目的是让程序只操作父类而不是子类,但是我们实际上往List中添加的类型是子类(American,Chinese),这里就发生了向上转型(子类对象赋值给父类引用)但是我们在调用People的say()方法时却又打印出子类的信息(这就是多态

下面我们来看把对象从List中取出来的代码:

ArrayList底层是通过数组实现的 下面是ArryList源码中的其中一个构造方法

transient Object[] elementData;

public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}


通过源码可以看出 elementData 是一个Object 数组。所以

List peoples= new ArrayList<>(); 其实是储存的Object[]数组,但是当我们从List中取出来时,如下面代码:

for (int i = 0 ;i <peoples.size();i++){
peoples.get(i).say();
}


当我们从List中取出来时却又表现出People的特点,那是因为我们从
List<People> peoples
中取出来时自动将Object转化为了People(向下转型,相当于People people = (People)object) 这就是RTTI的最基本的使用形式:在运行时进行类型检查因为我们在编译时已经知道
List<People> peoples
储存的都是People。所以在我们从容器中取出元素时系统自动会将Object转化为People。

那么RTTI是如何工作的喃?要理解RTTI的工作原理就必须要类型信息在运行时如何表示的。

Class对象简介

java 在运行时表示类型信息是通过class这个特殊对象来完成的,

class对象属于Class类,Class类用来表示一般类的信息,所以class对象包含了与类相关的信息,它主要有以下几个特点:

每个类都有一个与其相关的Class对象。

Class对象含有与其相关类的类信息。

Class对象用来创建与其相关类的“常规对象“

Class对象保存在与其同名的.class文件中。

那么Class对象是如何产生的喃?

Class对象是通过类加载器加载的,并且只有在第一次使用类时才会被加载。

当使用一个类时,JVM的类加载器会先检查这个类的class对象是否已经被加载,如果没有被加载,则查找同名的.class文件。如果一旦发现类的class对象已经被加载到内存了,则使用这个class对象来创建这个类的所有对象。

那么如何获取Class对象?

Class对象的获取

下面看下这个例子

public class Dog {
static {
System.out.println("狗");
}
}


public class Cat {
static {
System.out.println("猫");
}
}


public class People {
static {
System.out.println("人");
}
}


public class Test {
public static void main(String agrs[]){

try {
//获取Class 的第一种方法
Class catClass=  Class.forName("com.hbcode.Test.Cat");

Dog dog1 = new Dog();
//获取Class 的第二种方法
Class dog =dog1.getClass();
//获取Class 的第三种方法
Class people =People.class;

} catch (ClassNotFoundException e) {
e.printStackTrace();
}

}
}


上面就是获取类的class对象的三种方法,下面我们看下打印的信息



我们发现没有打印出来“人”那是因为使用.class来获取类的class对象并不会初始化类的class对象,实际上在获取class对象时做了一下三步:

类的加载:由类加载器执行,根据类名查找.class文件,并从中创建一个class对象

类的链接:验证.class文件(字节码文件)看其结构是否完整,并且为静态域分配空间。

类的初始化:如果类有父类则初始化父类,执行父类的静态初始化块,和静态初始化器,然后执行自己的。

延伸:笔试题,变量的初始化顺序

根据《java编程思想》一书上面的讲解,类的初始化其实是被延迟到了对静态方法的引用(构造函数也是隐式的静态方法)或者是非常数的静态域进行引用。

从上面可以看出
Class.forName()
其实就是调用的是类的静态函数可以被初始化。创建对象 new 对象时其实也是对构造函数(隐式的静态方法)的调用。

现在我们获取到了class对象,就可以获取其相对应的类型信息了,下一篇文章我们就来通过class对象来获取类的类型信息,并且通过反射来操作类
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐