在Oracle中,要按特定条件查询前N条记录,用个rownum就搞定了。 select * from emp where rownum <= 5 结果只查询5条记录, oracle会自动展现一个rownum的属性表示记录排序号
而select * from emp where rownum > 5 ;则是失败的。 因为:rownum是oracle预处理字段,默认标序是1,只有记录集已经满足条件后才会进行后续编号。由于第一条记录rownum默认是1,而你的条件是rownum>=6 对第一条记录比较它的rownum肯定不大于6 所以不满足条件 oracle舍弃第一条记录将数据库第二条记录标序为1再进行比较 肯定都不满足rownum>=6 这样循环也就是说由于没有记录满足rownum>=6所以记录一直被舍弃,rownum一直是1 。
解决方案: 利用查询时,自动生成的rownum属性. 排序方法: select * from ( select a1.*, rownum rwn from emp a1 where rownum <=10 ) where rwn >= 6; 或者 select * from ( select qx.*,row_number() over(order by qx.empno) rwn from emp qx ) where rwn between 6 and 10 ------------------------------------------------------------------------- 使用注意:
1排序导致数据重复:
table1中表有字段(其余字段省) ID 主键 DATA_UPDATE_TIME 数据更新时间(只存储了年月日)
分页查询的语句如下 select * from ( select row_.*, rownum rownum_ from ( select p.id from table1 p order by p.DATA_UPDATE_TIME desc ) row_ where rownum <= ) where rownum_ >
以每页显示10条为例 第一次 rownum <= 10) where rownum_ > 0 第二次 rownum <= 20) where rownum_ > 10
发现有一条记录在两次查询结果中重复出现,不知道问题出在哪里,请忙帮看看。
另: DATA_UPDATE_TIME 的值有重复,不知道跟它有没有关系。 如果按ID排的话就不会出现这个问题
解答: 如果order by 不能唯一确定记录的顺序就会出现这个问题。 解决的方法是把分页部分全部拿到最外层进行。
- select * from (
- select row_.*, rownum rownum_
- from (
- select p.id from table1 p
- order by p.DATA_UPDATE_TIME desc
- ) row_
- )
- where rownum_ > ? and rownum_ <= ?
2 排序的id顺序: Oracle中的rownum的是在取数据的时候产生的序号,所以想对指定排序的数据去指定的rowmun行数据就必须注意了。 SQL> select rownum ,id,name from student order by name; ROWNUM ID NAME ---------- ------ --------------------------------------------------- 3 200003 李三 2 200002 王二 1 200001 张一 4 200004 赵四 可以看出,rownum并不是按照name列来生成的序号。系统是按照记录插入时的顺序给记录排的号,rowid也是顺序分配的。为了解决这个问题,必须使用子查询 SQL> select rownum ,id,name from (select * from student order by name); ROWNUM ID NAME ---------- ------ --------------------------------------------------- 1 200003 李三 2 200002 王二 3 200001 张一 4 200004 赵四 这样就成了按name排序,并且用rownum标出正确序号(有小到大)
------------ 参考Oracle的rownum原理和使用 http://tenn.iteye.com/blog/99339
在Oracle中,要按特定条件查询前N条记录,用个rownum就搞定了。 select * from emp where rownum <= 5 而且书上也告诫,不能对rownum用">",这也就意味着,如果你想用 select * from emp where rownum > 5 则是失败的。要知道为什么会失败,则需要了解rownum背后的机制: 1 Oracle executes your query.
2 Oracle fetches the first row and calls it row number 1.
3 Have we gotten past row number meets the criteria? If no, then Oracle discards the row, If yes, then Oracle return the row.
4 Oracle fetches the next row and advances the row number (to 2, and then to 3, and then to 4, and so forth).
5 Go to step 3.
了解了原理,就知道rownum>不会成功,因为在第三步的时候查询出的行已经被丢弃,第四步查出来的rownum仍然是1,这样永远也不会成功。
同样道理,rownum如果单独用=,也只有在rownum=1时才有用。
对于rownum来说它是oracle系统顺序分配为从查询返回的行的编号,返回的第一行分配的是1,第二行是2,依此类推,这个伪字段可以用于限制查询返回的总行数,而且rownum不能以任何表的名称作为前缀。 举例说明: 例如表:student(学生)表,表结构为: ID char(6) --学号 name VARCHAR2(10) --姓名 create table student (ID char(6), name VARCHAR2(100)); insert into sale values('200001',‘张一’); insert into sale values('200002',‘王二’); insert into sale values('200003',‘李三’); insert into sale values('200004',‘赵四’); commit; (1) rownum 对于等于某值的查询条件 如果希望找到学生表中第一条学生的信息,可以使用rownum=1作为条件。但是想找到学生表中第二条学生的信息,使用rownum=2结果查不到数据。因为rownum都是从1开始,但是1以上的自然数在rownum做等于判断是时认为都是false条件,所以无法查到rownum = n(n>1的自然数)。 SQL> select rownum,id,name from student where rownum=1;(可以用在限制返回记录条数的地方,保证不出错,如:隐式游标) SQL> select rownum,id,name from student where rownum=1; ROWNUM ID NAME ---------- ------ --------------------------------------------------- 1 200001 张一 SQL> select rownum,id,name from student where rownum =2; ROWNUM ID NAME ---------- ------ --------------------------------------------------- (2)rownum对于大于某值的查询条件 如果想找到从第二行记录以后的记录,当使用rownum>2是查不出记录的,原因是由于rownum是一个总是从1开始的伪列,Oracle 认为rownum> n(n>1的自然数)这种条件依旧不成立,所以查不到记录 SQL> select rownum,id,name from student where rownum >2; ROWNUM ID NAME ---------- ------ --------------------------------------------------- 那如何才能找到第二行以后的记录呀。可以使用以下的子查询方法来解决。注意子查询中的rownum必须要有别名,否则还是不会查出记录来,这是因为rownum不是某个表的列,如果不起别名的话,无法知道rownum是子查询的列还是主查询的列。 SQL>select * from(select rownum no ,id,name from student) where no>2; NO ID NAME ---------- ------ --------------------------------------------------- 3 200003 李三 4 200004 赵四 SQL> select * from(select rownum,id,name from student)where rownum>2; ROWNUM ID NAME ---------- ------ --------------------------------------------------- (3)rownum对于小于某值的查询条件 如果想找到第三条记录以前的记录,当使用rownum<3是能得到两条记录的。显然rownum对于rownum<n((n>1的自然数)的条件认为是成立的,所以可以找到记录。 SQL> select rownum,id,name from student where rownum <3; ROWNUM ID NAME ---------- ------ --------------------------------------------------- 1 200001 张一 2 200002 王二 综上几种情况,可能有时候需要查询rownum在某区间的数据,那怎么办呀从上可以看出rownum对小于某值的查询条件是人为true的,rownum对于大于某值的查询条件直接认为是false的,但是可以间接的让它转为认为是true的。那就必须使用子查询。例如要查询rownum在第二行到第三行之间的数据,包括第二行和第三行数据,那么我们只能写以下语句,先让它返回小于等于三的记录行,然后在主查询中判断新的rownum的别名列大于等于二的记录行。但是这样的操作会在大数据集中影响速度。 SQL> select * from (select rownum no,id,name from student where rownum<=3 ) where no >=2; NO ID NAME ---------- ------ --------------------------------------------------- 2 200002 王二 3 200003 李三 (4)rownum和排序 Oracle中的rownum的是在取数据的时候产生的序号,所以想对指定排序的数据去指定的rowmun行数据就必须注意了。 SQL> select rownum ,id,name from student order by name; ROWNUM ID NAME ---------- ------ --------------------------------------------------- 3 200003 李三 2 200002 王二 1 200001 张一 4 200004 赵四 可以看出,rownum并不是按照name列来生成的序号。系统是按照记录插入时的顺序给记录排的号,rowid也是顺序分配的。为了解决这个问题,必须使用子查询 SQL> select rownum ,id,name from (select * from student order by name); ROWNUM ID NAME ---------- ------ --------------------------------------------------- 1 200003 李三 2 200002 王二 3 200001 张一 4 200004 赵四 这样就成了按name排序,并且用rownum标出正确序号(有小到大)
仅为个人理解.请指正Hibernate Session, 其作用无需多言. 在运用中为避免资源消耗,一般都会手动封装一个HibernateUtil类(未使用Spring管理的前提下). 该类的作用使Hibernate加载配置文件config, 创建sessionFactory等只运行一次. 实际运用中,经常需要将当前线程和session绑定.一般的用法为使用ThreadLocal: 在HibernateUtil类中封装hibernate的管理.通过openSession取得 session,并将其放入ThreadLocal变量中. 这样业务逻辑中仅需通过工具类取得当前线程对应的session.使用完毕后,调用工具类closeSession方法将 session关闭,当前线程的ThreadLocal变量置为NULL. 保证线程归还线程池复用后,ThreadLocal为空,以免出现导致其他线程访问到本线程变量. 而后,Hibernate的SessionFactory提供获取session的新方法getCurrentSession (获得与当前线程绑定的session). 内部通过代理封装,此方式得到的session 不仅和当前线程绑定,也无需手动开关. 默认在事务提交之后,session自动关闭. 需注意的是,必须在事务开启的前提之下才可使用此种方式获得的session.此外hibernate.cfg.xml配置文件中也许配置 <property name="current_session_context_class">thread</property> 基于线程 末了,引入Spring之后.sessionfactory的创建等都交给spring管理.Spring也提供了HibernateTemplate,HibernateDaoSupport这样的封装方法. 用户可以不再考虑session的管理,事务的开启关闭.只需配置事务即可. 而所谓session关闭后,因延迟加载导致前台无法显示的问题以往解决方式为强制全部加载,现在也可通过在web.xml中配置 org.springframework.orm.hibernate3.support.OpenSessionInViewFilter来解决. ------------------------------以下内容为工地资料------------------------------------------------------------------------------- 1 OpenSession : 手动打开,需手动关闭.[所以代码中充斥着try catch --sf.openSession --打开事务,提交-回滚 finall关闭session的代码]
2 threadlocal : hibernate给出的提示. 在HibernateUtil工具类中,new出threadlocal ,放入opensession.这样可以使当前线程绑定session.
使用后需关闭session,将threadlocal中session变量置为null .
3 getCurrentSession: hibernate3的新特性. 无需手动关闭session,自动获取当前线程的session,若无则新建之. 需在配置文件中配置thread属性.表明和当前线程绑定.
参考网友资料,getCurrentSession模式,内部开启了session自动提交的功能且使用getCurrentSession的session,及时做load操作,也需要打开事务.
以下为ThreadLocal的参考资料
http://space.itpub.net/9252210/viewspace-594453 今天在做数据导出的时候,由于用户名的密码使用的是特殊字符,所以遇到了错误 代码:“EXP-00056: 遇到 ORACLE 错误 12154”,网上查找原因,需要用引号扩起来,但是os不同,方式也不同: windows os: exp username/"""password"""@devdb --3个双引号扩密码 linux/unix os: exp 'username/"password"@devdb' --1个双引号扩密码,1个单引号扩全部 实验结果如下: 1.创建带有特殊字符密码的用户 C:\Documents and Settings\Home>sqlplus /nolog SQL*Plus: Release 10.2.0.1.0 - Production on 星期四 5月 7 17:37:36 2009 Copyright (c) 1982, 2005, Oracle. All rights reserved. SQL> connsys/oracle@devdbas sysdba 已连接。 SQL> create user exp identified by "12345!@#$%"; 用户已创建。 SQL> grant connect, resource to exp; 授权成功。 SQL> conn exp/"12345!@#$%"@devdb2 已连接。 SQL> create table table1 as select * from dual; 表已创建。 SQL> exit 2.windows os导出测试 C:\Documents and Settings\Home>expexp/12345!@#$%@devdbfile=c:\exp.dmp wner=exp Export: Release 9.2.0.1.0 - Production on 星期四 5月 7 17:39:42 2009 Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved. EXP-00056: 遇到 ORACLE 错误 12154 ORA-12154: TNS: 无法处理服务名 EXP-00000: 导出终止失败 C:\Documents and Settings\Home>exp exp/"12345!@#$%"@devdb2file=c:\exp.dmp wner=exp Export: Release 9.2.0.1.0 - Production on 星期四 5月 7 17:39:57 2009 Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved. EXP-00056: 遇到 ORACLE 错误 12154 ORA-12154: TNS: 无法处理服务名 EXP-00000: 导出终止失败 C:\Documents and Settings\Home>exp exp/"""12345!@#$%"""@devdb2file=c:\exp.dmp wner=exp Export: Release 9.2.0.1.0 - Production on 星期四 5月 7 17:41:54 2009 Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved. 连接到: Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - Production With the Partitioning, Real Application Clusters, OLAP, Data Mining and Real Application Testing options 已导出 ZHS16GBK 字符集和 AL16UTF16 NCHAR 字符集 . 正在导出 pre-schema 过程对象和操作 . 正在导出用户 EXP 的外部函数库名称 . 导出 PUBLIC 类型同义词 . 导出私有类型同义词 . 正在导出用户 EXP 的对象类型定义 即将导出 EXP 的对象 ... . 正在导出数据库链接 . 正在导出序号 . 正在导出群集定义 . 即将导出 EXP 的表通过常规路径 ... . . 正在导出表 TABLE1 1 行被导出 . 正在导出同义词 . 正在导出视图 . 正在导出存储的过程 . 正在导出运算符 . 正在导出引用完整性约束条件 . 正在导出触发器 . 正在导出索引类型 . 正在导出位图, 功能性索引和可扩展索引 . 正在导出后期表活动 . 正在导出实体化视图 . 正在导出快照日志 . 正在导出作业队列 . 正在导出刷新组和子组 . 正在导出维 . 正在导出 post-schema 过程对象和操作 . 正在导出统计 在没有警告的情况下成功终止导出。 3.linux/unix os导出测试 [oracle@rac2 ~]$ expexp/12345!@#$%@devdbfile=./exp.dmp wner=exp -bash:!@#$%@devdb: event not found [oracle@rac2 ~]$ exp exp/"""12345!@#$%"""@devdbfile=./exp.dmp wner=exp -bash:!@#$%"""@devdb: event not found [oracle@rac2 ~]$exp 'exp/"12345!@#$%"@devdb'file=./exp.dmp wner=exp Export: Release 10.2.0.4.0 - Production on Thu May 7 19:21:32 2009 Copyright (c) 1982, 2007, Oracle. All rights reserved. Connected to: Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - Production With the Partitioning, Real Application Clusters, OLAP, Data Mining and Real Application Testing options Export done in US7ASCII character set and AL16UTF16 NCHAR character set server uses ZHS16GBK character set (possible charset conversion) . exporting pre-schema procedural objects and actions . exporting foreign function library names for user EXP . exporting PUBLIC type synonyms . exporting private type synonyms . exporting object type definitions for user EXP About to export EXP's objects ... . exporting database links . exporting sequence numbers . exporting cluster definitions . about to export EXP's tables via Conventional Path ... . . exporting table TABLE1 1 rows exported . exporting synonyms . exporting views . exporting stored procedures . exporting operators . exporting referential integrity constraints . exporting triggers . exporting indextypes . exporting bitmap, functional and extensible indexes . exporting posttables actions . exporting materialized views . exporting snapshot logs . exporting job queues . exporting refresh groups and children . exporting dimensions . exporting post-schema procedural objects and actions . exporting statistics Export terminated successfully without warnings.
关于group by 的应用问题 数据库内容为下面
写一SQL得出下面内容:
贴出SQL结果如下:(MySQL版本) create table gosin_temp(rq varchar(10),shengfu nchar(1)); insert into gosin_temp values('2009-05-09','胜'); insert into gosin_temp values('2009-05-09','胜'); insert into gosin_temp values('2009-05-09','负'); insert into gosin_temp values('2009-05-09','负'); insert into gosin_temp values('2009-05-10','胜'); insert into gosin_temp values('2009-05-10','负'); insert into gosin_temp values('2009-05-10','负'); select * from gosin_temp; 得到结果的SQL: select a1.rq,a1.胜,b1.负 from (select a.rq, count(a.shengfu) 胜 from gosin_temp a where a.shengfu='胜' group by a.rq) a1, (select b.rq, count(b.shengfu) 负 from gosin_temp b where b.shengfu='负' group by b.rq) b1 where a1.rq = b1.rq 类似的题目还有很多,如: 胜 负 1 a b 2 b a 3 b a 要求写一SQL语句,输出如下结果: 胜 负 a 1 2 b 2 1 其实都一样 只要熟悉使用group by 就不觉得难了。
摘自: http://www.iteye.com/topic/766418 ----------------------------------------- 1,什么是Servlet 2,Servlet有什么作用 3,Servlet的生命周期 4,Servlet怎么处理一个请求 5,Servlet与JSP有什么区别 6,Servlet里的cookie技术 7,Servlet里的过滤器 8,Servlet里的监听器 一,什么是Servlet? Servlet是一个Java编写的程序,此程序是基于Http协议的,在服务器端运行的(如tomcat), 是按照Servlet规范编写的一个Java类。
二,Servlet有什么作用? 主要是处理客户端的请求并将其结果发送到客户端。
三,Servlet的生命周期? Servlet的生命周期是由Servlet的容器来控制的,它可以分为3个阶段;初始化,运行,销毁。
初始化阶段: 1,Servlet容器加载servlet类,把servlet类的.class文件中的数据读到内存中。 2,然后Servlet容器创建一个ServletConfig对象。ServletConfig对象包含了Servlet的初始化配置信息。 3,Servlet容器创建一个servlet对象。 4,Servlet容器调用servlet对象的init方法进行初始化。
运行阶段: 当servlet容器接收到一个请求时,servlet容器会针对这个请求创建servletRequest和servletResponse对象。 然后调用service方法。并把这两个参数传递给service方法。Service方法通过servletRequest对象获得请求的 信息。并处理该请求。再通过servletResponse对象生成这个请求的响应结果。然后销毁servletRequest和 servletResponse对象。我们不管这个请求是post提交的还是get提交的,最终这个请求都会由service方法来处理。
web服务器接受到一个http请求后,web服务器会将请求移交给 servlet容器,servlet容器首先对所请求的URL进行解析并根据 web.xml 配置文件找到相应的处理servlet,同时将request、response对象传递给它,servlet通过request对象可知道客户端 的请求 者、请求信息以及其他的信息等,servlet在处理完请求后会把所有需要返回的信息放入response对象中并返回到客户端, servlet一旦处理 完请求,servlet容器就会刷新response对象,并把控制权重新返回给web服务器。 销毁阶段: 当Web应用被终止时,servlet容器会先调用servlet对象的destrory方法,然后再销毁servlet对象, 同时也会销毁与servlet对象相关联的servletConfig对象。我们可以在destroy方法的实现中,释放 servlet所占用的资源,如关闭数据库连接,关闭文件输入输出流等。
在这里该注意的地方: 在servlet生命周期中,servlet的初始化和和销毁阶段只会发生一次,而service方法执行的次数则取决于servlet被客户 端访问的次数 四,Servlet怎么处理一个请求? 当用户发送一个请求到某个Servlet的时候,Servlet容器会创建一个ServletRequst和ServletResponse对象。 在ServletRequst对象中封装了用户的请求信息,然后Servlet容器把ServletRequst和ServletResponse对象 传给用户所请求的Servlet,Servlet把处理好的结果写在ServletResponse中,然后Servlet容器把响应结果传 给用户。 五,Servlet与JSP有什么区别? 1,jsp经编译后就是servlet,也可以说jsp等于servlet。 2,jsp更擅长页面(表现)。servlet更擅长逻辑编辑。 (最核心的区别)。 3,在实际应用中采用Servlet来控制业务流程,而采用JSP来生成动态网页.在struts框架中, JSP位于MVC设计模式的视图层,而Servlet位于控制层。 六,Servlet里的cookie技术? cookies是一种WEB服务器通过浏览器在访问者的硬盘上存储信息的手段,是由Netscape公司开发出来的。 cookie技术的好处: 1,Cookie有效期限未到时,Cookie能使用户在不键入密码和用户名的情况下进入曾经浏览过的一些站点。 2,Cookie能使站点跟踪特定访问者的访问次数、最后访问时间和访问者进入站点的路径。 创建一个cookie Java代码 - //里面的两个参数分别是cookie的名和cookie的值
-
- response.addCookie(new Cookie("abc","10000000"));
使用cookie Java代码 - Cookie[] cook =request.getCookies();//用一个Cookie数组来接收
-
- for(int j=0;j<cook.length;j++){//通过循环来打印Cookie
-
- cook[j].getName()://取cookie的名
- cook[j].getValue()://去cookie的值
-
- }
七,Servlet里的过滤器? 过滤器的主要作用 1,任何系统或网站都要判断用户是否登录。 2,网络聊天系统或论坛,功能是过滤非法文字 3,统一解决编码 (2)怎么创建一个过滤器: 1,生成一个普通的class类,实现Filter接口(javax.servlet.Filter;)。 2,重写接口里面的三个方法:init,doFilter,destroy。 3,然后在web.xml配置过滤器。
八,Servlet里的监听器? 监听器的作用:自动执行一些操作。
三种servlet监听器: 对request的监听。对session的监听。对application的监听。
怎么创建一个session监听器: 1,生成一个普通的class类,如果是对session的监听,则实现HttpSessionListener。 2,然后重写里面的五个方法: Java代码 - public void sessionCreated(HttpSessionEvent arg0) {} // 创建
-
- public void sessionDestroyed(HttpSessionEvent arg0) {} // 销毁
-
- public void attributeAdded(HttpSessionEvent arg0) {} // 增加
-
- public void attributeRemoved(HttpSessionEvent arg0) {} // 删除
-
- public void attributeReplaced(HttpSessionEvent arg0) {} // 替换
这个写的不错.有的没看懂. 目前没时间细看,暂时先转载. 摘自: http://www.jdon.com/artichect/state.htm --------------------------------------------------- 板桥里人 http://www.jdon.com 2006/1/2(转载请保留) 这是一个实战中非常重要但是容易被忽视的概念,说它重要,是因为它比数据库重要;说它容易被忽视也是同样的原因,它经常被数据库概念替代。 如果你经验和经历中没有状态这个概念,极端地说:可能你的Java系统经验还未积累到一定程度,状态是每个Java程序员深入Java系统后必然碰到的问题。 本文我想试图表达的是:状态分两种:活动的状态对象和持久化的状态。而数据库中的数据只是状态的一种持久化结果,而Java系统 运行时,我们更多的可能是和一种活动的状态打交道,这种活动的状态存在内存中,而不是持久化到硬盘上,当然,需要时你可以通过数据库/文件持久化到硬盘上。 但是,如果你以数据库数据替代状态,那么就可能导致数据库的频繁访问,而且 你的系统会变成一个非对象化的、紧耦合、到处是分散数据块的糟糕系统。这样的系统并不比传统的两层结构好到哪里!也不会比Jsp里嵌入Java代码伪三层系统高明到什么地方。 什么是状态? 只要有对象就可能有状态,任何一个对象活动时,都有自己的状态属性,类的 字段属性极有可能成为状态,我们现在经常使用的Domain model其实就是一种 包含状态的对象,如果你对状态没有深入掌握,就不可能真正掌握对象系统特点,或者是Domain Model的执行情况。 对于初学者,经常会疑问:我是将数据放在HttpSession中还是Request中,这里 其实已经开始接触状态,一旦你接触状态,你就要开始小心,因为你可能会将内存泄漏的恶魔导引进来。 内存泄漏的恶魔爆发时刻取决于你状态的生存周期和系统并发访问量。 状态的生存周期也就是包含这个状态的对象的生命周期,在简单系统中,我们只 需要通过new创建对象,然后它的消亡就会依靠JVM垃圾回收机制回收,但是事情会这么简单吗? 状态的危险还会发生在多线程环境下,当多个线程对同一个内存中状态写操作时,这时怎么办?如果这个状 态持久化在数据库中,我们会依赖数据库提供的强大事务机制防止这种并发死锁,但是如果是在内存中,你就很难办,因此,我们就尽量避免发生这种多线程同时访 问一个状态的现象,而Singleton单例模式极容易发生这种现象,因此实践中,单例模式是J2EE开发中需要避免的,相关帖子讨论见: http://www.jdon.com/jive/article.jsp?forum=91&thread=17578 我们接触的Web容器或Jsp/Servlet本质就是一个多线程,这也是很多初学者不知道的, 因为多线程编程是复杂或困难的,因此才有jsp/Servlet这样的上层封装,但是我们使用他们 时,实际在进行多线程编程。 生命周期和多线程并发使得我们简单的面向对象系统变得异常复杂和难以掌握起来。下面我从这个两个角度,给出两种模式思维解决之道。 生命周期(Scope) 生命周期(Scope)就是指状态的活动周期,状态对象是什么时候被创建;然后什么时候被销毁,很显然,如果状态对象还没有被创建或已经被销毁,你再 访问这个状态对象可能失败,而状态的生命周期控制是可能散落在运行程序的各个地方,如果不象状态模式那样进行统一控制,有可能整个系统是危机四伏的。 状态的生命周期其实就是对象生命周期,更加细化地说:是Domain Model这个对象的生命周期。这在一个以领域模型为驱动的设计概念中不可回避的课题,而领域模型实战的复杂性就复杂在此。 状态的生命周期在J2EE中目前有三种:Request/Session和 Application,Request是每个客户端发出的一次请求,这是J2EE系统中最基本的事件激活单元, 当服务器端推出一个页面到客户端时,意味着这个Request的结束。那么如果我们的状态保存在Request中,意味着在request结束之前,这个 请求经历的任何一个环节都可以对这个状态(对象)进行操作。(掌握这个原理,对于你学习Struts和JSF很有帮助) 如果是Session,则一直和该客户端有关,只要是该客户端发出的每次request的任何环节都可以对这个状态(对象)进行操作。 如果是Application,则意味着这个状态是当前Web项目的全局状态。 这三种状态形式都是以将状态保存在内存中形式存在的,是和持久化状态相对的。是一种内存活动状态。 生命周期的选取当然是越短越好,这样,这个状态对象就可以被自动销毁,从而避免了 大访问量下的内存泄漏,但是在大访问量下,对象频繁创建和销毁是耗费性能的。 那么,我们可能经常使用HttpSession来保存状态,这时你极有可能造成内存泄漏,我经常在 Jdon论坛上看到将很多数据库数据暂时保存在HttpSession中想法,这是相当危险的,因为一旦并发用户很多,相当多的HttpSession包 含了状态,而状态中有可能有更多其他引用,因此内存很快会爆满,或者垃圾回收机制频繁启动,造成应用系统运行暂停或缓慢。 当你将状态放入HttpSession时,难道没有考虑将其手工消除吗?你要知道所有Web容器 (Tomcat/Weblogic等)都不会自动替你清除那些你可能不用的状态对象啊。如果每个人只管新增元素,不管重整或管理,这个系统能不变得混乱 吗?代码上这种现象我们是通过Refactoring等结构/行为模式来解决,那么在运行时的状态管理呢? 状态管理模式或者说对象管理模式正是解决这种问题的。 按照该模式,你必须手工自己管理放在HttpSession的状态,比如你为每个HttpSession 设立一个状态容器最大尺寸,当超过这个尺寸时,你需要将不用的状态从容器去除, 但是如果这个客户端在Session失效期内又来访问这个状态怎么办?那么你可能需要先临时将状态序列化保存到硬盘上,等Session失效期到达后再真正删除。 是不是觉得很麻烦? 捷径是有: 1. 尽量少使用HttpSession保存状态,这对集群环境也是有利的,见该贴讨论: http://www.jdon.com/jive/article.jsp?forum=121&thread=22282 那么这些状态放在哪里?使用Application的缓存中, 2. 使用状态管理中间件,目前有几个选择:EJB的有态Bean;NanoContainer之类状态相关的微容器。那么Spring可以吗?目前没有发现有 该功能,甚至在Spring容器内无法直接使用Session性质的状态,只能通过线程级别的ThreadLocal来实现(对不起,你又要开始回到远古 的汇编线程时代了);而Jdon框架则可以。 下面我们谈谈Application的状态,在这个范围内,一个对象状态可以被多个用户反复访问,在这个级别,状态类似数据库中数据,因为可以使用数据库来替代这个级别的状态,所以将状态放入缓存这个深层次技术被大多数初学者忽视了,甚至产生了对数据库依赖心理。 缓存中的状态 虽然我们将状态保存在Application中,但是我们不可避免还是遇到Session同样的状态管理问题,这个问题所幸的是有专门缓存中间件解决 了,当然,在一个多服务器集群系统,如果一个客户端在一个服务器中存放了状态,那么能否在另外一个服务器的内存中访问到呢?回答是肯定的,前提是你必须使 用分布式缓存系统。 目前分布式缓存系统是靠EJB服务器完成,当JBoss 5在2006变成完全解耦、可肢解时, 我们就可以使用原本只支持EJB的JBoss分布式缓存系统来支持我们的普通JavaBeans了(POJO)。这其中目前可能会花费一些力气,因为还没有一个统一的POJO构件接口标准,我相信以后 可能会有。 如果你不想花费力气,而且可能就只是一台服务器,可以通过双核芯片提升性能,那么单态缓存如果实现?很简单,使用一个缓存产品如OsCache等,将其设定保存在 Application中,或者在web.xml中进行一下简单的配置即可。 但是,这时你可能碰到另外一个问题:状态的唯一标识,如何通过唯一标识从缓存中那么 多对象状态中取出你要的那一个呢?比较琐碎。 有没有一个框架帮助你省却这些麻烦,当然推荐Jdon Framework,只要将包含状态的类(主要是Domain Model)继承特定的类或接口(接口在1.4版本实现)即可,这个类的对象运行时就会被缓存或从缓存中读取,再也无需你照料缓存了,就这么简单。 当然,Jdon Framework的底层缓存器是可以被替代,使用你喜欢的缓存产品,因为jdon Framework是基于Ioc设计,构件之间是完全解耦、可彻底肢解,能够通过配置替代和更换的。 如果你不明白这个道理,需要好好研究一下Ioc模式带给我们革命性的新变化。 从以上也可以看出:java复杂性还在于我们需要在编码时,却要想象其运行时的情形。而这种翻译联想没有深厚的实践功底,是很难顺利完成的。 状态管理中间件 自从J2EE开辟中间件时代以来,就有相当多的高级中间件提供与具体应用无关的通用功能,状态管理中间件很早就有之,EJB的有态Session Bean是一个代表。 一个中间件不但要有良好的松耦合设计,我们暂时称为静态设计;更要有优秀的动态设计,例如状态管理就属于一种动态设计。 当然,如果你比较谦虚,不但要选择一些静态设计很好的框架或中间件;而且还要依赖一些拥有良好的动态运行管理的中间件。 EJB无论是EJB1.X/EJB2.X/EJB3.X.在状态管理上要更加优秀,当然EJB3.X又吸收了优秀的静态设计概念,但是因为需要有一个具体服务器实现过程,这个过程中存在一些陷阱,如In-Box问题等。 Spring无疑是一个静态设计非常优秀框架,它一直在AOP上孜孜不倦,力图探索一条从AOP角度进行动态运行管理干预捷径,相信会有惊人结果,当然,这种细粒度的AOP需要实践检验,当然如果整入JDK 6.0就更好。 而Jdon Framework则试图在目前两者之间寻求了一个平衡,既有Ioc/AOP优秀的静态设计,虽然在AOP上不及Spring前卫;但提供了切实Session和Cache状态管理; 如果你不需要EJB的分布式多服务器集群功能;又不是AOP的超级粉丝,无疑使用Jdon Framework之类的框架无疑是简化方便的。 状态设计的难点 最后,我不得不重申,并不是有了良好的状态管理框架就可以高枕无忧了,状态的设计其实是我们每个项目必须面临的可变课题,如果状态复杂了可以使用状态模式对付,可惜往往状态不够复杂。 一个对象本身属性和状态是应该耦合在一起,还是进行分离,属性和状态没有明显的泾渭分明的界限,我们举一个例子: 论坛Forum这个对象,它有一些字段属性,如论坛名称、论坛描述,还有其他一些相关属性:如该论坛 的最新发帖;该论坛的发贴量,后两者好像也是论坛字段,但是他们可能经常变化的,应该属于状态,那么状态和Forum这个主体对象是什么关系?是将该论坛 的最新发帖和该论坛的发贴量两个字段并入Forum这个Domain Model中,还是应该单独建立一个状态对象?如果进行分离,分离的依据是什么? 当然,这里分离的依据是因为对象的生存周期不同。对于我们熟悉的课题,我们能够马上分辨出其中的生存周期,如果是不熟悉领域的建模呢? 所以,大家已经明白:状态设计的难点是:如何粒度细化地创建模型对象;然后分辨出其中动态的状态性质。这是域建模实战中一个难点。 很多人问我:你提倡的域建模、设计模式和框架是什么意思?为什么说他们是Java开发设计的三件宝呢?或者说三个典型知识点呢?我想通过本篇我已经通过状态这个概念稍微解释了域建模的一些特点。 当前,MDA中的四色原型模式Archetype将帮助我们更好地分辨出类的属性、状态和行为,这是一场带来以后十年的软件革命,
我对浏览器请求流程的理解: (1) 访问流程: (1.1) 系统运行在某web容器,如Tomcat(其运行和weblogic不同,Tomcat只有一个进程). 其预设有初始的线程数. (1.2) 浏览器打开某网站,网站及给其分配一个sessionID(页面隐式的发起request, 由Tomcat 某个特定的监听线程给予response一个sessionid). 该sessionid用以识别本次访问. (1.3) 用户点击登录/注册, 浏览器发起一个新的request, 由Tomcat线程池中空闲的线程进行 处理. 反馈结果于前台展现.如线程池中线程不足,则Tomcat每次按照一定规则创建出更多的空闲线程(其初始值,增加值,及最大值依据配置文件/JDK/硬件).
来源: http://www.iteye.com/topic/960652 http://godsend-jin.iteye.com/blog/1004386 -------------------------------------------------------------------- 最近在做登录和权限控制模块,用到了session,发现session的好多方法都不熟悉,而且以前也听说过JsessionId 之类session窃取的事, 对这些一直都是一知半解。今天索性google了很多资料,先上sun的官网去看session的文档了解一些方法,又找了别人关于session的看法。 总结如下: 1,session是什么? what session经常译为会话,以打电话为例,从开始拨号到挂断电话就是你会话的生存周期。 2,session 做什么用的 why? 首先举个例子: 咖啡店举行 消费满5杯咖啡赠送一杯的活动,可每个人一次消费5杯的时候非常少。这时候有3种办法: 1,店员看到每个顾客时都能记住其消费了多少杯,从而给其优惠,这是协议本身具有状态 2,给每个顾客一个卡片,上面记录顾客的每次消费,这是客户端保存状态 3,给每个顾客一个卡片,卡片上只有一个编号,顾客每次的消费记录在店里,这就是 服务端有状态 而http本身是无状态的,所以我们只能使用2,3中方法,来保存一些信息。 实际采用的是第3种方法,服务器段保存一次会话所有的信息,并生成一个唯一的id,这个id没有规律而且不会重复,将这个id传回到客户段, 保存到cookie中。每次访问服务器时,客户端都会偷偷将这个id传到服务器,服务器根据id查到这次会话保存的内容。就能实现会话中共享一些数据。 3,session怎样创建和销毁 ? how session是保存在内存中的,所以会有一些性能上的影响。因此本着这个原则,session是只有在使用到的时候才会被创建,如果始终没有用到 session,这个session是永远不会被创建的。 比如: 访问servlet ,只要你代码中没有 request.getSession()或request.getSession(true);这两行是等价的,那session是不会创建。 又 当你访问静态页面时,根本不会生成servlet,所以也不会创建session。 下面解释一些疑惑: session是第一次请求时创建的? 大家都知道 jsp是被编译成servlet才执行的,问题就在jsp编译的过程。 jsp中有个<%@ page session="true/false"%> 这个开关表示是否创建session,当你不写这行时,它会默认给你加上这句。所以会造成上面的疑惑。 当然还有一些标签中可能有getSession()操作,会产生一些不必要的session。 session只能在服务端销毁,有三种途径: 1,到达session的最大请求间隔时间时,2,session。invalidate() 3,服务器进程当掉。 这里也有一些疑惑: 浏览器关闭时,session就会注销。 首先浏览器关闭时,浏览器并没有给服务器发送任何消息,所以服务器不会知道浏览器何时关闭了。 上面我们知道取得session 是因为浏览器cookie中有sessionid,而普通cookie通常会是会话cookie,也就是说浏览器关闭时,这个cookie会被注销, 所以当你再访问服务器时就没有sessionid了,所以造成session关闭了的假象,如果昵称通过特殊方法将sessionid传递给服务器,你会发现session还在。 如果想让cookie保存时间长一些,就需要手动指定cookie的过期时间 4,实际项目中的难点: 1,浏览器禁用cookie 这就没办法保存sessionid了,可以采用url重写,转发,加隐藏字段等方法来将sessionid传给服务器。 如: baidu.com:jsessionid=adfasdfasdfasdfasdfafdadf?asdfasdf baidu.com?jsessionid=asdfasdfasdfadsfad&&adfdf 这根据服务器的不同实现,第一种可以将普通参数区分开。 2,多人共用session的问题 例: a 访问 baidu.com ,但他没有帐号,于是他将连接 baidu.com/login.jsp?jsessionid=adsfasdfad(这个a的sessionid) 发给B, B登录 后,a就相当于用b的帐号登录了。你们可以在在本地试试。 解决方法: 当发现通过sessionid从url指定时, 创建一个新的session,将旧session的信息复制到 新sessoin中,然后将新session注销。 就能防止上面那种情况了。 3,一个帐号多地方登录 比如: 你用abc帐号登录了baidu.com,有打开了一个浏览器,又用abc帐号登录了一次。当不设计敏感操作时,这无所谓,而当你做一些敏感操 作时就必须禁止这样情况,防止同时操作,造成重复操作,或者数据损坏。 解决方法: 监听session,将username和sessionid对应起来,当username再次登录时,注销掉以前的session,保存现在的session,这也是 一种比较不错的方案。 这是 sghcel 画的图,挺不错的: 其他: 1、session在何时被创建 2、session何时被删除
3、如何做到在浏览器关闭时删除session
4、有个HttpSessionListener是怎么回事 你可以创建这样的listener去监控session的创建和销毁事件,使得在发生这样的事件时你可以做一些相应的工作。注意是session的创建和销毁动作触发listener, 而不是相反。类似的与HttpSession有关的listener还有 HttpSessionBindingListener,HttpSessionActivationListener和 HttpSessionAttributeListener。 5、存放在session中的对象必须是可序列化的吗 6、如何才能正确的应付客户端禁止cookie的可能性 7、开两个浏览器窗口访问应用程序会使用同一个session还是不同的session 参见第三小节对cookie的讨论,对session来说是只认id不认人,因此不同的浏览器,不同的窗口打开方式以及不同的cookie存储方式都会对这个问题 的答案有影响。 8、如何防止用户打开两个浏览器窗口操作导致的session混乱 9、为什么在Weblogic Server中改变session的值后要重新调用一次session.setValue
10、HttpSession 和 hibernate session 有什么区别?
AOP术语介绍
1. 正常的编程为从上到下的调用,执行
2. 加入了安全性检查,日志这样的代码. 这是一个横切的问题,其于正常的业务毫无关系.
横切的问题会散布在代码的各个角落 .
3.这个横切就是横切性的关注点: Cross cutting concern
4. 将横切的关注点都放在一个类中(如动态代理项目中的SecurityHandler.java).这个类就叫做切面.
对横切关注点进行模块化,这个模块化的类就叫做切面类(Aspect对应的类) ,
5. 在切面类中对某个问题如日志或安全性检查的具体实现方法,叫做横切关注点的具体实现(称为Advice).
6. 这个Advice可以进行分类. :在业务方法执行之前,之后.异常时候……
7. Advice应用的目标方法范围(那些方法之前,之后,异常….)这个过滤范围叫做切入点Pointcut()
8 .植入
Advice应用的目标方法的过程叫做植入(Weave)
Spring只支持针对业务方法执行前,执行后进行植入. 即只支持方法级别的植入
植入的地方就叫做连接点.
Spring的Aop是使用代理模式.
横切问题的处理思路:
发现横切性的问题,将其模块化(切片).然后形成切片类,在其中实现这些横切性的功能.
摘自: http://www.iteye.com/topic/257191 ----------------- 今天下午研究了半天hashcode()和equals()方法,终于有了一点点的明白,写下来与大家分享(zhaoxudong 2008.10.23晚21.36)。 1. 首先equals()和hashcode()这两个方法都是从object类中继承过来的。 equals()方法在object类中定义如下: public boolean equals(Object obj) { return (this == obj); } 很明显是对两个对象的地址值进行的比较(即比较引用是否相同)。但是我们必需清楚,当String 、Math、还有Integer、Double。。。。等这些封装类在使用equals()方法时,已经覆盖了object类的equals()方法。比 如在String类中如下: public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = count; if (n == anotherString.count) { char v1[] = value; char v2[] = anotherString.value; int i = offset; int j = anotherString.offset; while (n-- != 0) { if (v1[i++] != v2[j++]) return false; } return true; } } return false; } 很明显,这是进行的内容比较,而已经不再是地址的比较。依次类推Double、Integer、Math。。。。等等这些类都是重写了equals()方法的,从而进行的是内容的比较。当然了基本类型是进行值的比较,这个没有什么好说的。 我们还应该注意,Java语言对equals()的要求如下,这些要求是必须遵循的: • 对称性:如果x.equals(y)返回是“true”,那么y.equals(x)也应该返回是“true”。 • 反射性:x.equals(x)必须返回是“true”。 • 类推性:如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那么z.equals(x)也应该返回是“true”。 • 还有一致性:如果x.equals(y)返回是“true”,只要x和y内容一直不变,不管你重复x.equals(y)多少次,返回都是“true”。 • 任何情况下,x.equals(null),永远返回是“false”;x.equals(和x不同类型的对象)永远返回是“false”。 以上这五点是重写equals()方法时,必须遵守的准则,如果违反会出现意想不到的结果,请大家一定要遵守。 2. 其次是hashcode() 方法,在object类中定义如下: public native int hashCode(); 说明是一个本地方法,它的实现是根据本地机器相关的。当然我们可以在自己写的类中覆盖hashcode()方法,比如String、 Integer、Double。。。。等等这些类都是覆盖了hashcode()方法的。例如在String类中定义的hashcode()方法如下: public int hashCode() { int h = hash; if (h == 0) { int off = offset; char val[] = value; int len = count; for (int i = 0; i < len; i++) { h = 31*h + val[off++]; } hash = h; } return h; } 解释一下这个程序(String的API中写到): s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1] 使用 int 算法,这里 s[i] 是字符串的第 i 个字符,n 是字符串的长度,^ 表示求幂。(空字符串的哈希码为 0。) 3.这里我们首先要明白一个问题: equals()相等的两个对象,hashcode()一定相等; equals()不相等的两个对象,却并不能证明他们的hashcode()不相等。换句话说,equals()方法不相等的两个对象,hashcode()有可能相等。(我的理解是由于哈希码在生成的时候产生冲突造成的)。 反过来:hashcode()不等,一定能推出equals()也不等;hashcode()相等,equals()可能相等,也可能不等。解释 下第3点的使用范围,我的理解是在object、String等类中都能使用。在object类中,hashcode()方法是本地方法,返回的是对象的 地址值,而object类中的equals()方法比较的也是两个对象的地址值,如果equals()相等,说明两个对象地址值也相等,当然 hashcode()也就相等了;在String类中,equals()返回的是两个对象内容的比较,当两个对象内容相等时, Hashcode()方法根据String类的重写(第2点里面已经分析了)代码的分析,也可知道hashcode()返回结果也会相等。以此类 推,可以知道Integer、Double等封装类中经过重写的equals()和hashcode()方法也同样适合于这个原则。当然没有经过重写的 类,在继承了object类的equals()和hashcode()方法后,也会遵守这个原则。 4.谈到hashcode()和equals()就不能不说到hashset,hashmap,hashtable中的使用,具体是怎样呢,请看如下分析: Hashset是继承Set接口,Set接口又实现Collection接口,这是层次关系。那么hashset是根据什么原理来存取对象的呢? 在hashset中不允许出现重复对象,元素的位置也是不确定的。在hashset中又是怎样判定元素是否重复的呢?这就是问题的关键所在,经过一下午的查询求证终于获得了一点启示,和大家分享一下,在java的集合中,判断两个对象是否相等的规则是: 1),判断两个对象的hashCode是否相等 如果不相等,认为两个对象也不相等,完毕 如果相等,转入2) (这一点只是为了提高存储效率而要求的,其实理论上没有也可以,但如果没有,实际使用时效率会大大降低,所以我们这里将其做为必需的。后面会重点讲到这个问题。) 2),判断两个对象用equals运算是否相等 如果不相等,认为两个对象也不相等 如果相等,认为两个对象相等(equals()是判断两个对象是否相等的关键) 为什么是两条准则,难道用第一条不行吗?不行,因为前面已经说了,hashcode()相等时,equals()方法也可能不等,所以必须用第2条准则进行限制,才能保证加入的为非重复元素。 比如下面的代码: public static void main(String args[]){ String s1=new String("zhaoxudong"); String s2=new String("zhaoxudong"); System.out.println(s1==s2);//false System.out.println(s1.equals(s2));//true System.out.println(s1.hashCode());//s1.hashcode()等于s2.hashcode() System.out.println(s2.hashCode()); Set hashset=new HashSet(); hashset.add(s1); hashset.add(s2); /*实质上在添加s1,s2时,运用上面说到的两点准则,可以知道hashset认为s1和s2是相等的,是在添加重复元素,所以让s2覆盖了s1;*/ Iterator it=hashset.iterator(); while(it.hasNext()) { System.out.println(it.next()); } 最后在while循环的时候只打印出了一个”zhaoxudong”。 输出结果为:false true -967303459 -967303459 这是因为String类已经重写了equals()方法和hashcode()方法,所以在根据上面的第1.2条原则判定时,hashset认为它们是相等的对象,进行了重复添加。 但是看下面的程序: import java.util.*; public class HashSetTest { public static void main(String[] args) { HashSet hs=new HashSet(); hs.add(new Student(1,"zhangsan")); hs.add(new Student(2,"lisi")); hs.add(new Student(3,"wangwu")); hs.add(new Student(1,"zhangsan")); Iterator it=hs.iterator(); while(it.hasNext()) { System.out.println(it.next()); } } } class Student { int num; String name; Student(int num,String name) { this.num=num; this.name=name; } public String toString() { return num+":"+name; } } 输出结果为: 1:zhangsan 1:zhangsan 3:wangwu 2:lisi 问题出现了,为什么hashset添加了相等的元素呢,这是不是和hashset的原则违背了呢?回答是:没有 因为在根据hashcode()对两次建立的new Student(1,"zhangsan")对象进行比较时,生成的是不同的哈希码值,所以hashset把他当作不同的对象对待了,当然此时的 equals()方法返回的值也不等(这个不用解释了吧)。那么为什么会生成不同的哈希码值呢?上面我们在比较s1和s2的时候不是生成了同样的哈希码 吗?原因就在于我们自己写的Student类并没有重新自己的hashcode()和equals()方法,所以在比较时,是继承的object类中的 hashcode()方法,呵呵,各位还记得object类中的hashcode()方法比较的是什么吧!! 它是一个本地方法,比较的是对象的地址(引用地址),使用new方法创建对象,两次生成的当然是不同的对象了(这个大家都能理解吧。。。),造成 的结果就是两个对象的hashcode()返回的值不一样。所以根据第一个准则,hashset会把它们当作不同的对象对待,自然也用不着第二个准则进行 判定了。那么怎么解决这个问题呢?? 答案是:在Student类中重新hashcode()和equals()方法。 例如: class Student { int num; String name; Student(int num,String name) { this.num=num; this.name=name; } public int hashCode() { return num*name.hashCode(); } public boolean equals(Object o) { Student s=(Student)o; return num==s.num && name.equals(s.name); } public String toString() { return num+":"+name; } } 根据重写的方法,即便两次调用了new Student(1,"zhangsan"),我们在获得对象的哈希码时,根据重写的方法hashcode(),获得的哈希码肯定是一样的(这一点应该没有疑问吧)。 当然根据equals()方法我们也可判断是相同的。所以在向hashset集合中添加时把它们当作重复元素看待了。所以运行修改后的程序时,我们会发现运行结果是: 1:zhangsan 3:wangwu 2:lisi 可以看到重复元素的问题已经消除。 关于在hibernate的pojo类中,重新equals()和hashcode()的问题: 1),重点是equals,重写hashCode只是技术要求(为了提高效率) 2),为什么要重写equals呢,因为在java的集合框架中,是通过equals来判断两个对象是否相等的 3),在hibernate中,经常使用set集合来保存相关对象,而set集合是不允许重复的。我们再来谈谈前面提到在向hashset集合中添加元素时,怎样判断对象是否相同的准则,前面说了两条,其实只要重写equals()这一条也可以。 但当hashset中元素比较多时,或者是重写的equals()方法比较复杂时,我们只用equals()方法进行比较判断,效率也会非常低, 所以引入了hashcode()这个方法,只是为了提高效率,但是我觉得这是非常有必要的(所以我们在前面以两条准则来进行hashset的元素是否重复 的判断)。 比如可以这样写: public int hashCode(){ return 1;}//等价于hashcode无效 这样做的效果就是在比较哈希码的时候不能进行判断,因为每个对象返回的哈希码都是1,每次都必须要经过比较equals()方法后才能进行判断是否重复,这当然会引起效率的大大降低。 我有一个问题,如果像前面提到的在hashset中判断元素是否重复的必要方法是equals()方法(根据网上找到的观点),但是这里并没有涉及到关于哈希表的问题,可是这个集合却叫hashset,这是为什么?? 我想,在hashmap,hashtable中的存储操作,依然遵守上面的准则。所以这里不再多说。这些是今天看书,网上查询资料,自己总结出来 的,部分代码和语言是引述,但是千真万确是自己总结出来的。有错误之处和不详细不清楚的地方还请大家指出,我也是初学者,所以难免会有错误的地方,希望大 家共同讨论。
|
|
CALENDER
| 日 | 一 | 二 | 三 | 四 | 五 | 六 |
---|
27 | 28 | 29 | 30 | 31 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|
常用链接
留言簿(3)
随笔分类(22)
随笔档案(76)
文章分类(12)
文章档案(17)
搜索
积分与排名
最新评论
评论排行榜
Powered By: 博客园 模板提供:沪江博客
|