Pakej dan import statik di Jawa

Dalam tutorial Java 101 saya sebelumnya , anda belajar bagaimana mengatur kod anda dengan lebih baik dengan menyatakan jenis rujukan (juga dikenali sebagai kelas dan antara muka) sebagai anggota jenis dan blok rujukan yang lain. Saya juga menunjukkan cara menggunakan sarang untuk mengelakkan konflik nama antara jenis rujukan bersarang dan jenis rujukan tingkat atas yang mempunyai nama yang sama.

Bersama dengan bersarang, Java menggunakan pakej untuk menyelesaikan masalah nama sama dalam jenis rujukan tingkat atas. Menggunakan import statik juga mempermudah akses kepada ahli statik dalam jenis rujukan tingkat atas yang dikemas. Import statik akan menjimatkan penekanan kekunci anda ketika mengakses ahli ini dalam kod anda, tetapi ada beberapa perkara yang perlu diperhatikan semasa anda menggunakannya. Dalam tutorial ini, saya akan memperkenalkan anda untuk menggunakan pakej dan import statik dalam program Java anda.

muat turun Dapatkan kod Muat turun kod sumber misalnya aplikasi dalam tutorial Java ini. Dicipta oleh Jeff Friesen untuk JavaWorld.

Jenis rujukan pembungkusan

Pembangun Java mengumpulkan kelas dan antara muka yang berkaitan ke dalam pakej. Menggunakan pakej menjadikannya lebih mudah untuk mencari dan menggunakan jenis rujukan, mengelakkan konflik nama antara jenis nama yang sama, dan mengawal akses ke jenis.

Di bahagian ini, anda akan belajar mengenai pakej. Anda akan mengetahui pakej apa, belajar tentang packagedan importpernyataan, dan meneroka topik tambahan akses terlindung, fail JAR, dan carian jenis.

Apakah pakej di Java?

Dalam pembangunan perisian, kita biasanya mengatur item mengikut hubungan hierarki mereka. Sebagai contoh, dalam tutorial sebelumnya, saya menunjukkan cara mengisytiharkan kelas sebagai ahli kelas lain. Kita juga boleh menggunakan sistem fail untuk menyusun direktori di direktori lain.

Menggunakan struktur hierarki ini akan membantu anda mengelakkan konflik nama. Sebagai contoh, dalam sistem fail bukan hierarki (satu direktori), tidak mungkin untuk memberikan nama yang sama ke beberapa fail. Sebaliknya, sistem fail hierarki membolehkan fail dengan nama yang sama wujud dalam direktori yang berbeza. Begitu juga, dua kelas lampiran boleh mengandungi kelas bersarang dengan nama yang sama. Konflik nama tidak wujud kerana item dipartisi ke ruang nama yang berbeza.

Java juga membolehkan kita membahagikan jenis rujukan tingkat atas (tidak bersarang) ke dalam beberapa ruang nama supaya kita dapat mengatur jenis ini dengan lebih baik dan untuk mengelakkan konflik nama. Di Java, kami menggunakan fitur bahasa paket untuk membahagikan jenis rujukan tingkat atas ke dalam beberapa ruang nama. Dalam kes ini, pakej adalah ruang nama yang unik untuk menyimpan jenis rujukan. Pakej boleh menyimpan kelas dan antara muka, serta sub-paket, yang merupakan pakej yang bersarang di dalam pakej lain.

Pakej mempunyai nama, yang mestilah pengecam tidak terpelihara; sebagai contoh , java. Operator akses anggota ( .) memisahkan nama pakej dari nama subpakej dan memisahkan nama pakej atau subkemas dari nama jenis. Contohnya, pengendali akses dua anggota dalam java.lang.Systemnama pakej yang terpisah javadari langnama subpakej dan nama subpakej yang terpisah langdari Systemnama jenis.

Jenis rujukan mesti dinyatakan publicboleh diakses dari luar bungkusannya. Perkara yang sama berlaku untuk pemalar, konstruktor, kaedah, atau jenis bersarang yang mesti diakses. Anda akan melihat contohnya di tutorial nanti.

Penyataan pakej

Di Java, kami menggunakan pernyataan paket untuk membuat paket. Pernyataan ini muncul di bahagian atas fail sumber dan mengenal pasti paket yang menjadi milik jenis fail sumber. Ia mesti mematuhi sintaks berikut:

 package identifier[.identifier]*; 

Pernyataan bungkusan dimulakan dengan kata terpelihara packagedan dilanjutkan dengan pengecam, yang secara opsional diikuti oleh urutan pengenal yang dipisahkan noktah. Tanda titik koma ( ;) menamatkan pernyataan ini.

Pengecam pertama (paling kiri) menamakan paket, dan setiap pengecam berikutnya menamakan subkemas. Sebagai contoh, dalam package a.b;, semua jenis yang dinyatakan dalam fail sumber tergolong dalam bsubpakej apakej.

Konvensyen penamaan pakej / subpakej

Secara konvensional, kami menyatakan nama bungkusan atau sub paket dalam huruf kecil. Apabila namanya terdiri daripada beberapa perkataan, anda mungkin ingin menggunakan huruf besar setiap perkataan kecuali yang pertama; sebagai contoh , generalLedger.

Urutan nama pakej mesti unik untuk mengelakkan masalah penyusunan. Sebagai contoh, anggap anda membuat dua graphicspakej yang berbeza , dan menganggap bahawa setiap graphicspakej mengandungi Trianglekelas dengan antara muka yang berbeza. Apabila penyusun Java menemui sesuatu seperti yang ada di bawah, ia perlu mengesahkan bahawa Triangle(int, int, int, int)pembangunnya ada:

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

Kotak pengikat segitiga

Fikirkan Trianglepembina sebagai menentukan kotak pengikat untuk melukis segitiga. Dua parameter pertama mengenal pasti sudut kiri atas kotak, dan dua parameter kedua menentukan jangka panjang kotak.

Penyusun akan mencari semua pakej yang boleh diakses sehingga menemui graphicspakej yang mengandungi Trianglekelas. Sekiranya pakej yang dijumpai merangkumi Trianglekelas yang sesuai dengan Triangle(int, int, int, int)konstruktor, semuanya baik-baik saja. Jika tidak, jika Trianglekelas yang dijumpai tidak mempunyai Triangle(int, int, int, int)konstruktor, penyusun akan melaporkan ralat. (Saya akan mengatakan lebih banyak mengenai algoritma carian kemudian dalam tutorial ini.)

Senario ini menggambarkan pentingnya memilih urutan nama pakej yang unik. Konvensyen dalam memilih urutan nama yang unik adalah untuk membalikkan nama domain Internet anda dan menggunakannya sebagai awalan untuk urutan tersebut. Sebagai contoh, saya akan memilih ca.javajeffsebagai awalan saya kerana javajeff.canama domain saya. Saya kemudian akan menentukan ca.javajeff.graphics.Triangleuntuk mengakses Triangle.

Komponen nama domain dan nama pakej yang sah

Komponen nama domain tidak selalu merupakan nama pakej yang sah. Satu atau lebih nama komponen mungkin bermula dengan digit ( 3D.com), mengandungi tanda hubung ( -) atau watak haram lain ( ab-z.com), atau menjadi salah satu kata cadangan Java ( short.com). Konvensyen menetapkan bahawa anda awalan digit dengan garis bawah ( com._3D), gantikan watak tidak sah dengan garis bawah ( com.ab_z), dan akhiran kata terpelihara dengan garis bawah ( com.short_).

Anda perlu mengikuti beberapa peraturan untuk mengelakkan masalah tambahan dengan pernyataan pakej:

  1. Anda hanya boleh menyatakan satu pernyataan pakej dalam fail sumber.
  2. Anda tidak boleh mendahului penyataan pakej dengan apa-apa selain komen.

Peraturan pertama, yang merupakan kes khas dari peraturan kedua, ada kerana tidak masuk akal untuk menyimpan jenis rujukan dalam beberapa paket. Walaupun pakej boleh menyimpan pelbagai jenis, satu jenis hanya boleh dimiliki oleh satu bungkusan.

Apabila fail sumber tidak menyatakan pernyataan paket, jenis fail sumber dikatakan termasuk dalam paket yang tidak disebutkan namanya . Jenis rujukan bukan remeh biasanya disimpan dalam bungkusannya sendiri dan mengelakkan pakej 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

Semasa mengerjakan projek berbilang pembangun, elakkan menggunakan *wildcard supaya pembangun lain dapat dengan mudah melihat jenis yang digunakan dalam kod sumber anda.