Съдържание на курса
Урок 1 — Инсталиране на Java 25 на твоя Linux Mint
От Dorian Chávez · основател на Hábil и архитект на интеграции ·
Време: 90 мин (точно: инсталацията зависи от връзката ти, а ако нещо в нея се провали, остатъкът от урока се съкращава; не бързай)
Какво изграждаш: работната среда (работещ JDK 25) и първата си програма; оставяш създадена папката ~/curso-java/ с файла Hola.java и първия файл на проекта, revisor/Revisor.java
Какво научаваш: да инсталираш SDKMAN и с него Temurin 25 и да проверяваш версията в нов терминал с java --version и javac --version; защо не се използва default-jdk; компактния файл на Java 25 (void main() и IO.println); java Hola.java; компилиране с javac и изпълнение с java поотделно и какво е .class; изпробване на идеи с jshell; и четене на първата грешка на компилатора
Откъде идваш. От Урок 0, където видя пътя на кода: изходен код, javac, байткод (bytecode) и JVM; разликата между JDK и JRE; и какво е LTS. Този урок взима тази теория и я превръща в команди. Работиш в Linux Mint 22 (всяка 22.x: 22.3, „Zena“, е актуалната според страницата с версиите на Linux Mint, с поддръжка до април 2029 г.), който е базиран на Ubuntu „Noble“.
Какво не прави този урок. Не преподава синтаксиса на Java отвъд необходимото за първата програма: типовете, низовете и управлението на потока са от Урок 2. Не инсталира Maven (Урок 6 го прави, когато стане нужно). И не използва графична среда за разработка: всичко се прави с текстов редактор и терминал, защото така се вижда какво прави всяко парче и защото това ще имаш на един сървър.
За стартиранията и за това, което не успяхме да проверим. Програмите от този урок и техните съобщения за грешка бяха изпълнени с Temurin 25.0.4 под macOS и сравнени знак по знак с показаното тук; тези, които показват Java 21 и Java 17, са получени с Temurin 21.0.12 и 17.0.20. В твоя Linux Mint трябва да дават същото, защото това са съобщения на компилатора и на JVM, а не на системата, но тези програми не бяха стартирани в Mint. Това, което наистина зависи от твоя Linux Mint, е инсталацията, а тя беше измерена на 13 октомври 2026 г. в реален Linux Mint 22.3: какво отговаря терминалът, когато няма Java, какво отпечатва инсталаторът на SDKMAN, кои идентификатори на Temurin предлагаше този ден, какво казва java --version след това и капанът с терминала, който не разбира за промяната. Ако нещо от това, което виждаш на екрана си, се различава от показаното тук в тези точки, запиши го в своя дневник: това е полезна информация. Програмите са в programas/01-instalacion/, в хранилището на курса.
След урока ще можеш да
- Инсталираш SDKMAN и с него Temurin 25, като избереш правилния идентификатор от
sdk list java, и да провериш в нов терминал, сjava --versionиjavac --version, че са от версия 25. - Обясниш защо
sudo apt install default-jdkти дава Java 21 в Mint 22 и каква грешка причинява това с компактния файл. - Напишеш, изпълниш и промениш програма на Java от един файл с
void main()иIO.printlnи да я стартираш сjava Hola.java. - Компилираш с
javac, изпълниш сjavaи кажеш какво е файлът.class, който се появи, и кога трябва да компилираш отново. - Изпробваш отделни изрази в
jshellи да излезеш от него. - Прочетеш грешка на компилатора: файла, реда, стрелката
^и редоветеsymbolиlocation. - Създадеш първия файл на проекта,
~/curso-java/revisor/Revisor.java, и да го изпълниш.
Защо, преди как
Има причина този урок да е толкова рано в курса: среда, която не работи, прави невъзможно всичко останало, а много от проблемите, които хората имат с Java през първите си дни, са проблеми с инсталацията и с версията, а не с програмирането: инсталирана е различна версия от тази, която предполага ръководството, или има инсталирани повече от една и се ползва погрешната. Затова първата стъпка на урока не е да пишеш код, а да разбереш какво имаш, а последната от частта за инсталацията е да провериш коя версия използваш. Всичко останало, файлът Hola.java, javac, jshell, е построено върху сигурността, че командата java в терминала ти е тази на Java 25.
Има и причина, свързана с настроението: който идва в този курс, вече програмира и най-напред има нужда от бърз успех. Програмата, която ще напишеш в раздел 1.2, е с три реда и я изпълняваш с една-единствена команда. Преди Java 25 същата тази програма изискваше да декларираш клас и метод public static void main(String[] args) около нея; от Java 25 е това, което виждаш по-долу. Това е най-хубавата новина на Java за този, който идва от друг език, и затова курсът я използва.
Урокът има седем части: инсталиране на JDK (1.1), първата ти програма (1.2), компилиране и изпълнение поотделно (1.3), jshell (1.4), четене на първата грешка (1.5), когато версията е погрешната (1.6) и стилът на кода (1.7), и завършва с първата стъпка на проекта (1.8). В края на урока това ще видиш, когато изпълниш revisor, който засега не проверява нищо, но вече съществува:
revisor: todavía no revisa nada
Понятията
1.1 Инсталиране на JDK
1.1.1 Първо: какво имаш
Отвори терминал (в Mint: Ctrl+Alt+T) и задай два въпроса. Първият: коя е системата:
cat /etc/os-release
Сред отпечатаните редове потърси VERSION_CODENAME и UBUNTU_CODENAME. В Linux Mint първият е името на Mint (в 22.3 е zena), а вторият е името на версията на Ubuntu, на която се базира (noble). За да инсталираш Java по метода на този курс, не ти трябва нито един от двата, но е добре да ги знаеш: много рецепти в интернет са писани за Ubuntu и откриват „името на версията“ с командата lsb_release -cs, която в Mint 22.3 отговаря zena, а не noble (измерено в Mint 22.3). Рецепта, която използва този отговор, за да състави адреса на хранилище, те оставя да сочиш към адрес, който не съществува. Ако някога рецепта за Ubuntu се провали в твоя Mint, започни оттам.
Вторият въпрос, дали вече има Java:
java --version
Може да се случи едно от три неща. Ако няма Java, терминалът отговаря, че не намира командата. В прясно инсталиран Mint 22.3 беше измерено java: not found и нещо по-полезно от текста: кодът на изход (exit code), числото, което всяка програма оставя при завършване и което виждаш, като напишеш echo $? веднага след това. Беше 127, числото, което терминалът резервира за „тази команда не съществува“. Текстът на съобщението може да варира от терминал на терминал; 127 не. (Ако предпочиташ да питаш, без да изпълняваш нищо, which java търси програмата в системата ти: без Java не отпечатва нищо и завършва с код 1. Това са два различни кода за два различни въпроса: „изпълни това“ и „къде е това?“.) Ако отпечата версия, започваща с 21 (или 11, или 17), някой е инсталирал Java преди това, може би без да се усети, защото много програми я носят като зависимост; не пречи, а в 1.1.7 ще видиш защо не е нужно да я махаш. И ако отпечата 25 и пише Temurin, вече имаш това, което курсът използва, и можеш да прескочиш към 1.1.6, за да потвърдиш, че имаш и компилатора.
1.1.2 Защо не default-jdk и защо SDKMAN
Ако попиташ в търсачка как се инсталира Java в Ubuntu или в Mint, най-честият отговор е този:
sudo apt install default-jdk
Не го прави за този курс. Пакетът default-jdk не е „най-новата версия на Java“: той е „версията на Java, която дистрибуцията е избрала по подразбиране“, а този избор се замразява, когато излезе дистрибуцията. В Ubuntu Noble, основата на Mint 22, пакетът default-jdk зависи от openjdk-21-jdk (според страницата му в архива на пакетите на Ubuntu, справка от 7 октомври 2026 г., и измерено в Mint 22.3: apt предлага default-jdk във версия 2:1.21-75, тази на Java 21). Тоест инсталира Java 21. Java 21 е уважавана LTS версия, но не е тази, която преподава курсът, и за урока важното е това, което не може: не изпълнява компактния файл с void main(). Ще го видиш в 1.6 с точното му съобщение за грешка. Който инсталира default-jdk и следва този урок, вижда първата програма да се проваля и, без да знае защо, заключава, че курсът е сгрешен. Не е; версията е погрешната.
Курсът използва Eclipse Temurin 25, дистрибуцията, с която са измерени всичките му изходи (Урок 0 обясни какво е дистрибуция), и я инсталира със SDKMAN, мениджър на версии за инструменти на Java. Три причини го решават. Първата: SDKMAN инсталира в личната ти папка (~/.sdkman/), без sudo, без да пипа програмите на системата; ако нещо се обърка, не чупиш нищо, което Mint използва. Втората: позволява да имаш няколко версии на Java едновременно и да избираш коя да ползваш с една команда, нещо, което ще ти трябва в деня, когато чужд проект изиска друга версия. Третата: със същия инструмент ще инсталираш Maven в Урок 6, с точната версия на курса. Един-единствен метод за всичко, един и същ на всяка машина.
1.1.3 От какво се нуждае SDKMAN
SDKMAN се инсталира със скрипт на bash, който изтегля и разархивира файлове, и за това използва няколко инструмента на системата. При стартиране инсталаторът търси bash, unzip, zip, tar, find, curl и sed (измерено на 13 октомври 2026 г. в Linux Mint 22.3). bash, tar, find и sed винаги идват с Mint. Трите, които може да липсват, са curl (програмата, която изтегля неща от терминала), zip и unzip. За да провериш:
which curl zip unzip
which отпечатва пътя на всяка програма, която намира, и мълчи за онази, която не намира. Ако видиш трите пътя, продължавай. Ако някоя липсва, инсталирай я с пакетния мениджър на Mint (това е единственият път, когато курсът използва sudo, защото тези наистина са инструменти на системата):
sudo apt install curl zip unzip
sudo изпълнява това, което следва, с права на администратор и ти иска паролата (докато я пишеш, няма да видиш звездички; това е нормално). apt install инсталира пакетите, които назовеш; ако някой вече е бил там, го казва и продължава с останалите.
1.1.4 Инсталиране на SDKMAN
Инсталаторът е скрипт, който публикува самият проект на официалния си сайт. Страницата за инсталиране на SDKMAN дава тази команда:
curl -s "https://get.sdkman.io" | bash
Прочети я на части. curl -s "https://get.sdkman.io" изтегля инсталационния скрипт от сайта на SDKMAN през https (шифрованата връзка, която гарантира, че говориш с този сайт и че никой не е променил съдържанието по пътя), а -s премахва лентата за напредъка. Конвейерът (pipe) | подава този текст на bash, който го изпълнява. Тоест: изтегляш програма и я стартираш на същия ред. Тази форма има лоша слава и е добре да знаеш точно защо. Опасното не е формата, а откъде идва това, което изпълняваш: скрипт от сайт, който не познаваш, или копиран от форум, може да направи всичко с твоя потребител. Тук произходът е официалният сайт на проекта, през https, а скриптът не иска sudo: работи само в личната ти папка. И ако искаш да го видиш, преди да го изпълниш, можеш да го направиш на две стъпки, което е добър навик при всеки инсталатор: curl -s "https://get.sdkman.io" -o instalar-sdkman.sh го записва във файл, четеш го с less instalar-sdkman.sh и го изпълняваш с bash instalar-sdkman.sh.
Това е, което инсталаторът отпечата на 13 октомври 2026 г. в Linux Mint 22.3 (съкратено до редовете, които имат значение: трите точки отбелязват премахнатото, а пътят на личната ти папка е показан като ~):
$ curl -s "https://get.sdkman.io" | bash
...
Looking for a previous installation of SDKMAN...
Checking Bash version...
Looking for unzip...
Looking for zip...
Looking for tar...
Looking for find...
Looking for curl...
Looking for sed...
Installing SDKMAN scripts...
...
Set version to 5.23.2 ...
...
Added sdkman init snippet to ~/.bashrc
...
All done!
...
Please open a new terminal, or run the following in the existing one:
source "$HOME/.sdkman/bin/sdkman-init.sh"
Чете се отгоре надолу: търси предишна инсталация, проверява дали имаш версия на bash, която му върши работа, търси инструментите от 1.1.3 един по един, инсталира скриптовете си в ~/.sdkman/ и добавя към файла ти ~/.bashrc фрагмент, който зарежда SDKMAN всеки път, когато се отваря терминал. Последният ред е този, на който трябва да се подчиниш: терминалът, в който си, още не знае, че SDKMAN съществува, защото е прочел ~/.bashrc при отварянето си, преди инсталацията. Или отваряш нов терминал, или казваш на този да зареди SDKMAN веднага, с командата source, която ти показва. След това провери:
$ sdk version
SDKMAN!
script: 5.23.2
native: 0.7.34 (linux x86_64)
Ако вместо това видиш sdk: command not found, значи терминалът не е заредил SDKMAN. Отвори нов или зареди SDKMAN на ръка в този с source "$HOME/.sdkman/bin/sdkman-init.sh" и опитай пак sdk version. Ако source каже, че този файл не съществува, инсталацията не е завършила (почти винаги заради инструмент от 1.1.3, който е липсвал, или заради прекъсване на връзката): прочети съобщението на инсталатора, отстрани причината и стартирай отново командата за инсталиране. Номерата на версиите ще бъдат тези от твоя ден; важното е sdk да отговаря.
1.1.5 Избор и инсталиране на Temurin 25
SDKMAN не знае коя Java искаш: избираш я ти по нейния идентификатор. За да видиш предлаганите:
sdk list java
Отпечатва дълга таблица, с по един раздел за всяка дистрибуция (Corretto, GraalVM, Liberica, Temurin, Zulu и други). Потърси тази на Temurin: идентификаторите ѝ завършват на -tem. На 13 октомври 2026 г., в Mint 22.3, този раздел съдържаше тези: 27.0.0.0+35-tem, 26.0.2+1.1-tem, 25.0.3.0+9-tem, 21.0.12+1.1-tem, 17.0.20-tem, 11.0.32+1.1-tem и 8.0.504+1-tem. Правилото за избор е едно: този, който започва с 25 и завършва на -tem. Този ден беше 25.0.3.0+9-tem; на твоя екран може да е друг, защото пълният номер се променя с всяка актуализация на Temurin. Не копирай този от урока: копирай този от твоя списък.
Една измерена подробност, която е добре да знаеш, за да не се объркаш: в дъното на списъка SDKMAN предлага идентификатор по подразбиране (този ден пишеше install the default: 25.0.4-tem) и този идентификатор не фигурираше в списъка. Затова курсът не ти иска да ползваш предложението, а да избереш от списъка това, което наистина е налично. С идентификатора в ръка:
$ sdk install java 25.0.3.0+9-tem
...
Downloading: java 25.0.3.0+9-tem
...
Repackaging Java 25.0.3.0+9-tem...
...
Installing: java 25.0.3.0+9-tem
Done installing!
Setting java 25.0.3.0+9-tem as default.
Изтегля JDK, подрежда го и го инсталира в ~/.sdkman/candidates/java/, а последният ред казва нещо важно: оставя го като основен (default), този, който ще отговаря на командата java в терминалите, които отвориш оттук нататък. В зависимост от случая (например ако вече си имал друга Java, инсталирана със SDKMAN), вместо да го фиксира, може само да те попита дали искаш да е основният: отговори да, с Enter или Y. Ако си отговорил не или не си сигурен, поправяш го по всяко време с sdk default java и идентификатора, който си инсталирал (тук sdk default java 25.0.3.0+9-tem). Изтеглянето отнема от една до няколко минути, според връзката ти.
1.1.6 Проверка на версията, в нов терминал
Ето капана, който отнема най-много време, и той беше измерен в същия Mint: в терминала, в който инсталира, java --version продължи да дава предишната Java (на тази машина имаше една системна, в /usr/bin/java). Не е, че инсталацията се е провалила: този терминал е решил къде да търси програмите, когато е бил отворен, и не разбира за промяната. Затвори терминала и отвори нов. В него:
java --version
javac --version
Двете команди, а не само първата, защото java е програмата, която изпълнява, а javac — тази, която компилира, и една непълна инсталация може да даде едната без другата (точно това е разликата между JRE и JDK от Урок 0). Това е, което java --version отпечата на 13 октомври 2026 г. в Mint 22.3, в новия терминал:
$ java --version
openjdk 25.0.3 2026-04-21 LTS
OpenJDK Runtime Environment Temurin-25.0.3+9 (build 25.0.3+9-LTS)
OpenJDK 64-Bit Server VM Temurin-25.0.3+9 (build 25.0.3+9-LTS, mixed mode, sharing)
Има две неща, които трябва да разпознаеш в своята: първият ред започва с openjdk 25 и вторият казва Temurin. Останалото се променя с актуализацията: 25.0.3 е версията (25 е основната; 0.3 е номерът на кръпката), 2026-04-21 е датата на тази актуализация, а LTS потвърждава, че е версия с дългосрочна поддръжка. Изходите от останалите уроци са измерени на друга машина с по-нова кръпка (Temurin 25.0.4); кръпката не променя нищо от това, което курсът преподава. javac --version трябва да каже javac 25, следвано от кръпката му. И ако искаш да знаеш откъде идва командата java, която отговори, which java го казва: в измерения Mint, пътят вътре в папката на SDKMAN, който завършва на .sdkman/candidates/java/current/bin/java. (which отпечатва пълния път, с личната ти папка: /home/<tu usuario>/.sdkman/candidates/java/current/bin/java; тук е съкратен с ~.) Ако which java ти даде /usr/bin/java, си в стар терминал.
1.1.7 Ако имаш повече от една Java
Няколко JDK едновременно. Ако вече си имал системна Java или ако някой ден инсталираш друга версия със SDKMAN, не е нужно да деинсталираш нищо: SDKMAN избира коя отговаря на командата
java.sdk list javaотбелязва в таблицата си тези, които имаш инсталирани.sdk use java <идентификатор>сменя версията само в текущия терминал, за да изпробваш нещо;sdk default java <идентификатор>я оставя като основна във всички нови терминали. Акоjava --versionне казва 25 и Temurin, първото е да отвориш нов терминал; второто,sdk default javaс идентификатора от 1.1.5. След това повтори проверката от 1.1.6, която се повтаря всеки път, когато промениш нещо.
1.2 Първата ти програма
1.2.1 Папката и файлът
Създай папката на курса и влез в нея:
mkdir -p ~/curso-java/revisor
cd ~/curso-java
mkdir -p създава папката, а също и междинните, ако липсват; без -p би се провалила, ако ~/curso-java не съществува, когато се иска revisor вътре в нея. ~ е пряк път към личната ти папка. В ~/curso-java/ ще живее целият курс, а вътре в нея revisor/ ще пази проекта, който расте от урок на урок.
Създай файла Hola.java с редактора, който предпочиташ (в Mint идва Xed, който се отваря с xed Hola.java) и напиши това. Спазвай главната буква в началото на името и разширението. Първият ред, // fig01_01.java, е коментар: каквото следва след // до края на реда, компилаторът го игнорира. В курса този коментар е означението, което казва как се казва програмата в хранилището; в твоя Hola.java можеш да го копираш или не, все едно е:
// fig01_01.java
void main() {
IO.println("Hola desde Java");
}
Запази го и от терминала, в папката ~/curso-java, го изпълни с името на твоя файл:
$ java Hola.java
Hola desde Java
(В хранилището на курса същата програма се казва fig01_01.java и java fig01_01.java отпечатва същото.)
Това е цялата програма. Да видим какво казва всяка част.
1.2.2 Ред по ред
void main() { декларира входната точка: първото нещо, което Java изпълнява, когато програмата работи. Съставена е от три части. main е името, което Java търси, за да започне (не е запазена дума, а конвенция на езика, която стартерът познава). () са скобите, където биха били данните, които методът получава; тук не получава никакви. void означава „не връща нищо“: main върши неща, но не предава резултат на този, който го е извикал. Фигурните скоби { } ограничават тялото: всичко между отварящата и затварящата е това, което main върши. Този „метод“ без клас около него е новост на Java 25: нарича се компактен файл (JEP 512).
IO.println("Hola desde Java"); отпечатва ред текст. IO е клас от стандартната библиотека, java.lang.IO, добавен в Java 25 за прост вход и изход (документация); тъй като е в пакета java.lang, който Java импортира винаги, не е нужно да импортираш нищо. println („отпечатай ред“) пише каквото му се подаде и нов ред. Това, което е между скобите, "Hola desde Java", е текст, който в Java се пише в двойни кавички. Точката и запетая ; завършва инструкцията: в Java е задължителна (за разлика от Python или Go) и забравянето ѝ е най-честата грешка през първия ден.
} затваря тялото.
Две неща, които не съществуват: клас около кода, както и думите public static и String[] args. През останалата част от курса това е нормалното. Кой тогава е създал класа? Компилаторът, тихомълком: взима твоя файл, дава му за име на клас името на файла (Hola) и обвива твоя main в него. Ще го провериш в 1.3 с javap.
1.2.3 Как се е писало преди
Необходимо е да разпознаваш старата форма, защото тя е тази, която ще видиш в почти всички ръководства, книги и отговори в интернет, които не са от последните две години. Същата програма, написана така, както се изискваше до Java 24, е:
public class Viejo {
public static void main(String[] args) {
System.out.println("Hola desde Java");
}
}
Работи абсолютно същото в Java 25 (проверено: отпечатва Hola desde Java). Всяка допълнителна дума имаше функция, която Урок 6 обяснява подробно и която тук само се скицира, за да не те плашат, когато ги видиш. class Viejo { ... } дефинира клас, единицата, в която Java организира кода; public го прави видим извън файла. static означава, че методът принадлежи на класа, а не на конкретен обект, така че може да се извиква, без да се създава нищо преди това. String[] args е списъкът с аргументи, които потребителят пише след името на програмата в командния ред. А System.out.println е дългият път за отпечатване: System е клас, out е обект в него (стандартният изход), а println е това, което му се иска.
За програма от един ред всичко това е церемония. Java 25 я премахва, когато не е нужна, и тя става необходима отново, когато програмата расте. Курсът използва късата форма, докато програмата не порасне, и IO.println вместо System.out.println, което в този курс е по-кратко за писане и по-лесно за четене. Ако четеш код, който използва System.out.println, той върши същото; не е нужно да го променяш.
1.2.4 Името на файла и други подробности, които хапят
Има четири подробности, които е добре да знаеш отсега. Първа: в компактен файл компилаторът използва името на файла за име на класа, така че то трябва да е валидно име за Java и по конвенция да започва с главна буква (Hola.java, Revisor.java). Втора: програмата трябва да има метод main; с друго име компилаторът я отхвърля със съобщение, което го казва (ще го видиш в 1.5). Трета: текстът на файла трябва да е в UTF-8, което е това, което редакторите на Mint произвеждат по подразбиране. Четвърта: java Hola.java и java hola.java са различни файлове в Linux, където главните букви се броят; в macOS и Windows понякога е все едно, а това е класика на „на моята машина работеше“.
И още една подробност за това какво прави java Hola.java: програмата java вижда, че си ѝ дал файл .java, компилира го в паметта и го изпълнява, без да остави нищо на диска. Нарича се режим на стартиране на файл с изходен код (source-file mode) (JEP 330) и съществува от Java 11. Това е режимът, който курсът използва от Урок 1 до Урок 5.
1.3 javac и java поотделно
1.3.1 Компилиране
Това, което java Hola.java прави на една стъпка, може да се направи на две, а да го видиш така е най-добрият начин да разбереш какво е .class. В папката, където е Hola.java:
javac Hola.java
ls Hola.*
$ ls Hola.*
Hola.class Hola.java
javac Hola.java компилира файла и, ако не е имало грешки, не отпечата нищо (компилатор, който не казва нищо, е доволен компилатор), но остави нов файл: Hola.class. Този файл е байткодът (bytecode) от Урок 0. Тежи 320 байта и започва с осемте байта, които вече познаваш (ако твоят Mint няма xxd, пропусни тази команда: не се използва повече):
$ xxd -l 8 Hola.class
00000000: cafe babe 0000 0045 .......E
За да видиш какво съдържа, без да го четеш байт по байт, JDK носи дизасемблера javap:
$ javap Hola
Compiled from "Hola.java"
final class Hola {
Hola();
void main();
}
Ето доказателството за казаното в 1.2.2: компилаторът е създал клас на име Hola (като файла) и е сложил вътре твоя метод main, освен конструктор Hola(), който ти не си писал (конструкторът е това, което създава обект от класа; Урок 3 го обяснява). С javap -c -p Hola би видял и инструкциите на main, сред тях извикване на метода println на класа IO.
1.3.2 Изпълнение
За да изпълниш .class, на стартера вече не се подава файлът, а името на класа:
$ java Hola
Hola desde Java
Без разширение: Hola, не Hola.class, нито Hola.java. Това е най-честото объркване в този раздел. Трите грешки, които това поражда, бяха измерени и е добре да са пред очите ти; първата е изпълнението на класа преди компилиране, втората е добавянето на разширението, а третата е забравянето на разширението в компилатора:
$ java Hola
Error: Could not find or load main class Hola
Caused by: java.lang.ClassNotFoundException: Hola
$ java Hola.class
Error: Could not find or load main class Hola.class
Caused by: java.lang.ClassNotFoundException: Hola.class
$ javac Hola
error: Class names, 'Hola', are only accepted if annotation processing is explicitly requested
1 error
Първите две бяха получени с езика на Java на английски; в система с регионална настройка на испански същото съобщение излиза преведено (например „Error: no se ha encontrado o cargado la clase principal Hola“, тоест „Грешка: не е намерен или зареден основният клас Hola“). Научи се да ги разпознаваш на двата езика: „не можа да се намери или зареди основният клас“ означава че java е потърсил клас с това име и не го е намерил; почти винаги се дължи на това, че не е компилирано, че си сгрешил името или че си в друга папка. Третото съобщение е най-заплетеното от трите: javac е изтълкувал Hola като име на клас, а не като файл, и оплакването му говори за „обработка на анотации“, тема, която няма нищо общо с това, което ти се е случило. Поправката е същата като винаги: javac взима името на файла, с неговото .java.
Защо java търси класа там, където го търси: по подразбиране в текущата папка. Има механизъм, с който да му кажеш да търси на други места (classpath, който Урок 6 представя, а Урок 8 изучава), но докато си в папката на .class, той го намира.
1.3.3 Старият .class
.class е замразено копие: не се обновява сам, когато редактираш .java. Беше измерено така. Компилира се програма, която отпечатва version uno, изпълни се с java V, файлът се промени, за да отпечатва version dos, и java V се изпълни отново, без компилиране:
$ java V
version uno
Продължава да казва version uno, макар .java вече да казва друго, защото java V изпълнява .class, а .class не е разбрал. Трябва да компилираш отново с javac V.java. Затова пък java V.java (с разширението) прекомпилира при всяко изпълнение и винаги отразява текущия файл, което е още една причина курсът да го използва в първите уроци. В деня, когато проектът порасне, Maven ще се грижи да прекомпилира, когато е нужно, а това е едно от нещата, за които съществува (Урок 6).
1.4 jshell: изпробване на идея без създаване на файл
Сред инструментите на JDK има един, който ще използваш всеки ден: jshell, интерактивен интерпретатор (наръчник). Пишеш израз, Java го оценява и ти показва резултата, без файл, без main, без компилиране. Това е най-бързият начин да отговориш на „какво прави това?“. В терминала напиши jshell:
$ jshell
| Welcome to JShell -- Version 25.0.4
| For an introduction type: /help intro
jshell> 1 + 1
$1 ==> 2
jshell> String s = "hola"
s ==> "hola"
jshell> s.length()
$3 ==> 4
jshell> "142" + 1
$4 ==> "1421"
jshell> /vars
| int $1 = 2
| String s = "hola"
| int $3 = 4
| String $4 = "1421"
jshell> /exit
| Goodbye
Тази сесия е съставена от реалните отговори на стартиране на jshell със същите тези команди. Всеки път, когато напишеш нещо в подканата jshell>, то го оценява и отговаря със стрелка ==> и стойността. Изразите, които нямат име, получават име автоматично: $1, $3, $4. (Къде е $2? Инструкцията String s = "hola" е дала име на своята стойност, s, и затова броячът е скочил.) Командата /vars изброява променливите, които съществуват, с техния тип, а /exit излиза. Командите на jshell започват с наклонена черта, за да се различават от кода на Java.
Обърни внимание на четвъртия запис: "142" + 1 даде "1421", същия резултат, който видя във fig00_04.java. Това е ценното на jshell: служи, за да провериш едно съмнение за десет секунди. Показва ти и грешките без шум:
jshell> int x = 5
x ==> 5
jshell> x = "a"
| Error:
| incompatible types: java.lang.String cannot be converted to int
| x = "a"
| ^-^
jshell> foo()
| Error:
| cannot find symbol
| symbol: method foo()
| foo()
| ^-^
С три ограничения, които е добре да кажем наведнъж. jshell не замества програмата: каквото изпробваш там, не се запазва (има /save за това, но истинският файл е по-добър). Някои неща от езика, които се нуждаят от контекст, като main или клас с няколко метода, не са удобни там. И номерацията $1, $2... обърква в началото. Използвай го за съмнения около израз и около метод от библиотеката, не за да пишеш revisor.
1.5 Четене на първата грешка на компилатора
1.5.1 Предизвикване
Грешките на компилатора са първото нещо, което ще четеш всеки ден, затова е добре да свикнеш с тях отсега. Предизвиква се една нарочно. Тази програма има правописна грешка: методът се казва println, а програмата пише prinln. Запази я в ~/curso-java с името на фигурата, fig01_02.java, за да казва съобщението, което ще видиш, същото като по-долу (съобщението назовава файла), и копирай и коментара от първия ред, защото номерата на редовете се броят с него:
// fig01_02.java
void main() {
IO.prinln("hola");
}
$ java fig01_02.java
fig01_02.java:3: error: cannot find symbol
IO.prinln("hola");
^
symbol: method prinln(String)
location: class IO
1 error
error: compilation failed
1.5.2 Анатомия
Това е съобщение от шест части и всичките шест се повтарят, с малки варианти, във всяка грешка при компилация, която видиш в живота си.
fig01_02.java:3:е файлът и номерът на реда. Ред 1 е коментарът// fig01_02.java, 2 еvoid main() {, а 3 е този, който се проваля. Когато грешката посочва ред, това е мястото, където да гледаш първо; понякога истинският проблем е малко преди него (в предишен ред без;), но компилаторът го усеща чак тук.error: cannot find symbolе диагнозата: „не се намира символът“. Символът е име: на променлива, на метод, на клас. Компилаторът е видял име и не знае към какво се отнася.- Цитираният ред,
IO.prinln("hola");, копиран от файла, за да не се налага да го търсиш. - Стрелката
^, която сочи колоната, в която компилаторът е открил проблема. Тук е под точката, на границата междуIOи онова, което следва: компилаторът знае, чеIOсъществува, а това, което не намира, е онова, което идва след точката. symbol: method prinln(String)казва какво не е намерил с колкото подробности може: метод на имеprinln, който получаваString(текста). Забележи, че е изведъл типаStringот аргумента, който ти си подал.location: class IOказва къде го е търсил: в класаIO. Заедноsymbolиlocationсе четат като изречение: „потърсих методаprinln(String)в класаIOи не съществува“. И това изречение ти казва как да го поправиш: или не съществува, защото си го написал погрешно (това е случаят), или защото не е в този клас, или защото липсваimport.
Накрая 1 error брои колко е намерил, а error: compilation failed е стартерът java, който предупреждава, че поради това не е изпълнил нищо. Ако компилираш с javac, съобщението е същото, но без този последен ред, а javac завършва с код на изход (exit code) 1.
1.5.3 Как се чете компилатор
Има три навика, които спестяват часове. Чети първата грешка, не последната. Често един-единствен проблем поражда каскада: една незатворена скоба може да породи десет съобщения. Поправи първото, компилирай отново и виж колко остават. Прочети цялото съобщение, преди да търсиш в интернет. symbol и location обикновено са достатъчни. И когато не разбираш една грешка, намали програмата, докато остане редът, който я произвежда.
Още пет грешки, измерени с Temurin 25.0.4 с програми от три реда и описани тук, за да ги разпознаеш, когато ги видиш. Съобщенията са показани съкратени до първия им ред:
| Какво си написал | Какво казва компилаторът |
|---|---|
липсва ; накрая: IO.println("hola") |
error: ';' expected (със стрелката точно след скобата) |
липсва затваряне на кавичките: IO.println("hola); |
error: unclosed string literal |
io.println("hola"); (малка буква) |
error: cannot find symbol / symbol: variable io / location: class <име на файла> |
липсва последната скоба } |
error: reached end of file while parsing |
методът се казва principal, а не main |
error: compact source file does not have main method in the form of void main() or void main(String[] args) |
Третият заслужава прочит: io с малка буква не е IO; Java разграничава главните букви, така че компилаторът мисли, че io е променлива, и не я намира. Последният казва с всички букви какво очаква Java за компактен файл: метод main, без параметри или с един-единствен, String[] args.
1.6 Когато версията е погрешната
Този раздел обяснява една от най-честите спънки на този, който започва с Java. Програмата от 1.2, Hola.java, работи само в Java 25. С по-стара версия грешките са други и е добре да ги видиш, за да ги разпознаваш от пръв поглед. С Java 21 (тази, която default-jdk инсталира в Mint 22) същият Hola.java дава следното. (Тези два блока са фрагменти, а не фигури на курса: изпълнени са с друг JDK, 21 и 17, и затова не се сравняват с Java 25.)
$ java --version
openjdk 21.0.12 2026-07-21 LTS
OpenJDK Runtime Environment Temurin-21.0.12+8 (build 21.0.12+8-LTS)
OpenJDK 64-Bit Server VM Temurin-21.0.12+8 (build 21.0.12+8-LTS, mixed mode, sharing)
$ java Hola.java
Hola.java:2: error: unnamed classes are a preview feature and are disabled by default.
void main() {
^
(use --enable-preview to enable unnamed classes)
1 error
error: compilation failed
Казва, по същество, че „класовете без име са функция в предварителен преглед (preview) и са изключени“. В Java 21 компактният файл съществуваше, но като предварителен преглед (идеята от Урок 0): трябваше да се поиска с --enable-preview, и с друго име и друга форма. В Java 25 вече е окончателен. С Java 17:
$ java --version
openjdk 17.0.20 2026-07-21
OpenJDK Runtime Environment Temurin-17.0.20+8 (build 17.0.20+8)
OpenJDK 64-Bit Server VM Temurin-17.0.20+8 (build 17.0.20+8, mixed mode, sharing)
$ java Hola.java
Hola.java:2: error: class, interface, enum, or record expected
void main() {
^
Hola.java:4: error: class, interface, enum, or record expected
}
^
2 errors
error: compilation failed
„Очакваше се клас, интерфейс, изброим тип или record“: Java 17 не познава компактния файл и очаква всичко във файла да е вътре в клас.
Диагнозата е винаги една и съща: java --version. Ако казва 21 или 17, не е грешка на програмата ти, а на версията. За да го поправиш, инсталирай Temurin 25 със SDKMAN (1.1.5), отвори нов терминал и, ако имаш няколко, избери правилния с sdk default java (1.1.7). Ключът: не променяй програмата си, за да се компилира в стара версия. Целият курс е написан за Java 25.
1.7 Стилът на кода
Една последна подробност, която изглежда дребна и не е. Сигурно си забелязал, че кодът в този урок използва два интервала отстъп, не четири, нито табулации. Не е каприз: това е ръководството за стил на Google за Java (Google Java Style Guide), едно от най-разпространените, и е това, което инструментът Checkstyle проверява в Урок 10. Ако кодът от уроци 1 до 9 използваше друг стил, Урок 10 би трябвало да признае, че всичко преди това е било зле написано. Много код на Java в интернет използва четири интервала; работи същото и е само конвенция. Ръководството иска три неща още отсега: отстъп от 2 интервала, редове до най-много 100 колони и фигурни скоби винаги, дори в if от един ред (когато стигнеш до него).
1.8 Стъпката на проекта: първият файл на revisor
revisor започва днес. Още не проверява нищо; важното е да съществува, на правилното място, и да работи. В папката ~/curso-java/revisor/, която създаде в 1.2.1, създай файла Revisor.java със следното съдържание (коментара на първия ред можеш да го пропуснеш; там е, защото в хранилището фигурата се казва fig01_03.java):
// fig01_03.java
void main() {
IO.println("revisor: todavía no revisa nada");
}
От ~/curso-java/revisor/ го изпълни с името на твоя файл:
$ java Revisor.java
revisor: todavía no revisa nada
(Обърни внимание на í в „todavía“ („още“): Java пише ударението без проблем, стига терминалът ти да използва UTF-8, което е нормалното в Mint. Ако някой ден видиш ? на нейно място, това е регионалната настройка на системата, а не твоята програма; Урок 0 го измери.)
Сега го направи и по старому, за да видиш .class на проекта:
javac Revisor.java
java Revisor
javap Revisor
Отпечатва същото, а javap показва final class Revisor, с метода main вътре. Като приключиш, изтрий .class с rm Revisor.class: това е твой файл, който току-що си генерирал, и вече не ти трябва, защото от сега нататък начинът за стартиране на проекта е java Revisor.java, а един стар .class в папката е капан (1.3.3) за момента, когато редактираш .java и от навик стартираш .class.
Проектът расте оттук нататък. В Урок 2 Revisor.java се научава да класифицира една услуга; в 3 се разделя на няколко файла; в 6 се превръща в проект на Maven. revisor, с който курсът завършва, тръгва от тази програма от три реда.
Грешката, която ще видиш
Този урок има две основни грешки. Първата е обичайната: компилаторът, който не намира име. Втората е на погрешната версия и е тази, която най-много обърква начинаещия.
Първата: cannot find symbol. Това е тази от fig01_02.java и се чете с шестте части от 1.5.2:
fig01_02.java:3: error: cannot find symbol
IO.prinln("hola");
^
symbol: method prinln(String)
location: class IO
Почти винаги е правописна грешка, сбъркана главна буква или забравен import. Поправя се, като се коригира името и се стартира отново. Това, което не се прави, е да се трият редове, докато изчезне: съобщението ти казва точно кое име не намира и къде го е търсило.
Втората: компактният файл с по-стара версия. С Java 21:
error: unnamed classes are a preview feature and are disabled by default.
(use --enable-preview to enable unnamed classes)
С Java 17, error: class, interface, enum, or record expected. Това са двата симптома на една-единствена болест: версия на Java преди 25. Не се поправя, като се пипа програмата, а инсталацията: java --version, за да потвърдиш, и 1.1.5 и 1.1.7, за да поправиш. Това е грешката на този, който е инсталирал default-jdk или следва старо ръководство.
Трета, по-скромна: Error: Could not find or load main class Hola. Означава, че е изпълнено java Hola, без да е компилирано, или от папка, където няма .class. Компилирай с javac Hola.java или изпълни с java Hola.java.
Какво се прави погрешно
Инсталиране на default-jdk по навик. Това е най-популярният отговор и в Mint 22 инсталира Java 21, с което компактният файл се проваля. Пътят на курса е Temurin 25 със SDKMAN (1.1.4 и 1.1.5).
Непроверяване на версията след инсталацията. „Инсталира се без грешки“ не е „командата java сочи към 25“. Проверката от 1.1.6 са две команди и десет секунди и се повтаря всеки път, когато промениш нещо в системата.
Проверка в същия терминал, в който си инсталирал. Това е измереният капан от 1.1.6: този терминал продължава да използва Java, която е имал при отварянето си, и заключаваш, че инсталацията се е провалила. Отвори нов терминал, преди да провериш.
Проверка само на java, а не на javac. JRE има първото, но не и второто. За този курс ти трябват и двете.
Изпълнение на скриптове от източници, които не познаваш. curl ... | bash изпълнява каквото изтегли и затова важното е откъде идва: инсталаторът на SDKMAN идва от официалния сайт на проекта, през https, и може да се прочете, преди да се стартира (1.1.4). Скрипт, копиран от форум или от какъвто и да е сайт, не предлага нито едно от двете. Същото важи, още повече, за всяка команда с sudo, която променя системата ти с права на администратор: ако не знаеш какво прави всяка част, не я изпълнявай.
Изпълнение на java Hola без компилиране или редактиране без прекомпилиране. Това е старият .class от 1.3.3: програмата прави това, което е правела предишната версия, и мислиш, че промяната ти не е проработила. Докато учиш, използвай java Hola.java, който винаги компилира текущия файл.
Слагане на разширение, където не трябва, и махане, където трябва. java с файл с изходен код носи .java (java Hola.java); с компилиран клас не носи разширение (java Hola); а javac винаги носи .java. Това са три правила и се учат наизуст с употребата.
Сляпо поправяне на няколко грешки наведнъж. Една грешка при компилация може да породи други, които не съществуват наистина. Поправи първата, компилирай отново и виж какво остава.
Писане с табулации или с четири интервала „защото така правят всички“. Компилира се същото, но в Урок 10 инструментът за стил ще се оплаче от всеки ред. Настрой редактора си на два интервала (в Xed: Предпочитания, раздел Редактор, ширина на табулацията 2 и вмъкване на интервали вместо табулации).
Следване на ръководство, без да се погледне датата му. Ако ръководството слага public class и public static void main за програма от един файл, той е отпреди Java 25; работи, но не е стилът на този курс. И ако говори за default-jdk или за по-стар JDK, също.
Упражнения
Упражнение 1 — Първата ти програма, по твой начин
Промени Hola.java, така че да отпечатва три реда: името ти, името на курса и фразата „Java 25“. Изпълни я с java Hola.java. След това я компилирай с javac Hola.java, изпълни я с java Hola и сравни. Накрая смени една дума от текста, без да прекомпилираш, и изпълни отново java Hola. Запиши в дневника си какво се случи и защо.
Упражнение 2 — Предскажи и провери три грешки
Напиши три развалени версии на Hola.java, една по една: (а) без ; накрая на IO.println; (б) без затваряне на кавичките на текста; (в) с io.println с малки букви. Преди да изпълниш всяка, запиши в дневника си какво съобщение очакваш. Изпълни ги и сравни. За всяка посочи реда, който сочи компилаторът, и накъде сочи стрелката.
Упражнение 3 — Попитай jshell
Отвори jshell и оцени: "142" + 1, 2300 / 1000 и 2300 / 1000.0. За всеки запиши предварително какво мислиш, че дава. След това напиши /vars и излез с /exit. Един от трите отговора вероятно ще те изненада: запиши в дневника кой и защо мислиш, че дава каквото дава. (Пълното обяснение е в Урок 2; тук важното е да си го видял.)
Упражнение 4 — Диагностицирай по съобщението
За всяко съобщение кажи какво се е случило и как се поправя, без да изпълняваш нищо:
Error: Could not find or load main class Hola/Caused by: java.lang.ClassNotFoundException: Hola, при писане наjava Hola.error: Class names, 'Hola', are only accepted if annotation processing is explicitly requested, при писане наjavac Hola.error: unnamed classes are a preview feature and are disabled by default., при писане наjava Hola.java.
Решения
Решение 1
Възможен Hola.java:
void main() {
IO.println("Dorian Chávez");
IO.println("Curso de Java de Hábil");
IO.println("Java 25");
}
С java Hola.java отпечатва трите реда. След javac Hola.java, java Hola отпечатва същото. Ако сега промениш текста на някой IO.println в .java и изпълниш java Hola без да компилираш отново, отпечатва стария текст: .class е замразено копие (1.3.3) и не знае, че си променил изходния код. За да видиш промяната, трябва да изпълниш отново javac Hola.java или да използваш java Hola.java, който прекомпилира винаги. Какво записваш в дневника: .class не се обновява сам.
Решение 2
Трите съобщения, измерени (всяко е показано с отбелязания си ред; компилаторът винаги завършва с 1 error и error: compilation failed):
a.java:2: error: ';' expected
IO.println("hola")
^
b.java:2: error: unclosed string literal
IO.println("hola);
^
c.java:2: error: cannot find symbol
io.println("hola");
^
symbol: variable io
location: class c
В (а) компилаторът посочва ред 2, не ред по-късно, и слага стрелката точно след последния знак на IO.println("hola"), защото там се е очаквало ;. В (б) стрелката сочи отварящата кавичка: оттам започва текстът, който никога не е бил затворен. В (в) стрелката сочи io, а съобщението казва variable io, не class io: тъй като малката буква не съвпада с никое име, което Java познава, компилаторът предполага, че е име на променлива. Ако грешката (в) те е накарала да помислиш „трябва да каже, че io не съществува“, точно това казва, с речника на компилатора. (В location: class c името c е това на файла c.java, с който е измерено; в твоя файл ще пише твоето.)
Решение 3
Отговорите на jshell, измерени:
jshell> "142" + 1
$1 ==> "1421"
jshell> 2300 / 1000
$2 ==> 2
jshell> 2300 / 1000.0
$3 ==> 2.3
Тази, която изненадва, е втората: 2300 / 1000 дава 2, не 2.3. Когато двете числа са цели, Java прави целочислено деление и отхвърля десетичната част. За да получиш 2.3, едното от двете трябва да е десетично, като 1000.0 в третия. Това е въпрос, който идва на място по-нататък: revisor превръща 2300 милисекунди в „2.3s“ и, ако делеше цели числа, би показал 2s. Урок 2 го обяснява подробно. (Тук си го видял със собствените си очи, а това беше важното. Номерацията в твоята сесия може да се различава според това, което си оценявал преди.)
Решение 4
- Изпълнен е класът, без да е компилиран (или от друга папка).
java Holaтърси файлHola.classи го няма. Поправя се, като първо компилираш (javac Hola.java) или изпълниш директно файла с изходния код (java Hola.java). - На
javacе дадено името на класа, а не това на файла.javacвзима.java:javac Hola.java. Текстът за „обработка на анотации“ обърква, но това е начинът, по койтоjavacтълкува аргумент без разширение. - Версията на Java е преди 25, вероятно 21: компактният файл без
classе окончателен само в Java 25. Поправя се, като инсталираш Temurin 25 със SDKMAN (1.1.5), оставиш го като основен сsdk default java, ако има друга (1.1.7), и потвърдиш сjava --versionв нов терминал.
Как разбирам, че съм успял
- В нов терминал
java --versionотпечатва първи ред, който започва сopenjdk 25, и втори, който казваTemurin, и знаеш какво означаваше кодът 127, ако преди това не си имал Java. -
sdk versionотговаря и знаеш кой идентификатор на Temurin си инсталирал и защо точно този. -
javac --versionотпечатваjavac 25, следвано от номер на кръпка. -
java Hola.javaотпечатва това, което си написал, аjavac Hola.javaоставяHola.class, който се изпълнява сjava Hola. - Можеш да обясниш защо, ако редактираш
.javaи изпълнишjava Hola, не виждаш промяната си. - В
jshell1 + 1дава$1 ==> 2и знаеш как да излезеш с/exit. - Предизвикал си грешката от
fig01_02.javaи можеш да посочиш в нея реда, стрелката,symbolиlocation. - От
~/curso-java/revisor/java Revisor.javaотпечатваrevisor: todavía no revisa nada. - Изтрил си
Revisor.class, който генерира, и знаеш защо. - Записал си в дневника си какво ти каза
java --versionна твоята машина и какво се случи с Упражнение 1.
Обобщение
Отговори, без да гледаш урока:
- Защо
default-jdkв Mint 22 не върши работа за този курс? Какво съобщение произвежда компактният файл с тази версия? - Какво прави
curl -s "https://get.sdkman.io" | bashи какво го прави приемливо тук? Как избираш идентификатора на Temurin вsdk list java? Защо трябва да отвориш нов терминал след инсталирането? - Кои две команди се изпълняват, за да се провери инсталацията, и защо и двете?
- Какво е компактният файл? Кои две неща от традиционната програма вече не са нужни?
- Каква е разликата между
java Hola.javaиjava Hola? Кога.classостава остарял? - Какво съдържа
Hola.classи как го провери сjavap? - За какво служи
jshellи какво не е? - Какво казват шестте части на съобщение като
cannot find symbol? - Как разграничаваш, само по съобщението, грешката на една програма от грешката на погрешна версия?
За допълнително четене
- SDKMAN, инсталиране и употреба: изискванията, инсталаторът и командите
list,install,useиdefault. Посетено на 13 октомври 2026 г. - JEP 512, „Compact Source Files and Instance Main Methods“: предложението, което дефинира компактния файл,
void main()и класаIO; обяснява какво създава компилаторът вместо теб и защо. Посетено на 7 октомври 2026 г. - Oracle, наръчник на
jshell(JDK 25): всички команди наjshell, от/varsдо/save. Посетено на 7 октомври 2026 г. - Oracle, наръчник на командата
java(JDK 25): стартерът на файлове с изходен код и флаговете като--enable-preview. Посетено на 7 октомври 2026 г.
Предпочитате имейл? Пишете ни на hola@habil.mx