Привет, дорогой мой друг!
В этой статье я хотел бы поговорить с тобой о базовых понятиях в Java-мире. Как ты, возможно, слышал, "Java-программы пишутся единожды - выполняются везде" (по-английски это звучит так "Write once, run anywhere")
Если коротко, то это означает что java-код является переносимым. То есть его можно написать один раз, а потом запускать на самых разных архитектурах и операционных системах. Например,
- Windows (архитектуры x32, x64)
- Linux (x64, arm и так далее)
- MacOS (x64, aarch64)
- другие операционные системы, о которых мы слышали реже
Если сказать это по другому, то вы можете запускать свой java-код на любой системе, для которой реализована виртуальная машина java. Здесь, вот, мы подошли к первому понятию - JVM (Java Virtual Machine). Это и есть та самая виртуальная машина java, за счет которой достигается переносимость java-кода между платформами.
Чтобы не быть голословным, предлагаю тебе прям сейчас взглянуть на страницу выбора Java для скачивания: Latest Releases | Adoptium
Это страница одного из поставщиков JDK/JRE (другими словами Java). Таких поставщиков есть, как минимум несколько. Но обо всем по порядку. Как ты можешь увидеть, этот поставщик предлагает Java для нескольких операционных систем и для самых разных компьютерных архитектур: 32-битных, 64-битных и так далее.
Итак, что такое JVM
Есть виртуальная машина java, она же JVM (Java Virtual Machine), за счет которой достигается выполнение java-кода (java-программ) на разных операционных системах.
Продолжаем дальше
JVM - это тоже программа, только большая по размеру. Может быть написана, например, на языке С++. Цель этой программы - выполнять java байт-код. Байт-код - это промежуточный код наших java-программ. Давайте посмотрим на примере, чтобы было понятней.
Я написал небольшую программу на Java, которая читает построчно текстовый файл, создает из прочитанных данных HashMap (карту/мапу) и затем выводит этот результат на экран:
import java.io.File;
import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class Application {
public static void main(String[] args) {
Map<String, Integer> goods = new HashMap<>();
try {
File goodsFile = new File("goods.txt");
Scanner reader = new Scanner(goodsFile);
while (reader.hasNext()) {
String line = reader.nextLine();
String[] columns = line.split(":");
if (columns.length == 2) {
String good = columns[0].strip();
try {
int amount = Integer.parseInt(columns[1].strip());
goods.put(good, amount);
} catch (NumberFormatException e) {
System.out.printf("%s cannot be parsed. Continue...%n", columns[1]);
}
}
}
reader.close();
} catch (FileNotFoundException e) {
System.out.println("goods.txt cannot be found on a disk");
}
goods.forEach((key, value) -> System.out.println("Product: " + key + ", amount: " + value));
}
}
Файлик с данными, которые эта программа читает: goods.txt
apple: 200
orange: 150
avocado: 80
banana: 230
Как эту программу выполнить?
Любая java-программа проходит два важных этапа:
- Компиляция
- Выполнение
Компиляция
Компиляция - это, когда мы исходный код программы превращаем в промежуточный код (байт-код). Выше ты мог увидеть исходных код программы. Тот код, который понятен разработчику. Это код, написанный на языке программирования Java.
После компиляции этого кода мы получим байт-код. Он будет выглядеть примерно вот так:
Classfile /home/hp/projects/pet/app/Application.class
Last modified Mar 4, 2023; size 2566 bytes
SHA-256 checksum ec326395fb3e756a44d5f2d35a660f9424c8e0445acb7d962bce993f5bc00277
Compiled from "Application.java"
public class Application
minor version: 0
major version: 61
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #95 // Application
super_class: #2 // java/lang/Object
interfaces: 0, fields: 0, methods: 3, attributes: 3
Constant pool:
#1 = Methodref #2.#3 // java/lang/Object."<init>":()V
#2 = Class #4 // java/lang/Object
#3 = NameAndType #5:#6 // "<init>":()V
#4 = Utf8 java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = Class #8 // java/util/HashMap
#8 = Utf8 java/util/HashMap
#9 = Methodref #7.#3 // java/util/HashMap."<init>":()V
#10 = Class #11 // java/io/File
#11 = Utf8 java/io/File
#12 = String #13 // goods.txt
#13 = Utf8 goods.txt
#14 = Methodref #10.#15 // java/io/File."<init>":(Ljava/lang/String;)V
#15 = NameAndType #5:#16 // "<init>":(Ljava/lang/String;)V
#16 = Utf8 (Ljava/lang/String;)V
#17 = Class #18 // java/util/Scanner
#18 = Utf8 java/util/Scanner
#19 = Methodref #17.#20 // java/util/Scanner."<init>":(Ljava/io/File;)V
#20 = NameAndType #5:#21 // "<init>":(Ljava/io/File;)V
#21 = Utf8 (Ljava/io/File;)V
#22 = Methodref #17.#23 // java/util/Scanner.hasNext:()Z
#23 = NameAndType #24:#25 // hasNext:()Z
#24 = Utf8 hasNext
#25 = Utf8 ()Z
#26 = Methodref #17.#27 // java/util/Scanner.nextLine:()Ljava/lang/String;
#27 = NameAndType #28:#29 // nextLine:()Ljava/lang/String;
#28 = Utf8 nextLine
#29 = Utf8 ()Ljava/lang/String;
#30 = String #31 // :
#31 = Utf8 :
#32 = Methodref #33.#34 // java/lang/String.split:(Ljava/lang/String;)[Ljava/lang/String;
#33 = Class #35 // java/lang/String
#34 = NameAndType #36:#37 // split:(Ljava/lang/String;)[Ljava/lang/String;
#35 = Utf8 java/lang/String
#36 = Utf8 split
#37 = Utf8 (Ljava/lang/String;)[Ljava/lang/String;
#38 = Methodref #33.#39 // java/lang/String.strip:()Ljava/lang/String;
#39 = NameAndType #40:#29 // strip:()Ljava/lang/String;
#40 = Utf8 strip
#41 = Methodref #42.#43 // java/lang/Integer.parseInt:(Ljava/lang/String;)I
#42 = Class #44 // java/lang/Integer
#43 = NameAndType #45:#46 // parseInt:(Ljava/lang/String;)I
#44 = Utf8 java/lang/Integer
#45 = Utf8 parseInt
#46 = Utf8 (Ljava/lang/String;)I
#47 = Methodref #42.#48 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
#48 = NameAndType #49:#50 // valueOf:(I)Ljava/lang/Integer;
#49 = Utf8 valueOf
#50 = Utf8 (I)Ljava/lang/Integer;
#51 = InterfaceMethodref #52.#53 // java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
#52 = Class #54 // java/util/Map
#53 = NameAndType #55:#56 // put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
#54 = Utf8 java/util/Map
#55 = Utf8 put
#56 = Utf8 (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
#57 = Class #58 // java/lang/NumberFormatException
#58 = Utf8 java/lang/NumberFormatException
#59 = Fieldref #60.#61 // java/lang/System.out:Ljava/io/PrintStream;
#60 = Class #62 // java/lang/System
#61 = NameAndType #63:#64 // out:Ljava/io/PrintStream;
#62 = Utf8 java/lang/System
#63 = Utf8 out
#64 = Utf8 Ljava/io/PrintStream;
#65 = String #66 // %s cannot be parsed. Continue...%n
#66 = Utf8 %s cannot be parsed. Continue...%n
#67 = Methodref #68.#69 // java/io/PrintStream.printf:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;
#68 = Class #70 // java/io/PrintStream
#69 = NameAndType #71:#72 // printf:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;
#70 = Utf8 java/io/PrintStream
#71 = Utf8 printf
#72 = Utf8 (Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;
#73 = Methodref #17.#74 // java/util/Scanner.close:()V
#74 = NameAndType #75:#6 // close:()V
#75 = Utf8 close
#76 = Class #77 // java/io/FileNotFoundException
#77 = Utf8 java/io/FileNotFoundException
#78 = String #79 // goods.txt cannot be found on a disk
#79 = Utf8 goods.txt cannot be found on a disk
#80 = Methodref #68.#81 // java/io/PrintStream.println:(Ljava/lang/String;)V
#81 = NameAndType #82:#16 // println:(Ljava/lang/String;)V
#82 = Utf8 println
#83 = InvokeDynamic #0:#84 // #0:accept:()Ljava/util/function/BiConsumer;
#84 = NameAndType #85:#86 // accept:()Ljava/util/function/BiConsumer;
#85 = Utf8 accept
#86 = Utf8 ()Ljava/util/function/BiConsumer;
#87 = InterfaceMethodref #52.#88 // java/util/Map.forEach:(Ljava/util/function/BiConsumer;)V
#88 = NameAndType #89:#90 // forEach:(Ljava/util/function/BiConsumer;)V
#89 = Utf8 forEach
#90 = Utf8 (Ljava/util/function/BiConsumer;)V
#91 = InvokeDynamic #1:#92 // #1:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/Integer;)Ljava/lang/String;
#92 = NameAndType #93:#94 // makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/Integer;)Ljava/lang/String;
#93 = Utf8 makeConcatWithConstants
#94 = Utf8 (Ljava/lang/String;Ljava/lang/Integer;)Ljava/lang/String;
#95 = Class #96 // Application
#96 = Utf8 Application
#97 = Utf8 Code
#98 = Utf8 LineNumberTable
#99 = Utf8 main
#100 = Utf8 ([Ljava/lang/String;)V
#101 = Utf8 StackMapTable
#102 = Class #103 // "[Ljava/lang/String;"
#103 = Utf8 [Ljava/lang/String;
#104 = Utf8 lambda$main$0
#105 = Utf8 (Ljava/lang/String;Ljava/lang/Integer;)V
#106 = Utf8 SourceFile
#107 = Utf8 Application.java
#108 = Utf8 BootstrapMethods
#109 = MethodHandle 6:#110 // REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#110 = Methodref #111.#112 // java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#111 = Class #113 // java/lang/invoke/LambdaMetafactory
#112 = NameAndType #114:#115 // metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#113 = Utf8 java/lang/invoke/LambdaMetafactory
#114 = Utf8 metafactory
#115 = Utf8 (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#116 = MethodType #117 // (Ljava/lang/Object;Ljava/lang/Object;)V
#117 = Utf8 (Ljava/lang/Object;Ljava/lang/Object;)V
#118 = MethodHandle 6:#119 // REF_invokeStatic Application.lambda$main$0:(Ljava/lang/String;Ljava/lang/Integer;)V
#119 = Methodref #95.#120 // Application.lambda$main$0:(Ljava/lang/String;Ljava/lang/Integer;)V
#120 = NameAndType #104:#105 // lambda$main$0:(Ljava/lang/String;Ljava/lang/Integer;)V
#121 = MethodType #105 // (Ljava/lang/String;Ljava/lang/Integer;)V
#122 = MethodHandle 6:#123 // REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
#123 = Methodref #124.#125 // java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
#124 = Class #126 // java/lang/invoke/StringConcatFactory
#125 = NameAndType #93:#127 // makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
#126 = Utf8 java/lang/invoke/StringConcatFactory
#127 = Utf8 (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
#128 = String #129 // Product: \u0001, amount: \u0001
#129 = Utf8 Product: \u0001, amount: \u0001
#130 = Utf8 InnerClasses
#131 = Class #132 // java/lang/invoke/MethodHandles$Lookup
#132 = Utf8 java/lang/invoke/MethodHandles$Lookup
#133 = Class #134 // java/lang/invoke/MethodHandles
#134 = Utf8 java/lang/invoke/MethodHandles
#135 = Utf8 Lookup
{
public Application();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 7: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=7, locals=8, args_size=1
0: new #7 // class java/util/HashMap
3: dup
4: invokespecial #9 // Method java/util/HashMap."<init>":()V
7: astore_1
8: new #10 // class java/io/File
11: dup
12: ldc #12 // String goods.txt
14: invokespecial #14 // Method java/io/File."<init>":(Ljava/lang/String;)V
17: astore_2
18: new #17 // class java/util/Scanner
21: dup
22: aload_2
23: invokespecial #19 // Method java/util/Scanner."<init>":(Ljava/io/File;)V
26: astore_3
27: aload_3
28: invokevirtual #22 // Method java/util/Scanner.hasNext:()Z
31: ifeq 119
34: aload_3
35: invokevirtual #26 // Method java/util/Scanner.nextLine:()Ljava/lang/String;
38: astore 4
40: aload 4
42: ldc #30 // String :
44: invokevirtual #32 // Method java/lang/String.split:(Ljava/lang/String;)[Ljava/lang/String;
47: astore 5
49: aload 5
51: arraylength
52: iconst_2
53: if_icmpne 116
56: aload 5
58: iconst_0
59: aaload
60: invokevirtual #38 // Method java/lang/String.strip:()Ljava/lang/String;
63: astore 6
65: aload 5
67: iconst_1
68: aaload
69: invokevirtual #38 // Method java/lang/String.strip:()Ljava/lang/String;
72: invokestatic #41 // Method java/lang/Integer.parseInt:(Ljava/lang/String;)I
75: istore 7
77: aload_1
78: aload 6
80: iload 7
82: invokestatic #47 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
85: invokeinterface #51, 3 // InterfaceMethod java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
90: pop
91: goto 116
94: astore 7
96: getstatic #59 // Field java/lang/System.out:Ljava/io/PrintStream;
99: ldc #65 // String %s cannot be parsed. Continue...%n
101: iconst_1
102: anewarray #2 // class java/lang/Object
105: dup
106: iconst_0
107: aload 5
109: iconst_1
110: aaload
111: aastore
112: invokevirtual #67 // Method java/io/PrintStream.printf:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;
115: pop
116: goto 27
119: aload_3
120: invokevirtual #73 // Method java/util/Scanner.close:()V
123: goto 135
126: astore_2
127: getstatic #59 // Field java/lang/System.out:Ljava/io/PrintStream;
130: ldc #78 // String goods.txt cannot be found on a disk
132: invokevirtual #80 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
135: aload_1
136: invokedynamic #83, 0 // InvokeDynamic #0:accept:()Ljava/util/function/BiConsumer;
141: invokeinterface #87, 2 // InterfaceMethod java/util/Map.forEach:(Ljava/util/function/BiConsumer;)V
146: return
Exception table:
from to target type
65 91 94 Class java/lang/NumberFormatException
8 123 126 Class java/io/FileNotFoundException
LineNumberTable:
line 10: 0
line 13: 8
line 14: 18
line 16: 27
line 17: 34
line 18: 40
line 20: 49
line 21: 56
line 24: 65
line 25: 77
line 28: 91
line 26: 94
line 27: 96
line 30: 116
line 32: 119
line 35: 123
line 33: 126
line 34: 127
line 37: 135
line 38: 146
StackMapTable: number_of_entries = 6
frame_type = 254 /* append */
offset_delta = 27
locals = [ class java/util/Map, class java/io/File, class java/util/Scanner ]
frame_type = 255 /* full_frame */
offset_delta = 66
locals = [ class "[Ljava/lang/String;", class java/util/Map, class java/io/File, class java/util/Scanner, class java/lang/String, class "[Ljava/lang/String;", class java/lang/String ]
stack = [ class java/lang/NumberFormatException ]
frame_type = 248 /* chop */
offset_delta = 21
frame_type = 2 /* same */
frame_type = 255 /* full_frame */
offset_delta = 6
locals = [ class "[Ljava/lang/String;", class java/util/Map ]
stack = [ class java/io/FileNotFoundException ]
frame_type = 8 /* same */
}
SourceFile: "Application.java"
BootstrapMethods:
0: #109 REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
#116 (Ljava/lang/Object;Ljava/lang/Object;)V
#118 REF_invokeStatic Application.lambda$main$0:(Ljava/lang/String;Ljava/lang/Integer;)V
#121 (Ljava/lang/String;Ljava/lang/Integer;)V
1: #122 REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
Method arguments:
#128 Product: \u0001, amount: \u0001
InnerClasses:
public static final #135= #131 of #133; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
Этот код понятен лишь виртуальной машине java (JVM).
Выполнение
Байт-код у нас есть. Теперь этот код можно выполнить, но есть один очень важный момент: в дополнении к JVM еще идут java-библиотеки. В этих библиотеках находится реализация java-коллекций для работы с данными, классы для работы с потоками ввода/вывода и еще много чего полезного и необходимого для работы.
Так вот JVM + библиотеки = JRE (Java Runtime Environment).
JRE - это среда выполнения java. Для того чтобы запустить java-программу нам нужна именно JRE. Это минимальный набор для запуска и работы любой java-программы.
Но как быть, если вам нужно заниматься разработкой на java? Здесь уже не обойтись без JDK (Java Development Kit).
JDK - это полноценный набор для разработчика на java. В составе JDK ты найдешь:
- компилятор - чтобы получить байт-код из исходного кода своей программы;
- отладчик - чтобы находить причины ошибок в своем коде;
- инструмент для чтения байт-кода (если тебе это будет для чего-то нужно);
- генератор документации для твоих классов и их методов;
- и еще много чего полезного и интересного.
И в самом конце я представляю вашему вниманию полную обзорную картину, показывающую взаимосвязь между JVM, JRE и JDK:
Вместо заключения
Java - это:
- и язык программирования;
- и название платформы (по аналогии с .NET для C#).
В этой статье мы поговорили о платформе Java и о взаимосвязи между ее компонентами, такими как JVM, JRE, JDK. Напомню, что платформа Java - это программное обеспечение, дающее нам возможность разрабатывать и затем запускать java-программы.
Если говорить про языки программирования, то ты можешь писать не только на Java, но и на Scala, Kotlin, Groovy, Clojure. Это языки программирования, которые тоже совместимы с платформой Java. Совместимы - это значит, что они тоже компилируются в байт-код, который может выполнять JVM.