还未进行初始化就使用了这些内存(很常见的编程错误),那么很有可能会 发生一些无法预料的后果。 安全性是Java最看重的目标之一,因此许多困扰C和C++程序员的问 题在Java里面已经不复存在了。数组肯定会被初始化,而要想访问数组 以外的内存也已经不可能了。边界检查的代价就是,每个数组都会多占用 些内存,而且程序运行的时候也会有些开销。不过设计者们相信,与安 全性的增强和编程效率的提升相比,这点代价值。 当你创建对象数组的时候,实际上你是在创建 reference的数组。这些 reference会被自动地初始化为一个特殊的值:null。这是个关键词,意 思就是“没有”。Java一看到n山l就知道这个 reference没有指向任 何对象。使用 reference之前,你必须用对象对它进行赋值,如果你试 图用一个仍然是nu的 reference,那么程序运行的时候就会报错。由 此,Java防止了许多常见的数组错误。 你可以创建 primitive的数组。编译器也会进行初始化,因为它会把数组 的内存全部清零 以后章节会更详细的讲解数组。 你永远不需要清理对象 在绝大多数编程语言中,要弄清变量生命周期的概念都会耗费大量的精 力。变量要生存多长时间?如果要由你来进行清理,那么应该在什么时候 进行清理?在变量生命周期方面含糊其辞,会导致很多bug。这一章会告 诉你,Java是怎样通过接管所有的清理工作来大大简化这个问题的 作用域 绝大多数过程语言都有“作用域( scope)”的概念。它决定了在这个作用 域里定义的变量的可见性与生命周期。在C,C++和]ava中,作用域是 由花括号{}的位置决定。所以,假如 int x =12 // Only x available tq=96 / Both x &g available / Only x available // g" out of scope" 在作用域中定义的变量只能用到这个作用域的结尾。 第6页共26页
shhgs@wgqqh.com 6 ✁ ✂ 26 ✁ ë%&f^ç, Ì{(· (áâ )YZmÝ;º ì03ÌÜ?(Å c# Java b<(au3æò0V C C+ + âFQ(² F Java ˶ôõX6{F,OHOº¹f^à%|±²OH E½({[ôõXÝ;,?(¸ÍL×OHƺV 3Ì{àâF&(M[ºmÌX^ "> c#(áâØ·() >8 C¸ÍÕ ¡~¥OH(Mñ`J¡F~ reference (OH Ì reference º¹ý rf^3×1\(Õnu l l ׳¹ ÍmÏJava 3<g nu l l½t × reference mZ ¥ç reference u¡'(¥¥A%&!ÕÄÅ¡" #3×Û nu l l( referenceYZâF&(Mº$, òJava %+,0V· (OH ¡ÝE~ p ri m iti ve (OHáf[º%&f^æAºPOH ({c&' Eûߺ((©£OH F)ÂVOáâ*%*&5ª0»¼@(Ö+ƺ,-ª(A ã5ª%0{VÊ4Ê ÄÅ%,¡Q%&&¢YZvwFöZM %&&¢Ê F5ª0»¼@µ¶./§0º1_V b u g 3ûº# ¡Java Õ¦^UÂDm(&¢y1QÂÂ]^ ײ( )ÂVO^âÆmÍ12 (scope)Ï(Ö+ANO,F ×1 2ËO3(5ª(Ý #0»¼@F CC+ + Java *12 ,/{ }(§¨NODE9Ä { int x = 12; // Only x available { int q = 96; // Both x & q available } // Only x available // q “out of scope” } F12*O3(5ªó;g ×12(45
"之后的这行文字都是注释。 缩进使得Java的代码更易读。由于Java是一种形式自由的语言,因此 额外的空格,跳格以及回车不会对程序的结构造成任何影响。 注意,虽然在C和C++里这种写法是完全合法的,但是你不能在Java 里这么写: int x=96:// Illegal 编译器会说变量x已经定义过了。于是C和C++的,在更大的作用域里 “隐藏”变量的能力,就被Java给禁了。因为Java的设计者们认为这 会导致令人费解的程序。 对象的作用域 Java对象的生命周期同 primitive的不同。当你用new创建Java对象 之后,它会晃荡到作用域外面。如果你写: string s= new string("a string") 1// End of scope s这个 reference会在作用域结束之后消失。但是s所指的那个 String 仍然还占着内存。在这段代码里,你已经没法访问这个对象了,因为唯 指向它那个的 reference现在已经出了作用域。在后面的章节中你还会 看到,在程序的运行过程中,它是怎样传递和复制对象的 reference 的 只要你还用得着,那些用new创建的对象就不会跑开。看来用了这种方 法之后,C++里面的一大堆问题在Java里面已经不复存在了。C++编 程中最难的问题就是,等你要用对象的时候,你根本没法从语言中获得这 个对象还在不在的信息。而且更重要的是,用C++编程,你得亲自进行 对象的清理。 这就带来了一个有趣的问题。如果java就这样只管造对象,不管清理对 象,那么又是谁在那里阳止对象填满内存进而把程序给搞宕的呢?这确实 是C++所面临的问题。但这里就有点魔法了。Java有一个垃圾回收器 ( gabage collector),它会看着所有用new创建的对象,并且还会知道 第7页共26页
shhgs@wgqqh.com 7 ✁ ✂ 26 ✁ ‘//’ u( & Ƶ 6%ç Java (¸S,) Java 37´ý,(æò 8½(´è9èE:;<Xº¥âF(4òÚ ¹F C C+ + Ë «c(¡F Java Ë Z« { int x = 12; { int x = 96; // Illegal } } áfº$5ª x ôõO3^,) C C+ + (FÂ(12Ë Í=>Ï5ª(;ã¹ Java ¬?,æ Java ( "Ù º1_@ª-£(âF Java ¥(0»¼@ p ri m iti ve (X ¡ new ~ Java ¥ uAºABg12½¶ÄÅ¡« { String s = new String("a string"); } // End of scope s × reference ºF124Cu6 s DZ(Y× String Û.{F øˡôõ±² ×¥,æ3 ZAY×( reference íFôõ!,12F¶(ûß*¡º <gFâF(&^â*AÕ¦DE6¥( reference ( ó%¡.YÌ new ~(¥XºF<Q, µ uC+ + ˶(3ÂÆ²F Java ˶ôõX6{F,C+ + á â*bU(²-¡%¥(M¡G *H ×¥FXF(à%( C+ + áâ¡Iý%& ¥(&¢ dQ,3×mJ(²ÄÅ Java ¦óÂÚ¥XÂ&¢¥ YZüKFYËL+¥MN{%àPâF¬O(VÊ ñ C+ + D¶P(² ËmCQ,Java m3×RS;Àf (gabage collector)Aº<.Dm new ~(¥º½t
其中的哪些已经没有 reference指着了。然后它会释放那些没有 reference指着的对象所占据的内存,这样内存就能被新的对象用了。这 就是说你永远也不必为重新申请内存而操心。你只要创建对象就行了,用 完之后它们自会离开。这样就彻底解决了因程序员忘了释放内存而产生 的,所谓“内存泄漏( memory leak)”的编程问题了。 创建新的数据类型:类 如果万物皆对象,那么特定类型的对象的特征和行为又是由什么决定的 呢?换言之,类是由什么组成的?或许你会想应该有一个叫“type”的 关键词,当然这种想法很有道理。然而长久以来,绝大多数的面向对象的 语言都用了 class这个关键词,它的意思是“现在我要告诉你这种新类 型的对象都有些什么特征。” class关键词(这个词太常用了,所以本书 的后面部分就不再用黑体字表示了)后面跟着新类型的名字。例如 class ATypeName i / Class body goes here */ 尽管类的正文部分只是一段注释(星斜杠以及在它们中间的东西,后面会 讲到的),但是这已经是一种新的类型了。虽然没什么大用,但是你可以 用new来创建这种类型的对象: ATypeName a new ATypeName ( 但是除非你为它定义一些方法,否则你没法让它去作更多的事情(也就 是,你不能向它发送任何有意义的消息)。 数据成员与方法 你定义类的时候(ava编程要做的就是定义类,创建这些类的对象, 然后再向这些对象发送消息),你可以往类里放两种元素:数据(feld, 有时也被称为数据成员),以及方法( method,有时也被称为成员函数 member function)。数据可以是任何类型的,能通过 reference进行 操控的对象。它也可以是任何一种 primitive数据(这时它就不是 reference了)。如果它是一个对象的 reference,那么你就必须用一种 被称为构造函数( constructor会在第4章详细讲述)的特殊方法对它进行 初始化,这样才能确保将它连上一个真实的对象(用new,就像前面看到 的)。如果这是个 primitive数据,那么你可以在定义的时候直接对它进 行初始化。(过一会就能看到, reference也可以在定义时进行初始化。) 每个对象都会保存它自己的数据成员;数据成员不能共享。下面就是一个 带数据成员的类 第8页共26页
shhgs@wgqqh.com 8 ✁ ✂ 26 ✁ §*(úÌôõm reference Z.,AºµkYÌm reference Z.(¥DP({ ¦{;¹(¥, $¡TU[X'V{àMq¡ó%~¥&, uA"ýºW ¦Xh£N,æâFQY,µk{àZ0 (D[Í{\](m em ory l eak)Ï(áâ², ÄÅ^_`¥YZ1O(¥(1a&ü,öZNO( VÊ lu,öZH(Ê Y0¡º|vwm3×b ÍtypeÏ( ³ |mt¢àÊcEQ)ÂVO(¶¥( Æ, class ׳A(¹ÍíF%#¡ (¥ÆmÌöZ1aÏclass ³ ( ׳ɷ,DE (¶XÁdWX,)¶e.(ÑÄ class ATypeName { /* Class body goes here */ } ÁÂ(T ó3õ (fghE:FA"*4(34¶º ©g() ôõ3(,öZ¡ÝE new Q~ (¥ ATypeName a = new ATypeName(); @¯¡AO33̵ià¡ÇAr1V(([ ¡X;Aìm¹3( ) ¡O3(M (Java áâ%T(O3~ Ì(¥ Á Ì¥ì )¡ÝEËkeÓÔOP (field m[¹jOPQ )E:µ (methodm[¹jQO member function)OPÝE(;^ reference %& MN(¥A[ÝE3 p ri m iti ve OP ( AX reference ,)ÄÅA3×¥( referenceYZ¡'(3 ¹jòÚO(constructor ºF 4 û(©)(1\µ¥A%& f^ ¦k;|AkJ3×lñ(¥( new}¶<g ()ÄÅ × p ri m iti ve OPYZ¡ÝEFO3(MÒU¥A% &f^(^3º;<greference [ÝEFO3%&f^) Lץƺ|{Aý þ(OPQ OPQX;mn¶3× dOPQ(
class Dataonly t float f boolean b 这个类做不了任何事情,但是你可以创建这样一个对象 Dataonly d= new Dataonly( 你可以为这些数据成员赋值,但是首先你必须得知道该如何引用对象的数 据成员。你必须先声明对象的 reference的名字,然后加一个“点”, 后面再跟上对象成员的名字: objectReference. member 例如: d f 11f;// 'f after number indicates float constant db= false 此外,对象里面还会包含一些可能会需要修改的其它对象。这么作,只要 接着“往下点”就行了。就像这样: my Plane. leftTank capacity =100 Dataonly类除了保存数据,其它什么都干不了,因为它没带方法。要 想理解方法是怎样工作的,你必须先理解参数( arguerηt)和返回值 ( return value)的概念。这两个概念,我们过一会就讲。 primitive成员的缺省值 当 primitive数据成为类的成员时,即便你不进行初始化,它也会得到 个默认的值: Primitive类型「缺省值 boolean False 第9页共26页
shhgs@wgqqh.com 9 ✁ ✂ 26 ✁ class DataOnly { int i; float f; boolean b; } ×TX,¡ÝE~ ¦3×¥ DataOnly d = new DataOnly(); ¡ÝE ÌOPQ!Õ~/¡'(½twÄ¥(O PQ¡'(/nÒ¥( reference (Ñä3×ÍCÏ ¶ÁeJ¥Q(Ñ objectReference.member Ä d.i = 47; d.f = 1.1f; // ‘f’ after number indicates float constant d.b = false; ò½¥Ë¶º..3ÌÝ;ºî%ïð(§A¥ Z1ó% U.ÍCÏ&,} ¦ myPlane.leftTank.capacity = 100; DataOnl y @,|{OP§AöZÆoX,æAdµ% |¢£µÕ¦y1(¡'(/¢£O(argument)p;Õ (return value)(Ö+ e×Ö+"^3º© prim itive p ri m iti ve OP(Qxy¡X%&f^A[ºg3 ×qÙ(Õ Prim itive bool ean Fal se