1 从Hello World开始

那我们就先写一个Hello World程序吧。

1.1 编写Hello World

在正式开始编写程序之前,请先确定你可以看到文档的扩展名。

image-20190607231945560

选择一个文件夹来编写Java代码。然后请新创建一个“文本文件”

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

以下是注意点:

■ 扩展名是.java:这也就是你必须让“资源管理器”显示扩展名的原因。

■ 文档名与类名称必须相同。类名称是指class关键词(Keyword)后的名称,这个示例就是HelloWorld这个名称,这个名称必须与HelloWorld.java的主文档名(HelloWorld)相同。

■ 注意每个字母大小写。Java程序区分字母大小写,System与system对Java程序来说是不同的名称。

■ 空格只能是半角空格符或Tab字符:有些初学者可能不小心输入了全角空格符,这很不容易检查出来。

老实说,要对新手解释第一个Java程序并不容易,这个简单的程序就涉及文件管理、类(Class)定义、程序进入点、命令行自变量(Command line argument)等概念。

以下先针对这个示例做基本说明。

1.定义类

class是用来定义类的关键词,之后接上类名称(HelloWorld)。Java程序规定,所有程序代码都要定义在“类”中。class前有个public关键词,表示HelloWorld类是公开类,就目前为止你只要知道,一个.java文档可定义多个类,但是只能有一个公开类,而且文件名必须与公开类名称相同。

2.定义代码块(Block)

在程序中使用大括号“{”与“}”定义代码块,大括号两两成对,目的在于区别程序代码范围。例如,程序中HelloWorld类的区块包括了main()方法(Method),而main()方法的代码块包括了一句显示信息的程序代码。

3.定义main()方法

程序执行的起点就是程序进入点(Entry point),Java程序执行的起点是main()方法。方法的参数一定是一个String数组。也

public static void main(String[] args)

4.编写代码(Statement)

来看main()中的一行描述(Statement):

System.out.println("Hello World");

描述(Statement)是程序语言中的一行指令,简单地说,就是程序语言中的“一句话”。注意每句描述的结束要用分号(;),这句代码的意思就是请系统的输出装置显示一行文字HelloWorld。、

一个基本的Java程序这么写就对了。一下子要接受如此多概念确实不容易,如果现阶段无法了解,就先当这些是Java语法规范,以后就会明白了。

1.2 PATH是什么

在讲Java平台的时候,我们说过*.java必须编译为*.class才可以在JVM中执行,Java的编译程序工具程序是javac。装好JDK之后,工具程序就会放在JDK安装文件夹的bin文件夹中。

那我们来试一下

  • 首先我们需要打开命令行工具

同时按下win+R,在打开的运行对话框中输入:cmd后,回车打开。

image-20190607234016047

  • 编译

我们需要进入到我们代码的目录,然后执行javac

C:\Users\jundongpei>cd C:\workspace

C:\workspace>javac HelloWorld.java
'javac' は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません。

image-20190607234142096

失败了?为什么?这是操作系统Windows在跟你抱怨,它找不到javac放在哪里!

当要执行一个工具程序,那个指令放在哪,系统默认是不晓得的,除非你跟系统说工具程序存放的位置。

那么我们在试一下

C:\workspace>C:\Program Files\Java\jdk-11.0.3\bin\javac HelloWorld.java
'C:\Program' は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません。

C:\workspace>"C:\Program Files\Java\jdk-11.0.3\bin\javac" HelloWorld.java

C:\workspace>

image-20190607234754943

javac编译成功后会静悄悄地结束,所以没看到信息就是好消息,但是这样下每次要写Java的安装路径实在太麻烦了。

事实上,当你输入一个指令而没有指定路径信息时,操作系统会依照PATH环境变量中设定的路径顺序,依次寻找各路径下是否有这个指令。可以执行echo %PATH%来看看目前系统PATH环境变量中包括哪些路径信息。

C:\Users\jundongpei>echo %PATH%
C:\Program Files\MySQL\MySQL Server 5.7\bin;C:\Program Files (x86)\Parallels\Parallels Tools\Applications;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Program Files\Java\jdk-11.0.1\bin;C:\Users\jundongpei\AppData\Local\Microsoft\WindowsApps;;C:\Users\jundongpei\AppData\Local\Programs\Microsoft VS Code\bin

C:\Users\jundongpei>

image-20190607235331318

当我们输入java指令,系统会从第一个路径开始找有无java(.exe)工具程序,如果没有,再找下一个路径有无java(.exe)工具程序……找到的话就执行。

当系统找完PATH中所有路径后,都找不到javac(.exe)工具程序,就会和我们抱怨找不到。

那我们要做就是在PATH中添加Javac的目录,那怎么添加呢?

右击“计算机”,在弹出的快捷菜单中选择“属性”命令,

在打开的窗口中单击“高级系统设置”,进入“系统属性”对话框,

接着切换至“高级”选项卡,单击“环境变量”按钮,

在“环境变量”对话框的“USER的用户变量”或“系统变量”列表中编辑PATH变量

image-20190607235856131

image-20190607235929214

image-20190608000106320

image-20190608000124649

image-20190608000207087

然后全部的窗口都点击OK按钮

然后我们重新打开命令行工具,在重新编译一下。

image-20190608000842851

1.3 JVM(java)与CLASSPATH

完成编译HelloWorld.java之后,相同文件夹下就会出现HelloWorld.class。

image-20190608000710672

刚才我们讲过JVM的可执行文件扩展名是.class,接下来就要启动JVM,要求JVM执行HelloWorld.

启动JVM的指令是java,而要求JVM执行HelloWorld时,只要指定类名称(就像执行javac.exe工具程序,只要输入javac就可以了),不用附加.class扩展名,附加.class反而会有错误信息

C:\Users\jundongpei>cd C:\workspace

C:\workspace>javac HelloWorld.java

C:\workspace>java HelloWorld.class
エラー: メイン・クラスHelloWorld.classを検出およびロードできませんでした
原因: java.lang.ClassNotFoundException: HelloWorld.class

C:\workspace>java HelloWorld
Hello World

C:\workspace>

image-20190608001017371

JVM是Java程序唯一识别的操作系统,对JVM来说,可执行文件就是扩展名为.class的文档。想在JVM中执行某个可执行文件(.class),就要告诉JVM这个虚拟操作系统到哪些路径下寻找文档,方式是通过CLASSPATH指定其可执行文件(.class)的路径信息。

用Windows与JVM做个简单的对照,就可以很清楚地对照PATH与CLASSPATH

image-20190608001409451

如何在启动JVM时告知可执行文件(.class)的位置?

可以使用-classpath自变量来指定。

-classpath有个缩写形式-cp,这比较常用。如果有多个路径信息,则可以用分号分隔。

比如

C:\workspace>java -classpath C:\workspace HelloWorld

C:\workspace>java -classpath C:\workspace;C:\inetpub HelloWorld

如果找不到,就会报错。

C:\Users\jundongpei>java HelloWorld
エラー: メイン・クラスHelloWorldを検出およびロードできませんでした
原因: java.lang.ClassNotFoundException: HelloWorld

如果使用了Java开发了类库,这些类库中的类文档会封装为JAR(Java Archive)文件,也就是扩展名为.jar的文件。JAR文档实际使用ZIP格式压缩,当中包含一堆.class文件,

那么,如果你有个JAR文档,如何在CLASSPATH中设定?

答案是将JAR文件当作特别的文件夹,

java -cp C:\workspace;C:\inetpub\test.jar HelloWorld

如果某个文件夹中有许多.jar文档,从Java SE 6开始,可以使用“*”表示使用文件夹中所有.jar文档。例如,指定使用C:\jars下所有JAR文档:

java -cp C:\jars\* HelloWorld

1.4 编译程序(javac)与CLASSPATH

javac的CLASSPATH与java的一致。我们就不说了。

2 管理源代码与字节码

来观察一下目前你的C:\workspace,源代码(.java)文档与字节码(.class)都放在一起,想象一下,如果程序规模稍大,一堆.java与.class文档还放在一起,会有多么混乱,你需要有效率地管理源代码与字节码。

2.1 编译程序(javac)与SOURCEPATH

首先我们来解决源代码与字节码都放在一起的问题。

假设我们现在有两个源代码文件。

我们希望

  • src文件夹将用来放置源代码
  • 编译好的字节码,希望能指定存放至classes文件夹
C:\workspace\ch2>javac -sourcepath src -d classes src\Main.java

C:\workspace\ch2>

如果你想了解具体的细节,可以使用-verbose来查看日志。

C:\workspace\ch2>javac -verbose  -sourcepath src -d classes src\Main.java
[SimpleFileObject[C:\workspace\ch2\src\Main.java]を構文解析開始]
[22ミリ秒で構文解析完了]
[/modules/jdk.internal.jvmstat/module-info.classを読込み中]
[/modules/java.security.jgss/module-info.classを読込み中]
[/modules/jdk.internal.ed/module-info.classを読込み中]
[/modules/java.xml.crypto/module-info.classを読込み中]
[/modules/java.scripting/module-info.classを読込み中]
[/modules/jdk.security.jgss/module-info.classを読込み中]
[/modules/java.rmi/module-info.classを読込み中]
[/modules/jdk.jfr/module-info.classを読込み中]
[/modules/jdk.dynalink/module-info.classを読込み中]
[/modules/jdk.security.auth/module-info.classを読込み中]
[/modules/java.desktop/module-info.classを読込み中]
[/modules/jdk.unsupported/module-info.classを読込み中]
[/modules/jdk.internal.opt/module-info.classを読込み中]
[/modules/jdk.unsupported.desktop/module-info.classを読込み中]
[/modules/java.instrument/module-info.classを読込み中]
[/modules/jdk.jcmd/module-info.classを読込み中]
[/modules/jdk.jlink/module-info.classを読込み中]
[/modules/jdk.xml.dom/module-info.classを読込み中]
[/modules/jdk.jconsole/module-info.classを読込み中]
[/modules/jdk.jstatd/module-info.classを読込み中]
[/modules/jdk.compiler/module-info.classを読込み中]
[/modules/jdk.internal.vm.ci/module-info.classを読込み中]
[/modules/jdk.jartool/module-info.classを読込み中]
[/modules/java.se/module-info.classを読込み中]
[/modules/jdk.internal.vm.compiler/module-info.classを読込み中]
[/modules/jdk.internal.le/module-info.classを読込み中]
[/modules/jdk.charsets/module-info.classを読込み中]
[/modules/java.net.http/module-info.classを読込み中]
[/modules/java.base/module-info.classを読込み中]
[/modules/java.xml/module-info.classを読込み中]
[/modules/jdk.pack/module-info.classを読込み中]
[/modules/jdk.jsobject/module-info.classを読込み中]
[/modules/jdk.naming.rmi/module-info.classを読込み中]
[/modules/jdk.sctp/module-info.classを読込み中]
[/modules/jdk.jshell/module-info.classを読込み中]
[/modules/jdk.naming.dns/module-info.classを読込み中]
[/modules/jdk.net/module-info.classを読込み中]
[/modules/jdk.scripting.nashorn.shell/module-info.classを読込み中]
[/modules/java.datatransfer/module-info.classを読込み中]
[/modules/jdk.management/module-info.classを読込み中]
[/modules/jdk.httpserver/module-info.classを読込み中]
[/modules/java.sql.rowset/module-info.classを読込み中]
[/modules/java.management/module-info.classを読込み中]
[/modules/jdk.crypto.cryptoki/module-info.classを読込み中]
[/modules/java.management.rmi/module-info.classを読込み中]
[/modules/java.transaction.xa/module-info.classを読込み中]
[/modules/jdk.jdi/module-info.classを読込み中]
[/modules/jdk.management.agent/module-info.classを読込み中]
[/modules/java.security.sasl/module-info.classを読込み中]
[/modules/java.naming/module-info.classを読込み中]
[/modules/jdk.javadoc/module-info.classを読込み中]
[/modules/jdk.accessibility/module-info.classを読込み中]
[/modules/jdk.scripting.nashorn/module-info.classを読込み中]
[/modules/jdk.internal.vm.compiler.management/module-info.classを読込み中]
[/modules/java.prefs/module-info.classを読込み中]
[/modules/java.sql/module-info.classを読込み中]
[/modules/jdk.editpad/module-info.classを読込み中]
[/modules/jdk.jdwp.agent/module-info.classを読込み中]
[/modules/java.smartcardio/module-info.classを読込み中]
[/modules/jdk.attach/module-info.classを読込み中]
[/modules/jdk.zipfs/module-info.classを読込み中]
[/modules/jdk.jdeps/module-info.classを読込み中]
[/modules/jdk.rmic/module-info.classを読込み中]
[/modules/jdk.aot/module-info.classを読込み中]
[/modules/jdk.localedata/module-info.classを読込み中]
[/modules/jdk.crypto.ec/module-info.classを読込み中]
[/modules/jdk.crypto.mscapi/module-info.classを読込み中]
[/modules/java.logging/module-info.classを読込み中]
[/modules/java.compiler/module-info.classを読込み中]
[/modules/jdk.management.jfr/module-info.classを読込み中]
[/modules/jdk.hotspot.agent/module-info.classを読込み中]
[ソース・ファイルの検索パス: src]
[クラス・ファイルの検索パス: C:\Program Files\Java\jdk-11.0.3\lib\modules,.]
[/modules/java.base/java/lang/Object.classを読込み中]
[/modules/java.base/java/lang/String.classを読込み中]
[/modules/java.base/java/lang/Deprecated.classを読込み中]
[/modules/java.base/java/lang/annotation/Retention.classを読込み中]
[/modules/java.base/java/lang/annotation/RetentionPolicy.classを読込み中]
[/modules/java.base/java/lang/annotation/Target.classを読込み中]
[/modules/java.base/java/lang/annotation/ElementType.classを読込み中]
[Mainを確認中]
[/modules/java.base/java/io/Serializable.classを読込み中]
[/modules/java.base/java/lang/AutoCloseable.classを読込み中]
[src\Console.javaを読込み中]
[DirectoryFileObject[src:Console.java]を構文解析開始]
[1ミリ秒で構文解析完了]
[classes\Main.classを書込み完了]
[Consoleを確認中]
[/modules/java.base/java/lang/System.classを読込み中]
[/modules/java.base/java/io/PrintStream.classを読込み中]
[/modules/java.base/java/lang/Appendable.classを読込み中]
[/modules/java.base/java/io/Closeable.classを読込み中]
[/modules/java.base/java/io/FilterOutputStream.classを読込み中]
[/modules/java.base/java/io/OutputStream.classを読込み中]
[/modules/java.base/java/io/Flushable.classを読込み中]
[/modules/java.base/java/lang/Comparable.classを読込み中]
[/modules/java.base/java/lang/CharSequence.classを読込み中]
[classes\Console.classを書込み完了]
[合計418ミリ秒]

C:\workspace\ch2>

2.2 使用package管理类

现在所写的类,.java放在src文件夹中,编译出来的.class放置在classes文件夹下,就文档管理上比较好一些了,

但还不是很好,就如同你会分不同文件夹来放置不同作用的文档,类也应该分门别类加以放置。

举例来说,一个应用程序中会有多个类彼此合作,也有可能由多个团队共同分工,完成应用程序的某些功能块,再组合在一起。如果你的应用程序是多个团队共同合作,若不分门别类放置.class,那么若A部门写了个Util类并编译为Util.class,B部门写了个Util类并编译为Util.class,当他们要将应用程序整合时,就会发生文档覆盖的问题,而如果现在要统一管理原始码,也许原始码也会发生彼此覆盖的问题。

你要有个分门别类管理类的方式,无论实体文档上的分类管理,还是类名称上的分类管理,在Java语法中,有个package关键词,可以协助你达到这个目的。

当类原始码开始使用package进行分类时,就会具有4种管理上的意义:

■ 源代码要放置在与package所定义名称层级相同的文件夹层级中。

■ package所定义名称与class所定义名称,会结合而成类的完全吻合名称(Fully qualified name)。

■ 位码文档要放置在与package所定义名称层级相同的文件夹层级中。

■ 要在包间可以直接使用的类或方法(Method)必须声明为public

比如

Console类使用package定义在com.ripjava.util包下Console.java必须放在src文件夹中的cc/openhome/util文件夹里

Main类是位于com.ripjava包下

由于Main与Console类是位于不同的包中,在Main类中要使用Console类,就必须使用com.ripjava.util.Console

如果我们要编译源代码的话

javac -sourcepath src -d classes src\com\ripjava\Main.java
java -cp classes com.ripjava.Main.java

2.3 使用import

使用包管理,解决了源代码文件与撰写程序时类名称冲突的问题,但若每次撰写程序时,都得输入完全吻合名称,却也是件麻烦的事。想想看,有些包定义的名称很长时,单是要输入完全吻合名称得花多少时间。

我们可以用import来帮我们导入相关的类,我们就可以直接使用相关的类,而不需要每一次都写包名。

improt com.ripjava.Console;

不过,如果类位于同一包,彼此使用并不需要import.

3. 使用IDE