想象一下,我们有一些类A.java.它以某种方式使用B.java,即导入它,调用它的方法,使用它的属性等.我们编译文件.
现在我想更深入地了解我们得到了什么.
A.class是否存储了有关B内部的一些信息?什么信息 - 只是名称,被调用的方法,使用的最终变量?如果在编译之后A.class中没有关于B的那么多信息 - 为什么我们不能编译A而没有B.class?
如果我们将B.class中的B.class替换为另一个B.class - 当它工作时,何时不工作?
如果A.class使用新的B.class运行正常,A.class是否可以使用OLD B.class中的一些信息,这些信息在编译期间被合并到A.class中?也许我们最终可能在我们的项目中有一个B.class 的混合逻辑?
基于上述问题的答案:我们能否以某种方式编译依赖于其他类的类而不知道它们在编译时的确切实现,并仅在运行时提供依赖性?
“import”关键字实际上并不导入任何东西。基本上,它只是通过类名引用类的完整路径的一种方法。所以当我们说
import java.lang.String;
Run Code Online (Sandbox Code Playgroud)
那么我们可以在代码中通过名称“String”来引用该类。在编译时,任何我们有类“String”的地方都将被“java.lang.String”替换。就是这样。
这也是为什么如果您有多个同名但位于不同包中的类,您最多只能导入其中一个。您必须通过其完整限定名称来引用另一个类。
关于问题:
1. A.class内部是否存储了B的一些信息?只是名称,调用的方法,使用的最终变量?如果编译后 A.class 中没有那么多关于 B 的信息 - 为什么我们不能在没有 B.class 的情况下编译 A?
并不真地。当您调用其他类时,A 类只会使用完全限定的类名和方法签名。使用我们的字符串示例,调用
mystring.charAt(0)
Run Code Online (Sandbox Code Playgroud)
将编译成字节码,如下所示:
aload_1 [myString]
iconst_0
invokevirtual java.lang.String.charAt(int)
Run Code Online (Sandbox Code Playgroud)
除了可能内联的常量之外,我们的 A 类中不存储其他类的内部状态。public final因此,如果值将来可能发生变化,则在创建字段时要非常小心。(请参阅下面的解决方法)
当我们B.class编译 A 类时,编译器需要确保 B 类具有我们想要使用的方法。
2. 如果我们将包中的 B.class 替换为另一个 B.class - 什么时候有效,什么时候无效?
如果完整的限定名称(包等)相同并且它具有我们尝试使用的正确方法签名,则新的 B.class 将起作用。新的类 B 可以添加新的方法,甚至可以对我们使用的方法有不同的实现。但是,它不能修改我们使用的方法的方法签名。如果旧方法不再存在或者其签名不同,我们将java.lang.LinkageError在编译时得到一个。
3.如果A.class与新的B.class一起运行正常,A.class是否可以使用旧B.class中的一些信息,这些信息在编译期间并入A.class中?即我们的项目中最终可以有 B.class 的混合逻辑吗?
唯一的问题可能是旧 B 中的内联常量。这就是为什么Java 编码指南第 31 项(第 115 页)说“不要将 public final 应用于其值可能在以后版本中发生变化的常量”。相反,创建一个 getter 方法:例如:
class BadFoo{
//bad, VERSION could be inlined
//and later when we change the version, other classes will have the old value!
public static final int VERSION =1;
}
class BetterFoo{
private static int version =1;
//outside classes must call method getVersion()
//field version can not be inlined at compile time.
//JIT may inline at runtime which is OK
public static final int getVersion(){
return version;
}
}
Run Code Online (Sandbox Code Playgroud)
4. 基于上述问题的答案:我们是否可以在编译时不知道其他类的具体实现的情况下以某种方式编译依赖于其他类的类,并仅在运行时提供依赖关系?
是的,代码到接口。
该接口应该包含您需要调用的所有方法。我们的 A 类应该只通过接口引用调用者。如果传入了我们要调用的对象实例,则 A 类不知道(或需要知道)实际类型是什么,并且不需要在编译时知道它。
这是依赖注入的主要目的和优点之一
即使除了依赖注入之外,接口编码也有优点。例如,如果我们使用Map代替HashMap,我们可以稍后更改代码以使用不同的 Map 实现,例如ConcurrentHashMap仅更改代码中的一个位置。我们的其余代码将正常工作,因为它只知道它是一个地图。
| 归档时间: |
|
| 查看次数: |
236 次 |
| 最近记录: |