Статьи

Что мы можем узнать из программы Java HelloWorld?

Это программа, которую знает каждый программист на Java. Я хочу посмотреть, что можно узнать из этой простой программы. Простое начало может облегчить изучение более сложных вещей. Было бы здорово, если бы этот пост было интересно читать, не только для программистов начального уровня. Пожалуйста, оставьте свои комментарии, если привет мир значит больше для вас.

HelloWorld.java

 
public class HelloWorld {
 
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.out.println("Hello World");
	}
 
}

1. Почему все начинается с класса?

Java-программы построены из классов, каждый метод и поле должны быть в классе. Это связано с его объектно-ориентированной особенностью. Объектно-ориентированные языки программирования имеют много преимуществ, таких как модульность, расширяемость и т. Д.

2. Затем «основной» метод — вход в программу

«Основной» метод — это вход в программу, и он статический. «Статический» означает, что метод является частью своего класса, а не частью объектов.

Почему это? Почему мы не помещаем нестатический метод в качестве входа в программу?

Если метод не является статическим, то для его использования необходимо сначала создать объект. Потому что метод должен быть вызван на объекте. Для входа это не реально. Таким образом, метод входа в программу является статическим.

Параметр «String [] args» указывает, что в программу можно отправить массив строк, чтобы помочь с инициализацией программы.

3. Байт-код HelloWorld

Для выполнения программы файл Java сначала компилируется в байтовый код Java, хранящийся в файле .class.

Как выглядит байт-код?

Сам байт-код не читается. Если мы используем шестнадцатеричный редактор, это выглядит следующим образом:

Java-байт-код, например,

В приведенном выше байт-коде мы видим много кода операции (например, CA, 4C и т. Д.), Каждый из которых имеет соответствующий мнемонический код (например, aload_0 в приведенном ниже примере). Код операции не читается, но мы можем использовать javap, чтобы увидеть мнемоническую форму файла .class.

«Javap -c» печатает дизассемблированный код для каждого метода в классе. Разобранный код означает инструкции, которые содержат байт-коды Java.

javap -classpath. -c HelloWorld

Compiled from "HelloWorld.java"
public class HelloWorld extends java.lang.Object{
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
 
}

Приведенный выше код содержит два метода: один является конструктором по умолчанию, который выводится компилятором; другой основной метод.

Ниже каждого метода есть последовательность инструкций, таких как aload_0, invokespecial # 1 и т. Д. То, что делает каждая инструкция, можно посмотреть в  листингах инструкций байт-кода Java . Например, aload_0 загружает ссылку в стек из локальной переменной 0, gettatic извлекает значение статического поля класса. Обратите внимание, что «# 2» после gettatic указывает на пул констант во время выполнения. Пул констант является одной из  областей данных времени выполнения JVM . Это заставляет нас взглянуть на пул констант, что можно сделать с помощью команды «javap -verbose».

Кроме того, каждая инструкция начинается с числа, такого как 0, 1, 4 и т. Д. В файле .class каждый метод имеет соответствующий массив байт-кода. Эти числа соответствуют индексу массива, в котором хранятся каждый код операции и его параметры. Каждый код операции имеет длину 1 байт, а инструкции могут иметь 0 или несколько параметров. Вот почему эти цифры не являются последовательными.

Теперь мы можем использовать «javap -verbose» для дальнейшего изучения класса.

javap -classpath. -вербоз HelloWorld

Compiled from "HelloWorld.java"
public class HelloWorld extends java.lang.Object
  SourceFile: "HelloWorld.java"
  minor version: 0
  major version: 50
  Constant pool:
const #1 = Method	#6.#15;	//  java/lang/Object."<init>":()V
const #2 = Field	#16.#17;	//  java/lang/System.out:Ljava/io/PrintStream;
const #3 = String	#18;	//  Hello World
const #4 = Method	#19.#20;	//  java/io/PrintStream.println:(Ljava/lang/String;)V
const #5 = class	#21;	//  HelloWorld
const #6 = class	#22;	//  java/lang/Object
const #7 = Asciz	<init>;
const #8 = Asciz	()V;
const #9 = Asciz	Code;
const #10 = Asciz	LineNumberTable;
const #11 = Asciz	main;
const #12 = Asciz	([Ljava/lang/String;)V;
const #13 = Asciz	SourceFile;
const #14 = Asciz	HelloWorld.java;
const #15 = NameAndType	#7:#8;//  "<init>":()V
const #16 = class	#23;	//  java/lang/System
const #17 = NameAndType	#24:#25;//  out:Ljava/io/PrintStream;
const #18 = Asciz	Hello World;
const #19 = class	#26;	//  java/io/PrintStream
const #20 = NameAndType	#27:#28;//  println:(Ljava/lang/String;)V
const #21 = Asciz	HelloWorld;
const #22 = Asciz	java/lang/Object;
const #23 = Asciz	java/lang/System;
const #24 = Asciz	out;
const #25 = Asciz	Ljava/io/PrintStream;;
const #26 = Asciz	java/io/PrintStream;
const #27 = Asciz	println;
const #28 = Asciz	(Ljava/lang/String;)V;
 
{
public HelloWorld();
  Code:
   Stack=1, Locals=1, Args_size=1
   0:	aload_0
   1:	invokespecial	#1; //Method java/lang/Object."<init>":()V
   4:	return
  LineNumberTable: 
   line 2: 0
 
 
public static void main(java.lang.String[]);
  Code:
   Stack=2, Locals=1, Args_size=1
   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
  LineNumberTable: 
   line 9: 0
   line 10: 8
 
 
}

Из  спецификации JVM : пул констант во время выполнения выполняет функцию, аналогичную функции таблицы символов для обычного языка программирования, хотя он содержит более широкий диапазон данных, чем типичная таблица символов.

«# 1» в инструкции «invokespecial # 1» указывает на константу # 1 в пуле констант. Константа «Метод № 6. № 15;». Из числа мы можем получить конечную константу рекурсивно.

LineNumberTable предоставляет отладчику информацию для указания, какая строка исходного кода Java соответствует какой инструкции байт-кода. Например, строка 9 в исходном коде Java соответствует байтовому коду 0 в основном методе, а строка 10 соответствует байтовому коду 8.

Если вы хотите узнать больше о байт-коде, вы можете создать и скомпилировать более сложный класс для просмотра. HelloWorld действительно является отправной точкой для этого.

4. Как это выполняется в JVM?

Теперь вопрос в том, как JVM загружает класс и вызывает метод main?

Перед выполнением основного метода JVM должна загрузить, связать и инициализировать класс. Загрузка приносит двоичную форму для класса / интерфейса в JVM. Связывание включает данные бинарного типа в состояние выполнения JVM. Связывание делится на 3 этапа: проверка, подготовка и дополнительное разрешение. Проверка гарантирует, что класс / интерфейс является структурно правильным. Подготовка включает выделение памяти, необходимой классу / интерфейсу. Разрешение разрешает символические ссылки. И, наконец, на этапе инициализации переменные класса получают правильные начальные значения.

JVM-загрузка сшивание инициализация

Эта работа по загрузке выполняется Java Classloader. Когда JVM запускается, используются три загрузчика классов:

  1. Загрузчик класса Bootstrap: загружает основные библиотеки Java, расположенные в 

    Каталог / jre / lib.
    Он является частью базовой JVM и написан на нативном коде.
  2. Загрузчик класса расширений: загружает код в каталоги расширений (например, 

    / Банка / Библиотека / вн).
  3. Загрузчик системного класса: загружает код, найденный на CLASSPATH.

Так класс HelloWorld загружается классом системы. Когда основной метод выполняется, он запускает  загрузку, связывание и инициализацию других классов,  если они существуют.

Наконец, кадр main () помещается в стек JVM, и программный счетчик (ПК) устанавливается соответствующим образом. Затем ПК указывает, что нужно отправить кадр println () в стек JVM. Когда метод main () завершится, он выскочит из стека и выполнение будет завершено. ( источник  )

Ссылки:
1.  Java Bytecode
2.  Загрузка
3.  Механизм загрузки классов
4.  Загрузчик классов