开发第一个Java程序

本章将用Java语言来实现第一个程序,这个程序很简单就是用Java语言编写代码打印一句“hello, world”。这里花一分钟八卦一下“hello, world”相关的传说,很久以前有位计算机天才设计了一门称为C的高级程序语言,为了方便后人学习这位设计者同另外一名著名的计算机科学家合作写了一本《The C Programming Language》书,入门的第一个程序就是通过C语言打印一句“hello, world”(中文意思是“你好,世界”),后来因为这种入门方式简洁、高效,所以很多编程相关的技术入门或调试时都选择通过这种方式。

实现HelloWorld程序

1、在“D:\Java基础课程\开发第一个程序”目录下新建一个HelloWorld.txt文本文件,你也可以在习惯的任意目录下创建。

 

2、双击打开文件,敲入如下代码保存:

public class HelloWorld {


public static void main(String[] args){
 
       System.out.println("hello, world");
   }
}

然后将HelloWorld.txt重命名为HelloWorld.java,这里有个细节要注意,有的电脑文件后缀可能被设置为隐藏,这种情况自己去网上查询下如何将windows的后缀显示出来。

3、Win + R快捷键进入命令行窗口,然后cd到HelloWorld.java文件所在的目录下,进入后可以通过dir命令查看当前目录下文件列表。

4、接下来我们通过javac命令编译HelloWorld.java程序文件,然后再dir可以看到多出了一个同名后缀为class的文件,这个文件就是编译后的class二进制字节码文件。

 

5、通过上面编译之后,接下来我们通过java命令运行HelloWorld程序,执行后如果看到命令行输出了我们程序编写的hello, world,那么恭喜你已经成功编写并运行了第一个Java程序。

方法

任何Java程序运行时必须定义和指定一个入口主方法,也有很多资料称为主函数,在Java中函数和方法可以理解为同一个概念。主方法所在的类称为主类。其中前面我们编写的HelloWorld.java文件中的

public static void main(String[] args) {
 
}

就是主方法,主方法代码格式是固定且不变的,它所在的类HelloWorld.java称为主类。Java语言规范中,任何带java后缀且内容包含一个被定义为

public class xxx {
 
}

的文件均为java类文件,其中xxx为类名,大小写敏感,Java规范建议类名首字母大写且和文件名一致,class用来定义一个类,public表示这个类是对外公开的,花括号里面是类的定义,class和public都是Java语言规范的关键字。

classpath

Java软件工程(Java Software Project)只需要一个主类就可以了吗?理论上是可以,注意我说的是理论上,实际现实中没有任何一个Java软件工程是这么去做的,而是编写成千上万个类文件以及依赖各种第三方类库,第三方类库一般对外提供的是jar包,代码通过import关键字来引入其中的类。当面临成千上万个类文件和各种第三方类库时,如何编译、运行、管理庞大的Java软件工程就成为一个棘手的问题,现实软件工程开发中一般都是通过例如Maven、Gradle、Ant等项目管理工具来解决这个问题的,这些工具基本是通过JDK自带的命令功能进行封装的。JDK自带的命令功能是非常丰富的,比如我们直接在命令行裸执行javac编译命令可以看到关于javac命令各种参数使用说明,其它命令也是如此。

尽管Java开源社区有现成封装好的工具可以用,但是有两个核心的概念我们必须了解且掌握,分别是:classpath命令参数和jar命令。下面我们重点介绍它们。

  为了直观了解classpath概念,我们将HelloWorld.java复制并重命名为HelloWorld2.java,把他们放在不同的目录下,下面分别是两个类文件全路径和源代码信息:

D:\Java基础课程\开发第一个程序\hello\HelloWorld.java
D:\Java基础课程\开发第一个程序\hello2\HelloWorld2.java

HelloWord.java源代码如下:

package hello;
import hello2.HelloWorld2;
public class HelloWorld {
      public static void main(String[] args){
             System.out.println("hello, world");
      }
}

HelloWord2.java源代码如下:

package hello2;
public class HelloWorld2 {
      public static void main(String[] args){
             System.out.println("hello, world");   
      }
}

package关键字是类的包定义,包主要是用来区分模块的,不同模块实现不同的业务,不同模块类文件名称可以相同,不同的包实际上对应操作系统的不同目录。import关键字表示当前类引入其他包的类。此时我们进入到HelloWorld.java目录下对HelloWorld.java类文件进行编译,我们会得到一个报错信息“程序包hello2不存在”:

原因就是当我们执行javac命令时,编译器在编译过程一旦发现import关键字就会从classpath类路径查找对应的包目录,然后将classpath目录拼接import分割后的包目录来找到需要引入的类。当我们没有指定和配置classpath的情况下,classpath默认是当前目录(.),因为HelloWorld类文件中通过import关键字引用了HelloWorld2类,编译器从默认的当前类路径“D:\Java基础课程\开发第一个程序\hello”找不到对应的HelloWorld2类文件,所以编译器诚实的报错了,告诉我们它找不到对应的HelloWorld2类。要解决这个问题有两种方式,分别是:

  • 通过配置系统变量CLASSPATH来全局指定类路径。

  • 通过命令参数-classpath来指定当前编译的类路径。

这里笔者强烈建议永远不用通过配置CLASSPATH系统变量全局指定的方式来解决,因为这样会污染Java编译和运行环境的,切记!推荐的做法是每次通过命令参数方式来指定当前编译的类路径,正确的编译命令如下:

javac -classpath d:\Java基础课程\开发第一个程序 HelloWorld.java

这样编译器就会从“d:\Java基础课程\开发第一个程序”路径去查找第三方类库而不是当前目录,因为“d:\Java基础课程\开发第一个程序”目录下存在hello2\HelloWorld2类文件,所以编译器不再报错。

前面我们提到classpath默认是当前目录(.),实际上除了当前目录外还包括JDK安装包中核心类库jar包文件(%JAVA_HOME%\jre\lib就是其中一个),JDK安装包核心类库jar包我们不需要关心,编译能够自动找到。我们可以通过执行“javac -verbose HelloWorld.java”命令看到整个classpath查找路径信息,其中前面的一大堆就是编译器默认的JDK安装核心类库jar包文件,最后那个才是我们通过classpath指定的,由于我们没有指定所以默认是当前路径(.)。

 

前面我们讲了一大堆classpath对于javac编译命令的作用,那么classpath对于java命令的作用又是什么呢?为了方便直观理解,我们现在cd ..到上一级目录下或者你也可以cd到任意目录下,然后执行“java HelloWorld”命令,因为此时classpath默认是当前目录,而当前任意目录下是不存在HelloWorld.class的,所以你会收到一个来自JVM的错误“找不到主类”:

如果我们通过classpath参数指定类路径目录又会怎样呢?

java -classpath d:\Java基础课程\开发第一个程序 HelloWorld

执行效果如下:

总结:javac编译命令classpath参数是告诉编译器如何查找import关键字引用的类文件,而java运行命令classpath参数是告诉JVM去哪里加载类文件。

 

最后有一点要注意,如果我们工程引入第三方类库不是具体class类文件而是jar包的话,classpath进行指定时必须到jar包文件,而不是jar所在的目录,从前面的javac -verbose截图中就可以很直观地看到。现实当中通过jar包的方式引入第三方类库是最普遍的,因为jar包更方便管理和下载传输。

jar包

  我们平时传输大量文件的时候都知道将其根目录压缩为zip包,然后再传输给对方,Java也借鉴了这种方式,通过jar命令可以将某个目录下的所有文件进行压缩为jar包,例如将HelloWorld.java文件所在的目录压缩为jar包:

jar -cvf hello.jar .

注意最后有个点表示将当前目录进行压缩打包。从日志可以看到HelloWorld.java和HelloWorld.class都被添加进去了,并不是只加载class类文件。通过java -jar命令可以运行jar包,下面我们执行下看看:

 

JVM拒绝类我们的命令请求并告诉我们找不到主类,我们知道任何java程序必须指定入口主类,jar里面如果有多个类文件,需要在jar命令通过-e参数指定入口类是哪个,例如:

jar -cvfe hello.jar HelloWorld .

我们重新打包并运行,可以看到成运行HelloWorld程序。

通过zip解压缩工具进入jar包内部可以看到一个叫META-INF的目录,下面有个MANIFEST.MF文件,主类就是通过这个文件的Main-Class属性定义的,复制出来打开可以看到内容如下:

Manifest-Version: 1.0
Created-By: 1.8.0_152 (Oracle Corporation)
Main-Class: HelloWorld

Java运行机制

理解Java运行机制有助于我们在开发过程更加自信,遇到程序异常时能够更加快速定位问题。如果现阶段确实不好理解这块的话,可以先精读一遍即可,不理解也没关系。

前面我们提到过JDK包含了编译器以及Java运行环境JRE,其中JRE里面又包含了一个叫JVM虚拟机的程序,JVM暂时可以抽象理解为Java程序到操作系统调用的翻译官,它将我们编写的Java程序翻译成操作系统的调用。下面是JDK包含关系草图:

 

我们电脑安装好JDK之后,就拥有上面草图的运行环境,下面是Java程序的运行机制草图(从左往右看):

 

下面对上面草图文字的描述:

1、第一阶段是遵循Java开发语言规范编写Java程序,例如HelloWorld.java文件就属于这个阶段。

2、第二个阶段将编写的程序代码编译为JVM虚拟机可识别的class字节码文件,例如HelloWorld.class文件就属于这个阶段。编译器是非常强大的,它既需要遵循Java语言规范,又需要了解JVM规范。

3、第三个阶段是class类文件加载,”java HelloWorld”命令就属于这个阶段,在类加载前JVM会先检查是否存在主方法,如果不存在JVM会诚实地给出找不到主类地错误信息,如果找到主方法,这时JVM虚拟机开始启动开始加载class类文件。加载类过程是非常复杂的,从开发语言的角度,JVM类加载分为三大类:

l 启动类加载器(Bootstrap ClassLoader):这个类加载器使用C++语言实现的,内置在JVM虚拟机中,开发者无法直接使用它。它负责将JAVA_HOME/lib目录下或通过 -Xbootclasspath 命令参数指定的路径中且是JVM虚拟机能够识别的类库加载到虚拟机内存中。

l 扩展类加载器(Extension ClassLoader):这个类加载器是用Java语言实现的,在JVM虚拟机外,开发者可以直接使用它。它存在于我们下载的JDK工具包JAVA_HOME\jre\lib\rt.jar!\sun\misc\Launcher.class$ExtClassLoader中,它负责加载JAVA_HOME\lib\ext目录下或通过java.ext.dirs系统变量指定的路径中所有类库。

 

l 应用程序类加载器(Application ClassLoader):这个类加载器也是用Java语言实现的,在JVM虚拟机外,一般我们编写的程序class文件就是通过这个类加载器或子类加载器去加载的。它存在于我们下载的JDK工具包JAVA_HOME\jre\lib\rt.jar!\sun\misc\Launcher.class$AppClassLoader中,它负责加载命令参数classpath指定的类库,我们前面的HelloWrod.class正是通过走这个类加载器进行加载的。

其中在对每个加载时候会按“双亲委派模型”去进行,例如加载HelloWorld.class,理论上由应用程序类加载器Application ClassLoader先对其进行加载,实际上Application ClassLoader会先询问它的父加载器Extension ClassLoader是否已经加载该该类,如果Extension ClassLoader反馈已经加载过,那么Application ClassLoader将不再重复加载,否则Extension ClassLoader会继续询问它的父加载器Bootstrap ClassLoader是否已经加载该该类,如果Bootstrap ClassLoader反馈已经加载过,那么Application ClassLoader和Extension ClassLoader将不再重复加载该类,如果Bootstrap ClassLoader和Extension ClassLoader反馈均没有加载过,Application ClassLoader才会开始执行类加载,这就是“双亲委派模型”,但不是所有类都会遵循“双亲委派模型”也存在特殊的情况,通过线程上下文类加载器(ThreadContextClassLoader)作弊的方式来绕过双亲委派解决不了的问题。当类加载完成后JVM就会开始执行主方法。

小结

    本章介绍的部分内容可能已经超出Java入门阶段范畴了,但是学习Java入门时必须粗略了解一遍本章相关的原理和知识点,还是一开始那句话,很多知识现在不理解没关系,我们先精读一遍,大概有个印象即可。下面对本章内容进行总结:

    1、介绍如何从零实现一个Java程序。

    2、任何Java程序运行时必须定义和指定一个主方法,主方法所在的类称为主类。

    3、通过裸执行命令可以得到该命令可选参数的帮助说明。

    4、javac编译命令classpath参数是告诉编译器如何查找import关键字引用的类文件,而java运行命令classpath参数是告诉JVM去哪里加载类文件。

    5、classpath默认路径是当前目录,强烈建议永远不要通过配置系统变量CLASSPATH的方式全局指定类路径。这样会破坏Java全局类路径环境。

     

    6、jar包是为了更方便管理Java类文件,jar是可以直接被java命令运行的,但在打包时要通过-e参数指定主类。

    7、Java运行机制:编写程序源代码 > 编译 > 检查主方法 > 启动JVM虚拟机 > 类加载 > 运行主方法。 

关注我【Java软件编程之家】

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值