目录

《Head First Java》读书笔记(一)

摘要
《Head First Java》读书笔记(一)。

Chapter 1 Breaking the Surface

The way Java works

What you’ll do in Java

Source code –> Complier –> Bytecode –> Virtual Machine

A very brief history of Java

/images/Head_First_Java/A very brief history of Java.png
A very brief history of Java

Code structure in Java

Source file (源文件)

  • A source code file (with the .java extension) holds one class defini- tion. The class represents a piece of your program, although a very tiny application might need just a single class. The class must go within a pair of curly braces.

Class (类)

  • A class has one or more methods. In the Dog class, the bark method will hold instructions for how the Dog should bark. Your methods must be declared inside a class

    (in other words, within the curly braces of the class).

Method (方法)

  • Within the curly braces of a method, write your instructions for how that method should be performed. Method code is basi- cally a set of statements, and for now you can think of a method kind of like a function or proce- dure.

Anatomy of a class

在这一小节的开头,有这样一段话:

When the JVM starts running, it looks for the class you give it at the com- mand line. Then it starts looking for a specially-written method that looks exactly like:

1
2
3
public static void main (String[] args) { 
  // your code goes here
}

Next, the JVM runs everything between the curly braces { } of your main method. Every Java application has to have at least one class, and at least one main method (not one main per class; just one main per application).

这不禁让我思考,Java 中的 main() 方法,是如何被 JVM 找到并执行的

Java中的 main() 方法是如何被执行的

找到三篇非常有参考价值的文章:

简单总结一下:

  1. java.exe 大体上可以分为启动器部分和 hotspot 部分。代码里的 main() 方法肯定是由 hotspot 调用的。

    1. 启动器负责执行一些命令行解析,环境初始化等任务,hotspot部分则是真正的虚拟机干活的地方。
  2. 最新的openJdk源码(src/java.base/share/native/launcher/main.c)是这样的:

      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    
    /*
     * Copyright (c) 1995, 2022, Oracle and/or its affiliates. All rights reserved.
     * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     *
     * This code is free software; you can redistribute it and/or modify it
     * under the terms of the GNU General Public License version 2 only, as
     * published by the Free Software Foundation.  Oracle designates this
     * particular file as subject to the "Classpath" exception as provided
     * by Oracle in the LICENSE file that accompanied this code.
     *
     * This code is distributed in the hope that it will be useful, but WITHOUT
     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     * version 2 for more details (a copy is included in the LICENSE file that
     * accompanied this code).
     *
     * You should have received a copy of the GNU General Public License version
     * 2 along with this work; if not, write to the Free Software Foundation,
     * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
     *
     * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
     * or visit www.oracle.com if you need additional information or have any
     * questions.
     */
    
    
    /*
     * This file contains the main entry point into the launcher code
     * this is the only file which will be repeatedly compiled by other
     * tools. The rest of the files will be linked in.
     */
    
    #include "defines.h"
    #include "jli_util.h"
    #include "jni.h"
    
    /*
     * Entry point.
     */
    #ifdef JAVAW
    
    char **__initenv;
    
    int WINAPI
    WinMain(HINSTANCE inst, HINSTANCE previnst, LPSTR cmdline, int cmdshow)
    {
        int margc;
        char** margv;
        int jargc;
        char** jargv;
        const jboolean const_javaw = JNI_TRUE;
    
        __initenv = _environ;
    
    #else /* JAVAW */
    JNIEXPORT int
    main(int argc, char **argv)
    {
        int margc;
        char** margv;
        int jargc;
        char** jargv;
        const jboolean const_javaw = JNI_FALSE;
    #endif /* JAVAW */
        {
            int i, main_jargc, extra_jargc;
            JLI_List list;
    
            main_jargc = (sizeof(const_jargs) / sizeof(char *)) > 1
                ? sizeof(const_jargs) / sizeof(char *)
                : 0; // ignore the null terminator index
    
            extra_jargc = (sizeof(const_extra_jargs) / sizeof(char *)) > 1
                ? sizeof(const_extra_jargs) / sizeof(char *)
                : 0; // ignore the null terminator index
    
            if (main_jargc > 0 && extra_jargc > 0) { // combine extra java args
                jargc = main_jargc + extra_jargc;
                list = JLI_List_new(jargc + 1);
    
                for (i = 0 ; i < extra_jargc; i++) {
                    JLI_List_add(list, JLI_StringDup(const_extra_jargs[i]));
                }
    
                for (i = 0 ; i < main_jargc ; i++) {
                    JLI_List_add(list, JLI_StringDup(const_jargs[i]));
                }
    
                // terminate the list
                JLI_List_add(list, NULL);
                jargv = list->elements;
             } else if (extra_jargc > 0) { // should never happen
                fprintf(stderr, "EXTRA_JAVA_ARGS defined without JAVA_ARGS");
                abort();
             } else { // no extra args, business as usual
                jargc = main_jargc;
                jargv = (char **) const_jargs;
             }
        }
    
        JLI_InitArgProcessing(jargc > 0, const_disable_argfile);
    
    #ifdef _WIN32
        {
            int i = 0;
            if (getenv(JLDEBUG_ENV_ENTRY) != NULL) {
                printf("Windows original main args:\n");
                for (i = 0 ; i < __argc ; i++) {
                    printf("wwwd_args[%d] = %s\n", i, __argv[i]);
                }
            }
        }
        JLI_CmdToArgs(GetCommandLine());
        margc = JLI_GetStdArgc();
        // add one more to mark the end
        margv = (char **)JLI_MemAlloc((margc + 1) * (sizeof(char *)));
        {
            int i = 0;
            StdArg *stdargs = JLI_GetStdArgs();
            for (i = 0 ; i < margc ; i++) {
                margv[i] = stdargs[i].arg;
            }
            margv[i] = NULL;
        }
    #else /* *NIXES */
        {
            // accommodate the NULL at the end
            JLI_List args = JLI_List_new(argc + 1);
            int i = 0;
    
            // Add first arg, which is the app name
            JLI_List_add(args, JLI_StringDup(argv[0]));
            // Append JDK_JAVA_OPTIONS
            if (JLI_AddArgsFromEnvVar(args, JDK_JAVA_OPTIONS)) {
                // JLI_SetTraceLauncher is not called yet
                // Show _JAVA_OPTIONS content along with JDK_JAVA_OPTIONS to aid diagnosis
                if (getenv(JLDEBUG_ENV_ENTRY)) {
                    char *tmp = getenv("_JAVA_OPTIONS");
                    if (NULL != tmp) {
                        JLI_ReportMessage(ARG_INFO_ENVVAR, "_JAVA_OPTIONS", tmp);
                    }
                }
            }
            // Iterate the rest of command line
            for (i = 1; i < argc; i++) {
                JLI_List argsInFile = JLI_PreprocessArg(argv[i], JNI_TRUE);
                if (NULL == argsInFile) {
                    JLI_List_add(args, JLI_StringDup(argv[i]));
                } else {
                    int cnt, idx;
                    cnt = argsInFile->size;
                    for (idx = 0; idx < cnt; idx++) {
                        JLI_List_add(args, argsInFile->elements[idx]);
                    }
                    // Shallow free, we reuse the string to avoid copy
                    JLI_MemFree(argsInFile->elements);
                    JLI_MemFree(argsInFile);
                }
            }
            margc = args->size;
            // add the NULL pointer at argv[argc]
            JLI_List_add(args, NULL);
            margv = args->elements;
        }
    #endif /* WIN32 */
        return JLI_Launch(margc, margv,
                       jargc, (const char**) jargv,
                       0, NULL,
                       VERSION_STRING,
                       DOT_VERSION,
                       (const_progname != NULL) ? const_progname : *margv,
                       (const_launcher != NULL) ? const_launcher : *margv,
                       jargc > 0,
                       const_cpwildcard, const_javaw, 0);
    }
    
  3. JLI_Launch() 就是启动器函数。

  4. 大致顺序为:java.base —> JLI_Launcher() –-> JavaMain()

  5. JavaMain() 可以理解为,启动JVM的函数。

  6. 代码如下:(位置:src/java.base/share/native/libjli/java.c)

      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    
    int
    JavaMain(void* _args)
    {
        JavaMainArgs *args = (JavaMainArgs *)_args;
        int argc = args->argc;
        char **argv = args->argv;
        int mode = args->mode;
        char *what = args->what;
        InvocationFunctions ifn = args->ifn;
    
        JavaVM *vm = 0;
        JNIEnv *env = 0;
        jclass mainClass = NULL;
        jclass appClass = NULL; // actual application class being launched
        jmethodID mainID;
        jobjectArray mainArgs;
        int ret = 0;
        jlong start = 0, end = 0;
    
        RegisterThread();
    
        /* Initialize the virtual machine */
        start = CurrentTimeMicros();
        if (!InitializeJVM(&vm, &env, &ifn)) {
            JLI_ReportErrorMessage(JVM_ERROR1);
            exit(1);
        }
    
        if (showSettings != NULL) {
            ShowSettings(env, showSettings);
            CHECK_EXCEPTION_LEAVE(1);
        }
    
        // show resolved modules and continue
        if (showResolvedModules) {
            ShowResolvedModules(env);
            CHECK_EXCEPTION_LEAVE(1);
        }
    
        // list observable modules, then exit
        if (listModules) {
            ListModules(env);
            CHECK_EXCEPTION_LEAVE(1);
            LEAVE();
        }
    
        // describe a module, then exit
        if (describeModule != NULL) {
            DescribeModule(env, describeModule);
            CHECK_EXCEPTION_LEAVE(1);
            LEAVE();
        }
    
        if (printVersion || showVersion) {
            PrintJavaVersion(env);
            CHECK_EXCEPTION_LEAVE(0);
            if (printVersion) {
                LEAVE();
            }
        }
    
        // modules have been validated at startup so exit
        if (validateModules) {
            LEAVE();
        }
    
        /* If the user specified neither a class name nor a JAR file */
        if (printXUsage || printUsage || what == 0 || mode == LM_UNKNOWN) {
            PrintUsage(env, printXUsage);
            CHECK_EXCEPTION_LEAVE(1);
            LEAVE();
        }
    
        FreeKnownVMs(); /* after last possible PrintUsage */
    
        if (JLI_IsTraceLauncher()) {
            end = CurrentTimeMicros();
            JLI_TraceLauncher("%ld micro seconds to InitializeJVM\n", (long)(end-start));
        }
    
        /* At this stage, argc/argv have the application's arguments */
        if (JLI_IsTraceLauncher()){
            int i;
            printf("%s is '%s'\n", launchModeNames[mode], what);
            printf("App's argc is %d\n", argc);
            for (i=0; i < argc; i++) {
                printf("    argv[%2d] = '%s'\n", i, argv[i]);
            }
        }
    
        ret = 1;
    
        /*
         * Get the application's main class. It also checks if the main
         * method exists.
         *
         * See bugid 5030265.  The Main-Class name has already been parsed
         * from the manifest, but not parsed properly for UTF-8 support.
         * Hence the code here ignores the value previously extracted and
         * uses the pre-existing code to reextract the value.  This is
         * possibly an end of release cycle expedient.  However, it has
         * also been discovered that passing some character sets through
         * the environment has "strange" behavior on some variants of
         * Windows.  Hence, maybe the manifest parsing code local to the
         * launcher should never be enhanced.
         *
         * Hence, future work should either:
         *     1)   Correct the local parsing code and verify that the
         *          Main-Class attribute gets properly passed through
         *          all environments,
         *     2)   Remove the vestages of maintaining main_class through
         *          the environment (and remove these comments).
         *
         * This method also correctly handles launching existing JavaFX
         * applications that may or may not have a Main-Class manifest entry.
         */
        mainClass = LoadMainClass(env, mode, what);
        CHECK_EXCEPTION_NULL_LEAVE(mainClass);
        /*
         * In some cases when launching an application that needs a helper, e.g., a
         * JavaFX application with no main method, the mainClass will not be the
         * applications own main class but rather a helper class. To keep things
         * consistent in the UI we need to track and report the application main class.
         */
        appClass = GetApplicationClass(env);
        NULL_CHECK_RETURN_VALUE(appClass, -1);
    
        /* Build platform specific argument array */
        mainArgs = CreateApplicationArgs(env, argv, argc);
        CHECK_EXCEPTION_NULL_LEAVE(mainArgs);
    
        if (dryRun) {
            ret = 0;
            LEAVE();
        }
    
        /*
         * PostJVMInit uses the class name as the application name for GUI purposes,
         * for example, on OSX this sets the application name in the menu bar for
         * both SWT and JavaFX. So we'll pass the actual application class here
         * instead of mainClass as that may be a launcher or helper class instead
         * of the application class.
         */
        PostJVMInit(env, appClass, vm);
        CHECK_EXCEPTION_LEAVE(1);
    
        /*
         * The LoadMainClass not only loads the main class, it will also ensure
         * that the main method's signature is correct, therefore further checking
         * is not required. The main method is invoked here so that extraneous java
         * stacks are not in the application stack trace.
         */
        mainID = (*env)->GetStaticMethodID(env, mainClass, "main",
                                           "([Ljava/lang/String;)V");
        CHECK_EXCEPTION_NULL_LEAVE(mainID);
    
        /* Invoke main method. */
        (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);
    
        /*
         * The launcher's exit code (in the absence of calls to
         * System.exit) will be non-zero if main threw an exception.
         */
        ret = (*env)->ExceptionOccurred(env) == NULL ? 0 : 1;
    
        LEAVE();
    }
    
    /*
     * Loads a class and verifies that the main class is present and it is ok to
     * call it for more details refer to the java implementation.
     */
    static jclass
    LoadMainClass(JNIEnv *env, int mode, char *name)
    {
        jmethodID mid;
        jstring str;
        jobject result;
        jlong start = 0, end = 0;
        jclass cls = GetLauncherHelperClass(env);
        NULL_CHECK0(cls);
        if (JLI_IsTraceLauncher()) {
            start = CurrentTimeMicros();
        }
        NULL_CHECK0(mid = (*env)->GetStaticMethodID(env, cls,
                    "checkAndLoadMain",
                    "(ZILjava/lang/String;)Ljava/lang/Class;"));
    
        NULL_CHECK0(str = NewPlatformString(env, name));
        NULL_CHECK0(result = (*env)->CallStaticObjectMethod(env, cls, mid,
                                                            USE_STDERR, mode, str));
    
        if (JLI_IsTraceLauncher()) {
            end = CurrentTimeMicros();
            printf("%ld micro seconds to load main class\n", (long)(end-start));
            printf("----%s----\n", JLDEBUG_ENV_ENTRY);
        }
    
        return (jclass)result;
    }
    
    static jclass
    GetApplicationClass(JNIEnv *env)
    {
        jmethodID mid;
        jclass appClass;
        jclass cls = GetLauncherHelperClass(env);
        NULL_CHECK0(cls);
        NULL_CHECK0(mid = (*env)->GetStaticMethodID(env, cls,
                    "getApplicationClass",
                    "()Ljava/lang/Class;"));
    
        appClass = (*env)->CallStaticObjectMethod(env, cls, mid);
        CHECK_EXCEPTION_RETURN_VALUE(0);
        return appClass;
    }
    
    jclass
    GetLauncherHelperClass(JNIEnv *env)
    {
        if (helperClass == NULL) {
            NULL_CHECK0(helperClass = FindBootStrapClass(env,
                    "sun/launcher/LauncherHelper"));
        }
        return helperClass;
    }
    
  7. 核心在于 117 行的 LoadMainClass() ,可以参考注释。

  8. 153 行则是调用 main() 方法。

  9. 基本步骤为:

    1. 调用 InitializeJVM() 函数初始化JVM。
    2. 调用 LoadMainClass() 函数获取Java程序的启动类。
    3. 调用 GetStaticMethodId() 函数查找Java启动方法,其实就是 main() 方法。
    4. 调用 JNIEnv 中定义的 CallStaticVoidMethod() 方法,最终会调用 JavaCalls::call() 函数执行启动类中的 main() 方法。

Writing a class with a main

Java 程序始终由 main() 方法开始。

What can you say in the main method?

do something

  • Statements: declarations, assignments, method calls, etc.

do something again and again

  • Loops: for and while

do something under this condition

  • Branching: if/else tests

Syntax Fun

  1. Each statement must end in a semicolon.
  2. A single-line comment begins with two forward slashes.
  3. Most white space doesn’t matter.
  4. Variables are declared with a name and a type.
  5. Classes and methods must be defined within a pair of curly braces.