java静态导入

在我以前的Java 101教程中,您学习了如何通过将引用类型(也称为类和接口)声明为其他引用类型和块的成员来更好地组织代码 。 我还向您展示了如何使用嵌套来避免嵌套引用类型和共享相同名称的顶级引用类型之间的名称冲突。

Java与嵌套一起使用包来解决顶级引用类型中的同名问题。 使用静态导入还可以简化对打包的顶级引用类型中的静态成员的访问。 静态导入将在您访问代码中的这些成员时为您节省击键,但是使用它们时需要注意一些事项。 在本教程中,我将向您介绍在Java程序中使用包和静态导入的方法。



下载

获取代码



由Jeff Friesen为JavaWorld创建。



包装参考类型

Java开发人员将相关的类和接口分组到包中。 使用包可以更轻松地查找和使用引用类型,避免同名类型之间的名称冲突,并控制对类型的访问。




在本节中,您将了解软件包。 您将了解什么是软件包,了解packageimport语句,并探索受保护访问,JAR文件和类型搜索的其他主题。

什么是Java包?

在软件开发中,我们通常根据项目的层次关系来组织项目。 例如,在上一教程中,我向您展示了如何将类声明为其他类的成员。 我们还可以使用文件系统将目录嵌套在其他目录中。

使用这些层次结构将帮助您避免名称冲突。 例如,在非分层文件系统(单个目录)中,不可能为多个文件分配相同的名称。 相反,分层文件系统使同名文件存在于不同目录中。 同样,两个封闭类可以包含同名的嵌套类。 名称冲突不存在,因为项目被划分为不同的命名空间。

Java还允许我们将顶级(非嵌套)引用类型划分为多个命名空间,以便我们可以更好地组织这些类型并防止名称冲突。 在Java中,我们使用包语言功能将顶级引用类型划分为多个命名空间。 在这种情况下, 是用于存储引用类型的唯一名称空间。 软件包可以存储类和接口以及软件包这些软件包是嵌套在其他软件包中的软件包。




包具有名称,该名称必须是非保留标识符。 例如, java 。 成员访问运算符( . )将包名与子包名分开,并将包或子包名与类型名分开。 例如, java.lang.System的两个成员访问运算符将包名javalang子包名分开,并将子包名langSystem类型名分开。

必须将引用类型声明为public以便可以从其包外部进行访问。 这同样适用于必须可访问的任何常量,构造函数,方法或嵌套类型。 您将在本教程的后面部分看到这些示例。

包装说明

在Java中,我们使用package语句创建一个包。 该语句出现在源文件的顶部,并标识源文件类型所属的软件包。 它必须符合以下语法:

package identifier [. identifier ]*;


package identifier [. identifier ]*;

包语句以保留字package开头,并以标识符开头,该标识符后面还可以有一个句点分隔的标识符序列(可选)。 分号( ; )终止此语句。

第一个(最左侧)标识符为包命名,每个后续标识符为子包命名。 例如,在package ab; ,所有类型的源文件中声明属于b的分装的a包。

包/子包的命名约定

按照约定,我们用小写形式表示包或子包的名称。 当名称包含多个单词时,除第一个单词外,您可能希望将每个单词都大写; 例如, generalLedger

软件包名称的序列必须唯一,以避免编译问题。 例如,假设您创建了两个不同的graphics包,并假定每个graphics包都包含一个具有不同接口的Triangle类。 当Java编译器遇到类似下面的内容时,它需要验证Triangle(int, int, int, int)构造函数是否存在:

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


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

三角形边框

Triangle构造函数视为指定在其中绘制三角形的边界框。 前两个参数标识框的左上角,后两个参数定义框的范围。

编译器将搜索所有可访问的程序包,直到找到包含Triangle类的graphics程序包。 如果找到的包包括带有Triangle(int, int, int, int)构造函数的相应Triangle类,则一切正常。 否则,如果找到的Triangle类没有Triangle(int, int, int, int)构造函数,则编译器将报告错误。 (在本教程后面的内容中,我将详细介绍搜索算法。)

此方案说明选择唯一的程序包名称序列的重要性。 选择唯一名称序列的惯例是反转您的Internet域名,并将其用作序列的前缀。 例如,我将选择ca.javajeff作为我的前缀,因为javajeff.ca是我的域名。 然后,我将指定ca.javajeff.graphics.Triangle来访问Triangle

域名组件和有效的软件包名称

域名组件并非始终是有效的程序包名称。 一个或多个组件名称可能以数字( 3D.com3D.com ,包含连字符( - )或另一个非法字符( ab-z.com ),或者是Java的保留字之一( short.com )。 习惯上还是会把你前面加上下划线(数字com._3D ),替换用下划线(非法字符com.ab_z ),并用下划线(后缀保留字com.short_ )。

您需要遵循一些规则,以避免package语句出现其他问题:

  1. 您只能在源文件中声明一个package语句。
  2. 您不能在package语句之前加上注释。

之所以存在第一条规则是第二条规则的特例,是因为将引用类型存储在多个包中没有意义。 尽管一个包可以存储多个类型,但是一个类型只能属于一个包。

当源文件未声明package语句时,源文件的类型被称为未命名package 。 非平凡的引用类型通常存储在它们自己的包中,并避免使用未命名的包。

Java实现将程序包和子程序包名称映射到同名目录。 例如,一种实现会将graphics映射到名为graphics的目录。 在包装的情况下ab ,第一个字母 ,将映射到一个指定的目录ab将映射到一个b的子目录a 。 编译器将实现包类型的类文件存储在相应目录中。 请注意,未命名的程序包对应于当前目录。

示例:使用Java打包音频库

一个实际的示例有助于完全掌握package语句。 在本节中,我将在音频库的上下文中演示软件包,使您可以读取音频文件并获取音频数据。 为简便起见,我仅介绍该库的骨架版本。

音频库当前仅包含两个类: AudioWavReaderAudio描述了一个音频剪辑,并且是图书馆的主要课程。 清单1给出了其源代码。

清单1.打包语句示例(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
   }
}


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
   }
}

让我们逐步完成清单1。

  • 清单1中的Audio.java文件存储Audio类。 此清单以package语句开头,该语句将ca.javajeff.audio标识为该类的包。
  • Audio被声明为public Audio以便可以从其程序包外部引用它。 另外,它被声明为final ,因此不能扩展(意味着,子类化)。
  • Audio声明private samplessampleRate字段以存储音频数据。 这些字段被初始化为传递给Audio的构造函数的值。
  • Audio的构造函数声明为package-private (意味着,构造函数未声明为publicprivateprotected ),因此无法从其包外部实例化此类。
  • Audio提供了getSamples()getSampleRate()方法,用于返回音频剪辑的样本和采样率。 每个方法都声明为public方法,因此可以从Audio的程序包外部调用它。
  • Audiopublicstatic newAudio()工厂方法结束,该方法用于返回与filename参数相对应的Audio对象。 如果无法获得音频剪辑,则返回null
  • newAudio()filename的扩展名与.wav进行比较(此示例仅支持WAV音频)。 如果它们匹配,则执行return WavReader.read(filename)以返回带有基于WAV的音频数据的Audio对象。

清单2描述了WavReader

清单2. WavReader帮助器类(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);
   }
}


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旨在将WAV文件的内容读取到Audio对象中。 (该类最终将带有更多的private字段和方法,从而变得WavReader 。)请注意,该类未声明为public ,这使WavReader可以被Audio访问,但不能在ca.javajeff.audio包之外进行编码。 可以将WavReader视为一个辅助类,其存在的唯一原因是为Audio提供服务。

完成以下步骤来构建此库:

  1. 在文件系统中选择一个合适的位置作为当前目录。
  2. 在当前目录中创建一个ca/javajeff/audio子目录层次结构。
  3. 将清单1和2分别复制到文件Audio.javaWavReader.java ; 并将这些文件存储在audio子目录中。
  4. 假设当前目录包含ca子目录,请执行javac ca/javajeff/audio/*.java来编译ca/javajeff/audio的两个源文件。 如果一切顺利,您应该在audio子目录中发现Audio.classWavReader.class文件。 (或者,对于本示例,您可以切换到audio子目录并执行javac *.java 。)

现在,您已经创建了音频库,您将需要使用它。 很快,我们将看一个演示该库的小型Java应用程序。 首先,您需要了解import语句。

Java的导入语句

想象一下,必须为源代码中每次出现的Triangle重复指定ca.javajeff.graphics.Triangle 。 Java提供了import语句,作为省略冗长的软件包详细信息的便捷选择。

import语句通过告诉编译器在编译过程中在何处查找不合格 (没有包前缀)的类型名称,从而从包中导入类型。 它显示在源文件顶部附近,并且必须符合以下语法:

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


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

导入语句以保留字import开始,并以标识符开头,该标识符后面可以有句点分隔的标识符序列(可选)。 类型名称或星号( * )后跟,并以分号终止此语句。

该语法揭示了import语句的两种形式。 首先,您可以导入单个类型名称,该类型名称通过typeName标识。 其次,您可以导入所有通过星号标识的类型。

*符号是一个通配符,代表所有不合格的类型名称。 它告诉编译器在import语句的程序包序列的最右边的程序包中查找此类名称,除非在先前搜索的程序包中找到类型名称。 请注意,使用通配符不会降低性能或导致代码膨胀。 但是,这可能导致名称冲突,您将看到。

例如, import ca.javajeff.graphics.Triangle; 告诉编译器ca.javajeff.graphics软件包中存在不合格的Triangle类。 同样,类似

import
      ca.javajeff.graphics.*;


import
      ca.javajeff.graphics.*;

告诉编译器在遇到Triangle名称, Circle名称甚至是Account名称(如果尚未找到Account )时,在此程序包中查找。

避免在多开发人员项目中使用*

在从事多开发人员项目时,请避免使用*通配符,以便其他开发人员可以轻松查看源代码中使用了哪些类型。

翻译自: https://www.infoworld.com/article/3543349/packages-and-static-imports-in-java.html

java静态导入