学习Java基础入门

首先

最近有很多宣传着「即使没有经验也可以成为工程师」或者「即使文科出身也可以成为工程师」的编程学校,我在公司的培训中也受益于其中之一。

然而,由于它们主要以在短期内制作应用程序为目标,我感觉它们并没有太注重”基础”这一方面。因此,我打算为即将学习编程的新人,以及像我这样感觉自己”基础”不足的新人工程师,写一份能够作为Java基础入门的材料。

「基础」的意思是

迄今为止,“基础”一词我们都加了括号。

「基础」的意思是指让某物成立的根本部分,即源头。例如:「由于基础扎实,所以进步很快」「打下基础」「基础知识」。

本篇将作为学习Java的”基础”的第一步。

    1. Java和JVM

 

    直到显示出”Hello World”为止

我打算将注意力集中在这两个方面上。

Java和JVM

先让我们来看一下Java这种编程语言本身。

Java (只需要一种选项,以中文本地化地复述):

让我们首先简单地回顾一下编程语言本身。

程序员利用编程语言向计算机发送指令。然而,计算机只是一台通过电力运行的计算机。

顺便说一下,计算机这个词在德语中被称为”Rechner”,它来源于动词”rechnen”,意思是”进行计算”。真是个闲话。

据说,由于计算机只能接收到电信号的两个状态——开或关,过去的程序员使用0和1的二进制来编写程序。顺便提一下,由0和1组成的那些东西被称为机器语言。

由于二进制的位数较多,所以常常使用十六进制来表示,但这对人类来说仍然不够友好。因此,为了给机器语言表示的指令赋予类似昵称的东西(被称作”助记符”),产生了一种被称为”汇编语言”的东西。

然而,即使如此,程序仍然对人类来说不够易懂,并且随着时代的发展,需要的程序数量也增加了,复杂性也增加了。因此,我们开始开发更加容易理解的程序编写方法,以便更好地为人类服务。顺便提一下,这种对人类易懂的编程语言被称为”高级语言”,而计算机易于理解的语言则被称为”低级语言”。

人类能够理解的事物,对于计算机来说往往是难以理解的。因此,程序员需要将自己编写的代码转化为计算机可理解的机器语言,这个过程被称为“编译”,执行编译的程序被称为“编译器”。

编译器将用高级语言编写的程序转化为计算机可理解的机器语言,但编译器会根据具体的计算机环境进行编译。换句话说,不同环境的计算机无法理解相同的转化后的机器语言,因此需要一个特定的编译器。

终于在这里见到了Java的出现。

Java是由Sun Microsystems在1995年推出的。Java是一种高级语言,如前所述,需要进行编译并转化为机器语言才能运行,但与其他语言的不同之处在于它使用了Java虚拟机(JVM)。

JVM为计算机分配内存并启动。然后可以在JVM上运行程序,因此无需担心计算机的差异。

更详细地解释一下,Java代码以易于人类理解的方式编写(文件名为hoge.java),然后通过编译转换为称为中间语言的东西(文件名为hoge.class。中间语言这个名称不是官方名称,只是方便起见称之为如此,正确的名称似乎是Java字节码)。
然后,JVM加载这个中间语言,并逐行将其转换为机器语言并执行。这种逐行转换和执行的程序被称为“解释器”1。

换句话说,Java需要经过两次转换过程才能被执行。(一次由编译器,一次由解释器)

JVM
Java虚拟机

在构建Java环境时,我们会遇到JDK和JRE这两个东西。
JDK是Java开发工具包,用于在使用Java进行开发时所需的软件开发工具包。
而JDK中包含的是JRE(Java Runtime Environment),被称为Java运行环境。它是一组用于在计算机上运行Java应用程序的软件,其中包括JVM。

スクリーンショット 2019-04-07 16.45.37.png

直到“Hello World!”被显示出来。

接下来我们简单地确认一下直到显示”Hello World!”为止的过程。

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

让我们首先进行编译。
javac命令将编译java文件并创建class文件。

javac HelloWorld.java

执行上述操作后,将生成一个名为HelloWorld.class的文件。然后,我们首先尝试显示Hello World!通过使用java命令启动JVM,分析并执行被Java字节码编写的HelloWorld.class文件。

$ java HelloWorld
Hello World!

已经完成了。
现在我们来确认一下Java字节码。
通过使用javap命令,可以将class文件转换为人类可读的形式。

$ javap -c HelloWorld
Compiled from "HelloWorld.java"
public class HelloWorld {
  public HelloWorld();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #3                  // String Hello World!
       5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return
}

这个类以HelloWorld开头,有两个函数,第一个是构造函数,第二个是相当于main函数的字节码。原始代码中没有构造函数,但似乎是自动生成的。让我们来看一下函数的内部。
代码由三部分组成:“索引:指令 #常量池编号”。常量池是Java虚拟机中保存常量和符号的区域,先暂时看一下。
通过在javap命令后加上-v选项可以进行确认。

Constant pool:
   #1 = Methodref          #6.#15         // java/lang/Object."<init>":()V
   #2 = Fieldref           #16.#17        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #18            // Hello World!
   #4 = Methodref          #19.#20        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = Class              #21            // HelloWorld
   #6 = Class              #22            // java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               main
  #12 = Utf8               ([Ljava/lang/String;)V
  #13 = Utf8               SourceFile
  #14 = Utf8               HelloWorld.java
  #15 = NameAndType        #7:#8          // "<init>":()V
  #16 = Class              #23            // java/lang/System
  #17 = NameAndType        #24:#25        // out:Ljava/io/PrintStream;
  #18 = Utf8               Hello World!
  #19 = Class              #26            // java/io/PrintStream
  #20 = NameAndType        #27:#28        // println:(Ljava/lang/String;)V
  #21 = Utf8               HelloWorld
  #22 = Utf8               java/lang/Object
  #23 = Utf8               java/lang/System
  #24 = Utf8               out
  #25 = Utf8               Ljava/io/PrintStream;
  #26 = Utf8               java/io/PrintStream
  #27 = Utf8               println
  #28 = Utf8               (Ljava/lang/String;)V

让我们来查看主函数并理解Hello World的显示流程。

public static void main(java.lang.String[]);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #3                  // String Hello World!
       5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return

首先,我们来看getstatic指令,它引用了#2。这表示我们正在获取字段#2,即java/lang/System.out的值,它是一个PrintStream类型的字段。

下面是LDC指令,它在运行时从常量池加载常量值。
#3 = String #18 // 你好,世界!
#18 = Utf8 你好,世界!
在这里加载了字符串“你好,世界!”

然后,使用invokevirtual指令调用。
#4 = Methodref #19.#20 // java/io/PrintStream.println:(Ljava/lang/String;)V
将之前的字符串作为参数来执行println函数。

最后返回”void”并结束。
辛苦了,现在已经成功显示”Hello World!”。

总结

我在开始认真学习Java时,努力以我想知道的内容为重点,但是要简洁又能抓住要点的写作确实很难。我真的觉得将近一年前的我会想读这样的内容吗?
话虽如此,如果这篇文章对某人有帮助,我会感到幸运。如果你能因此对Java基础产生兴趣,那将是最好的。我决定好好学习Java基础,以便能继续以这样的方式分享。

如果有任何错误,请务必指出。非常感谢。

编译和编译器是按照英文语法进行区分的,然而解释器也应该按照相同的英文语法分为interpret和interpreter,但为什么没有进行这样的表达?