Java私塾跟我学系列――JAVA篇 第六章 常见类的使用

 

教学目标:

i理解和掌握Object

i理解和掌握String

i理解和掌握StringBuffer

i理解和掌握StringBuilder

i掌握Math的使用

i掌握日期相关类的使用

i掌握System类的使用


 

一、Object                                                   

java.lang包中定义的Object类是所有Java类的根父类,其中定义了一些实现和支持面向对象机制的重要方法。任何Java对象,如果没有父类,就默认它继承了Object类。因此,实际上,以前的定义是下面的简略:

public class Employee extends Object 和

public class Manager extends Employee

    Object类定义许多有用的方法,包括toString(),它就是为什么Java软件中每样东西都能转换成字符串表示法的原因。(即使这仅具有有限的用途)。 

1.equals方法                                                                           

Object类定义的equals方法用于判别某个指定的对象与当前对象(调用equals方法的对象)是否等价。在Java语言中数据等价的基本含义是指两个数据的值相等。

”==”进行比较的时候,引用类型数据比较的是引用,即内存地址,基本数据类型比较的是值。

1.1  equals方法与“==”运算符的关系

equals()方法只能比较引用类型,"=="可以比较引用类型及基本类型;

特例:当用equals()方法进行比较时,对类File、String、Date及包装类来说,是比较类型及内容而不考虑引用的是否是同一个实例,因为这些类中已经重写了equals方法,实现了比较内容的功能。源码可参看JDK目录下的src.zip中的相应部分。

用"=="进行比较时,符号两边的数据类型必须一致(可自动转换的数据类型除外),否则编译出错,而用equals方法比较的两个数据只要都是引用类型即可。

示例如下:

class MyDate {

    privateint day, month, year;

    public MyDate(int day, int month, int year) {

       this.day = day;

       this.month = month;

       this.year = year;

    }

}

publicclass Test {

    publicstaticvoid main(String[] args) {

       MyDate m1 = new MyDate(8, 8, 2008);

       MyDate m2 = new MyDate(8, 8, 2008);

 

       if (m1 == m2) {

           System.out.println("m1==m2");

       } else {

           System.out.println("m1!=m2");

       }

       if (m1.equals(m2)) {

           System.out.println("m1 is equal to m2");

       } else {

           System.out.println("m1 is not equal to m2");

       }

 

       m2 = m1;

       if (m1 == m2) {

           System.out.println("m1==m2");

       } else {

           System.out.println("m1!=m2");

       }

    }

}

程序运行结果为:

m1!=m2

m1 is not equal to m2

m1==m2

 小结一下:

在引用类型的比较上,Object里面的equals方法默认的比较方式,基本上等同于“==”,都是比较内存地址,只有那几个特殊的是比较的对象的内容(属性值)。自己写的类也可以做这样的覆盖工作。下面介绍详细的做法。

1.2 覆盖equals方法

对于程序员来说,如果一个对象需要调用equals方法,应该在类中覆盖equals方法。如果覆盖了equals方法,那么具体的比较就按照你的实现进行比较了。

一般来讲:为了比较两个分离的对象(也就是内存地址不同的两个对象),自行覆盖的equals方法里面都是检查类型和值是否相同。上面那几个特殊的情况就是这样,比如String类,它覆盖了equals方法,然后在里面进行值的比较。

覆盖equals方法的一般步骤如下:

(1)用==检查是否参数就是这个对象的引用

(2)判断要比较的对象是否为null,如果是null,返回false

(3)用instanceof判断参数的类型是否正确

(4)把参数转换成合适的类型

(5)比较对象属性值是不是匹配 

示例如下:

覆盖前equals==比较的都是内存地址:

public class Test{

    public static void main(String[] args) {

       A a1 = new A();

       a1.age = 3;

       A a2 = new A();

       a2.age = 3;

       System.out.println("a1 == a2 test ="+(a1==a2));

       System.out.println("a1 equals a2 test ="+a1.equals(a2));

    }

}

class A{

    public int age = 0;

}

运行结果是:

a1 == a2 test =false

a1 equals a2 test =false 

覆盖后equals比较的是值,==比较的是内存地址

public class Test{

    public static void main(String[] args) {

       Test t = new Test();

       A a1 = new A();

       a1.age = 3;

       A a2 = new A();

       a2.age = 3;

       System.out.println("a1 == a2 test ="+(a1==a2));     

       System.out.println("a1 equals a2 test ="+a1.equals(a2));

    }

}

class A{

    public int age = 0;

    public boolean equals(Object obj){

       //第一步先判断是否同一个实例

       if(this==obj){

           returntrue;

       }

//第二步判断要比较的对象是否为null

       if (obj == null){

           returnfalse;

       }

       //第三步判断是否同一个类型

       if(obj instanceof A){

           //第四步类型相同,先转换成为同一个类型

           A a = (A)obj;

           //第五步然后进行对象属性值的比较

           if(this.age == a.age){

              return true;

           }else{

              return false;

           }

       }else{

           //类型不同,直接返回false

           return false;

       }

    }

} 

说明:如果对象的属性又是一个引用类型的话,会继续调用该引用类型的equals方法,直到最后得出相同还是不同的结果。示例如下:

public class Test{

    public static void main(String[] args) {

       Test t = new Test();

       A a1 = new A();

       a1.age = 3;

       A a2 = new A();

       a2.age = 3;

       System.out.println("a1 == a2 test ="+(a1==a2));     

       System.out.println("a1 equals a2 test ="+a1.equals(a2));

    }

}

class A{

    public int age = 0;

    public String name = "Java私塾";

    public boolean equals(Object obj){

       //第一步先判断是否同一个实例

       if(this==obj){

           returntrue;

       }

//第二步判断要比较的对象是否为null

       if (obj == null){

           returnfalse;

       }

       //第三步判断是否同一个类型

       if(obj instanceof A){

           //第四步类型相同,先转换成为同一个类型

           A a = (A)obj;

           //第五步然后进行对象属性值的比较

           if(this.age== a.age&&this.name.equals(a.name)){

              return true;

           }else{

              return false;

           }

       }else{

           //类型不同,直接返回false

           return false;

       } 

最后重要的一点规则:覆盖equals方法应该连带覆盖hashCode方法。 

2.hashCode方法                                                                  

hashCode是按照一定的算法得到的一个数值,是对象的散列码值。主要用来在集合(后面会学到)中实现快速查找等操作,也可以用于对象的比较。

在Java中,对hashCode的规定如下:

(1)在同一个应用程序执行期间,对同一个对象调用hashCode(),必须返回相同的整数结果——前提是equals()所比较的信息都不曾被改动过。至于同一个应用程序在不同执行期所得的调用结果,无需一致。

(2)如果两个对象被equals(Object)方法视为相等,那么对这两个对象调用hashCode()必须获得相同的整数结果。

(3) 如果两个对象被equals(Object) 方法视为不相等,那么对这两个对象调用hashCode()不必产生不同的整数结果。然而程序员应该意识到,对不同对象产生不同的整数结果,有可能提升hashTable(后面会学到,集合框架中的一个类)的效率。

简单地说:如果两个对象相同,那么它们的hashCode值一定要相同;如果两个对象的hashCode相同,它们并不一定相同。

在Java规范里面规定,覆盖equals方法应该连带覆盖hashCode方法,这就涉及到一个如何实现hashCode方法的问题了。 

实现一:偷懒的做法:对同一对象始终返回相同的hashCode,如下:

public int hashCode(){

      return 1;

}

它是合法的,但是不好,因为每个对象具有相同的hashCode,会使得很多使用hashCode的类的运行效率大大降低,甚至发生错误。

 

实现二:采用一定的算法来保证

在高效Java编程这本书里面,给大家介绍了一个算法,现在eclipse自动生成equals方法和hashCode方法就是用的这个算法,下面介绍一下这个算法:

(1) 将一个非0常数,例如31,储存于int result变量

(2)对对象中的每个有意义的属性f(更确切的说是被equals()所考虑的每一个属性)进行如下处理:

 A. 对这个属性计算出类型为int的hash 码 c:

    i. 如果属性是个boolean,计算(f ? 0 : 1)。

    ii. 如果属性是个byte,char,short或int,计算(int)f。

    iii. 如果属性是个long,计算(int)(f^(f >>> 32))。

    iv. 如果属性是个float,计算Float.floatToIntBits(f)。

    v. 如果属性是个double,计算Double.doubleToLongBits(f),然后将计算结果按步骤2.A.iii处理。

    vi. 如果属性是个对象引用,而且class 的equals()通过递归调用equals()的方式来比较这一属性,那么就同样也对该属性递归调用hashCode()。如果需要更复杂的比较,请对该属性运算一个范式(canonical representation),并对该范式调用hashCode()。如果属性值是null,就返回0(或其它常数;返回0 是传统做法)。

    vii. 如果属性是个数组,请将每个元素视为独立属性。也就是说对每一个有意义的元素施行上述规则,用以计算出hash 码,然后再依步骤2.B将这些数值组合起来。

B. 将步骤A计算出来的hash码 c按下列公式组合到变量result中:

result = 31*result + c;

(3) 返回result。 

示例如下:这个就是用eclipse自动生成的

public class Test{     

    private byte byteValue;

    private char charValue;

    private short shortValue;

    private int intValue;

    private long longValue;

    private boolean booleanValue;

    private float floatValue;

    private double doubleValue;

    private String uuid;

    private int[] intArray = new int[3]   

    public int hashCode() {

        final int prime = 31;

        int result = 1;

        result = prime * result + (booleanValue ? 1231 : 1237);

        result = prime * result + charValue;

        long temp;

        temp = Double.doubleToLongBits(doubleValue);

        result = prime * result + (int) (temp ^ (temp >>> 32));

        result = prime * result + Float.floatToIntBits(floatValue);

        result = prime * result + Arrays.hashCode(intArray);

        result = prime * result + intValue;

        result = prime * result + (int) (longValue ^ (longValue >>> 32));

        result = prime * result + shortValue;

        result = prime * result + ((uuid == null) ? 0 : uuid.hashCode());

        return result;

    }

}

3.toString方法                                                                 

toString()方法是Object类中定义的另一个重要方法,其格式为:

public String toString() {  

}

方法的返回值是String类型,用于描述当前对象的有关信息。Object类中实现的toString()方法是返回当前对象的类型和内存地址信息,但在一些子类(如String, Date等)中进行了重写,也可以根据需要在用户自定义类型中重写toString()方法,以返回更适用的信息。

除显式调用对象的toString()方法外,在进行String与其它类型数据的连接操作时,会自动调用toString()方法,其中又分为两种情况:

(1)引用类型数据直接调用其toString()方法转换为String类型;

(2)基本类型数据先转换为对应的包装类型,再调用该包装类的toString()方法转换为String类型。

另外,在System.out.println()方法输出引用类型的数据时,也先自动调用了该对象的toString()方法,然后再将返回的字符串输出。

示例如下:

class MyDate{

    privateint day,month,year;

    public MyDate(int d, int m, int y){

       day = d;  month = m;  year = y;

    }

class YourDate{

    privateint day,month,year;

    public YourDate(int d, int m, int y){

       day = d;   month = m;    year = y;

    }

    public String toString(){

       return  day + "-" + month + "-" + year;   

    }     

}

 publicclass Test{

    publicstaticvoid main(String args[]){

       MyDate m = new MyDate(8,8,2008);

       System.out.println(m);

       System.out.println(m.toString());

       YourDate y = new YourDate(8,8,2008);

       System.out.println(y);  

    }  

运行结果:

    cn.javass.java6.test.MyDate@1fb8ee3

cn.javass.java6.test.MyDate@1fb8ee3

8-8-2008 

toString方法被用来将一个对象转换成String表达式。当自动字符串转换发生时,它被用作编译程序的参照。System.out.println()调用下述代码:

      Date now = new Date()

      System.out.println(now)

    将被翻译成:

          System.out.println(now.toString());

对象类定义缺省的toString()方法,它返回类名称和它的引用的地址(通常情况下不是很有用)。许多类覆盖toString()以提供更有用的信息。例如,所有的包装类覆盖toString()以提供它们所代表的值的字符串格式。甚至没有字符串格式的类为了调试目的常常实现toString()来返回对象状态信息。 

 

Java私塾跟我学系列——JAVA  网址:http://www.javass.cn  电话:010-68434236

你可能感兴趣的:(java,object,职场,目标,休闲)