auto commit

This commit is contained in:
CyC2018 2018-04-06 20:23:58 +08:00
parent 1085339936
commit 8a14e463c0
2 changed files with 292 additions and 81 deletions

View File

@ -4,8 +4,10 @@
* [static](#static) * [static](#static)
* [二、Object 通用方法](#二object-通用方法) * [二、Object 通用方法](#二object-通用方法)
* [概览](#概览) * [概览](#概览)
* [clone()](#clone)
* [equals()](#equals) * [equals()](#equals)
* [hashCode()](#hashcode)
* [toString()](#tostring)
* [clone()](#clone)
* [四、继承](#四继承) * [四、继承](#四继承)
* [访问权限](#访问权限) * [访问权限](#访问权限)
* [抽象类与接口](#抽象类与接口) * [抽象类与接口](#抽象类与接口)
@ -154,6 +156,147 @@ public final void wait() throws InterruptedException
protected void finalize() throws Throwable {} 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<EqualExample> 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() ## clone()
**1. cloneable** **1. cloneable**
@ -161,21 +304,48 @@ protected void finalize() throws Throwable {}
clone() 是 Object 的受保护方法,这意味着,如果一个类不显式去重载 clone() 就没有这个方法。 clone() 是 Object 的受保护方法,这意味着,如果一个类不显式去重载 clone() 就没有这个方法。
```java ```java
public class CloneTest { public class CloneExample {
private int a; private int a;
private int b; private int b;
} }
``` ```
```java ```java
CloneTest x = new CloneTest(); CloneExample e1 = new CloneExample();
CloneTest y = x.clone(); // 'clone()' has protected access in 'java.lang.Object' CloneExample e2 = e1.clone(); // 'clone()' has protected access in 'java.lang.Object'
``` ```
接下来重载 Object 的 clone() 得到以下实现: 接下来重载 Object 的 clone() 得到以下实现:
```java ```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 a;
private int b; private int b;
@ -186,44 +356,130 @@ public class CloneTest{
} }
``` ```
```java 应该注意的是clone() 方法并不是 Cloneable 接口的方法,而是 Object 的一个 protected 方法。Cloneable 接口只是规定,如果一个类没有实现 Cloneable 接口又调用了 clone() 方法,就会抛出 CloneNotSupportedException。
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。
**2. 深拷贝与浅拷贝** **2. 深拷贝与浅拷贝**
- 浅拷贝:拷贝对象和原对象的引用类型引用同一个对象; - 浅拷贝:拷贝实例和原始实例的引用类型引用同一个对象;
- 深拷贝:引用不同对象。 - 深拷贝:拷贝实例和原始实例的引用类型引用不同对象。
实现深拷贝的方法: ```java
public class ShallowCloneExample implements Cloneable {
private int[] arr;
- [Defensive copying](http://www.javapractices.com/topic/TopicAction.do?Id=15) public ShallowCloneExample() {
- [copy constructors](http://www.javapractices.com/topic/TopicAction.do?Id=12) arr = new int[10];
- [static factory methods](http://www.javapractices.com/topic/TopicAction.do?Id=21). 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() 方法。 ```java
- 对于引用类型,== 判断两个引用是否引用同一个对象,而 equals() 判断引用的对象是否等价。 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
```
# 四、继承 # 四、继承

View File

@ -5,8 +5,7 @@
* [二、容器中的设计模式](#二容器中的设计模式) * [二、容器中的设计模式](#二容器中的设计模式)
* [迭代器模式](#迭代器模式) * [迭代器模式](#迭代器模式)
* [适配器模式](#适配器模式) * [适配器模式](#适配器模式)
* [三、散列](#三散列) * [三、源码分析](#三源码分析)
* [四、源码分析](#四源码分析)
* [ArrayList](#arraylist) * [ArrayList](#arraylist)
* [Vector](#vector) * [Vector](#vector)
* [LinkedList](#linkedlist) * [LinkedList](#linkedlist)
@ -15,7 +14,7 @@
* [LinkedHashMap](#linkedhashmap) * [LinkedHashMap](#linkedhashmap)
* [ConcurrentHashMap - JDK 1.7](#concurrenthashmap---jdk-17) * [ConcurrentHashMap - JDK 1.7](#concurrenthashmap---jdk-17)
* [ConcurrentHashMap - JDK 1.8](#concurrenthashmap---jdk-18) * [ConcurrentHashMap - JDK 1.8](#concurrenthashmap---jdk-18)
* [五、参考资料](#参考资料) * [参考资料](#参考资料)
<!-- GFM-TOC --> <!-- GFM-TOC -->
@ -102,51 +101,7 @@ List list = Arrays.asList(arr);
List list = Arrays.asList(1,2,3); 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) 部分,对容器类源码的理解有很大帮助。 建议先阅读 [算法-查找](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 的实现不是用了 SegmentSegment 属于重入锁 ReentrantLock。
并且 JDK 1.8 的实现也在链表过长时会转换为红黑树。 并且 JDK 1.8 的实现也在链表过长时会转换为红黑树。
# 五、参考资料 # 参考资料
- Eckel B. Java 编程思想 [M]. 机械工业出版社, 2002. - Eckel B. Java 编程思想 [M]. 机械工业出版社, 2002.
- [Java Collection Framework](https://www.w3resource.com/java-tutorial/java-collections.php) - [Java Collection Framework](https://www.w3resource.com/java-tutorial/java-collections.php)