From 8a14e463c02a9b691d97548ab9d7dd0ffe3a14a6 Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Fri, 6 Apr 2018 20:23:58 +0800 Subject: [PATCH] auto commit --- notes/Java 基础.md | 320 ++++++++++++++++++++++++++++++++++++++++----- notes/Java 容器.md | 53 +------- 2 files changed, 292 insertions(+), 81 deletions(-) diff --git a/notes/Java 基础.md b/notes/Java 基础.md index 2a4e0221..19b9bcb5 100644 --- a/notes/Java 基础.md +++ b/notes/Java 基础.md @@ -4,8 +4,10 @@ * [static](#static) * [二、Object 通用方法](#二object-通用方法) * [概览](#概览) - * [clone()](#clone) * [equals()](#equals) + * [hashCode()](#hashcode) + * [toString()](#tostring) + * [clone()](#clone) * [四、继承](#四继承) * [访问权限](#访问权限) * [抽象类与接口](#抽象类与接口) @@ -154,6 +156,147 @@ public final void wait() throws InterruptedException protected void finalize() throws Throwable {} ``` +## equals() + +**1. equals() 与 == 的区别** + +- 对于基本类型,== 判断两个值是否相等,基本类型没有 equals() 方法。 +- 对于引用类型,== 判断两个实例是否引用同一个对象,而 equals() 判断引用的对象是否等价。 + +```java +Integer x = new Integer(1); +Integer y = new Integer(1); +System.out.println(x.equals(y)); // true +System.out.println(x == y); // false +``` + +**2. 等价关系** + +(一)自反性 + +```java +x.equals(x); // true +``` + +(二)对称性 + +```java +x.equals(y) == y.equals(x) // true +``` + +(三)传递性 + +```java +if(x.equals(y) && y.equals(z)) { + x.equals(z); // true; +} +``` + +(四)一致性 + +多次调用 equals() 方法结果不变 + +```java +x.equals(y) == x.equals(y); // true +``` + +(五)与 null 的比较 + +对任何不是 null 的对象 x 调用 x.equals(null) 结果都为 false + +```java +x.euqals(null); // false; +``` + +**3. 实现** + +- 检查是否为同一个对象的引用,如果是直接返回 true; +- 检查是否是同一个类型,如果不是,直接返回 false; +- 将 Object 实例进行转型; +- 判断每个关键域是否相等。 + +```java +public class EqualExample { + private int x; + private int y; + private int z; + + public EqualExample(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + EqualExample that = (EqualExample) o; + + if (x != that.x) return false; + if (y != that.y) return false; + return z == that.z; + } +} +``` + +## hashCode() + +hasCode() 返回散列值,而 equals() 是用来判断两个实例是否相等。相等的两个实例散列值一定要相同,但是散列值相同的两个实例不一定相等。 + +在覆盖 equals() 方法时应当总是覆盖 hashCode() 方法,保证相等的两个实例散列值也相等。 + +下面的代码中,新建了两个等价的实例,并将它们添加到 HashSet 中。我们希望将这两个实例当成一样的,只在集合中添加一个实例,但是因为 EqualExample 没有实现 hasCode() 方法,因此这两个实例的散列值是不同的,最终导致集合添加了两个等价的实例。 + +```java +EqualExample e1 = new EqualExample(1, 1, 1); +EqualExample e2 = new EqualExample(1, 1, 1); +System.out.println(e1.equals(e2)); // true +HashSet set = new HashSet<>(); +set.add(e1); +set.add(e2); +System.out.println(set.size()); // 2 +``` + +理想的散列函数应当具有均匀性,即不相等的实例应当均匀分不到所有可能的散列值上。这就要求了散列函数要把所有域的值都考虑进来,可以将每个域都当成 R 进制的某一位,然后组成一个 R 进制的整数。R 一般取 31,因为它是一个奇素数,如果是偶数的话,当出现乘法溢出,信息就会丢失,因为与 2 相乘相当于向左移一位。 + +一个数与 31 相乘可以转换成移位和减法:31\*x == (x<<5)-x。 + +```java +@Override +public int hashCode() { + int result = 17; + result = 31 * result + x; + result = 31 * result + y; + result = 31 * result + z; + return result; +} +``` + +## toString() + +默认返回 ToStringExample@4554617c 这种形式,其中 @ 后面的数值为散列码的无符号十六进制表示。 + +```java +public class ToStringExample { + private int number; + + public ToStringExample(int number) { + this.number = number; + } +} +``` + +```java +ToStringExample example = new ToStringExample(123); +System.out.println(example.toString()); +``` + +```html +ToStringExample@4554617c +``` + ## clone() **1. cloneable** @@ -161,21 +304,48 @@ protected void finalize() throws Throwable {} clone() 是 Object 的受保护方法,这意味着,如果一个类不显式去重载 clone() 就没有这个方法。 ```java -public class CloneTest { +public class CloneExample { private int a; private int b; } ``` ```java -CloneTest x = new CloneTest(); -CloneTest y = x.clone(); // 'clone()' has protected access in 'java.lang.Object' +CloneExample e1 = new CloneExample(); +CloneExample e2 = e1.clone(); // 'clone()' has protected access in 'java.lang.Object' ``` 接下来重载 Object 的 clone() 得到以下实现: ```java -public class CloneTest{ +public class CloneExample { + private int a; + private int b; + + @Override + protected CloneExample clone() throws CloneNotSupportedException { + return (CloneExample)super.clone(); + } +} +``` + +```java +CloneExample e1 = new CloneExample(); +try { + CloneExample e2 = e1.clone(); +} catch (CloneNotSupportedException e) { + e.printStackTrace(); +} +``` + +```html +java.lang.CloneNotSupportedException: CloneTest +``` + +以上抛出了 CloneNotSupportedException,这是因为 CloneTest 没有实现 Cloneable 接口。 + +```java +public class CloneExample implements Cloneable { private int a; private int b; @@ -186,44 +356,130 @@ public class CloneTest{ } ``` -```java -CloneTest x = new CloneTest(); -try { - CloneTest y = (CloneTest) x.clone(); -} catch (CloneNotSupportedException e) { - e.printStackTrace(); -} -``` - -```html -java.lang.CloneNotSupportedException: CloneTest -``` - -以上抛出了 CloneNotSupportedException,这是因为 CloneTest 没有实现 Cloneable 接口。应该注意的是,clone() 方法并不是 Cloneable 接口的方法,而是 Object 的一个 protected 方法。Cloneable 接口只是规定,如果一个类没有实现 Cloneable 接口又调用了 clone() 方法,就会抛出 CloneNotSupportedException。 +应该注意的是,clone() 方法并不是 Cloneable 接口的方法,而是 Object 的一个 protected 方法。Cloneable 接口只是规定,如果一个类没有实现 Cloneable 接口又调用了 clone() 方法,就会抛出 CloneNotSupportedException。 **2. 深拷贝与浅拷贝** -- 浅拷贝:拷贝对象和原对象的引用类型引用同一个对象; -- 深拷贝:引用不同对象。 +- 浅拷贝:拷贝实例和原始实例的引用类型引用同一个对象; +- 深拷贝:拷贝实例和原始实例的引用类型引用不同对象。 -实现深拷贝的方法: +```java +public class ShallowCloneExample implements Cloneable { + private int[] arr; -- [Defensive copying](http://www.javapractices.com/topic/TopicAction.do?Id=15) -- [copy constructors](http://www.javapractices.com/topic/TopicAction.do?Id=12) -- [static factory methods](http://www.javapractices.com/topic/TopicAction.do?Id=21). + public ShallowCloneExample() { + arr = new int[10]; + for (int i = 0; i < arr.length; i++) { + arr[i] = i; + } + } -> [How do I copy an object in Java?](https://stackoverflow.com/questions/869033/how-do-i-copy-an-object-in-java) + public void set(int index, int value) { + arr[index] = value; + } -## equals() + public int get(int index) { + return arr[index]; + } -**1. == 与 equals() 区别** + @Override + protected ShallowCloneExample clone() throws CloneNotSupportedException { + return (ShallowCloneExample) super.clone(); + } +} +``` -- 对于基本类型,== 判断两个值是否相等,基本类型没有 equals() 方法。 -- 对于引用类型,== 判断两个引用是否引用同一个对象,而 equals() 判断引用的对象是否等价。 +```java +ShallowCloneExample e1 = new ShallowCloneExample(); +ShallowCloneExample e2 = null; +try { + e2 = e1.clone(); +} catch (CloneNotSupportedException e) { + e.printStackTrace(); +} +e1.set(2, 222); +System.out.println(e2.get(2)); // 222 +``` -**2. 等价性** +```java +public class DeepCloneExample implements Cloneable { + private int[] arr; -> [散列](https://github.com/CyC2018/Interview-Notebook/blob/master/notes/Java%20%E5%AE%B9%E5%99%A8.md#%E4%B8%89%E6%95%A3%E5%88%977) + public DeepCloneExample() { + arr = new int[10]; + for (int i = 0; i < arr.length; i++) { + arr[i] = i; + } + } + + public void set(int index, int value) { + arr[index] = value; + } + + public int get(int index) { + return arr[index]; + } + + @Override + protected DeepCloneExample clone() throws CloneNotSupportedException { + DeepCloneExample result = (DeepCloneExample) super.clone(); + result.arr = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + result.arr[i] = arr[i]; + } + return result; + } +} +``` + +```java +DeepCloneExample e1 = new DeepCloneExample(); +DeepCloneExample e2 = null; +try { + e2 = e1.clone(); +} catch (CloneNotSupportedException e) { + e.printStackTrace(); +} +e1.set(2, 222); +System.out.println(e2.get(2)); // 2 +``` + +使用 clone() 方法来拷贝一个对象即复杂又有风险,它会抛出异常,并且还需要类型转换。Effective Java 书上讲到,最好不要去使用 clone(),可以使用拷贝构造函数或者拷贝工厂来拷贝一个对象。 + +```java +public class CloneConstructorExample { + private int[] arr; + + public CloneConstructorExample() { + arr = new int[10]; + for (int i = 0; i < arr.length; i++) { + arr[i] = i; + } + } + + public CloneConstructorExample(CloneConstructorExample original) { + arr = new int[original.arr.length]; + for (int i = 0; i < original.arr.length; i++) { + arr[i] = original.arr[i]; + } + } + + public void set(int index, int value) { + arr[index] = value; + } + + public int get(int index) { + return arr[index]; + } +} +``` + +```java +CloneConstructorExample e1 = new CloneConstructorExample(); +CloneConstructorExample e2 = new CloneConstructorExample(e1); +e1.set(2, 222); +System.out.println(e2.get(2)); // 2 +``` # 四、继承 diff --git a/notes/Java 容器.md b/notes/Java 容器.md index 8c79de2c..7c31b8a2 100644 --- a/notes/Java 容器.md +++ b/notes/Java 容器.md @@ -5,8 +5,7 @@ * [二、容器中的设计模式](#二容器中的设计模式) * [迭代器模式](#迭代器模式) * [适配器模式](#适配器模式) -* [三、散列](#三散列) -* [四、源码分析](#四源码分析) +* [三、源码分析](#三源码分析) * [ArrayList](#arraylist) * [Vector](#vector) * [LinkedList](#linkedlist) @@ -15,7 +14,7 @@ * [LinkedHashMap](#linkedhashmap) * [ConcurrentHashMap - JDK 1.7](#concurrenthashmap---jdk-17) * [ConcurrentHashMap - JDK 1.8](#concurrenthashmap---jdk-18) -* [五、参考资料](#五参考资料) +* [参考资料](#参考资料) @@ -102,51 +101,7 @@ List list = Arrays.asList(arr); List list = Arrays.asList(1,2,3); ``` -# 三、散列 - -hasCode() 返回散列值,使用的是对象的地址。 - -而 equals() 是用来判断两个对象是否相等的,相等的两个对象散列值一定要相同,但是散列值相同的两个对象不一定相等。 - -相等必须满足以下五个性质: - -**1. 自反性** - -```java -x.equals(x); // true -``` - -**2. 对称性** - -```java -x.equals(y) == y.equals(x) // true -``` - -**3. 传递性** - -```java -if(x.equals(y) && y.equals(z)) { - x.equals(z); // true; -} -``` - -**4. 一致性** - -多次调用 equals() 方法结果不变 - -```java -x.equals(y) == x.equals(y); // true -``` - -**5. 与 null 的比较** - -对任何不是 null 的对象 x 调用 x.equals(null) 结果都为 false - -```java -x.euqals(null); // false; -``` - -# 四、源码分析 +# 三、源码分析 建议先阅读 [算法-查找](https://github.com/CyC2018/Interview-Notebook/blob/master/notes/%E7%AE%97%E6%B3%95.md#%E6%9F%A5%E6%89%BE) 部分,对容器类源码的理解有很大帮助。 @@ -745,7 +700,7 @@ JDK 1.8 的实现不是用了 Segment,Segment 属于重入锁 ReentrantLock。 并且 JDK 1.8 的实现也在链表过长时会转换为红黑树。 -# 五、参考资料 +# 参考资料 - Eckel B. Java 编程思想 [M]. 机械工业出版社, 2002. - [Java Collection Framework](https://www.w3resource.com/java-tutorial/java-collections.php)