- 浏览: 618188 次
- 性别:
- 来自: 太原
文章分类
最新评论
-
zhoudatou:
同求,主要是 org.pdfbox.cos.COSDocume ...
关于lucene处理word、excel、ppt、txt、pdf等格式文件 -
u013858641:
同求楼主的demo,急需。非常感谢。425723818@qq ...
关于lucene处理word、excel、ppt、txt、pdf等格式文件 -
myxiaoribeng:
代码能不能格式化一下,看都不想看下去。
和JSF一起使用JSTL和其它的JSP定制标记 -
guji528:
写的很详细
JSF导航文件如何设置实例解析 -
shishouxing:
请问我用的是oracle instantclient pack ...
PL/SQL连接远程数据库
类URLEncoder 和 类URLDecoder
web设计者面临的众多难题之一便是怎样处理不同操作系统间的差异性。这些差异性能引起URL方面的问题:例如,一些操作系统允许文件名中含有空格符,有些又不允许。大多数操作系统不会认为文件名中含有符号“#”会有什么特殊含义;但是在一个URL中,符号“#”表示该文件名已经结束,后面会紧跟一个fragment(部分)标识符。其他的特殊字符,非字母数字字符集,它们在URL或另一个操作系统上都有其特殊的含义,表述着相似的问题。为了解决这些问题,我们在URL中使用的字符就必须是一个ASCII字符集的固定字集中的元素,具体如下:
1.大写字母A-Z
2.小写字母a-z
3.数字 0-9
4.标点符 - _ . ! ~ * ' (和 ,)
诸如字符: / & ? @ # ; $ + = 和 %也可以被使用,但是它们各有其特殊的用途,如果一个文件名包括了这些字符( / & ? @ # ; $ + = %),这些字符和所有其他字符就应该被编码。
编码过程非常简单,任何字符只要不是ASCII码数字,字母,或者前面提到的标点符,它们都将被转换成字节形式,每个字节都写成这种形式:一个“%”
后面跟着两位16进制的数值。空格是一个特殊情况,因为它们太平常了。它除了被编码成“%20”以外,还能编码为一个“+”。加号(+)本身被编码为%2B。当/ # = & 和?作为名字的一部分来使用时,而不是作为URL部分之间的分隔符来使用时,它们都应该被编码。
WARNING这种策略在存在大量字符集的异构环境中效果不甚理想。例如:在U.S. Windows 系统中, é 被编码为 %E9. 在 U.S. Mac中被编码为%8E。这种不确定性的存在是现存的URI的一个明显的不足。所以在将来URI的规范当中应该通过国际资源标识符(IRIs)进行改善。
类URL并不自动执行编码或解码工作。你能生成一个URL对象,它可以包括非法的ASCII和非ASCII字符和/或%xx。当用方法getPath() 和toExternalForm( ) 作为输出方法时,这种字符和转移符不会自动编码或解码。你应对被用来生成一个URL对象的字符串繁荣对象负责,确保所有字符都会被恰当地编码。
幸运的是,java提供了一个类URLEncoder把string编码成这种形式。Java1.2增加了一个类URLDecoder它能以这种形式解码string。这两个类都不用初始化
public class URLDecoder extends Object
public class URLEncoder extends Object
URLEncoder
在java1.3和早期版本中,类java.net.URLEncoder包括一个简单的静态方法encode( ) 它对string以如下规则进行编码:
public static String encode(String s)
这个方法总是用它所在平台的默认编码形式,所以在不同系统上,它就会产生不同的结果。结果java1.4中,这个方法被另一种方法取代了。该方法要求你自己指定编码形式:
public static String encode(String s, String encoding)
throws UnsupportedEncodingException
两种关于编码的方法,都把任何非字母数字字符转换成%xx(除了空格,下划线(_),连字符(—),句号(。),和星号(*))。
两者也都编码所以的非ASCII字符。空格被转换成一个加号。这些方法有一点过分累赘了;它们—也把“~”,“‘”,“()”转换成%xx,即使它们完全用不着这样做。尽管这样,但是这种转换并没被URL规范所禁止。所以web浏览器会自然地处理这些被过分编码后的URL。
两中关于编码的方法都返回一个新的被编码后的string,java1.3的方法encode( ) 使用了平台的默认编码形式,得到%xx。这些编码形式典型的有:在 U.S. Unix 系统上的ISO-8859-1, 在U.S. Windows 系统上的Cp1252,在U.S. Macs上的MacRoman,和其他本地字符集等。因为编码解码过程都是与本地操作平台相关的,所以这些方法是另人不爽的,不能跨平台的。这就明确地回答了为什么在java1.4中这种方法被抛弃了,转而投向了要求以自己指定编码形式的这种方法。尽管如此,如果你执意要使用所在平台的默认编码形式,你的程序将会像在java1.3中的程序一样,是本地平台相关的。在另一种编码的方法中,你应该总是事业UTF-8,而不是其他什么。UTF-8比起你选的其他的编码形式来说,它能与新的web浏览器和更多的其他软件相兼容。
例子7-8是使用URLEncoder.encode( ) 来打印输出各种被编码后的string。它需要在java1.4或更新的版本中编译和运行。
Example 7-8. x-www-form-urlencoded strings
import java.net.URLEncoder;
import java.io.UnsupportedEncodingException;
public class EncoderTest {
public static void main(String[] args) {
try {
System.out.println(URLEncoder.encode("This string has spaces",
"UTF-8"));
System.out.println(URLEncoder.encode("This*string*has*asterisks",
"UTF-8"));
System.out.println(URLEncoder.encode("This%string%has%percent%signs",
"UTF-8"));
System.out.println(URLEncoder.encode("This+string+has+pluses",
"UTF-8"));
System.out.println(URLEncoder.encode("This/string/has/slashes",
"UTF-8"));
System.out.println(URLEncoder.encode("This\"string\"has\"quote\"marks",
"UTF-8"));
System.out.println(URLEncoder.encode("This:string:has:colons",
"UTF-8"));
System.out.println(URLEncoder.encode("This~string~has~tildes",
"UTF-8"));
System.out.println(URLEncoder.encode("This(string)has(parentheses)",
"UTF-8"));
System.out.println(URLEncoder.encode("This.string.has.periods",
"UTF-8"));
System.out.println(URLEncoder.encode("This=string=has=equals=signs",
"UTF-8"));
System.out.println(URLEncoder.encode("This&string&has&ersands",
"UTF-8"));
System.out.println(URLEncoder.encode("Thiséstringéhasé
non-ASCII characters", "UTF-8"));
}
catch (UnsupportedEncodingException ex) {
throw new RuntimeException("Broken VM does not support UTF-8");
}
}
}
下面就是它的输出。需要注意的是这些代码应该以其他编码形式被保存而不是以ASCII码的形式,还有就是你选择的编码形式应该作为一个参数传给编译器,让编译器能据此对源代码中的非ASCII字符作出正确的解释。
% javac -encoding UTF8 EncoderTest
% java EncoderTest
This+string+has+spaces
This*string*has*asterisks
This%25string%25has%25percent%25signs
This%2Bstring%2Bhas%2Bpluses
This%2Fstring%2Fhas%2Fslashes
This%22string%22has%22quote%22marks
This%3Astring%3Ahas%3Acolons
This%7Estring%7Ehas%7Etildes
This%28string%29has%28parentheses%29
This.string.has.periods
This%3Dstring%3Dhas%3Dequals%3Dsigns
This%26string%26has%26ampersands
This%C3%A9string%C3%A9has%C3%A9non-ASCII+characters
特别需要注意的是这个方法编码了符号,“\”
,&,=,和:。它不会尝试着去规定在一个URL中这些字符怎样被使用。由此,所以你不得不分块编译你的URL,而不是把整个URL一次传给这个方法。这是很重要的,因为对类URLEncoder最通常的用法就是查询string,为了和服务器端使用GET方法的程序进行交互。例如,假设你想编码这个查询sting,它用来搜索AltaVista网站:
pg=q&kl=XX&stype=stext&q=+"Java+I/O"&search.x=38&search.y=3
这段代码对其进行编码:
String query = URLEncoder.encode(
"pg=q&kl=XX&stype=stext&q=+\"Java+I/O\"&search.x=38&search.y=3");
System.out.println(query);
不幸的是,得到的输出是:
pg%3Dq%26kl%3DXX%26stype%3Dstext%26q%3D%2B%22Java%2BI%2FO%22%26search
.x%3D38%26search.y%3D3
出现这个问题就是方法URLEncoder.encode( ) 在进行盲目地编码。它不能区分在URL或者查询string中被用到的特殊字符(象前面string中的“=”,和“&”)和确实需要被编码的字符。由此,所以URL需要像下面这样一次只编码一块:
String query = URLEncoder.encode("pg");
query += "=";
query += URLEncoder.encode("q");
query += "&";
query += URLEncoder.encode("kl");
query += "=";
query += URLEncoder.encode("XX");
query += "&";
query += URLEncoder.encode("stype");
query += "=";
query += URLEncoder.encode("stext");
query += "&";
query += URLEncoder.encode("q");
query += "=";
query += URLEncoder.encode("\"Java I/O\"");
query += "&";
query += URLEncoder.encode("search.x");
query += "=";
query += URLEncoder.encode("38");
query += "&";
query += URLEncoder.encode("search.y");
query += "=";
query += URLEncoder.encode("3");
System.out.println(query);
这才是你真正想得到的输出:
pg=q&kl=XX&stype=stext&q=%2B%22Java+I%2FO%22&search.x=38&search.y=3
例子7-9是一个QueryString 类。在一个java对象中,它使用了类URLEncoder 来编码连续的属性名和属性值对,这个java对象全被用来发送数据到服务器端的程序。当你在创建一个QueryString 对象时,你可以把查询string中的第一个属性对传递给类QueryString 的构造函数,得到初始string 。如果要继续加入后面的属性对,就应调用方法add(),它也能接受两个string作为参数,能对它们进行编码。方法getQuery( ) 返回一个属性对被逐个编码后得到的整个string。
Example 7-9. -The QueryString class
package com.macfaq.net;
import java.net.URLEncoder;
import java.io.UnsupportedEncodingException;
public class QueryString {
private StringBuffer query = new StringBuffer( );
public QueryString(String name, String value) {
encode(name, value);
}
public synchronized void add(String name, String value) {
query.append('&');
encode(name, value);
}
private synchronized void encode(String name, String value) {
try {
query.append(URLEncoder.encode(name, "UTF-8"));
query.append('=');
query.append(URLEncoder.encode(value, "UTF-8"));
}
catch (UnsupportedEncodingException ex) {
throw new RuntimeException("Broken VM does not support UTF-8");
}
}
public String getQuery( ) {
return query.toString( );
}
public String toString( ) {
return getQuery( );
}
}
利用这个类,现在我们就能对前面那个例子中的string进行编码了:
QueryString qs = new QueryString("pg", "q");
qs.add("kl", "XX");
qs.add("stype", "stext");
qs.add("q", "+\"Java I/O\"");
qs.add("search.x", "38");
qs.add("search.y", "3");
String url = "http://www.altavista.com/cgi-bin/query?" + qs;
System.out.println(url);
URLDecoder
与URLEncoder 类相对应的URLDecoder 类有两种静态方法。它们解码以x-www-form-url-encoded这种形式编码的string。也就是说,它们把所有的加号(+)转换成空格符,把所有的%xx分别转换成与之相对应的字符:
public static String decode(String s) throws Exception
public static String decode(String s, String encoding)// Java 1.4
throws UnsupportedEncodingException
第一种解码方法在java1.3和java1.2中使用。第二种解码方法在java1.4和更新的版本中使用。如果你拿不定主意用哪种编码方式,那就选择UTF-8吧。它比其他任何的编码形式更有可能得到正确的结果。
如果string包含了一个“%”,但紧跟其后的不是两位16进制的数或者被解码成非法序列,该方法就会抛出IllegalArgumentException 异常。当下次再出现这种情况时,它可能就不会被抛出了。这是与运行环境相关的,当检查到有非法序列抛不抛出IllegalArgumentException 异常,这时到底会发生什么是不确定的。在Sun's JDK 1.4中,不会抛出什么异常,它会把一些莫名其妙的字节加进不能被顺利编码的string中。这的确令人头疼,可能就是一个安全漏洞。
由于这个方法没有触及到非转义字符,所以你可以把整个URL作为参数传给该方法,不用像之前那样分块进行。例如:
String input = "http://www.altavista.com/cgi-bin/" +
"query?pg=q&kl=XX&stype=stext&q=%2B%22Java+I%2FO%22&search.x=38&search.y=3";
try {
String output = URLDecoder.decode(input, "UTF-8");
System.out.println(output);
}
The URI Class
URI是URL的一个抽象,它不仅包括了统一资源定位符(URL),还包括了统一资源名(URN).大多数实际应用中使用的URI都是URL,但是许多规范和标准像XML都是用URI来定义的.在java1.4和更新的版本中, URI被java.net.URI 类所表示.这个类与java.net.URL 相比有如下3点重要的区别:
·URI 类只关心资源的标识和对URI的解析.它没有方法来检索它的URI所标识的资源。
·URI 类与URL 类相比,它更能适应相关的规范。
·一个URI 对象能表示一个相对URI 。URL 类在存放之前,就已经对所有的URI进行了“绝对化”的处理。
简而言之,一个URL 对象就是网络应用层协议进行网络检索的一个代理,而一个URI 对象就只纯粹地做string的解析和操作的工作。URI 类没有进行网络检索的能力。URL 类有一些进行string解析的方法。比如getFile( ) 和 getRef( ) 方法,但很多都是蹩脚的方法,总是不完全像有关的规范上所说的那样好用。假如你现在用的是java1.4版本或更新的版本,这时你就可以做出选择,如果你想下载一个URL指示的内容时,你应该使用 URL类;如果你想使用URI类来进行标识的工作而不是用来检索的时候, 你应该用URI类。例如,去标识一个XML namespace 的URI。在一些情况下,当你同时需要实现这两种功能时,你可以用方法toURL( ) 把一个URI 转换成一个 URL 。在java1.5中,你还能用类URL 中的方法toURI( )把一个URL 转换成一个URI 。
构造一个 URI
URI是由string构成的。不像类URL ,类URI
不依靠底层协议的处理程序。只要URI是语义上正确的,java就不需要知道它的什么协议,然后才创建其代表的URI对象。因此,不象类URL ,类URI 可以被用到新的试验性的URI协议中去。
public URI(String uri) throws URISyntaxException
这是一个简单的构造函数,它用一个合适的string创建了一个新的URI 对象。例如:
URI voice = new URI("tel:+1-800-9988-9938");
URI web = new URI("http://www.xml.com/pub/a/2003/09/17/stax.html#id=_hbc");
URI book= new URI("urn:isbn:1-565-92870-9");
如果参数string不符合URI的语法规则—比如,如果它以“:”开始—构造函数就会抛出URISyntaxException 异常。这是一个需要被检察的异常,所以你要么捕获该异常,要么在构造函数会被执行的方法中,声明这个方法会抛出该异常。尽管如此,但是有一条语法规则是不能被检察到的。与URI规范相矛盾的是,在URI中使用的字符不会被限制在ASCII字符集上。它们可以包含其它的Unicode字符,比如ø 和 é。从语义上讲,对URI几乎就没有什么限制,特别是一旦不需要对非ASCII字符进行编码时,一切URI都是被允许的。几乎任何的string都可以被看成是URI。
public URI(String scheme, String schemeSpecificPart, String fragment) throws URISyntaxException
这个构造函数主要是用在不存在有层次关系的URI中。Scheme是指URI的协议,比如http, urn, tel等。它必须专门有ASCII字母,数字和标点符(+, -, 和.)组成。第一个位置上必须是一个字母。如果省略了参数scheme,就用null代替。这时创建的是一个相对URI。例如:
URI absolute = new URI("http", "//www.ibiblio.org" , null);
URI relative = new URI(null, "/javafaq/index.shtml", "today");
scheme-specific部分的内容遵从于参数scheme给出的URI协议;对一个http 协议的URL来说,是一种形式,对于mail协议的 URL来说又是另一种形式,对于tel协议的 URI又是其它的什么形式。因为类URI 会以%xx对非法字符进行编码,所以显而易见,你不会在这步犯语法方面的错误。最后,只要有需要的话,第三个参数可以是指一个部分标识符。同样地,在这个部分标识符中,字符的合法性也顺理成章地不需要检察。如果省略了参数fragment,就用null代替。
public URI(String scheme, String host, String path, String fragment) throws URISyntaxException
这个构造函数被用在有层次关系的URI中,比如http 和 ftp 协议的URL。主机名和路径名(用“/”分开)一起构成URI的scheme-specific部分。例如:
URI today= new URI("http", "www.ibiblio.org", "/javafaq/index.html", "today");
produces the URI http://www.ibiblio.org/javafaq/index.html#today
如果构造函数从得到的几个参数中不能获得一个有合法层次关系的URI—例如,假如给定了参数scheme,它要求这个URI是一个绝对的URI可是path参数却又不是一“/”开始的—它就会抛出异常URISyntaxException
Public URI(String scheme, String authority, String path, String query, String fragment) throws URISyntaxException
这个构造函数和前边所讲的那个基本相同,除了它增加了一个进行查询的string 参数。例如:
URI today= new URI("http", "www.ibiblio.org", "/javafaq/index.html",
"referrer=cnet&date=2004-08-23","today");
与通常一样,遇到任何的语法错误它都会抛出异常URISyntaxException ,如果其中的哪个参数被省略,就用null代替。
public URI(String scheme, String userInfo, String host, int port, String path, String query, String fragment) throws URISyntaxException
这是构造一个有层次关系的URI的父亲构造函数,也就是说,前面提到的两个构造函数都会调用它。它把认证信息(authority)分成了user info, host, 和 port几部分。它们中的每一个都具备其自身的语法规则。比如:
URI styles = new URI("ftp", "anonymous:elharo@metalab.unc.edu",
"ftp.oreilly.com",21, "/pub/stylesheet", null, null);
尽管这样,但是最后得到的URI仍然得遵从URI的一般规则。同样地,如果任何一个参数被省略,就用null代替。
public static URI create(String uri)
它不是个构造函数,而是个静态factory方法。不象构造函数那样,它不会抛出异常URISyntaxException 。如果你确信你的URI一定会是合法的,会遵从任一条规则,那么你就可以用该方法。比如,这次调用创建了一个用来匿名登陆一个FTP的URI ,它用一个邮箱地址当密码:
URI styles = URI.create(
"ftp://anonymous:elharo%40metalab.unc.edu@ftp.oreilly.com:
21/pub/stylesheet");
如果该URI确实存在问题,这个方法会抛出异常IllegalArgumentException 。它是在运行时才抛出的异常,所以你不能声明或捕获它。
The Parts of the URI
一个URI最多就三部分:scheme, scheme-specific , fragment identifier。一般形式:
scheme:scheme-specific-part:fragment
如果省略了scheme,这个URI就是相对的。如果fragment identifier被省略了,这个URI就很简单了。类URI有返回方法,它们能返回每个URI对象的这三部分。方法getRawFoo( ) 返回编码后的URI,与此类似的还有方法getFoo() ,它先解码%xx形式的字符,然后返回解码后的URI。
public String getScheme( )
public String getSchemeSpecificPart( )
public String getRawSchemeSpecificPart( )
public String getFragment( )
public String getRawFragment( )
注意: 这里没有方法getRawScheme( ),因为在URI规范中所有的协议名应专门由合法的ASCII字符组成。不允许%xx在协议名中出现。
如果URI 对象不具备相关的内容,这些方法都返回null:例如,对一个没指定协议的相对URI,或一个没有fragment identifier的http 协议的URI。一个指定了协议(scheme)的URI是一个绝对的URI。没有指定协议的URI是相对的URI。方法isAbsolute()返回true如果这个URI是绝对的,如果是相对的就返回false:
public boolean isAbsolute( )
scheme-specific在细节上的变化取决于scheme的类型。比如在一个tel URL中,scheme-specific需要用类似电话号码的语法。尽管如此,但是在许多有用的URI中,包括很常见的file 和 http URL里面,scheme-specific都有其独特的描述层次关系的模式,包括authority, path, query string。Authority又被细分为user info, host,port。方法isOpaque()返回false如果这是个有层次关系的URI,否则返回true—也就是说,如果是层次模糊的:
public boolean isOpaque( )
如果URI是层次模糊的,所有你能得到的就只是scheme, scheme-specific ,fragment identifier。尽管如此,但是如果URI是有层次关系的,下面的getter方法还是能获取该URI的所有内容:
public String getAuthority( )
public String getFragment( )
public String getHost( )
public String getPath( )
public String getPort( )
public String getQuery( )
public String getUserInfo( )
所有这些方法返回的都是解码后的内容;换句话说,这种形式%xx,例如%3c,都会被转换成它本身所表示的字符,比如符号“<”。如果你想得到未经解码的URI,这里有5个相并列的方法:
public String getRawAuthority( )
public String getRawFragment( )
public String getRawPath( )
public String getRawQuery( )
public String getRawUserInfo( )
记住类URI与URI规范是不同的。所以像é 和 ü 这种非ASCII字符绝不会在第一位置以%xx形式出现,所以在用方法getRawFoo() 返回得到的string中,仍将回出现在其中;除非本来被用做构建URI对象的string已被编码。
注意: 这里没有方法getRawPort( ) 和 getRawHost( ),因为这些部分总是被保证由ASCII字符构成的,至少现在是这样的。国际化的域名时代即将来临,希望在java以后的版本中能重新思考这一做法。
事实上,具体的URI不会包括这些信息—例如,这个URI,http://www.example.com 就没有user info, path, port, 或 query string—相应的方法就返回null。方法getPort( )是唯一例外。因为它被声明为返回一个int ,所以它不能返回null。它就用返回-1来代替,来表示没有port的信息。
由于各种技术上的也不会有多大实际影响的原因,java不会一开始就在认证部分做语法错误检察。这样做的一个很自然的后果就是不会返回认证的各个部分:port, host,user info。这种情况下,你可以调用parseServerAuthority()让认证(authority)被强制再解析:
public URI parseServerAuthority( )throws URISyntaxException
这个最初的URI 不会发生变化(URI对象是永远不会变的),但是URI 的返回值将会是authority被分开后的部分:user info, host,port。如果这个authority不能被解析,会抛出URISyntaxException 异常。
例子7-10使用这些方法把输入在命令行上的URI分成了它的那几个组成部分。这与例子7-4相似,只是它处理的是语法上正确的URI,而不要求JDK必须提供一个合适的protocal 处理器。
Example 7-10. The parts of a URI
import java.net.*;
public class URISplitter {
public static void main(String args[]) {
for (int i = 0; i < args.length; i++) {
try {
URI u = new URI(args[i]);
System.out.println("The URI is " + u);
if (u.isOpaque( )) {
System.out.println("This is an opaque URI.");
System.out.println("The scheme is " + u.getScheme( ));
System.out.println("The scheme specific part is "
+ u.getSchemeSpecificPart( ));
System.out.println("The fragment ID is " + u.getFragment( ));
}
else {
System.out.println("This is a hierarchical URI.");
System.out.println("The scheme is " + u.getScheme( ));
try {
u = u.parseServerAuthority( );
System.out.println("The host is " + u.getUserInfo( ));
System.out.println("The user info is " + u.getUserInfo( ));
System.out.println("The port is " + u.getPort( ));
}
catch (URISyntaxException ex) {
// Must be a registry based authority
System.out.println("The authority is " + u.getAuthority( ));
}
System.out.println("The path is " + u.getPath( ));
System.out.println("The query string is " + u.getQuery( ));
System.out.println("The fragment ID is " + u.getFragment( ));
} // end else
}// end try
catch (URISyntaxException ex) {
System.err.println(args[i] + " does not seem to be a URI.");
}
System.out.println( );
}// end for
}// end main
}// end URISplitter
下面就是运行这三个URI实例后的结果:
% java URISplitter tel:+1-800-9988-9938
\http://www.xml.com/pub/a/2003/09/17/stax.html#id=_hbc \urn:isbn:1-565-92870-9
The URI is tel:+1-800-9988-9938
This is an opaque URI.
The scheme is tel
The scheme specific part is +1-800-9988-9938
The fragment ID is null
The URI is http://www.xml.com/pub/a/2003/09/17/stax.html#id=_hbc
This is a hierarchical URI.
The scheme is http
The host is null
The user info is null
The port is -1
The path is /pub/a/2003/09/17/stax.html
The query string is null
The fragment ID is id=_hbc
The URI is urn:isbn:1-565-92870-9
This is an opaque URI.
The scheme is urn
The scheme specific part is isbn:1-565-92870-9
The fragment ID is null
处理相对的 URI
类URI 提供了三种方法进行相对的和绝对的URI之间的转换。
public URI resolve(URI uri)
它比较参数uri 和这个URI ,然后用它构成一个新的URI 对象,它会有一个绝对的URI。例如,思考这三行代码:
URI absolute = new URI("http://www.example.com/");
URI relative = new URI("images/logo.png");
URI resolved = absolute.resolve(relative);
在它们执行后,resolved 会有一个绝对的URI
http://www.example.com/images/logo.png.
如果这个被调用的URI 它本身就没有绝对的URI,那么方法resolve( )会尽量的处理这个URI,返回一个新的相对的URI对象。比如,下面这三个语句:
URI top = new URI("javafaq/books/");
URI relative = new URI("jnp3/examples/07/index.html");
URI resolved = top.resolve(relative);
在执行后,resolved 现在有一个相对的URI
javafaq/books/jnp3/examples/07/index.html
就没有scheme(协议)和authority(认证)
public URI resolve(String uri)
这是个简便的方法,它能方便地把参数string 转化成一个URI,然后与被调用的这个URI组合后,返回一个新的URI对象。也就是,它与resolve(newURI(str)) 是等价的。使用这个方法,前面的两个例子就可以重写如下:
URI absolute = new URI("http://www.example.com/");
URI resolved = absolute.resolve("images/logo.png");
URI top = new URI("javafaq/books/");
resolved = top.resolve("jnp3/examples/07/index.html");
public URI relativize(URI uri)
反向操作这个过程也可以;也就是,从一个绝对的URI到一个相对的URI。方法relativize( )用参数uri 创建一个新的URI 对象,这个uri 对被调用的URI 是相对的。参数没有改变。例如:
URI absolute = new URI("http://www.example.com/images/logo.png");
URI top = new URI("http://www.example.com/");
URI relative = top.relativize(absolute);
现在的 URI 对象包含了一个相对的 URI images/logo.png.
一些有用的方法
类URI 有不少普通的但很实用的方法:equals(), hashCode( ), toString( ), 和 compareTo( )
public boolean equals(Object o)
URI之间的比较与你期待中的一样好。它不是string之间的直接做比较。相同的URI必须要么是有层次关系的要么就是层次关系模糊的。Scheme和authority部分的比较是不区分大小写的。也就是http 和 HTTP 都表示的是相同的协议,authority 的比较www.example.com 和 www.EXAMPLE.com 也是一样的。URI中剩下的部分就得区分大小写了,除了用来转义非法字符的16进制的数字以外。在做比较前,%xx还没有被解码。
http://www.example.com/A and http://www.example.com/%41 are unequal URIs.
public int hashCode( )
方法hashCode( )是个普通的hashCode( )方法,没有什么特别的。相同的URI就会有相同的哈希码,不同的URI就很难拥有相同的哈希码了。
public int compareTo(Object o)
URI是可以排序的。排序是基于对各个部分字符比较的结果上的,以下面的顺序:
·如果scheme是不同的,就比较scheme,不考虑大小写。
·否则,如果scheme是相同的,我们 就认为层次关系模糊的URI比有层次关系的URI优先级高。
·如果比较的两个URI都是层次关系模糊的,这时就以它们的scheme-specific进行排序。
·如果scheme和层次关系模糊的scheme-specific都相同了,就比较URI的fragment
·如果做比较的URI都是有层次关系的,就看它们的authority,比较其中的user info, host, port。
·如果scheme和authority都相同了,就看它们的path。
·如果path也相同了,接着比较query string。
·如果query string也相同了,就比较fragment
URI除了和它们自身比较以外,不能和任何其他的类型做比较。把一个URI 和任何除了URI 以外的做比较会抛出异常ClassCastException
public String toString( )
方法toString( )返回一个URI 形式的未经编码的string。也就是说,像é 和 \这种字符就不会被转换成%xx,除非它们在用来生成URI 的string中已经被转换了。因此,调用这个方法后得到的结果不能保证它是一个语法正确的URI 。有时,这种形式有助于增强可读性,但是对检索来说就不一样了。
public String toASCIIString( )
方法toASCIIString( ) 返回一个编码后URI 形式的string。像é 和 \这样的字符总是是被转换成了%xx的,不管它们最初是否已经被转换。很多时间你就应该使用这种string形式 的URI。尽管toString( ) 返回的形式对于人来说更易于理解,但是它们仍然可能被复制粘贴到一些期望得到合法URI的地方。而方法toASCIIString( ) 返回语法正确的URI。
Proxies(代理服务)
许多系统都通过proxy 服务器访问网站和其他Internet上的不使用HTTP的地方。代理服务器接受从本地客户机发向远程服务器的请求,再把这个结果发送给本地客户机。这样做有时出于安全的原因,比如阻止远程服务器获取关于本地客户机网络配置的信息。有时通过过滤发出的请求和限制被访问的网站,阻止用户去访问那些被禁止访问的的网站。比如像一所小学就会阻止对http://www.playboy.com 的访问。当然有时这样做是单纯地出于对效率的考虑,当大量用户需要下载一台远程服务器上的相同文件时,就让他们从一个本地cache上下载,而不是不停地从远程服务器上下载。Java中基于URL 类的程序可以在大多数代理服务器和协议工作。的确,这就是你宁愿想选择使用URL 类而不愿意基于SOCKET自己编写HTTP包或者其他客户端程序的原因。
System Properties(系统属性)
对于基本操作而言,你需要做的就是设置一些系统属性指定你的本地代理服务器的地址。如果你使用的是一个单纯的HTTP代理,把http.proxyHost 设置为域名或者为你的代理服务器的IP地址,把http.proxyPort 设置为代理服务器的端口号(默认为80)。这里有许多种方法可以实现这个操作。包括在你的java代码中调用System.setProperty() 或者当启动程序时,使用命令选项“-D”。下面这个例子就把代理服务器设置为192.168.254.254,端口设置为9000:
% java -Dhttp.proxyHost=192.168.254.254-Dhttp.proxyPort=9000
com.domain.Program
你如果不想让某个主机的访问通过代理服务,想直接进行连接,那么就把系统属性http.nonProxyHosts 设置为它的主机名或ip地址。如果你需要对多个主机都进行这种操作,就用“|”把它们分开。比如,这段代码片段表示它会代理所有的除去java.oreilly.com 和 xml.oreilly.com:
System.setProperty("http.proxyHost", "192.168.254.254");
System.setProperty("http.proxyPort", "9000");
System.setProperty("http.nonProxyHosts", "java.oreilly.com|xml.oreilly.com");
你也可以使用“*”作为一个通配符,表示对所有对所有有特殊域或子域的主机的访问不需要被代理。例如,对除了对oreilly.com 域的主机不代理外,对其他的都可以:
% java -Dhttp.proxyHost=192.168.254.254-Dhttp.nonProxyHosts=*.oreilly.com
com.domain.Program
如果你使用的是个FTP代理服务器,可以用相同的方法设置系统属性,像ftp.proxyHost, ftp.proxyPort, 和 ftp.nonProxyHosts 。java不支持任何其他应用层的代理,除非如果你对所有的TCP连接都使用传输层的SOCKS代理,你可以使用相关的socksProxyHost 和 socksProxyPort 来设置系统属性。对于SOCKS,java没有提供对是否需要代理的选择。你要么选择都需要代理,要么选择都不需要代理。
The Proxy 类
Java1.5允许java程序对代理服务器进行更精细的操作。特别的是,你可以对不同的远程主机选择不同的代理服务器。代理本身是由java.net.Proxy 类的实例表示。这里仍然只有三种代理方式,HTTP, SOCKS, 和直接连接(也就是不需要代理),由Proxy.Type 型迭代器中的三个常量表示:
·Proxy.Type.DIRECT
·Proxy.Type.HTTP
·Proxy.Type.SOCKS
除了代理的类型外,其他关于代理重要信息包括它的地址和端口,由SocketAddress 的对象表示。例如,这个代码片段创建了一个代理对象,它表示proxy.example.com 端口80上的一个HTTP代理服务器:
SocketAddress address = new InetSocketAddress("proxy.example.com", 80);
Proxy proxy = new Proxy(Proxy.Type.HTTP, address);
尽管这里仅有三种代理对象,但是对于不同主机上的不同代理服务器,会有许多相同类型的代理。
The ProxySelector 类
每个java1.5的jvm都有一个java.net.ProxySelector 对象。它可以对不同的连接委派合适的代理服务器。默认的ProxySelector 仅仅是检测一下各种系统属性和URL协议,然后决定怎样连接不同的主机。尽管如此,但是你可以安装你自己的ProxySelector 子类来代替默认的选择器(selector),让它基于协议,主机,路径,日期,或其它标准来选择不同的代理。
这个类的关键就是抽象方法select( ) :
public abstract List<Proxy> select(URI uri)
java传一个表示需要被连接的主机的URI 对象给该方法。用类URL生成的这个连接对象通常是这种形式:http://www.example.com/ 或者 ftp://ftp.example.com/pub/files/, 或者其他类似的。对于由Socket类生成的单纯的TCP连接, URI会是这种形式:socket://host:port:,看一个实例,socket://www.example.com:80 。对象ProxySelector 对这种类型的对象会选择合适的代理,然后再以形式List<Proxy> 返回它们。这个类的第二个你必须实现的抽象方法是connectFailed( ):
public void connectFailed(URI uri, SocketAddress address, IOException ex)
这是个反馈方法,它警告程序代理服务器连接不成功。例子7-11表示一个ProxySelector 试图对所有的HTTP连接都使用在proxy.example.com 上的代理服务器,除非这个代理服务器在前面,对某个特殊的URL的连接已经失败了。在这种情况下,它会建议用一个直接连接代替。
Example 7-11. A ProxySelector that remembers what it can connect to
import java.net.*;
import java.util.*;
import java.io.*;
public class LocalProxySelector extends ProxySelector {
private List failed = new ArrayList( );
public List<Proxy> select(URI uri) {
List<Proxy> result = new ArrayList<Proxy>( );
if (failed.contains(uri)
|| "http".equalsIgnoreCase(uri.getScheme( ))) {
result.add(Proxy.NO_PROXY);
}
else {
SocketAddress proxyAddress
= new InetSocketAddress( "proxy.example.com", 8000);
Proxy proxy = new Proxy(Proxy.Type.HTTP, proxyAddress);
result.add(proxy);
}
return result;
}
public void connectFailed(URI uri, SocketAddress address, IOException ex) {
failed.add(uri);
}
}
我已经说过的,每个运行的jvm都肯定会有一个ProxySelector 。为了改变这个ProxySelector ,我们给静态方法ProxySelector.setDefault( )传一个新的选择器,像这样:
ProxySelector selector = new LocalProxySelector( ):
ProxySelector.setDefault(selector);
从这以后,这个jvm打开的所有连接都会询问这个ProxySelector 该使用哪个合适的代理。正常情况下,你不要在共享环境中使用这段代码。例如,在一个servlet中,你就不要去改变这个ProxySelector 了,因为对在相同容器中的所有servlet来说,它们的ProxySelector 也都被改变了。
通过GET与服务器端程序进行交互
类URL 使java applet和application与服务器端程序比如CGIs, servlets, PHP页面,和其他使用GET 方法程序的交互,变得更容易了。(我们将在15章讨论服务器端使用POST 方法会用到URLConnection 类的程序。)所有你需要知道的是程序希望接受名字和值之间采取怎样的连接,生成一个带有查询string的URL,这个string会提供正需要的名字和值。所有的名字和值必须是这样x-www-form-url-encoded—被方法URLEncoder.encode()编码的,这在前面已经讨论过了。
对一个特定程序的查询string,这里有许多方法来决定它的语法规则。如果你自己已经写过服务器端程序,那么你就已经知道了满足这种程序的名字和值对。如果你已经在自己的服务器上安装了第三方的程序,这个程序的说明文档会告诉你它需要什么样的名字和值对。另一方面,如果你正在与第三方服务器上的程序进行对话,可能就得麻烦多了。你可以总是问在远程服务器上的管理员,让他给你提供怎样和他的站点进行对话的规范说明。尽管如此,即使他们不介意这样做,恐怕也没有谁的工作就是“告诉与我们没有任何关系的黑客,怎样才能进入我们的服务器。”因此,除非你恰巧碰到一个特别热心的或者一个无聊的人,他除了写一封长长的email邮件来告诉你如何进入他们的服务器以外,就没有什么其他事干,否则的话,你不得不做一些逆向工程。
注意:这种情况正在得到改变。不少的网站开始认识到对第三方开发人员开放他们系统的重要性,以及发布一些开发人员需要的插件的重要性。这些插件提供了详细的信息,利用这些信息来构造URL,对他们网站上的服务进行访问。像站点Safari 和 Amazon都提供了RESTful, URL-based接口,通过URL 类,它们都很容易被访问。像eBay和 Google的 SOAP-based服务就很难使用了。
许多程序被设计用来处理表格输入,如果真是这种情况,就可以直截了当地指出程序需要的是什么输入就行了。表格使用的方法应该是FORM 元素的METHOD 属性的值。这个值要么是GET ,这时你可以使用这里所说的这种处理流程,要么是POST ,这时你可以使用将在15章讲的处理流程。FORM 元素的ACTION 属性的值给出了URL查询string前面的内容。需要注意这可能是个相对URL,这时你应该找到与之相对应的绝对URL。最后,名字和值对(name-value pairs)就是INPUT 元素的NAME 属性,TYPE 属性的值是submit的INPUT 元素例外。
比如,看一下我的Cafe con Leche站点上用于本地搜索引擎的HTML表格。你可以看见它使用了GET 方法。通过URL http://www.google.com/search ,这个处理表格的程序能被访问。它有四对名字和值对,它们中的三个有默认值:
<form name="search" action="http://www.google.com/search" method="get">
<input name="q" />
<input type="hidden" value="cafeconleche.org" name="domains" />
<input type="hidden" name="sitesearch" value="cafeconleche.org" />
<input type="hidden" name="sitesearch2" value="cafeconleche.org" />
<br />
<input type="image" height="22" width="55"
src="images/search_blue.gif" alt="search" border="0"
name="search-image" />
</form>
INPUT 的类型并不重要——例如,它是一个复选框的集合,一个下拉菜单条,或者是一个文本域都不重要——只有你给的每个INPUT 的名字和值才是重要的。只有submit型的input是个例外,它告知web浏览器什么时候发送数据,但不会给出服务器任何其他信息。在一些情况下,你可能会发现hidden型的INPUT 必须有一个特定的要求的默认值。这个表格有三个hidden型的INPUT 。在一些情形下,正在与你对话的程序不可能处理有歧义的文本型的string输入值。尽管如此,因为表格是拿来给大家看和填写的,所以它应该提供足够的线索来提示它需要得到什么样的输入;比如,某个域应该是一个两个字母的缩写或者是一个电话号码。
不返回表格的程序对于逆向工程来说会更难。例如,在http://www.ibiblio.org/nywc/bios.phtml,你会发现大量到PHP页面的链接,它们和数据库对话,用某个作曲家的名字来检索一堆音乐作品。尽管这样,这里没有与这个程序相关的表格。这些都被hardcoded的URL处理了。这种情况下,你最多能做的就是尽可能多的查看URL,看你通过他们能不能猜出服务器到底期望得到什么。如果这个设计者不想搞得太晦涩,这些信息是不难得到的。比如,看看在那个页面上所有能得到的URL:
http://www.ibiblio.org/nywc/compositionsbycomposer.phtml?last=Anderson
&first=Beth&middle=
http://www.ibiblio.org/nywc/compositionsbycomposer.phtml?last=Austin
&first=Dorothea&middle=
http://www.ibiblio.org/nywc/compositionsbycomposer.phtml?last=Bliss
&first=Marilyn&middle=
http://www.ibiblio.org/nywc/compositionsbycomposer.phtml?last=Hart
&first=Jane&middle=Smith
看到这些,你可以猜出这个程序希望得到三个输入,分别是first, middle, 和 last,它们的值分别由一个作曲家的first, middle, 和 last名构成。有时的输入不会是那种让人一看就明白的名字。这时,你需要做些实验,首先复制一些已知的值,然后慢慢提炼出哪些值是能或者不能被接受的。在java程序中,你不需要这样做。你可以简单地在你的web浏览器窗口上的地址栏中编辑URL。
注意: 由于其他的黑客可能也会对你的服务器端程序做这样的实验,所以你有理由让它们更健壮以应对那些意外的输入。
不管你怎样确定了这个服务器希望得到的名字和值对,一旦你知道了如何同他们进行交互,这就简单了。所有你需要做的就是创建一个包括必需的名字和值对的查询string,然后生成一个包含这个查询string的URL。发送这个查询string到服务器和读取它的反馈的方法,与你连接服务器和检索一个静态HTML页面的方法是相同的。一旦URL被创建,这里就没有什么特殊的协议需要被遵守了。(POST 方法需要遵守一个特殊的协议,尽管这样,这就是为什么它需要在第15章才讨论。)为了表明这个过程,让我们写一个非常简单的命令行程序来看看Netscape Open Directory (http://dmoz.org/)上的标题。站点如图7-3所示,它有变得真正简单的优势。
图7-3。Open Directory的基本用户界面
Open Directory的基本用户界面是一个简单的表格,它拥有一个名为search 的输入框;其中的输入会发送到http://search.dmoz.org/cgi-bin/search 上的一个CGI程序,它执行真正的搜索。表格的HTML语句像下面这样:
<form accept-charset="UTF-8"
action="http://search.dmoz.org/cgi-bin/search" method="GET">
<input size=30 name=search>
<input type=submit value="Search">
<a href="http://search.dmoz.org/cgi-bin/search?a.x=0">
<small><i>advanced</i></small></a>
</form>
表格里只有两个input:一个Submit按纽和一个名为Search的文本框。因此,如果要向Open Directory发送一个搜索请求,你需要收集搜索string ,把它编码成一个查询string,然后发送到http://search.dmoz.org/cgi-bin/search。例如,为了搜索“java”,你需要打开一个连接URL http://search.dmoz.org/cgi-bin/search?search=java 的连接,然后读取这个结果输入流。例子7-12就是按照这样来做的。
Example 7-12. Do an Open Directory search
import com.macfaq.net.*;
import java.net.*;
import java.io.*;
public class DMoz {
public static void main(String[] args) {
String target = "";
for (int i = 0; i < args.length; i++) {
target += args[i] + " ";
}
target = target.trim( );
QueryString query = new QueryString("search", target);
try {
URL u = new URL("http://search.dmoz.org/cgi-bin/search?" + query);
InputStream in = new BufferedInputStream(u.openStream( ));
InputStreamReader theHTML = new InputStreamReader(in);
int c;
while ((c = theHTML.read( )) != -1) {
System.out.print((char) c);
}
}
catch (MalformedURLException ex) {
System.err.println(ex);
}
catch (IOException ex) {
System.err.println(ex);
}
}
}
当然,在解析和显示结果上需要更多的努力。但是请注意与服务器进行对话的代码是多么的简单呀!。除去看上有趣的URL和组成它的一些部分需要被x-www-form-url-encoded的可能性更大以外,使用GET 与服务器进行对话的程序不比检索任何其他HTML页面的工作困难。
Accessing Password-Protected Sites
(进入受密码保护的站点)
许多流行的站点,像TheWall Street Journal,需要一个用户名和密码才能被访问。一些网站,像W3C的会员页面,通过HTTP认证正规地执行这些。其他的像Java Developer Connection,通过cookies 和 HTML非正规地执行这些认证。Java的URL类能访问使用http认证的站点,尽管你也需要告诉它你的用户名和密码。Java对那些使用非标准的、基于cookie认证的站点是提供支持的。一部分原因是在java1.4和早期版本中,java实际上是不支持cookie的,还有部分原因是:如果这样做,需要解析和发送html表格,最后是因为cookie与web架构是完全抵触的。(Java 1.5的确增加了对cookie的支持,我们将在下一章讨论它。尽管如此,但它没有对有认证功能的cookie与其他cookie区分开。)你可以自己提供这种支持,
使用URLConnection 类来读和写cookie被设置或被返回的http头文件。但是,要想这样做并不简单,经常需要你要连接站点的本地代码。这个难度不压于让web浏览器完全实现对html表格和cookie的支持。访问受标准的http认证保护的站点就简单多了。
Authenticator 类
包java.net 中有一个Authenticator 类,你能用它给受http验证保护的站点提供一个用户名和密码:
public abstract class Authenticator extends Object // Java 1.2
因为Authenticator 是一个抽象类,你必须生成它的子类。不同的子类会用不同的方法检索信息。例如,一个字符模式的程序可能会叫用户输入用户名和密码,用System.in 接受它们。一个GUI程序可能会显示一个对话框,像图7-4那样。一个自动机器会从一个加密文件中读出用户名。
图7-4。验证对话框
为了要让URL 类使用Authenticator 类的子类,就把它传递给静态方法Authenticator.setDefault() ,这样就把它安装成了默认的authenticator:
public static void setDefault(Authenticator a)
比如,如果你已经写好了一个叫DialogAuthenticator 的Authenticator 子类,你得这样安装它:
Authenticator.setDefault(new DialogAuthenticator( ));
你只需要这样做一次就行了。从这以后,当URL 类需要用户名和密码时,它会叫DialogAuthenticator 使用静态方法Authenticator.requestPasswordAuthentication():
public static PasswordAuthentication requestPasswordAuthentication(
InetAddress address, int port, String protocol, String prompt, String scheme)
throws SecurityException
参数address 就是需要进行验证的主机。参数port 就是那个主机上的端口,参数protocol 就是访问这个站点时所用的应用层协议。http服务器会提供参数prompt 。一般就是指需要被验证的访问域。(像www.ibiblio.org 这样的大网站会有许多的访问域,每个都要求用不同的用户名和密码。)参数scheme 是用来做验证的模式。(这里的单词scheme 与protocol (协议)不是同义词。而是一种基本的http验证模式。)
不被信任的applet不允许让用户提供用户名和密码。受信任的applet可以这样做,只要它们有requestPasswordAuthenticationNetPermission
否则的话,Authenticator.requestPasswordAuthentication( ) 抛出SecurityException 异常。Authenticator 的子类必须覆盖getPasswordAuthentication( )方法。这个方法里,你需要从用户或其他来源处取到用户名和密码,然后作为java.net.PasswordAuthentication 类的实例返回:
java.net.PasswordAuthentication class:
protected PasswordAuthentication getPasswordAuthentication( )
如果你不想验证这个请求,就返回null,java回告诉服务器它不知道该怎样验证这个连接。如果你发送一个不正确的用户名或密码,java会再次调用getPasswordAuthentication( ) ,再给你一次机会提交正确的数据。正常情况下,你有5次机会得到正确的用户名和密码;在那以后,openStream( )会抛出ProtocolException 异常。
用户名和密码被放在相同的虚拟机对话中。一旦你对一个访问域使用了正确的密码进行访问,你就不会被要求再次使用密码了,除非你已经很明确地清空了保存它的char 数组。你可以通过调用这些从超类Authenticator 继承来的方法获取更多详细的信息:
protected final InetAddress getRequestingSite( )
protected final int getRequestingPort( )
protected final StringgetRequestingProtocol( )
protected final StringgetRequestingPrompt( )
protected final StringgetRequestingScheme( )
protected final StringgetRequestingHost( )// Java 1.4
这些方法要么返回上次被requestPasswordAuthentication( ) 调用后得到的信息,要么返回null,如果那些信息已经不能使用了。(getRequestingPort( ) 返回-1,如果那个端口不能被使用了。)最后一个方法,getRequestingHost( ) ,只有在java1.4和更新的版本中才能被使用;在早期版本里,你可以调用getRequestingSite( ).getHostName( ) 代替。Java1.5又增加了两个方法到这个类中:
protected final String getRequestingURL( )// Java 1.5
protected Authenticator.RequestorType getRequestorType( )
方法getRequestingURL( )会返回一个被要求使用验证的完整的URL——这是个重要的 ,如果一个站点的不同文件需要使用不同的用户名和密码。方法getRequestorType( ) 会返回下面两个常量中的一个 Authenticator.RequestorType.PROXY 或者 Authenticator.RequestorType.SERVER,以此来区分是服务器还是代理服务器请求验证。
PasswordAuthentication 类
PasswordAuthentication 是一个非常简单的final类,它提供两个只读属性:用户名和密码。用户名是string存放的。密码被存放在char 数组中,所以当不需要时,可以擦除密码。一个string在它可以被擦除前,要等待垃圾回收器来收集,即使这时它仍有可能存在本地系统内存上的某个地方,也有可能在磁盘上,如果这时这个内存块已经被交换到作为虚拟内存的磁盘区域上。用户名和密码被设置在这个构造函数中:
public PasswordAuthentication(String userName, char[] password)
每个都是通过getter 方法被访问的:
public String getUserName( )
public char[] getPassword( )
JPasswordField 类
Swing中JPasswordField 组件有个用来向用户获取密码的有用工具:
public class JPasswordField extends JTextField
这个轻量级组件和文本框的行为几乎是一样的。但是用户输入的会以“*”形式显示出来。这样的话,就可以防止其他人在后面看见用户输入的密码了。
JPasswordField 也用char 数组来存放密码,这样当你不再需要是,你就可以清空它了。方法getPassword( )用来返回密码:
public char[] getPassword( )
不然的话,大多数时候你得使用继承超类JTextField 后得到的方法。例子7-13展示了一个来自Swing的Authenticator 的子类,它生成一个对话框向用户获取他的用户名和密码。下面的大多数代码都是用来生成GUI。JPasswordField 提取密码,JTextField 提取用户名。图7-4就是下面代码所生成的一个简单的对话框。
Example 7-13. A GUI authenticator
package com.macfaq.net;
import java.net.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class DialogAuthenticator extends Authenticator {
private JDialog passwordDialog;
private JLabel mainLabel
= new JLabel("Please enter username and password: ");
private JLabel userLabel = new JLabel("Username: ");
private JLabel passwordLabel = new JLabel("Password: ");
private JTextField usernameField = new JTextField(20);
private JPasswordField passwordField = new JPasswordField(20);
private JButton okButton = new JButton("OK");
private JButton cancelButton = new JButton("Cancel");
public DialogAuthenticator( ) {
this("", new JFrame( ));
}
public DialogAuthenticator(String username) {
this(username, new JFrame( ));
}
public DialogAuthenticator(JFrame parent) {
this("", parent);
}
public DialogAuthenticator(String username, JFrame parent) {
this.passwordDialog = new JDialog(parent, true);
Container pane = passwordDialog.getContentPane( );
pane.setLayout(new GridLayout(4, 1));
pane.add(mainLabel);
JPanel p2 = new JPanel( );
p2.add(userLabel);
p2.add(usernameField);
usernameField.setText(username);
pane.add(p2);
JPanel p3 = new JPanel( );
p3.add(passwordLabel);
p3.add(passwordField);
pane.add(p3);
JPanel p4 = new JPanel( );
p4.add(okButton);
p4.add(cancelButton);
pane.add(p4);
passwordDialog.pack( );
ActionListener al = new OKResponse( );
okButton.addActionListener(al);
usernameField.addActionListener(al);
passwordField.addActionListener(al);
cancelButton.addActionListener(new CancelResponse( ));
}
private void show( ) {
String prompt = this.getRequestingPrompt( );
if (prompt == null) {
String site = this.getRequestingSite( ).getHostName( );
String protocol = this.getRequestingProtocol( );
intport = this.getRequestingPort( );
if (site != null & protocol != null) {
prompt = protocol + "://" + site;
if (port > 0) prompt += ":" + port;
}
else {
prompt = "";
}
}
mainLabel.setText("Please enter username and password for "
+ prompt + ": ");
passwordDialog.pack( );
passwordDialog.show( );
}
PasswordAuthentication response = null;
class OKResponse implements ActionListener {
public void actionPerformed(ActionEvent e) {
passwordDialog.hide( );
// The password is returned as an array of
// chars for security reasons.
char[] password = passwordField.getPassword( );
String username = usernameField.getText( );
// Erase the password in case this is used again.
passwordField.setText("");
response = new PasswordAuthentication(username, password);
}
}
class CancelResponse implements Acti发表评论
相关推荐
#.urls Handler 用于解析 .urls ( application/x-url-list ) 并从中下载所有文件的小型Google Chrome扩展。 扩展使用http:// 、 https:// 、 ftp://和file://方案自动拦截对 .urls 文件的请求。
资源内容:Copy-All-Urls_v2.10.crx。解决一次性复制谷歌浏览器所有标签网址的问题。释放双手。 使用方法:安装谷歌chrome浏览器》下载文件双击打开安装
ZenCart 优化插件 ultimate_seo_urls 的使用和站内优化 ZenCart 优化插件 ultimate_seo_urls 是一个强大的插件,它可以将 ZenCart 的 URL 静态化,从而提高网站的搜索引擎优化(SEO)。下面是使用 ultimate_seo_...
)、等号(=)以及一串难于理解的参数,而静态URL则更易于人类阅读和搜索引擎解析。该插件将这些复杂参数转化为直观的目录结构,如 `example.com/products/shoes` 而非 `example....
usu-3.0.6是Ultimate SEO URLs插件的一个版本,表明该插件已经更新到了3.0.6版,通常意味着包含了更多改进和修复,以适应不断变化的搜索引擎算法和Zencart系统环境。 使用Ultimate SEO URLs插件后,商家可以自定义...
`d8s_urls`很可能是一个处理URL操作或解析的Python库,可能包含了对URL的验证、解析、构建等功能。其版本号0.6.0表示这是该库的第六个主要版本,可能包含了一些新功能、改进或者错误修复。 `.py2.py3-none-any`这...
const parseDataURL = require ( "data-urls" ) ; const textExample = parseDataURL ( "data:,Hello, World!" ) ; console . log ( textExample . mimeType . toString ( ) ) ; // "text/plain;charset=US-ASCII" ...
"Ultimate SEO URLs 2.109"的安装和配置相对简单,但需要一定的Zen Cart操作基础。在安装过程中,你需要确保你的Zen Cart版本为1.3.9,这是插件兼容的最低版本。在上传并解压压缩包`ultimate_seo_urls_2-109`后,你...
- **统一资源定位器(URIs)**:一种用于标识互联网上资源的标准格式,包括URLs(Uniform Resource Locators, 统一资源定位符)和URNs(Uniform Resource Names, 统一资源名称)。 - **X.500**:一个由ITU-T制定的...
AxureRP-extension-for-Chrome-0.6.2 Chrome Version 33.0.1750.146 m 因为众所周知的原因,此插件不能正常下载,但是可以通过离线安装 使用说明: 1 设置---更多工具--扩展程序 2 打开开发者模式 ...
通过`reachable-urls`,开发者可以更有效地管理和维护他们的在线资源,确保链接的正确性和可靠性。无论是在网站内容管理、爬虫项目还是自动化测试中,这个工具都能发挥重要作用,提高工作效率,减少因无效链接带来的...
在这个例子中,`doInBackground`方法负责网络请求和解析工作,而`onPostExecute`则在UI线程中运行,确保安全地更新UI。注意,`onPostExecute`中的操作必须快速,以免阻塞主线程。 为了提高用户体验,你还可以实现...
zencartultimate_seo_urls安装说明.pdf
`resolve-css-import-urls` 是一个非常实用的开源库,专门用于解析CSS中的`@import`规则,并将其转换为绝对或相对的完整URL,从而帮助开发者更好地理解和处理CSS文件的依赖关系。这个库对于构建工具、自动化流程或者...
Chrome插件-Copy All Urls优雅地保存-开启多个标签页.zip。Copy All Urls属于小而美地工具,如果你每天都需要查看几个固定的网页, Copy All Urls能帮你省很多时间。
Jsoup提供了一种简单、安全且有效的方法来解析和操作HTML文档,使得开发者能够轻松地从网页中提取结构化数据。 ### 1. Jsoup的基本概念 Jsoup的核心功能是解析HTML文档,并将其转化为一个DOM(Document Object ...
let inputString = " Slugify a string 对于URLs、文件名和IDs很实用 "; let urlFriendlyString = slugify(inputString); console.log(urlFriendlyString); // 输出:"slugify-a-string-对于urls文件名和ids很实用...
其次,Laravel的路由参数可以用于传递和解析动态数据。在上面的`/posts/{post_id}`示例中,`{post_id}`是一个占位符,Laravel会在执行对应的控制器方法时自动将URL中的值传递给该参数。这样,我们可以在控制器中直接...
parse_urls 解析任何格式的URL 说明 此函数可以解析任意格式的网址(格式良好),返回一个包含其各组成部分的一维数组,且不受PHP版本的限制。 参数 url 要解析的网址,支持以下任意格式: domain.com只有域名 //...
urllib是Python标准库的一部分,它包括了多个子模块,如urllib.request、urllib.parse、urllib.error等,这些子模块共同支持URL解析、打开和读取URLs、处理HTTP请求和响应等功能 urllib模块是Python中处理网络请求的...