- 浏览: 103331 次
- 性别:
- 来自: 长沙
文章分类
最新评论
-
mandrakeli:
不错,第一个问题解决了
hadoop错误汇总 -
jenew:
楼主 我要对比两个特征向量的相似度 该怎么做啊?谢谢
OpenCV——Mat、CvMat、IplImage类型浅析 -
luliangy:
哥哥,HDFS的GC好像是move到Trash目录下,然后三天 ...
突然发现的HDFS与GFS在实现上的一些不同 -
白粥若水:
ihopethatwell 写道楼主,如果Mat srcMat ...
OpenCV——Mat、CvMat、IplImage类型浅析 -
ihopethatwell:
楼主,如果Mat srcMat(Size(width, hei ...
OpenCV——Mat、CvMat、IplImage类型浅析
首先介绍一个本文后面会频繁提到的概念:RTTI(Runtime Type Information,或者,Run-Time Type Identification),运行时类型信息。简单来说,就是指程序能够在运行时发现和使用类型信息。
RTTI能做什么??它解放了程序在编期间执行的面向类型的操作,不管是程序的安全性还是可扩展性和可维护性,都得到了大大的加强。
我们一般使用二种方式来实现运行时识别对象和类的信息:“传统的”RTTI和“反射”机制。
一、一个大家都熟悉的例子
传统的RTTI假定我们在编译时已经知道了所有的信息。
下面我们来看一看一个很熟悉的例子:
package cn.OOP.Typeinfo;
import java.util.Arrays;
import java.util.List;
abstract class Student{
void study(){ System.out.println(this+".study()");}
abstract public String toString();
}
class PrimaryStudent extends Student{
public String toString(){ return "PrimaryStudent";}
}
class HighSchoolStudent extends Student{
public String toString(){ return "HighSchoolStudent";}
}
class UniversityStudent extends Student{
public String toString(){ return "UniversityStudent";}
}
public class Students {
public static void main(String args[]){
List<Student> studentList = Arrays.asList(
new PrimaryStudent(),new HighSchoolStudent(),new UniversityStudent()
);
for(Student s : studentList){
s.study();
}
}
}
/* output:
PrimaryStudent.study()
HighSchoolStudent.study()
UniversityStudent.study()
*///
基类Studeng中包含study方法,通过传递this参数给System.out.println()方法,间接使用toString()来打印类标识符。这里,toString()被声明为abstract,以此强制子类覆写该方法,并可以防止无格式的Student的实例化。
输出结果反映,子类通过覆盖toString方法,study()方法在不同的情况下会有不同的输出(多态)。
而且,在将Student对象放入List<Student>的数组时,对象被向上转型为Student类,但同时也丢失了Student对象的具体类型信息:对于程序而言,如果我们不对数组内的对象进行向下转型,那么,他们“只是”Student对象。
在上述例子中,还有一个地方用到了RTTI。容器List将它持有的对象都看做Object对象来处理。当我们从数组中取出对象时,对象被转型回Student类型。这是最基本的RTTI形式,因为在Java中,所有的类型转换都是在运行才进行正确性检查的。
还有一点,例子中的RTTI类型转换并不彻底:Object对象被转型为Student,而不是UStudent、PStudent、HStudent。这是因为程序只知道数组中保存的是Student,在编译时Java通过容器和泛型来确保这一点,而在运行时就由转型来实现。
例子很简单,但说明的东西很多。
二、一个特殊的编程问题
在现实当中,当我们能够知道某个泛化引用的确切类型的时候,我们可以方便快捷的解决它,怎么办??
比如:我们扫描某个区域的生物(animal),并将之装入一个数组。当用户要突出的显出其中的某一类,如人类,的时候,系统是并不容易判断的。因为,对于程序而言,数组中的对象都是aniaml。使用RTTI,可以查询animal引用的确切类型,然后选择或者剔除特例。
三、Class对象
Class对象是RTTI在Java中工作机制的核心。
我们知道,Java程序是由一个一个的类组成的。而对于每一个类,都有一个class对象与之对应。也就是说,每编译一个新类都会产生一个class对象(事实上,是这个class对象时被保存在同名和.class文件当中的)。这个过程涉及到类的加载,不在本文的内容之内。
无论何时,只要我们想要在运行时使用类型信息,就必须获得恰当的class对象的引用。
常规来讲,class对象有三种获得方式,而且,它包含很多有用的方法。看下面的程序:
package cn.OOP.Typeinfo; interface Drinkable{} interface Sellable{} class Coke{ Coke(){} //运行这个程序后,注释掉这个默认的无参构造器再试一试 Coke(int i ){} } class CocaCola extends Coke implements Drinkable,Sellable{ public CocaCola() { super(1);} } public class TestClass { static void printinfo(Class c){ System.out.println("Class Name:"+c.getName()+" is interface? ["+ c.isInterface()+"]"); System.out.println("Simple Name:"+c.getSimpleName()); System.out.println("Canonical Name:"+c.getCanonicalName()); } public static void main(String args[]){ Class c= null; try{ c = Class.forName("cn.OOP.Typeinfo.CocaCola"); //or we can init c in this way // CocaCola cc = new CocaCola(); // c = cc.getClass(); //we can also init c in this way // c = CocaCola.class; }catch(ClassNotFoundException e){ System.out.println("Can't find CocaCola!!"); System.exit(1); } printinfo(c); for(Class face : c.getInterfaces()){ printinfo(face); } } }/* output: Class Name:cn.OOP.Typeinfo.CocaCola is interface? [false] Simple Name:CocaCola Canonical Name:cn.OOP.Typeinfo.CocaCola Class Name:cn.OOP.Typeinfo.Drinkable is interface? [true] Simple Name:Drinkable Canonical Name:cn.OOP.Typeinfo.Drinkable Class Name:cn.OOP.Typeinfo.Sellable is interface? [true] Simple Name:Sellable Canonical Name:cn.OOP.Typeinfo.Sellable *///
CocaCola类继承自Cola类并实现了Drinkable、Sellable接口。在mian方法中,我们用forName()方法创建了一个
Class对象的引用。需要注意的是,forName()方法传入的参数必须是全限定名(就是包含包名)
在printinfo()方法中,分别使用getSimpleName()和getCanonicalName()来打印出不含包名的类名和全限定的类名。isInterface()很明显,是得到这个class对象是否表示一个接口。虽然,我们这里只是演示了class对象的3种方法,但实际上,通过class对象,我们发现我们能够了解到类型的几乎所有的信息(之所以是“几乎”是因为我们有时候并不需要客户了解我们提供的类的某些信息,而选择性的屏蔽。大家可以试一试用class对象查询某些类的private属性!)
例子有出现了三种不同的获得class对象的方法:
Class.forName():最简单的,也是最快捷的方式,因为我们并不需要为了获得class对象而持有该类的对象实例。
obj.getClass():当我们已经拥有了一个感兴趣的类型的对象时,这个方法很好用。
Obj.class : 类字面常量,这种方式很安全,因为它在编译时就会得到检查(因此不需要放到try语句块中),而且高效。
我们可以根据我们的程序的条件和需要,选择上面三种方式中的任何一种来实现RTTI。
四、泛化的Class引用
通过上面的例子我们可以很容易的知道,class引用表示的是它所指向的对象的确切类型,并且,通过class对象我们
能获取特定类的几乎所有信息。这很容易理解。
但是,Java的设计者并不止步于此。通过泛型,我们可以让class引用所指向的类型更加具体。
package cn.OOP.Typeinfo;
public class GenericClassReference {
public static void main(String args[]){
Class intClass = int.class;
Class<Integer> genericIntClass = int.class;
genericIntClass = Integer.class; //same thing
intClass = double.class;
// genericIntClass = double.class; //Illegal,Can not compiled
}
}
看这个例子,普通的Class引用intClass能被随意赋值指向任意类型,但是,使用了泛型之后,编译器会强制对class引用的重新赋值进行检查。
但这种泛型的使用与普通的泛型又是不同的,比如下面这条语句:
Class<Number> c = int.class;
乍一看,没什么不对,Integer继承自Number类,不就是父类引用指向子类对象么。但实际上,这句代码会在编译时就出错。因为Integer的class对象引用不是Number的Class引用的子类。这看起来很诡异,但却是事实。
事实上,正确的做法是使用通配符?。看下面:
package cn.OOP.Typeinfo;
public class WildClassReference { public static void main(String args[]){ Class<?> intClass = int.class; //? means everything intClass = double.class; Class<? extends Number> longClass = long.class; longClass = float.class; //Compile Success } }
通配符?表示“任何类”,所以intClass能够重新指向double.class。同时? extends Number 表示任何Number类的子类。
五、反射:RTTI实现和动态编程
就上面的例子我们看到,RTTI可以告诉你所有的你想知道的类型信息,前提是这个类型是在编译时候已知的。
这好像没有什么不对吧??
将眼界放开点:假设程序获取了一个我们程序空间以外的对象的引用。即编译时并不存在的类。例如:从本地硬盘,从网络。要知道,Java的一大优势就是适于现在的WEB环境!!!
看下面:
package cn.OOP.Typeinfo;
import java.util.Scanner; public class Reflection { public static void main(String args[]){ Class c = null; Scanner sc = new Scanner(System.in); System.out.println("Please put the name of the Class you want load:"); String ClassName = sc.next(); try { c = Class.forName(ClassName); System.out.println("successed load the Class:"+ClassName); } catch (ClassNotFoundException e) { System.out.println("Can not find the Class ACommonClass"); System.exit(1); } } }
当我们运行这个程序的时候,系统会阻塞在这一步:String ClassName = sc.next();
这时的输出是:
Please put the name of the Class you want load:
然后,由我们给定一个类名。假定,我们需要输入一个我们自定义的类:ACommonClass。注意,这是关键 。这个时候,我们并没有开始写这个类,更没有编译这个类,也就没有对应的.class文件。
这个时候,写第二个程序(类)
package cn.OOP.Typeinfo;
public class ACommonClass { //I am just a generic Class }
编译这个类,得到此类的.class 文件。然后,在程序一(注意,这个程序一直没有停止,一直在运行)输入类名:
cn.OOP.Typeinfo.ACommonClass,阻塞停止,打印输出。
最终结果:
Please put the name of the Class you want load:
cn.OOP.Typeinfo.ACommonClass successed load the Class:cn.OOP.Typeinfo.ACommonClass
在这个例子中,我们看到了一个和以前传统的编程截然不同的东西:在程序运行时,我们还能云淡风轻的写着程序必须的类,而且,这个类还能用于这个已经开始的程序!!!!神奇吧。
当然,这只是一个最简单的RTTI反射应用,通常的Java动态编程会更复杂,也更神奇!!
Class类与java.lang.reflect类库一起对反射机制提供了支持。当通过反射与一个未知类型的对象打交道时,JVM只是简单的检查这个对象,看他属于哪个特定的类(就像传统的RTTI一样)。当我们用反射机制做某些事情时,我们还是必须知道特定的类(也就是必须得到.class文件),要么在本地,要么从网络获取。所不同的是,由于设计体系的特殊,我们逃避了在编译期间的检查,知道运行时猜打开和检查.class文件。
未完待续!!!!
等会更新
发表评论
-
软件测试战略_测试那些事
2012-03-21 16:35 1730这几天看了一些关于 ... -
软件测试战略_初学者的看法
2012-03-21 16:04 5这几天看了一些关于软件工程里面软件测试方面的书籍,感觉蛮有收 ... -
《深入Java虚拟机》_类型的生命周期_Version2
2011-09-19 21:34 2851LastUpdataTime:11.11.14 ... -
《TCP/IP详解》_卷一_主机对接收帧的过滤
2011-09-01 22:19 894前面写了一些关于广播和多播的笔记,这里加上一点主机在信道的帧过 ... -
《TCP/IP详解》_卷一_广播和多播
2011-09-01 20:21 1851在前面学习IP地址的概 ... -
《TCP/IP详解》_卷一_ARP和RARP协议
2011-08-31 20:44 1289首先,我们要弄明白 ... -
《TCP/IP详解》_卷一_IP与路由的选择
2011-08-05 21:58 1405毫无疑问,IP是整个TCP/IP体系中最为重要的,也是最核心的 ... -
《TCP/IP详解》_卷一_链路层及其协议简述
2011-07-28 20:53 1662链路层,有时也被称为数据链路层或网络接口层。它是TCP/IP协 ... -
《TCP/IP详解》_卷一_TCP连接的正常建立与关闭
2011-07-26 21:23 1297TCP是一个面向连接的协议。这就意味着,通信双方之间有一个虚拟 ... -
《TCP/IP详解》_卷一_TCP简介和报文段结构简介
2011-07-26 20:08 3209此乃《TCP/IP详解》这本书的第一篇笔记。 关于概述 ... -
《深入Java虚拟机》_平台无关性与程序的最佳可移植性
2011-07-23 20:51 1854在前面的日志中, ... -
《深入Java虚拟机》_Java体系结构
2011-07-21 21:06 1351Java体系结构: 当程序员编写和运行一个J ... -
平台无关性——七个步骤保证程序的最佳可移植性
2011-05-27 00:43 8951、选择程序要运行的主机和设备的集合(你的“目标宿主机”) ... -
平台无关性——Java体系结构对平台无关性的支持
2011-05-27 00:42 872Java体系从四个方面对它的平台无关性进行了支持 1、Jav ...
相关推荐
《Thinking in Java》是Bruce Eckel的经典Java编程书籍,它为初学者和有经验的程序员提供了深入理解Java语言的全面指南。这本书强调了面向对象编程的概念,并通过丰富的实例来解释复杂的概念,使得学习过程更为直观...
10. **反射与注解**:Java反射允许在运行时检查类、接口、字段和方法的信息,而注解则提供了元数据的功能,这两部分在高级编程和框架开发中广泛应用。 11. **垃圾回收与内存管理**:Java的自动内存管理是其一大特点...
9. **反射机制**:使用Class类动态获取类信息,创建对象,调用方法,以及修改私有属性。 10. **枚举与注解**:枚举类型及其优势,注解的定义和使用,以及元注解的应用。 11. **设计模式**:可能涉及到单例模式、...
9. **反射和动态代理**:Java的反射机制允许我们在运行时检查类、接口、字段和方法的信息,甚至可以动态创建和操作对象。动态代理则可以在运行时创建代理类,用于实现AOP(面向切面编程)等高级功能。 10. **并发...
除了泛型的基本用法之外,《Thinking in Java》还探讨了更高级的主题,如反射机制,这是一种能够获取类的信息并动态创建对象的技术,在某些场景下非常有用。 ### 四、案例分析与实践 #### 1. 实战案例 书中包含多...
6. **反射机制**:Java的反射机制允许在运行时检查类的信息并动态调用方法,是实现元编程的重要工具。书中介绍了Class类、Constructor、Method和Field的使用。 7. **泛型**:泛型引入后,Java提高了代码的类型安全...
《Thinking in Java》是Java编程领域的一本经典著作,由Bruce Eckel撰写,深受程序员喜爱。这本书分为第三版和第四版,提供了英文版和中文版,适合不同语言背景的学习者。书中内容详实且深入,从基础知识到高级概念...
《Thinking in Java 3rd Edition中文版》是Java编程领域中一本备受推崇的教程,尤其适合初学者和有一定基础的开发者。这本书由Bruce Eckel撰写,深入浅出地讲解了Java语言的核心概念和技术,旨在帮助读者理解Java的...
8. **反射**:深入解析了Java反射机制,如何在运行时动态获取类信息、创建对象、调用方法等。 9. **枚举和注解**:解释了枚举类型和注解的用途,以及它们在实际编程中的应用。 10. **垃圾收集与内存管理**:分析了...
11. **反射(Reflection)**:Java的反射机制允许在运行时检查类的信息并动态调用方法,这在元编程和框架开发中非常有用。 12. **异常处理(Exception Handling)**:学习如何捕获和处理程序运行时可能出现的错误,...
《Thinking in Java》是Bruce Eckel的经典之作,它深度探讨了Java编程语言的各个方面,是许多Java程序员必备的参考书籍。这本书以其详尽的解释、丰富的示例和深入的理论见解,深受全球开发者喜爱。"java in Thinking...
《Thinking in Java 4th》是一本经典的Java编程教材,由Bruce Eckel撰写,被誉为学习Java的必备参考书。本书的第四版详细介绍了Java语言的核心概念和技术,旨在帮助读者深入理解Java编程思想,培养良好的编程习惯。...
8. **反射机制**:Java的反射机制允许在运行时检查类的信息,源码可能包含Class类的使用,动态创建对象和调用方法。 9. **设计模式**:作为高级主题,源码可能会实现一些常见的设计模式,如工厂模式、单例模式、...
除此之外,你还可以期待看到输入输出流、反射、注解、枚举、枚举类型、内省、动态代理等高级Java特性的源代码示例。这些内容将帮助你提升到更高级的Java编程水平。 总之,这份《Thinking in Java 4》的源码是一个...
《Thinking in Java》是Bruce Eckel的经典之作,第四版(thinkinjava4pdf)为许多Java程序员和学习者提供了深入理解这门语言的宝贵资源。这本书以其详尽的解释、丰富的示例和全面覆盖Java核心概念而著名。尽管标题...
6. **反射与注解**:通过反射,Java可以在运行时动态访问和修改类的信息,注解则为元数据提供了一种方式,使得编译器和运行时系统可以更好地理解代码。 7. **垃圾回收(GC)**:Java的自动内存管理是其一大特点,书中...
8. **反射**:Java的反射机制允许在运行时检查类、接口、字段和方法,以及动态调用方法,是Java灵活性的重要体现。 9. **垃圾回收和内存管理**:书中讨论了Java自动的内存管理机制,包括对象生命周期、引用类型和...
11. **反射**:Java反射机制允许在运行时动态获取类的信息,并能创建和调用类的对象,这在插件系统、框架开发等领域有广泛应用。 12. **注解(Annotation)**:注解是元数据的一种形式,可以为编译器和JVM提供额外...