来自Class对象的RTTI(一)

[size=medium] [color=red]The normal goal in object-oriented programming is for your code to manipulate references to the base type[/color].(面向对象编程中的基本目的是:让代码只操纵对基类的引用)——引自Thinking in java[/size]

[size=medium]如果有一天,当你发现编程对你来说变得愈发简单时,回头看一看你写过的代码,你会恍然大悟——原来多态无处不在。书中常说,面向基类(接口)的编程使代码更容易写、更容易读、更容易维护,设计上也更容易实现、理解和改变。我觉得这不是重点,重要的是这些特性会让你越来越喜欢他,而当你越来越喜欢它的时候,你所感受到的那种快乐已经远远超过了这些优点所带来的快感。[/size]
[size=medium]直入正题,为了进一步的了解多态,我们应该先了解一下动态绑定(Dynamic Binding)与静态绑定(Static Binding)。[/size]
[size=medium][color=red]一、动态绑定与静态绑定[/color][/size]
[size=medium][color=blue]1、动态绑定[/color][/size]
[size=medium]动态绑定是指,在执行期间判断所引用对象的实际类型,根据其实际类型调用其方法。动态绑定又名后期绑定(Late Binding),可以这样理解动态绑定,在编译期无法解析调用的方法,只有在运行时才能正确解析。如下示例。[/size]
class SuperClass {	
public void doSomething(){
System.out.println("SuperClass.doSomething");
}
}
class SubClass extends SuperClass{
public void doSomething(){
System.out.println("SubClass.doSomething");
}
}
public class Test {
public static void main(String[] args){
SuperClass sup = new SuperClass();
SuperClass sub = new SubClass();
sup.doSomething();
sub.doSomething();
}
}

[size=medium][color=blue]Output:[/color][/size]
SuperClass.doSomething
SubClass.doSomething

[size=medium]可以看到,在编译阶段无论是sup还是sub都是Super的引用,而在运行时,它们各自指向了SuperClass和SubClass。因此,我们可以看到,在Java中动态绑定绑定的方法是基于实际的对象类型的,而不是声明时对象的引用类型。([color=red]注:这些方法通常是可以被重写的派生方法,因此编译期无法去识别方法的版本[/color])[/size]
[size=medium][color=blue]2、静态绑定[/color][/size]
[size=medium]能被编译器在编译期解析的绑定称为静态绑定或早期绑定(Early Binding)。所有的实例方法调用都是在运行时进行解析的,而所有的static方法调用都是在编译期完成解析的。由于静态的方法是class的方法,而非对象的方法(当然,对象也可以调用此类方法),因此解析它们只需在编译期就可以了。([color=red]静态的方法可以被重写吗?[/color])[/size]
[size=medium]同样的,Java中的成员变量也是静态绑定的。Java没有提供成员变量的多态。如下示例。[/size]
class SuperClass {	
String variable = "the variable of SuperClass";
}
class SubClass extends SuperClass{
String variable = "the variable of SubClass";
}
public class Test {
public static void main(String[] args){
SuperClass sup = new SuperClass();
SuperClass sub = new SubClass();
System.out.println(sup.variable);
System.out.println(sub.variable);
}
}

[size=medium][color=blue]Output:[/color][/size]
the variable of SuperClass
the variable of SuperClass

[size=medium]输出结果相同,说明成员变量在编译器完成了绑定,而非运行时。根据这些线索,我们也可以推测出,private方法也是静态绑定的,因为无法实现它的派生方法。[/size]
[size=medium][color=blue]3、链接[/color][/size]
[size=medium]可以说,动态绑定使我们对多态有了更深的认识,当然它的底层实现还有待研究。这里之所以提到多态,是因为多态与RTTI是相辅相成的。所以怎样去理解它们,RTTI是什么,为什么要使用RTTI,等等这些问题还有待解决。[/size]
[size=medium][color=red]二、多态与RTTI[/color][/size]
[size=medium][color=blue]1、为什么需要RTTI[/color][/size]
[size=medium]一个典型的例子,来自Thinking in Java。[/size]
import java.util.List;
import java.util.Arrays;
public class Shapes {
public static void main(String[] args){
/**
*1、 当把Shape对象放入List<Shape>的数组时会向上转型。但向上转型为Shape的时候也丢失了Shape对象的具体类型。
*2、 对于数组而言,它们只是Shape类的对象——实际上它将所有的事物都当作Object持有。
*3、 当从数组中取出元素时,这种容器会自动将结果转型回Shape,这是RTTI最基本的使用形式。
*4、 Shape对象执行什么样的代码,是由引用所指向的具体对象Circle、Square或Triangle而决定的(多态)。
*/
List<Shape> shapeList = Arrays.asList(new Circle(),new Square(),new Triangle());
for(Shape shape : shapeList){
shape.draw();
}
}
}
abstract class Shape{
void draw(){System.out.println(this + ".draw()");}
/**
* 如果某个对象出现在字符串表达式中,toString()方法就会被自动调用,以生成表示该对象的String。
*/
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";
}
}

[size=medium][color=blue]Output:[/color][/size]
Circle.draw()
Square.draw()
Triangle.draw()

[size=medium][color=red]在Java中,所有的类型转换都是在运行时进行正确检查的。这也正是RTTI名字的含义:在运行时,识别一个对象的类型。[/color][/size]
[size=medium]这个实例用泛型、RTTI和多态共同完成了一项任务,泛型确保类编译器的类型转换;RTTI完成了运行时的类型转换;多态使对应的对象执行了正确的行为。[/size]
[size=medium]但是,如果仅仅至于一个对象家族的通用类打交道无法满足我们的需求,我们该怎么办呢?如果我们在运行时能够确定识别一个泛化引用的确切类型,这样我们就能满足我们对特殊问题的需求,那么这个技术点又该如何解决呢?[/size]
[size=medium][color=blue]2、链接[/color][/size]
[size=medium]RTTI能够为我们解决上面提到的问题,它能够使我们在运行时查询泛化引用的确切类型。理解RTTI的关键,还需从Class对象入手![/size]
[size=medium][color=red]三、Class对象[/color][/size]
[size=medium][color=blue]1、什么是Class对象[/color][/size]
[size=medium]每个类都有一个Class对象。Class对象包含了与类有关的信息,它用来创建类的所有的常规对象。[/size]
[size=medium][color=blue]2、Class对象的由来[/color][/size]
[size=medium]位于堆区中的Class对象,是类加载的最终产物——类加载器的行为目标。[/size]
[size=medium][color=blue]3、类的加载[/color][/size]
[size=medium]类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.class对象,用来封装类的方法区内的数据结构。[/size]
[size=medium][color=darkblue]1、所有的类都是在对其第一次使用时,动态加载到JVM中的。当程序创建第一个对类的静态成员引用时,就会加载这个类。[/color][/size]
[size=medium][color=darkblue]2、程序在它开始运行之前并非被完全加载,其个部分是在必需时才加载的。(动态加载)[/color][/size]
[size=medium][color=darkblue]3、类加载器首先检查这个类的Class对象是否已经加载。如果尚未加载,默认的类加载器(System ClassLoader)就会根据类名查找.class文件。[/color][/size]
[size=medium][color=darkblue]4、一旦某个类的Class对象被载入内存,它就被用来创建这个类的所有对象。[/color][/size]
[size=medium]如下示例:[/size]
class First{
static { System.out.println("Loading First!"); }
}
class Second{
static {System.out.println("Loading Second!");}
}
class Third{
static {System.out.println("Loading Third!");}
}
public class LoadTest {
//当程序创建第一个对类的静态成员的引用时,就会加载这个类
public static void main(String[] args){
System.out.println("start from main method");
new First();
new First();
new First();
System.out.println("After creating First");
try{
Class.forName("Second");
new Second();
}catch(ClassNotFoundException ep){
System.out.println("Couldn't find Second");
}
System.out.println("After Creating Second");
new Third();
System.out.println("After creating Third");
}
}

[size=medium][color=blue]Output:[/color][/size]
start from main method
Loading First!
After creating First
Loading Second!
After Creating Second
Loading Third!
After creating Third

[size=medium][color=blue]4、链接[/color][/size]
[size=medium]如果要使用一个类,仅仅是加载它是不够的。要使用它,还必需进行链接与初始化。这些仅仅是个开始,如何在运行时确定一个泛化引用的具体类型,如何安全严谨地使用Class对象,都是我们亟待解决的问题。[/size]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值