Paket dan impor statis di Java

Dalam tutorial Java 101 saya sebelumnya , Anda belajar bagaimana mengatur kode Anda dengan lebih baik dengan mendeklarasikan tipe referensi (juga dikenal sebagai kelas dan antarmuka) sebagai anggota tipe referensi dan blok lainnya. Saya juga menunjukkan kepada Anda cara menggunakan bersarang untuk menghindari konflik nama antara jenis referensi bertingkat dan jenis referensi tingkat atas yang memiliki nama yang sama.

Bersamaan dengan penyarangan, Java menggunakan paket untuk menyelesaikan masalah dengan nama yang sama dalam tipe referensi tingkat atas. Menggunakan impor statis juga menyederhanakan akses ke anggota statis dalam paket jenis referensi tingkat atas. Impor statis akan menghemat penekanan tombol saat mengakses anggota ini di kode Anda, tetapi ada beberapa hal yang harus diperhatikan saat Anda menggunakannya. Dalam tutorial ini, saya akan memperkenalkan Anda untuk menggunakan paket dan impor statis dalam program Java Anda.

download Dapatkan kode Download kode sumber untuk aplikasi contoh dalam tutorial Java ini. Dibuat oleh Jeff Friesen untuk JavaWorld.

Jenis referensi kemasan

Pengembang Java mengelompokkan kelas dan antarmuka terkait ke dalam paket. Menggunakan paket memudahkan untuk mencari dan menggunakan tipe referensi, menghindari konflik nama antara tipe bernama sama, dan mengontrol akses ke tipe.

Di bagian ini, Anda akan mempelajari tentang paket. Anda akan mengetahui paket yang, belajar tentang packagedan importpernyataan, dan mengeksplorasi topik tambahan akses yang dilindungi, file JAR, dan jenis pencarian.

Apa saja paket di Java?

Dalam pengembangan perangkat lunak, kami biasanya mengatur item sesuai dengan hubungan hierarkisnya. Misalnya, di tutorial sebelumnya, saya menunjukkan kepada Anda cara mendeklarasikan kelas sebagai anggota kelas lain. Kami juga dapat menggunakan sistem file untuk menyarangkan direktori di direktori lain.

Menggunakan struktur hierarki ini akan membantu Anda menghindari konflik nama. Misalnya, dalam sistem file non-hierarki (satu direktori), tidak mungkin menetapkan nama yang sama ke banyak file. Sebaliknya, sistem file hierarki memungkinkan file dengan nama yang sama ada di direktori yang berbeda. Demikian pula, dua kelas yang melingkupi bisa berisi kelas bertingkat dengan nama yang sama. Konflik nama tidak ada karena item dipartisi ke dalam ruang nama yang berbeda.

Java juga memungkinkan kita untuk mempartisi tipe referensi tingkat atas (tidak bersarang) menjadi beberapa ruang nama sehingga kita dapat mengatur tipe ini dengan lebih baik dan untuk mencegah konflik nama. Di Java, kami menggunakan fitur bahasa paket untuk mempartisi tipe referensi level atas menjadi beberapa namespace. Dalam kasus ini, paket adalah namespace unik untuk menyimpan tipe referensi. Paket dapat menyimpan kelas dan antarmuka, serta subpaket, yang merupakan paket bersarang di dalam paket lain.

Sebuah paket memiliki nama, yang harus merupakan pengenal non-reserved; misalnya java,. Operator akses anggota ( .) memisahkan nama paket dari nama subpaket dan memisahkan nama paket atau subpaket dari nama tipe. Misalnya, operator akses dua anggota dalam java.lang.Systemnama paket terpisah javadari langnama subpaket dan nama subpaket terpisah langdari Systemnama jenis.

Jenis referensi harus dideklarasikan publicagar dapat diakses dari luar paketnya. Hal yang sama berlaku untuk setiap konstanta, konstruktor, metode, atau tipe bertingkat yang harus dapat diakses. Anda akan melihat contohnya nanti di tutorial.

Pernyataan paket

Di Java, kami menggunakan pernyataan paket untuk membuat paket. Pernyataan ini muncul di bagian atas file sumber dan mengidentifikasi paket yang dimiliki jenis file sumber tersebut. Itu harus sesuai dengan sintaks berikut:

 package identifier[.identifier]*; 

Pernyataan paket dimulai dengan kata yang dipesan packagedan dilanjutkan dengan pengenal, yang secara opsional diikuti oleh urutan pengenal yang dipisahkan titik. Titik koma ( ;) mengakhiri pernyataan ini.

Pengenal pertama (paling kiri) menamai paket, dan setiap pengenal berikutnya menamai sub-paket. Misalnya, dalam package a.b;, semua tipe yang dideklarasikan di file sumber termasuk dalam bsubpaket apaket.

Konvensi penamaan paket / subpaket

Sesuai kesepakatan, kami menyatakan nama paket atau subpaket dalam huruf kecil. Jika nama terdiri dari beberapa kata, Anda mungkin ingin menggunakan huruf besar untuk setiap kata kecuali yang pertama; misalnya generalLedger,.

Urutan nama paket harus unik untuk menghindari masalah kompilasi. Misalnya, Anda membuat dua graphicspaket berbeda , dan menganggap bahwa setiap graphicspaket berisi Trianglekelas dengan antarmuka berbeda. Ketika compiler Java menemukan sesuatu seperti di bawah ini, ia perlu memverifikasi bahwa Triangle(int, int, int, int)konstruktornya ada:

 Triangle t = new Triangle(1, 20, 30, 40); 

Kotak pembatas segitiga

Pikirkan Trianglekonstruktor sebagai menentukan kotak pembatas untuk menggambar segitiga. Dua parameter pertama mengidentifikasi sudut kiri atas kotak, dan dua parameter kedua menentukan luasan kotak.

Kompilator akan mencari semua paket yang dapat diakses hingga menemukan graphicspaket yang berisi Trianglekelas. Jika paket yang ditemukan menyertakan Trianglekelas yang sesuai dengan Triangle(int, int, int, int)konstruktor, semuanya baik-baik saja. Sebaliknya, jika Trianglekelas yang ditemukan tidak memiliki Triangle(int, int, int, int)konstruktor, kompilator melaporkan kesalahan. (Saya akan mengatakan lebih banyak tentang algoritma pencarian nanti di tutorial ini.)

Skenario ini menggambarkan pentingnya memilih urutan nama paket yang unik. Ketentuan dalam memilih urutan nama yang unik adalah dengan membalik nama domain Internet Anda dan menggunakannya sebagai awalan untuk urutan tersebut. Misalnya, saya akan memilih ca.javajeffsebagai awalan saya karena javajeff.camerupakan nama domain saya. Saya kemudian akan menentukan ca.javajeff.graphics.Triangleuntuk mengakses Triangle.

Komponen nama domain dan nama paket yang valid

Komponen nama domain tidak selalu nama paket yang valid. Satu atau beberapa nama komponen mungkin diawali dengan digit ( 3D.com), berisi tanda hubung ( -) atau karakter ilegal lainnya ( ab-z.com), atau salah satu kata khusus Java ( short.com). Konvensi menentukan bahwa Anda mengawali digit dengan garis bawah ( com._3D), mengganti karakter ilegal dengan garis bawah ( com.ab_z), dan mengakhiri kata yang dicadangkan dengan garis bawah ( com.short_).

Anda perlu mengikuti beberapa aturan untuk menghindari masalah tambahan dengan pernyataan paket:

  1. Anda hanya dapat mendeklarasikan satu pernyataan paket dalam file sumber.
  2. Anda tidak dapat mendahului pernyataan paket dengan apapun selain dari komentar.

Aturan pertama, yang merupakan kasus khusus dari aturan kedua, ada karena tidak masuk akal untuk menyimpan tipe referensi dalam banyak paket. Meskipun sebuah paket dapat menyimpan beberapa tipe, sebuah tipe hanya dapat dimiliki oleh satu paket.

Jika file sumber tidak mendeklarasikan pernyataan paket, jenis file sumber dikatakan termasuk dalam paket tanpa nama . Tipe referensi non-sepele biasanya disimpan dalam paketnya sendiri dan menghindari paket yang tidak disebutkan namanya.

Java implementations map package and subpackage names to same-named directories. For example, an implementation would map graphics to a directory named graphics. In the case of the package a.b, the first letter, a would map to a directory named a and b would map to a b subdirectory of a. The compiler stores the class files that implement the package's types in the corresponding directory. Note that the unnamed package corresponds to the current directory.

Example: Packaging an audio library in Java

A practical example is helpful for fully grasping the package statement. In this section I demonstrate packages in the context of an audio library that lets you read audio files and obtain audio data. For brevity, I'll only present a skeletal version of the library.

The audio library currently consists of only two classes: Audio and WavReader. Audio describes an audio clip and is the library's main class. Listing 1 presents its source code.

Listing 1. Package statement example (Audio.java)

 package ca.javajeff.audio; public final class Audio { private int[] samples; private int sampleRate; Audio(int[] samples, int sampleRate) { this.samples = samples; this.sampleRate = sampleRate; } public int[] getSamples() { return samples; } public int getSampleRate() { return sampleRate; } public static Audio newAudio(String filename) { if (filename.toLowerCase().endsWith(".wav")) return WavReader.read(filename); else return null; // unsupported format } } 

Let's go through Listing 1 step by step.

  • The Audio.java file in Listing 1 stores the Audio class. This listing begins with a package statement that identifies ca.javajeff.audio as the class's package.
  • Audio is declared public so that it can be referenced from outside of its package. Also, it's declared final so that it cannot be extended (meaning, subclassed).
  • Audio declares privatesamples and sampleRate fields to store audio data. These fields are initialized to the values passed to Audio's constructor.
  • Audio's constructor is declared package-private (meaning, the constructor isn't declared public, private, or protected) so that this class cannot be instantiated from outside of its package.
  • Audio presents getSamples() and getSampleRate() methods for returning an audio clip's samples and sample rate. Each method is declared public so that it can be called from outside of Audio's package.
  • Audio concludes with a public and staticnewAudio() factory method for returning an Audio object corresponding to the filename argument. If the audio clip cannot be obtained, null is returned.
  • newAudio() compares filename's extension with .wav (this example only supports WAV audio). If they match, it executes return WavReader.read(filename) to return an Audio object with WAV-based audio data.

Listing 2 describes WavReader.

Listing 2. The WavReader helper class (WavReader.java)

 package ca.javajeff.audio; final class WavReader { static Audio read(String filename) { // Read the contents of filename's file and process it // into an array of sample values and a sample rate // value. If the file cannot be read, return null. For // brevity (and because I've yet to discuss Java's // file I/O APIs), I present only skeletal code that // always returns an Audio object with default values. return new Audio(new int[0], 0); } } 

WavReader is intended to read a WAV file's contents into an Audio object. (The class will eventually be larger with additional private fields and methods.) Notice that this class isn't declared public, which makes WavReader accessible to Audio but not to code outside of the ca.javajeff.audio package. Think of WavReader as a helper class whose only reason for existence is to serve Audio.

Complete the following steps to build this library:

  1. Select a suitable location in your file system as the current directory.
  2. Create a ca/javajeff/audio subdirectory hierarchy within the current directory.
  3. Copy Listings 1 and 2 to files Audio.java and WavReader.java, respectively; and store these files in the audio subdirectory.
  4. Assuming that the current directory contains the ca subdirectory, execute javac ca/javajeff/audio/*.java to compile the two source files in ca/javajeff/audio. If all goes well, you should discover Audio.class and WavReader.class files in the audio subdirectory. (Alternatively, for this example, you could switch to the audio subdirectory and execute javac *.java.)

Now that you've created the audio library, you'll want to use it. Soon, we'll look at a small Java application that demonstrates this library. First, you need to learn about the import statement.

Java's import statement

Imagine having to specify ca.javajeff.graphics.Triangle for each occurrence of Triangle in source code, repeatedly. Java provides the import statement as a convenient alternative for omitting lengthy package details.

The import statement imports types from a package by telling the compiler where to look for unqualified (no package prefix) type names during compilation. It appears near the top of a source file and must conform to the following syntax:

 import identifier[.identifier]*.(typeName | *); 

An import statement starts with reserved word import and continues with an identifier, which is optionally followed by a period-separated sequence of identifiers. A type name or asterisk (*) follows, and a semicolon terminates this statement.

The syntax reveals two forms of the import statement. First, you can import a single type name, which is identified via typeName. Second, you can import all types, which is identified via the asterisk.

The * symbol is a wildcard that represents all unqualified type names. It tells the compiler to look for such names in the right-most package of the import statement's package sequence unless the type name is found in a previously searched package. Note that using the wildcard doesn't have a performance penalty or lead to code bloat. However, it can lead to name conflicts, which you will see.

For example, import ca.javajeff.graphics.Triangle; tells the compiler that an unqualified Triangle class exists in the ca.javajeff.graphics package. Similarly, something like

 import ca.javajeff.graphics.*; 

tells the compiler to look in this package when it encounters a Triangle name, a Circle name, or even an Account name (if Account has not already been found).

Avoid the * in multi-developer projects

Saat mengerjakan proyek multi-pengembang, hindari menggunakan karakter *pengganti sehingga pengembang lain dapat dengan mudah melihat jenis apa yang digunakan dalam kode sumber Anda.