java 为什么有两个同名的类可用时,我没有冲突的名称冲突?

vql8enpb  于 2022-12-02  发布在  Java
关注(0)|答案(1)|浏览(173)

假设我有一个:

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.Stringorg.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中找不到任何适当的规范页。

7d7tgy0s

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按需类型导入声明

  • type-import-on-demand声明允许根据需要导入命名包、类或接口的所有可访问类和接口。*
TypeImportOnDemandDeclaration:
    import PackageOrTypeName . * ;

6.4.1 Shadowing是关于按需导入的:
按需类型导入声明绝不会导致任何其他声明被隐藏。
因此,java.lang.String永远不能遮蔽名为String的类(对于java.lang中的其他类也是如此)
与两个类

package org.something.a;

class String {}

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());
    }
}

您不能运行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
如果将代码更改为

package org.something.a;

class String {}

public class Main {
    public static void main(java.lang.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());
    }
}

那么您将能够运行该类,因为现在main方法的签名是正确的。
运行此代码将输出

org.something.a.String

要了解java.lang.Stringorg.something.a.String之间的区别,您应该扩展main方法以

package org.something.a;

class String {}

public class Main {
    public static void main(java.lang.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 has class " + a.getClass().getName());
        System.out.println("args has class " + args.getClass().getName());
        System.out.println("args has component type " + args.getClass().componentType().getName());
        java.lang.String b = new java.lang.String();
        System.out.println("b has class " + b.getClass().getName());
    }
}

将输出

a has class org.something.a.String
args has class [Ljava.lang.String;
args has component type java.lang.String
b has class java.lang.String

Stringjava.lang.String的Map如何消失?

它不会消失,也不会一开始就显现出来。
对于我们这些懒惰的程序员来说,理解所有这些import语句仅仅是一个方便的特性是很重要的。
import java.util.List;形式的单一类型导入语句让我们告诉编译器:
你看,每当我写List作为一个简单的,非限定类名我希望你使用java.util.List作为限定类名。
这个import语句并不阻止你使用类java.awt.List--它只是意味着如果你想使用这个特定的类,你必须使用完全限定的类名java.awt.List。而且它对编译过程没有任何改变--编译过程内部总是只使用完全限定的类名。
import java.lang.*;形式的按需导入语句隐含地是每个编译单元的第一个导入语句,它甚至更加懒惰,它告诉编译器:
看,每当我将String写为一个简单的非限定类名,而您在当前包或单一类型导入语句中都找不到String类的定义时,在所有按需导入的包中查找是否可以在任何一个包中找到这样的类。如果可以找到一个这样的类,则使用"的类的完全限定类名。
完整性:我写 * 如果你能找到一个这样类 * 的原因是:没有人阻止你写作

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

编译器仍然会很乐意编译您的代码--直到您尝试使用简单的类名List。只要您尝试同时使用import语句和简单类名List,编译器就会异常终止并显示错误,因为使用这两个import语句时,编译器无法知道您希望类名List引用java.awt.List还是java.util.List .
在你的例子中(类String和类Main在同一个包中),这个理由并不适用。

如果java.lang.String从不阴影化,那么它会去哪里

java.lang.String始终存在,只是简单的名称String不再表示java.lang.String,而是引用当前包中的类String

相关问题