我想做些事,我想做些有用的事,我想做些我可以做到的事,我想做些软件开发和测试的事……

发布新日志

  • JAVA对象中的hashCode()方法

    2008-02-28 14:02:09

    有效和正确定义hashCode()和equals() 

    每个Java对象都有hashCode()和 equals()方法。许多类忽略(Override)这些方法的缺省实施,以在对象实例之间提供更深层次的语义可比性。在Java理念和实践这一部分,Java开发人员Brian Goetz向您介绍在创建Java类以有效和准确定义hashCode()和equals()时应遵循的规则和指南。您可以在讨论论坛与作者和其它读者一同探讨您对本文的看法。(您还可以点击本文顶部或底部的讨论进入论坛。) 
    虽然Java语言不直接支持关联数组 -- 可以使用任何对象作为一个索引的数组 -- 但在根Object类中使用hashCode()方法明确表示期望广泛使用HashMap(及其前辈Hashtable)。理想情况下基于散列的容器提供有效插入和有效检索;直接在对象模式中支持散列可以促进基于散列的容器的开发和使用。 
    定义对象的相等性 
    Object类有两种方法来推断对象的标识:equals()和hashCode()。一般来说,如果您忽略了其中一种,您必须同时忽略这两种,因为两者之间有必须维持的至关重要的关系。特殊情况是根据equals() 方法,如果两个对象是相等的,它们必须有相同的hashCode()值(尽管这通常不是真的)。 
    特定类的equals()的语义在Implementer的左侧定义;定义对特定类来说equals()意味着什么是其设计工作的一部分。Object提供的缺省实施简单引用下面等式: 
    public boolean equals(Object obj) { return (this == obj); } 
    在这种缺省实施情况下,只有它们引用真正同一个对象时这两个引用才是相等的。同样,Object提供的hashCode()的缺省实施通过将对象的内存地址对映于一个整数值来生成。由于在某些架构上,地址空间大于int值的范围,两个不同的对象有相同的hashCode()是可能的。如果您忽略了hashCode(),您仍旧可以使用System.identityHashCode()方法来接入这类缺省值。 
    忽略 equals() -- 简单实例 
    缺省情况下,equals()和hashCode()基于标识的实施是合理的,但对于某些类来说,它们希望放宽等式的定义。例如,Integer类定义equals() 与下面类似: 
    public boolean equals(Object obj) { 
    return (obj instanceof Integer 
    && intValue() == ((Integer) obj).intValue()); 

    在这个定义中,只有在包含相同的整数值的情况下这两个Integer对象是相等的。结合将不可修改的Integer,这使得使用Integer作为HashMap中的关键字是切实可行的。这种基于值的Equal方法可以由Java类库中的所有原始封装类使用,如Integer、Float、Character和Boolean以及String(如果两个String对象包含相同顺序的字符,那它们是相等的)。由于这些类都是不可修改的并且可以实施hashCode()和equals(),它们都可以做为很好的散列关键字。 
    为什么忽略 equals()和hashCode()? 
    如果Integer不忽略equals() 和 hashCode()情况又将如何?如果我们从未在HashMap或其它基于散列的集合中使用Integer作为关键字的话,什么也不会发生。但是,如果我们在HashMap中使用这类Integer对象作为关键字,我们将不能够可靠地检索相关的值,除非我们在get()调用中使用与put()调用中极其类似的Integer实例。这要求确保在我们的整个程序中,只能使用对应于特定整数值的Integer对象的一个实例。不用说,这种方法极不方便而且错误频频。 
    Object的interface contract要求如果根据 equals()两个对象是相等的,那么它们必须有相同的hashCode()值。当其识别能力整个包含在equals()中时,为什么我们的根对象类需要hashCode()?hashCode()方法纯粹用于提高效率。Java平台设计人员预计到了典型Java应用程序中基于散列的集合类(Collection Class)的重要性--如Hashtable、HashMap和HashSet,并且使用equals()与许多对象进行比较在计算方面非常昂贵。使所有Java对象都能够支持 hashCode()并结合使用基于散列的集合,可以实现有效的存储和检索。 
    实施equals()和hashCode()的需求 
    实施equals()和 hashCode()有一些限制,Object文件中列举出了这些限制。特别是equals()方法必须显示以下属性: 
    Symmetry:两个引用,a和 b,a.equals(b) if and only if b.equals(a) 
    Reflexivity:所有非空引用, a.equals(a) 
    Transitivity:If a.equals(b) and b.equals(c), then a.equals(c) 
    Consistency with hashCode():两个相等的对象必须有相同的hashCode()值 
    Object的规范中并没有明确要求equals()和 hashCode() 必须一致 -- 它们的结果在随后的调用中将是相同的,假设“不改变对象相等性比较中使用的任何信息。”这听起来象“计算的结果将不改变,除非实际情况如此。”这一模糊声明通常解释为相等性和散列值计算应是对象的可确定性功能,而不是其它。 
    对象相等性意味着什么? 
    人们很容易满足Object类规范对equals() 和 hashCode() 的要求。决定是否和如何忽略equals()除了判断以外,还要求其它。在简单的不可修值类中,如Integer(事实上是几乎所有不可修改的类),选择相当明显 -- 相等性应基于基本对象状态的相等性。在Integer情况下,对象的唯一状态是基本的整数值。 
    对于可修改对象来说,答案并不总是如此清楚。equals() 和hashCode() 是否应基于对象的标识(象缺省实施)或对象的状态(象Integer和String)?没有简单的答案 -- 它取决于类的计划使用。对于象List和Map这样的容器来说,人们对此争论不已。Java类库中的大多数类,包括容器类,错误出现在根据对象状态来提供equals()和hashCode()实施。 
    如果对象的hashCode()值可以基于其状态进行更改,那么当使用这类对象作为基于散列的集合中的关键字时我们必须注意,确保当它们用于作为散列关键字时,我们并不允许更改它们的状态。所有基于散列的集合假设,当对象的散列值用于作为集合中的关键字时它不会改变。如果当关键字在集合中时它的散列代码被更改,那么将产生一些不可预测和容易混淆的结果。实践过程中这通常不是问题 -- 我们并不经常使用象List这样的可修改对象做为HashMap中的关键字。 
    一个简单的可修改类的例子是Point,它根据状态来定义equals()和hashCode()。如果两个Point 对象引用相同的(x, y)座标,Point的散列值来源于x和y座标值的IEEE 754-bit表示,那么它们是相等的。 
    对于比较复杂的类来说,equals()和hashCode()的行为可能甚至受到superclass或interface的影响。例如,List接口要求如果并且只有另一个对象是List,而且它们有相同顺序的相同的Elements(由Element上的Object.equals() 定义),List对象等于另一个对象。hashCode()的需求更特殊--list的hashCode()值必须符合以下计算: 
    hashCode = 1; 
    Iterator i = list.iterator(); 
    while (i.hasNext()) { 
    Object obj = i.next(); 
    hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode()); 

    不仅仅散列值取决于list的内容,而且还规定了结合各个Element的散列值的特殊算法。(String类规定类似的算法用于计算String的散列值。) 
    编写自己的equals()和hashCode()方法 
    忽略缺省的equals()方法比较简单,但如果不违反对称(Symmetry)或传递性(Transitivity)需求,忽略已经忽略的equals() 方法极其棘手。当忽略equals()时,您应该总是在equals()中包括一些Javadoc注释,以帮助那些希望能够正确扩展您的类的用户。 
    作为一个简单的例子,考虑以下类: 
    class A { 
    final B someNonNullField; 
    C someOtherField; 
    int someNonStateField; 

    我们应如何编写该类的equals()的方法?这种方法适用于许多情况: 
    public boolean equals(Object other) { 
    // Not strictly necessary, but often a good optimization 
    if (this == other) 
    return true; 
    if (!(other instanceof A)) 
    return false; 
    A otherA = (A) other; 
    return 
    (someNonNullField.equals(otherA.someNonNullField)) 
    && ((someOtherField == null) 
    ? otherA.someOtherField == null 
    : someOtherField.equals(otherA.someOtherField))); 

    现在我们定义了equals(),我们必须以统一的方法来定义hashCode()。一种统一但并不总是有效的定义hashCode()的方法如下: 
    public int hashCode() { return 0; } 
    这种方法将生成大量的条目并显著降低HashMaps的性能,但它符合规范。一个更合理的hashCode()实施应该是这样: 
    public int hashCode() { 
    int hash = 1; 
    hash = hash * 31 + someNonNullField.hashCode(); 
    hash = hash * 31 
    + (someOtherField == null ? 0 : someOtherField.hashCode()); 
    return hash; 

    注意:这两种实施都降低了类状态字段的equals()或hashCode()方法一定比例的计算能力。根据您使用的类,您可能希望降低superclass的equals()或hashCode()功能一部分计算能力。对于原始字段来说,在相关的封装类中有helper功能,可以帮助创建散列值,如Float.floatToIntBits。 
    编写一个完美的equals()方法是不现实的。通常,当扩展一个自身忽略了equals()的instantiable类时,忽略equals()是不切实际的,而且编写将被忽略的equals()方法(如在抽象类中)不同于为具体类编写equals()方法。关于实例以及说明的更详细信息请参阅Effective Java Programming Language Guide, Item 7 (参考资料) 。 
    有待改进? 
    将散列法构建到Java类库的根对象类中是一种非常明智的设计折衷方法 -- 它使使用基于散列的容器变得如此简单和高效。但是,人们对Java类库中的散列算法和对象相等性的方法和实施提出了许多批评。java.util中基于散列的容器非常方便和简便易用,但可能不适用于需要非常高性能的应用程序。虽然其中大部分将不会改变,但当您设计严重依赖于基于散列的容器效率的应用程序时必须考虑这些因素,它们包括: 
    太小的散列范围。使用int而不是long作为hashCode()的返回类型增加了散列冲突的几率。 
    糟糕的散列值分配。短strings和小型integers的散列值是它们自己的小整数,接近于其它“邻近”对象的散列值。一个循规导矩(Well-behaved)的散列函数将在该散列范围内更均匀地分配散列值。 
    无定义的散列操作。虽然某些类,如String和List,定义了将其Element的散列值结合到一个散列值中使用的散列算法,但语言规范不定义将多个对象的散列值结合到新散列值中的任何批准的方法。我们在前面编写自己的equals()和hashCode()方法中讨论的List、String或实例类A使用的诀窍都很简单,但算术上还远远不够完美。类库不提供任何散列算法的方便实施,它可以简化更先进的hashCode()实施的创建。
    当扩展已经忽略了equals()的 instantiable类时很难编写equals()。当扩展已经忽略了equals()的 instantiable类时,定义equals()的“显而易见的”方式都不能满足equals()方法的对称或传递性需求。这意味着当忽略equals()时,您必须了解您正在扩展的类的结构和实施详细信息,甚至需要暴露基本类中的机密字段,它违反了面向对象的设计的原则。 
    结束语 
    通过统一定义equals()和hashCode(),您可以提升类作为基于散列的集合中的关键字的使用性。有两种方法来定义对象的相等性和散列值:基于标识,它是Object提供的缺省方法;基于状态,它要求忽略equals()和hashCode()。当对象的状态更改时如果对象的散列值发生变化,确信当状态作为散列关键字使用时您不允许更更改其状态。 
  • MYECLIPSE6.0.1注册码(转载)!仅共学习研究使用

    2007-12-15 23:47:53

    package test;

    import java.io.*;

    public class MyEclipseGen {
        private static final String LL = "Decompiling this copyrighted software is a violation of both your license agreement and the Digital Millenium Copyright Act of 1998 (http://www.loc.gov/copyright/legislation/dmca.pdf). Under section 1204 of the DMCA, penalties range up to a $500,000 fine or up to five years imprisonment for a first offense. Think about it; pay for a license, avoid prosecution, and feel better about yourself.";
        public String getSerial(String userId, String licenseNum) {
            java.util.Calendar cal = java.util.Calendar.getInstance();
            cal.add(1, 3);
            cal.add(6, -1);
            java.text.NumberFormat nf = new java.text.DecimalFormat("000");
            licenseNum = nf.format(Integer.valueOf(licenseNum));
            String verTime = new StringBuilder("-").append(new java.text.
                    SimpleDateFormat("yyMMdd").format(cal.getTime())).append("0").
                             toString();
            String type = "YE3MP-";
            String need = new StringBuilder(userId.substring(0, 1)).append(type).
                          append("300").append(licenseNum).append(verTime).toString();
            String dx = new StringBuilder(need).append(LL).append(userId).toString();
            int suf = this.decode(dx);
            String code = new StringBuilder(need).append(String.valueOf(suf)).
                          toString();
            return this.change(code);
        }

        private int decode(String s) {
            int i;
            char[] ac;
            int j;
            int k;
            i = 0;
            ac = s.toCharArray();
            j = 0;
            k = ac.length;
            while (j < k) {
                i = (31 * i) + ac[j];
                j++;
            }
            return Math.abs(i);
        }

        private String change(String s) {
            byte[] abyte0;
            char[] ac;
            int i;
            int k;
            int j;
            abyte0 = s.getBytes();
            ac = new char[s.length()];
            i = 0;
            k = abyte0.length;
            while (i < k) {
                j = abyte0[i];
                if ((j >= 48) && (j <= 57)) {
                    j = (((j - 48) + 5) % 10) + 48;
                } else if ((j >= 65) && (j <= 90)) {
                    j = (((j - 65) + 13) % 26) + 65;
                } else if ((j >= 97) && (j <= 122)) {
                    j = (((j - 97) + 13) % 26) + 97;
                }
                ac[i] = (char) j;
                i++;
            }
            return String.valueOf(ac);
        }

        public MyEclipseGen() {
            super();
        }

        public static void main(String[] args) {
            try {
                System.out.println("please input register name:");
                BufferedReader reader = new BufferedReader(new InputStreamReader(
                        System.in));
                String userId = null;
                userId = reader.readLine();
                MyEclipseGen myeclipsegen = new MyEclipseGen();
                String res = myeclipsegen.getSerial(userId, "20");
                System.out.println("Serial:" + res);
                reader.readLine();
            } catch (IOException ex) {
            }
        }
    }

     

    ===========================

    运行该代码,在控制台中会出现:

    please input register name:
    ×××××(你的name)

    即生成序列号:

    please input register name:
    camille
    Serial:pLR8ZC-855575-53668556514322852

  • hibernate中的save和saveOrUpdate(单主键情况)[转]

    2007-12-15 23:39:33

    在单主键情况,两个方法到底有啥区别:

    如果持久对象使用了自增长的单主键(一般情况下也都是这么做)。通常两者没什么区别。

    但是如果持久对象使用自己赋值的单主键,那么使用saveOrUpdate就不大合适了。
    例如:如果你将一个持久对象的主键赋值了,你本想插入这条记录,但实际上执行的是更新,因为hibernate会认为你的主键存在了,那么它会采用更新。但如果你使用save,那么hibernate直接插入数据。

  • 后台报SQL Error: 1064, SQLState: 42000错误

    2007-12-15 23:15:10

     今天在调试程序中发现类似下面这种错误
    - SQL Error: 1064, SQLState: 42000- You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'REQUIRE, PLAN_DATE, COMPILE, AUDITOR, AGREE_PERSON, AGREE_DATE, ADD_BY, MOD_BY, ' at line 1- Could not synchronize database state with sessionorg.hibernate.exception.SQLGrammarException: Could not execute JDBC batch update

    所以要注意,在定义字段时,不要和MYSQL的保留字段有相同的。


    MySQL的保留字有以下这些,使用中需要注意。

    ADD ALL ALTER
    ANALYZE AND AS
    ASC ASENSITIVE BEFORE
    BETWEEN BIGINT BINARY
    BLOB BOTH BY
    CALL CASCADE CASE
    CHANGE CHAR CHARACTER
    CHECK COLLATE COLUMN
    CONDITION CONNECTION CONSTRAINT
    CONTINUE CONVERT CREATE
    CROSS CURRENT_DATE CURRENT_TIME
    CURRENT_TIMESTAMP CURRENT_USER CURSOR
    DATABASE DATABASES DAY_HOUR
    DAY_MICROSECOND DAY_MINUTE DAY_SECOND
    DEC DECIMAL DECLARE
    DEFAULT DELAYED DELETE
    DESC DESCRIBE DETERMINISTIC
    DISTINCT DISTINCTROW DIV
    DOUBLE DROP DUAL
    EACH ELSE ELSEIF
    ENCLOSED ESCAPED EXISTS
    EXIT EXPLAIN FALSE
    FETCH FLOAT FLOAT4
    FLOAT8 FOR FORCE
    FOREIGN FROM FULLTEXT
    GOTO GRANT GROUP
    HAVING HIGH_PRIORITY HOUR_MICROSECOND
    HOUR_MINUTE HOUR_SECOND IF
    IGNORE IN INDEX
    INFILE INNER INOUT
    INSENSITIVE INSERT INT
    INT1 INT2 INT3
    INT4 INT8 INTEGER
    INTERVAL INTO IS
    ITERATE JOIN KEY
    KEYS KILL LABEL
    LEADING LEAVE LEFT
    LIKE LIMIT LINEAR
    LINES LOAD LOCALTIME
    LOCALTIMESTAMP LOCK LONG
    LONGBLOB LONGTEXT LOOP
    LOW_PRIORITY MATCH MEDIUMBLOB
    MEDIUMINT MEDIUMTEXT MIDDLEINT
    MINUTE_MICROSECOND MINUTE_SECOND MOD
    MODIFIES NATURAL NOT
    NO_WRITE_TO_BINLOG NULL NUMERIC
    ON OPTIMIZE OPTION
    OPTIONALLY OR ORDER
    OUT OUTER OUTFILE
    PRECISION PRIMARY PROCEDURE
    PURGE RAID0 RANGE
    READ READS REAL
    REFERENCES REGEXP RELEASE
    RENAME REPEAT REPLACE
    REQUIRE RESTRICT RETURN
    REVOKE RIGHT RLIKE
    SCHEMA SCHEMAS SECOND_MICROSECOND
    SELECT SENSITIVE SEPARATOR
    SET SHOW SMALLINT
    SPATIAL SPECIFIC SQL
    SQLEXCEPTION SQLSTATE SQLWARNING
    SQL_BIG_RESULT SQL_CALC_FOUND_ROWS SQL_SMALL_RESULT
    SSL STARTING STRAIGHT_JOIN
    TABLE TERMINATED THEN
    TINYBLOB TINYINT TINYTEXT
    TO TRAILING TRIGGER
    TRUE UNDO UNION
    UNIQUE UNLOCK UNSIGNED
    UPDATE USAGE USE
    USING UTC_DATE UTC_TIME
    UTC_TIMESTAMP VALUES VARBINARY
    VARCHAR VARCHARACTER VARYING
    WHEN WHERE WHILE
    WITH WRITE X509
    XOR YEAR_MONTH ZEROFILL

  • eclipse中VI插件的安装,转码properties

    2007-11-21 17:41:18

         和大家分享一个不错的编写properties文件的Eclipse插件(plugin),有了它我们在编辑一些简体中文、繁体中文等Unicode文本时,就不必再使用native2ascii编码了。您可以通过Eclipse中的软件升级(Software Update)安装此插件,步骤如下:


    1、展开Eclipse的Help菜单,将鼠标移到Software Update子项,在出现的子菜单中点击Find and Install;
    2、在Install/Update对话框中选择Search for new features to install,点击Next;
    3、在Install对话框中点击New Remote Site;
    4、在New Update Site对话框的Name填入“PropEdit”或其它任意非空字符串,在URL中填入http://propedit.sourceforge.jp/eclipse/updates/;
    5、在Site to include to search列表中,除上一步加入的site外的其它选项去掉,点击Finsih;
    6、在弹出的Updates对话框中的Select the features to install列表中将所有结尾为“3.1.x”的选项去掉(适用于Eclipse 3.2版本的朋友);
    7、点击Finish关闭对话框;
    8、在下载后,同意安装,再按提示重启Eclipse,在工具条看到形似vi的按钮表示安装成功,插件可用。此时,Eclpise中所有properties文件的文件名前有绿色的P的图标作为标识。

Open Toolbar