十二年前,我的小儿子Dave出现在我的办公室,手里拿着Java教程。Dave让我教他编程,这样他就能自己写游戏了。那时候我已经写了几本关于Java的书,还同时教几门计算机编程课,但那都是面向成人的;Amazon上没有任何适合用来教孩子编程的书。在Google上搜索了几个小时,我能找到的一些为孩子准备的编程教程也只是浅尝辄止,或者是最基础的那种类似于“聪明兔(译者注:即reader rabbit,美国著名幼儿教育品牌)”的书。所以我决定自己写一本书。
那时我的大儿子Yuri在大学里主修动漫,他答应为我的书画插图。当我完成草稿的时候,没有出版商愿意出版它,原因有两个:
没什么大不了的。我在网上发布了此书的pdf版本供大家免费下载。一年后,几位好心的法国家长把它译成了法语,然后一些来自东欧的Java爱好者又把它译成了俄语和乌克兰语。这些版本仍然可以从网上 免费下载 到。
今年早些时候,我收到了一封意外的电子邮件,来自于No Starch出版社——问我是否有兴趣写一本给孩子看的Java编程书籍?我又问了一次,“你们愿意彩印吗?”,出乎我意料的是,他们马上同意了,并且给我看了很多他们出版的教孩子编程的书籍,是其他语种的。时代变了……长话短说,我接受了。一年很快要过去了,这本书将会在2015年春季付印。
为了回答这个问题,我们首先要定义“孩子”这个词。对于12岁以下的孩子来说,我相信Java作为第一门编程语言有点太复杂了。低龄一点的孩子一开始最好用一些写好的程序模块来搭建可视化程序。MIT创建了 Scratch ,对于8岁大的孩子来说,它是一个很好的激发编程兴趣的工具。10岁的小孩就可以更进一步,通过 Greenfoot 开始学习真正地使用Java来编程。
但是我认为,任何12岁以上的孩子,只要对编程有兴趣,就可以一本正经地去学习Java编程了,并且可以使用专业的工具来学。在之前为孩子写的那本书里,我使用Eclipse作为IDE,但在现在这本新书里,我选择了IntelliJ IDEA社区版,它也是免费的。
教成人和教小孩Java的区别在于,孩子们对于编写GUI程序的反响更热烈,而Java也很擅长做这个。我说的是JavaFX;Swing框架已经过时了,想用Swing来写出点像样的程序,需要很专业的知识。JavaFX则提供了丰富的工具,并且非常优雅地隔离了界面和应用逻辑。JavaFX自带了“Scene Builder”,一个可视化的GUI设计工具,它可以生成用FXML描述的GUI;FXML是一种基于XML的语言,可以通过声明的方式来创建GUI。不过不用担心,孩子们不需要自己去写FXML——他们只需要把UI控件拖拽到画布(canvas)上,Scene Builder就会生成FXML。
如果说12年前的孩子对编程还不是那么感兴趣的话,那么今天的很多孩子已经很喜欢编程了。时代变了……于是,世界各地都有编程爱好者在花时间和精力给孩子介绍编程。我们要向Stephan Janssen致敬,他创立了 Devoxx4Kids 运动,用一种创造性的方式来教孩子编程。这种运动最早开始于比利时,但如今很多国家都加入了。在美国,旧金山分会的领导人是Arun Gupta,而Matt Raible运营着丹佛分会。如果有位于曼哈顿的公司愿意提供场地给孩子们搞活动的话,我已经准备好贡献我的时间,在纽约建立一个分支机构。
我要为Stephen Chin献上掌声,他来自Oracle,他经常背着一个袋子满世界走,那个袋子里装满了可编程设备。今年早些时候,我参加了他的演讲“ 玛丽有个小小的lambda表达式 ”(译者注:英文名“Mary Had a Little Lambda”,此名字借用了著名的英语儿歌“Mary Had a Little Lamb”,lamb和lambda词形相近),在演讲中他通过演示复杂的游戏解释了Java。今年夏天,我参加了在希腊举行的 JCrete “非会议”(译者注:unconference,一种颠覆传统式会议的新形式,会议内容由参与者自己制定,主张开放式讨论)。其他与会者都带着笔记本电脑来,Stephen却带来了两打硬件开发套件,他举行了一个研讨会,在会上孩子们用Java编了一个机器人,可以跳Sirtaki舞(译者注:希腊民间舞蹈)。
我肯定世界各地还有其他的好心人在给孩子们普及Java。已经诞生20年之久的Java语言正在越变越年轻。
感谢有这样的机会能给InfoQ的读者呈现我将要出版的书籍“Java for Kids”中的两段节选。在本书中我使用了IntelliJ IDEA社区版,JetBrains出品的免费又好用的IDE。第一段节选是关于类和对象的简单介绍。第二段来自于Tic-Tac-Toe(译者注:一种棋类游戏)章节。教孩子的时候,程序的可视化程度越高越好。这就是为什么我要在总共12章中的5章都使用了JavaFX。读者们需要编写一个计算器和两个游戏:Tic-Tac-Toe和Ping-Pong。
在现实世界中你会看到和用到各种对象,每个对象都属于某个“种类”,比如玩具、食物、动物、电子产品等等。在Java中我们不说 一种对象 ,而说 一类对象 。类就像是一个对象的蓝图。你将要编写的所有程序都是由各种类组成的。
Java定义了各种 数据类型 。有些很简单,像 int
,代表整数。有些复杂一点,它们就是类。比如 System
类,它可以用于在屏幕上打印文字、退出程序或清理计算机的内存。
你安装了Java后,数千个Java类被下载到你的计算机中。你的Java程序中包含的类也可能代表来自真实世界的对象。如果说类是一种数据类型,那么对象就代表了一种特殊的类型。举个例子,你看到街上有十只狗,他们都代表了 Dog
这个类。
程序员在开始写程序之前,先要决定需要使用哪些类,每个类要创建多少对象。比如,在游戏应用中他们可以定义 Player
类,并以此创建两个对象。
当程序员互相交流的时候,他们可能会说“对 Player
类做某些操作”或者“用 Player
对象做某些操作”。我们需要分辨 类 和 对象 这两个词的不同含义。
我们来看看Java类是如何组成的。最基础的类可能什么都不包含。看看下面的类声明:
class VideoGame {}
这是个空的类——它不带有任何信息,也干不了任何事,因为两个大括号之间没有任何代码。从Java语法的角度看,这个类是合法的,计算机不会报错—— class 是一个有效的关键字,紧接着是名字 VideoGame ,大括号要成对出现。但如果我们的程序想干点什么,我们需要定义有实际意义的类。所以我们需要给类加装方法和属性:
我们来给 VideoGame 类填上内容。这个类可以有一些方法,告诉我们 这个类的对象能做什么 :开始游戏、停止游戏、保存分数、要求上Facebook点赞等等。这个类也可以有一些属性(也叫域):颜色、价格和其他。代表 VideoGame 类的一个对象看起来是这样的:
class VideoGame { String color; int price; void start () { // 开始游戏的代码放在这里 } void stop () { // 停止游戏的代码放在这里 } void saveScore(String playerName, int score) { // 保存分数的代码放在这里 } }
这个类有两个属性 color 和 price 。 color 属性是 String 类型的,这种类型用于保存文本。 price 是 int 型的,用于保存整数。 VideoGame 类有三个方法: start 、 stop 和 saveScore 。这些都是我们的视频游戏能采取的动作。目前,这些方法只有以双斜杠 //
开头的一行字。
如果一行文字以双斜杠开头,那它就是单行的注释——只是对代码段的描述。如果你需要写多行的注释,只要输入一个斜杠和一个星号 /*
,然后输入任意行的文字,最后以一个星号和一个斜杠结尾 */
,代表注释结束。
我们不会去创造一个真正的视频游戏,因为我们才开始学编程。但到本书的后面部分,我们会开发Tic-Tac-Toe和Ping-Pong游戏。
但是难道你不觉得所有游戏都有共同点吗?每个游戏都需要有一种方法来开始和停止或保存分数。当你以面向对象的方式编写一个程序,你要开始思考如何使类拥有的属性和方法保持绝对的最少。然后你可以基于第一个类来添加更多特殊的功能——在本章节后面部分你会学到 继承 。这就是为什么 VideoGame 类只拥有视频游戏所共有的属性和方法。
在本节中我将向你展示如何基于类来创建对象实例,并向你介绍 继承 的概念。
为了复用你或者其他人已经写好的代码,你可以基于一个类创建另一个,它会继承前一个类的所有属性和方法。继承别的类创建的类称为“子类”,被继承的类称为“父类”。下面的代码展示了继承 VideoGame 类的新类 PlayStation4 。
class PlayStation4 extends VideoGame{ String hardDiskSize; // 其他属性和方法可以在这里声明 // 方法shareOnFacebooks检查用户是否已经登录了 // 如果没有,它会显示一个窗口,带有以下信息 // “在分享你的分数之前请先登录Facebook” void shareOnFacebook(){ // 在Facebook上分享的代码在这里 System.out.println("Hey, Facebook, I got PlayStation4 with " + hardDiskSize); } void shareOnTwitter(){ // 在Twitter上分享的代码在这里 System.out.println("Hey, Twitter, I got PlayStation4 with " + hardDiskSize); } }
语句extends VideoGame 表示 PlayStation4 类是VideoGame类的子类。我们也可以说 PlayStation4 类继承了VideoGame类或者说“扩展”了VideoGame类,这也意味着子类( PlayStation4 )既可以使用父类(VideoGame)中定义的属性,也可以使用父类(VideoGame)中定义的所有方法(并扩展它们),我在书的后面部分会解释。注意 PlayStation4 类即使没有定义属性price和color,以及方法start、stop和savaScore,但这个类的对象还是可以使用所有这些属性和方法。它们是从 VideoGame 类继承过来的,这些代码不需要拷贝/粘贴到 PlayStation4 类中。
当我们创建一个对象的实例时,我们真正所做的是基于某个类的声明,在计算机内存中创建了一个对象。举个例子,下面的 CreatePlayStation4Objects 程序使用new运算符创建了两个 PlayStation4 类的实例。它声明了两个 PlayStation4 型的变量,分别是 firstPlayStation 和 secondPlayStation ,每个都指向了内存中的一个独立对象。注意这些对象的属性 hardDiskSize 有不同的值(500GB和1TB)。这个程序对第一个对象调用了 shareOnFacebook 方法,而对第二个对象调用了 shareOnTwitter 。
public class CreatePlayStation4Objects { public static void main(String[] args) { // 创建一个PlayStation4类的实例 PlayStation4 firstPlayStation = new PlayStation4(); firstPlayStation.hardDiskSize = "500GB"; // 对第一个实例调用shareOnFacebook方法 firstPlayStation.shareOnFacebook(); // 创建另一个PlayStation4类的实例 PlayStation4 secondPlayStation = new PlayStation4(); secondPlayStation.hardDiskSize = "1TB"; // 对第二个实例调用shareOnFacebook方法 secondPlayStation.shareOnTwitter(); }
运行 CreatePlayStation4Objects 会打印以下内容:
Hey, Facebook, I got PlayStation4 with 500GB Hey, Twitter, I got PlayStation4 with 1TB
如果一个游戏公司要生产10000个这样的游戏,那么程序员可以说他们创建了10000个 PlayStation4 的实例。游戏公司和一个真正的游戏的关系就类似于Java类和它在内存中的实例的关系。在现实世界中游戏公司制作一个真正的游戏,和Java编程世界中创建 PlayStation4 对象实例类似。只是每个实例会有些不一样,比如本例中的内部磁盘大小。
一般情况下,程序先创建Java类的对象实例,才能使用它的属性和方法。游戏公司也一样——他们使用同一份原本生产出数千份游戏拷贝。即使这些拷贝代表了同一个类,它们还是可能有不一样的属性——有些是黑的色,另一些则是银色的。有些有500GB硬盘,有些则有1TB。
这是第十章的节选,读到那里读者已经掌握了JavaFX的基础,写过一个登录窗口和一个计算器,并且知道如何在界面上应用CSS样式。Tic-Tac-Toe游戏的基本功能也已经实现,但是如果能使制胜的三颗棋子一目了然就更好了(译者注:Tic-Tac-Toe以三子在一直线上为胜)。
制胜棋子的样式应该可以动态改变(在运行的时候)。首先,我们需要给CSS文件添加一个样式来显示制胜的X-O棋子。然后 Controller 类可以对棋子调用 setStyle 方法,决定制胜的棋子使用哪种样式。
我想改变制胜棋子的背景,但是这次我不想用单色了,我想用渐变色。在计算机图形中,渐变色是指用混合的颜色去填充一个区域,并且相邻颜色之间是平滑过渡。颜色渐变分线性的和放射状的, 这篇维基百科的文章 有这两者的例子。
我们的游戏中使用放射状渐变。我们可以使用两种或更多的颜色来组成渐变色。我们给制胜棋子使用三种颜色吧。棋子的背景色会从白色转变到淡黄色,再到草绿色。棋子上的文字标签,我们使用红色。
要动态改变GUI组件的样式,你可以调用 setStyle ,并指定颜色作为参数,举个例子:
myButton.setStyle("-fx-text-fill: red;");
但是,在你的Java程序中写死CSS规则不是一个好主意。如果你想改变样式该怎么办(比如把颜色从红色改成粉色)?你不想搜遍所有的Java文件来找出所有使用这个样式的地方。而且,改变嵌在代码中的样式,会导致程序必须重新编译,谁会为了一个简单的修改这么干!所以把样式定义放在外部的CSS文件中会比较好,改起来就比较灵活。
至此,我们已经使用了CSS类型选择器来改变指定组件的样式。但是,CSS允许你定义一个样式并给它取名,这样,就不仅仅是特定的组件类型可以用它,各种组件都可以通过指定名字来使用它。在CSS中,这种样式叫做 类选择器(class selectors) 。我们来给 tictactoe.css 添加一个叫做 .winning-square 的类选择器。
.winning-square { -fx-text-fill: red; -fx-background-color: radial-gradient( radius 100%, white, lightyellow, lawngreen); }
这里的样式选择器 .winning-square 包括了两条样式规则:一条是要把按钮的颜色设为红色,另一条是要把按钮的背景色设为我们的放射状渐变色。我们的Java程序必须得到按钮现有样式的引用,然后再添加一个 .winning-square ,这就会覆盖按钮原来的文字和背景颜色。方法 highlightWinningCombo 看起来是这样的:
private void highlightWinningCombo(Button first, Button second, Button third){ first.getStyleClass().add("winning-square"); second.getStyleClass().add("winning-square"); third.getStyleClass().add("winning-square"); }
在给 Controller 类添加完 find3InARow 和 highlightWinningCombo 方法后,试着玩玩这个游戏。制胜的棋子看起来就是下面这个样子:
JavaFX包括了多种 视觉效果 和 变换动画 ,这些可以使你的界面变得更有意思。
要使用这些效果和动画,你需要在 javafx.scene.effect 和 javafx.animation 包中进行相应的选择。我会给你演示一下如何在我们的制胜棋子上使用淡出的变换效果。每个制胜棋子不停地淡出,然后又回复到初始状态。我们要给 Controller 类添加一个 applyFadeTransition 方法。
private void applyFadeTransition(Button winningButton) { FadeTransition ft = new FadeTransition(Duration.millis(1000), winningButton); ft.setFromValue(1.0); ft.setToValue(0.1); ft.setCycleCount(10); ft.setAutoReverse(true); ft.play(); }
这段代码创建了一个动画类 FadeTransition 的实例,这个动画会持续1000毫秒(等于1秒),然后把这个动画分配给制胜棋子。然后它又设置了淡出的参数,把棋子的透明度从1改到了0.1(0.0意味着完全透明)。
把样式数量设为10代表动画会播放10次。因为调用了 setAutoReverse(true) ,这个动画会在第一个周期中正向播放,在第二个周期中反向播放,如此循环往复。play方法则启动了动画。给 highlightWinningCombo 添加以下三行代码,这个动画就会在 winningButton 组件上播放。
applyFadeTransition(first); applyFadeTransition(second); applyFadeTransition(third);
下面的截屏告诉你动画播放结束后的样子。
图4:淡出的制胜棋子
把这张图和前面一张比一比。或者运行书中的代码,看看动画是什么样子的,就更好了。
我希望在看完这篇文章后,你们中的一些人开始渴望教自己的孩子编程。我给你们带来了好消息。出版商让我把 早期未经修改的初稿放在网上 ,这样我们就可以在此书付印前收集一些来自社区的反馈。你们可能会看到一些标点遗漏或其他语法错误,但是在出版前编辑们会搞定这些问题。
本书的 示例代码 可分享给大家,我把它们发布在GitHub上。如果你发现示例代码有bug或者有更好的方案,请告诉我。
教你们的孩子Java!和你的另一半一起享受Java!也把Java介绍给你们的父母!
Yakov Fain 是一个Java拥护者,以及两个软件公司的联合创始人:Farata Systems和SuranceBay。他写过几本技术书籍和很多关于软件开发的文章。他领导着普林斯顿Java用户小组和纽约Dart用户小组。最近,Yakov与人合著了 《Enterprise Web Development 》(O'Reilly出版社) 一书。他有两本书会在2015年出版:《Java For Kids》(No Starch出版社)和《Java 24-Hour Trainer》第二版(Wrox出版社)。
查看英文原文: Teaching Kids Java Programming