2014-10-16 76 views
5

想象一下,我们有一些类A.java。它以某种方式使用B.java,即导入它,调用它的方法,使用它的属性等。我们编译这些文件。编译后,来自导入类的哪些信息存储在编译类中?

现在我想深入了解我们所得到的结果。

  1. A.class是否存储关于B的一些信息?什么信息 - 只是名称,被调用的方法,使用的最终变量? 如果在编译后的A.class中没有太多有关B的信息 - 为什么我们不能编译没有B.class的A?

  2. 如果我们将bundle中的B.class替换为另一个B.class - 它何时工作,何时不工作?

  3. 如果A.class与新的B.class运行正常,那么A.class可以使用来自OLD B.class的一些信息,该信息在编译期间并入A.class中?即我们最终可能会在我们的项目中有一个混合的 B.class逻辑?

  4. 基于上述问题的答案:我们可以以某种方式编译依赖于其他类而不知道它们在编译时的确切实现的类吗,并且只在运行时提供依赖性吗?

+3

是不是“编程接口”是关于什么? – 2014-10-16 19:36:10

回答

3

“导入”关键字实际上并没有导入什么。基本上它只是一种通过类名引用类的完整路径的方法。所以当我们说

import java.lang.String; 

然后我们可以在我们的代码中通过它的名字“String”来引用那个类。在编译时,任何我们拥有类“String”的地方都会被替换为“java.lang.String”。而已。

这也是为什么,如果你有一个以上的类具有相同的名称,但在不同的包,你可以输入最多只是其中之一。其他课程必须以其全名。

走上问题:

1.确实的A.class存储有关其自身内部乙的一些信息?只是名称,被调用的方法,使用的最终变量?如果编译后的A.class中没有太多有关B的信息 - 为什么我们不能编译没有B.class的A?

不是真的。当您调用其他类时,类A将只使用完全限定的类名称和方法签名。使用我们的字符串例如,调用

mystring.charAt(0) 

将编译成字节码是这样看的东西:

aload_1 [myString] 
    iconst_0 
    invokevirtual java.lang.String.charAt(int) 

其他类的任何内部状态被存储在我们的A类里面除了内联可能常量。因此,如果价值可能在未来发生变化,请务必在字段public final上注意。 (参见下面的解决方法)

当我们编译A类时,我们需要B.class,因此编译器可以确保类B具有我们要使用的那些方法。

2.如果我们在与B.class包这是另外一个人更换B.class - 当它工作,何时不? 新B.class会工作,如果完全限定域名(包装等)是相同的,它具有我们正在尝试使用正确的方法签名。新的B类可以添加新的方法,甚至可以使用不同的方法实现。但是,它不能修改我们使用的方法的方法签名。如果旧方法不再存在或者其签名不同,我们将在编译时得到一个java.lang.LinkageError

3.如果A.class与新的B.class运行正常,那么A.class可以使用来自OLD B.class的一些信息,它在编译过程中并入A.class中?即我们最终是否可以在我们的项目中使用B.class的混合逻辑?从老B.这

唯一的问题可能是内联常量就是为什么Java Coding Guidelines项目31项(第115页)说:“不要使用公共决赛,其值在以后的版本可能恰克常量”。 相反,做一个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; 
    } 

} 

4.关于上述问题的答案筑底:我们能以某种方式编译类依赖于其他类的不知道他们在编译时确切的实现,并提供依赖只在运行时?

是,代码接口。

接口应该包含所有你需要调用的方法。我们的A类应该只能通过接口引用调用者。如果我们要调用的对象实例被传入,那么类A不知道(或者需要知道)实际类型是什么,并且在编译时不需要知道它。

这是主要目的和Dependency Injection

优势之一甚至除了依赖注入,编码到接口方面具有优势。例如,如果我们使用Map而不是HashMap,稍后我们可以通过更改代码中的一个位置来更改代码以使用不同的Map实现,例如ConcurrentHashMap。我们其余的代码将会工作,因为它只知道它是一个Map。

+0

很好的回答,谢谢! – MiamiBeach 2014-10-17 08:34:29

2

这由Java语言规范的Chapter 13. Binary Compatibility来解决。

由于这可能比你想更具体的,我会给出一个简要总结:

如果类A使用B类的功能fA类文件必须包含f一个象征性的参考。当加载A类时,但B不提供f,加载类A失败,出现java.lang.LinkageError,从而阻止使用A

编译器时间常量表达式是一个例外,编译器必须内联,即符号引用必须在编译时由它们的值替换,而类A将继续使用原始值,即使类B已更改。

另一个例外是添加或删除注释对程序的二进制表示的正确链接没有影响。

B没有其他信息被并入A在编译的时候,一切有关B可以改变,而不由已编译A类影响其使用。