posts - 73,  comments - 55,  trackbacks - 0
URL(Uniform Resoure Locator,统一资源定位器)是Internet中对资源进行统一定位和管理的标志。
一个完整的URL包括如下内容:
1.         应用协议名称,包括http,ftp,file等标志
2.         资源定位,是由(.)分割等网络路径
3.         端口号,按照规定,http应用端口是80,telnet协议应用端口是23。
4.         服务器中的文件路径
5.         文件中的编码位置
一个完整的URL如下:
http://SomeUser:mypassword@www.some_server.com:8080/path/file.html
 URL无法显示某些特殊符号,这个时候就要使用编码了。编码的格式为:一个百分号,后面跟对应字符的ASCII(16进制)码值。例如 空格的编码值是"%20"。(ASCII参考)
有些字符在URL中具有特殊含义,基本编码规则如下:
特殊含义                                                            十六进制值
1.+ 表示空格(在 URL 中不能使用空格)          %20
2./ 分隔目录和子目录                                              %2F
3.? 分隔实际的 URL 和参数                                   %3F
4.% 指定特殊字符                                                    %25
5.# 表示书签                                                             %23
6.& URL 中指定的参数间的分隔符                        %26
java中URL 的编码和解码函数
java.net.URLEncoder.encode(String s)和java.net.URLDecoder.decode(String s);
在javascript 中URL 的编码和解码函数
escape(String s)和unescape(String s) ;
posted @ 2007-01-12 21:14 保尔任 阅读(10260) | 评论 (0)编辑 收藏
(转自:http://blog.csdn.net/nomads/archive/2006/09/05/1178867.aspx)
Java提供了一套机制来动态执行方法和构造方法,以及数组操作等,这套机制就叫——反射。反射机制是如今很多流行框架的实现基础,其中包括Spring、Hibernate等。原理性的问题不是本文的重点,接下来让我们在实例中学习这套精彩的机制。

1. 得到某个对象的属性

1 public Object getProperty(Object owner, String fieldName) throws Exception {
2     Class ownerClass = owner.getClass();
3 
4     Field field = ownerClass.getField(fieldName);
5 
6     Object property = field.get(owner);
7 
8     return property;
9 }

Class ownerClass = owner.getClass():得到该对象的Class。

Field field = ownerClass.getField(fieldName):通过Class得到类声明的属性。

Object property = field.get(owner):通过对象得到该属性的实例,如果这个属性是非公有的,这里会报IllegalAccessException。



2. 得到某个类的静态属性

 1 public Object getStaticProperty(String className, String fieldName)
 2             throws Exception {
 3     Class ownerClass = Class.forName(className);
 4 
 5     Field field = ownerClass.getField(fieldName);
 6 
 7     Object property = field.get(ownerClass);
 8 
 9     return property;
10 }


Class ownerClass = Class.forName(className) :首先得到这个类的Class。

Field field = ownerClass.getField(fieldName):和上面一样,通过Class得到类声明的属性。

Object property = field.get(ownerClass) :这里和上面有些不同,因为该属性是静态的,所以直接从类的Class里取。


3. 执行某对象的方法

 1 public Object invokeMethod(Object owner, String methodName, Object[] args) throws Exception {
 2 
 3     Class ownerClass = owner.getClass();
 4 
 5     Class[] argsClass = new Class[args.length];
 6 
 7     for (int i = 0, j = args.length; i < j; i++) {
 8         argsClass[i] = args[i].getClass();
 9     }
10 
11     Method method = ownerClass.getMethod(methodName, argsClass);
12 
13     return method.invoke(owner, args);
14 }

Class owner_class = owner.getClass() :首先还是必须得到这个对象的Class。

5~9行:配置参数的Class数组,作为寻找Method的条件。

Method method = ownerClass.getMethod(methodName, argsClass):通过Method名和参数的Class数组得到要执行的Method。

method.invoke(owner, args):执行该Method,invoke方法的参数是执行这个方法的对象,和参数数组。返回值是Object,也既是该方法的返回值。


4. 执行某个类的静态方法

 1 public Object invokeStaticMethod(String className, String methodName,
 2             Object[] args) throws Exception {
 3     Class ownerClass = Class.forName(className);
 4 
 5     Class[] argsClass = new Class[args.length];
 6 
 7     for (int i = 0, j = args.length; i < j; i++) {
 8         argsClass[i] = args[i].getClass();
 9     }
10 
11     Method method = ownerClass.getMethod(methodName, argsClass);
12 
13     return method.invoke(null, args);
14 }


基本的原理和实例3相同,不同点是最后一行,invoke的一个参数是null,因为这是静态方法,不需要借助实例运行。



5. 新建实例
 1 
 2 public Object newInstance(String className, Object[] args) throws Exception {
 3     Class newoneClass = Class.forName(className);
 4 
 5     Class[] argsClass = new Class[args.length];
 6 
 7     for (int i = 0, j = args.length; i < j; i++) {
 8         argsClass[i] = args[i].getClass();
 9     }
10 
11     Constructor cons = newoneClass.getConstructor(argsClass);
12 
13     return cons.newInstance(args);
14 
15 }


这里说的方法是执行带参数的构造函数来新建实例的方法。如果不需要参数,可以直接使用newoneClass.newInstance()来实现。

Class newoneClass = Class.forName(className):第一步,得到要构造的实例的Class。

第5~第9行:得到参数的Class数组。

Constructor cons = newoneClass.getConstructor(argsClass):得到构造子。

cons.newInstance(args):新建实例。


6. 判断是否为某个类的实例

1 public boolean isInstance(Object obj, Class cls) {
2     return cls.isInstance(obj);
3 }



7. 得到数组中的某个元素
1 public Object getByArray(Object array, int index) {
2     return Array.get(array,index);
3 }



附完整源码:

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;


/**
 * Java Reflection Cookbook
 *
 * 
@author Michael Lee
 * 
@since 2006-8-23
 * 
@version 0.1a
 
*/

public class Reflection {
    
/**
     * 得到某个对象的公共属性
     *
     * 
@param owner, fieldName
     * 
@return 该属性对象
     * 
@throws Exception
     *
     
*/
    
public Object getProperty(Object owner, String fieldName) throws Exception {
        Class ownerClass 
= owner.getClass();

        Field field 
= ownerClass.getField(fieldName);

        Object property 
= field.get(owner);

        
return property;
    }

    
/**
     * 得到某类的静态公共属性
     *
     * 
@param className   类名
     * 
@param fieldName   属性名
     * 
@return 该属性对象
     * 
@throws Exception
     
*/
    
public Object getStaticProperty(String className, String fieldName)
            
throws Exception {
        Class ownerClass 
= Class.forName(className);

        Field field 
= ownerClass.getField(fieldName);

        Object property 
= field.get(ownerClass);

        
return property;
    }


    
/**
     * 执行某对象方法
     *
     * 
@param owner
     *            对象
     * 
@param methodName
     *            方法名
     * 
@param args
     *            参数
     * 
@return 方法返回值
     * 
@throws Exception
     
*/
    
public Object invokeMethod(Object owner, String methodName, Object[] args)
            
throws Exception {

        Class ownerClass 
= owner.getClass();

        Class[] argsClass 
= new Class[args.length];

        
for (int i = 0, j = args.length; i < j; i++) {
            argsClass[i] 
= args[i].getClass();
        }

        Method method 
= ownerClass.getMethod(methodName, argsClass);

        
return method.invoke(owner, args);
    }


      
/**
     * 执行某类的静态方法
     *
     * 
@param className
     *            类名
     * 
@param methodName
     *            方法名
     * 
@param args
     *            参数数组
     * 
@return 执行方法返回的结果
     * 
@throws Exception
     
*/
    
public Object invokeStaticMethod(String className, String methodName,
            Object[] args) 
throws Exception {
        Class ownerClass 
= Class.forName(className);

        Class[] argsClass 
= new Class[args.length];

        
for (int i = 0, j = args.length; i < j; i++) {
            argsClass[i] 
= args[i].getClass();
        }

        Method method 
= ownerClass.getMethod(methodName, argsClass);

        
return method.invoke(null, args);
    }



    
/**
     * 新建实例
     *
     * 
@param className
     *            类名
     * 
@param args
     *            构造函数的参数
     * 
@return 新建的实例
     * 
@throws Exception
     
*/
    
public Object newInstance(String className, Object[] args) throws Exception {
        Class newoneClass 
= Class.forName(className);

        Class[] argsClass 
= new Class[args.length];

        
for (int i = 0, j = args.length; i < j; i++) {
            argsClass[i] 
= args[i].getClass();
        }

        Constructor cons 
= newoneClass.getConstructor(argsClass);

        
return cons.newInstance(args);

    }


    
    
/**
     * 是不是某个类的实例
     * 
@param obj 实例
     * 
@param cls 类
     * 
@return 如果 obj 是此类的实例,则返回 true
     
*/
    
public boolean isInstance(Object obj, Class cls) {
        
return cls.isInstance(obj);
    }
    
    
/**
     * 得到数组中的某个元素
     * 
@param array 数组
     * 
@param index 索引
     * 
@return 返回指定数组对象中索引组件的值
     
*/
    
public Object getByArray(Object array, int index) {
        
return Array.get(array,index);
    }
}
 
posted @ 2007-01-12 10:03 保尔任 阅读(506) | 评论 (2)编辑 收藏

面试经验 综合/算法 C/C++/VC MS-SQL Server Java .NET技术 Oracle 其他
[微软系列] [INTEL] [网易] [中兴] [华为] [Google12] [DELL] [朗讯] [神州数码] [方正] [SUN]
 
[面试经验]
高薪是怎么跳出来的?(转载) (wjf4856)
最近几天招聘程序员的感想,另外附一份面试题,大家批批(jyk)
机会是给有准备的人的,我今天去上海微创面试的体会(sanjie88)
面试经历分享:北京文思创新(ghz)
面试的苦恼(palts520)
面试阿里巴巴(boona)
我的面试经历,一个字难!!!!!!!!!!(zxs3543)
如何面试软件工程师?(zenhan)
在网上看了不少公司面试笔试的文章,郁闷了(BLGT)
前天面试失败,特来此作检讨.从今以后开始要好好学习.(utmost100)
要到珠海优特公司面试,请教该做些什么准备?(Builder_Soft)
美工面试一般都会问什么问题(qs_25)
请问面试VB程序员要注意些什么?(chinayokel)
没有面试经验,请大家指导一下(chinayokel)
Asp.net面试一般问什么(zhukuanliang)
急请大家帮忙一会要去面试,听说是上机。一般都出什么题呀(tt5201)
紧急求助:面试时,企业考察编程能力一般会出怎样的题目,特别是VC++!(ab2)
问个问题(视频聊天)和写写4次面试的烦恼(jianjian8410)
[新手求助]电话面试都要准备那些方面?(OpenHero)
为什么每次面试之后都没有结果啊(fangza)
在华为地下室餐厅面试的来聊聊(xubai)
过些天去面试, 软件测试方面, 请各位大虾请提些建议,非常感谢(Ripple_wang)
江湖救急!马上要去面试一个跟物流有关的技术职位,有做物流开发经验的吗?(givemecool)
上周五去长沙大唐先一科技面试,十分郁闷,散分散心!(lingfeng0626)
关于面试(mystones)
做嵌入式开发,一般都面试哪方面的知识?(werqqq)
这次面试太丢脸了,郁闷啊!(feng13555)
继续那个:从东软面试回来郁闷中(转)(mysticality)
NEC公司明天面试,向去过或在这个公司的兄弟请教,顶者勿入(ATaoo)
面试5个月后收到华为录取通知,faint(gomydream)
在面试的时候,自我介绍应该说些什么?(luobo525)
 
[综合/算法]
101道经典面试题 - 01  23456789 (china_cooooooooooder)
一个年薪10万美金的面试题(ayungood)
面试考题,愁啊!!!(mysticality)
[调查]去西安交大的一个软件公司面试,面试官问了一个问题,看有多少人能回答来,顺便散分(bgqy2000)
Google面试的20题,知道的麻烦给点提示 .(hansin)
据说是微软面试题,有答案了,但不知道为什么会是这样!(DaChu)
月薪3万的一道面试题(看看你的IQ )(cgtsea)
SZSM面试题:写一个可以返回任意两个string串的最大公串的函数,语言不限(redbirdli)
分享一下:今天我们公司招聘产品设计师的面试题目……(KiteGirl)
面试时一道超难的算法问题!!!(justrun2005)
一道真正难倒亿人的智力题,这是微软的面试题(66766960)
据说是一道微软月薪5w职位的面试题(cjjfam)
DELL的英文面试题(硬件部分和操作系统),兄弟们直接写下答案,切磋以下!(zh050317)
从网上搜集到的网易笔试题,大家一起讨论一下.
 

[C/C++/VC]

再帖 几道面试题(据说是INTEL公司的)(zdl1016)
[转帖]大家来试试GOOGLE面试题!!(laiwusheng)
据说是朗训面试题(flyiner)
我的中兴面试题,求达人给个答案啊(flyabcd)
求面试题~南京联创的(110120119)
求教一面试题:编写一个函数比较两个整数大小,但不能使用任何比较操作符(neoadane)
C语言面试题大汇总,个人觉得还是比较全地!!!(free131)
前天的一道面试题,前辈帮看看!(67676373)
微软经典面试题(bastenf)
问个面试题(zjbirdman)
面试问题之反转字符串中单词的顺序(ugg)
求助:关于局部变量的一道面试题(neoadane)
昨天朋友面试碰到的一面试题,请教大家(petertangpei)
一个面试题(xiaojun19830916)
两道有趣的面试题目.(yangnix)
各位大侠请帮小弟作道面试题,先谢了!(open_ocean)
面试问题,求助,送分(OneAudollar)
1道面试题 大家帮忙看看(gggaaakkk)
面试题1(yangxudongseu)
 面试题目,求助!(OneAudollar)
又有一个面试题,大家看看(yifongzhou)
面试题,有点难,欢迎高手指点(xnkjdx1998)
大家看看面试题!(yanjun885)
求解几个面试题,今天面的,郁闷死我了(paskaa)
昨天的面试题,分享一下(jianyachu)
一道面试纠错题,大家看看吧(nysst_hxl)
一个关于指针的面试题,会者请进。(fansgq)
软件外企C++面试题,大家试试看?(OneAudollar)
两道C面试题,不小心你未必能做对哦(ilelf)
[高手进]一个面试题,希望高手帮我解答,图形方面!(yangc_83)
[要求置顶]开源软件的面试题——各位大哥们给点思路(dream2013)
面试时被人问到两个问题(yjukh)
求助:面试一些题目,高手们过来帮忙解答一下~(yjukh)
今天的面试题(自学C果然够呛,望大家多多提示)(ppsdog1956)
 
[MS-SQL Server]
sql server 面试时 的考题!!(整理)(2344095)
神州数码11道面试题求解(jinder22)
3棵面试题,大家来看看...................(hanchi8008)
关于事务的问题(面试题)(zyq_10_25)
sql server 面试时 的考题!!(2344095)
昨天看了一个朋友的面试题,大家也来看看(THE_ROCK)
这SQL语句我真的不会写?面试题目(hzchl219)
棘手的(SQL)面试题(songyutou)
面试题!(zyq_10_25)
我在面试时遇到的sql语句,请高人指点,跪求!!!!(polo_van)
面试问题,请帮忙解答!!(songcan)
这个sql语句该怎么写?(面试题)(csdn02)
这边还有40分.求面试题:关于存储过程和触发器的,并附答案呀!!!!!!!!!!!!!!(fairan)
面试问题 大表、小表 ,内联查询,大表放在前面还是后面查询速度更快(jietuan)
 
[Oracle]
DBA常见面试题征集答案,并希望达人补充,希望此贴让所有想做DBA的人有点方向。(pegtop)
面试题(zyq_10_25)
我今天面试的SQL题目,欢迎刚毕业的同学来试一试!!!(kong361)
 
[Java]
共享一些面试题(wts173)
方正的面试题目(jmh0525)
一个很妖的问题(sun公司的面试题)(CCJHJ)
高分求救!面试的时候,考官这样问我,我要怎么回答!! 谢谢!!定给高分!
外企的一道面试题.还请高手多多指点指点(急)!谢了!!(ayorange)
Java面试题(x_hong)
一公司面试题,做一翻译字典,要把不同的字典放在不同的服务器,高手帮忙啊(totti1110)
请教一道面试题?(javaf1)
本人自己遇到的面试题(急!急!急!)(caiheng520)
一道面试题:这么确定a是不是B类的一个实例。谢谢(luofengjava)
一个Hibernate 的面试题, 现场分析问题(this_is_alan)
面试题!在线等!(mousefog)
面试题目!谁能告诉我答案啊!(yinlei920)
面试题(Hmilyl)
感兴趣的话来看一下这道面试题(chenfive)
高分求教!!!一道面试题(url链接传中文参数值乱码问题)(tooker)
面试题60分!请大家帮忙(zyq_10_25)
面试题求答案(zhyhongyuan)
共享J2ME面试题目,欢迎大家积极参与(Mailbomb)
今天的面试题(Hmilyl)
一道java面试题(今天刚面试回来)(cuiter)
面试关于JSP+oracl的问题求救(coolcat_1981)
 
[.NET技术]  
在网上看到的面试题大全,有兴趣的朋友可以来试试看(greatbag)
dotNet高级软件工程师面试题(flyinsky333)
.net面试题集(liuxingjin)
闲来无事,做了一道面试题:100位数字相乘(czhenq)
**大型软件公司.net面试题!一定的看!(aspjsp)
我们公司面试基本完成,现公开部分面试题,感兴趣的来看看,同时也是给出标准答案供面试者参考。(Ivony)
昨天面试微软,只有这一题不会做。(amingo)
一道真正难倒亿人的智力题,这是微软的面试题(66766960)
【MS的一道面试题】Session问题(cxyppppp)
我们公司的一道面试题(jobenc)
有点麻烦的面试题,请给个核心算法(iacpdj)
一道.net面试题???(never_give_up520)
一道面试题,大家解答!(tigerlgf)
今天面试的两个问题,没能回答好,郁闷!(zhanghaif)
面试题大家都来看看 在线等急~~~~~~~~~~~~~~~~~~~~~~~~~~~~~(willyer)
一道面试题,怎么也没看懂,大家帮忙看看(iacpdj)
同学去面试的两个面试题!(longshaoye)
面试题(karso)
不知道是垃圾面试题还是好的面试题?大家看看吧!(rex1984)
面试遇到这种题,晕了!求各位施予援手!(songcan)
一道面试题,没做出来!(cloud_1981)
面试题目:如何在客户端新建一个项目到服务器端(evila_love)
一道外企机试面试--------高手请进(getbyling)
 
[其他]
网络安全方面的面试题(vagrantisme)
软件测试/质量评估(QA)面试的题目,大家帮忙想想(qiuzhizhe)
[汇编]来者有分,面试题,贴出来一起讨论,超难!!!!!(crtd024)
出了套面试题,大家看看,这让没经验值的人做合不合适(wojiudaofen)
急问一道关于测试的面试题(xjh_Love_paopao)
一道关于路由器的面试题,高手请进!!(Gamehal)
[Linux/Unix社区]今天面试的时候的问题没有能够回答出来的题,郁闷!各位帮忙看看
[Linux/Unix社区]请教面试题一则(ty263)
[Windows]昨天面试,考官问我平时用户端的数据怎么做备份的,大家来谈谈!(ballatong)
[Windows]求面试题(qiuyinggxnn)
[ASP]下午去面试遇上了一个很有挑战的问题,有没高手路过?(me121121)
[VB]VB面试过后不过,问问大家这几题该怎么回答(xiebird)
 
[微软系列]
[转贴]微软面试题系列(1) 2 3 4 5(fire_dragon)
一道真正难倒亿人的智力题,这是微软的面试题(66766960)
据说是一道微软月薪5w职位的面试题(cjjfam)
据说是微软面试题,有答案了,但不知道为什么会是这样!(DaChu)
微软经典面试题(bastenf)
昨天面试微软,只有这一题不会做。(amingo)
一道真正难倒亿人的智力题,这是微软的面试题(66766960)
【MS的一道面试题】Session问题(cxyppppp)

posted @ 2006-12-31 16:09 保尔任 阅读(5666) | 评论 (0)编辑 收藏

URI(Uniform Resoure Identifier:统一资源标识符),URL(Uniform Resoure Location:统一资源定位器),URN(Uniform Resource Name统一资源

名),URC(Uniform Resource Citation统一资源引用符)

URI、URL和URN是识别、定位和命名互联网上的资源的标准途径; URL,URN是URI的子集.

     URI不能定位或读取/写入资源。这是统一的资源定位器(URL)的任务。URL是一种URI,但是它的大纲组件是已知的网络协议(简称协议

),并且它把URI组件与某种协议处理程序(一种资源定位器和根据协议建立的约束规则与资源通讯的读/写机制)。

  URI一般不能为资源提供持久不便的名称。这是统一的资源命名(URN)的任务。URN也是一种URI,但是全球唯一的、持久不便的,即使资

源不在存在或不再使用。

      web上地址的基本形式是URI,它代表统一资源标识符。有两种形式:
      URL:目前URI的最普遍形式就是无处不在的URL或统一资源定位器。
      URN:URL的一种更新形式,统一资源名称(URN, Uniform Resource Name)不依赖于位置,并且有可能减少失效连接的个数。但是其流行

还需假以时日,因为它需要更精密软件的支持。

体系中的URI、URL和URN是彼此关联的。URI的范畴位于体系的顶层,URL和URN的范畴位于体系的底层。这种排列显示URL和URN都是URI的子范畴

, URI表示的是统一的资源标识,它是以某种统一的(标准化的)方式标识资源的简单字符串。典型情况下,这种字符串以scheme(命名URI

的名字空间的标识符--一组相关的名称)开头,语法如下:

[scheme:] scheme-specific-part

URI以scheme和冒号开头。Scheme用大写/小写字母开头,后面为空或者跟着更多的大写/小写字母、数字、加号、减号和点号。冒号把scheme与

scheme-specific-part分开了,并且scheme-specific-part的语法和语义(意思)由URI的名字空间决定。  

其中一个例子是http://www.cnn.com,其中http是scheme,//www.cnn.com是 scheme-specific-part,并且它的scheme与scheme-specific-

part被冒号分开了。

我们可以把URI按照绝对的或相对的分类。绝对的URI指以scheme(后面跟着冒号)开头的URI。前面提到的http://www.cnn.com就是绝对的URI

的一个例子,其它的例子还有mailto:jeff@javajeff.comnews:comp.lang.java.help和xyz://whatever。你可以把绝对的URI看作是以某种方

式引用某种资源,而这种方式对标识符出现的环境没有依赖。   

如果使用文件系统作类比,绝对的URI类似于从根目录开始的某个文件的路径。与绝对的URI不同的,相对的URI不是以scheme(后面跟着冒号)

开始的URI。

它的一个例子是articles/articles.html。你可以把相对的URI看作是以某种方式引用某种资源,而这种方式依赖于标识符出现的环境。如果用

文件系统作类比,相对的URI类似于从当前目录开始的文件路径。

URI:

Web上可用的每种资源 - HTML文档、图像、视频片段、程序等 - 由一个通过通用资源标志符(Universal Resource Identifier, 简称"URI")

进行定位。
URI一般由三部分组成:
1. 访问资源的命名机制。
2. 存放资源的主机名。
3. 资源自身的名称,由路径表示。
注:大多数人可能熟悉"URL",而不是URI。URL是URI命名机制的一个子集。

URL:

URL是Uniform Resource Location的缩写,译为"统一资源定位符"。通俗地说,URL是Internet上用来描述信息资源的字符串,主要用在各种

WWW客户程序和服务器程序上,特别是著名的Mosaic。采用URL可以用一种统一的格式来描述各种信息资源,包括文件、服务器的地址和目录等


URL的格式
URL的格式由下列三部分组成:
第一部分是协议(或称为服务方式);
第二部分是存有该资源的主机IP地址(有时也包括端口号);
第三部分是主机资源的具体地址。,如目录和文件名等。
第一部分和第二部分之间用"://"符号隔开,第二部分和第三部分用"/"符号隔开。第一部分和第二部分是不可缺少的,第三部分有时可以省略


URL的缺点:
当信息资源的存放地点发生变化时,必须对URL作相应的改变。因此人们正在研究新的信息资源表示方法,例如:URI(Universal Resource

Identifier)即"通用资源标识"(参见RFC 1630)、URN(Uniform Resource Name)即"统一资源名"和URC(Uniform Resource Citation)即"

统一资源引用符"等。

 

posted @ 2006-12-31 15:27 保尔任 阅读(1109) | 评论 (0)编辑 收藏
 
   Abstract:本文深入分析了Java程序设计中Java编译器对java源文件和JVM对class类文件的编码/解码过程,通过此过程的解析透视出了Java编程中中文问题产生的根本原因,最后给出了建议的最优化的解决Java中文问题的方法。 

  1、中文问题的来源

    计算机最初的操作系统支持的编码是单字节的字符编码,于是,在计算机中一切处理程序最初都是以单字节编码的英文为准进行处理。随着计算机的发展,为了适应世界其它民族的语言(当然包括我们的汉字),人们提出了UNICODE编码,它采用双字节编码,兼容英文字符和其它民族的双字节字符编码,所以,目前,大多数国际***的软件内部均采用UNICODE编码,在软件运行时,它获得本地支持系统(多数时间是操作系统)默认支持的编码格式,然后再将软件内部的 UNICODE转化为本地系统默认支持的格式显示出来。Java的JDK和JVM即是如此,我这里说的JDK是指国际版的JDK,我们大多数程序员使用的是国际化的JDK版本,以下所有的JDK均指国际化的JDK版本。我们的汉字是双字节编码语言,为了能让计算机处理中文,我们自己制定的gb2312、 GBK、GBK2K等标准以适应计算机处理的需求。所以,大部分的操作系统为了适应我们处理中文的需求,均定制有中文操作系统,它们采用的是GBK, GB2312编码格式以正确显示我们的汉字。如:中文Win2K默认采用的是GBK编码显示,在中文WIN2k中保存文件时默认采用的保存文件的编码格式也是GBK的,即,所有在中文WIN2K中保存的文件它的内部编码默认均采用GBK编码,注意:GBK是在GB2312基础上扩充来的。

    由于Java语言内部采用UNICODE编码,所以在JAVA程序运行时,就存在着一个从UNICODE编码和对应的操作系统及浏览器支持的编码格式转换输入、输出的问题,这个转换过程有着一系列的步骤,如果其中任何一步出错,则显示出来的汉字就会出是乱码,这就是我们常见的JAVA中文问题。

    同时,Java是一个跨平台的编程语言,也即我们编写的程序不仅能在中文windows上运行,也能在中文Linux等系统上运行,同时也要求能在英文等系统上运行(我们经常看到有人把在中文win2k上编写的JAVA程序,移植到英文Linux上运行)。这种移植操作也会带来中文问题。

    还有,有人使用英文的操作系统和英文的IE等浏览器,来运行带中文字符的程序和浏览中文网页,它们本身就不支持中文,也会带来中文问题。

    几乎所有的浏览器默认在传递参数时都是以UTF-8编码格式来传递,而不是按中文编码传递,所以,传递中文参数时也会有问题,从而带来乱码现象。

    总之,以上几个方面是JAVA中的中文问题的主要来源,我们把以上原因造成的程序不能正确运行而产生的问题称作:JAVA中文问题。

  2、JAVA编码转换的详细过程

    我们常见的JAVA程序包括以下类别:
     *直接在console上运行的类(包括可视化界面的类)
     *JSP代码类(注:JSP是Servlets类的变型)
     *Servelets类
     *EJB类
     *其它不可以直接运行的支持类

    这些类文件中,都有可能含有中文字符串,并且我们常用前三类JAVA程序和用户直接交互,用于输出和输入字符,如:我们在JSP和Servlet中得到客户端送来的字符,这些字符也包括中文字符。无论这些JAVA类的作用如何,这些JAVA程序的生命周期都是这样的:

    *编程人员在一定的操作系统上选择一个合适的编辑软件来实现源程序代码并以.java扩展名保存在操作系统中,例如我们在中文win2k中用记事本编辑一个java源程序;
     *编程人员用JDK中的javac.exe来编译这些源代码,形成.class类(JSP文件是由容器调用JDK来编译的);
     *直接运行这些类或将这些类布署到WEB容器中去运行,并输出结果。
    那么,在这些过程中,JDK和JVM是如何将这些文件如何编码和解码并运行的呢?

这里,我们以中文win2k操作系统为例说明JAVA类是如何来编码和被解码的。

    第一步,我们在中文win2k中用编辑软件如记事本编写一个Java源程序文件(包括以上五类JAVA 程序),程序文件在保存时默认采用了操作系统默认支持GBK编码格式(操作系统默认支持的格式为file.encoding格式)形成了一个.java文件,也即,java程序在被编译前,我们的JAVA源程序文件是采用操作系统默认支持的file.encoding编码格式保存的,java源程序中含有中文信息字符和英文程序代码;要查看系统的file.encoding参数,可以用以下代码:
  public class ShowSystemDefaultEncoding {
  public static void main(String[] args) {
  String encoding = System.getProperty("file.encoding");
  System.out.println(encoding);
  }}

    第二步,我们用JDK的javac.exe文件编译我们的Java源程序,由于JDK是国际版的,在编译的时候,如果我们没有用-encoding参数指定我们的 JAVA源程序的编码格式,则javac.exe首先获得我们操作系统默认采用的编码格式,也即在编译java程序时,若我们不指定源程序文件的编码格式,JDK首先获得操作系统的file.encoding参数(它保存的就是操作系统默认的编码格式,如WIN2k,它的值为GBK),然后JDK就把我们的java源程序从file.encoding编码格式转化为JAVA内部默认的 UNICODE格式放入内存中。然后,javac把转换后的unicode格式的文件进行编译成.class类文件,此时.class文件是 UNICODE编码的,它暂放在内存中,紧接着,JDK将此以UNICODE编码的编译后的class文件保存到我们的操作系统中形成我们见到的. class文件。对我们来说,我们最终获得的.class文件是内容以UNICODE编码格式保存的类文件,它内部包含我们源程序中的中文字符串,只不过此时它己经由file.encoding格式转化为UNICODE格式了。

    这一步中,对于JSP源程序文件是不同的,对于JSP,这个过程是这样的:即WEB容器调用JSP编译器,JSP编译器先查看JSP文件中是否设置有文件编码格式,如果JSP文件中没有设置JSP文件的编码格式,则JSP编译器调用JDK先把JSP文件用JVM默认的字符编码格式(也即WEB容器所在的操作系统的默认的file.encoding)转化为临时的Servlet类,然后再把它编译成UNICODE格式的class类,并保存在临时文件夹中。如:在中文win2k上,WEB容器就把JSP文件从GBK编码格式转化为UNICODE格式,然后编译成临时保存的Servlet类,以响应用户的请求。

    第三步,运行第二步编译出来的类,分为三种情况:

    A、 直接在console上运行的类
    B、 EJB类和不可以直接运行的支持类(如JavaBean类)
    C、 JSP代码和Servlet类
    D、 JAVA程序和数据库之间
    下面我们分这四种情况来看。
    A、直接在console上运行的类

    这种情况,运行该类首先需要JVM支持,即操作系统中必须安装有JRE。运行过程是这样的:首先java启动JVM,此时JVM读出操作系统中保存的 class文件并把内容读入内存中,此时内存中为UNICODE格式的class类,然后JVM运行它,如果此时此类需要接收用户输入,则类会默认用 file.encoding编码格式对用户输入的串进行编码并转化为unicode保存入内存(用户可以设置输入流的编码格式)。程序运行后,产生的字符串(UNICODE编码的)再回交给JVM,最后JRE把此字符串再转化为file.encoding格式(用户可以设置输出流的编码格式)传递给操作系统显示接口并输出到界面上。

    对于这种直接在console上运行的类,它的转化过程可用图1更加明确的表示出来:

图1

以上每一步的转化都需要正确的编码格式转化,才能最终不出现乱码现象。

    B、EJB类和不可以直接运行的支持类(如JavaBean类)

    由于EJB类和不可以直接运行的支持类,它们一般不与用户直接交互输入和输出,它们常常与其它的类进行交互输入和输出,所以它们在第二步被编译后,就形成了内容是UNICODE编码的类保存在操作系统中了,以后只要它与其它的类之间的交互在参数传递过程中没有丢失,则它就会正确的运行。
这种EJB类和不可以直接运行的支持类, 它的转化过程可用图2更加明确的表示出来:

图2


    C、JSP代码和Servlet类

    经过第二步后,JSP文件也被转化为Servlets类文件,只不过它不像标准的Servlets一校存在于classes目录中,它存在于WEB容器的临时目录中,故这一步中我们也把它做为Servlets来看。

    对于Servlets,客户端请求它时,WEB容器调用它的JVM来运行Servlet,首先,JVM把Servlet的class类从系统中读出并装入内存中,内存中是以UNICODE编码的Servlet类的代码,然后JVM在内存中运行该Servlet类,如果Servlet在运行的过程中,需要接受从客户端传来的字符如:表单输入的值和URL中传入的值,此时如果程序中没有设定接受参数时采用的编码格式,则WEB容器会默认采用ISO-8859- 1编码格式来接受传入的值并在JVM中转化为UNICODE格式的保存在WEB容器的内存中。Servlet运行后生成输出,输出的字符串是 UNICODE格式的,紧接着,容器将Servlet运行产生的UNICODE格式的串(如html语法,用户输出的串等)直接发送到客户端浏览器上并输出给用户,如果此时指定了发送时输出的编码格式,则按指定的编码格式输出到浏览器上,如果没有指定,则默认按ISO-8859-1编码发送到客户的浏览器上。这种JSP代码和Servlet类,它的转化过程可用图3更加明确地表示出来:

图3

D、Java程序和数据库之间

    对于几乎所有数据库的JDBC驱动程序,默认的在JAVA程序和数据库之间传递数据都是以ISO-8859-1为默认编码格式的,所以,我们的程序在向数据库内存储包含中文的数据时,JDBC首先是把程序内部的UNICODE编码格式的数据转化为ISO-8859-1的格式,然后传递到数据库中,在数据库保存数据时,它默认即以ISO-8859-1保存,所以,这是为什么我们常常在数据库中读出的中文数据是乱码。
    对于JAVA程序和数据库之间的数据传递,我们可以用图4清晰地表示出来

图4


    3、分析常见的JAVA中文问题几个必须清楚的原则

    首先,经过上面的详细分析,我们可以清晰地看到,任何JAVA程序的生命期中,其编码转换的关键过程是在于:最初编译成class文件的转码和最终向用户输出的转码过程。
    其次,我们必须了解JAVA在编译时支持的、常用的编码格式有以下几种:
    *ISO-8859-1,8-bit, 同8859_1,ISO-8859-1,ISO_8859_1等编码
    *Cp1252,美国英语编码,同ANSI标准编码
    *UTF-8,同unicode编码
    *GB2312,同gb2312-80,gb2312-1980等编码
    *GBK , 同MS936,它是gb2312的扩充
    及其它的编码,如韩文、日文、繁体中文等。同时,我们要注意这些编码间的兼容关体系如下:
    unicode和UTF-8编码是一一对应的关系。GB2312可以认为是GBK的子集,即GBK编码是在gb2312上扩展来的。同时,GBK编码包含了20902个汉字,编码范围为:0x8140-0xfefe,所有的字符可以一一对应到UNICODE2.0中来。

    再次,对于放在操作系统中的.java源程序文件,在编译时,我们可以指定它内容的编码格式,具体来说用-encoding来指定。注意:如果源程序中含有中文字符,而你用-encoding指定为其它的编码字符,显然是要出错的。用-encoding指定源文件的编码方式为GBK或gb2312,无论我们在什么系统上编译含有中文字符的JAVA源程序都不会有问题,它都会正确地将中文转化为UNICODE存储在class文件中。
    
    然后,我们必须清楚,几乎所有的WEB容器在其内部默认的字符编码格式都是以ISO-8859-1为默认值的,同时,几乎所有的浏览器在传递参数时都是默认以UTF-8的方式来传递参数的。所以,虽然我们的Java源文件在出入口的地方指定了正确的编码方式,但其在容器内部运行时还是以ISO-8859- 1来处理的。


 4、中文问题的分类及其建议最优解决办法

    了解以上JAVA处理文件的原理之后,我们就可以提出了一套建议最优的解决汉字问题的办法。
    我们的目标是:我们在中文系统中编辑的含有中文字符串或进行中文处理的JAVA源程序经编译后可以移值到任何其它的操作系统中正确运行,或拿到其它操作系统中编译后能正确运行,能正确地传递中文和英文参数,能正确地和数据库交流中英文字符串。
    我们的具体思路是:在JAVA程序转码的入口和出口及JAVA程序同用户有输入输出转换的地方限制编码方法使之正确即可。

    具体解决办法如下:

    1、 针对直接在console上运行的类
    对于这种情况,我们建议在程序编写时,如果需要从用户端接收用户的可能含有中文的输入或含有中文的输出,程序中应该采用字符流来处理输入和输出,具体来说,应用以下面向字符型节点流类型:
    对文件:FileReader,FileWrieter
        其字节型节点流类型为:FileInputStream,FileOutputStream
    对内存(数组):CharArrayReader,CharArrayWriter
        其字节型节点流类型为:ByteArrayInputStream,ByteArrayOutputStream
    对内存(字符串):StringReader,StringWriter
    对管道:PipedReader,PipedWriter
        其字节型节点流类型为:PipedInputStream,PipedOutputStream
    同时,应该用以下面向字符型处理流来处理输入和输出:
    BufferedWriter,BufferedReader
        其字节型的处理流为:BufferedInputeStream,BufferedOutputStream
    InputStreamReader,OutputStreamWriter
    其字节型的处理流为:DataInputStream,DataOutputStream
    其中InputStreamReader和InputStreamWriter用于将字节流按照指定的字符编码集转换到字符流,如:
    InputStreamReader in = new InputStreamReader(System.in,"GB2312");
    OutputStreamWriter out = new OutputStreamWriter (System.out,"GB2312");
    例如:采用如下的示例JAVA编码就达到了要求:

    //Read.java
    import java.io.*;
    public class Read {
    public static void main(String[] args) throws IOException {
    String str = "\n中文测试,这是内部硬编码的串"+"\ntest english character";
    String strin= "";
    BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in,"gb2312")); //设置输入接口按中文编码
    BufferedWriter stdout = new BufferedWriter(new OutputStreamWriter(System.out,"gb2312")); //设置输出接口按中文编码
    stdout.write("请输入:");
    stdout.flush();
    strin = stdin.readLine();
    stdout.write("这是从用户输入的串:"+strin);
    stdout.write(str);
    stdout.flush();
    }}
    同时,在编译程序时,我们用以下方式来进行:
    javac -encoding gb2312 Read.java
    其运行结果如图5所示:

    图5
2、 针对EJB类和不可以直接运行的支持类(如JavaBean类)

    由于这种类它们本身被其它的类调用,不直接与用户交互,故对这种类来说,我们的建议的处理方式是内部程序中应该采用字符流来处理程序内部的中文字符串(具体如上面一节中一样),同时,在编译类时用-encoding gb2312参数指示源文件是中文格式编码的即可。


    3、 针对Servlet类

    针对Servlet,我们建议用以下方法:(我建议将.java文件设置为UTF8编码的。当然如果是用Eclipse的话,只要设置下就是了。对于数据库,我以为编码最好设置为UTF8,便于国际化 。尽可能的使用UTF8码,  千里草)

    在编译Servlet类的源程序时,用-encoding指定编码为GBK或GB2312,且在向用户输出时的编码部分用response对象的 setContentType("text/html;charset=GBK");或gb2312来设置输出编码格式,同样在接收用户输入时,我们用 request.setCharacterEncoding("GB2312");这样无论我们的servlet类移植到什么操作系统中,只有客户端的浏览器支持中文显示,就可以正确显示。如下是一个正确的示例:

    //HelloWorld.java
    package hello;
    import java.io.*;
    import javax.servlet.*;
    import javax.servlet.http.*;
    public class HelloWorld extends HttpServlet
    {
    public void init() throws ServletException { }
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
    {
    request.setCharacterEncoding("GB2312"); //设置输入编码格式
    response.setContentType("text/html;charset=GB2312"); //设置输出编码格式
    PrintWriter out = response.getWriter(); //建议使用PrintWriter输出
    out.println("<hr>");
    out.println("Hello World! This is created by Servlet!测试中文!");
    out.println("<hr>");
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
    {
    request.setCharacterEncoding("GB2312"); //设置输入编码格式
    response.setContentType("text/html;charset=GB2312"); //设置输出编码格式
    String name = request.getParameter("name");
    String id = request.getParameter("id");
    if(name==null) name="";
    if(id==null) id="";
    PrintWriter out = response.getWriter(); //建议使用PrintWriter输出
    out.println("<hr>");
    out.println("你传入的中文字串是:" + name);
    out.println("<hr>你输入的id是:" + id);
    out.println("<hr>");
    }
    public void destroy() { }
    }
        请用javac -encoding gb2312 HelloWorld.java来编译此程序。
        测试此Servlet的程序如下所示:
    <%@page contentType="text/html; charset=gb2312"%>
    <%request.setCharacterEncoding("GB2312");%>
    <html><head><title></title>
    <Script language="JavaScript">
    function Submit() {
    //通过URL传递中文字符串值给Servlet
    document.base.action = "./HelloWorld?name=中文";
    document.base.method = "POST";
    document.base.submit();
    }
    </Script>
    </head>

<body bgcolor="#FFFFFF" text="#000000" topmargin="5">
    <form name="base" method = "POST" target="_self">
    <input name="id" type="text" value="" size="30">
    <a href = "JavaScript:Submit()">传给Servlet</a>
    </form></body></html>
    其运行结果如图6所示:

    图6
    4、 JAVA程序和数据库之间

    为避免JAVA程序和数据库之间数据传递出现乱码现象,我们建议采用以下最优方法来处理:
    1、 对于JAVA程序的处理方法按我们指定的方法处理。
    2、 把数据库默认支持的编码格式改为GBK或GB2312的。

    如:在mysql中,我们可以在配置文件my.ini中加入以下语句实现:
    在[mysqld]区增加:
    default-character-set=gbk
    并增加:
    [client]
    default-character-set=gbk
    在SQL Server2K中,我们可以将数据库默认的语言设置为Simplified Chinese来达到目的。

    5、 针对JSP代码

    由于JSP是在运行时,由WEB容器进行动态编译的,如果我们没有指定JSP源文件的编码格式,则JSP编译器会获得服务器操作系统的 file.encoding值来对JSP文件编译的,它在移植时最容易出问题,如在中文win2k中可以很好运行的jsp文件拿到英文linux中就不行,尽管客户端都是一样的,那是因为容器在编译JSP文件时获取的操作系统的编码不同造成的(在中文wink中的file.encoding和在英文 Linux中file.encoding是不同的,且英文Linux的file.encoding对中文不支持,所以编译出来的JSP类就会有问题)。网络上讨论的大多数是此类问题,多是因为JSP文件移植平台时不能正确显示的问题,对于这类问题,我们了解了JAVA中程序编码转换的原理,解决起来就容易多了。我们建议的解决办法如下:

    1、我们要保证JSP向客户端输出时是采用中文编码方式输出的,即无论如何我们首先在我们的JSP源代编中加入以下一行:

    <%@page contentType="text/html; charset=gb2312"%>
    2、为了让JSP能正确获得传入的参数,我们在JSP源文件头加入下面一句:
    <%request.setCharacterEncoding("GB2312");%>
    3、为了让JSP编译器能正确地解码我们的含有中文字符的JSP文件,我们需要在JSP源文件中指定我们的JSP源文件的编码格式,具体来说,我们在JSP源文件头上加入下面的一句即可:
    <%@page pageEncoding="GB2312"%>或<%@page pageEncoding="GBK"%>
    这是JSP规范2.0新增加的指令。
    我们建议使用此方法来解JSP文件中的中文问题,下面的代码是一个正确做法的JSP文件的测试程序:

//testchinese.jsp
    <%@page pageEncoding="GB2312"%>
    <%@page contentType="text/html; charset=gb2312"%>
    <%request.setCharacterEncoding("GB2312");%>
    <%
    String action = request.getParameter("ACTION");
    String name = "";
    String str = "";
    if(action!=null && action.equals("SENT"))
    {
    name = request.getParameter("name");
    str = request.getParameter("str");
    }
    %>
    <html>
    <head>
    <title></title>
    <Script language="JavaScript">
    function Submit()
    {
    document.base.action = "?ACTION=SENT&str=传入的中文";
    document.base.method = "POST";
    document.base.submit();
    }
    </Script>
    </head>
    <body bgcolor="#FFFFFF" text="#000000" topmargin="5">
    <form name="base" method = "POST" target="_self">
    <input type="text" name="name" value="" size="30">
    <a href = "JavaScript:Submit()">提交</a>
    </form>
    <%
    if(action!=null && action.equals("SENT"))
    {
    out.println("<br>你输入的字符为:"+name);
    out.println("<br>你通过URL传入的字符为:"+str);
    }
    %>
    </body>
    </html>
    如图7是此程序运行的结果示意图:

    图7

    5、总结

    在上面的详细分析中,我们清晰地给出了JAVA在处理源程序过程中的详细转换过程,为我们正确解决JAVA编程中的中文问题提供了基础。同时,我们给出了认为是最优的解决JAVA中文问题的办法。

我的补充(需要特别注意): 在表单(form )提交时,如果提交的方法为get,那么request.setCharacterEncoding() 是不起作用的。此时在服务器端得到的字符编码仍然是ISO8859-1,而不是你设置的编码。所以一般我们在提交数据时,尽量使用post方法,此时 request.setCharacterEncoding()方法起效。

 如果非要使用get方法传form则要转换一下才行: 
  -----  
  <%@   page   contentType="text/html;charset=gb2312"%>  
  <%!  
          public   String   getStr(String   str){  
  try{  
  String   temp_p=str;  
  byte[]   temp_t=temp_p.getBytes("ISO8859-1");  
  String   temp=new   String(temp_t);  
  return   temp;  
  }  
  catch(Exception   e){  
  }  
  return   "null";  
    }  
    %>  
  然后把String   userId=request.getParameter("userId");改成  
  String   userId=getStr(request.getParameter("userId"));  
--------------------------------------------
我来说一下tomcat如何实现JSP的你就明白了。
预备知识:
 1.字节和unicode
  Java内核是unicode的,就连class文件也是,但是很多媒体,包括文件/流的保存方式
  是使用字节流的。 因此Java要对这些字节流经行转化。char是unicode的,而byte是字节.
  Java中byte/char互转的函数在sun.io的包中间有。其中ByteToCharConverter类是中调度,
  可以用来告诉你,你用的Convertor。其中两个很常用的静态函数是
   public static ByteToCharConverter getDefault() ;
   public static ByteToCharConverter getConverter(String encoding);
  如果你不指定converter,则系统会自动使用当前的Encoding,GB平台上用GBK,EN平台上用
  8859_1
  
  我们来就一个简单的例子:
     "你"的gb码是:0xC4E3 ,unicode是0x4F60
     你用:
     --encoding="gb2312";
     --byte b[]={(byte)'\u00c4',(byte)'\u00E3'};
     --convertor=ByteToCharConverter.getConverter(encoding);
     --char [] c=converter.convertAll(b);
     --for(int i=0;i<c.length;c++)
     --{
     -- System.out.println(Integer.toHexString(c[i]));
     --}
     --打印出来是0x4F60
     --但是如果使用8859_1的编码,打印出来是
     --0x00C4,0x00E3
     ----例1
     反过来:
      --encoding="gb2312";
      --char c[]={'\u4F60'};
     --convertor=ByteToCharConverter.getConverter(encoding);
     --byte [] b=converter.convertAll(c);
     --for(int i=0;i<b.length;c++)
     --{
     -- System.out.println(Integer.toHexString(b[i]));
     --}
      --打印出来是:0xC4,0xE3
      ----例2
      --如果用8859_1就是0x3F,?号,表示无法转化      --
      很多中文问题就是从这两个最简单的类派生出来的。而却有很多类  
  不直接支持把Encoding输入,这给我们带来诸多不便。很多程序难得用encoding
  了,直接用default的encoding,这就给我们移植带来了很多困难
  --
  2.UTF-8
  --UTF-8是和Unicode一一对应的,其实现很简单
  --
  -- 7位的Unicode: 0 _ _ _ _ _ _ _
  --11位的Unicode: 1 1 0 _ _ _ _ _ 1 0 _ _ _ _ _ _
  --16位的Unicode: 1 1 1 0 _ _ _ _ 1 0 _ _ _ _ _ _ 1 0 _ _ _ _ _ _
  --21位的Unicode: 1 1 1 1 0 _ _ _ 1 0 _ _ _ _ _ _ 1 0 _ _ _ _ _ _ 1 0 _ _ _ _ _ _
  --大多数情况是只使用到16位以下的Unicode:
  --"你"的gb码是:0xC4E3 ,unicode是0x4F60
  --我们还是用上面的例子
  --  --例1:0xC4E3的二进制:
  --  --    1 1 0 0 0 1 0 0 1 1 1 0 0 0 1 1
  --  --    由于只有两位我们按照两位的编码来排,但是我们发现这行不通,
  --  --    因为第7位不是0因此,返回"?"
  --  --   
  --  --例2:0x4F60的二进制:
  --  --    0 1 0 0 1 1 1 1 0 1 1 0 0 0 0 0
  --  --    我们用UTF-8补齐,变成:
  --  --    11100100 10111101 10100000
  --  --    E4--BD-- A0
  --  --    于是返回0xE4,0xBD,0xA0
  --  --
  3.String和byte[]
  --String其实核心是char[],然而要把byte转化成String,必须经过编码。
  --String.length()其实就是char数组的长度,如果使用不同的编码,很可
  --能会错分,造成散字和乱码。
  --例:
  ----byte [] b={(byte)'\u00c4',(byte)'\u00e3'};
  ----String str=new String(b,encoding);  ----
  ----如果encoding=8859_1,会有两个字,但是encoding=gb2312只有一个字  ----
  --这个问题在处理分页是经常发生
  4.Reader,Writer/InputStream,OutputStream
  --Reader和Writer核心是char,InputStream和OutputStream核心是byte。
  --但是Reader和Writer的主要目的是要把Char读/写InputStream/OutputStream
--一个reader的例子:
--文件test.txt只有一个"你"字,0xC4,0xE3--
--String encoding=;
--InputStreamReader reader=new InputStreamReader(
----new FileInputStream("text.txt"),encoding);
--char []c=new char[10];
--int length=reader.read(c);
--for(int i=0;i<c.length;i++)
----System.out.println(c[i]);
  --如果encoding是gb2312,则只有一个字符,如果encoding=8859_1,则有两个字符
  --------
--
--
  
   ----
 2.我们要对Java的编译器有所了解:
 --javac -encoding
  我们常常没有用到ENCODING这个参数。其实Encoding这个参数对于跨平台的操作是很重要的。
  如果没有指定Encoding,则按照系统的默认Encoding,gb平台上是gb2312,英文平台上是ISO8859_1。 
 --Java的编译器实际上是调用sun.tools.javac.Main的类,对文件进行编译,这个类 --
 有compile函数中间有一个encoding的变量,-encoding的参数其实直接传给encoding变量。
 编译器就是根据这个变量来读取java文件的,然后把用UTF-8形式编译成class文件。
 一个例子:
 --public void test()
 --{
 ----String str="你";
 ----FileWriter write=new FileWriter("test.txt");
 ----write.write(str);
 ----write.close();
 --}
 ----例3
--如果用gb2312编译,你会找到E4 BD A0的字段
--
--如果用8859_1编译,
--00C4 00E3的二进制:
--00000000 11000100 00000000 11100011--
--因为每个字符都大于7位,因此用11位编码:
--11000001 10000100 11000011 10100011
--C1-- 84-- C3--  A3
--你会找到C1 84 C3 A3 --
    
  但是我们往往忽略掉这个参数,因此这样往往会有跨平台的问题:
  --  例3在中文平台上编译,生成ZhClass
  --  例3在英文平台上编译,输出EnClass
  --1.  ZhClass在中文平台上执行OK,但是在英文平台上不行
  --2.  EnClass在英文平台上执行OK,但是在中文平台上不行
  原因:
 --1.在中文平台上编译后,其实str在运行态的char[]是0x4F60, ----
 --在中文平台上运行,FileWriter的缺省编码是gb2312,因此
 --CharToByteConverter会自动用调用gb2312的converter,把str转化
 --成byte输入到FileOutputStream中,于是0xC4,0xE3放进了文件。
 --但是如果是在英文平台下,CharToByteConverter的缺省值是8859_1,
 --FileWriter会自动调用8859_1去转化str,但是他无法解释,因此他会
 --输出"?" ----
 --2. 在英文平台上编译后,其实str在运行态的char[]是0x00C4 0x00E3, ----
 --在中文平台上运行,中文无法识别,因此会出现??
 --  在英文平台上,0x00C4-->0xC4,0x00E3->0xE3,因此0xC4,0xE3被放进了
 --文件
----
1.对于JSP正文的解释:
--Tomcat首先看一下你的叶面中有没有"<%@page include的符号。有,则在相同
--地方设定response.setContentType(..);按照encoding的来读,没有他按照8859_1
--读取文件,然后用UTF-8写成.java文件,然后用sun.tools.Main去读取这个文件,
--(当然它使用UTF-8去读),然后编译成class文件
--setContentType改变的是out的属性,out变量缺省的encoding是8859_1
2.对Parameter的解释
--很不幸Parameter只有ISO8859_1的解释,这个质料可以在servlet的实现代码中找到。
3.对include的解释
格式的,但是很不幸,由于那个写"org.apache.jasper.compiler.Parser"的人
在数组JspUtil.ValidAttribute[]忘记加了一个参数:encoding,因此导致不支
持这种方式。你完全可以编译源代码,加上对encoding的支持
总结:
如果你在NT底下,最简单的方法就是欺骗java,不加任何Encoding变量:
<html>
你好<%=request.getParameter("value")%>
</html>
http://localhost/test/test.jsp?value=你
结果:你好你
但这种方法局限性较大,比如对上传的文章分段,这样的做法是死定的,最好的
解决方案是用这种方案:
<%@ page contentType="text/html;charset=gb2312" %>
<html>
你好<%=new String(request.getParameter("value").getBytes("8859_1"),"gb2312")%>
</html>

<select name="account.accountId" >
    <OPTION value="<%=account.getAccountId()%>">我的日志</OPTION>
    <OPTION value="">所有日志</OPTION>
    <OPTION <%=s%> value="<%=a.getAccountId()%>"><%=a.getAccountName()%></OPTION>
   </select>
posted @ 2006-12-31 10:13 保尔任 阅读(311) | 评论 (0)编辑 收藏
 
  J2EE学习者越来越多,J2EE本身技术不断在发展,涌现出各种概念,本文章试图从一种容易理解的角度对这些概念向初学者进行解释,以便掌握学习J2EE学习方向。
  首先我们需要知道Java和J2EE是两个不同概念,Java不只是指一种语言,已经代表与微软不同的另外一个巨大阵营,所以Java有时是指一种软件系统的流派,当然目前主要是.NET和Java两大主流体系。
  J2EE可以说指Java在数据库信息系统上实现,数据库信息系统从早期的dBase、到Delphi/VB等C/S结构,发展到B/S(Browser浏览器/Server服务器)结构,而J2EE主要是指B/S结构的实现。
  J2EE又是一种框架和标准,框架类似API、库的概念,但是要超出它们。如果需要详细了解框架,可先从设计模式开始学习。
  J2EE是一个虚的大的概念,J2EE标准主要有三种子技术标准:WEB技术、EJB技术和JMS,谈到J2EE应该说最终要落实到这三个子概念上。
  这三种技术的每个技术在应用时都涉及两个部分:容器部分和应用部分,Web容器也是指Jsp/Servlet容器,你如果要开发一个Web应用,无论是编译或运行,都必须要有Jsp/Servlet库或API支持(除了JDK/J2SE以外)。
  Web技术中除了Jsp/Servlet技术外,还需要JavaBeans或Java Class实现一些功能或者包装携带数据,所以Web技术最初裸体简称为Jsp/Servlet+JavaBeans系统。
  谈到JavaBeans技术,就涉及到组件构件技术(component),这是Java的核心基础部分,很多软件设计概念(设计模式)都是通过JavaBeans实现的。
  JavaBeans不属于J2EE概念范畴中,如果一个JavaBeans对象被Web技术(也就是Jsp/Servlet)调用,那么JavaBeans就运行在J2EE的Web容器中;如果它被EJB调用,它就运行在EJB容器中。
  EJB(企业JavaBeans)是普通JavaBeans的一种提升和规范,因为企业信息系统开发中需要一个可伸缩的性能和事务、安全机制,这样能保证企业系统平滑发展,而不是发展到一种规模重新更换一套软件系统。
  至此,JavaBeans组件发展到EJB后,并不是说以前的那种JavaBeans形式就消失了,这就自然形成了两种JavaBeans技术:EJB 和POJO,POJO完全不同于EJB概念,指的是普通JavaBeans,而且这个JavaBeans不依附某种框架,或者干脆可以说:这个 JavaBeans是你为这个应用程序单独开发创建的。
  J2EE应用系统开发工具有很多:如JBuilder、 Eclipse等,这些IDE首先是Java开发工具,也就是说,它们首要基本功能是可以开发出JavaBeans或Java class,但是如果要开发出J2EE系统,就要落实到要么是Web技术或EJB技术,那么就有可能要一些专门模块功能(如eclipse需要 lomboz插件),最重要的是,因为J2EE系统区分为容器和应用两个部分,所以,在任何开发工具中开发J2EE都需要指定J2EE容器。
  J2EE容器分为WEB容器和EJB容器,Tomcat/Resin是Web容器;JBoss是EJB容器+Web容器等,其中Web容器直接使用 Tomcat实现的。所以你开发的Web应用程序可以在上面两种容器运行,而你开发的Web+EJB应用则只可以在JBoss服务器上运行,商业产品 Websphere/Weblogic等和JBoss属于同一种性质。
  J2EE容器也称为J2EE服务器,大部分时它们概念是一致的。
  如果你的J2EE应用系统的数据库连接是通过JNDI获得,也就是说是从容器中获得,那么你的J2EE应用系统基本与数据库无关,如果你在你的J2EE 应用系统耦合了数据库JDBC驱动的配置,那么你的J2EE应用系统就有数据库概念色彩,作为一个成熟需要推广的J2EE应用系统,不推荐和具体数据库耦合,当然这其中如何保证J2EE应用系统运行性能又是体现你的设计水平了。
  衡量J2EE应用系统设计开发水平高低的标准就是:解耦性;你的应用系统各个功能是否能够彻底脱离?是否不相互依赖,也只有这样,才能体现可维护性、可拓展性的软件设计目标。
  为了达到这个目的,诞生各种框架概念,J2EE框架标准将一个系统划分为WEB和EJB主要部分,当然我们有时不是以这个具体技术区分,而是从设计上抽象为表现层、服务层和持久层,这三个层次从一个高度将J2EE分离开来,实现解耦目的。
  因此,我们实际编程中,也要将自己的功能向这三个层次上靠,做到大方向清楚,泾渭分明,但是没有技术上约束限制要做到这点是很不容易的,因此我们还是必须借助J2EE具体技术来实现,这时,你可以使用EJB规范实现服务层和持久层,Web技术实现表现层;
  EJB为什么能将服务层从Jsp/Servlet手中分离出来,因为它对JavaBeans编码有强制的约束,现在有一种对JavaBeans弱约束,使用Ioc模式实现的(当然EJB 3.0也采取这种方式),在Ioc模式诞生前,一般都是通过工厂模式来对JavaBeans约束,形成一个服务层,这也是是Jive这样开源论坛设计原理之一。
  由此,将服务层从表现层中分离出来目前有两种可选架构选择:管理普通JavaBeans(POJO)框架(如 Spring、JdonFramework)以及管理EJB的EJB框架,因为EJB不只是框架,还是标准,而标准可以扩展发展,所以,这两种区别将来是可能模糊,被纳入同一个标准了。 但是,个人认为:标准制定是为某个目的服务的,总要牺牲一些换取另外一些,所以,这两种架构会长时间并存。
  这两种架构分歧也曾经诞生一个新名词:完全POJO的系统也称为轻量级系统(lightweight),其实这个名词本身就没有一个严格定义,更多是一个吸引人的招牌,轻量是指容易学习容易使用吗?按照这个定义,其实轻量Spring等系统并不容易学习;而且EJB 3.0(依然叫EJB)以后的系统是否可称为轻量级了呢?
  前面谈了服务层框架,使用服务层框架可以将 JavaBeans从Jsp/Servlet中分离出来,而使用表现层框架则可以将Jsp中剩余的JavaBeans完全分离,这部分JavaBeans 主要负责显示相关,一般是通过标签库(taglib)实现,不同框架有不同自己的标签库,Struts是应用比较广泛的一种表现层框架。
  这样,表现层和服务层的分离是通过两种框架达到目的,剩余的就是持久层框架了,通过持久层的框架将数据库存储从服务层中分离出来是其目的,持久层框架有两种方向:直接自己编写JDBC等SQL语句(如iBatis);使用O/R Mapping技术实现的Hibernate和JDO技术;当然还有EJB中的实体Bean技术。
  持久层框架目前呈现百花齐放,各有优缺点的现状,所以正如表现层框架一样,目前没有一个框架被指定为标准框架,当然,表现层框架现在又出来了一个JSF,它代表的页面组件概念是一个新的发展方向,但是复杂的实现让人有些忘而却步。
  在所有这些J2EE技术中,虽然SUN公司发挥了很大的作用,不过总体来说:网络上有这样一个评价:SUN的理论天下无敌;SUN的产品用起来撞墙;对于初学者,特别是那些试图通过或已经通过SUN认证的初学者,赶快摆脱SUN的阴影,立即开溜,使用开源领域的产品来实现自己的应用系统。
  最后,你的J2EE应用系统如果采取上面提到的表现层、服务层和持久层的框架实现,基本你也可以在无需深刻掌握设计模式的情况下开发出一个高质量的应用系统了。
  还要注意的是: 开发出一个高质量的J2EE系统还需要正确的业务需求理解,那么域建模提供了一种比较切实可行的正确理解业务需求的方法,相关详细知识可从UML角度结合理解。
  当然,如果你想设计自己的行业框架,那么第一步从设计模式开始吧,因为设计模式提供你一个实现JavaBeans或类之间解耦参考实现方法,当你学会了系统基本单元JavaBean或类之间解耦时,那么系统模块之间的解耦你就可能掌握,进而你就可以实现行业框架的提炼了,这又是另外一个发展方向了。
  以上理念可以总结为一句话:Java学习开发三件宝: Domain Model(域建模)、Patterns(模式)和Framework(框架)。集三宝理念于一身,小中型J2EE项目快速开发工具:Jdon Framework
----------------------------------------------------------------------------------------------------
JoannaYe ask:
你好 Banq先生 关注你的文章很长一段时间了, 对你在Java领域的技术水平,以及在很多问题上的看法, 也非常佩服. 国内目前达到你的水平的人真是很少(当然高人也许都隐居起来了). 但是, 有几个问题想与你讨论:
首先,软件是一个绝对的应用技术,任何技术离开了具体的应用, 坦率地说是毫无价值的.我看,Jdon也有在这方面的尝试,如网站,网上商店生成系统等.但这与真正的企业应用还有非常大的距离. 我不了解,你在这一领域里为什么没有涉足,是因为你认为很困难,基本上是以我们国内目前的技术水平无法到达呢, 还是因为你不屑于这方面的深入, 认为你所追求的是纯粹超然的技术概念呢.
我的其他问题有赖于了解你关于这个问题的回答,让我们继续关注和讨论.
banq answer:
 
>但这与真正的企业应用还有非常大的距离. 我不了解,你在这一领域里为什么没有涉足,是因为你认为很困难,基本上是以我们国内目前的技术
多谢探讨,这个问题很复杂,大概有下列几点:
1. 现在软件技术不再象以前的技术,以前的技术可以说只有做个这个行业大型软件系统的经验的人才可以说对这些软件技术有掌握,而现在的技术则不必了,J2EE 讲究架构,J2EE它是一套应用软件的规范,也就是说,J2EE是很多做过大型软件的人进行汇总后的经验精华,一个大型系统需要哪些技术部分、什么时候适合什么技术,在J2EE标准中基本都有涉及,例如EJB技术、JMS等。
这样,如果你能完全掌握和驾驭这些J2EE架构技术,你有时确实不必一定要做个大型软件经验才型,这称为站在巨人的肩膀上。
但是反过来,如果你没有丰富的软件系统实战经验,你去理解EJB/JMS等就很困难,所以这两个技术对初学者比较难的原因之一。
2. UML结合J2EE这样OO一套实施过程从方法论以及模式角度固化了软件数据库系统的分析设计开发,这也是因为有MDA(将这些过程用软件自动生成代码)诞生的原因。虽然这些简化了我们开发系统的过程,但是这只是解决了应用系统的一部分问题,工作流等尚未成熟,使用这样方式开发系统,依据我的经验,最后会将烦琐和细致的工作压在Jsp页面上,目前开发一个系统,结合标签库和用户界面需求这个工作反而花费我更多时间,希望JSF在这方面能有效率提升,等这些技术细节都能解决,基本J2EE非常成熟了。
3.目前我通过咨询角色和一些软件公司一起承接一些企业应用项目,例如去年承接一个大型外资人事系统,他们要求管理GE 等几家外资企业的人事福利(这些企业外包人事给他们),如果专为一家公司开发人事很简单,但是要求这个人事适合多家,那么重用性要求很高,设计抽象面很高,他们在新加坡有类似系统,但技术很老,我听过新加坡的系统,他们也有一些经验总结,大部分和我的J2EE设计相吻合,我和新加坡的人交流过想法,他们很惊奇,不太相信,加上费用问题,只进行了初步架构设计就搁浅了。
4.不要小看网站系统,以前网站系统都是用PHP Perl做,功能很弱,无法和企业系统相比,但是随着Inernet普及,更多人要求联网,例如如果一家公司的ERP通过互联网实现,那么老总出差就很方便,但是现在为一家公司开发一个基于internet的ERP很贵,比传统的贵,这不合理,这也是SOA提出的目的之一,以后ERP实现网上租用,就象你申请一个Blog或论坛或Email,你可以为你的企业申请一个ERP系统,这样只要企业付租费就可以了,这可理想目前已经接近,前段时间美国一家提供这种服务的企业来上海做宣传,他们的业绩增长速度极其快 500%.
通过网站提供ERP等企业服务对于软件设计的重用性要求很高,就一套邮箱系统可以服务很多用户一样,你必须设计出一套重要性、灵活性很高的ERP系统适合不同的用户,可见网站软件的水平是极其高的。前面我做的网站自动生成系统到现在我都认为完成不够好,现在很多网站都提供这种服务,这象Blog,但是Blog等只限制你网站模板,而不是自由定制页面,所以 Blog这些都是小孩玩家家,根本无发走向商业,著名的那个方兴东鼓吹Blog,其实没有技术革新,靠你媒体吹呼就是革命了。
 
JoannaYe ask:
谢谢 Banq 先生在6月23日非常认真的回复(抱歉由于忙,没能马上回复). 总结起来, 如果我的理解不错的话, 你的结论是 1)你认为网站系统并不可小觑(同意,一个高访问量,同时能够实现网上交易的网站的确如此).2)EJB/JMS技术对于初学者来说是不容易,但是对你来说,你是可以Handdle的. 3)你也有承接企业系统的实际经验,象你说的那个HR系统. 但不知您以咨询身份参加的这个HR系统到底都解决的是实际管理中的什么样的问题?在性能方面都达到了什么样的水平? 具体来说,采用了哪些技术(诸如您帖中提到的一些技术)应对了实际中具体的什么样的问题. 此外以你在这个HR系统的经验来看, 是一个多少人的Team,采取什么样的开发方式和开发进度(人员和时间的分配比例)开发的.你认为在这样的一个项目的开发过程中最关键的是什么?最影响 Prductivity的又是什么?
对这样一些问题看上去似乎很空泛,但是实际上能够真正反映出我一开始提出的 issue,"软件是一个绝对的应用技术,任何技术离开了具体的应用, 坦率地说是毫无价值的".举个例子来说,书本上,名家们会告诉你, Value List Handler 这个设计模式是解决这样的问题:"You have a remote client that wants to iterate over a large results list." 一般来说,如果是一个大量地查找某一些"topic/dimension"下的数据,这样的问题,我们也毫无疑问地确定要用到这个模式.但是,如果对一条具体的数据,如某一个销售员,要和他的客户讨论(在线谈判)他们之间的一个具体合同,这时候会不会也需要用到这个模式.如果要用这个模式,到底是用 Stateful Session Bean 还是用 Stateless Session Bean 实现呢,他们各自在实现方法上对性能的影响是什么, 当你决定采用了某种实现方法,你到底是怎样Tradeoff的呢; 最后对这个设计模式来说,在最终的设计方案中如何把它抽象到对一个通用的,普遍的业务问题,而不是仅仅对"某一个销售员,要和他的客户讨论他们之间的一个具体合同"这样的一个特例问题,作出一个通用的解决方案,适应任意规模,任意业务的企业,真正达到软件工程的目标:高度的Reusing 和 Scalablity. 实际的企业应用系统就是充满着类似这样的问题,很有挑战.但有些技术人员就仅仅满足于自己可以用某项技术做出一些小的Demo了,就不愿意,或根本不屑于深入下去面对一个实际的应用问题.
因此, 我相信您应该能够非常理解,我为什么感兴趣了解您对我上面提出问题答案.
您的很多看法都很不错, 我非常同意, 希望我们能在今后进一步深入地探讨. 谢谢!
banq answer:
>你认为在这样的一个项目的开发过程中最关键的是什么?最影响 Prductivity的又是什么?
当这样的项目使用框架组件组合后,由于系统重要重用的功能已经封装在框架软件中,所以,只要能够组装出应用系统,一般第一次测试就会立即通过,我已经不止一次体会这种快感,我现在基本告别以前那种花费大量时间在Java调试上时代,我相信很多初学者还在这个泥潭里挣扎,这就成为影响一个产品的主要原因,现在使用jdon框架开发,几乎消灭这个因素。
那么,现在最影响 Prductivity的是什么?就是技术外的因素:项目管理。
关于你提的性能方面设计达到什么水平等,这些我已经整合进入Jdon框架,使用Jdon框架开发,几乎无需考虑性能设计,一开始就具有优越的性能,这些都是有测试数据,Java产品的好处就是一切可以自己动手,不必听从第三方评价,因为那些都有失公正,服务器配置上Jprofile/Optimizeit,客户端配置Jmeter,启动几个线程一跑,Jdon框架和应用程序的性能真相就出来了,所以,在Java领域,开源和商业产品是在同一起跑线,面对不同的用户:前者是更有头脑,自己动手;后者是对自己缺乏自信的人;服务是两者的重点。
>在最终的设计方案中如何把它抽象到对一个通用的,普遍的业务问题,而不>是仅仅对"某一个销售员,要和他的客户讨论他们之间的一个具体合同"这>样的一个特例问题
其实你说的行业框架提炼的问题,这和业务相关,Jdon框架等都是基础框架,没有这些组件框架的优雅解决方式,就没有行业框架的好的开始,我想你不希望在行业框架提炼之后,发现无法加入一些纵向功能,就象数据库设计好之后,几年以后却成为你发展的障碍。
行业框架需要资深的行业背景,这也不是一般人做的,但是工作流/Portal等都是行业框架的提炼,这些也是我们以后发展的方向。
就我个人来说,我愿意解决重要问题,然后我告诉更多人解决方向,如果他们相信,大家一起努力来解决所有问题,而不是靠我一个人解决所有问题。
JoannaYe ask:
谢谢Banq先生的回复, 你的很多观点都很好,我非常同意.象你所说最影响Prductivity的是技术外的因素:项目管理. 但我不知你能不能有一些具体的看法.因为任何行业,最终的问题, 竞争力的问题都是如何通过管理来提高Prductivity. 不知你对软件这一行业有没有特别的见解.
开源项目的确有它的优势,特别是作这些开源项目的人,往往是一些技术的精英.但是, 我还是以为应该以成熟的Commercial产品作为自己开发的基础,即所谓"巨人的肩膀". 这是因为, 成功的Commercial产品往往更注重最终用户, 这是这些产品能够给它的公司带来巨大的商业利润的源泉.纯技术的专家往往会忽视这一点.
要成就一件事(一个大型企业管理应用的项目), 是需要很多人踏踏实实,坚持不懈(这也非常重要)的努力.这和去上上课,或者在场外指导一下,有很大的区别.
我希望通过你这个论坛, 结识一些志同道合的朋友, 能够作成这样一件事.再次谢谢你的回复, 我因为很多时候很忙,有一些Deadline非常紧的事情,有时没能马上给您回复, 请你见谅.
banq answer:
非常感谢JoannaYe 讨论,从言论中感觉你是一个职业的项目经理。非常专业。
项目经理和设计师良好沟通和理解交流,是一个项目成功的关键。
关于开源和Commercial区别,我个人觉得它们之间真的没有严格的区别,只不过是两种思路的表现:开源通过免费产品卖服务;Commercial是既想卖产品又卖服务,不能因为它的产品卖钱,就是技术好,一般是市场品牌好。
就拿EJB实现来说,在所有J2EE服务器中只有开源JBoss 4.0使用AOP实现,坚持AOP的一些纯设计派认为EJB过时了,那么Weblogic /Websphere等这些以支持EJB自诩的服务器产品反而不如开源产品呢。这些人认为:EJB
但是正如你说:为什么客户还是购买Websphere等服务器,因为它们注重最终用户。
我认为一直试图在这两者之间寻找平衡是挑战的事情。
-------------------------------------------------------------------------------------------------------------------
 
shuiwx ask:
 
banq老师好,最近大致抽象总结出了一个比较浅显的规律,既是您平均一两个月能够发一篇比较的适合初学者的帖子,但每一篇都可以对偶的有关知识的梳理和导向能够起到很重要的作用,不敢说终生受用但也似乎会持久难忘了,在此还是要道一声感谢。
既然题目是初学者...高质量的J2EE系统,那么就题目本身这个用例来说,参与者该是“novice”了,领域模型应该是"高质量的"+"J2EE系统 ",那么能否请您再深一步的举个样例来说明何为"high quality j2ee system"呢?估计您不会选petshop,但有可能会将jive和jdon算进来,但偶真正想看到的是一个就您个人来讲曾经有过 consultant经验的项目作为例子来简要阐述下高质量+j2ee系统的概貌,或者象您前面某篇论oo和数据库的矛盾的文章一样,能否前瞻性的给出一个在您心目中最理想的高质量j2ee系统的轮廓呢?比如jsf(new version>1.1)+ejb3.0+j2ee设计模式?偶觉得struts+spring+hibernate并不敢称为高质量的或是 j2ee系统,所以总觉得从现在开始既该有意识的用一下jsf+ejb3来设计了,但由于不知道有没有人在这方面开始吃螃蟹了,所以只好去随大流的关心些什么ajax,xp之类的流行名词了。但从内心来讲,无论是javascript还是组件式编程,无论是spring+hibernate还是ejb3, 无论是xp还是fdd,无非是想尽量按照客户的要求迅速提交一个界面新颖,结构稳定的一个能够跨平台的良好的系统吧。假如能预知何为一个好的系统的话,似乎事情会变的简单些,也就不必为那些喋喋不休的争论着技术名词的人们所困扰了。
但由于目前偶的能力所限和所处的时期的特殊性,似乎想马上就拿jsf+ejb3来首选做企业级开发还有点不现实,那么作为一个apprentice来说,能够做的似乎只有学习模式了,偶不知道关于模式该学到多深才合适,只相信尽量选择从建模的时候就配合着设计模式来考虑可能会有助于系统的稳定和重用,谈到这里有引申出关于题目的另外一个话题,就是 “初学者”,偶觉得如果想作为计算机编程人员的话,面对着不停的新技术名词和版本更迭,似乎偶总要做一名初学着来的说,于是最近有意识的在看一些数据结构方面的课程,希望能够从一些理论基础中来寻找那些所谓的新技术背后所蕴涵的知识,但还是那句话,由于能力有限,所得甚浅,所以希望您如果能站在一个咨询家的角度来看,能否指点一下,就您认为的如果想设计一个好的软件系统来说,或许不仅限于j2ee,该多看看哪些computer science中的理论知识呢?偶不知道这个问题提的对不对,但总觉得设计模式对于系统的意义,是类似于数据结构和算法之相对于程序的意义的,所以假如您在类似的方面能有些心得的话,希望能够得到一点指点。
(偶的废话似乎不少,希望banq老师能忍受)
 
banq answer:
很抱歉现在才回复你的问题:
>如果想设计一个好的软件系统来说,或许不仅限于j2ee,该多看看哪些>computer science中的理论知识
设计一个好的软件系统我文章里其实写了,掌握分层解耦宗旨,学习使用一些现成的框架就可以了,如果你不原意囫囵吞枣,那么研究一下这些框架设计原理和模式,这些会花费你很长探索,数据结构、编译原理这些已经成为底层机制,就象汇编是底层一样,现在的大学计算机教学完全是错误的,学习的都是正确的废话。所以没有必要在那些大学课程上浪费时间。
增强项目经验,研读源码,自己动手编写项目是提升水平的唯一道路。
以上只是我个人意见。
posted @ 2006-12-31 10:11 保尔任 阅读(422) | 评论 (0)编辑 收藏
 

作者:郎云鹏(dev2dev ID: hippiewolf)

摘要:虽然session机制在web应用程序中被采用已经很长时间了,但是仍然有很多人不清楚session机制的本质,以至不能正确的应用这一技术。本文将详细讨论session的工作机制并且对在Java web application中应用session机制时常见的问题作出解答。

目录:
一、术语session
二、HTTP协议与状态保持
三、理解cookie机制
四、理解session机制
五、理解javax.servlet.http.HttpSession
六、HttpSession常见问题
七、跨应用程序的session共享
八、总结
参考文档

一、术语session
在我的经验里,session这个词被滥用的程度大概仅次于transaction,更加有趣的是transaction与session在某些语境下的含义是相同的。

session,中文经常翻译为会话,其本来的含义是指有始有终的一系列动作/消息,比如打电话时从拿起电话拨号到挂断电话这中间的一系列过程可以称之为一个 session。有时候我们可以看到这样的话“在一个浏览器会话期间,...”,这里的会话一词用的就是其本义,是指从一个浏览器窗口打开到关闭这个期间 ①。最混乱的是“用户(客户端)在一次会话期间”这样一句话,它可能指用户的一系列动作(一般情况下是同某个具体目的相关的一系列动作,比如从登录到选购商品到结账登出这样一个网上购物的过程,有时候也被称为一个transaction),然而有时候也可能仅仅是指一次连接,也有可能是指含义①,其中的差别只能靠上下文来推断②。

然而当session一词与网络协议相关联时,它又往往隐含了“面向连接”和/或“保持状态”这样两个含义,“面向连接”指的是在通信双方在通信之前要先建立一个通信的渠道,比如打电话,直到对方接了电话通信才能开始,与此相对的是写信,在你把信发出去的时候你并不能确认对方的地址是否正确,通信渠道不一定能建立,但对发信人来说,通信已经开始了。“保持状态”则是指通信的一方能够把一系列的消息关联起来,使得消息之间可以互相依赖,比如一个服务员能够认出再次光临的老顾客并且记得上次这个顾客还欠店里一块钱。这一类的例子有“一个TCP session”或者“一个POP3 session”③。

而到了web服务器蓬勃发展的时代,session在web开发语境下的语义又有了新的扩展,它的含义是指一类用来在客户端与服务器之间保持状态的解决方案 ④。有时候session也用来指这种解决方案的存储结构,如“把xxx保存在session里”⑤。由于各种用于web开发的语言在一定程度上都提供了对这种解决方案的支持,所以在某种特定语言的语境下,session也被用来指代该语言的解决方案,比如经常把Java里提供的 javax.servlet.http.HttpSession简称为session⑥。

鉴于这种混乱已不可改变,本文中session一词的运用也会根据上下文有不同的含义,请大家注意分辨。
在本文中,使用中文“浏览器会话期间”来表达含义①,使用“session机制”来表达含义④,使用“session”表达含义⑤,使用具体的“HttpSession”来表达含义⑥

二、HTTP协议与状态保持
HTTP协议本身是无状态的,这与HTTP协议本来的目的是相符的,客户端只需要简单的向服务器请求下载某些文件,无论是客户端还是服务器都没有必要纪录彼此过去的行为,每一次请求之间都是独立的,好比一个顾客和一个自动售货机或者一个普通的(非会员制)大卖场之间的关系一样。

然而聪明(或者贪心?)的人们很快发现如果能够提供一些按需生成的动态信息会使web变得更加有用,就像给有线电视加上点播功能一样。这种需求一方面迫使 HTML逐步添加了表单、脚本、DOM等客户端行为,另一方面在服务器端则出现了CGI规范以响应客户端的动态请求,作为传输载体的HTTP协议也添加了文件上载、cookie这些特性。其中cookie的作用就是为了解决HTTP协议无状态的缺陷所作出的努力。至于后来出现的session机制则是又一种在客户端与服务器之间保持状态的解决方案。

让我们用几个例子来描述一下cookie和session机制之间的区别与联系。笔者曾经常去的一家咖啡店有喝5杯咖啡免费赠一杯咖啡的优惠,然而一次性消费5杯咖啡的机会微乎其微,这时就需要某种方式来纪录某位顾客的消费数量。想象一下其实也无外乎下面的几种方案:
1、该店的店员很厉害,能记住每位顾客的消费数量,只要顾客一走进咖啡店,店员就知道该怎么对待了。这种做法就是协议本身支持状态。
2、发给顾客一张卡片,上面记录着消费的数量,一般还有个有效期限。每次消费时,如果顾客出示这张卡片,则此次消费就会与以前或以后的消费相联系起来。这种做法就是在客户端保持状态。
3、发给顾客一张会员卡,除了卡号之外什么信息也不纪录,每次消费时,如果顾客出示该卡片,则店员在店里的纪录本上找到这个卡号对应的纪录添加一些消费信息。这种做法就是在服务器端保持状态。

由于HTTP协议是无状态的,而出于种种考虑也不希望使之成为有状态的,因此,后面两种方案就成为现实的选择。具体来说cookie机制采用的是在客户端保持状态的方案,而session机制采用的是在服务器端保持状态的方案。同时我们也看到,由于采用服务器端保持状态的方案在客户端也需要保存一个标识,所以session机制可能需要借助于cookie机制来达到保存标识的目的,但实际上它还有其他选择。

三、理解cookie机制
cookie机制的基本原理就如上面的例子一样简单,但是还有几个问题需要解决:“会员卡”如何分发;“会员卡”的内容;以及客户如何使用“会员卡”。

正统的cookie分发是通过扩展HTTP协议来实现的,服务器通过在HTTP的响应头中加上一行特殊的指示以提示浏览器按照指示生成相应的cookie。然而纯粹的客户端脚本如Javascript或者VBscript也可以生成cookie。

而cookie 的使用是由浏览器按照一定的原则在后台自动发送给服务器的。浏览器检查所有存储的cookie,如果某个cookie所声明的作用范围大于等于将要请求的资源所在的位置,则把该cookie附在请求资源的HTTP请求头上发送给服务器。意思是麦当劳的会员卡只能在麦当劳的店里出示,如果某家分店还发行了自己的会员卡,那么进这家店的时候除了要出示麦当劳的会员卡,还要出示这家店的会员卡。

cookie的内容主要包括:名字,值,过期时间,路径和域。
其中域可以指定某一个域比如.google.com,相当于总店招牌,比如宝洁公司,也可以指定一个域下的具体某台机器比如www.google.com或者froogle.google.com,可以用飘柔来做比。
路径就是跟在域名后面的URL路径,比如/或者/foo等等,可以用某飘柔专柜做比。
路径与域合在一起就构成了cookie的作用范围。
如果不设置过期时间,则表示这个cookie的生命期为浏览器会话期间,只要关闭浏览器窗口,cookie就消失了。这种生命期为浏览器会话期的 cookie被称为会话cookie。会话cookie一般不存储在硬盘上而是保存在内存里,当然这种行为并不是规范规定的。如果设置了过期时间,浏览器就会把cookie保存到硬盘上,关闭后再次打开浏览器,这些cookie仍然有效直到超过设定的过期时间。

存储在硬盘上的cookie可以在不同的浏览器进程间共享,比如两个IE窗口。而对于保存在内存里的cookie,不同的浏览器有不同的处理方式。对于 IE,在一个打开的窗口上按Ctrl-N(或者从文件菜单)打开的窗口可以与原窗口共享,而使用其他方式新开的IE进程则不能共享已经打开的窗口的内存 cookie;对于Mozilla Firefox0.8,所有的进程和标签页都可以共享同样的cookie。一般来说是用javascript的window.open打开的窗口会与原窗口共享内存cookie。浏览器对于会话cookie的这种只认cookie不认人的处理方式经常给采用session机制的web应用程序开发者造成很大的困扰。

下面就是一个goolge设置cookie的响应头的例子
HTTP/1.1 302 Found
Location: http://www.google.com/intl/zh-CN/
Set-Cookie: PREF=ID=0565f77e132de138:NW=1:TM=1098082649:LM=1098082649:S=KaeaCFPo49RiA_d8; expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.google.com
Content-Type: text/html


这是使用HTTPLook这个HTTP Sniffer软件来俘获的HTTP通讯纪录的一部分


浏览器在再次访问goolge的资源时自动向外发送cookie


使用Firefox可以很容易的观察现有的cookie的值
使用HTTPLook配合Firefox可以很容易的理解cookie的工作原理。


IE也可以设置在接受cookie前询问


这是一个询问接受cookie的对话框。

四、理解session机制
session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息。

当程序需要为某个客户端的请求创建一个session的时候,服务器首先检查这个客户端的请求里是否已包含了一个session标识 - 称为session id,如果已包含一个session id则说明以前已经为此客户端创建过session,服务器就按照session id把这个session检索出来使用(如果检索不到,可能会新建一个),如果客户端请求不包含session id,则为此客户端创建一个session并且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个session id将被在本次响应中返回给客户端保存。

保存这个session id的方式可以采用cookie,这样在交互过程中浏览器可以自动的按照规则把这个标识发挥给服务器。一般这个cookie的名字都是类似于 SEEESIONID,而。比如weblogic对于web应用程序生成的cookie,JSESSIONID= ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764,它的名字就是 JSESSIONID。

由于 cookie可以被人为的禁止,必须有其他机制以便在cookie被禁止时仍然能够把session id传递回服务器。经常被使用的一种技术叫做URL重写,就是把session id直接附加在URL路径的后面,附加方式也有两种,一种是作为URL路径的附加信息,表现形式为http://...../xxx; jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
另一种是作为查询字符串附加在URL后面,表现形式为http://...../xxx?jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
这两种方式对于用户来说是没有区别的,只是服务器在解析的时候处理的方式不同,采用第一种方式也有利于把session id的信息和正常程序参数区分开来。
为了在整个交互过程中始终保持状态,就必须在每个客户端可能请求的路径后面都包含这个session id。

另一种技术叫做表单隐藏字段。就是服务器会自动修改表单,添加一个隐藏字段,以便在表单提交时能够把session id传递回服务器。比如下面的表单
<form name="testform" action="/xxx">
<input type="text">
</form>
在被传递给客户端之前将被改写成
<form name="testform" action="/xxx">
<input type="hidden" name="jsessionid" value="ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764">
<input type="text">
</form>
这种技术现在已较少应用,笔者接触过的很古老的iPlanet6(SunONE应用服务器的前身)就使用了这种技术。
实际上这种技术可以简单的用对action应用URL重写来代替。

在谈论session机制的时候,常常听到这样一种误解“只要关闭浏览器,session就消失了”。其实可以想象一下会员卡的例子,除非顾客主动对店家提出销卡,否则店家绝对不会轻易删除顾客的资料。对session来说也是一样的,除非程序通知服务器删除一个session,否则服务器会一直保留,程序一般都是在用户做log off的时候发个指令去删除session。然而浏览器从来不会主动在关闭之前通知服务器它将要关闭,因此服务器根本不会有机会知道浏览器已经关闭,之所以会有这种错觉,是大部分session机制都使用会话cookie来保存session id,而关闭浏览器后这个session id就消失了,再次连接服务器时也就无法找到原来的session。如果服务器设置的cookie被保存到硬盘上,或者使用某种手段改写浏览器发出的 HTTP请求头,把原来的session id发送给服务器,则再次打开浏览器仍然能够找到原来的session。

恰恰是由于关闭浏览器不会导致session被删除,迫使服务器为seesion设置了一个失效时间,当距离客户端上一次使用session的时间超过这个失效时间时,服务器就可以认为客户端已经停止了活动,才会把session删除以节省存储空间。

五、理解javax.servlet.http.HttpSession
HttpSession是Java平台对session机制的实现规范,因为它仅仅是个接口,具体到每个web应用服务器的提供商,除了对规范支持之外,仍然会有一些规范里没有规定的细微差异。这里我们以BEA的Weblogic Server8.1作为例子来演示。

首先,Weblogic Server提供了一系列的参数来控制它的HttpSession的实现,包括使用cookie的开关选项,使用URL重写的开关选项,session持久化的设置,session失效时间的设置,以及针对cookie的各种设置,比如设置cookie的名字、路径、域,cookie的生存时间等。

一般情况下,session都是存储在内存里,当服务器进程被停止或者重启的时候,内存里的session也会被清空,如果设置了session的持久化特性,服务器就会把session保存到硬盘上,当服务器进程重新启动或这些信息将能够被再次使用,Weblogic Server支持的持久性方式包括文件、数据库、客户端cookie保存和复制。

复制严格说来不算持久化保存,因为session实际上还是保存在内存里,不过同样的信息被复制到各个cluster内的服务器进程中,这样即使某个服务器进程停止工作也仍然可以从其他进程中取得session。

cookie生存时间的设置则会影响浏览器生成的cookie是否是一个会话cookie。默认是使用会话cookie。有兴趣的可以用它来试验我们在第四节里提到的那个误解。

cookie的路径对于web应用程序来说是一个非常重要的选项,Weblogic Server对这个选项的默认处理方式使得它与其他服务器有明显的区别。后面我们会专题讨论。

关于session的设置参考[5] http://e-docs.bea.com/wls/docs70/webapp/weblogic_xml.html#1036869

六、HttpSession常见问题
(在本小节中session的含义为⑤和⑥的混合)


1、session在何时被创建
一个常见的误解是以为session在有客户端访问时就被创建,然而事实是直到某server端程序调用 HttpServletRequest.getSession(true)这样的语句时才被创建,注意如果JSP没有显示的使用 <%@page session="false"%> 关闭session,则JSP文件在编译成Servlet时将会自动加上这样一条语句HttpSession session = HttpServletRequest.getSession(true);这也是JSP中隐含的session对象的来历。

由于session会消耗内存资源,因此,如果不打算使用session,应该在所有的JSP中关闭它。

2、session何时被删除
综合前面的讨论,session在下列情况下被删除a.程序调用HttpSession.invalidate();或b.距离上一次收到客户端发送的session id时间间隔超过了session的超时设置;或c.服务器进程被停止(非持久session)

3、如何做到在浏览器关闭时删除session
严格的讲,做不到这一点。可以做一点努力的办法是在所有的客户端页面里使用javascript代码window.oncolose来监视浏览器的关闭动作,然后向服务器发送一个请求来删除session。但是对于浏览器崩溃或者强行杀死进程这些非常规手段仍然无能为力。

4、有个HttpSessionListener是怎么回事
你可以创建这样的listener去监控session的创建和销毁事件,使得在发生这样的事件时你可以做一些相应的工作。注意是session的创建和销毁动作触发listener,而不是相反。类似的与HttpSession有关的listener还有 HttpSessionBindingListener,HttpSessionActivationListener和 HttpSessionAttributeListener。

5、存放在session中的对象必须是可序列化的吗
不是必需的。要求对象可序列化只是为了session能够在集群中被复制或者能够持久保存或者在必要时server能够暂时把session交换出内存。在 Weblogic Server的session中放置一个不可序列化的对象在控制台上会收到一个警告。我所用过的某个iPlanet版本如果session中有不可序列化的对象,在session销毁时会有一个Exception,很奇怪。

6、如何才能正确的应付客户端禁止cookie的可能性
对所有的URL使用URL重写,包括超链接,form的action,和重定向的URL,具体做法参见[6]
http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770

7、开两个浏览器窗口访问应用程序会使用同一个session还是不同的session
参见第三小节对cookie的讨论,对session来说是只认id不认人,因此不同的浏览器,不同的窗口打开方式以及不同的cookie存储方式都会对这个问题的答案有影响。

8、如何防止用户打开两个浏览器窗口操作导致的session混乱
这个问题与防止表单多次提交是类似的,可以通过设置客户端的令牌来解决。就是在服务器每次生成一个不同的id返回给客户端,同时保存在session里,客户端提交表单时必须把这个id也返回服务器,程序首先比较返回的id与保存在session里的值是否一致,如果不一致则说明本次操作已经被提交过了。可以参看《J2EE核心模式》关于表示层模式的部分。需要注意的是对于使用javascript window.open打开的窗口,一般不设置这个id,或者使用单独的id,以防主窗口无法操作,建议不要再window.open打开的窗口里做修改操作,这样就可以不用设置。

9、为什么在Weblogic Server中改变session的值后要重新调用一次session.setValue
做这个动作主要是为了在集群环境中提示Weblogic Server session中的值发生了改变,需要向其他服务器进程复制新的session值。

10、为什么session不见了
排除session正常失效的因素之外,服务器本身的可能性应该是微乎其微的,虽然笔者在iPlanet6SP1加若干补丁的Solaris版本上倒也遇到过;浏览器插件的可能性次之,笔者也遇到过3721插件造成的问题;理论上防火墙或者代理服务器在cookie处理上也有可能会出现问题。
出现这一问题的大部分原因都是程序的错误,最常见的就是在一个应用程序中去访问另外一个应用程序。我们在下一节讨论这个问题。

七、跨应用程序的session共享

常常有这样的情况,一个大项目被分割成若干小项目开发,为了能够互不干扰,要求每个小项目作为一个单独的web应用程序开发,可是到了最后突然发现某几个小项目之间需要共享一些信息,或者想使用session来实现SSO(single sign on),在session中保存login的用户信息,最自然的要求是应用程序间能够访问彼此的session。

然而按照Servlet规范,session的作用范围应该仅仅限于当前应用程序下,不同的应用程序之间是不能够互相访问对方的session的。各个应用服务器从实际效果上都遵守了这一规范,但是实现的细节却可能各有不同,因此解决跨应用程序session共享的方法也各不相同。

首先来看一下Tomcat是如何实现web应用程序之间session的隔离的,从Tomcat设置的cookie路径来看,它对不同的应用程序设置的 cookie路径是不同的,这样不同的应用程序所用的session id是不同的,因此即使在同一个浏览器窗口里访问不同的应用程序,发送给服务器的session id也可以是不同的。

根据这个特性,我们可以推测Tomcat中session的内存结构大致如下。

笔者以前用过的iPlanet也采用的是同样的方式,估计SunONE与iPlanet之间不会有太大的差别。对于这种方式的服务器,解决的思路很简单,实际实行起来也不难。要么让所有的应用程序共享一个session id,要么让应用程序能够获得其他应用程序的session id。

iPlanet中有一种很简单的方法来实现共享一个session id,那就是把各个应用程序的cookie路径都设为/(实际上应该是/NASApp,对于应用程序来讲它的作用相当于根)。
<session-info>
<path>/NASApp</path>
</session-info>

需要注意的是,操作共享的session应该遵循一些编程约定,比如在session attribute名字的前面加上应用程序的前缀,使得setAttribute("name", "neo")变成setAttribute("app1.name", "neo"),以防止命名空间冲突,导致互相覆盖。


在Tomcat 中则没有这么方便的选择。在Tomcat版本3上,我们还可以有一些手段来共享session。对于版本4以上的Tomcat,目前笔者尚未发现简单的办法。只能借助于第三方的力量,比如使用文件、数据库、JMS或者客户端cookie,URL参数或者隐藏字段等手段。

我们再看一下Weblogic Server是如何处理session的。

从截屏画面上可以看到Weblogic Server对所有的应用程序设置的cookie的路径都是/,这是不是意味着在Weblogic Server中默认的就可以共享session了呢?然而一个小实验即可证明即使不同的应用程序使用的是同一个session,各个应用程序仍然只能访问自己所设置的那些属性。这说明Weblogic Server中的session的内存结构可能如下

对于这样一种结构,在session机制本身上来解决session共享的问题应该是不可能的了。除了借助于第三方的力量,比如使用文件、数据库、JMS或者客户端cookie,URL参数或者隐藏字段等手段,还有一种较为方便的做法,就是把一个应用程序的session放到ServletContext 中,这样另外一个应用程序就可以从ServletContext中取得前一个应用程序的引用。示例代码如下,

应用程序A
context.setAttribute("appA", session);

应用程序B
contextA = context.getContext("/appA");
HttpSession sessionA = (HttpSession)contextA.getAttribute("appA");

值得注意的是这种用法不可移植,因为根据ServletContext的JavaDoc,应用服务器可以处于安全的原因对于context.getContext("/appA");返回空值,以上做法在Weblogic Server 8.1中通过。

那么Weblogic Server为什么要把所有的应用程序的cookie路径都设为/呢?原来是为了SSO,凡是共享这个session的应用程序都可以共享认证的信息。一个简单的实验就可以证明这一点,修改首先登录的那个应用程序的描述符weblogic.xml,把cookie路径修改为/appA访问另外一个应用程序会重新要求登录,即使是反过来,先访问cookie路径为/的应用程序,再访问修改过路径的这个,虽然不再提示登录,但是登录的用户信息也会丢失。注意做这个实验时认证方式应该使用FORM,因为浏览器和web服务器对basic认证方式有其他的处理方式,第二次请求的认证不是通过session来实现的。具体请参看[7] secion 14.8 Authorization,你可以修改所附的示例程序来做这些试验。

八、总结
session机制本身并不复杂,然而其实现和配置上的灵活性却使得具体情况复杂多变。这也要求我们不能把仅仅某一次的经验或者某一个浏览器,服务器的经验当作普遍适用的经验,而是始终需要具体情况具体分析。

关于作者:
郎云鹏(dev2dev ID: hippiewolf),软件工程师,从事J2EE开发
电子邮件:langyunpeng@yahoo.com.cn
地址:大连软件园路31号科技大厦A座大连博涵咨询服务有限公司

参考文档:
[1] Preliminary Specification http://wp.netscape.com/newsref/std/cookie_spec.html
[2] RFC2109 http://www.rfc-editor.org/rfc/rfc2109.txt
[3] RFC2965 http://www.rfc-editor.org/rfc/rfc2965.txt
[4] The Unofficial Cookie FAQ http://www.cookiecentral.com/faq/
[5] http://e-docs.bea.com/wls/docs70/webapp/weblogic_xml.html#1036869
[6] http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770
[7] RFC2616 http://www.rfc-editor.org/rfc/rfc2616.txt

代码下载sampleApp.zip

posted @ 2006-12-31 10:10 保尔任 阅读(300) | 评论 (0)编辑 收藏

早在Java 1.2推出之时,Java平台中就引入了一个新的支持:java.lang.ThreadLocal,给我们在编写多线程程序时提供了一种新的选择。使用这个工具类可以很简洁地编写出优美的多线程程序,虽然ThreadLocal非常有用,但是似乎现在了解它、使用它的朋友还不多。

ThreadLocal是什么

ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是thread local variable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量。线程局部变量并不是Java的新发明,在其它的一些语言编译器实现(如IBM XL FORTRAN)中,它在语言的层次提供了直接的支持。因为Java中没有提供在语言层次的直接支持,而是提供了一个ThreadLocal的类来提供支持,所以,在Java中编写线程局部变量的代码相对比较笨拙,这也许是线程局部变量没有在Java中得到很好的普及的一个原因吧。

ThreadLocal的设计

首先看看ThreadLocal的接口:

Object get() ;

// 返回当前线程的线程局部变量副本 protected Object initialValue(); // 返回该线程局部变量的当前线程的初始值

void set(Object value);

// 设置当前线程的线程局部变量副本的值

ThreadLocal有3个方法,其中值得注意的是initialValue(),该方法是一个protected的方法,显然是为了子类重写而特意实现的。该方法返回当前线程在该线程局部变量的初始值,这个方法是一个延迟调用方法,在一个线程第1次调用get()或者set(Object)时才执行,并且仅执行1次。ThreadLocal中的确实实现直接返回一个null:

protected Object initialValue() { return null; }

ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单,在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。比如下面的示例实现:


public class ThreadLocal
{
private Map values = Collections.synchronizedMap(new HashMap());
public Object get()
{
Thread curThread = Thread.currentThread();
Object o = values.get(curThread);
if (o == null && !values.containsKey(curThread))
{
o = initialValue();
values.put(curThread, o);
}
return o;
}

public void set(Object newValue)
{
values.put(Thread.currentThread(), newValue);
}

public Object initialValue()
{
return null;
}
}

 


当然,这并不是一个工业强度的实现,但JDK中的ThreadLocal的实现总体思路也类似于此。

ThreadLocal的使用

如果希望线程局部变量初始化其它值,那么需要自己实现ThreadLocal的子类并重写该方法,通常使用一个内部匿名类对ThreadLocal进行子类化,比如下面的例子,SerialNum类为每一个类分配一个序号


public class SerialNum
{
// The next serial number to be assigned

private static int nextSerialNum = 0;

private static ThreadLocal serialNum = new ThreadLocal()
{
protected synchronized Object initialValue()
{
return new Integer(nextSerialNum++);
}
};


public static int get()
{
return ((Integer) (serialNum.get())).intValue();
}

}

 


SerialNum类的使用将非常地简单,因为get()方法是static的,所以在需要获取当前线程的序号时,简单地调用:

int serial = SerialNum.get();

即可。

在线程是活动的并且ThreadLocal对象是可访问的时,该线程就持有一个到该线程局部变量副本的隐含引用,当该线程运行结束后,该线程拥有的所以线程局部变量的副本都将失效,并等待垃圾收集器收集。

ThreadLocal与其它同步机制的比较

ThreadLocal和其它同步机制相比有什么优势呢?ThreadLocal和其它所有的同步机制都是为了解决多线程中的对同一变量的访问冲突,在普通的同步机制中,是通过对象加锁来实现多个线程对同一变量的安全访问的。这时该变量是多个线程共享的,使用这种同步机制需要很细致地分析在什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放该对象的锁等等很多。所有这些都是因为多个线程共享了资源造成的。ThreadLocal就从另一个角度来解决多线程的并发访问,ThreadLocal会为每一个线程维护一个和该线程绑定的变量的副本,从而隔离了多个线程的数据,每一个线程都拥有自己的变量副本,从而也就没有必要对该变量

posted @ 2006-12-27 21:37 保尔任 阅读(324) | 评论 (0)编辑 收藏
 

第一,文件的的编码方式其实就包括两方面:存和取,存文件必须以一种编码存;读文件也必须以一种编码读。如果存取按照相同的编码方式,则不会有问题,关键就是很多时候存取的方式不一致,产生乱码。,如不特别设置取系统默认的编码,中文windows为GBK编码。

从.java->.class过程是,先编写.java文件并按莫种编码方式保存,然后用javac方法编译此文件,注意如.java没按系统默认编码保存则要带encoding参数指明实际编码,否则出错,生成的.class文件存为系统默认编码。

从.jsp->.java->.class,先存为某种编码的.jsp文件,然后tomcat根据pageEncoding读取并转化为servlet存为系统默认编码,然后同上面.java->.class过程。

第二,IDE的encoding为对系统下文件打开的解码方式或保存的编码方式。特例:如果.jsp文件有<%@ page language="java" pageEncoding="UTF-8"%>,则eclipse会自动存为UTF-8方式,不管eclipse的encoding是什么,这也是 eclipse的聪明之处。

第三,
pageEncoding="UTF-8"表示此文件的编码方式,必须与此文件存储方式一致(所以eclipse会首选根据它来存文件),tomcat根据这个来读此.jsp文件并编译为servlet(至于编译成的.java和.class文件应该为tomcat服务器默认编码)。
contentType="text/html;charset=UTF-8"表示当服务器给浏览器传页面文件时编码方式为UTF-8,形式为HTML。例如:
<%@ page language="java" pageEncoding="UTF-8"%>
<%@ page contentType="text/html;charset=GBK"%>
<html>
 <head>
  <title>test</title>
 </head>
 <body>
  我是个好人
 </body>
</html>

表示本jsp文件存为UTF-8字符集,当浏览器打开此页面后,查看原码就会发现源码为GBK字符集。

第四,
request.setCharacterEncoding("UTF-8")是把提交内容的字符集设为UTF-8
response.setCharacterEncoding("UTF-8")可以把页面中的<%@ page contentType="text/html;charset=iso8859-1"%>换为charset=UTF-8,是给告诉浏览器我这个文件的编码方式。

第五,表单提交:无论何种表单提交都可以在后台的java文件中通过String des = new String(s.getBytes("iso8859-1"),"UTF-8");来转换成你想要的UTF-8编码方式。但如果每处都加词句太麻烦,故分post和get两种方式区分提交(tomcat5以后分开处理,之前处理方式一样,即都可以用 request.setCharacterEncoding("UTF-8")方法处理,不过tomcat5以后get提交方法用此语句无效)。
1,post提交的数据:
程序加上org.springframework.web.filter.CharacterEncodingFilter过滤器.
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>

<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>*.html</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>

因为规范要求浏览器提交数据都要用utf8编码,所以这里设置编码方式为UTF8.

特别注意:
a,这个过滤器只是简单的调用:request.setCharacterEncoding(this.encoding);
在这个语句之前不能调用任何的request.getParameter()方法,否则会设置tomcat的缺省字符集为"ISO-8859-1",并且使 setCharacterEncoding的调用失效.所以在这个过滤器之前的过滤器中不能有对getParameter这类方法的调用,比较安全的做法就是把这个过滤器尽量靠前放.
b,在server.xml中不能加上<Valve className="org.apache.catalina.valves.RequestDumperValve"/>
这个value也设置tomcat的缺省字符集为"ISO-8859-1",使setCharacterEncoding的调用失效.可能其他的value也有这个问题,我没有测试过.
如果要观察http请求参数,可以考虑用过滤器或者其他工具,例如ethereal(http://www.ethereal.com/)

2,get提交的数据:
两种情况:
a,如果从地址栏直接输入汉字,则一般编码为"GBK",需要用
new String(request.getParameter("something").getBytes("ISO-8859-1"),"GBK")
取出
b,如果是页面超连接连接中带的汉字,则编码根据页面编码的不同而不同,如果页面的
content="text/html; charset=utf-8",则在tomcat/conf/server.xml中的配置文件中:
<!-- Define a non-SSL Coyote HTTP/1.1 Connector on port 8080 -->
<Connector port="8080"
maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" redirectPort="8443" acceptCount="100"
debug="0" connectionTimeout="20000" useBodyEncodingForURI="true"
disableUploadTimeout="true" />

加上:useBodyEncodingForURI="true"即可正常使用getParameter取出正确内容.
如果content="text/html; charset=GBK",需用
new String(request.getParameter("something").getBytes("ISO-8859-1"),"GBK")
取出,其他情况类似.

总结:
1,所有页面使用utf8编码,
2,服务器加上过滤器,
3,server.xml中不要使用
<Valve className="org.apache.catalina.valves.RequestDumperValve"/>
4,server.xml文件加上useBodyEncodingForURI="true"
这样应该可以搞定大多数前台的中文问题.至于地址栏输入中文,不支持也罢,一般的程序很少要求
从这里输入.

第六,连接数据库
 

1、mysql配置文件:
修改mysql在windows\my.ini里default-character-set=utf-8

2、mysql里数据库和表也都设为utf8_unicode_ci

3、数据库连结:jdbc:mysql://localhost/mydb?useUnicode=true&characterEncoding=utf-8
注意,关键就在于此:此句中间是'&'不是'&amp;'这是因为数据库连结时,在.jsp和.java文件中应该用&号,而XML文件中需要用&amp

posted @ 2006-12-26 10:52 保尔任 阅读(327) | 评论 (0)编辑 收藏
  

Java基础方面:

1、作用域public,private,protected,以及不写时的区别
答:区别如下:
作用域           当前类       同一package  子孙类       其他package
public            √              √                  √             √
protected        √              √                  √             ×
friendly          √              √                   ×            ×
private           √              ×                   ×            ×
不写时默认为friendly

2、ArrayList和Vector的区别,HashMap和Hashtable的区别
答:
就ArrayList与Vector主要从二方面来说.
一.同步性:Vector是线程安全的,也就是说是同步的,而ArrayList是线程序不安全的,不是同步的
二.数据增长:当需要增长时,Vector默认增长为原来一培,而ArrayList却是原来的一半
就HashMap与HashTable主要从三方面来说。
一.历史原因:Hashtable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一个实现
二.同步性:Hashtable是线程安全的,也就是说是同步的,而HashMap是线程序不安全的,不是同步的
三.值:只有HashMap可以让你将空值作为一个表的条目的key或value

3、char型变量中能不能存贮一个中文汉字?为什么?
答:
是能够定义成为一个中文的,因为java中以unicode编码,一个char占16个字节,所以放一个中文是没问题的

4、多线程有几种实现方法,都是什么?同步有几种实现方法,都是什么?
答:
多线程有两种实现方法,分别是继承Thread类与实现Runnable接口
同步的实现方面有两种,分别是synchronized,wait与notify

5、继承时候类的执行顺序问题,一般都是选择题,问你将会打印出什么?
答:
父类:
package test;
public class  FatherClass
{
    public FatherClass()
 {
  System.out.println("FatherClass Create");
 }
}
子类:
package test;
import test.FatherClass;
public class  ChildClass extends FatherClass
{
 public ChildClass()
 {
  System.out.println("ChildClass Create");
 }
 public static void main(String[] args)
 {
  FatherClass fc = new FatherClass();
  ChildClass cc = new ChildClass();
 }
}
输出结果:
C:\>java test.ChildClass
FatherClass Create
FatherClass Create
ChildClass Create

6、内部类的实现方式?
答:
示例代码如下:
package test;
public class  OuterClass
{
 private class InterClass
 {
  public InterClass()
  {
   System.out.println("InterClass Create");
  }
 }
 public OuterClass()
 {
  InterClass ic = new InterClass();
  System.out.println("OuterClass Create");
 }
 public static void main(String[] args)
 {
  OuterClass oc = new OuterClass();
 }
}
输出结果:
C:\>java test/OuterClass
InterClass Create
OuterClass Create
再一个例题:
public class OuterClass {
  private double d1 = 1.0;
    //insert code here
}
You need to insert an inner class declaration at line 3. Which two inner class declarations are

valid?(Choose two.)
A. class InnerOne{
     public static double methoda() {return d1;}
   }
B. public class InnerOne{
     static double methoda() {return d1;}
   }
C. private class InnerOne{
     double methoda() {return d1;}
   }
D. static class InnerOne{
     protected double methoda() {return d1;}
   }
E. abstract class InnerOne{
     public abstract double methoda();
   }
说明如下:
一.静态内部类可以有静态成员,而非静态内部类则不能有静态成员。 故 A、B 错
二.静态内部类的非静态成员可以访问外部类的静态变量,而不可访问外部类的非静态变量;return d1 出错。

故 D 错
三.非静态内部类的非静态成员可以访问外部类的非静态变量。 故 C 正确
四.答案为C、E

7、垃圾回收机制,如何优化程序?
希望大家补上,谢谢

8、float型float f=3.4是否正确?
答:
不正确。精度不准确,应该用强制类型转换,如下所示:float f=(float)3.4

9、介绍JAVA中的Collection FrameWork(包括如何写自己的数据结构)?
答:
Collection FrameWork如下:
Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set
Map
├Hashtable
├HashMap
└WeakHashMap
Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)
Map提供key到value的映射

10、Java中异常处理机制,事件机制?

11、JAVA中的多形与继承?
希望大家补上,谢谢

12、抽象类与接口?
答:
抽象类与接口都用于抽象,但是抽象类(JAVA中)可以有自己的部分实现,而接口则完全是一个标识(同时有多重继承的功能)。

13、Java 的通信编程,编程题(或问答),用JAVA SOCKET编程,读服务器几个字符,再写入本地显示?
答:
Server端程序:
package test;
import java.net.*;
import java.io.*;

public class Server
{
 private ServerSocket ss;
 private Socket socket;
 private BufferedReader in;
 private PrintWriter out;
 public Server()
 {
  try
  {
   ss=new ServerSocket(10000);
   while(true)
   {
    socket = ss.accept();
    String RemoteIP = socket.getInetAddress().getHostAddress();
    String RemotePort = ":"+socket.getLocalPort();
    System.out.println("A client come in!IP:"+RemoteIP+RemotePort);
    in = new BufferedReader(new

InputStreamReader(socket.getInputStream()));
    String line = in.readLine();
    System.out.println("Cleint send is :" + line);
    out = new PrintWriter(socket.getOutputStream(),true);
    out.println("Your Message Received!");
    out.close();
    in.close();
    socket.close();
   }
  }catch (IOException e)
  {
   out.println("wrong");
  }
 }
 public static void main(String[] args)
 {
  new Server();
 }
};
Client端程序:
package test;
import java.io.*;
import java.net.*;

public class Client
{
 Socket socket;
 BufferedReader in;
 PrintWriter out;
 public Client()
 {
  try
  {
   System.out.println("Try to Connect to 127.0.0.1:10000");
   socket = new Socket("127.0.0.1",10000);
   System.out.println("The Server Connected!");
   System.out.println("Please enter some Character:");
   BufferedReader line = new BufferedReader(new

InputStreamReader(System.in));
   out = new PrintWriter(socket.getOutputStream(),true);
   out.println(line.readLine());
   in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
   System.out.println(in.readLine());
   out.close();
   in.close();
   socket.close();
  }catch(IOException e)
  {
   out.println("Wrong");
  }
 }
 public static void main(String[] args)
 {
  new Client();
 }
};

14、用JAVA实现一种排序,JAVA类实现序列化的方法(二种)? 如在COLLECTION框架中,实现比较要实现什么样的接口?
答:
用插入法进行排序代码如下
package test;
import java.util.*;
class  InsertSort
{
 ArrayList al;
 public InsertSort(int num,int mod)
 {
  al = new ArrayList(num);
  Random rand = new Random();
  System.out.println("The ArrayList Sort Before:");
  for (int i=0;i<num ;i++ )
  {
   al.add(new Integer(Math.abs(rand.nextInt()) % mod + 1));
   System.out.println("al["+i+"]="+al.get(i));
  }
 }
 public void SortIt()
 {
  Integer tempInt;
  int MaxSize=1;
  for(int i=1;i<al.size();i++)
  {
       tempInt = (Integer)al.remove(i);
    if(tempInt.intValue()>=((Integer)al.get(MaxSize-1)).intValue())
    {
     al.add(MaxSize,tempInt);
     MaxSize++;
     System.out.println(al.toString());
    } else {
     for (int j=0;j<MaxSize ;j++ )
     {
      if

(((Integer)al.get(j)).intValue()>=tempInt.intValue())
      {
       al.add(j,tempInt);
       MaxSize++;
       System.out.println(al.toString());
       break;
      }
     }
    }
  }
  System.out.println("The ArrayList Sort After:");
  for(int i=0;i<al.size();i++)
  {
   System.out.println("al["+i+"]="+al.get(i));
  }
 }
 public static void main(String[] args)
 {
  InsertSort is = new InsertSort(10,100);
  is.SortIt();
 }
}
JAVA类实现序例化的方法是实现java.io.Serializable接口
Collection框架中实现比较要实现Comparable 接口和 Comparator 接口

15、编程:编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串。 但是要保证汉字不被截半个,如“我ABC”4,应该截为“我AB”,输入“我ABC汉DEF”,6,应该输出为“我ABC”而不是“我ABC+汉的半个”。
答:
代码如下:
package test;

class  SplitString
{
 String SplitStr;
 int SplitByte;
 public SplitString(String str,int bytes)
 {
  SplitStr=str;
  SplitByte=bytes;
  System.out.println("The String is:'"+SplitStr+"';SplitBytes="+SplitByte);
 }
 public void SplitIt()
 {
  int loopCount;
  

loopCount=(SplitStr.length()%SplitByte==0)?(SplitStr.length()/SplitByte):(SplitStr.length()/Split

Byte+1);
  System.out.println("Will Split into "+loopCount);
  for (int i=1;i<=loopCount ;i++ )
  {
   if (i==loopCount){
    

System.out.println(SplitStr.substring((i-1)*SplitByte,SplitStr.length()));
   } else {
    

System.out.println(SplitStr.substring((i-1)*SplitByte,(i*SplitByte)));
   }
  }
 }
 public static void main(String[] args)
 {
  SplitString ss = new SplitString("test中dd文dsaf中男大3443n中国43中国人

0ewldfls=103",4);
  ss.SplitIt();
 }
}

16、JAVA多线程编程。 用JAVA写一个多线程程序,如写四个线程,二个加1,二个对一个变量减一,输出。
希望大家补上,谢谢

17、STRING与STRINGBUFFER的区别。
答:
STRING的长度是不可变的,STRINGBUFFER的长度是可变的。如果你对字符串中的内容经常进行操作,特别是内容要修改时,那么使用StringBuffer,如果最后需要String,那么使用StringBuffer的toString()方法

Jsp方面

1、jsp有哪些内置对象?作用分别是什么?
答:
JSP共有以下9种基本内置组件(可与ASP的6种内部组件相对应):
 request 用户端请求,此请求会包含来自GET/POST请求的参数
   response 网页传回用户端的回应
   pageContext 网页的属性是在这里管理
   session 与请求有关的会话期
   application servlet 正在执行的内容
   out 用来传送回应的输出
   config servlet的构架部件
   page JSP网页本身
   exception 针对错误网页,未捕捉的例外

2、jsp有哪些动作?作用分别是什么?
答:
JSP共有以下6种基本动作
   jsp:include:在页面被请求的时候引入一个文件。
   jsp:useBean:寻找或者实例化一个JavaBean。
   jsp:setProperty:设置JavaBean的属性。
   jsp:getProperty:输出某个JavaBean的属性。
   jsp:forward:把请求转到一个新的页面。
   jsp:plugin:根据浏览器类型为Java插件生成OBJECT或EMBED标记

3、JSP中动态INCLUDE与静态INCLUDE的区别?
答:
动态INCLUDE用jsp:include动作实现
   <jsp:include page="included.jsp" flush="true" />它总是会检查所含文件中的变化,适合用于包含动态页面,并且可以带参数
   静态INCLUDE用include伪码实现,定不会检查所含文件的变化,适用于包含静态页面
   <%@ include file="included.htm" %>

4、两种跳转方式分别是什么?有什么区别?
答:
有两种,分别为:
  <jsp:include page="included.jsp" flush="true">
  <jsp:forward page= "nextpage.jsp"/>
  前者页面不会转向include所指的页面,只是显示该页的结果,主页面还是原来的页面。执行完后还会回来,相当于函数调用。并且可以带参数.后者完全转向新页面,不会再回来。相当于go to 语句。

Servlet方面

1、说一说Servlet的生命周期?
答:
servlet有良好的生存期的定义,包括加载和实例化、初始化、处理请求以及服务结束。这个生存期由javax.servlet.Servlet接口的init,service和destroy方法表达。

2、Servlet版本间(忘了问的是哪两个版本了)的不同?
希望大家补上,谢谢

3、JAVA SERVLET API中forward() 与redirect()的区别?
答:
前者仅是容器中控制权的转向,在客户端浏览器地址栏中不会显示出转向后的地址;后者则是完全的跳转,浏览器将会得到跳转的地址,并重新发送请求链接。这样,从浏览器的地址栏中可以看到跳转后的链接地址。所以,前者更加高效,在前者可以满足需要时,尽量使用forward()方法,并且,这样也有助于隐藏实际的链接。在有些情况下,比如,需要跳转到一个其它服务器上的资源,则必须使用sendRedirect()方法。

4、Servlet的基本架构
public class ServletName extends HttpServlet {
  public void doPost(HttpServletRequest request, HttpServletResponse response) throws
      ServletException, IOException  {
      }
  public void doGet(HttpServletRequest request, HttpServletResponse response) throws
      ServletException, IOException  {
      }
}

Jdbc、Jdo方面

1、可能会让你写一段Jdbc连Oracle的程序,并实现数据查询.
答:
程序如下:
package hello.ant;
import java.sql.*;
public class  jdbc
{
 String dbUrl="jdbc:oracle:thin:@127.0.0.1:1521:orcl";
 String theUser="admin";
 String thePw="manager";
 Connection c=null;
 Statement conn;
 ResultSet rs=null;
 public jdbc()
 {
  try{
    Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();
          c = DriverManager.getConnection(dbUrl,theUser,thePw);
    conn=c.createStatement();
  }catch(Exception e){
   e.printStackTrace();
  }
 }
 public boolean executeUpdate(String sql)
 {
   try
   {
     conn.executeUpdate(sql);
     return true;
   }
   catch (SQLException e)
   {
     e.printStackTrace();
     return false;
   }
 }
 public ResultSet executeQuery(String sql)
 {
   rs=null;
   try
   {
     rs=conn.executeQuery(sql);
   }
   catch (SQLException e)
   {
     e.printStackTrace();
   }
   return rs;
 }
 public void close()
 {
   try
   {
     conn.close();
     c.close();
   }
   catch (Exception e)
   {
     e.printStackTrace();
   }
 }
 public static void main(String[] args)
 {
  ResultSet rs;
  jdbc conn = new jdbc();
  rs=conn.executeQuery("select * from test");
  try{
  while (rs.next())
  {
   System.out.println(rs.getString("id"));
   System.out.println(rs.getString("name"));
  }
  }catch(Exception e)
  {
   e.printStackTrace();
  }
 }
}

2、Class.forName的作用?为什么要用?
答:
调用该访问返回一个以字符串指定类名的类的对象。

3、Jdo是什么?
答:
JDO 是Java对象持久化的新的规范,为java data object的简称,也是一个用于存取某种数据仓库中的对象的标准化API。JDO提供了透明的对象存储,因此对开发人员来说,存储数据对象完全不需要额外的代码(如JDBC API的使用)。这些繁琐的例行工作已经转移到JDO产品提供商身上,使开发人员解脱出来,从而集中时间和精力在业务逻辑上。另外,JDO很灵活,因为它可以在任何数据底层上运行。JDBC只是面向关系数据库(RDBMS)JDO更通用,提供到任何数据底层的存储功能,比如关系数据库、文件、XML以及对象数据库(ODBMS)等等,使得应用可移植性更强。

4、在ORACLE大数据量下的分页解决方法。一般用截取ID方法,还有是三层嵌套方法。
答:
一种分页方法
<%
  int i=1;
  int numPages=14;
  String pages = request.getParameter("page") ;
  int currentPage = 1;
  currentPage=(pages==null)?(1):{Integer.parseInt(pages)}
  sql = "select count(*) from tables";
  ResultSet rs = DBLink.executeQuery(sql) ;
  while(rs.next()) i = rs.getInt(1) ;
  int intPageCount=1;
  intPageCount=(i%numPages==0)?(i/numPages):(i/numPages+1);
  int nextPage ;
  int upPage;
  nextPage = currentPage+1;
  if (nextPage>=intPageCount) nextPage=intPageCount;
  upPage = currentPage-1;
  if (upPage<=1) upPage=1;
  rs.close();
  sql="select * from tables";
  rs=DBLink.executeQuery(sql);
  i=0;
  while((i<numPages*(currentPage-1))&&rs.next()){i++;}
%>
//输出内容
//输出翻页连接
合计:<%=currentPage%>/<%=intPageCount%><a href="List.jsp?page=1">第一页</a><a

href="List.jsp?page=<%=upPage%>">上一页</a>
<%
  for(int j=1;j<=intPageCount;j++){
  if(currentPage!=j){
%>
  <a href="list.jsp?page=<%=j%>">[<%=j%>]</a>
<%
  }else{
  out.println(j);
  }
  }
%>
<a href="List.jsp?page=<%=nextPage%>">下一页</a><a href="List.jsp?page=<%=intPageCount%>">最后页

</a>


Xml方面

1、xml有哪些解析技术?区别是什么?
答:
有DOM,SAX,STAX等
DOM: 处理大型文件时其性能下降的非常厉害。这个问题是由DOM的树结构所造成的,这种结构占用的内存较多,而且DOM必须在解析文件之前把整个文档装入内存, 适合对 XML的随机访问SAX:不现于DOM,SAX是事件驱动型的XML解析方式。它顺序读取XML文件,不需要一次全部装载整个文件。当遇到像文件开头,文档结束,或者标签开头与标签结束时,它会触发一个事件,用户通过在其回调事件中写入处理代码来处理XML文件,适合对XML的顺序访问
STAX:Streaming API for XML (StAX)

2、你在项目中用到了xml技术的哪些方面?如何实现的?
答:
用到了数据存贮,信息配置两方面。在做数据交换平台时,将不能数据源的数据组装成XML文件,然后将XML文件压缩打包加密后通过网络传送给接收者,接收解密与解压缩后再同XML文件中还原相关信息进行处理。在做软件配置时,利用XML可以很方便的进行,软件的各种配置参数都存贮在XML文件中。

3、用jdom解析xml文件时如何解决中文问题?如何解析?
答:
看如下代码,用编码方式加以解决
package test;
import java.io.*;
public class DOMTest
{
 private String inFile = "c:\\people.xml";
 private String outFile = "c:\\people.xml"; 
 public static void main(String args[])
 {
     new DOMTest();
    }
 public DOMTest()
 {
  try
     { 
      javax.xml.parsers.DocumentBuilder builder =
       

javax.xml.parsers.DocumentBuilderFactory.newInstance().newDocumentBuilder();
      org.w3c.dom.Document doc = builder.newDocument();
      org.w3c.dom.Element root = doc.createElement("老师");
      org.w3c.dom.Element wang = doc.createElement("王");
   org.w3c.dom.Element liu = doc.createElement("刘");
      wang.appendChild(doc.createTextNode("我是王老师"));
      root.appendChild(wang);
      doc.appendChild(root);
      javax.xml.transform.Transformer transformer =
       javax.xml.transform.TransformerFactory.newInstance().newTransformer();
      transformer.setOutputProperty(javax.xml.transform.OutputKeys.ENCODING, "gb2312");
      transformer.setOutputProperty(javax.xml.transform.OutputKeys.INDENT, "yes");  

   
      transformer.transform(new javax.xml.transform.dom.DOMSource(doc),
            new

javax.xml.transform.stream.StreamResult(outFile));
     }
     catch (Exception e)
     {
      System.out.println (e.getMessage());
     }
    }
}

4、编程用JAVA解析XML的方式.
答:
用SAX方式解析XML,XML文件如下:
<?xml version="1.0" encoding="gb2312"?>
<person>
  <name>王小明</name>
  <college>信息学院</college>  
  <telephone>6258113</telephone>
  <notes>男,1955年生,博士,95年调入海南大学</notes>
 </person>
 事件回调类SAXHandler.java
 import java.io.*;
import java.util.Hashtable;
import org.xml.sax.*;
public class SAXHandler extends HandlerBase
  {
  private Hashtable table = new Hashtable();
  private String currentElement = null;
  private String currentValue = null;
  public void setTable(Hashtable table)
    {
    this.table = table;
    }
  public Hashtable getTable()
    {
    return table;
    }
  public void startElement(String tag, AttributeList attrs)
  throws SAXException
    {
    currentElement = tag;
    }
  public void characters(char[] ch, int start, int length)
  throws SAXException
    {
    currentValue = new String(ch, start, length);
    }
  public void endElement(String name) throws SAXException
    {
    if (currentElement.equals(name))
      table.put(currentElement, currentValue);
    }
  }
JSP内容显示源码,SaxXml.jsp:
<HTML>
<HEAD>
<TITLE>剖析XML文件people.xml</TITLE>
</HEAD>
<BODY>
<%@ page errorPage="ErrPage.jsp"
contentType="text/html;charset=GB2312" %>
<%@ page import="java.io.*" %>
<%@ page import="java.util.Hashtable" %>
<%@ page import="org.w3c.dom.*" %>
<%@ page import="org.xml.sax.*" %>
<%@ page import="javax.xml.parsers.SAXParserFactory" %>
<%@ page import="javax.xml.parsers.SAXParser" %>
<%@ page import="SAXHandler" %>
<%
File file = new File("c:\\people.xml");
FileReader reader = new FileReader(file);
Parser parser;
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
SAXHandler handler = new SAXHandler();
sp.parse(new InputSource(reader), handler);
Hashtable hashTable = handler.getTable();
out.println("<TABLE BORDER=2><CAPTION>教师信息表</CAPTION>");
out.println("<TR><TD>姓名</TD>" + "<TD>" +
  (String)hashTable.get(new String("name")) + "</TD></TR>");
out.println("<TR><TD>学院</TD>" + "<TD>" +
  (String)hashTable.get(new String("college"))+"</TD></TR>");
out.println("<TR><TD>电话</TD>" + "<TD>" +
  (String)hashTable.get(new String("telephone")) + "</TD></TR>");
out.println("<TR><TD>备注</TD>" + "<TD>" +
  (String)hashTable.get(new String("notes")) + "</TD></TR>");
out.println("</TABLE>");
%>
</BODY>
</HTML>

EJB方面

1、EJB2.0有哪些内容?分别用在什么场合? EJB2.0和EJB1.1的区别?
答:
规范内容包括Bean提供者,应用程序装配者,EJB容器,EJB配置工具,EJB服务提供者,系统管理员。这里面,EJB容器是EJB之所以能够运行的核心。 EJB容器管理着EJB的创建,撤消,激活,去活,与数据库的连接等等重要的核心工作。JSP,Servlet,EJB,JNDI,JDBC, JMS.....

2、EJB与JAVA BEAN的区别?
答:
Java Bean 是可复用的组件,对Java Bean并没有严格的规范,理论上讲,任何一个Java类都可以是一个Bean。但通常情况下,由于Java Bean是被容器所创建(如Tomcat)的,所以Java Bean应具有一个无参的构造器,另外,通常Java Bean还要实现Serializable接口用于实现Bean的持久性。Java Bean实际上相当于微软COM模型中的本地进程内COM组件,它是不能被跨进程访问的。Enterprise Java Bean 相当于DCOM,即分布式组件。它是基于Java的远程方法调用(RMI)技术的,所以EJB可以被远程访问(跨进程、跨计算机)。但EJB必须被布署在诸如Webspere、WebLogic这样的容器中,EJB客户从不直接访问真正的EJB组件,而是通过其容器访问。EJB容器是EJB组件的代理, EJB组件由容器所创建和管理。客户通过容器来访问真正的EJB组件。

3、EJB的基本架构
答:
一个EJB包括三个部分:
  Remote Interface 接口的代码
  package Beans;
  import javax.ejb.EJBObject;
  import java.rmi.RemoteException;
  public interface Add extends EJBObject
  {
   //some method declare
  }
  Home Interface 接口的代码
  package Beans;
  import java.rmi.RemoteException;
  import jaax.ejb.CreateException;
  import javax.ejb.EJBHome;
  public interface AddHome extends EJBHome
  {
    //some method declare
  }
  EJB类的代码
  package Beans;
  import java.rmi.RemoteException;
  import javax.ejb.SessionBean;
  import javx.ejb.SessionContext;
  public class AddBean Implements SessionBean
  {
    //some method declare
  } 

J2EE,MVC方面

1、MVC的各个部分都有那些技术来实现?如何实现?
答:
MVC 是Model-View- Controller的简写。"Model" 代表的是应用的业务逻辑(通过JavaBean,EJB组件实现), "View" 是应用的表示面(由JSP页面产生),"Controller" 是提供应用的处理过程控制(一般是一个Servlet),通过这种设计模型把应用逻辑,处理过程和显示逻辑分成不同的组件实现。这些组件可以进行交互和重用。

2、应用服务器与WEB SERVER的区别?
希望大家补上,谢谢


3、J2EE是什么?
答:
Je22 是Sun公司提出的多层(multi-diered),分布式(distributed),基于组件(component-base)的企业级应用模型 (enterpriese application model).在这样的一个应用系统中,可按照功能划分为不同的组件,这些组件又可在不同计算机上,并且处于相应的层次(tier)中。所属层次包括客户层(clietn tier)组件,web层和组件,Business层和组件,企业信息系统(EIS)层。

4、WEB SERVICE名词解释。JSWDL开发包的介绍。JAXP、JAXM的解释。SOAP、UDDI,WSDL解释。
答:
Web Service描述语言WSDL
SOAP即简单对象访问协议(Simple Object Access Protocol),它是用于交换XML编码信息的轻量级协议。
UDDI 的目的是为电子商务建立标准;UDDI是一套基于Web的、分布式的、为Web Service提供的、信息注册中心的实现标准规范,同时也包含一组使企业能将自身提供的Web Service注册,以使别的企业能够发现的访问协议的实现标准。


5、BS与CS的联系与区别。
希望大家补上,谢谢

6、STRUTS的应用(如STRUTS架构)
答:
Struts 是采用Java Servlet/JavaServer Pages技术,开发Web应用程序的开放源码的framework。采用Struts能开发出基于MVC(Model-View- Controller)设计模式的应用构架。 Struts有如下的主要功能:
一.包含一个controller servlet,能将用户的请求发送到相应的Action对象。
二.JSP自由tag库,并且在controller servlet中提供关联支持,帮助开发员创建交互式表单应用。
三.提供了一系列实用对象:XML处理、通过Java reflection APIs自动处理JavaBeans属性、国际化的提示和消息。

设计模式方面

1、开发中都用到了那些设计模式?用在什么场合?
答:
每个模式都描述了一个在我们的环境中不断出现的问题,然后描述了该问题的解决方案的核心。通过这种方式,你可以无数次地使用那些已有的解决方案,无需在重复相同的工作。主要用到了MVC的设计模式。用来开发JSP/Servlet或者J2EE的相关应用。简单工厂模式等。


2、UML方面
答:
标准建模语言UML。用例图,静态图(包括类图、对象图和包图),行为图,交互图(顺序图,合作图),实现图,

JavaScript方面

1、如何校验数字型?
var re=/^\d{1,8}$|\.\d{1,2}$/;
var str=document.form1.all(i).value;
var r=str.match(re);
if (r==null)
{
   sign=-4;
   break;
}
else{
   document.form1.all(i).value=parseFloat(str);
}


CORBA方面

1、CORBA是什么?用途是什么?
答:
CORBA 标准是公共对象请求代理结构(Common Object Request Broker Architecture),由对象管理组织 (Object Management Group,缩写为 OMG)标准化。它的组成是接口定义语言(IDL), 语言绑定(binding:也译为联编)和允许应用程序间互操作的协议。其目的为:
用不同的程序设计语言书写
在不同的进程中运行
为不同的操作系统开发


LINUX方面

1、LINUX下线程,GDI类的解释。
答:
LINUX实现的就是基于核心轻量级进程的"一对一"线程模型,一个线程实体对应一个核心轻量级进程,而线程之间的管理在核外函数库中实现。
GDI类为图像设备编程接口类库。

posted @ 2006-12-26 10:51 保尔任 阅读(235) | 评论 (0)编辑 收藏
仅列出标题
共8页: 上一页 1 2 3 4 5 6 7 8 下一页 

<2024年5月>
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

常用链接

留言簿(4)

随笔分类

随笔档案

文章分类

文章档案

搜索

  •  

最新评论

阅读排行榜

评论排行榜