java基础总结(一)

来源:互联网 发布:mac入门使用教程 编辑:程序博客网 时间:2024/05/01 23:14

文章是参考:http://blog.csdn.net/lifetragedy/article/details/9751079 的总结

总结源码地址:https://github.com/TangXW777/java-base/tree/master

一、hashcode和equals()

覆写equals方法:
1 使用instanceof操作符检查“实参是否为正确的类型”。
2 对于类中的每一个“关键域”,检查实参中的域与当前对象中对应的域值。
3. 对于非float和double类型的原语类型域,使用==比较;
4 对于对象引用域,递归调用equals方法;
5 对于float域,使用Float.floatToIntBits(afloat)转换为int,再使用==比较;
6 对于double域,使用Double.doubleToLongBits(adouble)转换为int,再使用==比较;
7 对于数组域,调用Arrays.equals方法。

覆写hashcode:
1. 把某个非零常数值,例如17,保存在int变量result中;
2. 对于对象中每一个关键域f(指equals方法中考虑的每一个域):
3, boolean型,计算(f? 0 : 1);
4. byte,char,short型,计算(int);
5. long型,计算(int)(f ^ (f>>>32));
6. float型,计算Float.floatToIntBits(afloat);
7. double型,计算Double.doubleToLongBits(adouble)得到一个long,再执行[2.3];
8. 对象引用,递归调用它的hashCode方法;
9. 数组域,对其中每个元素调用它的hashCode方法。
10. 将上面计算得到的散列码保存到int变量c,然后执行result=37*result+c;
11. 返回result。


我们可以总结为:equals()相等的,那么hashcode一定相同,但是hashcode相同,equals不一定相同,因为hashcode比较的是引用的地址,equals比较的是逻辑的值是否相当,比如一个人的name,age是否相同,而hashcode的引用地址值往往是根据name,age这些值计算得出。
下面看代码:
public class HashTest {    private short ashort;    private char achar;    private byte abyte;    private boolean abool;    private long along;    private float afloat;    private double adouble;    private Unit aObject;    private int[] ints;    private Unit[] units;    @Override    public boolean equals(Object obj) {        if(!(obj instanceof HashTest)){            return false;        }        HashTest hashTest = (HashTest)obj;        return hashTest.ashort ==  ashort                && hashTest.achar == achar                && hashTest.abyte == abyte                && hashTest.abool == abool                && hashTest.along == along                && Float.floatToIntBits(hashTest.afloat) ==                    Float.floatToIntBits(afloat)                && Double.doubleToLongBits(hashTest.adouble) ==                    Double.doubleToLongBits(adouble)                && hashTest.aObject.equals(aObject)                && equalsInts(hashTest.ints)                && equalsUnits(hashTest.units);    }    // 比较int数组    private boolean equalsInts(int[] aints){        return Arrays.equals(ints, aints);    }    // 比较unit数组    private boolean equalsUnits(Unit[] aUnits){        return Arrays.equals(units, aUnits);    }    @Override    public int hashCode() {        int result = 17;  // 随意赋一个初始值        result = 31 * result + (int) ashort;        result = 31 * result + (int) achar;        result = 31 * result + (int) abyte;        result = 31 * result + (abool ? 0 : 1);        result = 31 * result + (int) (along ^ (along >>> 32));        result = 31 * result + Float.floatToIntBits(afloat);        long tolong = Double.doubleToLongBits(adouble);        result = 31 * result + (int) (tolong ^ (tolong >>> 32));        result = 31 * result + aObject.hashCode();        result = 31 * result + intsHashCode(ints);        result = 31 * result + unitsHashCode(units);        return result;    }    private int intsHashCode(int[] aints) {        int result = 17;        for (int i = 0; i < aints.length; i++)            result = 31 * result + aints[i];        return result;    }    private int unitsHashCode(Unit[] aUnits) {        int result = 17;        for (int i = 0; i < aUnits.length; i++)            result = 31 * result + aUnits[i].hashCode();        return result;    }}
那么上面重写hashcode的时候为什么都要* 31,因为31是个神奇的数字,因为任何数n * 31就可以被JVM优化为 (n << 5) -n,移位和减法的操作效率要比乘法的操作效率高的多,对左移现在很多虚拟机里面都有做相关优化,并且31只占用5bits!


二、shallow clone(浅复制)和deep clone(深复制)

shallow clone简单说就是克隆引用类型时,其克隆的是引用的地址,所以当克隆的对象值改变时,原值也会改变。但是deep clone克隆引用类型时,克隆的是引用的对象值,所以克隆对象值改变时,原值不改变,下面看代码:

shallow clone:
public class ShadowClone implements Cloneable{    // 基本类型    private int a;    // 非基本类型    private String b;    // 非基本类型    private int[] c;    @Override    public Object clone(){        ShadowClone sc = null;        try{            sc = (ShadowClone)super.clone();        }catch(CloneNotSupportedException e){            e.printStackTrace();        }        return sc;    }    public int getA() {        return a;    }    public void setA(int a) {        this.a = a;    }    public String getB() {        return b;    }    public void setB(String b) {        this.b = b;    }    public int[] getC() {        return c;    }    public void setC(int[] c) {        this.c = c;    }    // 测试    public static void main(String[] args) throws CloneNotSupportedException{        ShadowClone c1 = new ShadowClone();        c1.setA(100) ;        c1.setB("clone1") ;        c1.setC(new int[]{1000}) ;        System.out.println("克隆前: c1.a="+c1.getA() );        System.out.println("克隆前: c1.b="+c1.getB() );        System.out.println("克隆前: c1.c[0]="+c1.getC()[0]);        System.out.println("-----------") ;        // 克隆对象c2,并对c2的属性A,B,C进行修改        ShadowClone c2 = (ShadowClone) c1.clone();        // 对c2进行修改        c2.setA(50) ;        c2.setB("clone2");        int []a = c2.getC() ;        a[0]=500 ;        c2.setC(a);        System.out.println("克隆修改后: c1.a="+c1.getA() );        System.out.println("克隆修改后: c1.b="+c1.getB() );        System.out.println("克隆修改后: c1.c[0]="+c1.getC()[0]);        System.out.println("---------------") ;        System.out.println("克隆后: c2.a=" + c2.getA());        System.out.println("克隆后: c2.b=" + c2.getB());        System.out.println("克隆后: c2.c[0]=" + c2.getC()[0]);    }}
测试结果:
克隆前: c1.a=100
克隆前: c1.b=clone1
克隆前: c1.c[0]=1000
-----------
克隆修改后: c1.a=100
克隆修改后: c1.b=clone1
克隆修改后: c1.c[0]=500
---------------
克隆后: c2.a=50
克隆后: c2.b=clone2
克隆后: c2.c[0]=500
从测试结果中可以看到,c2改变了数组值,那么c1也改变,那么为什么String是非基本类型,c1的String值为什么不改变。因为String比较特俗,String是不可变类,每一次在复制都是heap堆上的一个新的对像,你可以把String clone时当作基本类型看。

deep clone:
// 深克隆必须实现Serializable接口public class DeepClone implements Serializable{    private int a;    private String b;    private int[] c;    public int getA() {        return a;    }    public void setA(int a) {        this.a = a;    }    public String getB() {        return b;    }    public void setB(String b) {        this.b = b;    }    public int[] getC() {        return c;    }    public void setC(int[] c) {        this.c = c;    }    // 用序列化和发序列化实现深克隆    public static Object deepClone(Object src){        Object o = null;        try{            if(src != null){                ByteArrayOutputStream baos = new ByteArrayOutputStream();                ObjectOutputStream oos = new ObjectOutputStream(baos);                oos.writeObject(src);                oos.close();                ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());                ObjectInputStream ois = new ObjectInputStream(bais);                o = ois.readObject();                ois.close();            }        }catch (IOException e){            e.printStackTrace();        }catch (ClassNotFoundException e){            e.printStackTrace();        }        return o;    }    public static void main(String[] args) {        DeepClone dc1 = new DeepClone();        // 对dc1赋值        dc1.setA(100);        dc1.setB("clone1");        dc1.setC(new int[] { 1000 });        System.out.println("克隆前: dc1.a=" + dc1.getA());        System.out.println("克隆前: dc1.b=" + dc1.getB());        System.out.println("克隆前: dc1.c[0]=" + dc1.getC()[0]);        System.out.println("-----------");        DeepClone dc2 = (DeepClone) deepClone(dc1);        // 对c2进行修改        dc2.setA(50);        dc2.setB("clone2");        int[] a = dc2.getC();        a[0] = 500;        dc2.setC(a);        System.out.println("克隆修改前: dc1.a=" + dc1.getA());        System.out.println("克隆修改前: dc1.b=" + dc1.getB());        System.out.println("克隆修改前: dc1.c[0]=" + dc1.getC()[0]);        System.out.println("-----------");        System.out.println("克隆后: dc2.a=" + dc2.getA());        System.out.println("克隆后: dc2.b=" + dc2.getB());        System.out.println("克隆后: dc2.c[0]=" + dc2.getC()[0]);    }}
测试结果:
克隆前: dc1.a=100
克隆前: dc1.b=clone1
克隆前: dc1.c[0]=1000
-----------
克隆修改前: dc1.a=100
克隆修改前: dc1.b=clone1
克隆修改前: dc1.c[0]=1000
-----------
克隆后: dc2.a=50
克隆后: dc2.b=clone2
克隆后: dc2.c[0]=500
可以看到深克隆后克隆对象值改变,原始值不会该改变。


原创粉丝点击