Java z poziomu linii poleceń

Dzisiaj na przekór moim wszystkim radom, proszę Cie nie korzystaj z IDE. Wyłącz IntelliJ Idea czy Eclipse. Przejdź przez cały artykuł używając wyłącznie podstawowego edytora tekstu. Moim celem jest przeprowadzenie Cię przez cały proces pisania kodu w Javie używając podstawowych narzędzi.

Moim zdaniem takie podejście pozwoli Ci zrozumieć te podstawy, na których opiera się cała reszta. W moim przypadku, dużo łatwiej jest mi pojąć bardziej skomplikowane rzeczy jeśli dokładnie wiem jak działają podstawowe klocki, których używa się do budowania tych bardziej skomplikowanych elementów.

Dzisiaj używał będę programu gedit, pracując w systemie Windows możesz użyć standardowego systemowego notatnika. Istotne jest to, aby program ten był w stanie utworzyć zwykły plik tekstowy, któremu nadasz rozszerzenie java.

Narzędzia dostępne w JDK

O tym czym jest JDK i czym różni się od JRE pisałem w jednym z pierwszych artykułów na blogu – o przygotowaniu środowiska programisty. Dowiesz się też tam jak zainstalować JDK. Dzisiaj będą dla Ciebie istotne trzy programy. java dostarczana jest wraz z JRE, javac i jar dostępne są wyłącznie w JDK:

  • java uruchamia maszynę wirtualną, wywołując metodę main w odpowiedniej klasie,
  • javac to kompilator, który jest w stanie utworzyć plik class z pliku java,
  • jar to narzędzie, które jest w stanie tworzyć pliki jar.

Programy javac, java czy jar dostępne są w katalogu, w którym jest zainstalowane JDK. W zależności od systemu operacyjnego katalog ten może być różny. W niektórych systemach operacyjnych po instalacji programy te od razu dostępne są na ścieżce przeszukiwania. Innymi słowy dostępne są w jednym z katalogów wskazanych przez zmienną środowiskową PATH.

Jeśli w Twoim przypadku katalog gdzie zainstalowałeś JDK nie znajduje się w zmiennej środowiskowej PATH wówczas każdy z tych programów będziesz musiał poprzedzać pełną ścieżką. Na przykład:

  • C:\Program Files\Java\jdk1.8.0_16\bin\javac
  • /home/mapi/custom/path/jdk/bin/javac

Na dłuższą metę wygodniej jest ustawić odpowiednią wartość zmiennej PATH. Szczegółową instrukcję jak to zrobić znajdziesz pod tym adresem.

Pierwsza klasa w notatniku

Zacznijmy od początku, od programu wyświetlającego Twoje imię na konsoli. Będzie to zwykła klasa o nazwie DisplayName w pakiecie domyślnym, która będzie miała metodę public static void main(String ... args). Wewnątrz tej metody wpisz instrukcję, która wypisze Twoje imię. Spróbuj napisać ten program bez IDE i zapisz go w pliku z rozszerzeniem java.

Dla mnie takie ćwiczenia na początku były dość trudne, pokazywały jak ważne i pomocne jest IDE w codziennej pracy. Mam nadzieję, że Tobie też pomoże to uświadomić.

Po tym wszystkim powinieneś mieć plik DisplayName.java, który będzie wyglądał podobnie do przykładu poniżej:

public class DisplayName {
    public static void main(String ... args) {
        System.out.println(Marcin);
    }
}

javac – skompiluj swoją klasę

I teraz dochodzimy do sedna sprawy, jak uruchomić taki program? Otóż trzeba go na początku skompilować. Do tego celu służy jedno z podstawowych i najczęściej używanych narzędzi dostarczonych wraz z JDK – kompilator języka Java. Jest to program javac. Wpisując w konsoli polecenie javac -help pokaże Ci się taka lista opcji (tutaj pokazałem wyłącznie kilka pierwszych linijek):

$ javac -help
Usage: javac <options> <source files>
where possible options include:
  -g                         Generate all debugging info
  -g:none                    Generate no debugging info
  -g:{lines,vars,source}     Generate only some debugging info
  -nowarn                    Generate no warnings
  -verbose                   Output messages about what the compiler is doing
  -deprecation               Output source locations where deprecated APIs are used
  -classpath <path>          Specify where to find user class files and annotation processors
  -cp <path>                 Specify where to find user class files and annotation processors
...

Więc skompilujmy tę pierwszą klasę :). Aby to zrobić należy uruchomić komendę javac DisplayName.java. Po jej uruchomieniu kompilator powinien utworzyć plik binarny z rozszerzeniem class – DisplayName.class. Plik ten zawiera instrukcje, które są zrozumiałe dla wirtualnej maszyny Javy.

A oto jak wygląda katalog, w którym aktualnie znajduje się kod źródłowy razem ze skompilowaną klasą:

$ tree .
.
├── DisplayName.class
└── DisplayName.java

0 directories, 2 files

Pobierz opracowania zadań z rozmów kwalifikacyjnych

Przygotowałem rozwiązania kilku zadań algorytmicznych z rozmów kwalifikacyjnych. Rozkładam je na czynniki pierwsze i pokazuję różne sposoby ich rozwiązania. Dołącz do grupy ponad 6147 Samouków, którzy jako pierwsi dowiadują się o nowych treściach na blogu, a prześlę je na Twój e-mail.

java – uruchom swoją klasę

Skoro mamy już kod, mamy też skompilowaną klasę przydałoby się ją jakoś uruchomić :). Z pomocą przychodzi kolejny bardzo ważny program – java. Program ten uruchamia wirtualną maszynę Javy. Wpisując w konsoli java -help ponownie pokażą się dostępne opcje (skróciłem je także tutaj):

$ java -help
Usage: java [-options] class [args...]
           (to execute a class)
   or  java [-options] -jar jarfile [args...]
           (to execute a jar file)
where options include:
    ...
    -server      to select the "server" VM
                  The default VM is server,
                  because you are running on a server-class machine.
    -cp <class search path of directories and zip/jar files>
    -classpath <class search path of directories and zip/jar files>
                  A : separated list of directories, JAR archives,
                  and ZIP archives to search for class files.
    ...

Teraz masz już wszystkie potrzebne składniki do uruchomienia programu. Możesz to zrobić wpisując java DisplayName:

$ java DisplayName
Marcin

Pracowity skrzat, IDE

Niby prosty program, a do jego uruchomienia trzeba użyć dwóch magicznych programów. Takie właśnie czynności robi za nas IDE. Za każdym razem1 gdy w IntelliJ użyjesz klawiszy Ctr+Schift+F10 (uruchom program) IntelliJ Idea robi podobne rzeczy. Kompiluje kod używając do tego programu javac a następnie uruchamia w odpowiedni sposób JRE używając programu java.

Pakiety

A teraz zrób krok do przodu – utwórz tę samą klasę w pakiecie pl.samouczekprogramisty.commandline i skompiluj ją przy pomocy javac. Przykład poniżej pokazuje poprawna strukturę katalogów dla klasy w takim pakiecie:

$ tree .
.
└── pl
    └── samouczekprogramisty
        └── commandline
            └── DisplayName.java

3 directories, 2 files

Aby to zrobić musisz utworzyć plik ze źródłem w odpowiednim folderze, oczywiście wtedy deklaracja package na początku pliku powinna odzwierciedlać tę ścieżkę. W moim przypadku plik wygląda następująco:

package pl.samouczekprogramisty.commandline;

public class DisplayName {
    public static void main(String ... args) {
        System.out.println("Marcin pakiet");
    }
}

Następinie używając programów javac i java mogę skompilować i uruchomić odpowiednią klasę używając poleceń:

javac pl/samouczekprogramisty/commandline/DisplayName.java
java pl.samouczekprogramisty.commandline.DisplayName

W przypadku systemów z rodziny Windows katalogi w ścieżce do klasy, którą kompilujesz oddzielone są znakiem \ a nie / jak w przykładzie.

Jak widzisz javac przyjmuje jako parametr ścieżkę do pliku, który chcesz skompilować. Program java natomiast przyjmuje pełną nazwę klasy (wraz z pakietem) wewnątrz której znajduje się metoda main. W moim przypadku oba programy uruchomiłem z katalogu, w którym jest katalog pl. Katalog pl odpowiada pierwszemu członowi pakietu.

Dochodzimy tutaj do pewnej istotnej rzeczy, gdzie program java ma “szukać” tej klasy? Odpowiedzią na to pytanie jest classpath. Po polsku będę to nazywał ścieżką przeszukiwania.

Czym jest classpath

No właśnie, czym jest classpath, magiczna ścieżka przeszukiwania? Wyjaśniając to pojęcie w jednym zdaniu – classpath to ścieżka, gdzie program java szuka klas, które potrzebne są w trakcie uruchomienia programu.

Możesz zatem zapytać “dlaczego poprzednio java DisplayName działało?” Działało, ponieważ jeśli nie ustawisz żadnej wartości, ścieżka przeszukiwania przyjmuje wartość domyślną – .. Ta kropka oznacza katalog, w którym aktualnie się znajdujesz.

Jako ćwiczenie możesz spróbować przejść do innego katalogu i uruchomić to samo polecenie, przykład poniżej pokazuje zachowanie jakie możesz uzyskać:

$ tree .
.
└── pl
    └── samouczekprogramisty
        └── commandline
            ├── DisplayName.class
            └── DisplayName.java
 
3 directories, 2 files
~$ java pl.samouczekprogramisty.commandline.DisplayName
Marcin pakiet
~$ cd pl
~/pl$ java pl.samouczekprogramisty.commandline.DisplayName
Error: Could not find or load main class pl.samouczekprogramisty.commandline.DisplayName

Jak widzisz w ostatniej linii program java wyświetlił błąd informujący, że nie może znaleźć klasy pl.samouczekprogramisty.commandline.DisplayName na aktualnej ścieżce przeszukiwania. Program szukał struktury pakietów pl/samouczekrogramisty/commandline, a w katalogu pl był jedynie katalog samouczekprogramisty, nie było katalogu pl.

Ścieżka przeszukiwania to lista katalogów oddzielonych odpowiednim znakiem gdzie program java powinien szukać klas. Na tej ścieżce poza katalogami mogą znajdować się też pliki zip czy pliki jar2. Przykład poniżej pokazuje ścieżkę przeszukiwania na której znajdują się trzy elementy:

some/path:.:other/path/file.jar

W przypadku sytemów z rodziny Windows do rozdzielenia elementów na ścieżce przeszukiwania używa się znaku ;. W pozostałych znanych mi systemach jest to znak : jak widzisz w przykładzie powyżej.

Pierwszy z nich to katalog some/path. Drugi z nich to katalog bieżący oznaczony znakiem .. Ostatni to ścieżka do pliku jar, wewnątrz którego znajdują się skompilowane klasy.

Jak ustawić classpath

Ścieżka przeszukiwania może być ustalona na dwa sposoby. Pierwszym z nich jest użycie argumentu linii poleceń -cp lub -classpath. Drugim jest ustawienie zmiennej środowiskowej CLASSPATH.

Jeśli nie użyjesz żadnej z tych metod, ścieżka przeszukiwania przyjmie wspomnianą już domyślną wartość.

Classpath w trakcie kompilacji

Nie tylko program java używa ścieżki przeszukiwania. Jest ona także używana w trakcie kompilacji. Wyobraź sobie swoją klasę, która zależy od innych klas. Na przykład używasz biblioteki Apache Commons Lang i klasy StringUtils.

Załóżmy, że chcesz użyć metody StringUtils.containsIgnoreCase. Spróbuj napisać kod w notatniku, bez pomocy IDE, który pobierze od użytkownika łańcuch znaków. Następnie sprawdzi (ignorując wielkość liter), czy w tym łańcuchu znajduje się Twoje imię.

Zdaję sobie sprawę, że jest to trudne ćwiczenie, jednak spróbuj wykonać je samodzielnie. Do wykonania tego ćwiczenia możesz użyć klasy java.util.Scanner i wspomnianej klasy StringUtils. Nie zapomnij o odpowiednich deklaracjach import. Prawdopodobnie bez dostępu do IDE nie zrobisz tego zadania bezbłędnie za pierwszym razem. Nie przejmuj się, to właśnie przy poprawianiu błędów nauczysz się najwięcej.

Plik JAR z tą biblioteką możesz pobrać z repozytorium Mavena (jeśli nie wiesz czym jest takie repozytorium odsyłam Cię do artykułu Wstęp do Gradle).

Kod, który napisałeś mógłby wyglądać jak w przykładzie poniżej:

package pl.samouczekprogramisty.commandline;
 
import java.util.Scanner;
import org.apache.commons.lang3.StringUtils;
 
public class CheckName {
    public static void main(String ... args) {
        System.out.println("Podaj zdanie");
        Scanner scanner = new Scanner(System.in);
        String sentence = scanner.nextLine();
 
        if (StringUtils.containsIgnoreCase(sentence, "Marcin")) {
            System.out.println("To zdanie zawiera moje imie!");
        }
        else {
            System.out.println("To zdanie nie zawiera mojego imienia!");
        }
    }
}

Jak widzisz, użyłem tu wcześniej wspomnianej klasy Scanner i StringUtils. Pierwsza z nich znajduje się w bibliotece standardowej Javy. Jest ona domyślnie dostępna w trakcie uruchomienia i kompilacji. Jednak w przypadku klasy StringUtils jest inaczej.

Jest to klasa zewnętrza więc musi być dostarczona zarówno w trakcie kompilacji jak i uruchomienia programu. Spójrz proszę na komendy użyte do kompilacji i uruchomienia programu:

javac -cp commons-lang3-3.5.jar pl/samouczekprogramisty/commandline/CheckName.java
java -cp .:commons-lang3-3.5.jar pl.samouczekprogramisty.commandline.CheckName

W pierwszej linii jako ścieżkę przeszukiwania ustawiamy plik jar zawierający klasę StringUtils. To wywołanie kompiluje klasę CheckName. Dzięki temu uzyskamy plik CheckName.class, który chcemy uruchomić.

Kolejna linijka to właśnie to uruchomienie. Zauważ, że w tym przypadku ścieżka przeszukiwania zawiera zarówno aktualny katalog jak i plik jar. Aktualny katalog jest niezbędny żeby znaleźć klasę CheckName. Plik jar natomiast jest wymagany do odnalezienia klasy StringUtils.

Teraz czas na Twoje eksperymenty. Co się stanie jeśli uruchomisz swój program bez -cp? :)

Pliki JAR

No dobrze, a co jeśli chcemy w łatwy sposób przekazać komuś skompilowany kod? Bardzo dobrze do tego celu nadają się pliki JAR. JAR (ang. Java Archive) to nic innego jak plik zip, wewnątrz którego znajduje się zestaw plików class ze skompilowanymi klasami3.

Klasy wewnątrz archiwum znajdują się w odpowiednich katalogach, które odzwierciedlają strukturę pakietów. Spróbujmy teraz przygotować Twój pierwszy plik jar z linii poleceń.

Tworzenie pliku JAR jest dość proste. Wystarczy podać odpowiedni zestaw parametrów jak w przykładzie poniżej:

jar cf <nazwa pliku wyjściowego> <lista katalogów, klas do umieszczenia w pliku JAR>

Program jar ma także inne zastosowania. Na przykład przy jego pomocy możesz wyświetlić zawartość istniejącego pliku JAR. Służy do tego komenda pokazana poniżej:

jar tf <ścieżka pliku JAR>

W przypadku przykładu używanego poprzednio cały zestaw komend wyglądałby następująco:

tree .
.
└── pl
    └── samouczekprogramisty
        └── commandline
            ├── CheckName.class
            ├── CheckName.java
            ├── DisplayName.class
            └── DisplayName.java
 
3 directories, 4 files
$ jar cf JavaCommandline.jar .
$ jar tf JavaCommandline.jar
META-INF/
META-INF/MANIFEST.MF
pl/
pl/samouczekprogramisty/
pl/samouczekprogramisty/commandline/
pl/samouczekprogramisty/commandline/DisplayName.java
pl/samouczekprogramisty/commandline/DisplayName.class
pl/samouczekprogramisty/commandline/CheckName.class
pl/samouczekprogramisty/commandline/CheckName.java

Jak widzisz, pliki znajdujące się w katalogu zostały dodane do pliku JAR. Program tutaj nie przeprowadził żadnego filtrowania – umieścił w archiwum także pliki java. Dlatego właśnie bardzo często pliki class umieszczamy w zupełnie innym katalogu niż pliki źródłowe.

Dodatkowo wewnątrz pliku JAR znajduje się też plik MANIFEST.MF, który zawiera metadane na temat tego archiwum. Wewnątrz tego pliku możemy skonfigurować na przykład domyślną klasę, która powinna być uruchamiana podczas wykonania polecenia

java -jar <ścieżka do pliku JAR>

Odpowiedni wpis w tym pliku może zostać utworzony4 automatycznie w trakcie tworzenia pliku jar. Możesz to tego użyć przełącznika e:

jar cfe <nazwa pliku wyjściowego> <klasa z metodą main> <lista katalogów, klas do umieszczenia w pliku JAR>

Na przykład aby utworzyć plik output.jar, który będzie zawierał wszystkie pliki z aktualnego katalogu i wskaże pl.samouczekprogramisty.commandline.MainClass jako klasę z metodą main możesz wywołać taką komendę:

jar cfe output.jar pl.samouczekprogramisty.commandline.MainClass .

Dodatkowe materiały do nauki

Ten artykuł był wybitnie praktyczny. Zależało mi na tym żeby krok po kroku pozwolił Ci realizować poszczególne etapy. Jeśli tego nie zrobiłeś, bardzo proszę spróbuj. Jeśli chciałbyś dowiedzieć się więcej o narzędziach, które opisałem w artykule mam dla Ciebie zestaw kilku dodatkowych dokumentów:

Podsumowanie

Mam nadzieję, że taki praktyczny przewodnik krok po krodu po podstawowych narzędziach dostepnych z JRE i JDK przypadł Ci do gustu. Daj znać w komentarzach jak Ci poszło z przerobieniem ćwiczeń z treści artykułu :).

Po przeczytaniu tekstu znasz podstawowe narzędzia używane przy programowaniu w języku Java. Jesteś w stanie stworzyć swój plik jar i uruchomić klasy, które skmpilujesz podstawowymi narzędziami. Dzięki temu lepiej rozumiesz “magię”, którą na codzień robi IDE. Wiesz jak bardzo upraszcza ono Twoją pracę z kodem.

Jeśli nie chcesz pominąć kolejnych artykułów dopisz się do mojego newslettera i polub Samouczka na facebooku.

Na koniec mam do Ciebie standardową prośbę, podziel się linkiem do artykułu ze znajomymi, zależy mi na dotarciu do jak największej liczby samouków, którzy chcą pogłębiać swoją wiedzę. Do następnego razu!

  1. Jak zwykle są wyjątki, ale nie są one istotne w tym przypadku. 

  2. Tak naprawdę plik jar to plik zip z innym rozszerzeniem. 

  3. Oczywiście pliki jar mogą zawierać także pliki innego rodzaju, jednak na tym etapie wystarczy wiedza o class i pliku tekstowym Manifest. 

  4. Bądź nadpisany, jeśli plik MANIFEST.MF miał już określoną klasę z metodą main. 

Pobierz opracowania zadań z rozmów kwalifikacyjnych

Przygotowałem rozwiązania kilku zadań algorytmicznych z rozmów kwalifikacyjnych. Rozkładam je na czynniki pierwsze i pokazuję różne sposoby ich rozwiązania. Dołącz do grupy ponad 6147 Samouków, którzy jako pierwsi dowiadują się o nowych treściach na blogu, a prześlę je na Twój e-mail.

Kategorie: ,

Ostatnia aktualizacja:

Autor: Marcin Pietraszek


Nie popełnia błędów tylko ten, kto nic nie robi ;). Bardzo możliwe, że znajdziesz błąd, literówkę, coś co wymaga poprawy. Jeśli chcesz możesz samodzielnie poprawić tę stronę. Jeśli nie chcesz poprawiać błędu, który udało Ci się znaleźć będę wdzięczny jeśli go zgłosisz. Z góry dziękuję!

Zostaw komentarz