假设我有一个:
package org.something.a;
public class String {
...
}
和
package org.something.a;
public class Main {
public static void main(String[] args) {
String a = new String(); //IDE shows that this is a org.something.a.String type and not java.lang.String
System.out.println(a.getClass().getName());
}
}
我想知道为什么String
没有名称冲突,因为java.lang.String
和org.something.a.String
都在org.something.a
包中,在编译时类路径上,为什么自定义类(org.something.b
)有一个首选项,因为IDE(IntelliJ)将String
解析为org.something.a.String
?
此外,如果我用javac *.java
编译这两个文件,然后调用java Main
,我会得到:
错误:在类Main中找不到Main方法,请将Main方法定义为:public静态void main(String []参数)
但如果我将main方法签名改为:
public static void main(java.lang.String[] args) {...}
然后编译并输出String
。这意味着,它编译导入了java.lang.String
的整个文件,之前失败是因为,main
方法没有java.lang.String
数组作为参数。
我很清楚这一点:《日本劳工标准》第7条:
- 编译单元中的代码自动访问其包中声明的所有类和接口,还自动导入预定义包java.lang中声明的所有公共类和接口。*
就我所知,这 * 一定 * 是名称冲突。但即使不是,上面的行为也很不清楚-为什么自定义类有一个首选项,为什么如果我只是将java.lang
附加到main方法的参数,一切都会改变。
在JLS中找不到任何适当的规范页。
1条答案
按热度按时间7d7tgy0s1#
6.3宣言的范围规定:
顶级类或接口的作用域(参见7.6节)是声明顶级类或接口的包中的所有类和接口声明。
那么,既然
java.lang.*
被导入到每个编译单元中,为什么org.something.a.String
不与java.lang.String
冲突呢?7.3编译单元声明:
每个编译单元都隐式地导入在预定义包java.lang中声明的每个公共类或接口,就好像声明**
import java.lang.*;
**出现在每个编译单元的开头,紧接着任何包声明。因此,所有这些类和接口的名称在每个编译单元中都可以作为简单名称使用。请注意,它将import语句显式声明为
import java.lang.*;
,这称为“按需导入”:7.5.2按需类型导入声明
6.4.1 Shadowing是关于按需导入的:
按需类型导入声明绝不会导致任何其他声明被隐藏。
因此,
java.lang.String
永远不能遮蔽名为String
的类(对于java.lang
中的其他类也是如此)与两个类
您不能运行
org.something.a.Main
,因为main
方法具有public static void main(org.something.a.String[] args)
签名,但是要运行该类,签名必须是public static void main(java.lang.String[] args)
。请注意,大多数情况下,您可以只将该方法写为public static void main(String[] args)
,因为当前包中没有类String
来隐藏java.lang.String
。如果将代码更改为
那么您将能够运行该类,因为现在main方法的签名是正确的。
运行此代码将输出
要了解
java.lang.String
和org.something.a.String
之间的区别,您应该扩展main
方法以将输出
String
到java.lang.String
的Map如何消失?它不会消失,也不会一开始就显现出来。
对于我们这些懒惰的程序员来说,理解所有这些import语句仅仅是一个方便的特性是很重要的。
import java.util.List;
形式的单一类型导入语句让我们告诉编译器:你看,每当我写
List
作为一个简单的,非限定类名我希望你使用java.util.List
作为限定类名。这个import语句并不阻止你使用类
java.awt.List
--它只是意味着如果你想使用这个特定的类,你必须使用完全限定的类名java.awt.List
。而且它对编译过程没有任何改变--编译过程内部总是只使用完全限定的类名。import java.lang.*;
形式的按需导入语句隐含地是每个编译单元的第一个导入语句,它甚至更加懒惰,它告诉编译器:看,每当我将
String
写为一个简单的非限定类名,而您在当前包或单一类型导入语句中都找不到String
类的定义时,在所有按需导入的包中查找是否可以在任何一个包中找到这样的类。如果可以找到一个这样的类,则使用"的类的完全限定类名。完整性:我写 * 如果你能找到一个这样类 * 的原因是:没有人阻止你写作
编译器仍然会很乐意编译您的代码--直到您尝试使用简单的类名
List
。只要您尝试同时使用import语句和简单类名List
,编译器就会异常终止并显示错误,因为使用这两个import语句时,编译器无法知道您希望类名List
引用java.awt.List
还是java.util.List
.在你的例子中(类
String
和类Main
在同一个包中),这个理由并不适用。如果
java.lang.String
从不阴影化,那么它会去哪里java.lang.String
始终存在,只是简单的名称String
不再表示java.lang.String
,而是引用当前包中的类String
。