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。
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时当作基本类型看。
克隆前: 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
可以看到深克隆后克隆对象值改变,原始值不会该改变。
克隆前: 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
可以看到深克隆后克隆对象值改变,原始值不会该改变。
阅读全文
0 0
- JAVA基础总结(一)
- java基础总结(一)!!!
- Java基础总结(一)
- java基础总结(一)
- Java基础总结(一)
- Java基础总结(一)
- java基础总结(一)
- JAVA基础总结(一)
- JAVA基础总结一:
- java基础总结一
- Java基础总结(一)
- JAVA基础总结一
- Java并发总结(一):线程基础
- Java基础的自我总结(一)
- Java基础总结大纲(一)
- Java并发总结(一):线程基础
- 新手java基础总结(一)
- Java基础知识点总结(一)
- Cookie和Session的区别
- 穿越时空的思念
- python自动化运维学习------使用模块psutil获取系统cpu、内存、磁盘、网络、进程等信息
- shell删除和复制文件(带文件名更新)
- Freesacle主板方案,CVBS四路模拟视频主板,仪器|仪表|数据采集|数据分析|信号控制|硬件定制,单片机
- java基础总结(一)
- 移动平台对 META 标签的定义
- 【位运算 && 线段树】SDUT 3930 皮卡丘的梦想2
- 解决乱码
- 转吴军数学之美对几种类型的科学从事人员
- SIT与UAT的分别
- python基础教程(第二版)
- Docker挂载主机目录Docker访问出现Permission denied的解决办法
- 从spark streaming checkpoint文件中还原数据