第2章 启程:Hello, World!的探索之旅

第2章 启程:Hello, World!的探索之旅

"学而不思则罔,思而不学则殆。"

------孔子(古代中国教育家)

在这一章中,我们将踏上一场充满探索的旅程,从一个极其基础的Java程序开始。这个程序虽然简单,但它却是一个起点,一个孕育着无限可能的种子。我们的目的是:

· 引领你初次踏入编程的世界,熟悉你的编程环境。

· 让你体验第一次指挥计算机为你工作的感觉。

因此,我们将介绍程序的概念,并演示如何使用编译器。编译器是一个神奇的工具,它可以将我们易于理解的人类语言转化为机器指令。然后,这些指令会在目标平台上被一一执行,让计算机按照我们的意愿工作。

现在,想象一下,你正坐在电脑前,准备开始你的编程之旅。你打开了一个文本编辑器,准备写下你的第一个Java程序。这个程序非常简单,它只会在屏幕上显示一句话:"Hello, World!"。但是,不要小看这个程序,它是你编程之路的起点,是你与计算机交流的第一次尝试。

随着你逐渐深入编程的世界,你会发现,编程不仅仅是一种技能,更是一种思考方式。它需要你不断地学习、思考和实践。就像孔子所说的那样,"学而不思则罔,思而不学则殆。"在编程的道路上,你需要不断地学习和思考,才能不断进步。

所以,让我们开始吧!从Hello, World!开始,踏上你的编程之旅。在这个过程中,你会遇到挑战,也会收获成就。但无论如何,只要你坚持下去,你一定会发现编程的魅力所在。现在,就让我们一起写下第一个Java程序,开启这场充满探索的旅程吧!

2.1 程序之探索

要让计算机执行某项任务,你(或他人)需向其提供详尽无遗的指令,精确到每一个细微步骤。这种对"执行步骤"的详尽描述,我们称之为程序,而编写并测试这些程序的过程,则被称为编程。

从某种意义上而言,我们每个人都曾涉足过编程的领域。毕竟,我们都曾接受过关于如何完成特定任务的指导,例如"如何烘焙一个美味的蛋糕"、"如何组装一台自行车"或"如何使用遥控器操作电视"。然而,这些描述与程序之间的显著差异在于精确度:人与人之间的指示往往较为模糊,但我们可以通过常识来弥补这一不足,计算机则无法做到这一点。以如何烘焙一个美味的蛋糕为例,"取适量面粉、糖和鸡蛋混合,放入烤箱中烘烤"可能是一个简洁明了的描述。但是,当你深入审视这些简单的指令时,你会发现其中存在语法上的不严谨和指令的不完整性。人类可以轻松地进行弥补和解读,例如,你不需要被告知要选择哪种类型的面粉、糖和鸡蛋的具体比例、烤箱应该预热到多少度、烘烤多长时间等细节。

相比之下,计算机则显得尤为"笨拙"。它们执行的每一项任务都需要准确且详细的指令。我们再次以"取适量面粉、糖和鸡蛋混合,放入烤箱中烘烤"为例进行说明。适量的面粉、糖和鸡蛋具体是多少克?什么是混合?如何定义"放入烤箱中烘烤"这个动作?烤箱应该预热到多少度?我该如何判断蛋糕是否烘烤完成?(是观察颜色变化还是使用温度计?)为了向计算机精确描述这些"事物"和"动作",我们需要一种具有特定语法且定义精确的语言(因为英语等自然语言的结构对于这一目标而言显得过于松散和模糊),同时还需要针对我们要执行的各种操作明确定义的词汇表。这种专门用于编程的语言被称为编程语言,而Java就是一种为各种编程任务而精心设计的编程语言。

2.2 初探:首个Java程序

这是一段经典的初识程序,它的功能是在你的屏幕上显示"Hello, World!":

java 复制代码
// 该程序在屏幕上输出 "Hello, World!"
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!"); // 输出 "Hello, World!"
    }
}

我们可以把这段代码视作给计算机的一组执行指令,就像我们给厨师的一张菜谱,或是我们组装新玩具时遵循的步骤指南。接下来,我们将探讨这个程序的每一行代码是如何工作的,首先从这行开始:

java 复制代码
System.out.println("Hello, World!"); // 输出 "Hello, World!"

这行代码负责实际的输出工作。它会在屏幕上打印出字符串"Hello, World!",并在其后添加一个换行符,意味着在打印完"Hello, World!"之后,光标会移动到下一行的起始位置。在Java中,字符串常量是由双引号(")界定的,因此"Hello, World!\n"是一个字符串。需要注意的是,在Java中,换行符可以直接通过println方法实现,无需像C++中那样显式添加\n

System.out是Java中的一个标准输出流,与C++中的cout类似。使用输出方法println可以将字符发送到屏幕上显示。System.out的发音是"system out",代表"system output stream"的缩写。在编程过程中,你会经常遇到各种缩写,初次接触时可能需要花点时间去记忆,但随着使用的增多,它们会变得非常自然,对于保持代码的简洁和可控至关重要。

这行代码的末尾:

java 复制代码
// 输出 "Hello, World!"

是一段注释。在Java中,//之后的任何内容都被视为注释,编译器会忽略它。注释是写给需要阅读代码的人看的。在这里,我们使用注释来说明这行代码的实际作用。

注释用于阐述程序的意图,它提供的信息对于理解代码的逻辑非常有帮助,但无法直接在代码中表达。最有可能通过代码中的注释得到帮助的人是你自己------设想你在未来某个时刻回头阅读代码,却忘记了当初为何这样编写时的情景吧。因此,请为你的程序编写清晰的注释。在后续章节中,我们将深入探讨如何编写有效的注释。

程序是写给两类读者看的。显然,我们编写代码是为了在计算机中执行。然而,程序员也需要花费大量时间来阅读和修改代码。从这个角度来看,程序员也是程序的另一个重要读者。因此,编写代码也是人与人之间沟通的一种方式。实际上,将人类视为代码的主要读者是很有意义的:如果他们(我们)发现代码难以理解,那么代码很可能存在错误。所以,请牢记代码是用来阅读的,你应尽可能提高其可读性。值得注意的是,注释只对人类读者有益;计算机不会解析注释中的文本。

这个程序的第一行是一个典型的注释,它简要地告诉人类读者这个程序的目的:

java 复制代码
// 该程序在屏幕上输出 "Hello, World!"

由于代码本身只是描述了程序的具体行为,而没有说明我们的期望,因此这样的注释是非常有用的。通常,向人类(大致)解释一个程序的功能,要比通过代码向计算机(详细)描述同样的功能来得简洁得多。这种注释通常是我们编写程序的第一个步骤。如果没有其他特殊情况,它会提醒我们正在尝试实现的目标。

接下来的代码行:

java 复制代码
public class HelloWorld {

定义了一个名为HelloWorld的公共类。在Java中,每个程序都至少包含一个类,并且程序的执行始终从main方法开始。这个类包含了我们想要程序执行的所有指令。

那么,计算机如何知道从哪里开始执行一个Java程序呢?它会查找一个名为main的方法,并从那里开始执行指令。这是"Hello, World!"程序的main方法:

java 复制代码
public static void main(String[] args) {
    System.out.println("Hello, World!"); // 输出 "Hello, World!"
}

每个Java程序都必须包含一个名为main的方法,这是程序执行的起点。一个方法本质上是一个具有特定名称的指令序列,计算机会按照指令的编写顺序来执行。一个方法由以下四个部分组成:

  • 一个返回类型,在这里是void,表示该方法不返回任何值。
  • 一个名称,在这里是main
  • 一个参数列表,封闭在一组圆括号中,在这里是String[] args;这表示main方法可以接受一个字符串数组作为参数。
  • 一个方法体,封闭在一组大括号{}中,列出了这个方法将要执行的动作(称为语句)。

因此,最简单的Java程序可以是这样的:

java 复制代码
public class SimplestProgram {
    public static void main(String[] args) {
    }
}

由于这个程序没有执行任何操作,因此它并没有多少实际用处。我们的"Hello, World!"程序的main方法中包含了一个语句:

java 复制代码
System.out.println("Hello, World!"); // 输出 "Hello, World!"

它在屏幕上输出"Hello, World!"。在Java程序中,用于描述一个行为的代码,并且它不是一个注释或其他特殊的指令,被称为一条语句。

2.3 编译过程

Java是一种编译型语言。这意味着,为了让程序运行,我们需要将人类可读的格式转换为机器能够理解的形式。这一转换任务由编译器完成。人类编写和阅读的部分被称为源代码或程序文本,而计算机能够执行的部分则被称为字节码或机器代码。典型的Java源代码文件后缀为.java(例如HelloWorld.java),编译后的字节码文件后缀为.class(例如HelloWorld.class)。因此,"代码"一词可能引发歧义,应谨慎使用;除非明确指出,我们通常用"代码"来指代"源代码",甚至是"不包含注释的源代码",因为注释仅供人类阅读,编译器在生成字节码时会忽略它们。

Java源代码->Java编译器->字节码

编译器会读取你的源代码,并尝试理解其内容。它会检查程序语法是否正确,每个单词是否具有预定含义,以及是否能在不实际执行程序的情况下检测到明显错误。你会发现,计算机对语法要求非常严格。忽略源代码中的某些细节(如import语句、分号或大括号)将导致编译错误。同样,编译器对拼写错误也毫不留情。接下来,我们将通过一系列示例来解释这些概念,每个示例中都包含一个小错误,这些错误是我们常见的错误类型:

java 复制代码
// 此处缺少import语句
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World!"); // 输出 "Hello World!"
    }
}

我们没有导入任何包来告诉编译器System是什么,因此编译器会报错。为了修正这个错误,我们添加一个import语句:

java 复制代码
import java.lang.*;

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}

不幸的是,编译器仍然报错:我们实际上不需要显式导入java.lang包,因为它默认被导入。编译器同样不支持以下代码:

java 复制代码
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World!"; // 字符串未正确终止
    }
}

我们没有使用双引号来终止字符串。编译器也不接受以下代码:

java 复制代码
public class HelloWorld {
    public static integer main(String[] args) { // 使用了错误的返回类型
        System.out.println("Hello World!");
        return 0;
    }
}

在Java中,应使用int而不是integer作为返回类型。编译器同样不支持以下代码:

java 复制代码
public class HelloWorld {
    public static void main(String[] args) {
        System.out < "Hello World!\n"; // 使用了错误的运算符
    }
}

我们使用了<(小于运算符)而不是println方法来输出字符串。编译器也不支持以下代码:

java 复制代码
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World!\n'; // 字符串使用了错误的引号
    }
}

我们使用了单引号而不是双引号来界定字符串。最后,编译器会发现这样的错误:

java 复制代码
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World!\n") // 缺少分号
    }
}

我们忘记了使用分号来终止输出语句。需要注意的是,许多Java语句都以分号(;)结束。编译器通过这些分号来识别语句的结束和新语句的开始。关于何处需要分号,没有简短、完全正确且非技术性的总结。目前,请遵循我们的模式:"在每个不由右侧大括号(})结束的表达式后放置一个分号。"

为什么我们要花费时间来展示这些包含琐碎错误的小程序示例呢?和其他程序员一样,你会在源代码中花费大量时间查找错误。很多时候,我们都在处理包含错误的代码。毕竟,如果我们确信某些代码是正确的,我们通常会去查看其他代码或休息片刻。令早期的计算机先驱们惊讶的是,他们会犯很多代码错误,并不得不花费大量时间来查找。现在,这也是大多数编程新手感到惊讶的地方。

当你编程时,有时可能会对编译器感到恼火。有时,编译器会抱怨无关紧要的细节(如缺少分号),或者一些你认为"显然正确"的东西。但是,编译器通常是正确的:当它给出错误消息并拒绝为你的源代码生成字节码时,你的程序中确实存在不正确之处;这意味着你编写的程序不符合Java标准的精确定义。

编译器没有常识(它不是人类),它对细节非常挑剔。由于编译器没有常识,因此你不能让它尝试猜测那些"看起来正确"但不符合Java定义的东西的含义。如果编译器这样做,并且它的猜测与你的设想不同,那么你需要花费大量时间来找出为什么程序没有按你的要求工作。总的来说,编译器会将你从许多自己造成的问题中解脱出来。与由此产生的问题相比,编译器将我们从更多问题中拯救出来。因此,请记住:编译器是你的朋友,可能是你在编程时最好的朋友。

2.4 类加载和链接

Java程序通常由多个独立的部分构成,这些部分往往由不同的开发者负责。以"Hello,Java!"程序为例,它既包括我们自行编写的代码,也涵盖了Java标准库的内容。这些独立的部分(在Java中常被称为类或包)必须经过编译,随后生成的字节码需要被链接(在Java中,这一过程更多地体现在类加载和链接阶段),以形成一个可以执行的应用程序。负责执行这一链接步骤的程序,自然而然地,被称作链接器,在Java中,编译过程是在程序执行之前完成的,它将源代码转换为字节码。随后,当Java程序运行时,Java虚拟机(JVM)的类加载器负责加载这些字节码,并进行链接,以形成一个可执行的应用程序。这一过程(类加载和链接)通常在JVM运行时进行。

需要留意的是,字节码和执行程序(即Java应用程序)并不具备跨系统移植的能力。例如,为Windows系统编译的字节码,其内含的对Windows平台的特定支持,无法在Linux系统上直接运行。

库在Java中表现为一系列代码的集合,这些代码通常由第三方编写,我们通过import语句来访问库中的类或接口。import语句在这里起到了声明的作用,它指明了程序将如何使用某个库中的特定部分,我们将在后续的章节(特别是涉及包和访问控制的章节)中对此进行详尽的探讨。

编译器检测到的错误被归类为编译时错误,而链接器(或更准确地说,是JVM的类加载器和链接器)在加载和链接类时发现的错误,则被称为链接时错误。那些只有在程序实际运行时才会浮现的错误,被称为运行时错误或逻辑错误。通常,编译时错误相较于链接时错误而言,更易于理解和修正;而链接时错误,相较于运行时错误和逻辑错误,又更容易被发现和修正。

2.5 编程环境

在Java编程的世界里,我们利用编程语言来编织程序。编译器成为了我们的桥梁,将编写的源代码转化为字节码,随后链接器(在Java中,这一角色通常由Java虚拟机(JVM)在运行时担任)将这些字节码联结成一个可执行的应用程序。此外,我们还需要一些特定的程序来在计算机中输入和编辑源代码文本。这些核心且不可或缺的工具,共同构建了程序员的"程序开发环境"。

如果你倾向于使用命令行界面,就像众多资深程序员那样,那么你将需要亲自执行编译和链接的指令。同时,也有许多专业程序员选择使用IDE(即"交互式开发环境"或"集成式开发环境")。在IDE的辅助下,只需轻轻点击相应的按钮,一切便能轻松搞定。

IDE通常配备了一个功能丰富的编辑器,它能帮助我们通过颜色区分源代码中的注释、关键字和其他组成部分,从而极大地提升了代码的可读性。除此之外,IDE还提供了诸多实用的功能,如代码调试、编译和运行等,为我们的开发工作带来了极大的便利。调试,作为编程中不可或缺的一环,它能帮助我们发现并修正程序中的错误;在编程的道路上,你将频繁地与它打交道。

在学习本教程的过程中,你可以选择任何一种最新且符合标准的Java实现作为你的开发工具。我们所介绍的大部分内容(仅需进行微小的调整)都适用于所有的Java实现,并且你的代码将能够在任何Java支持的平台上顺畅运行。在我们的日常工作中,我们也会根据需求选择不同的Java实现来进行开发。

相关推荐
程序伍六七1 分钟前
day16
开发语言·c++
wkj0016 分钟前
php操作redis
开发语言·redis·php
武子康8 分钟前
大数据-230 离线数仓 - ODS层的构建 Hive处理 UDF 与 SerDe 处理 与 当前总结
java·大数据·数据仓库·hive·hadoop·sql·hdfs
武子康10 分钟前
大数据-231 离线数仓 - DWS 层、ADS 层的创建 Hive 执行脚本
java·大数据·数据仓库·hive·hadoop·mysql
极客代码11 分钟前
【Python TensorFlow】进阶指南(续篇三)
开发语言·人工智能·python·深度学习·tensorflow
苏-言17 分钟前
Spring IOC实战指南:从零到一的构建过程
java·数据库·spring
土豆湿17 分钟前
拥抱极简主义前端开发:NoCss.js 引领无 CSS 编程潮流
开发语言·javascript·css
界面开发小八哥24 分钟前
更高效的Java 23开发,IntelliJ IDEA助力全面升级
java·开发语言·ide·intellij-idea·开发工具
草莓base37 分钟前
【手写一个spring】spring源码的简单实现--容器启动
java·后端·spring
Allen Bright1 小时前
maven概述
java·maven