文章目录
- 1. 简介
- 2. 创建URL
- 3. 从URL获取数据
- 4. 分解URL
- 5. 相等性和比较
1. 简介
java.net.URL类是对统一资源定位符的抽象。它扩展了Object类,是一个final类,不能对其派生子类。它不依赖于继承来配置不同的URL实例,而使用了策略设计模式,协议处理器就是策略,URL类构成上下文,通过它来选择不同的策略。URL将URL作为对象考虑,这个对象的字段包括模式(也就是协议)、主机名、端口、路径、查询字符串和片段标识符(ref),每个字段都可以单独设置。URL对象由于有final修饰,所以它是不可变的,这样就保证了线程安全。
策略设计模式
:策略设计模式(Strategy Design Pattern)是一种行为设计模式,它允许在运行时选择算法或行为的策略,而无需在使用策略的类中直接实现该算法或行为。
原理
:
- 定义策略接口:首先,需要定义一个公共的策略接口(或抽象类),该接口声明了策略类(或具体策略类)必须实现的方法。
- 实现具体策略类:针对不同的算法或行为,实现具体的策略类,它们分别实现了策略接口中定义的方法。
- 使用策略:在使用策略的类中,定义一个策略对象的引用,并提供一个方法来设置或切换具体的策略对象。该方法将在运行时接受一个策略对象作为参数,并将其赋值给策略对象的引用。
- 调用策略:在需要使用特定算法或行为的地方,通过策略对象的引用调用相应的方法。
优点
:策略设计模式提供了一种灵活的方式,使得算法或行为能够在运行时动态选择或切换,而不需要修改使用策略的类。
- 它遵循开闭原则,允许在不修改现有代码的情况下添加新的策略类。
- 策略类之间相互独立,易于理解、扩展和维护。
适用场景
:
- 当一个系统需要在不同情况下采用不同的算法或行为时,可以使用策略模式来避免使用大量的条件语句。
- 当一个类具有多种相关的行为,并且这些行为在不同的情况下会发生变化,可以使用策略模式来封装这些变化的部分,使得每种行为都可以独立演化,易于修改和扩展。
总结
:策略设计模式通过将算法或行为封装成独立的策略类,并通过在使用策略的类中持有策略对象的引用,实现了动态选择或切换策略的能力。它提供了一种灵活的方式来管理不同的算法或行为,并遵循了开闭原则,使得系统易于扩展和维护。
2. 创建URL
常见的构成函数如下
public URL(String url) throws MalformedURLeException
public URL(String protocol ,String hostname, String file)throws MalformedURLeException
public URL(String protocol, String host, int port ,String file)throws MalformedURLeException
public URL(URL base, String relative)throws MalformedURLeException
使用哪种构造函数取决于提供了哪些信息,支持的协议可能不同的虚拟机有着不同的协议支持。Java除了验证URL模式外,不会对构造函数构造的URL进行任何正确性检查,例如不会检查主机名是否包括空格等,所以确保URL是合法的工作还是要交给程序员。
- 案例:测试JVM虚拟机支持哪些协议
public class Main {
public static void main(String[] args) {
//超文本传输协议
testProtocol("http://www.baidu.com");
//安全http
testProtocol("http://www.amazon.com/exec/obidos/order2/");
//文件传输协议
testProtocol("ftp://ibiblio.org/pub/languages/java/javafaq");
//简单邮件传输协议
testProtocol("mailto:elharo@ibiblio.org");
//telnet
testProtocol("telnet://dibner.poly.edu/");
//本地文件访问
testProtocol("file:///etc/passwd");
//gopher
testProtocol("gopher://gopher.anc.org.za/");
//轻量组目录访问协议
testProtocol("ldap://ldap.itd.unich.edu/o=University%20of%20Michigan,c=US?postalAddress");
//jar
testProtocol("jar:http://cafeaulait.org/books/javaio/ioexamples/javaio.jar");
//NFS,网络文件系统
testProtocol("nfs://utopia.poly.edu/usr/tmp/");
//JDBC定制协议
testProtocol("rmi://ibiblio.org/RenderEngine");
//rmi,远程方法调用协议
testProtocol("rmi://ibiblio.org/RenderEngine");
//HOTJava的定制协议
testProtocol("doc://UsersGuide/release.html");
testProtocol("netdoc:/UsersGuide/release.html");
testProtocol("systemresource://www.adc.org/+/index.html");
testProtocol("verbatim:http://www.adc.org");
}
private static void testProtocol(String url){
try{
URL u=new URL(url);
System.out.println(u.getProtocol()+"is supported");
}catch(MalformedURLException ex){
String protocol=url.substring(0,url.indexOf(':'));
System.out.println(protocol+"is not supported");
}
}
}
- 其它URL来源
除了使用上面介绍的构造函数来获取指定的URL,还有其它方法同样也可以获得URL
- 在applet中,
getDocumetBase()
会返回包含这个applet的页面的URL,getCodeBase()
会返回applet.class文件的URLjava.io.File
类有一个toURL()
方法,它返回指定文件的file的URL,由于不同的操作系统的文件系统是不同的,所以可能获得URL的类型不同- 类加载器不仅用于加载类,也能加载资源,如图片和视频,静态方法
ClassLoader.getSystemResource(String name)
返回一个URL,通过它可以获取一个资源。ClassLoader.getSystemResources(String name)
返回一个Enumeration,其中包含一个URL列表,通过这些URL可以读取指定的资源。实例方法getResource()
会所引用类加载器使用的路径中搜索指定的资源的URL
3. 从URL获取数据
获取了URL,我们就可以获得URL所指向的文档中包含的数据。常用的方法有:
public InputStream openStream()throws IOException
public URLconnection openConnection()throws IOException
public URLconnection openConnection()throws IOException
public Object getContent() throws IOException
public Object getContent(Class[] classes)throws IOException
public final InputStream openStream() throws IOException
该方法连接到URL所引用的资源,在客户端和服务器之间完成必要的握手(TCP),返回一个InputStream,可以由此读取数据。从这个InputStream获得的是引用的原始内容(未解释过的内容)。
public class Main {
public static void main(String[] args) {
try{
URL u=new URL("http://www.baidu.com");
InputStream in=u.openStream();
int c;
while((c=in.read())!=-1) System.out.println(c);
in.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
案例:下载一个Web页面
public class Main {
public static void main(String[] args) {
InputStream in=null;
try{
//打开URL进行读取
URL u=new URL("http://www.baidu.com");
in=u.openStream();
//缓冲输入以提高性能
in=new BufferedInputStream(in);
//将InputStream串联到一个Reader
Reader r=new InputStreamReader(in);
int c;
while((c=in.read())!=-1) System.out.print((char)c);
}catch(IOException e){
e.printStackTrace();
}finally{
if(in!=null){
try{
in.close();
}catch(IOException e){
}
}
}
}
}
public URLConnection openConnection() throws IOException
该方法为指定的URL打开一个Socket,并返回一个URLConnetcion对象。URLConnection表示一个网络资源打开的连接。如果调用失败会抛出IOException异常。
public class Main {
public static void main(String[] args) {
InputStream in=null;
try{
//打开URL进行读取
URL u=new URL("http://www.baidu.com");
URLConnection uc=u.openConnection();
in=uc.getInputStream();
int c=0;
while((c=in.read())!=-1)System.out.print((char)c);
}catch(IOException e){
e.printStackTrace();
}finally{
if(in!=null){
try{
in.close();
}catch(IOException e){
}
}
}
}
}
如果希望和服务器直接通信,应对使用这个方法,通过URLConection,你可以访问服务器发送的所有数据:除了原始的文档本身,还可以访问这个协议指定的所有元数据。
public final Object getContent() throws IOException
getContent()方法是下载URL引用数据的第三种方法,该方法获取由URL引用的数据,尝试由它建立某种类型的对象。如果URL指示某种文本,返回InputStream。如果指示一个图像,返回一个java.awt.ImageProducer。这两个不同的类有一个共同点,它们本身并不是数据对象,而是一种途径,程序可以利用它们构造数据对象。该方法的做法是,在从服务器获得的数据首部中查找Content-type字段。如果服务器没有Mime首部,或者是一个不熟悉的Content-type,该方法会返回某种InputStream,可以通过它读取数据。这里可以看出该方法的缺点:很难预测将获得哪种对象,可能得到的InputStream或ImageProducer,需要用instanceof操作符检查。
public class Main {
public static void main(String[] args) {
InputStream in=null;
try{
//打开URL进行读取
URL u=new URL("https://cn.bing.com/images/search?q=%E5%9B%BE%E7%89%87&FORM=IQFRBA&id=4929EB0212CFAC8CB6AB59DB53A9D2D99C54FF6A");
Object o=u.getContent();
System.out.println(o.getClass().getName());
}catch(IOException e){
e.printStackTrace();
}finally{
if(in!=null){
try{
in.close();
}catch(IOException e){
}
}
}
}
}
public final Object getContent(Class[] classes) throws IOException
URL的内容处理器可以提供一个资源的不同视图。getContent()方法的这个重载版本允许你选择希望将内容作为哪个类返回。这个方法尝试以第一种可用的格式返回URL的内容。
public class Main {
public static void main(String[] args) {
InputStream in=null;
try{
//打开URL进行读取
URL u=new URL("https://cn.bing.com/images/search?q=%E5%9B%BE%E7%89%87&FORM=IQFRBA&id=4929EB0212CFAC8CB6AB59DB53A9D2D99C54FF6A");
Class<?>[] types=new Class[3];
types[0]=String.class;
types[1]=Reader.class;
types[2]=InputStream.class;
Object o=u.getContent(types);
if(o instanceof Reader){
System.out.println("1");
}else if(o instanceof InputStream){
System.out.println("2");
}else{
System.out.println("3");
}
}catch(IOException e){
e.printStackTrace();
}finally{
if(in!=null){
try{
in.close();
}catch(IOException e){
}
}
}
}
}
4. 分解URL
URL由一下5部分组成
- 模式,也称为协议
- 授权机构
- 路径
- 片段标识符,也称为段或Ref
- 查询字符串
如http://www.ibiblio.org/javafaq/books/jnp/index.html?isbn=1565922069#toc
- 模式是
http
- 授权机构是
www.ibiblio.org
授权机构又包括:用户信息、主机和端口
- 路径是
/javafaq/books/jnp/index.html
- 片段标识符
isbn=1565922069
对应的方法如下:
- getProtocol():获得URL的模式
- getHost():获得主机名
- getPort():获得授权机构中的端口
- getDefaultPort():返回URL协议对应的默认端口
- getFile():获得一个包含URL的路径的String
- getPath():几乎和getFile()一样,返回一个包含URL的路径和文件部分,但不包括查询字符串
- getRef():返回URL的片段标识符(#后面的)
- getQuery():获得URL的查询字符串
- getUserInfo():获得用户信息
- getAuthority():获得授权机构
public class Main {
public static void main(String[] args) {
String myurl="http://mp3:mp3@www.baidu.com:8080/java/myfile/java.html?nihao=2#1231313";
try{
URL u=new URL(myurl);
System.out.println("The URL is "+ u);
System.out.println("The scheme is "+ u.getProtocol());
System.out.println("The user info is "+ u.getUserInfo());
String host=u.getHost();
if(host !=null){
int atSign=host.indexOf('@');
if(atSign!=-1) host=host.substring(atSign+1);
System.out.println("The host is "+ host);
}else{
System.out.println("The host is null");
}
System.out.println("The port is "+u.getPort());
System.out.println("The path is "+u.getPath());
System.out.println("The ref is" + u.getRef());
System.out.println("The query string is "+ u.getQuery());
}catch(MalformedURLException e){
System.err.println(myurl+ "is not a URL I understand");
}
}
}
5. 相等性和比较
- 相等性
URL类包含通常的equals()和hashcode()方法,当且仅当两个URL都指向相同主机、端口和路径上的相同资源,而且有相同的片段标识符和查询字符串,才任务这两个URL是相等的。实际上equals()方法会尝试用DNS解析主机,来判断所指向的资源是否相同。URL类还有一个sameFile()方法,可以检查两个URL指向相同的资源,这个比较和equals()基本相同,但是sameFike()不会考虑片段标识符。
public class Main {
public static void main(String[] args) {
String myurl1="http://mp3:mp3@www.baidu.com:8080/java/myfile/java.html?nihao=2#1231313";
String myurl2="http://mp3:mp3@www.baidu.com:8080/java/myfile/java.html?nihao=2";
try{
URL u1=new URL(myurl1);
URL u2=new URL(myurl2);
System.out.println("equals方法:"+u1.equals(u2));
System.out.println("sameFile方法:"+u1.sameFile(u2));
}catch(MalformedURLException e){
System.err.println( "is not a URL I understand");
}
}
}
- 比较
URL有三个方法可以将一个实例转换为另一种形式,分别toString()、toExternalForm()和toURI()。
toString()
:与所有的类一样,java.net.URL有一个toString方法,会生成绝对URL的字符串。
public class Main {
public static void main(String[] args) {
String myurl1="http://mp3:mp3@www.baidu.com:8080/java/myfile/java.html?nihao=2#1231313";
try{
URL u1=new URL(myurl1);
System.out.println(u1.toString());
}catch(MalformedURLException e){
System.err.println( "is not a URL I understand");
}
}
}
toExternalForm()
:该方法将一个URL对象转换为一个字符串,可以在HTML链接和Web浏览器的打开URL对话框中使用。(toString的底层就是返回toExternalForm方法)
public class Main {
public static void main(String[] args) {
String myurl1="http://mp3:mp3@www.baidu.com:8080/java/myfile/java.html?nihao=2#1231313";
try{
URL u1=new URL(myurl1);
System.out.println(u1.toExternalForm());
}catch(MalformedURLException e){
System.err.println( "is not a URL I understand");
}
}
}
toURI()
:该方法可以将URL对象转换为URI对象(可以在同专栏下查看我的URI文章)
public class Main {
public static void main(String[] args) {
String myurl1="http://mp3:mp3@www.baidu.com:8080/java/myfile/java.html?nihao=2#1231313";
try{
URL u1=new URL(myurl1);
System.out.println(u1.toURI());
}catch(MalformedURLException e){
System.err.println( "is not a URL I understand");
}catch(URISyntaxException e){
e.printStackTrace();
}
}
}