2

嗨,我正在尝试深入学习 JAVA,因此我将在以下几行中深入研究 JDK 源代码:

URL url = new URL("http://www.google.com");
URLConnection tmpConn = url.openConnection();

我附上源代码并在第二行设置断点并进入代码。我可以看到代码流是: URL.openConnection() -> sun.net.www.protocol.http.Handler.openConnection() 我对此有两个问题

首先在 URL.openConnection() 中,代码是:

public URLConnection openConnection() throws java.io.IOException {
        return handler.openConnection(this);
    }

handler是URLStreamHandler的对象,定义为blow

transient URLStreamHandler handler;

但是URLStreamHandler是一个抽象类,openConnection()方法并没有在里面实现,所以当handler调用这个方法的时候,应该去找一个实现这个方法的子类吧?但是 sun.net.www.protocol 中有很多类实现了这个方法(比如 http.Hanlder, ftp.Handler ) 代码应该如何知道它应该调用哪个“openConnection”方法?在这个例子中,这个 handler.openConnection() 将进入 http.Handler 并且它是正确的。(如果我将 url 设置为ftp://www.google.com,它将进入 ftp.Handler)我无法理解机制。

第二。我附上了源代码,因此我可以进入 JDK 并查看变量,但是对于 sun.net.www.protocol.http.Handler 等许多类,src.zip 中没有源代码。我用谷歌搜索了这个类,网上有源代码我可以得到,但为什么他们没有把它(和许多其他类)放在 src.zip 中?我在哪里可以找到源代码的综合版本?

谢谢!

4

3 回答 3

8

首先是简单的部分:

...我用谷歌搜索了这个类,我可以在网上找到源代码,但为什么他们没有把它(和许多其他类)放在 src.zip 中?

两个原因:

  • 在过去,Java 代码库是专有的,这被视为秘密......并且不包含在src.zip. 当他们在 GPL 下重新授权 Java 6 时,他们并没有费心去改变它。(不知道为什么。问 Oracle。)

  • 因为sun.*树中的任何代码都是正式的“实现细节,如有更改,恕不另行通知”。如果他们直接提供代码,它会帮助客户忽略该建议。当客户代码因未经通知的代码更改而中断时,这可能会导致更多的摩擦/负面新闻sun.*

我在哪里可以找到源代码的综合版本?

您可以在 OpenJDK 6 / 7 / 8 存储库和相关的下载包中找到它:


现在是关于“深入学习 Java”的部分。

首先,我认为您可能正在以“次优”的方式进行这种学习。我认为您应该阅读有关 Java 和设计模式的书籍并为自己编写代码,而不是阅读 Java 类库。

具体到:

但是URLStreamHandler是一个抽象类和方法openConnection()在里面没有实现所以handler在调用这个方法的时候应该去找一个实现这个方法的子类吧?

在处理程序调用比方法时,它是在子类的实例上调用它。所以找到正确的方法是由 JVM 处理的......就像任何其他多态调度一样。

棘手的部分是您如何获得sun.net.www.protocol.*处理程序类的实例。这会发生这样的事情:

  1. 创建 URL 对象时,它会调用getURLStreamHandler(protocol)以获取处理程序实例。

  2. 此方法的代码查看协议的处理程序实例是否已经存在,如果存在则返回。

  3. 否则,它会查看是否存在协议处理程序工厂,如果存在,则使用它来创建处理程序实例。(协议处理程序工厂对象可以由应用程序设置。)

  4. 否则,搜索 Java 包的可配置列表以查找 FQN 为 的类package + "." + protocol + "." + "Handler",加载它,并使用反射创建实例。(配置是通过系统属性。)

  5. 对处理程序的引用存储在 URL 的处理程序字段中,并且 URL 构造继续进行。

因此,稍后,当您调用openConnection()URL 对象时,该方法使用Handler特定于协议的实例URL来创建连接对象。

这个复杂过程的目的是支持开放式协议集的 URL 连接,允许应用程序为新协议提供处理程序,并以静态和动态方式用自己的处理程序替换现有协议。(而且代码比我上面描述的更复杂,因为它必须处理多个线程。)

这利用了许多设计模式(缓存、适配器、工厂对象等)以及 Java 特定的东西,例如系统属性和反射。但是如果你没有阅读和理解这些设计模式等等,你是不可能认出它们的,结果你很可能会发现代码完全令人迷惑。因此我的建议是:先学基础!!

于 2012-10-06T23:59:37.887 回答
0

但是 URLStreamHandler 是一个抽象类,openConnection() 方法并没有在其中实现,所以当 handler 调用这个方法时,它应该去找一个实现这个方法的子类,对吧?

它必须在 URLStreamHandler 中声明或抽象或实现。然后,如果您提供一个扩展 URLStreamHandler 类型为 URLStreamHandler 的类的实例并调用 openConnection() 方法,它将调用您在扩展 URLStreamHandler 的类的实例中覆盖的实例(如果有),如果没有,它将尝试调用如果实现 URLStreamHandler 中的那个,否则它可能会抛出异常或其他东西。

于 2012-10-06T23:31:15.987 回答
0

看看URL.javaopenConnection使用URLStreamHandler先前在 URL 对象本身中设置的。

构造函数调用getURLStreamHandler动态生成类名并使用类加载器加载和实例化适当的类。

于 2012-10-06T23:33:30.950 回答