Java从环境到类与对象 · SSH 实操

第一单元 · 终端、变量、输入 · 第 1 / 12 节

在 SSH 里写下第一个 Java 程序

从 Linux shell 提示符开始,建 lesson01 工作目录,写 Hello.java,分清提示符/源码/输入/输出,用 javac -encoding UTF-8 编译、java 运行。

约 25 分钟 · 7 个步骤 · 进度只存本机

把网页放在左边,SSH 终端放在右边:网页负责讲清楚“做什么、为什么”,右边负责真正敲命令、看结果。本节的每个命令都可以从网页复制,但请先读一遍再粘贴,别把整页一次性倒进终端。

开始本节前,你已经通过 SSH 登录学习服务器。SSH 是一种加密的远程登录方式:你在自己电脑上输入命令,真正执行命令的是服务器。登录后屏幕上有一个 Linux shell 提示符;shell 是终端里读命令、把命令交给 Linux 的程序。提示符的样子随账号和配置变化,可能是 ubuntu@host:~$,也可能是 root@host:~#;它不是命令的一部分,复制时不要带上。如果这是新开的终端,先按 setup.html 的说明加载课程环境,通常是:

SSH 服务器终端bash
$ source ~/java-practice/env.sh

加载后 java -version 和 javac -version 都应是 17.x。看不到 17.x 就先回 setup 页排查,不要继续往下走,也不要在服务器上自己安装 JDK。

先把屏幕上四种东西分开,后面每一节都按这个规则看:

你看到的东西它是什么怎么处理
root@host:~# 或 ubuntu@host:~$SSH 的 Linux shell 提示符,表示终端在等你输入命令不复制
public class Hello { ... }Java 源码,属于某个 .java 文件复制进文件
javac -encoding UTF-8 Hello.java给 shell 的编译命令复制到终端执行
你好,Java!程序运行后写到标准输出(stdout)的文字不复制,只用来对照

Java 的第一个硬规则:**一个 .java 文件里最多有一个 public 类,而且这个类的名字必须和文件名完全一致,大小写也不能差。** 下面的类叫 Hello,所以文件必须叫 Hello.java,不能叫 hello.java、Hello.Java 或 Test.java。Linux 区分大小写,这一点和 Windows 不一样。

第二个硬规则:程序从 main 方法开始执行。public static void main(String[] args) 这一行先当成“入口的固定写法”,一个字符都不要少;每个单词的含义后面会逐步展开。

本节没有键盘输入,网页上不会出现 input 块;input 块从 lesson03 开始才有。现在只要分清 shell 命令、Java 源码和程序输出。

SSH 服务器终端Java 源码程序交互输入输出示例(只读)复制 Java 源码不带文件名;复制命令不带 $ 提示符;输出和输入示例没有复制按钮。
这是教程网页,不是在线运行环境。Java 代码和 SSH 命令都要在你的服务器上执行;网页不会执行代码、不会连接服务器、也不会接收密码。

已完成 0 / 7 步

第 1 步:认提示符,确认 JDK 17

先确认自己在哪里、工具是不是 17。source 这一行让当前 shell 临时使用课程提供的 JDK;它只影响这个终端窗口,不会改系统默认设置。

SSH 服务器终端bash
$ source ~/java-practice/env.sh
$ java -version
$ javac -version
$ pwd

java -version 和 javac -version 都应出现 17.x(例如 17.0.20.1)。java -version 把版本写到标准错误(stderr),这是正常现象,不是报错。pwd 显示当前工作目录,刚登录时通常是 /root 或 /home/你的用户名。

如果 source 提示找不到文件,说明课程环境还没准备好;回 setup.html 按顺序做,不要用 apt install 之类的方法自己补 JDK。服务器上还有别的服务,装错版本会影响别人。

第 2 步:建 lesson01 目录,先保护旧文件

每个小节用独立目录,避免上一节的 Hello.class 和这一节混在一起。下面的 mkdir -p 表示“目录不存在就建,已经存在也不报错”;cd 进入目录;pwd 再确认一次。

SSH 服务器终端bash
$ mkdir -p ~/java-practice/lesson01
$ cd ~/java-practice/lesson01
$ pwd
$ ls -la
$ if [ -e Hello.java ]; then mv Hello.java "Hello.java.bak.$(date +%s)"; fi

最后一行是保护动作,拆开看:

  • [ -e Hello.java ]:测试 Hello.java 是否存在;
  • mv Hello.java "Hello.java.bak.$(date +%s)":存在就改名,$(date +%s) 会插入当前时间的秒数,所以备份名不会重复;
  • 双引号把文件名包住,避免空格或特殊字符出问题;
  • 文件不存在时,if 什么也不做。

这里出现的是 **shell 的 if**,不是 Java 的 if;本单元不教 Java 分支,看到它不用紧张。ls -la 现在应该只看到 .、..,或者你以前留下的备份;如果看到 Hello.java 已经被改名成 Hello.java.bak.数字,说明保护生效了,旧内容还在。

第 3 步:用 nano 写下 Hello.java

nano 是 Linux 上最简单的文本编辑器。它必须单独占一个命令块:如果把 nano 和后面的 javac 放在同一块里复制,编辑器会“吞掉”后面的命令,看起来像卡住。

SSH 服务器终端bash
$ nano Hello.java

打开后,把下面这一整份内容粘贴进去。粘贴完按 Ctrl+O,屏幕底部会问文件名,直接按 Enter 确认;再按 Ctrl+X 退出。记不住就念一遍:Ctrl+O、Enter、Ctrl+X。如果服务器没有 nano,可以用 vi Hello.java:按 i 进入插入模式,粘贴后按 Esc,输入 :wq 再回车。

Java 源码Hello.java
public class Hello {
    public static void main(String[] args) {
        System.out.println("你好,Java!");
    }
}

逐行认识这份源码:

  • public class Hello:声明一个叫 Hello 的类。class 是 Java 的“类”关键字,现在把它当成程序的外壳;public 表示这个类可以被外部启动。
  • 两个花括号 { 和 }:包住类的内容。花括号必须成对,少一个就会编译失败。
  • public static void main(String[] args):程序的入口方法。main 这个名字和参数写法是 Java 规定的,先照写。
  • System.out.println("你好,Java!"):把双引号里的文字打印到屏幕,并换一行。System 是 Java 自带的系统类,out 是输出通道,println 是“打印一行”。
  • 行末的分号 ;:Java 语句的结束标记,不能省。
  • 缩进只是为了人看,Java 不靠缩进判断层次;但保持缩进能让错误更容易发现。

源码里的标点必须是英文半角:();{}" 这些字符。中文引号 “”、中文分号 ; 会让编译器看不懂。字符串里面的中文是内容,可以正常写。

第 4 步:检查文件,编译成 Hello.class

先看文件在不在、编码是不是 UTF-8,再编译。javac 是 Java 编译器,它读 .java 源文件,生成 .class 字节码文件;字节码是给 Java 虚拟机(JVM)执行的中间格式,不是给人直接读的。

SSH 服务器终端bash
$ ls -l Hello.java
$ file Hello.java
$ javac -encoding UTF-8 Hello.java
$ ls -l Hello.class

file Hello.java 一般会显示 UTF-8 text 或 Unicode text 之类;如果显示别的编码,先回编辑器确认保存格式。javac 成功时不会打印“成功”两个字,这是命令行工具的常见习惯:没有输出通常就是成功。ls -l Hello.class 能看到文件,才说明编译真的产出了结果。

-encoding UTF-8 明确告诉编译器按 UTF-8 读源码。UTF-8 是一种把字符存成字节的编码;源文件里有中文时建议每次都写,换一台机器或换一个 locale,少了它就可能出现乱码或“不可映射字符”。

第 5 步:运行,分清类名和文件名

编译成功后,用 java 启动类。注意这里写的是类名 Hello,不是文件名 Hello.java,也不是 Hello.class。

SSH 服务器终端bash
$ java Hello

屏幕上应该出现:

输出示例(只读)output不可复制
你好,Java!

java Hello 会在当前目录(默认 classpath)找 Hello.class,然后从 main 方法开始执行。classpath 是 JVM 查找 .class 的路径列表,默认包含当前目录。System.out.println 写出的内容就是标准输出(stdout),也就是上面 output 块里的那一行。网页把 output 标成只读,是因为它是程序结果,不是你要输入的命令。

如果 java Hello 报“找不到或无法加载主类”,先别改代码,按顺序检查:pwd 是不是 ~/java-practice/lesson01、ls -l Hello.class 在不在、javac 那一步有没有报错。运行和编译是两层,不要把两类错误混在一起。

第 6 步:故意错一次,看见“类名=文件名”

现在做一个安全的实验:复制一份 Hello.java 叫 WrongName.java,但里面的 public class 仍然是 Hello。这会让编译器发现“public 类名和文件名不一致”。

SSH 服务器终端bash
$ if [ -e WrongName.java ]; then mv WrongName.java "WrongName.java.bak.$(date +%s)"; fi
$ cp Hello.java WrongName.java
$ javac -encoding UTF-8 WrongName.java
$ rm -f WrongName.java

cp 只复制,不改 Hello.java;rm -f WrongName.java 只删掉这次实验用的副本,不会碰你的 Hello.java 和 Hello.class。最后一行在 javac 报错后仍会执行,因为 shell 默认会继续跑后面的命令;如果你是一行一行手敲,看到错误后再敲 rm 也一样。

注意 预期会看到编译错误

英文环境通常会看到类似 class Hello is public, should be declared in a file named Hello.java 的片段;中文环境会显示对应的翻译,但同样会指出 Hello.java。不同 JDK 补丁和 locale 的完整措辞可能不同,不要逐字背报错,只要抓住“public 类名”和“文件名”这两个关键词。

这个实验证明:类名和文件名一致不是“建议”,而是 public 类的硬性要求。实验完 ls -l 应仍能看到 Hello.java 和 Hello.class;如果 WrongName.java 还在,说明 rm 那行没执行,手动删掉这个临时副本即可。

第 7 步:改一行,输出两行,再跑一次

最后改一次文件:把 Hello.java 的内容整份替换成下面这份,而不是只在末尾追加。整份替换可以避免花括号或分号漏掉。

SSH 服务器终端bash
$ nano Hello.java

打开后先删掉旧内容,再粘贴下面整份源码;保存方式仍是 Ctrl+O、Enter、Ctrl+X。

Java 源码Hello.java
public class Hello {
    public static void main(String[] args) {
        System.out.println("你好,Java!");
        System.out.println("我的第一个类叫 Hello。");
    }
}

然后用单独的命令块重新编译、运行:

SSH 服务器终端bash
$ javac -encoding UTF-8 Hello.java
$ java Hello

输出应该是两行:

输出示例(只读)output不可复制
你好,Java!
我的第一个类叫 Hello。

每改一次源码都要重新 javac,再 java;只运行旧的 .class 不会看到新文字。反过来,如果只改文字不改类名,Hello.class 会被新编译结果覆盖,这是正常的构建产物更新。

说明 重复执行时备份会怎样

如果你再次执行第 2 步最后那行保护命令,Hello.java 会被改名成 Hello.java.bak.<秒数>,然后第 3 步的 nano Hello.java 会新建一个同名文件。旧内容没有丢,只是在备份文件里。用 ls -lt 可以按时间查看这些备份;不需要它们时可以只删除自己确认过的 .bak 文件,不要动别人的目录。

常见错误

  • **现象:class Hello is public, should be declared in a file named Hello.java。原因:public class 后面的类名和文件名不一致,或大小写不同。先检查什么:ls -l 看文件名,head -n 3 Hello.java 看类名,两边逐字对照。**
  • **现象:javac: file not found: Hello.java(中文“找不到文件”)。原因:当前目录不是 ~/java-practice/lesson01,或文件没保存成功。先检查什么:pwd、ls -l;不在目录里就 cd ~/java-practice/lesson01,文件不在就回第 3 步重写。**
  • **现象:Error: Could not find or load main class Hello(中文“找不到或无法加载主类”)。原因:没有 Hello.class,或 java 在别的目录运行,或把类名写成了文件名。先检查什么:ls -l Hello.class,确认 javac 成功,并在同一目录执行 java Hello。**
  • **现象:error: unmappable character (0x...) for encoding,或中文变成问号/乱码。原因:源文件不是 UTF-8,或终端 locale 与文件编码不一致(locale 是系统的语言/编码环境)。先检查什么:file Hello.java、locale;本课程统一 UTF-8,编译时带 -encoding UTF-8。**
  • **现象:javac: command not found 或 java: command not found。原因:当前 shell 没有加载课程 JDK,PATH 里找不到命令。先检查什么:source ~/java-practice/env.sh,再跑 java -version 和 javac -version;仍不是 17.x 就停下报告环境问题,不要自己装 JDK。**
  • **现象:reached end of file while parsing。原因:花括号或分号漏了,常见于只粘贴了方法的一部分。先检查什么:从 public class 到最后一个 } 数一遍花括号,确认每条语句末尾有分号。**

练习

  1. 把第二行文字改成 我在 lesson01 目录里。,重新编译运行,写出两行输出。
  2. 先预测:如果把文件另存为全小写的 hello.java,里面的 public class Hello 不变,执行 javac -encoding UTF-8 hello.java 会成功吗?为什么?预测后再实际试一次,最后把文件名改回 Hello.java。

先自己改、先写预测,再展开答案。

提示 卡住时看这里

第 1 题只改 println 双引号里的文字,类名和文件名都保持 Hello。第 2 题看 public class 后面的名字和文件名是否逐字一致;Linux 区分大小写,hello.java 和 Hello.java 是两个不同的文件名。

参考答案 对答案

第 1 题改好的整份源码:

Java 源码Hello.java
public class Hello {
    public static void main(String[] args) {
        System.out.println("你好,Java!");
        System.out.println("我在 lesson01 目录里。");
    }
}

运行后是两行:

输出示例(只读)output不可复制
你好,Java!
我在 lesson01 目录里。

第 2 题:不会成功。文件叫 hello.java,里面的 public class 叫 Hello,大小写不一致,编译器仍会报“public 类应声明在 Hello.java 中”。把文件名改回 Hello.java(或把类名改成 hello 并同步改文件名)才能通过。这个练习也说明:在 Linux 上,文件名大小写不是小事。

做到这里

做到这里今天可以停。你已经会:确认 JDK、建独立目录、用 nano 新建文件、用 javac -encoding UTF-8 编译、用 java 类名 运行,也能在报错时先判断是“文件名/类名”“目录”“编译”还是“运行”哪一层的问题。

本节不需要提前学 if、循环、数组或自定义方法。下一节 lesson02 才引入变量和类型;本节写的 Hello 只是程序外壳,不是第四单元那种“自己设计的类”。

来源 来源与改编说明(非官方)

主题参考:黑马程序员 Java 入门到起飞(BV17F411T7Ao,2022-04-27)P001–P016 的 Java 入门、JDK、HelloWorld 主题。本站为独立原创讲解,非官方站点/教材,未获授权,不逐字复刻视频或讲义;示例、错误清单与练习为原创补充。详见 source/SOURCE.md。

公开核对入口:

  • 课程页:https://www.bilibili.com/video/BV17F411T7Ao/
  • Oracle JDK 17 javac:https://docs.oracle.com/en/java/javase/17/docs/specs/man/javac.html
  • Oracle JDK 17 java:https://docs.oracle.com/en/java/javase/17/docs/specs/man/java.html

说明:本站是个人学习整理,不是官方教材;不包含原课程的书本、视频或受版权保护的整本内容。原课程与商标归其权利人。