本篇文章给大家谈谈怎么看懂java代码电子书下载,以及怎么看懂java代码电子书下载的文件对应的知识点,希望对各位有所帮助,不要忘了收藏本站喔。
本文目录一览:
求一份完整的java自学学习方法?
java自学网Java从入门到精通(第3版)PDF电子书.zip 免费下载
链接:
提取码: wut5
Java是一门面向对象的编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程。
怎么读懂JAVA和C++代码??
简介
我听很多人和我说他们擅长C++或Java但是完全搞不懂Smalltalk。按他们的说法Smalltalk有若天书!我想了一下,觉得他们说的或许非常在理。假如我只懂Java,如果我从多年以来写的代码里随便挑一段我肯定看不懂它。在理解Smalltalk之前必须要澄清一些很简单的概念连带一些细微诡异的语法概念。要是“老王不懂Smalltalk”,也许我能对他的状况进行改善。我希望能让读者快速上手。我假设读者懂面向对象编程。如果你已经会Smalltalk了就请恕我班门弄斧一下。
噢词法细节真简单
初读 Smalltalk遇见的一些协定和惯用法细节可能与其它语言大相径庭从而把你搞晕,像双引号括注释,单引号括字符串,还有字符的特殊语法表示(例:$x代表“x”)。还有symbol的概念,symbol是在内存中仅有唯一实例的字符串;例如,当一个symbol被构造时(通常是编译期),先从内存里查找是否相同的实例,如果有则直接使用。这样做目的不是节省内存而是优化比较效率(下文详述):
"this is a comment"
'this is a string'
#'this is a symbol'
#thisIsASymbolToo
赋值和比较运算符有细微差别:
:= // 赋值
= // 内容相等比较,深比较
== // 唯一性比较,浅比较
如果你给我两个被不同变量“a”和“b”引用的不同对象,我就能告诉你它们是不是相同对象(通过a == b进行)或者只是看起来相同的不同对象(通过a = b进行)。直白的说,==比较两个指针而=比较对象的整个内容。
Smalltalk中很少出现逗号,因为它不充当语法要素。这就是为什么数组直接明了,例如下面没有冗余逗号的数组:
#(1 2 3 4 5)
尽管如此逗号还是有意义的,它是一个运算符。你偶尔能看到它被用来连接两个字符串,例如:
'string1','string2'
关键字无孔不入
在Smalltalk中关键字无处不在。但它们有益于可读性而不是扰乱。想知道为什么,让我们从一个C++和Java片断入手。例如你可能对下面的写法再熟悉不过了:
t-rotate(a, v); // C++
t.rotate(a, v); // Java
t对象被夹带着参数a和v发送了rotate消息。读者想理解这样的代码通常需要找到变量的声明处并判断出类型。我们假定声明如下:
Transformation t;
float a;
Vector v;
在Smalltalk中变量可以引用任意类型的对象。所以类型说明不需要,但是我们还是要声明一下变量,例如:
|t a v|
在不看声明的情况下,好的Smalltalk程序员会通过变量顾名思义判断其类型。那么我们换一种写法如下:
|aTransformation angle aVector|
但请允许我继续沿用最初的短命名来避免示例代码太长影响阅读。我们来通过去除不必要的因素来“改进”C++和Java的语法。例如,下面的代码仍然明了:
t.rotate(a, v); // 原始写法
t rotate(a, v); // 谁需要点号?
t rotate a, v; // 谁需要括号?
为了进一步改进语法我们需要知道参数a和v代表什么。我们假定整个示例意为“绕向量v旋转角度a(译注:rotate by angle a around vector v)”。则进一步改进为:
t rotate by a around v; // 谁需要逗号?
我们能明确每个成分是什么吗?没问题,因为在我们改进的这个示例中,“t”是一个变量,“rotate”是方法名,“by”是分隔符,“a”是变量,“around”是分隔符,最后的“v”也是一个变量。为了消除潜在歧义我们设立一个规定:分隔符后面紧跟一个冒号。我们得到:
t rotate by: a around: v; // 谁需要模棱两可?
最后我们强调一下分隔符是方法名的一部分;例如我们假定需要一个形如“rotate by: around:”的函数,去掉空格我们得到“rotateby: around”作为最终命名,再将非首单词首字母大写来提高可读性得到“rotateBy: around”。那么我们的示例可以写为:
t rotateBy: a around: v // 这才是Smalltalk
方法名被打碎成几部分。幸运的是聚拢这些碎片成一个完整的名字很容易。当在类中时我们如下定义方法名:
self rotateBy: angle around: vector
|result|
result := COMPUTE ANSWER.
^result
在运行时,“t”和“self”,“a”和“angle”,“v”和“vector”之间有着一对一的关系。注意“^”意味着结果被返回了;这是 Smalltalk中“return”关键字的写法。变量“self”是“this”的同意字,如果方法结束没有返回语句则“^self”被当作隐含语句执行;你可能完结一个方法时忘记添加返回语句,但没事。这也意味着即使消息发送者不需要返回值,方法也会返回它。
实际上被惯用的地道Smalltalk语法要求“self”不显式的出现在方法头(但必隐含),例如:
rotateBy: angle around: vector
|result|
result := COMPUTE ANSWER.
^result
关键字语法的精妙之处在于我们可以为不同的方法定义不同的关键字。例如我们可以如下定义第二个方法:
t rotateAround: vector by: angle
不必死记硬背参数顺序。关键字提示我们顺序。当然程序员有滥用关键字的能力,例如如果我们如下定义关键字:
t rotate: angle and: vector
读者很难弄清参数正确的顺序。这就是个极差的编程风格,如果只有一个参数还好办。只有一个参数时我们仍然需要方法名;例如:
t rotateAroundXBy: angle
t rotateAroundYBy: angle
我们希望关键字(因冒号而易区分)成为参数的说明。但方法没有参数时怎么办:
t makeIdentity: // 结尾的冒号有意义吗?
如果关键字代表参数的说明,那我们在没有参数的情况就用不到关键字。所以零参数的消息应为:
t makeIdentity // 这才是Smalltalk
当然二元操作符同理,但一元操作符(makeIdentity是一元消息但不是一元操作符)并非如此。当多种消息一起出现时我们的表达式也许形如:
a negative | (b between: c and: d)
ifTrue: [a := c negated]
作为读者应该知道“a”被发送了一个返回true或者false的名为“negative”(零参数)的消息;“b”也被发送了一个返回true或者 false的为“between: c and: d”的消息。两项的结果or到一起成为消息“ifTrue: [a := c negated]”的接收者。这就是if-then控制结构的地道写法而不是特殊语法。仅是以布尔值作为接收者,以“ifTrue”作为关键字,并且以“[a := c negated]”(我们称其block)作为参数的标准关键字语法。在Smalltalk中你永远遇不到“a := -c”因为不存在一元操作符,但你会看到“-5”这种常量,“-”在此充当常量的一部分。
所以如果你看到形如“-3.5 negated truncated factorial”的表达式时应该立即意识到这其中没有关键字。所以“-3.5”必定是被发送了“negated”消息;执行结果3.5被发送了“truncated”消息;然后执行结果3被发送了“factorial”,产生最终结果6。
当然,还有诸如运算优先级的规则(从左到右),消息优先级(零参数最高,二元运算次之,最后关键字)。写代码时这些很重要,但读代码不必刻意在意这些细节。如下从左到右的表达式:
1 + 2 * 3 得 9
没有优先级,但是你很难遇到有Smalltalk程序员这么写表达式,因为这会迷惑非Smalltalk读者。一般Smalltalk程序员使用如下替代写法:
(1 + 2) * 3
即使括号是没必要的。
分号和句号不同
大多数非Smalltalk程序员把分号当成语句结束的标识,但在Smalltalk中使用句号表达此意。所以我们不会写:
account deposit: 100 dollars;
collection add: transformation;
而是写成:
account deposit: 100 dollars.
collection add: transformation.
嗯!“dollars” 消息迷惑你了吗?不要觉得不可思议。此处必有一个在Integer类中构造一个“Dollar”对象并返回它的名为“dollars”的方法。它不是 Smalltalk标准环境中的但是我们可以扩充它。基(内建)类可以在需要时像用户自定义类那样被扩充。
所以,句号是语句终止符号而且在最后一条语句中是可选的(如果你愿意,可以把它当成语句终止符)。但分号仍然是合法的特殊分隔符(不是终止符)。它被用来指定接收者是可缩略的。所以,如下写法:
|p|
p := Client new.
p name: 'Jack'.
p age: 32.
p address: 'Earth'.
可以写为:
|p|
p := Client new.
p
name: 'Jack';
age: 32;
address: 'Earth'.
或者更好的写法:
|p|
p := Client new
name: 'Jack';
age: 32;
address: 'Earth'.
Smalltalk对排版不敏感。我们甚至可以把所有语句放到同一行中。本质上讲分号指定前一个消息被发送用来修改接收者,并且下一个消息应当被发送给相同的接收者(而不是被发送到被忽略抛弃的运算结果)。
最后的例子中,“new”被发送给一个类以获得实例(运算结果)。然后“name: 'Jack'”被发送给那个实例。第一个分号指定“name: 'Jack'”的结果被忽略,“age: 32”应被发送给之前的接收者(相同的那个实例)。第二个分号指定“age: 32”的结果被忽略,“address: 'Earth'”应被发送给之前的接收者(仍然是那个相同的实例)。最后“address: 'Earth'”的运算结果被赋值给p。修改接收者的方法通常返回接收者本身,所以p被绑定到最近修改的Client实例上。
我们可以通过用英文词组“AND ALSO”取代分号来简化上面的赋值。即“new”被发送给类“Client”,并且结果实例被发送了“name: 'Jack'” AND ALSO “age: 32” AND ALSO “address: 'Earth'”消息。重复的向相同接收者发送不同的消息在Smalltalk中被称为层叠(译注:cascading)。分号也可以出现在子表达式中,如“p := (Client new name: 'Jack'; age: 32; address: 'Earth')”——注意圆括号。
Get/Set方法与变量实例同名
在Smalltalk中诸如name,age,address这样的Client类实例的成员变量都是private的。但可以通过实现一定的方法访问它们。例如在C++(Java类似)中,我们经常写出如下访问方法(通常被称为get/set方法):
long getAge() { return age; }
void setAge(long newAge) { age = newAge; }
如果你在大把的类上应用这种方式,你将写出成百个以get和set开头的消息。如果你偶然决定通过使用精简的命名来简化这些方法(稍后写出),即便Java编译器能做出正确识别,也会给C++编译器造成解析混乱,因为它无法区分变量和方法:
long age() { return age; }
void age(long newAge) { age = newAge; }
你能区分变量“age”和消息“age”吗?理应区分。当你使用消息时需要带上圆括号如“age()或age(22)”;当你使用变量时就不必带上圆括号。Smalltalk中的等价写法为:
age ^age
age: newAge age := newAge
我们通常使用如下分行写法来提高可读性:
age
^age
age: newAge
age := newAge
在Smalltalk中你不必依赖圆括号就能轻松区分变量和消息。如果你对它们的区别很明了,就能看出下面的表达式有多少个变量:
age age age: age age + age age
嗯!答案是3个;第一个和第四个age必为变量(紧跟关键字的子表达式和所有表达式必以一个变量开头),第七个也必为变量(二元操作符后的子表达式也必以一个变量开头)。再看一个更明显的类似的典型例子:
name size // name必为变量
self name // name必为变量
广泛使用的集合
在Smalltalk中使用最普遍的两种集合分别是有序集合(ordered collection)和字典(dictionary)。数组的概念等效于大小不可变的有序集合。
|a b|
a := OrderedCollection new
add: #red;
add: #green;
yourself.
b := Dictionary new
at: #red put: #rouge;
at: #green put: #vert;
yourself.
上面的每个赋值中变量都被绑定到最后一条消息的执行结果上;例如“yourself”的结果就是最后一次创建的集合。“yourself”消息被设计成返回消息接收者(像个无运算操作)但“add:”和“at: put:”并非如此(它们返回最后一个参数)。所以如果没有“yourself”就成了“a”绑定到“#green”,“b”绑定到“#vert”。
我故意使用层叠写法来解释“yourself”为什么独立的出现在内建类的方法中。
Smalltalk中集合的优势是你可以存任意类型的对象进去。即使是字典中的键都可以使任意类型;同一集合中的对象也可以是不同类型。我们不必为了在一个实例中聚集一批新的类型而重新发明新的集合类型。
可以像访问数组一样访问有序集合;例如“a at: 1”索引到元素1。字典也能用相同的方式访问;例如“b at: #red”。但很多应用场合我们不必关心键。如此这般,元素迭代循环很容易:
a do: [:item |
USE item FOR SOMETHING].
b do: [:item |
USE item FOR SOMETHING].
即便集合中的元素是不同类型的,“item”变量也会一个接一个的获取到每一个元素。如果需要我们能在运行时知道一个对象是什么可以写成“item isKindOf: Boat”,它返回true或false。同时还有许多特殊类型查询消息,像“item isCollection”或“item isNumber”。更进一步还有很多创建新的集合的循环构造消息如:
c := clients select: [:client | client netWorth 500000].
d := clients collect: [:client | client name].
上例中前者我们得到大款客户的集合。后者我们获得客户名字的集合(原始集合是一堆客户的集合)。
有序抽象无需新类的构建
读者经常看到如下代码:
stuff value: x value: y value: z
此处关键字全是“value:”。对一个非Smalltalk程序员来说这样写毫无意义混乱不堪。程序员在此已经(而且经常)创建新的抽象。
让我来解释一下仅有Smalltalk支持的特性。还以我们已经多次介绍的Client类为例,假设我们有一个遍历某个客户所有部分的简单需求;例如我们想先遍历到name,然后是age,最后是address。
C++ 和Java对此需求的惯例解决方案是创建一个新的特殊流化(stream)类或枚举器(enumerator)类,也许叫ClientIterator,它带有初始化、判断是否迭代结束、如果未结束迭代下一个对象的迭代器等方法。利用这些方法我们就能写一个循环初始化迭代器,获取下一个对象并处理它直到迭代结束。迭代器的优点是在顺序处理中它能提供一个单独的变量用于跟踪迭代到的位置;没必要把Client类展开成用于迭代的“临时”变量。
下面是一段刻意抽象的代码:
aClient := CODE TO GET ONE.
aClient partsDo: [:object |
object printOn: Transcript]
注意partsDo:像一个以object为循环变量的循环。第一次遍历我们得到name并打印到transcript(一个Smalltalk编程环境中特殊的工作区)。然后第二次遍历得到age,最后第三次遍历得到address。同样值得注意的是“partsDo:”是一个以“aClient”为接收者,以“[:object | object printOn: Transcript]”(一个block)为参数的关键字消息。
在深入之前我先给出Smalltalk的解决方案。然后我解释一下它的工作原理并给出更多的惯用法的例子。我们要做的就是给Client添加如下方法:
partsDo: aBlock
aBlock value: self name.
aBlock value: self age.
aBlock value: self address.
要理解这段代码要先认清这些block是匿名函数。为了更好的理解我所讲,请设想我们想把一个函数赋值给一个变量但是不调用它。我来写出它的类C语法风格写法(我知道用C语法做这件事的确切写法,不过那对阐述关键思想没有什么帮助;所以我就不写严格的C语法了):
a = f(x) { return x + 1; } // 类C风格语法
a := [:x | x + 1] // Smalltalk语法
这里变量“a”成了一个函数对象。f是一个函数,因此我们可以通过“f(10)”调用它得到11。但我们还能通用执行“a(10)”调用它因为a的值是一个函数。通过变量“a”执行函数不需要知晓和它原始名相关的信息。
所以在Smalltalk中我们甚至不纠结于函数的名字。我们可以轻易的把它赋给任意一个变量并通用此变量使用它。在上面简单演示函数调用的例子中,我们设定“a value: 10”使它返回11。在执行过程中,参数x被绑定为10,参与x + 1运算,一直执行到block末时最终计算出的结果被返回。
通常我们极少直接执行block。取而代之的我们写成形如“partsDo:”,隐藏笨拙的block“调用”来提供抽象功能。
来看更多的例子。假设我们有一个维护一个旅客链表的Airplane类。我们尝试一下遍历访问旅客中的所有儿童(假设定义12岁及以下为儿童)。实现此功能的代码如下:
anAirplane passengers do: [: person |
person age = 12
ifTrue: [.. DO SOMETHING with person ..]]
如果我们需要在其它上下文中遍历访问儿童,稍作抽象会很有助于简化代码。我们所需要做的就是在Airplane类中实现一个名为“kidsDo:”的抽象(为便于引用说明,我为代码加上了行号):
1. kidsDo: aBlock
2. "此处self是一个Airplane"
3. self passengers do: [:person |
4. person age = 12
5. ifTrue: [aBlock value: person]]
我们调整示例代码如下来表述抽象:
6. anAirplane kidsDo: [:kid |
7. .. DO SOMETHING with kid ..].
8. "完成。"
你能看出第6行代码是如何工作的吗?当第6行的“kidsDo: ...”消息执行时第一行的“kidsDo:”方法就被调用了。然后第1行的变量“aBlock”就被绑定了“[:kid | .. DO SOMETHING with kid ..]”(暂且称其kid block)。kidsDo:方法中第3行的“do:”会遍历所有旅客。第5行中aBlock仅在旅客年龄不高于12岁时才被发送一个“value:”消息。当以“person”为参数的“value:”消息执行时,就会引发一个对kid block的函数调用并导致“kid”绑定到“person”和第7行的“.. DO SOMETHING with kid ..”。执行到block末时执行流程从“kidsDo:”返回到“do:”循环(第5行末),然后继续如此处理其他kid。循环结束后执行流程从第6行进行的“kidsDo:”方法调用返回并到达第8行。
一言以蔽之,第6行的代码导致第1到第5行的代码循环执行,1到5行又会使kid block(第7行)执行。
总体上说,block为Smalltalk提供了一种最简洁的实现控制流抽象的手段。它也同样被精妙的设计用来执行语义上的返回语句,而且这是唯一可以表达此语义的方式。让我通过给Airplane添加一个和第6到8行类似的方法来阐述这个问题:
10. findAnySickKid
11. "这里self也是一个AirPlane"
12. self kidsDo: [:kid |
13. kid isSick
14. ifTrue: [^kid]].
15. ^nil "不存在生病的"
通读代码,我相信你不会看出什么不寻常之处。这也是一个遍历飞机上所有儿童的循环。如果发现了一个生病的儿童就返回。另外,更进一步的迭代下去如果没有生病的儿童循环终止并返回nil(一个可被容易检测的特殊对象)。那这里值得注意的是什么呢?嗯有三点重要的地方:第10行findAnySickKid方法开始处,第1行kidsDo:方法开始处,还有最后第13、14行kid block。通过执行“anAirplane findAnySickKid”,先后调用了方法findAnySickKid,进而调用kidsDo:,进而kid block。在kid block里面执行“^kid”并不返回给发送者(kidsDo方法)而是返回给findAnySickKid的发送者。不管从kidsDo:到kid block内部的消息链多长,“^kid”始终从findAnySickKid返回。恕我孤陋寡闻还没听说过这个特性的称谓,我个人称其短路返回(译注:short circuit return)。
请问怎么快速看懂java代码?
这代码有两个重点:一是entity是个什么类型,大概是数据库映射的实体类,那么就要多看看相关的书籍了,二是业务逻辑,也就是和订单相关的一系列流程,先自己理理。我比较熟悉C#,对java一窍不通,看着和你一样的发晕啊。
怎么看懂java代码电子书下载的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于怎么看懂java代码电子书下载的文件、怎么看懂java代码电子书下载的信息别忘了在本站进行查找喔。