Menjalankan program file tunggal di Java 11 tanpa kompilasi



Biarkan file sumber HelloUniverse.java berisi definisi kelas dan metode main statis yang menampilkan satu baris teks ke terminal:

 public class HelloUniverse{ public static void main(String[] args) { System.out.println("Hello InfoQ Universe"); } } 

Biasanya, untuk menjalankan kelas ini, Anda harus mengkompilasinya terlebih dahulu menggunakan Java compiler (javac), yang membuat file HelloUniverse.class:

 mohamed_taman$ javac HelloUniverse.java 

Maka Anda perlu menjalankan file yang dihasilkan menggunakan perintah Java virtual machine (interpreter):

 mohamed_taman$ java HelloUniverse Hello InfoQ Universe 

Kemudian virtualka akan mulai lebih dulu, yang akan memuat kelas dan mengeksekusi kode.

Dan jika Anda perlu dengan cepat memeriksa sepotong kode? Atau Anda baru mengenal Java ( dalam hal ini, titik kunci ) dan ingin bereksperimen dengan bahasa tersebut? Dua langkah yang dijelaskan dapat memperumit masalah.

Di Java SE 11, Anda dapat langsung menjalankan file sumber tunggal tanpa kompilasi perantara.

Fitur ini sangat berguna untuk pemula yang ingin bekerja dengan program sederhana. Dikombinasikan dengan jshell, Anda mendapatkan seperangkat alat yang hebat untuk mendidik pemula.

Profesional dapat menggunakan alat ini untuk mempelajari inovasi dalam bahasa atau menguji API yang tidak dikenal. Menurut pendapat kami, lebih baik mengotomatiskan banyak tugas, seperti menulis program Java dalam bentuk skrip dengan eksekusi selanjutnya dari OS shell. Sebagai hasilnya, kita dapat bekerja secara fleksibel dengan skrip shell dan menggunakan semua fitur Java. Mari kita bicarakan ini secara lebih rinci di bagian kedua artikel ini.

Fitur hebat dari Java 11 ini memungkinkan Anda untuk langsung mengeksekusi file sumber tunggal tanpa kompilasi. Mari kita bahas.

Apa yang kamu butuhkan


Untuk menjalankan kode yang disediakan dalam artikel, Anda memerlukan versi Java tidak lebih rendah dari 11. Pada saat penulisan, rilis saat ini adalah Java SE Development Kit 12.0.1 - versi terakhir ada di sini , cukup terima persyaratan lisensi dan klik tautan untuk OS Anda. Jika Anda ingin bereksperimen dengan fitur-fitur terbaru, Anda dapat mengunduh akses awal JDK 13.

Harap dicatat bahwa rilis berbagai vendor OpenJDK sekarang juga tersedia, termasuk AdoptOpenJDK .

Pada artikel ini, kita akan menggunakan editor teks biasa dan bukan Java IDE untuk menghindari semua keajaiban IDE, dan menggunakan baris perintah Java langsung di terminal.

Jalankan .java dengan Java


Fungsi JEP 330 (menjalankan program file tunggal dengan kode sumber) muncul di JDK 11. Ini memungkinkan Anda untuk secara langsung mengeksekusi file sumber dengan kode sumber Java, tanpa menggunakan juru bahasa. Kode sumber dikompilasi dalam memori dan kemudian dieksekusi oleh interpreter tanpa membuat file .class pada disk.

Namun, fungsi ini terbatas pada kode yang disimpan dalam satu file. Anda tidak dapat menjalankan banyak file sumber sekaligus.

Untuk mengatasi batasan ini, semua kelas harus didefinisikan dalam satu file. Tidak ada batasan jumlah mereka. Selain itu, ketika mereka berada di file yang sama, tidak masalah apakah itu publik atau pribadi.

Kelas pertama yang didefinisikan dalam file akan dianggap sebagai yang utama, dan metode utama harus ditempatkan di dalamnya. Artinya, ketertiban itu penting.

Contoh pertama


Mari kita mulai dengan contoh sederhana klasik - Hello Universe!

Kami akan mendemonstrasikan fitur yang dideskripsikan dengan berbagai contoh sehingga Anda mendapatkan ide bagaimana itu dapat digunakan dalam pemrograman sehari-hari.

Buat file HelloUniverse.java dengan kode dari awal artikel, kompilasi dan jalankan file kelas yang dihasilkan. Kemudian hapus, sekarang Anda akan mengerti mengapa:

 mohamed_taman$ rm HelloUniverse.class 

Jika sekarang menggunakan Java interpreter Anda menjalankan file kelas tanpa kompilasi:

 mohamed_taman$ java HelloUniverse.java Hello InfoQ Universe 

Anda akan melihat hasil yang sama: file akan dieksekusi.

Ini berarti bahwa sekarang Anda bisa menjalankan java HelloUniverse.java . Kami mentransfer kode sumber itu sendiri, dan bukan file kelas: sistem di dalamnya sendiri mengkompilasinya, meluncurkan dan menampilkan pesan di konsol.

Artinya, kompilasi masih dilakukan di bawah tenda. Dan dalam hal kesalahannya, kami akan menerima pemberitahuan tentang ini. Anda dapat memeriksa struktur direktori dan memastikan bahwa file kelas tidak dihasilkan, kompilasi dilakukan dalam memori.

Sekarang mari kita cari tahu cara kerjanya.

Bagaimana interpreter Java mengeksekusi program HelloUniverse


Di JDK 10, peluncur Java dapat beroperasi dalam tiga mode:

  1. Eksekusi file kelas.
  2. Eksekusi kelas utama dari file JAR.
  3. Eksekusi kelas utama modul.

Dan di Java 11, mode keempat muncul:

  1. Eksekusi kelas yang dinyatakan dalam file sumber.

Dalam mode ini, file sumber dikompilasi dalam memori, dan kemudian kelas pertama dari file ini dijalankan.

Sistem menentukan niat Anda untuk memasukkan file sumber sesuai dengan dua kriteria:

  1. Item pertama pada baris perintah bukanlah opsi atau bagian dari opsi.
  2. Baris ini mungkin berisi --source <vrsion> .

Dalam kasus pertama, Java pertama akan mencari tahu apakah elemen pertama dari perintah adalah opsi atau bagian dari itu. Jika ini adalah nama file yang diakhiri dengan .java, maka sistem akan menganggapnya sebagai kode sumber yang perlu dikompilasi dan dijalankan. Anda juga dapat menambahkan opsi ke perintah Java sebelum nama file sumber. Misalnya, jika Anda ingin mengatur jalur kelas ketika file sumber menggunakan dependensi eksternal.

Dalam kasus kedua, mode bekerja dengan file sumber dipilih, dan elemen pertama pada baris perintah, yang bukan merupakan pilihan, dianggap sebagai file sumber yang perlu dikompilasi dan dijalankan.

Jika file tidak memiliki ekstensi .java, maka Anda perlu menggunakan opsi --source untuk memaksanya masuk ke mode bekerja dengan file sumber.

Ini penting dalam kasus di mana file sumber adalah "skrip" yang perlu dieksekusi, dan nama file tidak sesuai dengan konvensi yang biasa untuk menamai file sumber dengan kode Java.

Menggunakan opsi --source , --source dapat menentukan versi bahasa sumber. Kami akan membicarakan ini di bawah ini.

Bisakah saya meneruskan argumen di baris perintah?


Mari kita memperluas program Hello Universe kami sehingga menampilkan salam pribadi kepada setiap pengguna yang mengunjungi InfoQ Universe:

 public class HelloUniverse2{ public static void main(String[] args){ if ( args == null || args.length< 1 ){ System.err.println("Name required"); System.exit(1); } var name = args[0]; System.out.printf("Hello, %s to InfoQ Universe!! %n", name); } } 

Simpan kode di file Greater.java. Perhatikan bahwa nama file tidak cocok dengan nama kelas publik. Ini melanggar aturan spesifikasi Java.

Jalankan kode:

 mohamed_taman$ java Greater.java "Mo. Taman" Hello, Mo. Taman to InfoQ universe!! 

Seperti yang Anda lihat, tidak masalah sama sekali bahwa kelas dan nama file tidak cocok. Pembaca yang penuh perhatian mungkin juga memperhatikan bahwa kami meneruskan argumen ke kode setelah memproses nama file. Ini berarti bahwa setiap argumen pada baris perintah setelah nama file diteruskan ke metode utama standar.

Tentukan tingkat kode sumber menggunakan opsi --source


Ada dua skenario untuk menggunakan opsi --source :

  1. Menentukan level kode sumber.
  2. Paksa Java runtime ke mode sumber.

Dalam kasus pertama, jika Anda tidak menentukan level kode sumber, versi JDK saat ini diambil untuk itu. Dan dalam kasus kedua, file dengan ekstensi selain .java dapat ditransfer untuk kompilasi dan eksekusi dengan cepat.

Mari kita simak skenario kedua dulu. Ganti nama Greater.java menjadi lebih besar tanpa ekstensi dan coba jalankan:

 mohamed_taman$ java greater "Mo. Taman" Error: Could not find or load main class greater Caused by: java.lang.ClassNotFoundException: greater 

Dengan tidak adanya ekstensi .java, penerjemah perintah mencari kelas yang dikompilasi dengan nama yang diteruskan sebagai argumen - ini adalah mode operasi pertama peluncur Java. Untuk mencegah hal ini terjadi, gunakan opsi --source untuk memaksa beralih ke mode file sumber:

 mohamed_taman$ java --source 11 greater "Mo. Taman" Hello, Mo. Taman to InfoQ universe!! 

Sekarang mari kita beralih ke skenario pertama. Kelas Greater.java kompatibel dengan JDK 10 karena mengandung kata kunci var , tetapi tidak kompatibel dengan JDK 9. Ubah source ke 10 :

 mohamed_taman$ java --source 10 Greater.java "Mo. Taman" Hello Mo. Taman to InfoQ universe!! 

Jalankan perintah sebelumnya lagi, tapi kali ini berikan --source 9 bukannya 10 :

 mohamed_taman$ java --source 9 Greater.java "Mo. Taman" Greater.java:8: warning: as of release 10, 'var' is a restricted local variable type and cannot be used for type declarations or as the element type of an array var name = args[0]; ^ Greater.java:8: error: cannot find symbol var name = args[0]; ^ symbol: class var location: class HelloWorld 1 error 1 warning error: compilation failed 

Catatan: kompiler memperingatkan bahwa var telah menjadi nama tipe terbatas di JDK 10. Tetapi karena kami memiliki bahasa level 10, kompilasi berlanjut. Namun, kerusakan terjadi karena file sumber tidak memiliki tipe bernama var .

Semuanya sederhana. Sekarang pertimbangkan penggunaan beberapa kelas.

Apakah pendekatan ini bekerja dengan banyak kelas?


Ya itu.

Pertimbangkan sebuah contoh dengan dua kelas. Kode memeriksa untuk melihat apakah nilai string yang diberikan adalah palindrome .

Berikut adalah kode yang disimpan dalam file PalindromeChecker.java:

 import static java.lang.System.*; public class PalindromeChecker { public static void main(String[] args) { if ( args == null || args.length< 1 ){ err.println("String is required!!"); exit(1); } out.printf("The string {%s} is a Palindrome!! %b %n", args[0], StringUtils .isPalindrome(args[0])); } } public class StringUtils { public static Boolean isPalindrome(String word) { return (new StringBuilder(word)) .reverse() .toString() .equalsIgnoreCase(word); } } 

Jalankan file:

 mohamed_taman:code$ java PalindromeChecker.java RediVidEr The string {RediVidEr} is a Palindrome!! True 

Jalankan lagi, ganti "RaceCar", bukan "MadAm":

 mohamed_taman:code$ java PalindromeChecker.java RaceCar The string {RaceCar} is a Palindrome!! True 

Sekarang gantikan "Mohamed" alih-alih "RaceCar":

 mohamed_taman:code$ java PalindromeChecker.java Taman The string {Taman} is a Palindrome!! false 

Seperti yang Anda lihat, Anda bisa menambahkan kelas publik sebanyak mungkin ke satu file sumber. Pastikan bahwa metode utama didefinisikan terlebih dahulu. Penerjemah akan menggunakan kelas pertama sebagai titik awal untuk memulai program setelah mengkompilasi kode dalam memori.

Bisakah saya menggunakan modul?


Ya, tidak ada batasan. Kode yang dikompilasi memori dijalankan sebagai bagian dari modul yang tidak disebutkan namanya dengan opsi --add-modules=ALL-DEFAULT , yang memberikan akses ke semua modul yang dikirimkan bersama JDK.

Artinya, kode dapat menggunakan modul yang berbeda tanpa perlu secara eksplisit mendefinisikan dependensi menggunakan module-info.java.

Mari kita lihat kode yang membuat panggilan HTTP menggunakan HTTP Client API baru, diperkenalkan di JDK 11. Perhatikan bahwa API ini diperkenalkan di Java SE 9 sebagai fitur eksperimental, tetapi sekarang mereka memiliki status fungsi penuh dari modul java.net.http .

Dalam contoh ini, kami akan memanggil REST API sederhana menggunakan metode GET untuk mendapatkan daftar pengguna. Kami beralih ke reqres.in/api/users?page=2 layanan publik. Kami menyimpan kode dalam file yang disebut UsersHttpClient.java:

 import static java.lang.System.*; import java.net.http.*; import java.net.http.HttpResponse.BodyHandlers; import java.net.*; import java.io.IOException; public class UsersHttpClient{ public static void main(String[] args) throws Exception{ var client = HttpClient.newBuilder().build(); var request = HttpRequest.newBuilder() .GET() .uri(URI.create("https://reqres.in/api/users?page=2")) .build(); var response = client.send(request, BodyHandlers.ofString()); out.printf("Response code is: %d %n",response.statusCode()); out.printf("The response body is:%n %s %n", response.body()); } } 

Jalankan program dan dapatkan hasilnya:

 mohamed_taman:code$ java UsersHttpClient.java Response code is: 200 The response body is: {"page":2,"per_page":3,"total":12,"total_pages":4,"data":[{"id":4,"first_name":"Eve","last_name":"Holt","avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/marcoramires/128.jpg"},{"id":5,"first_name":"Charles","last_name":"Morris","avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/stephenmoon/128.jpg"},{"id":6,"first_name":"Tracey","last_name":"Ramos","avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/bigmancho/128.jpg"}]} 

Sekarang Anda dapat dengan cepat menguji fitur-fitur baru yang disediakan oleh modul yang berbeda tanpa membuat modul Anda sendiri.

Mengapa skrip penting di Jawa?


Pertama, mari kita ingat apa itu skrip:

Skrip adalah program yang ditulis untuk lingkungan runtime tertentu yang mengotomatiskan pelaksanaan tugas atau perintah yang dapat dijalankan seseorang secara bergantian.

Dari definisi umum ini, kita dapat memperoleh definisi sederhana dari bahasa scripting - ini adalah bahasa pemrograman yang menggunakan konstruksi tingkat tinggi untuk menafsirkan dan mengeksekusi satu perintah (atau perintah) pada suatu waktu.

Bahasa scripting menggunakan serangkaian perintah yang ditulis dalam file. Seringkali bahasa-bahasa ini ditafsirkan (bukan dikompilasi) dan mematuhi gaya pemrograman prosedural (meskipun beberapa bahasa skrip juga memiliki sifat-sifat bahasa berorientasi objek).

Secara umum, bahasa skrip lebih mudah dipelajari dan lebih cepat mengetik daripada dibandingkan dengan bahasa kompilasi yang lebih terstruktur seperti Java, C, dan C ++. Bahasa scripting sisi server termasuk Perl, PHP, dan Python, dan di sisi klien , JavaScript.

Untuk waktu yang lama, Java dianggap sebagai bahasa kompilasi yang terstruktur dengan sangat baik yang ditafsirkan oleh mesin virtual untuk dijalankan pada arsitektur komputasi apa pun. Namun, Java tidak mudah dipelajari dan dibuat prototipe dibandingkan dengan bahasa scripting lainnya.

Namun demikian, Java telah berusia 24 tahun, digunakan oleh sekitar 10 juta pengembang di seluruh dunia. Rilis terbaru telah menambahkan sejumlah fitur baru untuk memudahkan programmer muda mempelajari bahasa ini, serta menggunakan fungsi bahasa dan API tanpa kompilasi dan IDE. Sebagai contoh, Java SE 9 memperkenalkan JShell tool (REPL), yang mendukung pemrograman interaktif.

Dan dengan dirilisnya JDK 11, bahasa ini mendapatkan kemampuan untuk mendukung skrip, karena sekarang Anda dapat mengeksekusi kode dengan panggilan sederhana ke perintah java !

Ada dua cara utama untuk menggunakan skrip di Java 11:

  1. Panggilan langsung ke perintah java .
  2. Menggunakan skrip * nix untuk baris perintah, mirip dengan skrip Bash.

Kami sudah mempertimbangkan opsi pertama, sekarang kami akan berurusan dengan opsi kedua. Ini membuka banyak kemungkinan bagi kita.

File Shebang: jalankan Java sebagai skrip shell


Jadi, di Java SE 11, dukungan untuk skrip muncul, termasuk file shebang tradisional dari dunia * nix. Untuk mendukung mereka, spesifikasi bahasa tidak diperlukan.

Dalam file shebang, dua byte pertama harus 0x23 dan 0x21. Ini adalah pengkodean karakter ASCII #! .. Semua byte berikutnya dalam file tersebut dibaca berdasarkan pada sistem pengkodean default pada platform ini.

Dengan demikian, agar file dapat dieksekusi menggunakan mekanisme shebang bawaan OS, hanya ada satu persyaratan: baris pertama dimulai dengan #! .. Ini berarti bahwa kami tidak memerlukan baris pertama khusus ketika peluncur Java secara eksplisit digunakan untuk menjalankan kode dari file sumber, seperti halnya dengan HelloUniverse.java.

Jalankan contoh berikut di terminal yang menjalankan macOS Mojave 10.14.5 . Namun pertama-tama, kami akan menetapkan aturan penting yang harus diikuti saat membuat file shebang:

  • Jangan mencampur kode Java dengan kode bahasa skrip dari skrip shell OS Anda.
  • Jika Anda perlu menambahkan opsi mesin virtual, Anda harus menentukan --source opsi pertama setelah nama file yang dapat dieksekusi di file shebang. Opsi-opsi mesin virtual meliputi: --class-path , --module-path --add-exports , --add-exports , --add-modules , --limit-modules --patch-module , --patch-module , - --upgrade-module-path , serta variasi apa pun daripadanya. Juga termasuk dalam daftar ini adalah opsi baru --enable-preview , dijelaskan dalam JEP 12 .
  • Anda harus menentukan versi Java yang digunakan dalam file sumber.
  • Baris pertama file harus dimulai dengan karakter shebang (#!). Sebagai contoh:
    #!/path/to/java --source <vrsion>
  • Untuk file sumber Java, Anda TIDAK HARUS menggunakan mekanisme shebang untuk mengeksekusi file yang sesuai dengan konvensi penamaan standar (diakhiri dengan .java)
  • Anda harus menandai file sebagai executable dengan perintah:
    chmod +x <Filname>.<Extnsion> .

Mari kita membuat file-shebang (program skrip), yang akan mencantumkan isi direktori yang namanya akan diteruskan sebagai parameter. Jika tidak ada parameter yang dilewati, direktori saat ini akan diambil secara default.

 #!/usr/bin/java --source 11 import java.nio.file.*; import static java.lang.System.*; public class DirectoryLister { public static void main(String[] args) throws Exception { vardirName = "."; if ( args == null || args.length< 1 ){ err.println("Will list the current directory"); } else { dirName = args[0]; } Files .walk(Paths.get(dirName)) .forEach(out::println); } } 

Simpan kode ke file yang disebut dirlist tanpa ekstensi, dan kemudian tandai sebagai executable: mohamed_taman:code$ chmod +x dirlist .

Jalankan file:

 mohamed_taman:code$ ./dirlist Will list the current directory . ./PalindromeChecker.java ./greater ./UsersHttpClient.java ./HelloWorld.java ./Greater.java ./dirlist 

Jalankan lagi menggunakan perintah yang melewati direktori induk, dan periksa hasilnya.

 mohamed_taman:code$ ./dirlist ../ 

Catatan: ketika mengevaluasi kode sumber, penerjemah mengabaikan garis shebang (baris pertama). Dengan demikian, file shebang dapat secara eksplisit disebut menggunakan peluncur, misalnya, dengan opsi tambahan:

 $ java -Dtrace=true --source 11 dirlist 

Ini juga harus diperhatikan: jika file skrip ada di direktori saat ini, maka Anda dapat menjalankannya seperti ini:

 $ ./dirlist 

Dan jika skrip terletak di direktori yang jalurnya ditentukan di PATH pengguna, maka Anda dapat menjalankannya seperti ini:

 $ dirlist 

Dan akhirnya, saya akan memberi Anda beberapa tips yang perlu diingat saat menggunakan skrip.

Kiat


  1. Beberapa opsi yang akan Anda lewati ke javac mungkin tidak diteruskan (atau tidak dikenali) ke java , misalnya, opsi -Werror atau -Werror .
  2. Jika ada file .class dan .java di classpath, maka peluncur akan memaksa Anda untuk menggunakan file kelas.

     mohamed_taman:code$ javac HelloUniverse.java mohamed_taman:code$ java HelloUniverse.java error: class found on application class path: HelloUniverse 

  3. Waspadai kemungkinan konflik antara kelas dan nama paket. Lihatlah struktur direktori ini:

     mohamed_taman:code$ tree . ├── Greater.java ├── HelloUniverse │ ├── java.class │ └── java.java ├── HelloUniverse.java ├── PalindromeChecker.java ├── UsersHttpClient.java ├── dirlist └── greater 

    Perhatikan dua java.java dalam paket HelloUniverse dan file HelloUniverse.java di direktori yang sama. Jika Anda mencoba menjalankan:

     mohamed_taman:code$ java HelloUniverse.java 

    lalu file mana yang akan dieksekusi pertama dan yang kedua? Peluncur tidak lagi merujuk ke file kelas dalam paket HelloUniverse. Sebaliknya, itu akan memuat dan mengeksekusi file HelloUniverse.java yang asli, mis. File tersebut akan diluncurkan di direktori saat ini.

File Shebang membuka banyak kemungkinan untuk membuat skrip untuk mengotomatisasi semua jenis tugas menggunakan alat Java.

Ringkasan


Dimulai dengan Java SE 11 dan untuk pertama kalinya dalam sejarah pemrograman, Anda dapat langsung menjalankan skrip dengan kode Java tanpa kompilasi. Ini memungkinkan Anda untuk menulis skrip Java dan menjalankannya dari baris * nix-command.

Lakukan percobaan dengan fitur ini dan bagikan pengetahuan Anda dengan orang lain.

Sumber yang bermanfaat


Source: https://habr.com/ru/post/id483888/


All Articles