508

使用像这样的单个语句更方便、更简洁

import java.awt.*;

而不是导入一堆单独的类

import java.awt.Panel;
import java.awt.Graphics;
import java.awt.Canvas;
...

import在语句中使用通配符有什么问题?

4

18 回答 18

639

唯一的问题是它使您的本地命名空间变得混乱。例如,假设您正在编写一个 Swing 应用程序,因此需要java.awt.Event,并且还与公司的日历系统交互,该系统具有com.mycompany.calendar.Event. 如果您使用通配符方法导入两者,则会发生以下三种情况之一:

  1. java.awt.Event您在and之间存在彻底的命名冲突com.mycompany.calendar.Event,因此您甚至无法编译。
  2. 你实际上只导入了一个(你的两个导入中只有一个.*),但它是错误的,你很难弄清楚为什么你的代码声称类型是错误的。
  3. 当您编译代码时,没有com.mycompany.calendar.Event,但是当他们后来添加一个时,您以前有效的代码突然停止编译。

显式列出所有导入的好处是我可以一眼看出您打算使用哪个类,这使得阅读代码变得更加容易。如果你只是做一件快速的一次性事情,没有什么明显的错误,但未来的维护者会感谢你的清晰说明。

于 2008-09-29T03:58:50.553 回答
228

这是对明星进口的投票。导入语句旨在导入,而不是类。导入整个包要干净得多;此处确定的问题(例如java.sql.Datevs java.util.Date)可以通过其他方式轻松解决,具体导入并未真正解决,当然也不能证明对所有类进行疯狂迂腐的导入。没有什么比打开一个源文件并不得不翻阅 100 个导入语句更令人不安的了。

进行特定的导入会使重构更加困难;如果您删除/重命名一个类,则需要删除其所有特定导入。如果将实现切换到同一个包中的不同类,则必须修复导入。虽然这些额外的步骤可以自动化,但它们确实是对生产力的打击,并没有真正的收益。

如果 Eclipse 默认不进行特定的类导入,那么每个人仍然会进行星型导入。很抱歉,但确实没有合理的理由进行特定的进口。

以下是处理类冲突的方法:

import java.sql.*;
import java.util.*;
import java.sql.Date;
于 2008-09-29T15:45:56.250 回答
201

请参阅我的文章按需导入是邪恶的

简而言之,最大的问题是当将类添加到您导入的包中时,您的代码可能会中断。例如:

import java.awt.*;
import java.util.*;

// ...

List list;

在 Java 1.1 中,这很好;在 java.awt 中找到 List 并且没有冲突。

现在假设您签入了完美运行的代码,一年后有人将它拿出来进行编辑,并且正在使用 Java 1.2。

Java 1.2 向 java.util 添加了一个名为 List 的接口。繁荣!冲突。完美运行的代码不再有效。

这是EVIL语言功能。没有理由仅仅因为将类型添加到包中而停止编译代码......

此外,读者很难确定您使用的是哪个“Foo”。

于 2008-09-29T19:42:46.867 回答
79

在 Java 导入语句中使用通配符也不错。

Clean Code中,Robert C. Martin 实际上建议使用它们来避免长导入列表。

这是建议:

J1:使用通配符避免长导入列表

如果您使用包中的两个或多个类,则使用以下命令导入整个包

导入包。*;

长长的进口清单令读者望而生畏。我们不想用 80 行导入来弄乱我们模块的顶部。相反,我们希望导入是关于我们与哪些包合作的简明陈述。

特定的导入是硬依赖,而通配符导入不是。如果您专门导入一个类,则该类必须存在。但是,如果您使用通配符导入包,则不需要存在特定的类。import 语句只是在搜索名称时将包添加到搜索路径中。因此,此类导入不会创建真正的依赖关系,因此它们有助于减少我们的模块耦合。

有时,一长串特定的进口产品可能会很有用。例如,如果您正在处理遗留代码并且想要找出需要为哪些类构建模拟和存根,您可以遍历特定导入列表以找出所有这些类的真正限定名称,然后输入适当的存根到位。但是,这种用于特定进口的用途非常罕见。此外,大多数现代 IDE 将允许您使用单个命令将通配符导入转换为特定导入列表。因此,即使在遗留情况下,最好导入通配符。

通配符导入有时会导致名称冲突和歧义。两个具有相同名称但在不同包中的类将需要专门导入,或者在使用时至少需要专门限定。这可能很麻烦,但很少见,使用通配符导入通常仍然比特定导入更好。

于 2010-01-02T20:17:19.890 回答
29

性能:对性能没有影响,因为字节码是相同的。虽然它会导致一些编译开销。

编译:在我的个人机器上,编译一个空白类而不导入任何东西需要 100 毫秒,但导入 java.* 时相同的类需要 170 毫秒。

于 2014-05-06T18:21:25.977 回答
26

它使您的命名空间变得混乱,要求您完全指定任何不明确的类名。最常见的情况是:

import java.util.*;
import java.awt.*;

...
List blah; // Ambiguous, needs to be qualified.

它还有助于使您的依赖项具体化,因为您的所有依赖项都列在文件的顶部。

于 2008-09-29T04:05:04.873 回答
22
  1. 它有助于识别类名冲突:不同包中具有相同名称的两个类。这可以用 * 导入来掩盖。
  2. 它使依赖项变得明确,以便以后必须阅读您的代码的任何人都知道您要导入什么以及您不打算导入什么。
  3. 它可以使一些编译更快,因为编译器不必搜索整个包来识别依赖关系,尽管这对于现代编译器来说通常不是什么大问题。
  4. 现代 IDE 将显式导入的不便之处降至最低。大多数 IDE 允许您折叠导入部分,使其不碍事,在需要时自动填充导入,并自动识别未使用的导入以帮助清理它们。

我工作过的大多数使用大量 Java 的地方都将显式导入作为编码标准的一部分。我有时仍然使用 * 进行快速原型设计,然后在产品化代码时扩展导入列表(某些 IDE 也会为您执行此操作)。

于 2008-09-29T04:42:24.847 回答
11

我更喜欢特定的导入,因为它允许我查看文件中使用的所有外部引用,而无需查看整个文件。(是的,我知道它不一定会显示完全限定的引用。但我尽可能避免使用它们。)

于 2008-09-29T03:59:11.813 回答
10

在之前的项目中,我发现从 *-imports 更改为特定的导入将编译时间减少了一半(从大约 10 分钟到大约 5 分钟)。*-import 使编译器在列出的每个包中搜索与您使用的类匹配的类。虽然这个时间可能很小,但它对于大型项目来说是加起来的。

*-import 的一个副作用是开发人员会复制和粘贴常见的导入行,而不是考虑他们需要什么。

于 2008-10-10T15:57:56.600 回答
6

DDD 书中

在实现将基于的任何开发技术中,寻找最小化重构 MODULES 工作的方法。在 Java 中,无法避免导入单个类,但您至少可以一次导入整个包,这反映了包是高度内聚的单元的意图,同时减少了更改包名称的工作量。

如果它使本地命名空间混乱,这不是你的错 - 归咎于包的大小。

于 2016-06-17T08:54:23.197 回答
4
  • 没有运行时影响,因为编译器会自动将 * 替换为具体的类名。如果你反编译 .class 文件,你永远不会看到import ...*.

  • C#总是使用 * (隐式),因为您只能using包名称。您根本无法指定类名。Java 引入了 c# 之后的特性。(Java 在许多方面都非常棘手,但它超出了这个主题)。

  • 在 Intellij Idea 中,当您“组织导入”时,它会自动将同一包的多个导入替换为 *. 这是一项强制性功能,因为您无法将其关闭(尽管您可以提高阈值)。

  • 接受的答复列出的案例无效。没有 * 你仍然有同样的问题。无论您是否使用 *,您都需要在代码中指定 pakcage 名称。

于 2018-03-18T05:29:51.747 回答
3

最重要的一点是,导入java.awt.*会使您的程序与未来的 Java 版本不兼容:

假设您有一个名为“ABC”的类,您使用的是 JDK 8,并且您导入了java.util.*. 现在,假设 Java 9 出来了,它的包java.util中有一个新类,巧合的是,它也被称为“ABC”。您的程序现在无法在 Java 9 上编译,因为编译器不知道名称“ABC”是指您自己的类还是java.awt.

java.awt当您仅从您实际使用的类中显式导入这些类时,您将不会遇到这个问题。

资源:

Java 导入

于 2016-09-20T17:27:35.887 回答
3

以下是我发现的关于这个主题的几件事。

  • 在编译期间,编译器会尝试从 .* 导入中查找代码中使用的类,并通过从 .* 导入中选择使用的类来生成相应的字节码。所以使用 .* import 或 .class names import 的字节码是相同的,因为相同的字节码,运行时性能也会相同。

  • 在每次编译中,编译器必须扫描 .* 包的所有类,以匹配代码中实际使用的类。因此,与使用 .class 名称导入相比,使用 .* 导入的代码在编译过程中需要更多时间。

  • 使用 .* import 有助于使代码更简洁

  • 当我们使用来自两个不同包的两个同名类时,使用 .* import 会产生歧义。例如,日期在两个包中都可用。

      import java.util.*;
      import java.sql.*;
    
      public class DateDemo {
          private Date utilDate;
          private Date sqlDate;
      }
    
于 2020-10-11T09:55:39.030 回答
2

在双方提出的所有有效观点中,我没有找到避免使用通配符的主要原因:我喜欢能够阅读代码并直接知道每个类是什么,或者它的定义是否不在语言中或文件,在哪里可以找到它。如果使用 * 导入了多个包,我必须搜索每个包以找到我不认识的类。可读性是至高无上的,我同意代码不应该需要IDE 来阅读它。

于 2017-12-21T00:25:52.767 回答
2

作为记录:当你添加一个导入时,你也表明了你的依赖关系。

您可以快速看到文件的依赖关系(不包括相同命名空间的类)。

于 2019-02-07T11:44:39.840 回答
1

忘掉杂乱的命名空间……想想可怜的灵魂,他们必须在 GitHub、vi、Notepad++ 或其他一些非 IDE 文本编辑器中阅读和理解你的代码。

该人必须煞费苦心地查找来自通配符之一的每个标记与每个通配符范围内的所有类和引用......只是为了弄清楚到底发生了什么。

如果你只为编译器编写代码——而且你知道自己在做什么——我相信通配符没有问题。

但是,如果其他人——包括未来的你——想在一次阅读中快速理解特定的代码文件,那么显式引用会有很大帮助。

于 2020-10-03T18:20:50.180 回答
-1

使用通配符导入还不错,因为 Oracle 的 Java 教程使用通配符导入。我不认为 Oracle 的 Java 人员会做错事。

请看这里:https ://docs.oracle.com/javase/tutorial/uiswing/examples/components/CustomComboBoxDemoProject/src/components/CustomComboBoxDemo.java

上述程序使用通配符导入:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

您可以在此处查看更多程序: https ://docs.oracle.com/javase/tutorial/uiswing/examples/components/ 。

于 2021-12-16T13:44:16.607 回答
-2

导入包中的所有类被认为是一种盲目的方法。造成这种情况的一个主要原因是它使类命名空间变得混乱,并可能导致不同包中具有相同名称的类之间发生冲突。

专门填充必要的类可以避免这个问题,并清楚地显示需要哪些版本。这有利于代码的可维护性。

于 2020-07-26T01:27:56.460 回答