2009年12月2日星期三

[转]application session request page

application:全局作用范围,整个应用程序共享,就是在部署文件中的同一个webApp共享,生命周期为:应用程序启动到停止。
session:会话作用域,当用户首次访问时,产生一个新的会话,以后服务器就可以记住这个会话状态。生命周期:会话超时,或者服务器端强制使会话失效。
request:请求作用域,就是客户端的一次请求。
page:一个JSP页面。
以上作用范围使越来越小, request和page的生命周期都是短暂的,他们之间的区别就是:一个request可以包含多个page页(include,forward以及filter)。

[转]浅析编写JSP Tag

编写一个JSP Tag涉及三个步骤:
(1)编写JSP
(2)编写JSP Tag的java程序
(3)编写JSP Tag库的描述文件tld(实际是一个XML文件)
这三个步骤之间没有顺序约束,下面是一个简单的例子:

1 编写HelloTag.jsp

  1. <%@page contentType="text/html"%>   
  2. <html>   
  3. <head><title>Hello Tags Page</title></head>   
  4. <body>   
  5. <%@ taglib uri="/WEB-INF/classes/tags/helloTag.tld" prefix="hello" %> 
  6. <hello:helloTag />   
  7. </body>   
  8. </html> 

2 编写JSP Tag

HelloTag.java

  1. package tags;   
  2. //注意:必须放在一个包中   
  3. import java.io.*;   
  4. import javax.servlet.jsp.*;   
  5. import javax.servlet.jsp.tagext.*;   
  6. public class HelloTag extends TagSupport {   
  7. public int doStartTag() {   
  8. try {   
  9. //使用JspWriter获得JSP的输出对象   
  10. JspWriter jspWriterOutput = pageContext.getOut(); 
  11. jspWriterOutput.print("Hello Tag!");   
  12. }   
  13. catch (IOException ioEx) {   
  14. System .out.println("IOException in HelloTag " + ioEx);   
  15. }   
  16. return (SKIP_BODY);   
  17. }   

3 编写helloTag.tld

这是Tag库的描述部分:

  1. <?xml version="1.0" encoding="UTF-8" ?> 
  2. <!DOCTYPE taglib  
  3. PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"  
  4. "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd"> 
  5. <taglib> 
  6. <tlib-version>1.0</tlib-version> 
  7. <jsp-version>1.2</jsp-version> 
  8. <shorttag&#65279; -name>helloTag</short -name> 
  9. <uri>/WEB-INFtagshelloTag</uri> 
  10. <display-name>helloTag</display-name> 
  11. <small-icon></small-icon> 
  12. <large-icon></large-icon> 
  13. <description>simple hello tags tag  
  14. </description> 
  15. <tag> 
  16. <name>helloTag</name> 
  17. <tag-class >tags.HelloTag</tag-class > 
  18. <body-content>empty</body-content> 
  19. <small-icon></small-icon> 
  20. <large-icon></large-icon> 
  21. <description></description> 
  22. <example></example> 
  23. </tag> 
  24. </taglib>  

4 注意:

通常手工编写XML文件,但是Sun的教程建议使用IDE工具编写自定义tag,比如NetBeans
一般,直接把tld文件放到WEB-INF目录中。

2009年12月1日星期二

[note]URI与URL区别

url:统一资源定位符   
uri:统一资源标志符   
   
URI是URL的一部分

url定位客户端连接到服务器所需要的信息,如   
http://www.csdn.net   
http://tcc.com:8080/servlet/logon?name=zhangsan&addr=tttt   
完整构成   
<protocol>://<servername>[:port]/<url-path>[?query-string]   
   
uri是url组成的一部分,没有域名和查询字符串,
即域名之后查询字符串之前所有的信息,用于制定资源。   
    
例如,对于请求地址   
http://java.sun.com/products/servlet/index.html?id=09   
    
url--->http://java.sun.com/products/servlet/index.html?id=09   
uri--->/products/servlet/index.html  

[转]response.setContentType()的String参数及对应类型

response.setContentType()的String参数及对应类型

<option   value="image/bmp">BMP</option>  
<option   value="image/gif">GIF</option>  
<option   value="image/jpeg">JPEG</option>  
<option   value="image/tiff">TIFF</option>  
<option   value="image/x-dcx">DCX</option>  
<option   value="image/x-pcx">PCX</option>  
<option   value="text/html">HTML</option>  
<option   value="text/plain">TXT</option>  
<option   value="text/xml">XML</option>  
<option   value="application/afp">AFP</option>  
<option   value="application/pdf">PDF</option>  
<option   value="application/rtf">RTF</option>  
<option   value="application/msword">MSWORD</option>  
<option   value="application/vnd.ms-excel">MSEXCEL</option>  
<option   value="application/vnd.ms-powerpoint">MSPOWERPOINT</option>  
<option   value="application/wordperfect5.1">WORDPERFECT</option>  
<option   value="application/vnd.lotus-wordpro">WORDPRO</option>  
<option   value="application/vnd.visio">VISIO</option>  
<option   value="application/vnd.framemaker">FRAMEMAKER</option>  
<option   value="application/vnd.lotus-1-2-3">LOTUS123</option>

2009年11月28日星期六

[原创]Java Web开发参数传输的编码心得

今天做了个测试:

表单的Get或Post方式、AJAX的Get或POST参数传值的情况。使用英文时没什么问题,但是当使用中文时就会出现不同情况。

在此做一个完全的测试。

1. 测试环境一

测试字符为“中文”,“中文”被UTF-8编码一次后为“%E4%B8%AD%E6%96%87”,两次为“%25E4%25B8%25AD%25E6%2596%2587”。
客户端Html编码为UTF-8,服务器端setCharaterEncoding("utf-8");并且不设置server.xml中connector元素的URIEncoding属性。
测试浏览器为Firefox 3.5 和IE 8.服务器为Tomcat 6.
客户端提交时使用的jquery的get和post方法提交的参数。

表单Get方式
                                                            服务器端不解码               服务器端UTF-8解码一次             服务器端UTF-8解码两次
客户端不加密                                              乱码                                 乱码                                       乱码

表单Post方式
                                                            服务器端不解码              服务器端UTF-8解码一次             服务器端UTF-8解码两次
客户端不加密                                              中文                                 中文                                       中文

AJAX的Get方式
                                                            服务器端不解码              服务器端UTF-8解码一次             服务器端UTF-8解码两次
客户端不加密                                              乱码                                 乱码                                       乱码
客户端encodeURIComponent加密一次   编码一次后结果                     中文                                       中文
客户端encodeURIComponent加密两次   编码两次后结果                    编码一次后结果                        中文

AJAX的Post方式
                                                            服务器端不解码              服务器端UTF-8解码一次             服务器端UTF-8解码两次
客户端不加密                                             中文                                    中文                                     中文
客户端encodeURIComponent加密一次  编码一次后结果                        中文                                      中文
客户端encodeURIComponent加密两次  编码两次后结果                    编码一次后结果                          中文

2. 测试环境二

测试字符为“中文”,“中文”被UTF-8编码一次后为“%E4%B8%AD%E6%96%87”,两次为“%25E4%25B8%25AD%25E6%2596%2587”。
客户端Html编码为UTF-8,服务器端不设置setCharaterEncoding("utf-8");并且不设置server.xml中connector元素的URIEncoding属性。
测试浏览器为Firefox 3.5 和IE 8.服务器为Tomcat 6.
客户端提交时使用的jquery的get和post方法提交的参数。

表单Get方式
                                                            服务器端不解码               服务器端UTF-8解码一次             服务器端UTF-8解码两次
客 户端不加密                                              乱码                                 乱码                                       乱码

表单Post方式
                                                            服务器端不解码              服务器端UTF-8解码一次             服务器端UTF-8解码两次
客户端不加密                                               乱码                                 乱码                                       乱码

AJAX的Get方式
                                                            服务器端不解码              服务器端UTF-8解码一次             服务器端UTF-8解码两次
客户端不加密                                              乱码                                 乱码                                       乱码
客户端encodeURIComponent加密一次   编码一次后结果                     中文                                       中文
客户端encodeURIComponent加密两次   编码两次后结果                    编码一次后结果                        中文

AJAX的Post方式
                                                            服务器端不解码              服务器端UTF-8解码一次             服务器端UTF-8解码两次
客户端不加密                                              乱码                                  乱码                                       乱码
客户端encodeURIComponent加密一次  编码一次后结果                        中文                                    中文
客户端encodeURIComponent加密两次  编码两次后结果                    编码一次后结果                        中文

3. 总结

  • 对比两次实验,setCharaterEncoding("utf-8");只有在表单的Post方式和AJAX的post方式时会起作用。post方式会对参数进行utf-8编码(取决于html的编码),而服务器端得默认解码是iso-8859-1,不设置编码的类型所以会产生乱码。
  • Get方式时,始终使用默认的编码类型“iso-8895-1”进行解码,除非设置server.xml的配置文件。所以也可以在客户端不编码的情况下,在服务器端用iso-8859-1编码,再用utf-8解码。(如:new String(old.getBytes("iso-8859-1"),"utf-8"));)。之所以要用utf-8编码是为了保持一致性,.java文件都是utf-8编码存储的。
  • 客户端在参数的传输时,都是先根据网页的编码,对参数编码,此处的情况就是utf-8。
  • 中文时,用utf-8编码,再用iso-8859-1解码就会出现乱码,但是英文则不会。因为UTF-8和iso-8859-1对非中文字符的编码是一样的。
  • 为了同时照顾到get和post的两种情况,以及表单提交的情况。
    • 首先编码需一致采用utf-8(自定),使用setCharaterEncoding("utf-8");
    • 表单方式提交数据时不使用get,既不安全又有乱码问题。实在需要就是用new String(old.getBytes("iso-8859-1"),"utf-8"));的方法。
    • AJAX的get方式时,可以在客户端编码一次(或两次),在服务器端对应的解码一次(两次)。
4. 疑问

  • 在客户端进行一次(两次)编码,在服务器端手动配置了一次(两次)解码。但是服务器默认的getParameter也会进行解码!那不就多解码了一次?
  • 原因是这里使用了jquery,它的ajax方法会自动对参数进行编码
  •         function add( key, value ){
                s[ s.length ] = encodeURIComponent(key) + '=' + encodeURIComponent(value);
            };
  •  如果您是手动添加参数到url上的,那么客户端必须还要encodeURIComponent(value)一次。

2009年11月27日星期五

[转]java类初始化顺序

我们大家都知道,对于静态变量、静态初始化块、变量、初始化块、构造器,它们的初始化顺序依次是(静态变量、静态初始化块)>(变量、初始化块)>构造器。我们也可以通过下面的测试代码来验证这一点:
Java代码
  1. public class InitialOrderTest {   
  2.   
  3.     // 静态变量   
  4.     public static String staticField = "静态变量";   
  5.     // 变量   
  6.     public String field = "变量";   
  7.   
  8.     // 静态初始化块   
  9.     static {   
  10.         System.out.println(staticField);   
  11.         System.out.println("静态初始化块");   
  12.     }   
  13.   
  14.     // 初始化块   
  15.     {   
  16.         System.out.println(field);   
  17.         System.out.println("初始化块");   
  18.     }   
  19.   
  20.     // 构造器   
  21.     public InitialOrderTest() {   
  22.         System.out.println("构造器");   
  23.     }   
  24.   
  25.     public static void main(String[] args) {   
  26.         new InitialOrderTest();   
  27.     }   
  28. }  

运行以上代码,我们会得到如下的输出结果:
  1. 静态变量
  2. 静态初始化块
  3. 变量
  4. 初始化块
  5. 构造器

这与上文中说的完全符合。那么对于继承情况下又会怎样呢?我们仍然以一段测试代码来获取最终结果:
Java代码 
  1. class Parent {   
  2.     // 静态变量   
  3.     public static String p_StaticField = "父类--静态变量";   
  4.     // 变量   
  5.     public String p_Field = "父类--变量";   
  6.   
  7.     // 静态初始化块   
  8.     static {   
  9.         System.out.println(p_StaticField);   
  10.         System.out.println("父类--静态初始化块");   
  11.     }   
  12.   
  13.     // 初始化块   
  14.     {   
  15.         System.out.println(p_Field);   
  16.         System.out.println("父类--初始化块");   
  17.     }   
  18.   
  19.     // 构造器   
  20.     public Parent() {   
  21.         System.out.println("父类--构造器");   
  22.     }   
  23. }   
  24.   
  25. public class SubClass extends Parent {   
  26.     // 静态变量   
  27.     public static String s_StaticField = "子类--静态变量";   
  28.     // 变量   
  29.     public String s_Field = "子类--变量";   
  30.     // 静态初始化块   
  31.     static {   
  32.         System.out.println(s_StaticField);   
  33.         System.out.println("子类--静态初始化块");   
  34.     }   
  35.     // 初始化块   
  36.     {   
  37.         System.out.println(s_Field);   
  38.         System.out.println("子类--初始化块");   
  39.     }   
  40.   
  41.     // 构造器   
  42.     public SubClass() {   
  43.         System.out.println("子类--构造器");   
  44.     }   
  45.   
  46.     // 程序入口   
  47.     public static void main(String[] args) {   
  48.         new SubClass();   
  49.     }   
  50. }  

运行一下上面的代码,结果马上呈现在我们的眼前:
  1. 父类--静态变量
  2. 父类--静态初始化块
  3. 子类--静态变量
  4. 子类--静态初始化块
  5. 父类--变量
  6. 父类--初始化块
  7. 父类--构造器
  8. 子类--变量
  9. 子类--初始化块
  10. 子类--构造器

现在,结果已经不言自明了。大家可能会注意到一点,那就是,并不是父类完全初始化完毕后才进行子类的初始化,实际上子类的静态变量和静态初始化块的初始化是在父类的变量、初始化块和构造器初始化之前就完成了。

那么对于静态变量和静态初始化块之间、变量和初始化块之间的先后顺序又是怎样呢?是否静态变量总是先于静态初始化块,变量总是先于初始化块就被初始化了呢?实际上这取决于它们在类中出现的先后顺序。我们以静态变量和静态初始化块为例来进行说明。

同样,我们还是写一个类来进行测试:
Java代码
  1. public class TestOrder {   
  2.     // 静态变量   
  3.     public static TestA a = new TestA();   
  4.        
  5.     // 静态初始化块   
  6.     static {   
  7.         System.out.println("静态初始化块");   
  8.     }   
  9.        
  10.     // 静态变量   
  11.     public static TestB b = new TestB();   
  12.   
  13.     public static void main(String[] args) {   
  14.         new TestOrder();   
  15.     }   
  16. }   
  17.   
  18. class TestA {   
  19.     public TestA() {   
  20.         System.out.println("Test--A");   
  21.     }   
  22. }   
  23.   
  24. class TestB {   
  25.     public TestB() {   
  26.         System.out.println("Test--B");   
  27.     }   
  28. }  

运行上面的代码,会得到如下的结果:
  1. Test--A
  2. 静态初始化块
  3. Test--B

[原创][精品]一道Java题

在网上看到一道题目,解答之后对类的重写又有了新一步认识,在此分享解答及经验。

代码如下:

/**
 * 父类
 * 
@author rongxinhua
 *
 
*/
public class Father{
    
private String name="FATHER";
    
public Father(){
        whoAmI();
        tellName(name);
    }
    
public void whoAmI(){
        System.out.println(
"Father says, I am " + name);
    }
    
public void tellName(String name){
        System.out.println(
"Father's name is " + name);
    }
}

/**
 * 子类
 * 
@author rongxinhua
 *
 
*/
public class Son extends Father{
    
private String name="SON";
    
public Son(){
        whoAmI();
        tellName(name);
    }
    
public void whoAmI(){
        System.out.println(
"Son says, I am " + name);
    }
    
public void tellName(String name){
        System.out.println(
"Son's name is " + name);
    }
}
问题:当执行 Father who = new Son(); 时,会输出什么?

结果为:

Son says, I am null
Son's name is FATHER
Son says, I am SON
Son's name is SON

 下面两个打印结果很容易想到。但是前面两个就有点摸不着头脑。我一开始也难以理解,主要是考虑的方向错误,误以为是与初始化顺序有关的题目。但其实是一道关于”重写“的题目。

解答:
Son的两个方法whoAmI和tellName重写了父类的方法,这一点是毋庸置疑的。这里有一点:重写是静态的,重写的过程在编译时就完成了,而重载则是动态的(延迟绑定)。所以当执行 Father who = new Son(); 时,实际的执行顺序如下

1. Son的whoAmI()方法:System.out.println("Father says, I am " + name);
2. Son的tellName(String name)方法:System.out.println("Son's name is " + name);
3. Son的whoAmI()方法:System.out.println("Father says, I am " + name);
4. Son的tellName(String name)方法:System.out.println("Son's name is " + name);
看起来有点奇怪,这不都一样吗?的确差不多。但是name不同!3与4的name是Son德name属性(这很容易理解)。
2的name是方法参数传过来的,而且是在父类的构造函数中执行的,那么name的值应该是父类的name属性。可能有点不理解,那么在父类的构造函数中加上一句
        System.out.println("Father says, I am " + name);
得到的结果是”Father says, I am FATHER“。所以name属性在父类的构造方法中的确为”Father“且传给了被重写的tellName方法。
1的name值又为什么为null呢?因为whoAmI方法中的name值直接来自类的实例变量。或许你认为Father的name值应该传给它,不就打印出Son's name is FATHER吗?其实不是。虽然变量名都是name,但是这两个变量是不同的。在编译成字节码的时候这两个变量就区分出来了。Java中方法可以重写,但类的属性不行。对此你可以把name的访问权限都改成public结果仍旧不变。还不理解,那就把Father的name属性名改为name_f,并且在子类的whoAmI方法中加入如下代码:

super.whoAmI();

得到的结果为:

Son says, I am null
Son's name is FATHER
Father says, I am FATHER
Son says, I am SON
Son's name is SON
可见父类的whoAmI方法仍旧可以正常打印结果。这样理解就方便多了!

结论:从这到题可以知道几点:

Java的重写是在编译成字节码时执行的。
Java的方法可以重写但是变量不行。
父类的变量会被保留,如果此变量的权限不为private,则可以通过super来访问。
被重写的方法中引用的实例变量是子类的变量,即使与父类变量名重复,也不会在父类的执行环境中(如构造函数)使用父类变量。
被重写的父类方法可以用super访问。