您好我正在尝试深入学习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并且它是正确的。 (如果我将网址设置为ftp://www.google.com,它将进入ftp.Handler)我无法理解该机制。
秒。我已经附加了源代码,所以我可以进入JDK并查看变量,但对于许多类,如sun.net.www.protocol.http.Handler,src.zip中没有源代码。我用谷歌搜索了这个类,我可以在线获取源代码,但为什么他们没有把它(以及许多其他类)放在src.zip中?我在哪里可以找到全面的源代码版本?
谢谢!
答案 0 :(得分:8)
首先是简单的部分:
...我用Google搜索了这个类,我可以在线获取源代码,但为什么他们没有把它(以及许多其他类)放在src.zip中?
有两个原因:
在Java代码库是专有的时代,这被视为秘密...并且不包含在src.zip
中。当他们在GPL下重新许可Java 6时,他们并没有费心去改变它。 (不知道为什么。问问Oracle。)
因为sun.*
树中的任何代码都是正式的“实施细节,如有更改,恕不另行通知”。如果他们直接提供代码,它可以帮助客户忽略该建议。当客户代码因sun.*
代码的突然更改而中断时,这可能会导致更多摩擦/坏消息。
在哪里可以找到全面的源代码版本?
您可以在OpenJDK 6/7/8存储库和相关的下载包中找到它:
现在关于“深入学习Java”的部分。
首先,我认为你可能会以“次优”的方式进行这种学习。我认为您应该阅读有关Java和设计模式以及为自己编写代码的书籍,而不是阅读Java类库。
具体细节:
但是
URLStreamHandler
是一个抽象类,方法openConnection()
没有在其中实现,所以当处理程序调用这个方法时,它应该去找一个实现这个方法的子类,对吧?
在处理程序调用而不是方法时,它在子类的实例上调用它。因此,找到正确的方法由JVM处理......就像任何其他多态分派一样。
棘手的部分是如何获得sun.net.www.protocol.*
处理程序类的实例。这就发生了这样的事情:
创建URL对象时,它会调用getURLStreamHandler(protocol)
来获取处理程序实例。
此方法的代码查看协议的处理程序实例是否已存在,如果存在,则返回该实例。
否则,它会查看协议处理程序工厂是否存在,如果存在,则使用它来创建处理程序实例。 (协议处理程序工厂对象可以由应用程序设置。)
否则,搜索可配置的Java包列表以查找FQN为package + "." + protocol + "." + "Handler"
的类,加载它,并使用反射创建实例。 (配置是通过系统属性。)
对处理程序的引用存储在URL的处理程序字段中,并且URL构造仍在继续。
因此,稍后,当您在URL对象上调用openConnection()
时,该方法使用特定于Handler
协议的URL
实例来创建连接对象。
这个复杂过程的目的是支持开放式协议集的URL连接,允许应用程序为新协议提供处理程序,并且可以静态和动态地将自己的处理程序替换为现有协议。 (而且代码比我上面描述的更复杂,因为它必须处理多个线程。)
这使用了许多设计模式(缓存,适配器,工厂对象等)以及Java特定的东西,例如系统属性和反射。但是如果你还没有阅读和理解这些设计模式,等等,你就不太可能认出它们,因此你可能会发现代码完全是竹子的。因此,我的建议如下:首先学习基础知识!!
答案 1 :(得分:0)
但是URLStreamHandler是一个抽象类和方法openConnection() 是没有实现它,所以当处理程序调用此方法时,它应该去 找到实现此方法的子类,对吧?
必须在URLStreamHandler中声明或抽象或实现它。如果你给一个扩展URLStreamHandler的类的实例并使用类型URLStreamHandler并调用openConnection()方法,它将调用你在扩展URLStreamHandler的类的实例中覆盖的那个(如果有的话),如果没有它将尝试调用如果实现了URLStreamHandler中的那个,否则它可能会抛出异常或其他东西。
答案 2 :(得分:0)
看看URL.java。 openConnection
使用之前在网址对象中设置的URLStreamHandler
。
constructor调用getURLStreamHandler
,它动态生成一个类名并加载,并使用类加载器实例化相应的类。