Java中URL原理用法示例中文源码分析
文章目录
含义
类{@code URL}表示统一资源定位符(URL),它是指向万维网上的“资源”的指针。
资源可以是一个简单的文件或目录,也可以是对更复杂对象的引用,例如对数据库或搜索引擎的查询。
有关URL类型及其格式的更多信息,请参阅:URL类型
通常情况下,URL可以被分成几个部分。考虑以下示例:
http://www.example.com/docs/resource1.html
上面的URL表示要使用的协议是{@code http}(超文本传输协议),信息驻留在名为{@code www.example.com}的主机上。
该主机上的信息被命名为{@code /docs/resource1.html}。此名称在主机上的确切含义取决于协议和主机。
信息通常驻留在文件中,但也可能是动态生成的。URL的这个组成部分称为路径组件。
URL还可以选择性地指定一个“端口”,即远程主机上要进行TCP连接的端口号。
如果未指定端口,则默认使用该协议的默认端口。
例如,{@code http}的默认端口是{@code 80}。
可以指定一个替代端口,如下所示:
http://www.example.com:1080/docs/resource1.html
{@code URL}的语法由RFC 2396:统一资源标识符(URI):通用语法定义,
并由RFC 2732:URL中文字面IPv6地址的格式修订。
文字IPv6地址格式也支持scope_id。有关scope_id的语法和用法,请参见这里。
URL可能会附加一个“片段”,也称为“引用”或“引用”。片段由井号字符“#”后跟更多字符来表示。
例如,
http://java.sun.com/index.html#chapter1
此片段在技术上不是URL的一部分。它表示在检索指定的资源之后,应用程序特别关注带有标签{@code chapter1}的文档的那一部分。
标签的含义与资源相关。
应用程序还可以指定“相对URL”,它只包含足够的信息以相对于另一个URL访问资源。
相对URL通常在HTML页面中使用。例如,如果URL的内容为:
http://java.sun.com/index.html
其中包含相对URL:
FAQ.html
它将是以下缩写形式:
http://java.sun.com/FAQ.html
相对URL不需要指定URL的所有组件。如果协议、主机名或端口号丢失,则该值将继承自完全指定的URL。
必须指定文件组件。可选的片段不会被继承。
URL类本身不会根据RFC2396中定义的转义机制对任何URL组件进行编码或解码。
调用者有责任在调用URL之前对需要进行转义的字段进行编码,并对返回的已转义字段进行解码。
此外,因为URL对URL转义没有了解,它不会识别相同URL的编码或解码形式之间的等价性。
例如,两个URL:
http://foo.com/hello world/
和
http://foo.com/hello%20world
将被认为不相等。
注意,{@link java.net.URI}类在某些情况下会对其组件字段执行转义。
管理URL的编码和解码的推荐方式是使用{@link java.net.URI},并使用{@link #toURI()}和{@link URI#toURL()}之间进行转换。
{@link URLEncoder}和{@link URLDecoder}类也可以用于HTML表单编码,
但这与RFC2396中定义的编码方案不同。
原理
Java中的URL类是用于处理统一资源定位符(URL)的实用工具。它提供了一种方便的方式来解析、构建和操作URL。
URL类的实现基于以下原理:
-
URL表示:
- URL由协议、主机、端口、路径、查询参数和片段标识符等组成。
- URL可以通过字符串形式进行表示,例如:“https://www.example.com/index.html?param=value#section”。
- URL类使用字符串构造函数或各个部分的setter方法来创建URL对象。
-
解析URL:
- URL类使用URLStreamHandler来处理不同协议的URL。每个支持的协议都有对应的URLStreamHandler实现。
- URLStreamHandlerFactory接口允许应用程序设置自定义的URLStreamHandlerFactory,以便支持自定义协议。
-
打开URL连接:
- URL.openConnection()方法打开与URL的连接,并返回一个URLConnection对象。
- URLConnection提供了与URL相关的IO操作,如读取和写入数据。
-
读取URL内容:
- URL.openStream()方法返回一个输入流,用于读取URL的内容。
- URL.getContent()方法获取URL的内容对象,类型可能根据URL的内容类型而异。
-
URL编码和解码:
- URL类提供了静态的encode()和decode()方法,用于URL的编码和解码。
- URL编码用于将特殊字符转换为URL安全的形式,以便在URL中传递参数和数据。
-
其他功能:
- URL类提供了许多其他方法,如获取URL的各个部分、比较URL对象、获取URL的响应状态码等。
- URL类还支持设置连接超时时间、代理服务器等功能。
总体而言,URL类通过封装URL的表示和操作,提供了一种方便的方式来处理URL。它基于URLStreamHandler和URLConnection实现与不同协议相关的功能,并提供了丰富的方法来处理URL的各个部分、读取内容、编码解码等操作。
用法
Java中的URL类提供了许多方法来处理URL。以下是URL类的一些常用方法:
构造函数:
-
URL(String spec)
:根据指定的URL字符串创建URL对象。 -
URL(String protocol, String host, int port, String file)
:根据给定的协议、主机、端口和文件路径创建URL对象。 -
URL(URL context, String spec)
:使用相对URL和基础URL创建URL对象。
获取URL的各个部分:
-
String getProtocol()
:获取URL的协议部分。 -
String getHost()
:获取URL的主机部分。 -
int getPort()
:获取URL的端口号。 -
String getFile()
:获取URL的文件路径部分。 -
String getQuery()
:获取URL的查询参数部分。 -
String getRef()
:获取URL的片段标识符部分。
连接管理:
-
URLConnection openConnection()
:打开与URL的连接并返回一个URLConnection对象,可以用于读取和写入数据。 -
URLConnection openConnection(Proxy proxy)
:使用指定的代理服务器打开与URL的连接。
读取URL内容:
-
InputStream openStream()
:打开URL连接并返回一个输入流,用于读取URL的内容。 -
Object getContent()
:获取URL的内容对象,返回的对象类型可能是不同的,具体取决于URL的内容类型。
URL编码和解码:
-
static String encode(String s)
:对指定的字符串进行URL编码。 -
static String decode(String s)
:对指定的URL编码字符串进行解码。
其他:
boolean equals(Object obj)
:比较URL对象与另一个对象是否相等。int hashCode()
:返回URL对象的哈希码值。String toString()
:将URL对象转换为字符串表示形式。
以上仅列举了URL类的一些常用方法,还有其他更多方法可以根据具体需求使用。
示例
以下是使用URL类的一些代码示例:
1.使用构造函数创建URL对象:
public class UrlTest {
public static void main(String[] args) {
try {
URL url = new URL("https://www.baidu.com");
System.out.println("Protocol: " + url.getProtocol());
System.out.println("Host: " + url.getHost());
System.out.println("Port: " + url.getPort());
System.out.println("File: " + url.getFile());
} catch (IOException e) {
e.printStackTrace();
}
}
}
// Protocol: https
// Host: www.baidu.com
// Port: -1
// File:
2.打开URL连接并读取内容:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class UrlTest {
public static void main(String[] args) {
try {
URL url = new URL("https://www.baidu.com");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
InputStream inputStream = connection.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
reader.close();
connection.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.对URL进行编码和解码:
public class UrlTest {
public static void main(String[] args) {
try {
String originalUrl = "https://www.example.com/search?q=java programming";
String encodedUrl = URLEncoder.encode(originalUrl, "UTF-8");
System.out.println("Encoded URL: " + encodedUrl);
String decodedUrl = URLDecoder.decode(encodedUrl, "UTF-8");
System.out.println("Decoded URL: " + decodedUrl);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
//Encoded URL: https%3A%2F%2Fwww.example.com%2Fsearch%3Fq%3Djava+programming
//Decoded URL: https://www.example.com/search?q=java programming
4.使用相对URL和基础URL创建URL对象:
class UrlTest2 {
public static void main(String[] args) {
try {
URL baseURL = new URL("https://www.example.com");
URL relativeURL = new URL(baseURL, "/about.html");
System.out.println("Base URL: " + baseURL.toString());
System.out.println("Relative URL: " + relativeURL.toString());
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
}
//Base URL: https://www.example.com
//Relative URL: https://www.example.com/about.html
5.获取URL的查询参数:
class UrlTest3 {
public static void main(String[] args) {
try {
URL url = new URL("https://www.example.com/search?q=java+programming&lang=en");
String query = url.getQuery();
if (query != null) {
String[] params = query.split("&");
for (String param : params) {
String[] keyValue = param.split("=");
String key = URLDecoder.decode(keyValue[0], "UTF-8");
String value = URLDecoder.decode(keyValue[1], "UTF-8");
System.out.println(key + ": " + value);
}
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
//q: java programming
//lang: en
6.比较URL对象:
class UrlTest4 {
public static void main(String[] args) {
try {
URL url1 = new URL("https://www.example.com");
URL url2 = new URL("https://www.example.com");
boolean isEqual = url1.equals(url2);
System.out.println("URLs are equal: " + isEqual);
int hashCode1 = url1.hashCode();
int hashCode2 = url2.hashCode();
System.out.println("HashCode 1: " + hashCode1);
System.out.println("HashCode 2: " + hashCode2);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
}
//URLs are equal: true
//HashCode 1: 1672012488
//HashCode 2: 1672012488
7.设置URL连接的超时时间:
try {
URL url = new URL("https://www.example.com");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(5000); // 设置连接超时时间为5秒
connection.setReadTimeout(10000); // 设置读取超时时间为10秒
// 进行其他操作,如读取数据
connection.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
8.获取URL的响应头信息:
class UrlTest7 {
public static void main(String[] args) {
try {
URL url = new URL("https://www.baidu.com");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
Map<String, List<String>> headers = connection.getHeaderFields();
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
String key = entry.getKey();
List<String> values = entry.getValue();
System.out.println(key + ": " + values);
}
connection.disconnect();
connection.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//null: [HTTP/1.1 200 OK]
//Server: [bfe]
//Content-Length: [2443]
//Date: [Sun, 15 Oct 2023 15:56:20 GMT]
//Content-Type: [text/html]
9.使用代理服务器打开URL连接:
try {
URL url = new URL("https://www.example.com");
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy.example.com", 8080));
HttpURLConnection connection = (HttpURLConnection) url.openConnection(proxy);
// 进行其他操作,如读取数据
connection.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
10.获取URL的输入流,并将其保存到文件中:
class UrlTest9 {
public static void main(String[] args) {
try {
URL url = new URL("https://pic35.photophoto.cn/20150511/0034034892281415_b.jpg");
InputStream inputStream = url.openStream();
FileOutputStream outputStream = new FileOutputStream("image.jpg");
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
inputStream.close();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}}
11.获取URL的响应状态码:
class UrlTest8 {
public static void main(String[] args) {
try {
URL url = new URL("https://www.baidu.com");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
int statusCode = connection.getResponseCode();
System.out.println("Status Code: " + statusCode);
connection.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// Status Code: 200
中文源码
/**
* 类{@code URL}表示统一资源定位符(URL),它是指向万维网上的“资源”的指针。
* 资源可以是一个简单的文件或目录,也可以是对更复杂对象的引用,例如对数据库或搜索引擎的查询。
* 有关URL类型及其格式的更多信息,请参阅:<a href="http://web.archive.org/web/20051219043731/http://archive.ncsa.uiuc.edu/SDG/Software/Mosaic/Demo/url-primer.html"><i>URL类型</i></a>
*
* 通常情况下,URL可以被分成几个部分。考虑以下示例:
*
* http://www.example.com/docs/resource1.html
*
* 上面的URL表示要使用的协议是{@code http}(超文本传输协议),信息驻留在名为{@code www.example.com}的主机上。
* 该主机上的信息被命名为{@code /docs/resource1.html}。此名称在主机上的确切含义取决于协议和主机。
* 信息通常驻留在文件中,但也可能是动态生成的。URL的这个组成部分称为<i>路径</i>组件。
*
* URL还可以选择性地指定一个“端口”,即远程主机上要进行TCP连接的端口号。
* 如果未指定端口,则默认使用该协议的默认端口。
* 例如,{@code http}的默认端口是{@code 80}。
* 可以指定一个替代端口,如下所示:
*
* http://www.example.com:1080/docs/resource1.html
*
* {@code URL}的语法由<a href="http://www.ietf.org/rfc/rfc2396.txt"><i>RFC 2396:统一资源标识符(URI):通用语法</i></a>定义,
* 并由<a href="http://www.ietf.org/rfc/rfc2732.txt"><i>RFC 2732:URL中文字面IPv6地址的格式</i></a>修订。
* 文字IPv6地址格式也支持scope_id。有关scope_id的语法和用法,请参见<a href="Inet6Address.html#scoped">这里</a>。
*
* URL可能会附加一个“片段”,也称为“引用”或“引用”。片段由井号字符“#”后跟更多字符来表示。
* 例如,
*
* http://java.sun.com/index.html#chapter1
*
* 此片段在技术上不是URL的一部分。它表示在检索指定的资源之后,应用程序特别关注带有标签{@code chapter1}的文档的那一部分。
* 标签的含义与资源相关。
*
* 应用程序还可以指定“相对URL”,它只包含足够的信息以相对于另一个URL访问资源。
* 相对URL通常在HTML页面中使用。例如,如果URL的内容为:
*
* http://java.sun.com/index.html
*
* 其中包含相对URL:
*
* FAQ.html
*
* 它将是以下缩写形式:
*
* http://java.sun.com/FAQ.html
*
* 相对URL不需要指定URL的所有组件。如果协议、主机名或端口号丢失,则该值将继承自完全指定的URL。
* 必须指定文件组件。可选的片段不会被继承。
*
* URL类本身不会根据RFC2396中定义的转义机制对任何URL组件进行编码或解码。
* 调用者有责任在调用URL之前对需要进行转义的字段进行编码,并对返回的已转义字段进行解码。
* 此外,因为URL对URL转义没有了解,它不会识别相同URL的编码或解码形式之间的等价性。
* 例如,两个URL:
* http://foo.com/hello world/
* 和
* http://foo.com/hello%20world
* 将被认为不相等。
*
* 注意,{@link java.net.URI}类在某些情况下会对其组件字段执行转义。
* 管理URL的编码和解码的推荐方式是使用{@link java.net.URI},并使用{@link #toURI()}和{@link URI#toURL()}之间进行转换。
*
* {@link URLEncoder}和{@link URLDecoder}类也可以用于HTML表单编码,
* 但这与RFC2396中定义的编码方案不同。
*
* @author James Gosling
* @since JDK1.0
*/
public final class URL implements java.io.Serializable {
static final String BUILTIN_HANDLERS_PREFIX = "sun.net.www.protocol";
static final long serialVersionUID = -7627629688361524110L;
/**
* 指定要扫描的协议处理程序的包前缀列表的属性。该属性的值(如果有)应为以竖线分隔的包名称列表,
* 用于搜索要加载的协议处理程序。
* 该类的策略是所有协议处理程序都将位于名为<protocolname>.Handler的类中,
* 并且会依次检查列表中的每个包是否存在匹配的处理程序。
* 如果找不到(或未指定属性),则使用默认的包前缀sun.net.www.protocol。
* 搜索从列表中的第一个包开始,到最后一个包结束,并在找到匹配项时停止。
*/
private static final String protocolPathProp = "java.protocol.handler.pkgs";
/**
* 要使用的协议(ftp、http、nntp等)。
* @serial
*/
private String protocol;
/**
* 要连接的主机名。
* @serial
*/
private String host;
/**
* 要连接的协议端口。
* @serial
*/
private int port = -1;
/**
* 主机上指定的文件名。{@code file}被定义为{@code path[?query]}。
* @serial
*/
private String file;
/**
* 此URL的查询部分。
*/
private transient String query;
/**
* 此URL的授权部分。
* @serial
*/
private String authority;
/**
* 此URL的路径部分。
*/
private transient String path;
/**
* 此URL的用户信息部分。
*/
private transient String userInfo;
/**
* 引用。
* @serial
*/
private String ref;
/**
* 主机的IP地址,用于equals和hashCode。根据需要计算。未初始化或未知的hostAddress为null。
*/
transient InetAddress hostAddress;
/**
* 此URL的URLStreamHandler。
*/
transient URLStreamHandler handler;
/* 我们的哈希码。
* @serial
*/
private int hashCode = -1;
private transient UrlDeserializedState tempState;
/**
* 从指定的 protocol、host、port 号和 file 创建一个 URL 对象。
*
* host 可以表示为主机名或字面 IP 地址。如果使用 IPv6 字面地址,则应将其括在方括号([ 和 ])中,如 RFC 2732 所指定;但是,也接受 RFC 2373:IP Version 6 Addressing Architecture 中定义的字面 IPv6 地址格式。
*
* 指定 port 号为 -1 表示 URL 应使用协议的默认端口。
*
* 如果这是使用指定协议创建的第一个 URL 对象,则会为该协议创建一个流协议处理程序对象,即 URLStreamHandler 类的实例:
* - 如果应用程序先前设置了 URLStreamHandlerFactory 的实例作为流处理程序工厂,则调用该实例的 createURLStreamHandler 方法,参数为协议字符串,以创建流协议处理程序。
* - 如果尚未设置 URLStreamHandlerFactory,或者如果工厂的 createURLStreamHandler 方法返回 null,则构造函数找到系统属性的值 java.protocol.handler.pkgs。
* 如果该系统属性的值不为 null,则将其解释为由竖线字符 | 分隔的包列表。构造函数尝试加载名为 <package>.<protocol>.Handler 的类,其中 <package> 被包的名称替换,而 <protocol> 被协议的名称替换。
* 如果此类不存在,或者如果该类存在但不是 URLStreamHandler 的子类,则尝试下一个包。
* - 如果前面的步骤无法找到协议处理程序,则构造函数尝试从系统默认包中加载 <system default package>.<protocol>.Handler。
* 如果此类不存在,或者如果该类存在但不是 URLStreamHandler 的子类,则抛出 MalformedURLException。
*
* 以下协议的协议处理程序保证存在于搜索路径上:
* - http
* - https
* - file
* - jar
* 还可以提供其他协议的协议处理程序。
*
* 此构造函数不执行输入的任何验证。
*
* @param protocol 要使用的协议的名称。
* @param host 主机的名称。
* @param port 主机上的端口号。
* @param file 主机上的文件。
* @exception MalformedURLException 如果指定了未知协议。
* @see java.lang.System#getProperty(java.lang.String)
* @see java.net.URL#setURLStreamHandlerFactory(java.net.URLStreamHandlerFactory)
* @see java.net.URLStreamHandler
* @see java.net.URLStreamHandlerFactory#createURLStreamHandler(java.lang.String)
*/
public URL(String protocol, String host, int port, String file) throws MalformedURLException {
this(protocol, host, port, file, null);
}
/**
* 通过指定的协议名称、主机名和文件名创建一个 URL 对象。将使用指定协议的默认端口。
*
* 此方法相当于使用参数 protocol、host、-1 和 file 调用四个参数的构造函数。
*
* 此构造函数不执行输入的任何验证。
*
* @param protocol 协议名称。
* @param host 主机名。
* @param file 文件名。
* @exception MalformedURLException 如果未指定协议、找到未知协议或 spec 为 null。
* @see java.net.URL#URL(java.lang.String, java.lang.String, int, java.lang.String)
*/
public URL(String protocol, String host, String file) throws MalformedURLException {
this(protocol, host, -1, file);
}
/**
* 通过指定的协议、主机、端口号、文件和处理程序创建一个 URL 对象。指定端口号为 -1 表示该 URL 应使用协议的默认端口。指定处理程序为 null 表示 URL 应使用协议的默认流处理程序。
*
* 如果处理程序不为 null 并且存在安全管理器,则会使用安全管理器的 checkPermission 方法检查 NetPermission("specifyStreamHandler") 权限。这可能导致 SecurityException。
*
* 此构造函数不执行输入的任何验证。
*
* @param protocol 协议名称。
* @param host 主机名。
* @param port 主机上的端口号。
* @param file 主机上的文件。
* @param handler URL 的流处理程序。
* @exception MalformedURLException 如果未指定协议、找到未知协议或 spec 为 null。
* @exception SecurityException 如果存在安全管理器且其 checkPermission 方法不允许显式指定处理程序。
* @see java.lang.System#getProperty(java.lang.String)
* @see java.net.URL#setURLStreamHandlerFactory(java.net.URLStreamHandlerFactory)
* @see java.net.URLStreamHandler
* @see java.net.URLStreamHandlerFactory#createURLStreamHandler(java.lang.String)
* @see SecurityManager#checkPermission
* @see java.net.NetPermission
*/
public URL(String protocol, String host, int port, String file, URLStreamHandler handler) throws MalformedURLException {
if (handler != null) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkSpecifyHandler(sm);
}
}
protocol = protocol.toLowerCase();
this.protocol = protocol;
if (host != null) {
if (host.indexOf(':') >= 0 && !host.startsWith("[")) {
host = "[" + host + "]";
}
this.host = host;
if (port < -1) {
throw new MalformedURLException("Invalid port number: " + port);
}
this.port = port;
authority = (port == -1) ? host : host + ":" + port;
}
Parts parts = new Parts(file);
path = parts.getPath();
query = parts.getQuery();
if (query != null) {
this.file = path + "?" + query;
} else {
this.file = path;
}
ref = parts.getRef();
if (handler == null && (handler = getURLStreamHandler(protocol)) == null) {
throw new MalformedURLException("Unknown protocol: " + protocol);
}
this.handler = handler;
if (host != null && isBuiltinStreamHandler(handler)) {
String s = IPAddressUtil.checkExternalForm(this);
if (s != null) {
throw new MalformedURLException(s);
}
}
if ("jar".equalsIgnoreCase(protocol)) {
if (handler instanceof sun.net.www.protocol.jar.Handler) {
String s = ((sun.net.www.protocol.jar.Handler) handler).checkNestedProtocol(file);
if (s != null) {
throw new MalformedURLException(s);
}
}
}
}
/**
* 通过解析给定的字符串表示形式创建一个 URL 对象。
*
* 此构造函数相当于调用两个参数的构造函数,第一个参数为 null。
*
* @param spec 要解析为 URL 的字符串。
* @exception MalformedURLException 如果未指定协议、找到未知协议或 spec 为 null。
* @see java.net.URL#URL(java.net.URL, java.lang.String)
*/
public URL(String spec) throws MalformedURLException {
this(null, spec);
}
/**
* 通过在指定上下文中解析给定的规范创建一个 URL。
*
* 新的 URL 是根据给定的上下文 URL 和规范参数创建的,如 RFC2396“Uniform Resource Identifiers : Generic Syntax”中所述:
* <scheme>://<authority><path>?<query>#<fragment>
* 引用将被解析为方案、权限、路径、查询和片段部分。如果路径组件为空且未定义方案、权限和查询组件,则新的 URL 是对当前文档的引用。否则,在新 URL 中使用规范中的片段和查询部分。
*
* 如果规范中定义了方案组件并且与上下文的方案不匹配,则新 URL 将作为仅基于规范的绝对 URL 创建。否则,方案组件将继承自上下文 URL。
*
* 如果规范中存在权限组件,则规范将被视为绝对的,并且规范权限和路径将替换上下文权限和路径。如果规范中不存在权限组件,则新 URL 的权限将继承自上下文。
*
* 如果规范的路径组件以斜杠字符“/”开头,则该路径被视为绝对路径,并且规范路径将替换上下文路径。
*
* 否则,路径被视为相对路径,并追加到上下文路径中,如 RFC2396 中所述。此外,在这种情况下,通过删除出现的“..”和“.”来对路径进行规范化。
*
* 有关 URL 解析的更详细描述,请参见 RFC2396。
*
* @param context 要解析规范的上下文。
* @param spec 要解析为 URL 的字符串。
* @exception MalformedURLException 如果未指定协议、找到未知协议或 spec 为 null。
* @see java.net.URL#URL(java.lang.String, java.lang.String, int, java.lang.String)
* @see java.net.URLStreamHandler
* @see java.net.URLStreamHandler#parseURL(java.net.URL, java.lang.String, int, int)
*/
public URL(URL context, String spec) throws MalformedURLException {
this(context, spec, null);
}
/**
* 通过在指定上下文中使用指定处理程序解析给定的规范创建 URL。如果处理程序为 null,则与两个参数的构造函数一样进行解析。
*
* @param context 要解析规范的上下文。
* @param spec 要解析为 URL 的字符串。
* @param handler URL 的流处理程序。
* @exception MalformedURLException 如果未指定协议、找到未知协议或 spec 为 null。
* @exception SecurityException 如果存在安全管理器且其 checkPermission 方法不允许指定流处理程序。
* @see java.net.URL#URL(java.lang.String, java.lang.String, int, java.lang.String)
* @see java.net.URLStreamHandler
* @see java.net.URLStreamHandler#parseURL(java.net.URL, java.lang.String, int, int)
*/
public URL(URL context, String spec, URLStreamHandler handler) throws MalformedURLException {
String original = spec;
int i, limit, c;
int start = 0;
String newProtocol = null;
boolean aRef = false;
boolean isRelative = false;
if (handler != null) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkSpecifyHandler(sm);
}
}
try {
limit = spec.length();
while ((limit > 0) && (spec.charAt(limit - 1) <= ' ')) {
limit--;
}
while ((start < limit) && (spec.charAt(start) <= ' ')) {
start++;
}
if (spec.regionMatches(true, start, "url:", 0, 4)) {
start += 4;
}
if (start < spec.length() && spec.charAt(start) == '#') {
aRef = true;
}
for (i = start ; !aRef && (i < limit) && ((c = spec.charAt(i)) != '/') ; i++) {
if (c == ':') {
String s = spec.substring(start, i).toLowerCase();
if (isValidProtocol(s)) {
newProtocol = s;
start = i + 1;
}
break;
}
}
protocol = newProtocol;
if ((context != null) && ((newProtocol == null) || newProtocol.equalsIgnoreCase(context.protocol))) {
if (handler == null) {
handler = context.handler;
}
if (context.path != null && context.path.startsWith("/")) {
newProtocol = null;
}
if (newProtocol == null) {
protocol = context.protocol;
authority = context.authority;
userInfo = context.userInfo;
host = context.host;
port = context.port;
file = context.file;
path = context.path;
isRelative = true;
}
}
if (protocol == null) {
throw new MalformedURLException("No protocol specified: " + original);
}
if (handler == null && (handler = getURLStreamHandler(protocol)) == null) {
throw new MalformedURLException("Unknown protocol: " + protocol);
}
this.handler = handler;
i = spec.indexOf('#', start);
if (i >= 0) {
ref = spec.substring(i + 1, limit);
limit = i;
}
if (isRelative && start == limit) {
query = context.query;
if (ref == null) {
ref = context.ref;
}
}
handler.parseURL(this, spec, start, limit);
} catch (MalformedURLException e) {
throw e;
} catch (Exception e) {
MalformedURLException exception = new MalformedURLException(e.getMessage());
exception.initCause(e);
throw exception;
}
}
/**
* 判断指定的字符串是否是有效的协议名称。
*/
private boolean isValidProtocol(String protocol) {
int len = protocol.length();
if (len < 1)
return false;
char c = protocol.charAt(0);
if (!Character.isLetter(c))
return false;
for (int i = 1; i < len; i++) {
c = protocol.charAt(i);
if (!Character.isLetterOrDigit(c) && c != '.' && c != '+' &&
c != '-') {
return false;
}
}
return true;
}
/**
* 检查是否有权限指定流处理程序。
*/
private void checkSpecifyHandler(SecurityManager sm) {
sm.checkPermission(SecurityConstants.SPECIFY_HANDLER_PERMISSION);
}
/**
* 设置URL的字段。这不是一个公共方法,以便只有URLStreamHandlers可以修改URL字段。URL通常是不可变的。
*
* @param protocol 使用的协议的名称
* @param host 主机名
* @param port 主机上的端口号
* @param file 主机上的文件
* @param ref URL中的内部引用
*/
void set(String protocol, String host, int port,
String file, String ref) {
synchronized (this) {
this.protocol = protocol;
this.host = host;
authority = port == -1 ? host : host + ":" + port;
this.port = port;
this.file = file;
this.ref = ref;
/* 这非常重要。在URL被改变后我们必须重新计算hashCode。 */
hashCode = -1;
hostAddress = null;
int q = file.lastIndexOf('?');
if (q != -1) {
query = file.substring(q+1);
path = file.substring(0, q);
} else
path = file;
}
}
/**
* 设置指定的8个URL字段。这不是一个公共方法,以便只有URLStreamHandlers可以修改URL字段。URL通常是不可变的。
*
* @param protocol 使用的协议的名称
* @param host 主机名
* @param port 主机上的端口号
* @param authority URL的授权部分
* @param userInfo 用户名和密码
* @param path 主机上的文件
* @param ref URL中的内部引用
* @param query 这个URL的查询部分
* @since 1.3
*/
void set(String protocol, String host, int port,
String authority, String userInfo, String path,
String query, String ref) {
synchronized (this) {
this.protocol = protocol;
this.host = host;
this.port = port;
this.file = query == null ? path : path + "?" + query;
this.userInfo = userInfo;
this.path = path;
this.ref = ref;
/* 这非常重要。在URL被改变后我们必须重新计算hashCode。 */
hashCode = -1;
hostAddress = null;
this.query = query;
this.authority = authority;
}
}
/**
* 获取此URL的查询部分。
*
* @return 此URL的查询部分,如果不存在则返回null
* @since 1.3
*/
public String getQuery() {
return query;
}
/**
* 获取此URL的路径部分。
*
* @return 此URL的路径部分,如果不存在则返回空字符串
* @since 1.3
*/
public String getPath() {
return path;
}
/**
* 获取此URL的userInfo部分。
*
* @return 此URL的userInfo部分,如果不存在则返回null
* @since 1.3
*/
public String getUserInfo() {
return userInfo;
}
/**
* 获取此URL的authority部分。
*
* @return 此URL的authority部分
* @since 1.3
*/
public String getAuthority() {
return authority;
}
/**
* 获取此URL的端口号。
*
* @return 端口号,如果未设置端口则返回-1
*/
public int getPort() {
return port;
}
/**
* 获取与此URL关联的协议的默认端口号。如果URL方案或URLStreamHandler未定义默认端口号,则返回-1。
*
* @return 端口号
* @since 1.4
*/
public int getDefaultPort() {
return handler.getDefaultPort();
}
/**
* 获取此URL的协议名称。
*
* @return 此URL的协议
*/
public String getProtocol() {
return protocol;
}
/**
* 获取此URL的主机名(如果适用)。主机名的格式符合RFC 2732,即对于字面IPv6地址,此方法将返回用方括号('['和']')括起来的IPv6地址。
*
* @return 此URL的主机名
*/
public String getHost() {
return host;
}
/**
* 获取此URL的文件名。返回的文件部分将与getPath()相同,加上如果有的话getQuery()的值。如果没有查询部分,这个方法和getPath()将返回相同的结果。
*
* @return 此URL的文件名,如果不存在则返回空字符串
*/
public String getFile() {
return file;
}
/**
* 获取此URL的锚点(也称为“引用”)。
*
* @return 此URL的锚点,如果不存在则返回null
*/
public String getRef() {
return ref;
}
/**
* 将此URL与另一个对象进行比较,判断是否相等。
*
* 如果给定的对象不是URL,则该方法立即返回false。
*
* 两个URL对象相等的条件是:它们具有相同的协议、引用等效的主机、在主机上具有相同的端口号以及相同的文件和文件片段。
*
* 如果两个主机名称可以解析为相同的IP地址,则认为两个主机是等效的;否则,如果任一主机名称无法解析,主机名称必须相等,而不区分大小写;或者两个主机名称都为null。
*
* 由于主机比较需要进行名称解析,这个操作是一个阻塞操作。
*
* 注意:已知{@code equals}的定义行为与HTTP中的虚拟主机不一致。
*
* @param obj 要比较的URL。
* @return 如果对象相同则返回true;否则返回false。
*/
public boolean equals(Object obj) {
if (!(obj instanceof URL))
return false;
URL u2 = (URL)obj;
return handler.equals(this, u2);
}
/**
* 创建适合哈希表索引的整数。
*
* 哈希码基于所有与URL比较相关的URL组件。因此,这个操作是一个阻塞操作。
*
* @return 这个URL的哈希码。
*/
public synchronized int hashCode() {
if (hashCode != -1)
return hashCode;
hashCode = handler.hashCode(this);
return hashCode;
}
/**
* 比较两个URL,不包括片段部分。
*
* 如果此URL和{@code other}参数在不考虑片段部分的情况下相等,则返回{@code true}。
*
* @param other 要比较的URL。
* @return 如果它们引用相同的远程对象,则返回{@code true};否则返回{@code false}。
*/
public boolean sameFile(URL other) {
return handler.sameFile(this, other);
}
/**
* 构造此URL的字符串表示形式。通过调用此对象的流协议处理程序的{@code toExternalForm}方法创建字符串。
*
* @return 此对象的字符串表示形式。
* @see java.net.URL#URL(java.lang.String, java.lang.String, int,
* java.lang.String)
* @see java.net.URLStreamHandler#toExternalForm(java.net.URL)
*/
public String toString() {
return toExternalForm();
}
/**
* 构造此URL的字符串表示形式。通过调用此对象的流协议处理程序的{@code toExternalForm}方法创建字符串。
*
* @return 此对象的字符串表示形式。
* @see java.net.URL#URL(java.lang.String, java.lang.String,
* int, java.lang.String)
* @see java.net.URLStreamHandler#toExternalForm(java.net.URL)
*/
public String toExternalForm() {
return handler.toExternalForm(this);
}
/**
* 返回与此URL等效的{@link java.net.URI}。此方法的功能方式与{@code new URI (this.toString())}相同。
* 注意,任何符合RFC 2396的URL实例都可以转换为URI。然而,某些不严格符合规范的URL无法转换为URI。
*
* @exception URISyntaxException 如果此URL的格式不严格符合RFC2396,并且无法转换为URI。
*
* @return 与此URL等效的URI实例。
* @since 1.5
*/
public URI toURI() throws URISyntaxException {
URI uri = new URI(toString());
if (authority != null && isBuiltinStreamHandler(handler)) {
String s = IPAddressUtil.checkAuthority(this);
if (s != null) throw new URISyntaxException(authority, s);
}
return uri;
}
/**
* 返回表示到{@code URL}所引用的远程对象的连接的{@link java.net.URLConnection}实例。
*
* 每次调用此URL的协议处理程序的{@linkplain java.net.URLStreamHandler#openConnection(URL)URLStreamHandler.openConnection(URL)}方法时,
* 都会创建一个新的{@linkplain java.net.URLConnection URLConnection}实例。</P>
*
* 需要注意的是,URLConnection实例在创建时不会建立实际的网络连接。只有在调用{@linkplain java.net.URLConnection#connect() URLConnection.connect()}时才会发生连接。</P>
*
* 如果URL的协议(如HTTP或JAR)存在于以下包或其子包中的公共专用URLConnection子类:java.lang、java.io、java.util、java.net,则返回的连接将是该子类。
* 例如,对于HTTP,将返回一个HttpURLConnection;对于JAR,将返回一个JarURLConnection。</P>
*
* @return 一个链接到URL的{@link java.net.URLConnection}。
* @exception IOException 如果发生I/O异常。
* @see java.net.URL#URL(java.lang.String, java.lang.String,
* int, java.lang.String)
*/
public URLConnection openConnection() throws java.io.IOException {
return handler.openConnection(this);
}
/**
* 与{@link #openConnection()}相同,只是连接将通过指定的代理进行。
* 不支持代理的协议处理程序将忽略proxy参数并进行正常连接。
*
* 调用此方法会优先于系统的默认ProxySelector设置。
*
* @param proxy 将通过该代理进行连接。如果要直接连接,则应指定Proxy.NO_PROXY。
* @return 链接到URL的{@code URLConnection}。
* @exception IOException 如果发生I/O异常。
* @exception SecurityException 如果存在安全管理器且调用者没有连接到代理的权限。
* @exception IllegalArgumentException 如果proxy为null,或proxy类型不正确
* @exception UnsupportedOperationException 如果实现协议处理程序的子类不支持此方法。
* @see java.net.URL#URL(java.lang.String, java.lang.String,
* int, java.lang.String)
* @see java.net.URLConnection
* @see java.net.URLStreamHandler#openConnection(java.net.URL,
* java.net.Proxy)
* @since 1.5
*/
public URLConnection openConnection(Proxy proxy)
throws java.io.IOException {
if (proxy == null) {
throw new IllegalArgumentException("proxy can not be null");
}
// 创建代理的副本作为一种安全措施
Proxy p = proxy == Proxy.NO_PROXY ? Proxy.NO_PROXY : sun.net.ApplicationProxy.create(proxy);
SecurityManager sm = System.getSecurityManager();
if (p.type() != Proxy.Type.DIRECT && sm != null) {
InetSocketAddress epoint = (InetSocketAddress) p.address();
if (epoint.isUnresolved())
sm.checkConnect(epoint.getHostName(), epoint.getPort());
else
sm.checkConnect(epoint.getAddress().getHostAddress(),
epoint.getPort());
}
return handler.openConnection(this, p);
}
/**
* 打开与此URL的连接并返回一个用于从该连接读取的{@code InputStream}。
* 这个方法是以下代码的简写形式:
* <blockquote><pre>
* openConnection().getInputStream()
* </pre></blockquote>
*
* @return 用于从URL连接读取的输入流。
* @exception IOException 如果发生I/O异常。
* @see java.net.URL#openConnection()
* @see java.net.URLConnection#getInputStream()
*/
public final InputStream openStream() throws java.io.IOException {
return openConnection().getInputStream();
}
/**
* 获取此URL的内容。此方法是以下方法的简写形式:
* <blockquote><pre>
* openConnection().getContent()
* </pre></blockquote>
*
* @return 此URL的内容。
* @exception IOException 如果发生I/O异常。
* @see java.net.URLConnection#getContent()
*/
public final Object getContent() throws java.io.IOException {
return openConnection().getContent();
}
/**
* 获取此URL的内容。此方法是以下方法的简写形式:
* <blockquote><pre>
* openConnection().getContent(Class[])
* </pre></blockquote>
*
* @param classes Java类型数组
* @return 此URL的内容对象,即与classes数组中指定类型匹配的第一个对象。
* 如果没有请求的类型被支持,则返回null。
* @exception IOException 如果发生I/O异常。
* @see java.net.URLConnection#getContent(Class[])
* @since 1.3
*/
public final Object getContent(Class[] classes)
throws java.io.IOException {
return openConnection().getContent(classes);
}
/**
* URLStreamHandler工厂。
*/
static URLStreamHandlerFactory factory;
/**
* 设置应用程序的{@code URLStreamHandlerFactory}。
* 在给定的Java虚拟机中,此方法最多可以调用一次。
*
* {@code URLStreamHandlerFactory}实例用于
*从协议名称构造流协议处理程序。
*
* 如果存在安全管理器,此方法首先调用
* 安全管理器的{@code checkSetFactory}方法,
* 以确保允许该操作。
* 这可能导致SecurityException。
*
* @param fac 所需的工厂。
* @exception Error 如果应用程序已经设置了工厂。
* @exception SecurityException 如果存在安全管理器,并且
* 其{@code checkSetFactory}方法不允许该操作。
* @see java.net.URL#URL(java.lang.String, java.lang.String,
* int, java.lang.String)
* @see java.net.URLStreamHandlerFactory
* @see SecurityManager#checkSetFactory
*/
public static void setURLStreamHandlerFactory(URLStreamHandlerFactory fac) {
synchronized (streamHandlerLock) {
if (factory != null) {
throw new Error("factory already defined");
}
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkSetFactory();
}
handlers.clear();
factory = fac;
}
}
/**
* 协议处理程序的表。
*/
static Hashtable<String,URLStreamHandler> handlers = new Hashtable<>();
private static Object streamHandlerLock = new Object();
/**
* 返回流处理程序。
* @param protocol 要使用的协议
*/
static URLStreamHandler getURLStreamHandler(String protocol) {
URLStreamHandler handler = handlers.get(protocol);
if (handler == null) {
boolean checkedWithFactory = false;
// 使用工厂(如果有)
if (factory != null) {
handler = factory.createURLStreamHandler(protocol);
checkedWithFactory = true;
}
// 尝试java协议处理程序
if (handler == null) {
String packagePrefixList = null;
packagePrefixList
= java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction(
protocolPathProp,""));
if (packagePrefixList != "") {
packagePrefixList += "|";
}
// REMIND: 决定是否允许“null”类前缀
// 或不允许。
packagePrefixList += "sun.net.www.protocol";
StringTokenizer packagePrefixIter =
new StringTokenizer(packagePrefixList, "|");
while (handler == null &&
packagePrefixIter.hasMoreTokens()) {
String packagePrefix =
packagePrefixIter.nextToken().trim();
try {
String clsName = packagePrefix + "." + protocol +
".Handler";
Class<?> cls = null;
try {
cls = Class.forName(clsName);
} catch (ClassNotFoundException e) {
ClassLoader cl = ClassLoader.getSystemClassLoader();
if (cl != null) {
cls = cl.loadClass(clsName);
}
}
if (cls != null) {
handler =
(URLStreamHandler)cls.newInstance();
}
} catch (Exception e) {
// 可能会抛出多个异常
}
}
}
synchronized (streamHandlerLock) {
URLStreamHandler handler2 = null;
// 再次检查哈希表,以防另一个线程自上次检查以来创建了处理程序
handler2 = handlers.get(protocol);
if (handler2 != null) {
return handler2;
}
// 如果另一个线程自我们上次检查以来设置了工厂,请与工厂检查
if (!checkedWithFactory && factory != null) {
handler2 = factory.createURLStreamHandler(protocol);
}
if (handler2 != null) {
// 工厂的处理程序必须更重要。放弃
// 此线程创建的默认处理程序。
handler = handler2;
}
// 将此处理程序插入哈希表
if (handler != null) {
handlers.put(protocol, handler);
}
}
}
return handler;
}
/**
* @serialField protocol String
*
* @serialField host String
*
* @serialField port int
*
* @serialField authority String
*
* @serialField file String
*
* @serialField ref String
*
* @serialField hashCode int
*
*/
private static final ObjectStreamField[] serialPersistentFields = {
new ObjectStreamField("protocol", String.class),
new ObjectStreamField("host", String.class),
new ObjectStreamField("port", int.class),
new ObjectStreamField("authority", String.class),
new ObjectStreamField("file", String.class),
new ObjectStreamField("ref", String.class),
new ObjectStreamField("hashCode", int.class), };
/**
* WriteObject被调用以将URL的状态保存到ObjectOutputStream中。处理程序不保存,因为它特定于该系统。
*
* @serialData 默认写对象值。在读取时,
* 读者必须确保使用协议变量调用getURLStreamHandler返回一个有效的URLStreamHandler,
* 如果不是,则抛出IOException。
*/
private synchronized void writeObject(java.io.ObjectOutputStream s)
throws IOException
{
s.defaultWriteObject(); // 写入字段
}
/**
* readObject被调用以从流中恢复URL的状态。它读取URL的组件并查找本地流处理程序。
*/
private synchronized void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException {
GetField gf = s.readFields();
String protocol = (String)gf.get("protocol", null);
if (getURLStreamHandler(protocol) == null) {
throw new IOException("unknown protocol: " + protocol);
}
String host = (String)gf.get("host", null);
int port = gf.get("port", -1);
String authority = (String)gf.get("authority", null);
String file = (String)gf.get("file", null);
String ref = (String)gf.get("ref", null);
int hashCode = gf.get("hashCode", -1);
if (authority == null
&& ((host != null && host.length() > 0) || port != -1)) {
if (host == null)
host = "";
authority = (port == -1) ? host : host + ":" + port;
}
tempState = new UrlDeserializedState(protocol, host, port, authority,
file, ref, hashCode);
}
/**
* 使用URL对象替换反序列化的对象。
*
* @return 从反序列化状态创建的新对象。
*
* @throws ObjectStreamException 如果无法创建代替此对象的新对象
*/
private Object readResolve() throws ObjectStreamException {
URLStreamHandler handler = null;
// 已在readObject中检查过
handler = getURLStreamHandler(tempState.getProtocol());
URL replacementURL = null;
if (isBuiltinStreamHandler(handler.getClass().getName())) {
replacementURL = fabricateNewURL();
} else {
replacementURL = setDeserializedFields(handler);
}
return replacementURL;
}
private URL setDeserializedFields(URLStreamHandler handler) {
URL replacementURL;
String userInfo = null;
String protocol = tempState.getProtocol();
String host = tempState.getHost();
int port = tempState.getPort();
String authority = tempState.getAuthority();
String file = tempState.getFile();
String ref = tempState.getRef();
int hashCode = tempState.getHashCode();
// 构造授权部分
if (authority == null
&& ((host != null && host.length() > 0) || port != -1)) {
if (host == null)
host = "";
authority = (port == -1) ? host : host + ":" + port;
// 处理具有userInfo的主机
int at = host.lastIndexOf('@');
if (at != -1) {
userInfo = host.substring(0, at);
host = host.substring(at+1);
}
} else if (authority != null) {
// 构造用户信息部分
int ind = authority.indexOf('@');
if (ind != -1)
userInfo = authority.substring(0, ind);
}
// 构造路径和查询部分
String path = null;
String query = null;
if (file != null) {
// 修复:仅在层次结构时才这样做?
int q = file.lastIndexOf('?');
if (q != -1) {
query = file.substring(q+1);
path = file.substring(0, q);
} else
path = file;
}
// 设置对象字段。
this.protocol = protocol;
this.host = host;
this.port = port;
this.file = file;
this.authority = authority;
this.ref = ref;
this.hashCode = hashCode;
this.handler = handler;
this.query = query;
this.path = path;
this.userInfo = userInfo;
replacementURL = this;
return replacementURL;
}
private URL fabricateNewURL()
throws InvalidObjectException {
// 从反序列化的对象创建URL字符串
URL replacementURL = null;
String urlString = tempState.reconstituteUrlString();
try {
replacementURL = new URL(urlString);
} catch (MalformedURLException mEx) {
resetState();
InvalidObjectException invoEx = new InvalidObjectException(
"Malformed URL: " + urlString);
invoEx.initCause(mEx);
throw invoEx;
}
replacementURL.setSerializedHashCode(tempState.getHashCode());
resetState();
return replacementURL;
}
boolean isBuiltinStreamHandler(URLStreamHandler handler) {
Class<?> handlerClass = handler.getClass();
return isBuiltinStreamHandler(handlerClass.getName())
|| (handlerClass.getClassLoader() == null);
}
private boolean isBuiltinStreamHandler(String handlerClassName) {
return (handlerClassName.startsWith(BUILTIN_HANDLERS_PREFIX));
}
private void resetState() {
this.protocol = null;
this.host = null;
this.port = -1;
this.file = null;
this.authority = null;
this.ref = null;
this.hashCode = -1;
this.handler = null;
this.query = null;
this.path = null;
this.userInfo = null;
this.tempState = null;
}
private void setSerializedHashCode(int hc) {
this.hashCode = hc;
}
}
class Parts {
String path, query, ref;
Parts(String file) {
int ind = file.indexOf('#');
ref = ind < 0 ? null: file.substring(ind + 1);
file = ind < 0 ? file: file.substring(0, ind);
int q = file.lastIndexOf('?');
if (q != -1) {
query = file.substring(q+1);
path = file.substring(0, q);
} else {
path = file;
}
}
String getPath() {
return path;
}
String getQuery() {
return query;
}
String getRef() {
return ref;
}
}
final class UrlDeserializedState {
private final String protocol;
private final String host;
private final int port;
private final String authority;
private final String file;
private final String ref;
private final int hashCode;
public UrlDeserializedState(String protocol,
String host, int port,
String authority, String file,
String ref, int hashCode) {
this.protocol = protocol;
this.host = host;
this.port = port;
this.authority = authority;
this.file = file;
this.ref = ref;
this.hashCode = hashCode;
}
String getProtocol() {
return protocol;
}
String getHost() {
return host;
}
String getAuthority () {
return authority;
}
int getPort() {
return port;
}
String getFile () {
return file;
}
String getRef () {
return ref;
}
int getHashCode () {
return hashCode;
}
String reconstituteUrlString() {
// 预先计算StringBuilder的长度
int len = protocol.length() + 1;
if (authority != null && authority.length() > 0)
len += 2 + authority.length();
if (file != null) {
len += file.length();
}
if (ref != null)
len += 1 + ref.length();
StringBuilder result = new StringBuilder(len);
result.append(protocol);
result.append(":");
if (authority != null && authority.length() > 0) {
result.append("//");
result.append(authority);
}
if (file != null) {
result.append(file);
}
if (ref != null) {
result.append("#");
result.append(ref);
}
return result.toString();
}
}
ate final String protocol;
private final String host;
private final int port;
private final String authority;
private final String file;
private final String ref;
private final int hashCode;
public UrlDeserializedState(String protocol,
String host, int port,
String authority, String file,
String ref, int hashCode) {
this.protocol = protocol;
this.host = host;
this.port = port;
this.authority = authority;
this.file = file;
this.ref = ref;
this.hashCode = hashCode;
}
String getProtocol() {
return protocol;
}
String getHost() {
return host;
}
String getAuthority () {
return authority;
}
int getPort() {
return port;
}
String getFile () {
return file;
}
String getRef () {
return ref;
}
int getHashCode () {
return hashCode;
}
String reconstituteUrlString() {
// 预先计算StringBuilder的长度
int len = protocol.length() + 1;
if (authority != null && authority.length() > 0)
len += 2 + authority.length();
if (file != null) {
len += file.length();
}
if (ref != null)
len += 1 + ref.length();
StringBuilder result = new StringBuilder(len);
result.append(protocol);
result.append(":");
if (authority != null && authority.length() > 0) {
result.append("//");
result.append(authority);
}
if (file != null) {
result.append(file);
}
if (ref != null) {
result.append("#");
result.append(ref);
}
return result.toString();
}
}