2010年11月24日星期三

认识Java - 篇一

A
先暂且只涉及Java语言本身,来列举一下参考资料:
The Java Programming Language, Fourth Edition
The Java Language Specification, Third Edition
The Java Tutorial, Fourth Edition
Java SE 6 API Javadocs
其中后三者有在线版本可以获得
http://java.sun.com/docs/books/jls/index.html
http://download.oracle.com/javase/tutorial/
http://download.oracle.com/javase/6/docs/api/
不过下面的内容仅是以这里的第一本书为基础来写一些想法。
希望可以留下若干片段来从Java中品尝出一些有趣的东西。

B
Java是一种编程语言,同时也是一个平台。最初由Sun Microsystems的James Gosling开发的,于1995年发布第一个版本。整体看起来有点像C++和SmallTalk结合的感觉,是一种运行在虚拟机JVM上的静态类型语言。开发通过JDK,将源代码编译为平台无关的字节码。Java当初针对嵌入式而产生,重视移植性和安全性,目前是服务端应用开发最为广泛使用的平台。
保持着向后兼容性,Java现在的稳定是Java6,且已得到普遍的部署。从发展历史上说1.1/1.2/1.5是较重要的几个版本,由于其中新特性的引入使得代码的编写产生了新的习惯。本文将依照Java5的规范来说明,对旧的代码将不再提倡,具体细节会在下文中有所提及。

C
凭着自己之前对C++的感受,编译时间实在很痛苦。而在Eclipse的增量编译中,构建会随着代码的修改而自动完成。使得源代码保存之后,便可以立即直接执行。
关于编辑器,之前用到了Geany这个很平凡而使用的文本编辑器。话说初学应该拿语法高亮都没有的记事本的,可以减少干扰和依赖。然后用了Sun的NetBeans,其优点是开箱即用。通过它的GUI可以调用若干Java工具,同时补全的存在缓和了对类库的恐惧。目前还是转到了喜欢的Eclipse上来了,一个顺手的环境可以使代码的编写更为轻快。
Eclipse支持Emacs风格的按键,可以安装更多插件,并且有很好用的代码导航和重构功能。之前对C++和Python自己也把它视为终极IDE的,除非目标很小让它庞大的身躯的启动得累赘。对我这种菜水平,它似乎介意我:)。

D
从C++到Java,个人起初的动力仅是想多个额外的工具写算法方便一些,此外Java的庞大的社区也是一个吸引人的要素。这样的对语言平台的扩展还是一个很平滑的过程,从可预见的收益来说,虽然是盲目乐观了一些,但也可以说是差不多是值得的。
在TJPL一书中,除去开篇那段和TC++PL一样是K&R风格的概括性教程,内容是从OO开始的。先介绍了对象的使用和类的扩展,然后才转向表达式和控制流程。这和TC++PL正好顺序相反,C++是扮演着一个OO的推动者的角色,而在Java中已经把OO视为了理所当然的,或者说是本能的事情了。类库方面两者都是作为垫后内容来说。
虽然Java都C++都可以通过各自的标准库来做到很多的事情,不过Java的库要丰富得多。而像线程这一特性在Java中是直接从语言的层面考虑,融入了程序的执行流程之中。这使得在对Java的接触中看到一些对浅薄的C++知识来说很是新鲜的东西。
不过,C++的库也有很鲜明的特征,特别是在Boost中使用库的层面来扩充语法。它让代码显示出了魔幻的一面,使得多种范式的程序风格都可以在同一种语言中得到实现。自己接触了比较熟悉的库是Qt,它是一个GUI库,不过它也提供了语言层面的限定与扩展。其实Qt和Java有着相当大的共同点,自己一直是把Qt当作Java风格的C++来看待的。比如双方都是自Object而下的单继承结构,都有固定的getter/setter写法。由于Java的风格对其他现代语言有着无法回避的影响,这样来在接触Java的时候反而有种亲切之感。

E
本文的分节没有很明了的原则,完全是想到哪里写到哪里。觉得差不多是一个内容了,或者篇幅差不多了,就单独作为一个段落。
记得Java的书上说过要忘记C++,因为两者之间的差别会花费不必要的经历,而且还容易带来混淆。我对这句话是很认可,两者毕竟是类似却又不同的语言,在同一个时候只会凭借一种语言的角度在思考。不过有一个差别还是要提一下,那就是指针。C语言用户有嵌套指针的习惯,并且有时以此为乐。不过这种风格在结构良好的C++程序中并不常见,阅读起来便较为清晰。恩,这样来看Java也是属于代码较容易阅读的类型。
下面说一下学习和使用间的关系,像Java这样的带着庞大的库的语言说什么完完全全掌握是一件不现实的事情。对于熟悉一种工具而言,就涉及了成本和收益之间的关系。先再把C++提上舞台,它基于了C并把OO提到语言层面来。OO是一种思想,它有它组织程序的益处,在C中并不是不可以使用。但是对C的使用者而言,即使知道OO但不熟悉OO的话,它很可能不会去OO。而在C++中OO成为了语言的一部分,并有了一致的实现方式。这使得C++的使用者对OO有所学习,并在实际的项目中运用OO的益处。就像现在ROR让Web应用的MVC普及一样,因为它提供了易于接受的方式,并且让其他平台上也转入对同样模式的使用。
这里想说的是,学习是有目的,但也是有偏泛的。在现实生活中不是一种保守型的一对一关系,作为一个工具来说人们是使用他熟悉的东西来解决不熟悉的问题。本文虽然很散乱,但还是参照了很系统的书目来写的。依照着Java的风格使用他,而不仅仅是抽取着语言的部分。不会去像从C++中抽取了已知的C部分,然后便以此为全部地使用C++。也不会因为看到了其中新鲜的东西,而去忽略语言它自身成为的一个整体。这样,虽然说这里还只是在从语言层面上谈,一个从实际运用角度说还有欠缺的层面。不过毕竟,话题总得有的浅薄的开端吧。

F
先来说Stream,按字面意思就是流。单说这个字看起来有所费解,还是觉得英文单词的表意比单独的中文字要清晰一些。Stream是一个很形象的单词,它是对I/O的一种抽象方式。该项机制源自Unix系统,为输入输出、文件操作、用户交互、进程协作、网络通讯提供一致的界面。Java和C++一样,都试图在IO方面提供较C更多的抽象概念和工具,以方便统一地解决在流的使用中常见的问题。
Java中的流,按方向区分有输入流和输出流两种,按数据划分有字节流和字符流两种。其中以字节流为基础,提供了InputStream和OutputStream这两个界面。在使用Stream时我们通常关注的是界面能怎么用,而将具体类的实现方式隐藏在界面之下。这里说界面其实是不恰当的,虽然在使用时可以这样理解。因为InputStream和OutputStream实际上是抽象类,有两组对应的抽象方法叫read和write。这样做的获得的好处是,在实现自己的Stream类时只提供read(int)和write(int),而其他方法可以使用依赖于它们的默认实现。Stream类的其他方法暂不说明,其中有个要注意的是它的方法有阻塞与否的区别,这对使用流的方法的返回值有着不可忽略的影响。和字符相关的IO流是实现了Reader和Writer这两个抽象类,从抽象层次上说它们和Stream的两个抽象类是平行的。它们提供的方法在名称上相对应,并同样有着各自的不同用途的实现。其间的差异是,字符流在字符编码上是保持着统一的,这样便实现了与实际使用编码的无关性,而字符流是直接地反映了数据自身的。在字节流和字符流之间,是可以通过InputStreamReader和OutputStreamWriter来转化的。
这是因为Java中的流实现是可以通过序列的方式来使用的,以其他的流作为当前流的输入或者输出,以达到在使用某个具体的Stream时可以实现功能上的扩充。例如在输出时通过OutputStreamWriter使用OutputStream时,将对输出的字符进行编码,编码方式可以用字符串指定或者由系统默认。调用某一个流的flush或者close方法都会使得它说使用的流也被调用同样的方法,不然的话它所使用的流的read和write方法一般将只会在该流认为需要的时候被执行。在Java中还提供有Filter、Buffered、Piped、Print、LineNumber、SequenceInput、Pushback、Tokenizer,Data可作为中间流使用,他们可以最终使用ByteArray、CharArray、String、File或者其他Stream的实现作为实际IO的对象。这些中间流除了可以用来提供过滤功能外,也可提供比Stream抽象类更多的可调用方法,或者用来实现将Stream转化为不同的接口供不同的目的使用。例如对象持久化就是通过流来实现了对象的保存和传递机制。
顺便说一下,负责字符编码的包是java.nio.charset,通常是用在与外界的IO操作中。Java的char类型会一致地使用UTF-16编码,这样就在程序的内部逻辑上尽量避免编码问题了。

G
下面开始内容简略一些,既然本文的本来目的是一片流水帐,就让它更像流水帐一些吧。想到关于Java语言本身有趣的或者说是值得关注的东西记录下来一些,想来随着这段时间的记忆过去也可以发掘其中遗留下的些许价值吧。
Java是直接提供对线程的支持的,一个线程即一个Thread对象。我们的方法main是被默认创建的一个线程,它可以调用其他Thread对象的start方法,让线程对象的run方法可以和自己同时执行。也就是说他们中的任何一个方法陷入阻塞或者死循环,其他执行着的方法依然可以不受影响的运行。自己的run方法可以通过派生Thread类实现,或者在单继承的限制中去派生Runnable后用来构造Thread对象。
当同时运行的线程要使用同一个数据时,如果该项操作不是原子的,那么可能会造成互相依赖的一组操作之间混入其他进程的数据。这就需要在数据操作前对所操作的对象加锁,然后在操作完成之后释放。进程在操作加锁的数据时会产生阻塞,直到其他进程完成任务后,再继续执行自身的代码。
使用java.util.concurrent包里的对象来实现同步任务是可以的,不过较常用的是使用synchronized关键词。它可以作为关键词来修饰方法,那么对象方法会以对象自身作为锁,类方法会以它所在的类作为锁。当某锁因为因为某个进程的使用处于locked状态时,其他进程调用要求同样的锁的方法时就会发生阻塞,直到先前的方法调用完成。通常在Java的类中数据成员是私有的,然后通过公开的方法来使用数据。如果一个方法是非同步的,那么它运行的前后它说使用的数据将可能被同时执行的另一个自己或者同类的方法所修改,在一些场合这种不一致是会造成功能上的错误的。有时会需要获得一个成员的最新状态,则需要使用volatile修饰符。
除了用来修饰方法,synchronized也可以作为语句使用仅用来保护需要保护的代码。在一段同步的代码中,它可能因为某些条件不满足希望暂停执行。它可以释放锁让其他和它同步的方法可以被调用,并在适当的时候通知它可以尝试继续执行。为实现这样的模型,Java的在Object对象上为我们定义了wait和notifyAll方法。当一个线程在调用某方法遇到wait时,它会阻塞并释放锁,直到有其他进程调用了包含notifyAll的方法时再重新争取锁并执行。这样便实现了多线程调用同步方法当中,实现阻塞与否的移交以达成协作的目的。这也有些像把有待执行的代码放到一个方法列队中供其他方法调用,不过这里使用的锁的机制是让有待执行的方法阻塞,而其他的方法的任务仅仅是相当于通知阻塞的进程而不是这个列队中的方法的执行者。

H
为了编写数据结构的重用,Java提供了Collection框架。它和C++中的STL一样,定义了容器和迭代的概念。这样使得算法和结构之间建立了接口上的抽象,让同样的操作能够以同样的界面去用使用到它满足它应用的数据结构上。既可以直接使用Java提供的相关的实现,也为用户的扩充带来了方便。用户可以直接使用Collection实现自己的算法,也可以编写Collection类来执行已有的算法,或者以此为基础增加双方面的扩充。
在Java中对Collection的使用是从接口和实现两方面考虑,接口提供了容器的用法,而实现则提供了可实际工作的对象。和C++中运用泛型和模板来实现容器与迭代的不同,Java不是通过静态多态让某个因为提供了某些方法而能做怎样的使用那样的“窄”界面,而是从Collection这个能提供Iterator对象(通过iterator方法)的“肥”界面进行实现,并让不能实现的方法抛出UnsupportedOperationException异常。此外,出于对用途的限制并提供能多的方法,从Collection派生出的界面有Set(SortedSet)、Queue、List,以及方法名相同但无法直接派生的Map(SortedMap)。
对于容器的使用通常是通过容器的迭代器来使用的,它提供方法hasNext和next以遍历数据,还有一个可选实现的remove方法产生对容器的修改。其子界面ListIterator还提供了可选实现的set与add方法,以及Collection界面自身的add和remove(包括提供其他的冗余的函数)都可以用产生副作用。不过在一个迭代器使用容器的时候,该容器被其他的其他的方法修改会造成功能上的错误,Java有时会通过抛出异常来实现自我保护。由于容器实际上是储存了对象的引用,修改容器的成员和对某个成员的修改是不同的概念,有时为当前要操作的容器建立共有界面的副本也是种可行的做法。此外,有些容器需要对它所含的元素排序,它会要求其所含对象实现Comparable的compareTo方法,或者提供有compare方法的Comparator。String类出于不同的排序要求,对两种方式都有提供。
再具体一些来列举,界面Collection还提供有方法size、isEmpty、contains、toArray、~All,并确保可以迭代。子界面Set限定元素唯一(!equal),List增加的方法get、set、indexOf,Queue增加了增加peek、poll、offer了,Sorted的类型增加了first、last、subSet并限定了排序。对于容器的选择,需要从用途和实现两个方面考虑以获取更好的性能。Java中提供有实现HashSet、LinkedHashSet、treeSet、ArrayList、LinkedList、PriorityQueue、HashMap、LinkedHashMap、IdentityHashMap、WeakHashMap、treeMap。这些默认实现的名称已经表明的它们的工作方式了,如Linked使得修改变慢而遍历变快,没有多少选择的余地。不过在使用的时候,也就是为实现创建实例之后,依然是通过接口所提供的方法来进行操作。此外,和STL名称相同的若干类依然存在,它们是现有的Collection框架之前的实现,针对enum类型还有一组专门的容器。
对Collection创建副本,虽然它们有共有的元素,但已经是不同的容器对象了。而上面提及的subSet一类对Sorted方法皆适用的方法,而是为原容器创建一个视图,视图与容器之间会保持着同步的关系。Wrapper是另一种创建视图的方式,如Java提供有Unmodifiable和Checked。可能这些View在使用时并不常用,但确实是一种implement和extend之外定义类的使用的常用模式。

I
继续关于Collection框架。默认实现的算法在Collections和Arrays下面,如sort和binarySearch,以及其他一些如fill、copy、shuffle、reverseOrder、frequency等等有用的工具,外加提供一些特殊的如singleton、empty的容器对象。使用同步操作的话,可以使用SynchronizedWrapper,或者用java.util.concurrent里允许阻塞的特殊的列队实现。在实现自己的容器时通常从Abstract类派生而不是去直接实现接口,例如至少有size和iterator方法,一些方法会提供默认实现或抛出异常。
说到容器,就要来说一下泛型。C++的STL正如其名,正是在模板机制提供之后才被实现的。不过Java中情况特殊的是Collection是Java1.2的特性,而Generic是Java5中添加的内容。虽然现在通常容器和泛型总是同时使用的,但他们运行时是以不同的方式存在的。Java为了达到向后兼容的目的,对泛型采用了擦除机制。这也就是说在编译的时候,Java会为变量选择一个较抽象的类型,而在使用到具体的类型时插入Cast运算。例如对于没有指定类型参数的Collection容器,便是默认以Object类型为方法的参数。
Java有着比C++更为严格的类型机制,在使用着以类为可见性的同时,要求子类可以充当父类进行使用。这也就是说继承与实现不仅仅是一种机制上的,也是一种概念上的。它限定了子类若干行为,如不能缩小方法可见性,不能扩大抛出的异常,以使得这种严格的“is a”关系确保实现。旧Java中的Stack is a Vector便被认为是一个此方面的过失。
在泛型中,Java也同样维持着类的关系的存在。对于泛型类或泛型方法,从无类型参数的raw type,到提供了?、extends、super作为参数的类,到提供了具体的类提供的类,它们在概念上趋于严格。如果代码编译时存在对要求类型的范围有更严格的限定时,编译器会更具不同情况拒绝编译或者提示unchecked。我们称变量的类型是静态类型而对象的实际类型是动态类型,Java中的泛型就是一种在静态类型之中实现的机制,像“new List<String>[1]”这样直接依赖类型参数的语句由于擦除机制是不可行的。从使用上说,目前泛型是作为一种编译时的类型检查来看待的,仅仅主要运用在容器框架当中。

J
再顺便说一下Exception,也就是Java中的异常机制。在C++中异常是以一种对错误处理的抽象机制被添加到语言中的,它分离异常的产生者和异常的处理者使得代码更关注于自身的事情,也避免了返回值方式或全局变量方式易产生的疏忽。不过由于异常机制实现方式的差异,throw和try通常只在一个模块内部进行,也就是说在类库提交给外界的接口上异常是很难见到的。而在Java中,异常成为了程序逻辑的一个一个部分,和语言自身有着更加紧密的联系。
这里仅仅谈Exception,尽管他不是唯一Throwable的东西,它允许通过字符串的message来构造或者用initCause构造异常链。这里的重点是,Exception可以划分为RuntimeException和其他Exception两种,前者是unchecked的,不加捕获的Exception将会造成程序的终止。而后者,也就是checked的异常,是要求它所在的方法是写明throws它或者它的父类的,而调用的这样的方法也明确要求语句放在try块中不然不予编译。
在这里checked异常是作为程序的控制流程的一部分来看待的,也就是说一个函数的调用结果有两种。一种是正常结束返回它写明了所返回的类型,另一种结果则是抛出它写明的可能抛出的异常,它们都是方法运行允许的方式。
异常可以通过它所包含的信息来区分,也可以使用不同异常或建立异常间的继承关系。后者使得使得通过catch语句对不同的异常和某个异常的范围的判别变得简单而清晰。而finally语句中的代码会确保执行,即使有break和return也一样,常会和try前的某条语句配对使用,例如某个文件指针不为null便调用close方法。不过虽然异常也是一种程序的逻辑,但是在应该使用条件语句的时候,还是不要滥用异常。还有一个方便调试的assert语句,它不影响程序逻辑,默认未开启。

K
反射机制通过java.lang.reflect包使用,它为程序添加了在运行时使用类型信息的能力,例如实现一个简单的类型浏览器。不过反射作为一种机制提供,它只是被认为是可以用来做什么,除了权限外没有对它的行为有所限定。除非有明确目的,应该使用编译时代码中的Token来使用对象,而不是运行时的字符串来使用。
这里可以想到的两个反射的用法是动态载入和解析DSL,因为动态语言较依赖运行时,它们之间会见到一些相似的地方。
Java中为反射提供的类型有Class、Annotation、Modifier、Member(包括Field、Constructor、Method)、Arrays、Package,它们与语言中的类型直接对应,并提供了访问、修改和使用它们的方法,将一些语言层面的功能以方法调用的方式来实现。Class还有一个父类叫Type,它随着Java5泛型的添加包含了运行参数的信息。不过我们使用依然是以Class为基础开始的。我们可以使用对象的getClass方法、使用.class语法、通过Class.forName或者其他返回Class对象的操作来获得对象。
动态载入的目的是在编译时我们不能确定某个类时候存在,例如调用某个数据库后端。我们就需要使用类的名称作为字符串调用forName方法,它可能返回我们所需要的类也可能抛出ClassNotFoundException。通过获取类的Constructor对象,便能通过其newInstance方法来创建我们所需要的实例。如果连调用的方法也需要运行时确定时,可以通过字符串获得Method对象后调用其的invoke(this)方法。获得Annotation实例,或者根据名称找到可修改的方法也是可以的。关于Class的类型转换,有asSubclass和cast,可能会抛出ClassCastException。例如有语句“Class cls = Class.forName("java.lang.String").asSubclass(String.class);”,并有一个叫getName的方法与之对应。
其他方面的,Java持久化里用反射来调用私有方法,还有InvocationHandler和ClassLoader两个在特定场合很有应用价值的类。不过日常里和反射机制本身一样都不算常见,自己不大熟悉。再由于相关文档都有很详尽的说明,这里就暂且略过。

L
Java在内存管理上使用了垃圾收集机制,在JVM认为有必要的时候,它会自动执行System.gc()清理不再使用的对象。对象可以定义它的finalize方法供gc时执行,不过如果在程序退出前没有gc的话,这段代码也不会被运行到。这里使用这个概念会对Reference对象有特别的差异,如用于缓存或内存原因的动态加载。
对于单线程程序,main方法结束,整个程序也就结束了,并返回状态0。对于多线程程序,可以向其他线程发送interrupt消息,使得那个线程可以通过interrupted方法以及InterruptedException来接受到这个消息,来自行退出。不然Java会一直运行直到所有的用户进程运行完毕,除非某些进程setDaemon(true)或者System.exit被调用。其中后者也是Java中用来返回状态码的方式,而不同于C中int(main)。TimerTask都属于守护进程。在程序因为自身或外界的因素被要求退出时,有短暂的时间可以执行ShutdownHooks。还有另一种意外地使程序运行时退出的是运行时异常,由main或者run抛出而不会被try语句保护,此时可以为线程提供UncaughtExceptionHandler对象。出于调试目的,常使用异常的printStackTrace方法。

M
Java中的原始类型有boolean、char、byte、short、int、long、float、double,并将它们包装为Boolean、Character、Number(Byte、Short、Integer、Long、Double)、Void,并可以在使用时自动于两者之间转化。这些包装后的方法除了继承自Object的toString用于类型转化equals用于比较,还提供有果然类型相关的静态方法如valueOf用于构建parseType解析(两者是对应的)字符串,以及对象方法compareTo用于比较typeValue转回原生类型。一些基本的类型相关的方法,也由Number或Character类的静态方法提供。数学函数则由java.math,也包括高精足算法,浮点晕眩有实现无关的Strict模式。随机数用Math.random。时间相关的除了System.currentTimeMillis返回long,其他属于Date和Calendar对象,非原生类型。还有一个BitSet比位移操作使用起来直观一些。
Java字符串用String类,表示一个不可修改的字符序列。它提供的方法有charAt、length、subSequence、indexOf、startsWith、intern、replace、trim、toLowerCase、getBytes(这个涉及编码转换)。此外还有replaceFirst、split为使用正则表达式的提供了简便的方法。正则表达式由包java.util.regex提供,是一个风格类似于perl5的实现。其中包含Pattern和Pattern两个概念,前者用来compile表达式并对CharSequence使用split或matcher,后者提供matches、lookingAt、find并使用自身保存匹配的状态,其状态通过返回值以及start、end、group获得。正则表达式比写一串字符串相关算法要显得简单而清晰。
结合IO很实用的类有Formatter和Scanner,前者用来格式化输出,在printf中会自动调用,后者的功能正好相对应,通过迭代器风格的接口使用。自己合成字符串的话建议用StringBuilder,原先还有一个线程安全的StringBuffer。不同编码会统一以UTF-16的codePoint来实现,它是一个用于char的int类型值,也用于字符流。

N
javadoc是Java中用来从源码生成的文档的工具,清晰的文档可以便于理解代码的结构,也使得代码的复用的接口有了清晰的说明。
文档注释通过/** doc comment */插在所要标注的元素之前,每行以*开始,使用HTML语法。注释其中的第一段用于表示总述,后跟多个空格表示风格。多余空缺的注释,会默认按照一定的规则继承其父类的描述。还可以使用的元素有:@see、{@link}、{@linkPlain}、@param p、@return、@throws E、@exception E、@deprecated、@author、@version、@since、{@literal text}、{@code text}、{@value field}、@serial、@serialField、@serialData、{@inheritDoc}、{@docRoot}。方法名和类名之间用#分割。包的翁、当写在package-info.java中。
Annotation虽然也是多代码元素的一种描述,但常用的有CLASS和RUNTIME的区分。后者在运行时可以通过反射获得,获得一个@interface的实例。系统还提供了@Target、@Retention、@Deprecated、@Documented、@Inherited、@Override、@SuppressWarnings。例如使用@Override修饰的方法会在编译是检查没有应为参数的错误而对父类方法进行了overload。对Package也同样适用。

O
最后来罗列一些零散的内容,不是说它们不重要或没有值得说的东西,只是在这里不做展开了。
java.util中有Observer/Observable类型用于实现观察者模式。这是设计模式中常见的一种,通过维护一个关联对象的列表,用于对象间的协作。所以用派生是一种让双反接口相协调的办法。当然,模式只是一种类之间的结构方式,它在实现方式上并不唯一。
系统变量的获得可以使用例如System.getProperty("user.home");之类的语句。通过调用Runtime.exec可以获得一个Processes对象,然后以流的方式可以进行通讯。如果是使用Java类的话,可以通过java.security限定权限以保障安全。
使用ResourceBundle.getBundle可以载入ListResourceBundle对象或者.properties文件里描述的资源,不同语言的版本可以实现派生关系对通用版本进行修改。不同的语言版本会自动根据环境进行选择,这是Java中程序实现i18n的方式。
JavaBeans是Java中的一个复用组件,可实现BeanInfo或用反射生成。通过get和set使用一个serializable的有默认构造器的类。
嵌套类从Java1.1开始运行,不过编译器会用$分割符把它抹平。静态的嵌套类问题不大,仅相当于有了一个命名空间,以及一些访问权限上的规则。不然的话这个类存在于与外部类中或者存在于方法中或者是一个匿名类的话,它的实例需要保持一个对外部类示例的引用。这会用到如this.new这样不常见的语法,内部实例可以访问它外部示例的成员。
枚举Enum也是Java5新增的,它源自C语言。原本在Java是通过final static来实现枚举作用的,现在有了语法糖衣之后依然是这样实现的。和C一样用枚举表示数字当然可以,不过原本Java中还有一个用法习惯是枚举的成员是不同的对象,每个枚举元素可以有它的属性和方法。所以现在Enum也是作为一个抽象类使用,枚举元素可以利用方便的语言构造或者派生。
Java6里增加的特性让我印象深刻的是自带了JavaScript的支持,它可以和Java代码之间互相使用。JS作为一门标准化后的语言,这里用的是Mozilla的实现。动态语言的好处是类型属于值而变量不需要类型,类的接口是出于概念上约定而不必复杂的层次关系。
文件操作除了File***Stream,还有File类和RandomAccessFile类,用途和接口上更加传统一些。

P
内容还有一些其他可以说的,例如上面有没提到或者未提详细的地方,不过从品尝的想法和时间的允许的角度说,也只能挑选着内容来说了。下面这本书的目录会是一个更详细的提示列表,是关于Java的各种特性方面的:http://www.amazon.com/Java-TM-Programming-Language-4th/dp/0321349806 。
以后会后篇二,不过话题会有所不同,将谈到Java语言自身之外的更多的东西。那啥啥啥的,这篇的文字呢也就一个通常长度,当废话看,当游泳池看什么的都是可以的。
真的就到这行结束了,本文的下一行就真的什么都没有了,至少传说中的最后一行就是这一行了。
晚安!

没有评论: