十二年前,我的小儿子 Dave 出现在我的办公室,手里拿着 Java 教程。Dave 让我教他编程,这样他就能自己写游戏了。那时候我已经写了几本关于 Java 的书,还同时教几门计算机编程课,但那都是面向成人的;Amazon 上没有任何适合用来教孩子编程的书。在 Google 上搜索了几个小时,我能找到的一些为孩子准备的编程教程也只是浅尝辄止,或者是最基础的那种类似于“聪明兔(译者注:即 reader rabbit,美国著名幼儿教育品牌)”的书。所以我决定自己写一本书。
那时我的大儿子 Yuri 在大学里主修动漫,他答应为我的书画插图。当我完成草稿的时候,没有出版商愿意出版它,原因有两个:
- 少儿编程书根本没有市场。
- 我希望这本书能彩印,较之于黑白印刷,这大大增加了成本。
没什么大不了的。我在网上发布了此书的 pdf 版本供大家免费下载。一年后,几位好心的法国家长把它译成了法语,然后一些来自东欧的 Java 爱好者又把它译成了俄语和乌克兰语。这些版本仍然可以从网上免费下载到。
今年早些时候,我收到了一封意外的电子邮件,来自于No Starch 出版社——问我是否有兴趣写一本给孩子看的Java 编程书籍?我又问了一次,“你们愿意彩印吗?”,出乎我意料的是,他们马上同意了,并且给我看了很多他们出版的教孩子编程的书籍,是其他语种的。时代变了……长话短说,我接受了。一年很快要过去了,这本书将会在2015 年春季付印。
用Java 来教孩子编程好吗?
为了回答这个问题,我们首先要定义“孩子”这个词。对于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。
少儿 Java 教育家
如果说 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。
本书节选 1:类和对象
在现实世界中你会看到和用到各种对象,每个对象都属于某个“种类”,比如玩具、食物、动物、电子产品等等。在 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类只拥有视频游戏所共有的属性和方法。
图 1:视频游戏的对象
类还是对象
在本节中我将向你展示如何基于类来创建对象实例,并向你介绍 _ 继承 _ 的概念。
为了复用你或者其他人已经写好的代码,你可以基于一个类创建另一个,它会继承前一个类的所有属性和方法。继承别的类创建的类称为“子类”,被继承的类称为“父类”。下面的代码展示了继承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对象实例类似。只是每个实例会有些不一样,比如本例中的内部磁盘大小。
图 2:一个类,两个实例
一般情况下,程序先创建 Java 类的对象实例,才能使用它的属性和方法。游戏公司也一样——他们使用同一份原本生产出数千份游戏拷贝。即使这些拷贝代表了同一个类,它们还是可能有不一样的属性——有些是黑的色,另一些则是银色的。有些有 500GB 硬盘,有些则有 1TB。
本书节选 2:改变样式和使用效果
这是第十章的节选,读到那里读者已经掌握了 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方法后,试着玩玩这个游戏。制胜的棋子看起来就是下面这个样子:
图 3:我们赢了!
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
评论