
@@ -340,7 +340,7 @@ Thread[线程 2,5,main]waiting get resource1
1. 不剥夺条件:线程已获得的资源在末使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。
1. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
-### 如何避免线程死锁?
+### 如何预防线程死锁?
我们只要破坏产生死锁的四个条件中的其中一个就可以了。
diff --git "a/docs/java/What's New in JDK8/JDK8\346\216\245\345\217\243\350\247\204\350\214\203-\351\235\231\346\200\201\343\200\201\351\273\230\350\256\244\346\226\271\346\263\225.md" "b/docs/java/What's New in JDK8/JDK8\346\216\245\345\217\243\350\247\204\350\214\203-\351\235\231\346\200\201\343\200\201\351\273\230\350\256\244\346\226\271\346\263\225.md"
deleted file mode 100644
index ee1dd8c2448..00000000000
--- "a/docs/java/What's New in JDK8/JDK8\346\216\245\345\217\243\350\247\204\350\214\203-\351\235\231\346\200\201\343\200\201\351\273\230\350\256\244\346\226\271\346\263\225.md"
+++ /dev/null
@@ -1,163 +0,0 @@
-JDK8接口规范
-===
-在JDK8中引入了lambda表达式,出现了函数式接口的概念,为了在扩展接口时保持向前兼容性(比如泛型也是为了保持兼容性而失去了在一些别的语言泛型拥有的功能),Java接口规范发生了一些改变。。
----
-## 1.JDK8以前的接口规范
-- JDK8以前接口可以定义的变量和方法
- - 所有变量(Field)不论是否
显式 的声明为```public static final```,它实际上都是```public static final```的。
- - 所有方法(Method)不论是否
显示 的声明为```public abstract```,它实际上都是```public abstract```的。
-```java
-public interface AInterfaceBeforeJDK8 {
- int FIELD = 0;
- void simpleMethod();
-}
-```
-以上接口信息反编译以后可以看到字节码信息里Filed是public static final的,而方法是public abstract的,即是你没有显示的去声明它。
-```java
-{
- public static final int FIELD;
- descriptor: I
- flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
- ConstantValue: int 0
-
- public abstract void simpleMethod();
- descriptor: ()V
- flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
-}
-```
-## 2.JDK8之后的接口规范
-- JDK8之后接口可以定义的变量和方法
- - 变量(Field)仍然必须是 ```java public static final```的
- - 方法(Method)除了可以是public abstract之外,还可以是public static或者是default(相当于仅public修饰的实例方法)的。
-从以上改变不难看出,修改接口的规范主要是为了能在扩展接口时保持向前兼容。
-
下面是一个JDK8之后的接口例子
-```java
-public interface AInterfaceInJDK8 {
- int simpleFiled = 0;
- static int staticField = 1;
-
- public static void main(String[] args) {
- }
- static void staticMethod(){}
-
- default void defaultMethod(){}
-
- void simpleMethod() throws IOException;
-
-}
-```
-进行反编译(去除了一些没用信息)
-```java
-{
- public static final int simpleFiled;
- flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
-
- public static final int staticField;
- flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
-
- public static void main(java.lang.String[]);
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-
- public static void staticMethod();
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-
- public void defaultMethod();
- flags: (0x0001) ACC_PUBLIC
-
- public abstract void simpleMethod() throws java.io.IOException;
- flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
- Exceptions:
- throws java.io.IOException
-}
-```
-可以看到 default关键字修饰的方法是像实例方法一样定义的,所以我们来定义一个只有default的方法并且实现一下试一试。
-```java
-interface Default {
- default int defaultMethod() {
- return 4396;
- }
-}
-
-public class DefaultMethod implements Default {
- public static void main(String[] args) {
- DefaultMethod defaultMethod = new DefaultMethod();
- System.out.println(defaultMethod.defaultMethod());
- //compile error : Non-static method 'defaultMethod()' cannot be referenced from a static context
- //! DefaultMethod.defaultMethod();
- }
-}
-```
-可以看到default方法确实像实例方法一样,必须有实例对象才能调用,并且子类在实现接口时,可以不用实现default方法,也可以覆盖该方法。
-这有点像子类继承父类实例方法。
-
-接口静态方法就像是类静态方法,唯一的区别是**接口静态方法只能通过接口名调用,而类静态方法既可以通过类名调用也可以通过实例调用**
-```java
-interface Static {
- static int staticMethod() {
- return 4396;
- }
-}
- ... main(String...args)
- //!compile error: Static method may be invoked on containing interface class only
- //!aInstanceOfStatic.staticMethod();
- ...
-```
-另一个问题是多继承问题,大家知道Java中类是不支持多继承的,但是接口是多继承和多实现(implements后跟多个接口)的,
-那么如果一个接口继承另一个接口,两个接口都有同名的default方法会怎么样呢?答案是会像类继承一样覆写(@Override),以下代码在IDE中可以顺利编译
-```java
-interface Default {
- default int defaultMethod() {
- return 4396;
- }
-}
-interface Default2 extends Default {
- @Override
- default int defaultMethod() {
- return 9527;
- }
-}
-public class DefaultMethod implements Default,Default2 {
- public static void main(String[] args) {
- DefaultMethod defaultMethod = new DefaultMethod();
- System.out.println(defaultMethod.defaultMethod());
- }
-}
-
-输出 : 9527
-```
-出现上面的情况时,会优先找继承树上近的方法,类似于“短路优先”。
-
-那么如果一个类实现了两个没有继承关系的接口,且这两个接口有同名方法的话会怎么样呢?IDE会要求你重写这个冲突的方法,让你自己选择去执行哪个方法,因为IDE它
-还没智能到你不告诉它,它就知道你想执行哪个方法。可以通过```java 接口名.super```指针来访问接口中定义的实例(default)方法。
-```java
-interface Default {
- default int defaultMethod() {
- return 4396;
- }
-}
-
-interface Default2 {
- default int defaultMethod() {
- return 9527;
- }
-}
-//如果不重写
-//compile error : defaults.DefaultMethod inherits unrelated defaults for defaultMethod() from types defaults.Default and defaults.Default2
-public class DefaultMethod implements Default,Default2 {
-@Override
- public int defaultMethod() {
- System.out.println(Default.super.defaultMethod());
- System.out.println(Default2.super.defaultMethod());
- return 996;
- }
- public static void main(String[] args) {
- DefaultMethod defaultMethod = new DefaultMethod();
- System.out.println(defaultMethod.defaultMethod());
- }
-}
-
-运行输出 :
-4396
-9527
-996
-```
diff --git a/docs/java/What's New in JDK8/Java8Tutorial.md b/docs/java/What's New in JDK8/Java8Tutorial.md
index cee4d4e4f0a..56e7f52d5b0 100644
--- a/docs/java/What's New in JDK8/Java8Tutorial.md
+++ b/docs/java/What's New in JDK8/Java8Tutorial.md
@@ -1,3 +1,5 @@
+点击关注[公众号](#公众号)及时获取笔主最新更新文章,并可免费领取本文档配套的《Java面试突击》以及Java工程师必备学习资源。
+
随着 Java 8 的普及度越来越高,很多人都提到面试中关于Java 8 也是非常常问的知识点。应各位要求和需要,我打算对这部分知识做一个总结。本来准备自己总结的,后面看到Github 上有一个相关的仓库,地址:
[https://github.com/winterbe/java8-tutorial](https://github.com/winterbe/java8-tutorial)。这个仓库是英文的,我对其进行了翻译并添加和修改了部分内容,下面是正文了。
@@ -442,15 +444,15 @@ optional.ifPresent((s) -> System.out.println(s.charAt(0))); // "b"
首先看看Stream是怎么用,首先创建实例代码的用到的数据List:
```java
-List
stringCollection = new ArrayList<>();
-stringCollection.add("ddd2");
-stringCollection.add("aaa2");
-stringCollection.add("bbb1");
-stringCollection.add("aaa1");
-stringCollection.add("bbb3");
-stringCollection.add("ccc");
-stringCollection.add("bbb2");
-stringCollection.add("ddd1");
+List stringList = new ArrayList<>();
+stringList.add("ddd2");
+stringList.add("aaa2");
+stringList.add("bbb1");
+stringList.add("aaa1");
+stringList.add("bbb3");
+stringList.add("ccc");
+stringList.add("bbb2");
+stringList.add("ddd1");
```
Java 8扩展了集合类,可以通过 Collection.stream() 或者 Collection.parallelStream() 来创建一个Stream。下面几节将详细解释常用的Stream操作:
@@ -492,7 +494,7 @@ forEach 是为 Lambda 而设计的,保持了最紧凑的风格。而且 Lambda
中间操作 map 会将元素根据指定的 Function 接口来依次将元素转成另外的对象。
-下面的示例展示了将字符串转换为大写字符串。你也可以通过map来讲对象转换成其他类型,map返回的Stream类型是根据你map传递进去的函数的返回值决定的。
+下面的示例展示了将字符串转换为大写字符串。你也可以通过map来将对象转换成其他类型,map返回的Stream类型是根据你map传递进去的函数的返回值决定的。
```java
// 测试 Map 操作
@@ -916,9 +918,16 @@ System.out.println(hints2.length); // 2
@interface MyAnnotation {}
```
+## Where to go from here?
+关于Java 8的新特性就写到这了,肯定还有更多的特性等待发掘。JDK 1.8里还有很多很有用的东西,比如`Arrays.parallelSort`, `StampedLock`和`CompletableFuture`等等。
-## Whete to go from here?
+## 公众号
-关于Java 8的新特性就写到这了,肯定还有更多的特性等待发掘。JDK 1.8里还有很多很有用的东西,比如`Arrays.parallelSort`, `StampedLock`和`CompletableFuture`等等。
+如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。
+
+**《Java面试突击》:** 由本文档衍生的专为面试而生的《Java面试突击》V2.0 PDF 版本[公众号](#公众号)后台回复 **"Java面试突击"** 即可免费领取!
+
+**Java工程师必备学习资源:** 一些Java工程师常用学习资源[公众号](#公众号)后台回复关键字 **“1”** 即可免费无套路获取。
+
diff --git "a/docs/java/What's New in JDK8/Java8foreach\346\214\207\345\215\227.md" "b/docs/java/What's New in JDK8/Java8foreach\346\214\207\345\215\227.md"
new file mode 100644
index 00000000000..48cda3abfd9
--- /dev/null
+++ "b/docs/java/What's New in JDK8/Java8foreach\346\214\207\345\215\227.md"
@@ -0,0 +1,139 @@
+> 本文由 JavaGuide 翻译,原文地址:https://www.baeldung.com/foreach-java
+
+## 1 概述
+
+在Java 8中引入的*forEach*循环为程序员提供了一种新的,简洁而有趣的迭代集合的方式。
+
+在本文中,我们将看到如何将*forEach*与集合*一起*使用,它采用何种参数以及此循环与增强的*for*循环的不同之处。
+
+## 2 基础知识
+
+```Java
+public interface Collection extends Iterable
+```
+
+Collection 接口实现了 Iterable 接口,而 Iterable 接口在 Java 8开始具有一个新的 API:
+
+```java
+void forEach(Consumer super T> action)//对 Iterable的每个元素执行给定的操作,直到所有元素都被处理或动作引发异常。
+```
+
+使用*forEach*,我们可以迭代一个集合并对每个元素执行给定的操作,就像任何其他*迭代器一样。*
+
+例如,迭代和打印字符串集合*的*for循环版本:
+
+```java
+for (String name : names) {
+ System.out.println(name);
+}
+```
+
+我们可以使用*forEach*写这个 :
+
+```java
+names.forEach(name -> {
+ System.out.println(name);
+});
+```
+
+## 3.使用forEach方法
+
+### 3.1 匿名类
+
+我们使用 *forEach*迭代集合并对每个元素执行特定操作。**要执行的操作包含在实现Consumer接口的类中,并作为参数传递给forEach 。**
+
+所述*消费者*接口是一个功能接口(具有单个抽象方法的接口)。它接受输入并且不返回任何结果。
+
+Consumer 接口定义如下:
+
+```java
+@FunctionalInterface
+public interface Consumer {
+ void accept(T t);
+}
+```
+任何实现,例如,只是打印字符串的消费者:
+
+```java
+Consumer printConsumer = new Consumer() {
+ public void accept(String name) {
+ System.out.println(name);
+ };
+};
+```
+
+可以作为参数传递给*forEach*:
+
+```java
+names.forEach(printConsumer);
+```
+
+但这不是通过消费者和使用*forEach* API 创建操作的唯一方法。让我们看看我们将使用*forEach*方法的另外2种最流行的方式:
+
+### 3.2 Lambda表达式
+
+Java 8功能接口的主要优点是我们可以使用Lambda表达式来实例化它们,并避免使用庞大的匿名类实现。
+
+由于 Consumer 接口属于函数式接口,我们可以通过以下形式在Lambda中表达它:
+
+```java
+(argument) -> { body }
+name -> System.out.println(name)
+names.forEach(name -> System.out.println(name));
+```
+
+### 3.3 方法参考
+
+我们可以使用方法引用语法而不是普通的Lambda语法,其中已存在一个方法来对类执行操作:
+
+```java
+names.forEach(System.out::println);
+```
+
+## 4.forEach在集合中的使用
+
+### 4.1.迭代集合
+
+**任何类型Collection的可迭代 - 列表,集合,队列 等都具有使用forEach的相同语法。**
+
+因此,正如我们已经看到的,迭代列表的元素:
+
+```java
+List names = Arrays.asList("Larry", "Steve", "James");
+
+names.forEach(System.out::println);
+```
+
+同样对于一组:
+
+```java
+Set uniqueNames = new HashSet<>(Arrays.asList("Larry", "Steve", "James"));
+
+uniqueNames.forEach(System.out::println);
+```
+
+或者让我们说一个*队列*也是一个*集合*:
+
+```java
+Queue namesQueue = new ArrayDeque<>(Arrays.asList("Larry", "Steve", "James"));
+
+namesQueue.forEach(System.out::println);
+```
+
+### 4.2.迭代Map - 使用Map的forEach
+
+Map没有实现Iterable接口,但它**提供了自己的forEach 变体,它接受BiConsumer**。*
+
+```java
+Map namesMap = new HashMap<>();
+namesMap.put(1, "Larry");
+namesMap.put(2, "Steve");
+namesMap.put(3, "James");
+namesMap.forEach((key, value) -> System.out.println(key + " " + value));
+```
+
+### 4.3.迭代一个Map - 通过迭代entrySet
+
+```java
+namesMap.entrySet().forEach(entry -> System.out.println(entry.getKey() + " " + entry.getValue()));
+```
\ No newline at end of file
diff --git "a/docs/java/What's New in JDK8/Java8\346\225\231\347\250\213\346\216\250\350\215\220.md" "b/docs/java/What's New in JDK8/Java8\346\225\231\347\250\213\346\216\250\350\215\220.md"
index 43e4539ccac..7de58352a5f 100644
--- "a/docs/java/What's New in JDK8/Java8\346\225\231\347\250\213\346\216\250\350\215\220.md"
+++ "b/docs/java/What's New in JDK8/Java8\346\225\231\347\250\213\346\216\250\350\215\220.md"
@@ -1,5 +1,3 @@
-
-
### 书籍
- **《Java8 In Action》**
diff --git "a/docs/java/What's New in JDK8/Lambda\350\241\250\350\276\276\345\274\217.md" "b/docs/java/What's New in JDK8/Lambda\350\241\250\350\276\276\345\274\217.md"
deleted file mode 100644
index 359c4714473..00000000000
--- "a/docs/java/What's New in JDK8/Lambda\350\241\250\350\276\276\345\274\217.md"
+++ /dev/null
@@ -1,235 +0,0 @@
-JDK8--Lambda表达式
-===
-## 1.什么是Lambda表达式
-**Lambda表达式实质上是一个可传递的代码块,Lambda又称为闭包或者匿名函数,是函数式编程语法,让方法可以像普通参数一样传递**
-
-## 2.Lambda表达式语法
-```(参数列表) -> {执行代码块}```
-
参数列表可以为空```()->{}```
-
可以加类型声明比如```(String para1, int para2) -> {return para1 + para2;}```我们可以看到,lambda同样可以有返回值.
-
在编译器可以推断出类型的时候,可以将类型声明省略,比如```(para1, para2) -> {return para1 + para2;}```
-
(lambda有点像动态类型语言语法。lambda在字节码层面是用invokedynamic实现的,而这条指令就是为了让JVM更好的支持运行在其上的动态类型语言)
-
-## 3.函数式接口
-在了解Lambda表达式之前,有必要先了解什么是函数式接口```(@FunctionalInterface)```
-**函数式接口指的是有且只有一个抽象(abstract)方法的接口**
-当需要一个函数式接口的对象时,就可以用Lambda表达式来实现,举个常用的例子:
-
-```java
- Thread thread = new Thread(() -> {
- System.out.println("This is JDK8's Lambda!");
- });
-```
-这段代码和函数式接口有啥关系?我们回忆一下,Thread类的构造函数里是不是有一个以Runnable接口为参数的?
-```java
-public Thread(Runnable target) {...}
-
-/**
- * Runnable Interface
- */
-@FunctionalInterface
-public interface Runnable {
- public abstract void run();
-}
-```
-到这里大家可能已经明白了,**Lambda表达式相当于一个匿名类或者说是一个匿名方法**。上面Thread的例子相当于
-```java
- Thread thread = new Thread(new Runnable() {
- @Override
- public void run() {
- System.out.println("Anonymous class");
- }
- });
-```
-也就是说,上面的lambda表达式相当于实现了这个run()方法,然后当做参数传入(个人感觉可以这么理解,lambda表达式就是一个函数,只不过它的返回值、参数列表都
-由编译器帮我们推断,因此可以减少很多代码量)。
-
Lambda也可以这样用 :
-```java
- Runnable runnable = () -> {...};
-```
-其实这和上面的用法没有什么本质上的区别。
-
至此大家应该明白什么是函数式接口以及函数式接口和lambda表达式之间的关系了。在JDK8中修改了接口的规范,
-目的是为了在给接口添加新的功能时保持向前兼容(个人理解),比如一个已经定义了的函数式接口,某天我们想给它添加新功能,那么就不能保持向前兼容了,
-因为在旧的接口规范下,添加新功能必定会破坏这个函数式接口[(JDK8中接口规范)]()
-
-除了上面说的Runnable接口之外,JDK中已经存在了很多函数式接口
-比如(当然不止这些):
-- ```java.util.concurrent.Callable```
-- ```java.util.Comparator```
-- ```java.io.FileFilter```
-
**关于JDK中的预定义的函数式接口**
-
-- JDK在```java.util.function```下预定义了很多函数式接口
- - ```Function {R apply(T t);}``` 接受一个T对象,然后返回一个R对象,就像普通的函数。
- - ```Consumer {void accept(T t);}``` 消费者 接受一个T对象,没有返回值。
- - ```Predicate {boolean test(T t);}``` 判断,接受一个T对象,返回一个布尔值。
- - ```Supplier {T get();} 提供者(工厂)``` 返回一个T对象。
- - 其他的跟上面的相似,大家可以看一下function包下的具体接口。
-## 4.变量作用域
-```java
-public class VaraibleHide {
- @FunctionalInterface
- interface IInner {
- void printInt(int x);
- }
- public static void main(String[] args) {
- int x = 20;
- IInner inner = new IInner() {
- int x = 10;
- @Override
- public void printInt(int x) {
- System.out.println(x);
- }
- };
- inner.printInt(30);
-
- inner = (s) -> {
- //Variable used in lambda expression should be final or effectively final
- //!int x = 10;
- //!x= 50; error
- System.out.print(x);
- };
- inner.printInt(30);
- }
-}
-输出 :
-30
-20
-```
-对于lambda表达式```java inner = (s) -> {System.out.print(x);};```,变量x并不是在lambda表达式中定义的,像这样并不是在lambda中定义或者通过lambda的参数列表()获取的变量成为自由变量,它是被lambda表达式捕获的。
-
lambda表达式和内部类一样,对外部自由变量捕获时,外部自由变量必须为final或者是最终变量(effectively final)的,也就是说这个变量初始化后就不能为它赋新值,
-同时lambda不像内部类/匿名类,lambda表达式与外围嵌套块有着相同的作用域,因此对变量命名的有关规则对lambda同样适用。大家阅读上面的代码对这些概念应该
-不难理解。
-## 5.方法引用
-**只需要提供方法的名字,具体的调用过程由Lambda和函数式接口来确定,这样的方法调用成为方法引用。**
-
下面的例子会打印list中的每个元素:
-```java
-List list = new ArrayList<>();
- for (int i = 0; i < 10; ++i) {
- list.add(i);
- }
- list.forEach(System.out::println);
-```
-其中```System.out::println```这个就是一个方法引用,等价于Lambda表达式 ```(para)->{System.out.println(para);}```
-
我们看一下List#forEach方法 ```default void forEach(Consumer super T> action)```可以看到它的参数是一个Consumer接口,该接口是一个函数式接口
-```java
-@FunctionalInterface
-public interface Consumer {
- void accept(T t);
-```
-大家能发现这个函数接口的方法和```System.out::println```有什么相似的么?没错,它们有着相似的参数列表和返回值。
-
我们自己定义一个方法,看看能不能像标准输出的打印函数一样被调用
-```java
-public class MethodReference {
- public static void main(String[] args) {
- List list = new ArrayList<>();
- for (int i = 0; i < 10; ++i) {
- list.add(i);
- }
- list.forEach(MethodReference::myPrint);
- }
-
- static void myPrint(int i) {
- System.out.print(i + ", ");
- }
-}
-
-输出: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
-```
-可以看到,我们自己定义的方法也可以当做方法引用。
-
到这里大家多少对方法引用有了一定的了解,我们再来说一下方法引用的形式。
-- 方法引用
- - 类名::静态方法名
- - 类名::实例方法名
- - 类名::new (构造方法引用)
- - 实例名::实例方法名
-可以看出,方法引用是通过(方法归属名)::(方法名)来调用的。通过上面的例子已经讲解了一个`类名::静态方法名`的使用方法了,下面再依次介绍其余的几种
-方法引用的使用方法。
-**类名::实例方法名**
-先来看一段代码
-```java
- String[] strings = new String[10];
- Arrays.sort(strings, String::compareToIgnoreCase);
-```
-**上面的String::compareToIgnoreCase等价于(x, y) -> {return x.compareToIgnoreCase(y);}**
-我们看一下`Arrays#sort`方法`public static void sort(T[] a, Comparator super T> c)`,
-可以看到第二个参数是一个Comparator接口,该接口也是一个函数式接口,其中的抽象方法是`int compare(T o1, T o2);`,再看一下
-`String#compareToIgnoreCase`方法,`public int compareToIgnoreCase(String str)`,这个方法好像和上面讲方法引用中`类名::静态方法名`不大一样啊,它
-的参数列表和函数式接口的参数列表不一样啊,虽然它的返回值一样?
-
是的,确实不一样但是别忘了,String类的这个方法是个实例方法,而不是静态方法,也就是说,这个方法是需要有一个接收者的。所谓接收者就是
-instance.method(x)中的instance,
-它是某个类的实例,有的朋友可能已经明白了。上面函数式接口的`compare(T o1, T o2)`中的第一个参数作为了实例方法的接收者,而第二个参数作为了实例方法的
-参数。我们再举一个自己实现的例子:
-```java
-public class MethodReference {
- static Random random = new Random(47);
- public static void main(String[] args) {
- MethodReference[] methodReferences = new MethodReference[10];
- Arrays.sort(methodReferences, MethodReference::myCompare);
- }
- int myCompare(MethodReference o) {
- return random.nextInt(2) - 1;
- }
-}
-```
-上面的例子可以在IDE里通过编译,大家有兴趣的可以模仿上面的例子自己写一个程序,打印出排序后的结果。
-
**构造器引用**
-构造器引用仍然需要与特定的函数式接口配合使用,并不能像下面这样直接使用。IDE会提示String不是一个函数式接口
-```java
- //compile error : String is not a functional interface
- String str = String::new;
-```
-下面是一个使用构造器引用的例子,可以看出构造器引用可以和这种工厂型的函数式接口一起使用的。
-```java
- interface IFunctional {
- T func();
-}
-
-public class ConstructorReference {
-
- public ConstructorReference() {
- }
-
- public static void main(String[] args) {
- Supplier supplier0 = () -> new ConstructorReference();
- Supplier supplier1 = ConstructorReference::new;
- IFunctional functional = () -> new ConstructorReference();
- IFunctional functional1 = ConstructorReference::new;
- }
-}
-```
-下面是一个JDK官方的例子
-```java
- public static , DEST extends Collection>
- DEST transferElements(
- SOURCE sourceCollection,
- Supplier collectionFactory) {
-
- DEST result = collectionFactory.get();
- for (T t : sourceCollection) {
- result.add(t);
- }
- return result;
- }
-
- ...
-
- Set rosterSet = transferElements(
- roster, HashSet::new);
-```
-
-**实例::实例方法**
-
-其实开始那个例子就是一个实例::实例方法的引用
-```java
-List list = new ArrayList<>();
- for (int i = 0; i < 10; ++i) {
- list.add(i);
- }
- list.forEach(System.out::println);
-```
-其中System.out就是一个实例,println是一个实例方法。相信不用再给大家做解释了。
-## 总结
-Lambda表达式是JDK8引入Java的函数式编程语法,使用Lambda需要直接或者间接的与函数式接口配合,在开发中使用Lambda可以减少代码量,
-但是并不是说必须要使用Lambda(虽然它是一个很酷的东西)。有些情况下使用Lambda会使代码的可读性急剧下降,并且也节省不了多少代码,
-所以在实际开发中还是需要仔细斟酌是否要使用Lambda。和Lambda相似的还有JDK10中加入的var类型推断,同样对于这个特性需要斟酌使用。
diff --git a/docs/java/What's New in JDK8/README.md b/docs/java/What's New in JDK8/README.md
deleted file mode 100644
index fa71e907410..00000000000
--- a/docs/java/What's New in JDK8/README.md
+++ /dev/null
@@ -1,556 +0,0 @@
-JDK8新特性总结
-======
-总结了部分JDK8新特性,另外一些新特性可以通过Oracle的官方文档查看,毕竟是官方文档,各种新特性都会介绍,有兴趣的可以去看。
-[Oracle官方文档:What's New in JDK8](https://www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html)
------
-- [Java语言特性](#JavaProgrammingLanguage)
- - [Lambda表达式是一个新的语言特性,已经在JDK8中加入。它是一个可以传递的代码块,你也可以把它们当做方法参数。
- Lambda表达式允许您更紧凑地创建单虚方法接口(称为功能接口)的实例。](#LambdaExpressions)
-
- - [方法引用为已经存在的具名方法提供易于阅读的Lambda表达式](#MethodReferences)
-
- - [默认方法允许将新功能添加到库的接口,并确保与为这些接口的旧版本编写的代码的二进制兼容性。](#DefaultMethods)
-
- - [改进的类型推断。](#ImprovedTypeInference)
-
- - [方法参数反射(通过反射获得方法参数信息)](#MethodParameterReflection)
-
-- [流(stream)](#stream)
- - [新java.util.stream包中的类提供Stream API以支持对元素流的功能样式操作。流(stream)和I/O里的流不是同一个概念
- ,使用stream API可以更方便的操作集合。]()
-
-- [国际化]()
- - 待办
-- 待办
-___
-
-
-
-
-
-
-
-## Lambda表达式
-### 1.什么是Lambda表达式
-**Lambda表达式实质上是一个可传递的代码块,Lambda又称为闭包或者匿名函数,是函数式编程语法,让方法可以像普通参数一样传递**
-
-### 2.Lambda表达式语法
-```(参数列表) -> {执行代码块}```
-
参数列表可以为空```()->{}```
-
可以加类型声明比如```(String para1, int para2) -> {return para1 + para2;}```我们可以看到,lambda同样可以有返回值.
-
在编译器可以推断出类型的时候,可以将类型声明省略,比如```(para1, para2) -> {return para1 + para2;}```
-
(lambda有点像动态类型语言语法。lambda在字节码层面是用invokedynamic实现的,而这条指令就是为了让JVM更好的支持运行在其上的动态类型语言)
-
-### 3.函数式接口
-在了解Lambda表达式之前,有必要先了解什么是函数式接口```(@FunctionalInterface)```
-**函数式接口指的是有且只有一个抽象(abstract)方法的接口**
-当需要一个函数式接口的对象时,就可以用Lambda表达式来实现,举个常用的例子:
-
-```java
- Thread thread = new Thread(() -> {
- System.out.println("This is JDK8's Lambda!");
- });
-```
-这段代码和函数式接口有啥关系?我们回忆一下,Thread类的构造函数里是不是有一个以Runnable接口为参数的?
-```java
-public Thread(Runnable target) {...}
-
-/**
- * Runnable Interface
- */
-@FunctionalInterface
-public interface Runnable {
- public abstract void run();
-}
-```
-到这里大家可能已经明白了,**Lambda表达式相当于一个匿名类或者说是一个匿名方法**。上面Thread的例子相当于
-```java
- Thread thread = new Thread(new Runnable() {
- @Override
- public void run() {
- System.out.println("Anonymous class");
- }
- });
-```
-也就是说,上面的lambda表达式相当于实现了这个run()方法,然后当做参数传入(个人感觉可以这么理解,lambda表达式就是一个函数,只不过它的返回值、参数列表都
-由编译器帮我们推断,因此可以减少很多代码量)。
-
Lambda也可以这样用 :
-```java
- Runnable runnable = () -> {...};
-```
-其实这和上面的用法没有什么本质上的区别。
-
至此大家应该明白什么是函数式接口以及函数式接口和lambda表达式之间的关系了。在JDK8中修改了接口的规范,
-目的是为了在给接口添加新的功能时保持向前兼容(个人理解),比如一个已经定义了的函数式接口,某天我们想给它添加新功能,那么就不能保持向前兼容了,
-因为在旧的接口规范下,添加新功能必定会破坏这个函数式接口[(JDK8中接口规范)]()
-
-除了上面说的Runnable接口之外,JDK中已经存在了很多函数式接口
-比如(当然不止这些):
-- ```java.util.concurrent.Callable```
-- ```java.util.Comparator```
-- ```java.io.FileFilter```
-
**关于JDK中的预定义的函数式接口**
-
-- JDK在```java.util.function```下预定义了很多函数式接口
- - ```Function {R apply(T t);}``` 接受一个T对象,然后返回一个R对象,就像普通的函数。
- - ```Consumer {void accept(T t);}``` 消费者 接受一个T对象,没有返回值。
- - ```Predicate {boolean test(T t);}``` 判断,接受一个T对象,返回一个布尔值。
- - ```Supplier {T get();} 提供者(工厂)``` 返回一个T对象。
- - 其他的跟上面的相似,大家可以看一下function包下的具体接口。
-### 4.变量作用域
-```java
-public class VaraibleHide {
- @FunctionalInterface
- interface IInner {
- void printInt(int x);
- }
- public static void main(String[] args) {
- int x = 20;
- IInner inner = new IInner() {
- int x = 10;
- @Override
- public void printInt(int x) {
- System.out.println(x);
- }
- };
- inner.printInt(30);
-
- inner = (s) -> {
- //Variable used in lambda expression should be final or effectively final
- //!int x = 10;
- //!x= 50; error
- System.out.print(x);
- };
- inner.printInt(30);
- }
-}
-输出 :
-30
-20
-```
-对于lambda表达式```java inner = (s) -> {System.out.print(x);};```,变量x并不是在lambda表达式中定义的,像这样并不是在lambda中定义或者通过lambda
-的参数列表()获取的变量成为自由变量,它是被lambda表达式捕获的。
-
lambda表达式和内部类一样,对外部自由变量捕获时,外部自由变量必须为final或者是最终变量(effectively final)的,也就是说这个变量初始化后就不能为它赋新值,同时lambda不像内部类/匿名类,lambda表达式与外围嵌套块有着相同的作用域,因此对变量命名的有关规则对lambda同样适用。大家阅读上面的代码对这些概念应该不难理解。
-
-### 5.方法引用
-**只需要提供方法的名字,具体的调用过程由Lambda和函数式接口来确定,这样的方法调用成为方法引用。**
-
下面的例子会打印list中的每个元素:
-```java
-List list = new ArrayList<>();
- for (int i = 0; i < 10; ++i) {
- list.add(i);
- }
- list.forEach(System.out::println);
-```
-其中```System.out::println```这个就是一个方法引用,等价于Lambda表达式 ```(para)->{System.out.println(para);}```
-
我们看一下List#forEach方法 ```default void forEach(Consumer super T> action)```可以看到它的参数是一个Consumer接口,该接口是一个函数式接口
-```java
-@FunctionalInterface
-public interface Consumer {
- void accept(T t);
-```
-大家能发现这个函数接口的方法和```System.out::println```有什么相似的么?没错,它们有着相似的参数列表和返回值。
-
我们自己定义一个方法,看看能不能像标准输出的打印函数一样被调用
-```java
-public class MethodReference {
- public static void main(String[] args) {
- List list = new ArrayList<>();
- for (int i = 0; i < 10; ++i) {
- list.add(i);
- }
- list.forEach(MethodReference::myPrint);
- }
-
- static void myPrint(int i) {
- System.out.print(i + ", ");
- }
-}
-
-输出: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
-```
-可以看到,我们自己定义的方法也可以当做方法引用。
-
到这里大家多少对方法引用有了一定的了解,我们再来说一下方法引用的形式。
-- 方法引用
- - 类名::静态方法名
- - 类名::实例方法名
- - 类名::new (构造方法引用)
- - 实例名::实例方法名
-可以看出,方法引用是通过(方法归属名)::(方法名)来调用的。通过上面的例子已经讲解了一个`类名::静态方法名`的使用方法了,下面再依次介绍其余的几种
-方法引用的使用方法。
-**类名::实例方法名**
-先来看一段代码
-```java
- String[] strings = new String[10];
- Arrays.sort(strings, String::compareToIgnoreCase);
-```
-**上面的String::compareToIgnoreCase等价于(x, y) -> {return x.compareToIgnoreCase(y);}**
-我们看一下`Arrays#sort`方法`public static void sort(T[] a, Comparator super T> c)`,
-可以看到第二个参数是一个Comparator接口,该接口也是一个函数式接口,其中的抽象方法是`int compare(T o1, T o2);`,再看一下
-`String#compareToIgnoreCase`方法,`public int compareToIgnoreCase(String str)`,这个方法好像和上面讲方法引用中`类名::静态方法名`不大一样啊,它
-的参数列表和函数式接口的参数列表不一样啊,虽然它的返回值一样?
-
是的,确实不一样但是别忘了,String类的这个方法是个实例方法,而不是静态方法,也就是说,这个方法是需要有一个接收者的。所谓接收者就是
-instance.method(x)中的instance,
-它是某个类的实例,有的朋友可能已经明白了。上面函数式接口的`compare(T o1, T o2)`中的第一个参数作为了实例方法的接收者,而第二个参数作为了实例方法的
-参数。我们再举一个自己实现的例子:
-```java
-public class MethodReference {
- static Random random = new Random(47);
- public static void main(String[] args) {
- MethodReference[] methodReferences = new MethodReference[10];
- Arrays.sort(methodReferences, MethodReference::myCompare);
- }
- int myCompare(MethodReference o) {
- return random.nextInt(2) - 1;
- }
-}
-```
-上面的例子可以在IDE里通过编译,大家有兴趣的可以模仿上面的例子自己写一个程序,打印出排序后的结果。
-
**构造器引用**
-构造器引用仍然需要与特定的函数式接口配合使用,并不能像下面这样直接使用。IDE会提示String不是一个函数式接口
-```java
- //compile error : String is not a functional interface
- String str = String::new;
-```
-下面是一个使用构造器引用的例子,可以看出构造器引用可以和这种工厂型的函数式接口一起使用的。
-```java
- interface IFunctional {
- T func();
-}
-
-public class ConstructorReference {
-
- public ConstructorReference() {
- }
-
- public static void main(String[] args) {
- Supplier supplier0 = () -> new ConstructorReference();
- Supplier supplier1 = ConstructorReference::new;
- IFunctional functional = () -> new ConstructorReference();
- IFunctional functional1 = ConstructorReference::new;
- }
-}
-```
-下面是一个JDK官方的例子
-```java
- public static , DEST extends Collection>
- DEST transferElements(
- SOURCE sourceCollection,
- Supplier collectionFactory) {
-
- DEST result = collectionFactory.get();
- for (T t : sourceCollection) {
- result.add(t);
- }
- return result;
- }
-
- ...
-
- Set rosterSet = transferElements(
- roster, HashSet::new);
-```
-
-**实例::实例方法**
-
-其实开始那个例子就是一个实例::实例方法的引用
-```java
-List list = new ArrayList<>();
- for (int i = 0; i < 10; ++i) {
- list.add(i);
- }
- list.forEach(System.out::println);
-```
-其中System.out就是一个实例,println是一个实例方法。相信不用再给大家做解释了。
-### 总结
-Lambda表达式是JDK8引入Java的函数式编程语法,使用Lambda需要直接或者间接的与函数式接口配合,在开发中使用Lambda可以减少代码量,
-但是并不是说必须要使用Lambda(虽然它是一个很酷的东西)。有些情况下使用Lambda会使代码的可读性急剧下降,并且也节省不了多少代码,
-所以在实际开发中还是需要仔细斟酌是否要使用Lambda。和Lambda相似的还有JDK10中加入的var类型推断,同样对于这个特性需要斟酌使用。
-
-
-___
-
-
-## JDK8接口规范
-### 在JDK8中引入了lambda表达式,出现了函数式接口的概念,为了在扩展接口时保持向前兼容性(JDK8之前扩展接口会使得实现了该接口的类必须实现添加的方法,否则会报错。为了保持兼容性而做出妥协的特性还有泛型,泛型也是为了保持兼容性而失去了在一些别的语言泛型拥有的功能),Java接口规范发生了一些改变。
-### 1.JDK8以前的接口规范
-- JDK8以前接口可以定义的变量和方法
- - 所有变量(Field)不论是否显式 的声明为```public static final```,它实际上都是```public static final```的。
- - 所有方法(Method)不论是否显示 的声明为```public abstract```,它实际上都是```public abstract```的。
-```java
-public interface AInterfaceBeforeJDK8 {
- int FIELD = 0;
- void simpleMethod();
-}
-```
-以上接口信息反编译以后可以看到字节码信息里Filed是public static final的,而方法是public abstract的,即是你没有显示的去声明它。
-```java
-{
- public static final int FIELD;
- descriptor: I
- flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
- ConstantValue: int 0
-
- public abstract void simpleMethod();
- descriptor: ()V
- flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
-}
-```
-### 2.JDK8之后的接口规范
-- JDK8之后接口可以定义的变量和方法
- - 变量(Field)仍然必须是 ```java public static final```的
- - 方法(Method)除了可以是public abstract之外,还可以是public static或者是default(相当于仅public修饰的实例方法)的。
-从以上改变不难看出,修改接口的规范主要是为了能在扩展接口时保持向前兼容。
-
下面是一个JDK8之后的接口例子
-```java
-public interface AInterfaceInJDK8 {
- int simpleFiled = 0;
- static int staticField = 1;
-
- public static void main(String[] args) {
- }
- static void staticMethod(){}
-
- default void defaultMethod(){}
-
- void simpleMethod() throws IOException;
-
-}
-```
-进行反编译(去除了一些没用信息)
-```java
-{
- public static final int simpleFiled;
- flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
-
- public static final int staticField;
- flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
-
- public static void main(java.lang.String[]);
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-
- public static void staticMethod();
- flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-
- public void defaultMethod();
- flags: (0x0001) ACC_PUBLIC
-
- public abstract void simpleMethod() throws java.io.IOException;
- flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
- Exceptions:
- throws java.io.IOException
-}
-```
-可以看到 default关键字修饰的方法是像实例方法(就是普通类中定义的普通方法)一样定义的,所以我们来定义一个只有default方法的接口并且实现一下这个接口试一
-试。
-```java
-interface Default {
- default int defaultMethod() {
- return 4396;
- }
-}
-
-public class DefaultMethod implements Default {
- public static void main(String[] args) {
- DefaultMethod defaultMethod = new DefaultMethod();
- System.out.println(defaultMethod.defaultMethod());
- //compile error : Non-static method 'defaultMethod()' cannot be referenced from a static context
- //! DefaultMethod.defaultMethod();
- }
-}
-```
-可以看到default方法确实像实例方法一样,必须有实例对象才能调用,并且子类在实现接口时,可以不用实现default方法,也可以选择覆盖该方法。
-这有点像子类继承父类实例方法。
-
-接口静态方法就像是类静态方法,唯一的区别是**接口静态方法只能通过接口名调用,而类静态方法既可以通过类名调用也可以通过实例调用**
-```java
-interface Static {
- static int staticMethod() {
- return 4396;
- }
-}
- ... main(String...args)
- //!compile error: Static method may be invoked on containing interface class only
- //!aInstanceOfStatic.staticMethod();
- ...
-```
-另一个问题是多继承问题,大家知道Java中类是不支持多继承的,但是接口是多继承和多实现(implements后跟多个接口)的,
-那么如果一个接口继承另一个接口,两个接口都有同名的default方法会怎么样呢?答案是会像类继承一样覆写(@Override),以下代码在IDE中可以顺利编译
-```java
-interface Default {
- default int defaultMethod() {
- return 4396;
- }
-}
-interface Default2 extends Default {
- @Override
- default int defaultMethod() {
- return 9527;
- }
-}
-public class DefaultMethod implements Default,Default2 {
- public static void main(String[] args) {
- DefaultMethod defaultMethod = new DefaultMethod();
- System.out.println(defaultMethod.defaultMethod());
- }
-}
-
-输出 : 9527
-```
-出现上面的情况时,会优先找继承树上近的方法,类似于“短路优先”。
-
-那么如果一个类实现了两个没有继承关系的接口,且这两个接口有同名方法的话会怎么样呢?IDE会要求你重写这个冲突的方法,让你自己选择去执行哪个方法,因为IDE它还没智能到你不告诉它,它就知道你想执行哪个方法。可以通过```java 接口名.super```指针来访问接口中定义的实例(default)方法。
-```java
-interface Default {
- default int defaultMethod() {
- return 4396;
- }
-}
-
-interface Default2 {
- default int defaultMethod() {
- return 9527;
- }
-}
-//如果不重写
-//compile error : defaults.DefaultMethod inherits unrelated defaults for defaultMethod() from types defaults.Default and defaults.Default2
-public class DefaultMethod implements Default,Default2 {
-@Override
- public int defaultMethod() {
- System.out.println(Default.super.defaultMethod());
- System.out.println(Default2.super.defaultMethod());
- return 996;
- }
- public static void main(String[] args) {
- DefaultMethod defaultMethod = new DefaultMethod();
- System.out.println(defaultMethod.defaultMethod());
- }
-}
-
-运行输出 :
-4396
-9527
-996
-```
-
-
-___
-
-
-## 改进的类型推断
-### 1.什么是类型推断
-类型推断就像它的字面意思一样,编译器根据你显示声明的已知的信息 推断出你没有显示声明的类型,这就是类型推断。
-看过《Java编程思想 第四版》的朋友可能还记得里面讲解泛型一章的时候,里面很多例子是下面这样的:
-```java
- Map map = new Map();
-```
-而我们平常写的都是这样的:
-```java
- Map map = new Map<>();
-```
-这就是类型推断,《Java编程思想 第四版》这本书出书的时候最新的JDK只有1.6(JDK7推出的类型推断),在Java编程思想里Bruce Eckel大叔还提到过这个问题
-(可能JDK的官方人员看了Bruce Eckel大叔的Thinking in Java才加的类型推断,☺),在JDK7中推出了上面这样的类型推断,可以减少一些无用的代码。
-(Java编程思想到现在还只有第四版,是不是因为Bruce Eckel大叔觉得Java新推出的语言特性“然并卵”呢?/滑稽)
-
-在JDK7中,类型推断只有上面例子的那样的能力,即只有在使用**赋值语句**时才能自动推断出泛型参数信息(即<>里的信息),下面的官方文档里的例子在JDK7里会编译
-错误
-```java
- List stringList = new ArrayList<>();
- stringList.add("A");
- //error : addAll(java.util.Collection extends java.lang.String>)in List cannot be applied to (java.util.List)
- stringList.addAll(Arrays.asList());
-```
-但是上面的代码在JDK8里可以通过,也就说,JDK8里,类型推断不仅可以用于赋值语句,而且可以根据代码中上下文里的信息推断出更多的信息,因此我们需要些的代码
-会更少。加强的类型推断还有一个就是用于Lambda表达式了。
-
-大家其实不必细究类型推断,在日常使用中IDE会自动判断,当IDE自己无法推断出足够的信息时,就需要我们额外做一下工作,比如在<>里添加更多的类型信息,
-相信随着Java的进化,这些便利的功能会越来越强大。
-
-
-____
-
-
-## 通过反射获得方法的参数信息
-JDK8之前 .class文件是不会存储方法参数信息的,因此也就无法通过反射获取该信息(想想反射获取类信息的入口是什么?当然就是Class类了)。即是是在JDK11里
-也不会默认生成这些信息,可以通过在javac加上-parameters参数来让javac生成这些信息(javac就是java编译器,可以把java文件编译成.class文件)。生成额外
-的信息(运行时非必须信息)会消耗内存并且有可能公布敏感信息(某些方法参数比如password,JDK文档里这么说的),并且确实很多信息javac并不会为我们生成,比如
-LocalVariableTable,javac就不会默认生成,需要你加上 -g:vars来强制让编译器生成,同样的,方法参数信息也需要加上
--parameters来让javac为你在.class文件中生成这些信息,否则运行时反射是无法获取到这些信息的。在讲解Java语言层面的方法之前,先看一下javac加上该
-参数和不加生成的信息有什么区别(不感兴趣想直接看运行代码的可以跳过这段)。下面是随便写的一个类。
-```java
-public class ByteCodeParameters {
- public String simpleMethod(String canUGetMyName, Object yesICan) {
- return "9527";
- }
-}
-```
-先来不加参数编译和反编译一下这个类javac ByteCodeParameters.java , javap -v ByteCodeParameters:
-```java
- //只截取了部分信息
- public java.lang.String simpleMethod(java.lang.String, java.lang.Object);
- descriptor: (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=1, locals=3, args_size=3
- 0: ldc #2 // String 9527
- 2: areturn
- LineNumberTable:
- line 5: 0
- //这个方法的描述到这里就结束了
-```
-接下来我们加上参数javac -parameters ByteCodeParameters.java 再来看反编译的信息:
-```java
- public java.lang.String simpleMethod(java.lang.String, java.lang.Object);
- descriptor: (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=1, locals=3, args_size=3
- 0: ldc #2 // String 9527
- 2: areturn
- LineNumberTable:
- line 8: 0
- MethodParameters:
- Name Flags
- canUGetMyName
- yesICan
-```
-可以看到.class文件里多了一个MethodParameters信息,这就是参数的名字,可以看到默认是不保存的。
-
下面看一下在Intelj Idea里运行的这个例子,我们试一下通过反射获取方法名 :
-```java
-public class ByteCodeParameters {
- public String simpleMethod(String canUGetMyName, Object yesICan) {
- return "9527";
- }
-
- public static void main(String[] args) throws NoSuchMethodException {
- Class> clazz = ByteCodeParameters.class;
- Method simple = clazz.getDeclaredMethod("simpleMethod", String.class, Object.class);
- Parameter[] parameters = simple.getParameters();
- for (Parameter p : parameters) {
- System.out.println(p.getName());
- }
- }
-}
-输出 :
-arg0
-arg1
-```
-???说好的方法名呢????别急,哈哈。前面说了,默认是不生成参数名信息的,因此我们需要做一些配置,我们找到IDEA的settings里的Java Compiler选项,在
-Additional command line parameters:一行加上-parameters(Eclipse 也是找到Java Compiler选中Stoer information about method parameters),或者自
-己编译一个.class文件放在IDEA的out下,然后再来运行 :
-```java
-输出 :
-canUGetMyName
-yesICan
-```
-这样我们就通过反射获取到参数信息了。想要了解更多的同学可以自己研究一下 [官方文档]
-(https://docs.oracle.com/javase/tutorial/reflect/member/methodparameterreflection.html)
-
-## 总结与补充
-在JDK8之后,可以通过-parameters参数来让编译器生成参数信息然后在运行时通过反射获取方法参数信息,其实在SpringFramework
-里面也有一个LocalVariableTableParameterNameDiscoverer对象可以获取方法参数名信息,有兴趣的同学可以自行百度(这个类在打印日志时可能会比较有用吧,个人感觉)。
-
-____
-
-
-
-
-___
diff --git a/docs/java/What's New in JDK8/Stream.md b/docs/java/What's New in JDK8/Stream.md
deleted file mode 100644
index de7c86e3f2e..00000000000
--- a/docs/java/What's New in JDK8/Stream.md
+++ /dev/null
@@ -1,75 +0,0 @@
-Stream API 旨在让编码更高效率、干净、简洁。
-
-### 从迭代器到Stream操作
-
-当使用 `Stream` 时,我们一般会通过三个阶段建立一个流水线:
-
-1. 创建一个 `Stream`;
-2. 进行一个或多个中间操作;
-3. 使用终止操作产生一个结果,`Stream` 就不会再被使用了。
-
-**案例1:统计 List 中的单词长度大于6的个数**
-
-```java
-/**
-* 案例1:统计 List 中的单词长度大于6的个数
-*/
-ArrayList wordsList = new ArrayList();
-wordsList.add("Charles");
-wordsList.add("Vincent");
-wordsList.add("William");
-wordsList.add("Joseph");
-wordsList.add("Henry");
-wordsList.add("Bill");
-wordsList.add("Joan");
-wordsList.add("Linda");
-int count = 0;
-```
-Java8之前我们通常用迭代方法来完成上面的需求:
-
-```java
-//迭代(Java8之前的常用方法)
-//迭代不好的地方:1. 代码多;2 很难被并行运算。
-for (String word : wordsList) {
- if (word.length() > 6) {
- count++;
- }
-}
-System.out.println(count);//3
-```
-Java8之前我们使用 `Stream` 一行代码就能解决了,而且可以瞬间转换为并行执行的效果:
-
-```java
-//Stream
-//将stream()改为parallelStream()就可以瞬间将代码编程并行执行的效果
-long count2=wordsList.stream()
- .filter(w->w.length()>6)
- .count();
-long count3=wordsList.parallelStream()
- .filter(w->w.length()>6)
- .count();
-System.out.println(count2);
-System.out.println(count3);
-```
-
-### `distinct()`
-
-去除 List 中重复的 String
-
-```java
-List list = list.stream()
- .distinct()
- .collect(Collectors.toList());
-```
-
-### `map`
-
-map 方法用于映射每个元素到对应的结果,以下代码片段使用 map 输出了元素对应的平方数:
-
-```java
-List numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
-// 获取 List 中每个元素对应的平方数并去重
-List squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
-System.out.println(squaresList.toString());//[9, 4, 49, 25]
-```
-
diff --git "a/docs/java/What's New in JDK8/\346\224\271\350\277\233\347\232\204\347\261\273\345\236\213\346\216\250\346\226\255.md" "b/docs/java/What's New in JDK8/\346\224\271\350\277\233\347\232\204\347\261\273\345\236\213\346\216\250\346\226\255.md"
deleted file mode 100644
index b5cff7bb0c0..00000000000
--- "a/docs/java/What's New in JDK8/\346\224\271\350\277\233\347\232\204\347\261\273\345\236\213\346\216\250\346\226\255.md"
+++ /dev/null
@@ -1,30 +0,0 @@
-## 改进的类型推断
-### 1.什么是类型推断
-类型推断就像它的字面意思一样,编译器根据你显示声明的已知的信息 推断出你没有显示声明的类型,这就是类型推断。
-看过《Java编程思想 第四版》的朋友可能还记得里面讲解泛型一章的时候,里面很多例子是下面这样的:
-```java
- Map map = new Map();
-```
-而我们平常写的都是这样的:
-```java
- Map map = new Map<>();
-```
-这就是类型推断,《Java编程思想 第四版》这本书出书的时候最新的JDK只有1.6(JDK7推出的类型推断),在Java编程思想里Bruce Eckel大叔还提到过这个问题
-(可能JDK的官方人员看了Bruce Eckel大叔的Thinking in Java才加的类型推断,☺),在JDK7中推出了上面这样的类型推断,可以减少一些无用的代码。
-(Java编程思想到现在还只有第四版,是不是因为Bruce Eckel大叔觉得Java新推出的语言特性“然并卵”呢?/滑稽)
-
-在JDK7中,类型推断只有上面例子的那样的能力,即只有在使用**赋值语句**时才能自动推断出泛型参数信息(即<>里的信息),下面的官方文档里的例子在JDK7里会编译
-错误
-```java
- List stringList = new ArrayList<>();
- stringList.add("A");
- //error : addAll(java.util.Collection extends java.lang.String>)in List cannot be applied to (java.util.List)
- stringList.addAll(Arrays.asList());
-```
-但是上面的代码在JDK8里可以通过,也就说,JDK8里,类型推断不仅可以用于赋值语句,而且可以根据代码中上下文里的信息推断出更多的信息,因此我们需要些的代码
-会更少。加强的类型推断还有一个就是用于Lambda表达式了。
-
-大家其实不必细究类型推断,在日常使用中IDE会自动判断,当IDE自己无法推断出足够的信息时,就需要我们额外做一下工作,比如在<>里添加更多的类型信息,
-相信随着Java的进化,这些便利的功能会越来越强大。
-
-
diff --git "a/docs/java/What's New in JDK8/\351\200\232\350\277\207\345\217\215\345\260\204\350\216\267\345\276\227\346\226\271\346\263\225\347\232\204\345\217\202\346\225\260\344\277\241\346\201\257.md" "b/docs/java/What's New in JDK8/\351\200\232\350\277\207\345\217\215\345\260\204\350\216\267\345\276\227\346\226\271\346\263\225\347\232\204\345\217\202\346\225\260\344\277\241\346\201\257.md"
deleted file mode 100644
index a1d91c4b2fe..00000000000
--- "a/docs/java/What's New in JDK8/\351\200\232\350\277\207\345\217\215\345\260\204\350\216\267\345\276\227\346\226\271\346\263\225\347\232\204\345\217\202\346\225\260\344\277\241\346\201\257.md"
+++ /dev/null
@@ -1,79 +0,0 @@
-## 通过反射获得方法的参数信息
-JDK8之前 .class文件是不会存储方法参数信息的,因此也就无法通过反射获取该信息(想想反射获取类信息的入口是什么?当然就是Class类了)。即是是在JDK11里
-也不会默认生成这些信息,可以通过在javac加上-parameters参数来让javac生成这些信息(javac就是java编译器,可以把java文件编译成.class文件)。生成额外
-的信息(运行时非必须信息)会消耗内存并且有可能公布敏感信息(某些方法参数比如password,JDK文档里这么说的),并且确实很多信息javac并不会为我们生成,比如
-LocalVariableTable,javac就不会默认生成,需要你加上 -g:vars来强制让编译器生成,同样的,方法参数信息也需要加上
--parameters来让javac为你在.class文件中生成这些信息,否则运行时反射是无法获取到这些信息的。在讲解Java语言层面的方法之前,先看一下javac加上该
-参数和不加生成的信息有什么区别(不感兴趣想直接看运行代码的可以跳过这段)。下面是随便写的一个类。
-```java
-public class ByteCodeParameters {
- public String simpleMethod(String canUGetMyName, Object yesICan) {
- return "9527";
- }
-}
-```
-先来不加参数编译和反编译一下这个类javac ByteCodeParameters.java , javap -v ByteCodeParameters:
-```java
- //只截取了部分信息
- public java.lang.String simpleMethod(java.lang.String, java.lang.Object);
- descriptor: (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=1, locals=3, args_size=3
- 0: ldc #2 // String 9527
- 2: areturn
- LineNumberTable:
- line 5: 0
- //这个方法的描述到这里就结束了
-```
-接下来我们加上参数javac -parameters ByteCodeParameters.java 再来看反编译的信息:
-```java
- public java.lang.String simpleMethod(java.lang.String, java.lang.Object);
- descriptor: (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String;
- flags: (0x0001) ACC_PUBLIC
- Code:
- stack=1, locals=3, args_size=3
- 0: ldc #2 // String 9527
- 2: areturn
- LineNumberTable:
- line 8: 0
- MethodParameters:
- Name Flags
- canUGetMyName
- yesICan
-```
-可以看到.class文件里多了一个MethodParameters信息,这就是参数的名字,可以看到默认是不保存的。
-
下面看一下在Intelj Idea里运行的这个例子,我们试一下通过反射获取方法名 :
-```java
-public class ByteCodeParameters {
- public String simpleMethod(String canUGetMyName, Object yesICan) {
- return "9527";
- }
-
- public static void main(String[] args) throws NoSuchMethodException {
- Class> clazz = ByteCodeParameters.class;
- Method simple = clazz.getDeclaredMethod("simpleMethod", String.class, Object.class);
- Parameter[] parameters = simple.getParameters();
- for (Parameter p : parameters) {
- System.out.println(p.getName());
- }
- }
-}
-输出 :
-arg0
-arg1
-```
-???说好的方法名呢????别急,哈哈。前面说了,默认是不生成参数名信息的,因此我们需要做一些配置,我们找到IDEA的settings里的Java Compiler选项,在
-Additional command line parameters:一行加上-parameters(Eclipse 也是找到Java Compiler选中Stoer information about method parameters),或者自
-己编译一个.class文件放在IDEA的out下,然后再来运行 :
-```java
-输出 :
-canUGetMyName
-yesICan
-```
-这样我们就通过反射获取到参数信息了。想要了解更多的同学可以自己研究一下 [官方文档]
-(https://docs.oracle.com/javase/tutorial/reflect/member/methodparameterreflection.html)
-
-## 总结与补充
-在JDK8之后,可以通过-parameters参数来让编译器生成参数信息然后在运行时通过反射获取方法参数信息,其实在SpringFramework
-里面也有一个LocalVariableTableParameterNameDiscoverer对象可以获取方法参数名信息,有兴趣的同学可以自行百度(这个类在打印日志时可能会比较有用吧,个人感觉)。
diff --git a/docs/java/ArrayList-Grow.md b/docs/java/collection/ArrayList-Grow.md
similarity index 99%
rename from docs/java/ArrayList-Grow.md
rename to docs/java/collection/ArrayList-Grow.md
index 6dd4cc93daf..06fa5388d76 100644
--- a/docs/java/ArrayList-Grow.md
+++ b/docs/java/collection/ArrayList-Grow.md
@@ -270,7 +270,6 @@ public class ArrayscopyOfTest {
10
```
-
### 3.3 两者联系和区别
**联系:**
@@ -281,8 +280,6 @@ public class ArrayscopyOfTest {
`arraycopy()` 需要目标数组,将原数组拷贝到你自己定义的数组里或者原数组,而且可以选择拷贝的起点和长度以及放入新数组中的位置 `copyOf()` 是系统自动在内部新建一个数组,并返回该数组。
-
-
## 四 `ensureCapacity`方法
ArrayList 源码中有一个 `ensureCapacity` 方法不知道大家注意到没有,这个方法 ArrayList 内部没有被调用过,所以很显然是提供给用户调用的,那么这个方法有什么作用呢?
@@ -341,7 +338,6 @@ public class EnsureCapacityTest {
```
使用ensureCapacity方法前:4637
使用ensureCapacity方法后:241
-
```
通过运行结果,我们可以很明显的看出向 ArrayList 添加大量元素之前最好先使用`ensureCapacity` 方法,以减少增量重新分配的次数
diff --git a/docs/java/ArrayList.md b/docs/java/collection/ArrayList.md
similarity index 98%
rename from docs/java/ArrayList.md
rename to docs/java/collection/ArrayList.md
index c3e8dd47896..f6578a7a784 100644
--- a/docs/java/ArrayList.md
+++ b/docs/java/collection/ArrayList.md
@@ -660,7 +660,7 @@ public class ArrayList extends AbstractList
(3)private class SubList extends AbstractList implements RandomAccess
(4)static final class ArrayListSpliterator implements Spliterator
```
- ArrayList有四个内部类,其中的**Itr是实现了Iterator接口**,同时重写了里面的**hasNext()**,**next()**,**remove()**等方法;其中的**ListItr**继承**Itr**,实现了**ListIterator接口**,同时重写了**hasPrevious()**,**nextIndex()**,**previousIndex()**,**previous()**,**set(E e)**,**add(E e)**等方法,所以这也可以看出了 **Iterator和ListIterator的区别:**ListIterator在Iterator的基础上增加了添加对象,修改对象,逆向遍历等方法,这些是Iterator不能实现的。
+ ArrayList有四个内部类,其中的**Itr是实现了Iterator接口**,同时重写了里面的**hasNext()**, **next()**, **remove()** 等方法;其中的**ListItr** 继承 **Itr**,实现了**ListIterator接口**,同时重写了**hasPrevious()**, **nextIndex()**, **previousIndex()**, **previous()**, **set(E e)**, **add(E e)** 等方法,所以这也可以看出了 **Iterator和ListIterator的区别:** ListIterator在Iterator的基础上增加了添加对象,修改对象,逆向遍历等方法,这些是Iterator不能实现的。
### ArrayList经典Demo
```java
diff --git a/docs/java/HashMap.md b/docs/java/collection/HashMap.md
similarity index 98%
rename from docs/java/HashMap.md
rename to docs/java/collection/HashMap.md
index 716bb1f34a5..80cd8b571bc 100644
--- a/docs/java/HashMap.md
+++ b/docs/java/collection/HashMap.md
@@ -56,7 +56,7 @@ static int hash(int h) {
所谓 **“拉链法”** 就是:将链表和数组相结合。也就是说创建一个链表数组,数组中每一格就是一个链表。若遇到哈希冲突,则将冲突的值加到链表中即可。
-
+
### JDK1.8之后
相比于之前的版本,jdk1.8在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。
@@ -170,7 +170,9 @@ static final class TreeNode extends LinkedHashMap.Entry {
```
## HashMap源码分析
### 构造方法
-
+
+HashMap 中有四个构造方法,它们分别如下:
+
```java
// 默认构造函数。
public HashMap() {
@@ -237,9 +239,7 @@ HashMap只提供了put用于添加元素,putVal方法只是给put方法调用
- ①如果定位到的数组位置没有元素 就直接插入。
- ②如果定位到的数组位置有元素就和要插入的key比较,如果key相同就直接覆盖,如果key不相同,就判断p是否是一个树节点,如果是就调用`e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value)`将元素添加进入。如果不是就遍历链表插入(插入的是链表尾部)。
-
-
-
+
```java
public V put(K key, V value) {
diff --git "a/docs/java/collection/Java\351\233\206\345\220\210\346\241\206\346\236\266\345\270\270\350\247\201\351\235\242\350\257\225\351\242\230.md" "b/docs/java/collection/Java\351\233\206\345\220\210\346\241\206\346\236\266\345\270\270\350\247\201\351\235\242\350\257\225\351\242\230.md"
new file mode 100644
index 00000000000..db10324fcc3
--- /dev/null
+++ "b/docs/java/collection/Java\351\233\206\345\220\210\346\241\206\346\236\266\345\270\270\350\247\201\351\235\242\350\257\225\351\242\230.md"
@@ -0,0 +1,456 @@
+点击关注[公众号](#公众号)及时获取笔主最新更新文章,并可免费领取本文档配套的《Java面试突击》以及Java工程师必备学习资源。
+
+
+
+- [剖析面试最常见问题之Java基础知识](#剖析面试最常见问题之java基础知识)
+ - [说说List,Set,Map三者的区别?](#说说listsetmap三者的区别)
+ - [Arraylist 与 LinkedList 区别?](#arraylist-与-linkedlist-区别)
+ - [补充内容:RandomAccess接口](#补充内容randomaccess接口)
+ - [补充内容:双向链表和双向循环链表](#补充内容双向链表和双向循环链表)
+ - [ArrayList 与 Vector 区别呢?为什么要用Arraylist取代Vector呢?](#arraylist-与-vector-区别呢为什么要用arraylist取代vector呢)
+ - [说一说 ArrayList 的扩容机制吧](#说一说-arraylist-的扩容机制吧)
+ - [HashMap 和 Hashtable 的区别](#hashmap-和-hashtable-的区别)
+ - [HashMap 和 HashSet区别](#hashmap-和-hashset区别)
+ - [HashSet如何检查重复](#hashset如何检查重复)
+ - [HashMap的底层实现](#hashmap的底层实现)
+ - [JDK1.8之前](#jdk18之前)
+ - [JDK1.8之后](#jdk18之后)
+ - [HashMap 的长度为什么是2的幂次方](#hashmap-的长度为什么是2的幂次方)
+ - [HashMap 多线程操作导致死循环问题](#hashmap-多线程操作导致死循环问题)
+ - [ConcurrentHashMap 和 Hashtable 的区别](#concurrenthashmap-和-hashtable-的区别)
+ - [ConcurrentHashMap线程安全的具体实现方式/底层具体实现](#concurrenthashmap线程安全的具体实现方式底层具体实现)
+ - [JDK1.7(上面有示意图)](#jdk17上面有示意图)
+ - [JDK1.8 (上面有示意图)](#jdk18-上面有示意图)
+ - [comparable 和 Comparator的区别](#comparable-和-comparator的区别)
+ - [Comparator定制排序](#comparator定制排序)
+ - [重写compareTo方法实现按年龄来排序](#重写compareto方法实现按年龄来排序)
+ - [集合框架底层数据结构总结](#集合框架底层数据结构总结)
+ - [Collection](#collection)
+ - [1. List](#1-list)
+ - [2. Set](#2-set)
+ - [Map](#map)
+ - [如何选用集合?](#如何选用集合)
+
+
+
+# 剖析面试最常见问题之Java基础知识
+
+## 说说List,Set,Map三者的区别?
+
+- **List(对付顺序的好帮手):** List接口存储一组不唯一(可以有多个元素引用相同的对象),有序的对象
+- **Set(注重独一无二的性质):** 不允许重复的集合。不会有多个元素引用相同的对象。
+- **Map(用Key来搜索的专家):** 使用键值对存储。Map会维护与Key有关联的值。两个Key可以引用相同的对象,但Key不能重复,典型的Key是String类型,但也可以是任何对象。
+
+## Arraylist 与 LinkedList 区别?
+
+- **1. 是否保证线程安全:** `ArrayList` 和 `LinkedList` 都是不同步的,也就是不保证线程安全;
+
+- **2. 底层数据结构:** `Arraylist` 底层使用的是 **`Object` 数组**;`LinkedList` 底层使用的是 **双向链表** 数据结构(JDK1.6之前为循环链表,JDK1.7取消了循环。注意双向链表和双向循环链表的区别,下面有介绍到!)
+
+- **3. 插入和删除是否受元素位置的影响:** ① **`ArrayList` 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。** 比如:执行`add(E e) `方法的时候, `ArrayList` 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是O(1)。但是如果要在指定位置 i 插入和删除元素的话(`add(int index, E element) `)时间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。 ② **`LinkedList` 采用链表存储,所以对于`add(E e)`方法的插入,删除元素时间复杂度不受元素位置的影响,近似 O(1),如果是要在指定位置`i`插入和删除元素的话(`(add(int index, E element)`) 时间复杂度应为`o(n))`因为需要新创立一个新的链表,复制前i-1个元素并在第i位加入新的元素,最后附上n-i个元素。**
+
+- **4. 是否支持快速随机访问:** `LinkedList` 不支持高效的随机元素访问,而 `ArrayList` 支持。快速随机访问就是通过元素的序号快速获取元素对象(对应于`get(int index) `方法)。
+
+- **5. 内存空间占用:** ArrayList的空 间浪费主要体现在在list列表的结尾会预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗比ArrayList更多的空间(因为要存放直接后继和直接前驱以及数据)。
+
+### **补充内容:RandomAccess接口**
+
+```java
+public interface RandomAccess {
+}
+```
+
+查看源码我们发现实际上 `RandomAccess` 接口中什么都没有定义。所以,在我看来 `RandomAccess` 接口不过是一个标识罢了。标识什么? 标识实现这个接口的类具有随机访问功能。
+
+在 `binarySearch(`)方法中,它要判断传入的list 是否 `RamdomAccess` 的实例,如果是,调用`indexedBinarySearch()`方法,如果不是,那么调用`iteratorBinarySearch()`方法
+
+```java
+ public static
+ int binarySearch(List extends Comparable super T>> list, T key) {
+ if (list instanceof RandomAccess || list.size() MAXIMUM_CAPACITY)
+ initialCapacity = MAXIMUM_CAPACITY;
+ if (loadFactor <= 0 || Float.isNaN(loadFactor))
+ throw new IllegalArgumentException("Illegal load factor: " +
+ loadFactor);
+ this.loadFactor = loadFactor;
+ this.threshold = tableSizeFor(initialCapacity);
+ }
+ public HashMap(int initialCapacity) {
+ this(initialCapacity, DEFAULT_LOAD_FACTOR);
+ }
+```
+
+下面这个方法保证了 HashMap 总是使用2的幂作为哈希表的大小。
+
+```java
+ /**
+ * Returns a power of two size for the given target capacity.
+ */
+ static final int tableSizeFor(int cap) {
+ int n = cap - 1;
+ n |= n >>> 1;
+ n |= n >>> 2;
+ n |= n >>> 4;
+ n |= n >>> 8;
+ n |= n >>> 16;
+ return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
+ }
+```
+
+## HashMap 和 HashSet区别
+
+如果你看过 `HashSet` 源码的话就应该知道:HashSet 底层就是基于 HashMap 实现的。(HashSet 的源码非常非常少,因为除了 `clone() `、`writeObject()`、`readObject()`是 HashSet 自己不得不实现之外,其他方法都是直接调用 HashMap 中的方法。
+
+| HashMap | HashSet |
+| :------------------------------: | :----------------------------------------------------------: |
+| 实现了Map接口 | 实现Set接口 |
+| 存储键值对 | 仅存储对象 |
+| 调用 `put()`向map中添加元素 | 调用 `add()`方法向Set中添加元素 |
+| HashMap使用键(Key)计算Hashcode | HashSet使用成员对象来计算hashcode值,对于两个对象来说hashcode可能相同,所以equals()方法用来判断对象的相等性, |
+
+## HashSet如何检查重复
+
+当你把对象加入`HashSet`时,HashSet会先计算对象的`hashcode`值来判断对象加入的位置,同时也会与其他加入的对象的hashcode值作比较,如果没有相符的hashcode,HashSet会假设对象没有重复出现。但是如果发现有相同hashcode值的对象,这时会调用`equals()`方法来检查hashcode相等的对象是否真的相同。如果两者相同,HashSet就不会让加入操作成功。(摘自我的Java启蒙书《Head fist java》第二版)
+
+**hashCode()与equals()的相关规定:**
+
+1. 如果两个对象相等,则hashcode一定也是相同的
+2. 两个对象相等,对两个equals方法返回true
+3. 两个对象有相同的hashcode值,它们也不一定是相等的
+4. 综上,equals方法被覆盖过,则hashCode方法也必须被覆盖
+5. hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)。
+
+**==与equals的区别**
+
+1. ==是判断两个变量或实例是不是指向同一个内存空间 equals是判断两个变量或实例所指向的内存空间的值是不是相同
+2. ==是指对内存地址进行比较 equals()是对字符串的内容进行比较
+3. ==指引用是否相同 equals()指的是值是否相同
+
+## HashMap的底层实现
+
+### JDK1.8之前
+
+JDK1.8 之前 `HashMap` 底层是 **数组和链表** 结合在一起使用也就是 **链表散列**。**HashMap 通过 key 的 hashCode 经过扰动函数处理过后得到 hash 值,然后通过 (n - 1) & hash 判断当前元素存放的位置(这里的 n 指的是数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的 hash 值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。**
+
+**所谓扰动函数指的就是 HashMap 的 hash 方法。使用 hash 方法也就是扰动函数是为了防止一些实现比较差的 hashCode() 方法 换句话说使用扰动函数之后可以减少碰撞。**
+
+**JDK 1.8 HashMap 的 hash 方法源码:**
+
+JDK 1.8 的 hash方法 相比于 JDK 1.7 hash 方法更加简化,但是原理不变。
+
+```java
+ static final int hash(Object key) {
+ int h;
+ // key.hashCode():返回散列值也就是hashcode
+ // ^ :按位异或
+ // >>>:无符号右移,忽略符号位,空位都以0补齐
+ return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
+ }
+```
+
+对比一下 JDK1.7的 HashMap 的 hash 方法源码.
+
+```java
+static int hash(int h) {
+ // This function ensures that hashCodes that differ only by
+ // constant multiples at each bit position have a bounded
+ // number of collisions (approximately 8 at default load factor).
+
+ h ^= (h >>> 20) ^ (h >>> 12);
+ return h ^ (h >>> 7) ^ (h >>> 4);
+}
+```
+
+相比于 JDK1.8 的 hash 方法 ,JDK 1.7 的 hash 方法的性能会稍差一点点,因为毕竟扰动了 4 次。
+
+所谓 **“拉链法”** 就是:将链表和数组相结合。也就是说创建一个链表数组,数组中每一格就是一个链表。若遇到哈希冲突,则将冲突的值加到链表中即可。
+
+
+
+### JDK1.8之后
+
+相比于之前的版本, JDK1.8之后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。
+
+
+
+> TreeMap、TreeSet以及JDK1.8之后的HashMap底层都用到了红黑树。红黑树就是为了解决二叉查找树的缺陷,因为二叉查找树在某些情况下会退化成一个线性结构。
+
+**推荐阅读:**
+
+- 《Java 8系列之重新认识HashMap》 :
+
+## HashMap 的长度为什么是2的幂次方
+
+为了能让 HashMap 存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀。我们上面也讲到了过了,Hash 值的范围值-2147483648到2147483647,前后加起来大概40亿的映射空间,只要哈希函数映射得比较均匀松散,一般应用是很难出现碰撞的。但问题是一个40亿长度的数组,内存是放不下的。所以这个散列值是不能直接拿来用的。用之前还要先做对数组的长度取模运算,得到的余数才能用来要存放的位置也就是对应的数组下标。这个数组下标的计算方法是“ `(n - 1) & hash`”。(n代表数组长度)。这也就解释了 HashMap 的长度为什么是2的幂次方。
+
+**这个算法应该如何设计呢?**
+
+我们首先可能会想到采用%取余的操作来实现。但是,重点来了:**“取余(%)操作中如果除数是2的幂次则等价于与其除数减一的与(&)操作(也就是说 hash%length==hash&(length-1)的前提是 length 是2的 n 次方;)。”** 并且 **采用二进制位操作 &,相对于%能够提高运算效率,这就解释了 HashMap 的长度为什么是2的幂次方。**
+
+## HashMap 多线程操作导致死循环问题
+
+主要原因在于 并发下的Rehash 会造成元素之间会形成一个循环链表。不过,jdk 1.8 后解决了这个问题,但是还是不建议在多线程下使用 HashMap,因为多线程下使用 HashMap 还是会存在其他问题比如数据丢失。并发环境下推荐使用 ConcurrentHashMap 。
+
+详情请查看:
+
+## ConcurrentHashMap 和 Hashtable 的区别
+
+ConcurrentHashMap 和 Hashtable 的区别主要体现在实现线程安全的方式上不同。
+
+- **底层数据结构:** JDK1.7的 ConcurrentHashMap 底层采用 **分段的数组+链表** 实现,JDK1.8 采用的数据结构跟HashMap1.8的结构一样,数组+链表/红黑二叉树。Hashtable 和 JDK1.8 之前的 HashMap 的底层数据结构类似都是采用 **数组+链表** 的形式,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的;
+- **实现线程安全的方式(重要):** ① **在JDK1.7的时候,ConcurrentHashMap(分段锁)** 对整个桶数组进行了分割分段(Segment),每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。 **到了 JDK1.8 的时候已经摒弃了Segment的概念,而是直接用 Node 数组+链表+红黑树的数据结构来实现,并发控制使用 synchronized 和 CAS 来操作。(JDK1.6以后 对 synchronized锁做了很多优化)** 整个看起来就像是优化过且线程安全的 HashMap,虽然在JDK1.8中还能看到 Segment 的数据结构,但是已经简化了属性,只是为了兼容旧版本;② **Hashtable(同一把锁)** :使用 synchronized 来保证线程安全,效率非常低下。当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态,如使用 put 添加元素,另一个线程不能使用 put 添加元素,也不能使用 get,竞争会越来越激烈效率越低。
+
+**两者的对比图:**
+
+图片来源:
+
+**HashTable:**
+
+
+
+**JDK1.7的ConcurrentHashMap:**
+
+
+
+**JDK1.8的ConcurrentHashMap(TreeBin: 红黑二叉树节点 Node: 链表节点):**
+
+
+
+## ConcurrentHashMap线程安全的具体实现方式/底层具体实现
+
+### JDK1.7(上面有示意图)
+
+首先将数据分为一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据时,其他段的数据也能被其他线程访问。
+
+**ConcurrentHashMap 是由 Segment 数组结构和 HashEntry 数组结构组成**。
+
+Segment 实现了 ReentrantLock,所以 Segment 是一种可重入锁,扮演锁的角色。HashEntry 用于存储键值对数据。
+
+```java
+static class Segment extends ReentrantLock implements Serializable {
+}
+```
+
+一个 ConcurrentHashMap 里包含一个 Segment 数组。Segment 的结构和HashMap类似,是一种数组和链表结构,一个 Segment 包含一个 HashEntry 数组,每个 HashEntry 是一个链表结构的元素,每个 Segment 守护着一个HashEntry数组里的元素,当对 HashEntry 数组的数据进行修改时,必须首先获得对应的 Segment的锁。
+
+### JDK1.8 (上面有示意图)
+
+ConcurrentHashMap取消了Segment分段锁,采用CAS和synchronized来保证并发安全。数据结构跟HashMap1.8的结构类似,数组+链表/红黑二叉树。Java 8在链表长度超过一定阈值(8)时将链表(寻址时间复杂度为O(N))转换为红黑树(寻址时间复杂度为O(log(N)))
+
+synchronized只锁定当前链表或红黑二叉树的首节点,这样只要hash不冲突,就不会产生并发,效率又提升N倍。
+
+## comparable 和 Comparator的区别
+
+- comparable接口实际上是出自java.lang包 它有一个 `compareTo(Object obj)`方法用来排序
+- comparator接口实际上是出自 java.util 包它有一个`compare(Object obj1, Object obj2)`方法用来排序
+
+一般我们需要对一个集合使用自定义排序时,我们就要重写`compareTo()`方法或`compare()`方法,当我们需要对某一个集合实现两种排序方式,比如一个song对象中的歌名和歌手名分别采用一种排序方法的话,我们可以重写`compareTo()`方法和使用自制的Comparator方法或者以两个Comparator来实现歌名排序和歌星名排序,第二种代表我们只能使用两个参数版的 `Collections.sort()`.
+
+### Comparator定制排序
+
+```java
+ ArrayList arrayList = new ArrayList();
+ arrayList.add(-1);
+ arrayList.add(3);
+ arrayList.add(3);
+ arrayList.add(-5);
+ arrayList.add(7);
+ arrayList.add(4);
+ arrayList.add(-9);
+ arrayList.add(-7);
+ System.out.println("原始数组:");
+ System.out.println(arrayList);
+ // void reverse(List list):反转
+ Collections.reverse(arrayList);
+ System.out.println("Collections.reverse(arrayList):");
+ System.out.println(arrayList);
+
+ // void sort(List list),按自然排序的升序排序
+ Collections.sort(arrayList);
+ System.out.println("Collections.sort(arrayList):");
+ System.out.println(arrayList);
+ // 定制排序的用法
+ Collections.sort(arrayList, new Comparator() {
+
+ @Override
+ public int compare(Integer o1, Integer o2) {
+ return o2.compareTo(o1);
+ }
+ });
+ System.out.println("定制排序后:");
+ System.out.println(arrayList);
+```
+
+Output:
+
+```
+原始数组:
+[-1, 3, 3, -5, 7, 4, -9, -7]
+Collections.reverse(arrayList):
+[-7, -9, 4, 7, -5, 3, 3, -1]
+Collections.sort(arrayList):
+[-9, -7, -5, -1, 3, 3, 4, 7]
+定制排序后:
+[7, 4, 3, 3, -1, -5, -7, -9]
+```
+
+### 重写compareTo方法实现按年龄来排序
+
+```java
+// person对象没有实现Comparable接口,所以必须实现,这样才不会出错,才可以使treemap中的数据按顺序排列
+// 前面一个例子的String类已经默认实现了Comparable接口,详细可以查看String类的API文档,另外其他
+// 像Integer类等都已经实现了Comparable接口,所以不需要另外实现了
+
+public class Person implements Comparable {
+ private String name;
+ private int age;
+
+ public Person(String name, int age) {
+ super();
+ this.name = name;
+ this.age = age;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public void setAge(int age) {
+ this.age = age;
+ }
+
+ /**
+ * TODO重写compareTo方法实现按年龄来排序
+ */
+ @Override
+ public int compareTo(Person o) {
+ // TODO Auto-generated method stub
+ if (this.age > o.getAge()) {
+ return 1;
+ } else if (this.age < o.getAge()) {
+ return -1;
+ }
+ return age;
+ }
+}
+
+```
+
+```java
+ public static void main(String[] args) {
+ TreeMap pdata = new TreeMap();
+ pdata.put(new Person("张三", 30), "zhangsan");
+ pdata.put(new Person("李四", 20), "lisi");
+ pdata.put(new Person("王五", 10), "wangwu");
+ pdata.put(new Person("小红", 5), "xiaohong");
+ // 得到key的值的同时得到key所对应的值
+ Set keys = pdata.keySet();
+ for (Person key : keys) {
+ System.out.println(key.getAge() + "-" + key.getName());
+
+ }
+ }
+```
+
+Output:
+
+```
+5-小红
+10-王五
+20-李四
+30-张三
+```
+
+## 集合框架底层数据结构总结
+
+### Collection
+
+#### 1. List
+
+- **Arraylist:** Object数组
+- **Vector:** Object数组
+- **LinkedList:** 双向链表(JDK1.6之前为循环链表,JDK1.7取消了循环)
+
+#### 2. Set
+
+- **HashSet(无序,唯一):** 基于 HashMap 实现的,底层采用 HashMap 来保存元素
+- **LinkedHashSet:** LinkedHashSet 继承于 HashSet,并且其内部是通过 LinkedHashMap 来实现的。有点类似于我们之前说的LinkedHashMap 其内部是基于 HashMap 实现一样,不过还是有一点点区别的
+- **TreeSet(有序,唯一):** 红黑树(自平衡的排序二叉树)
+
+### Map
+
+- **HashMap:** JDK1.8之前HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突)。JDK1.8以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间
+- **LinkedHashMap:** LinkedHashMap 继承自 HashMap,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,LinkedHashMap 在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。详细可以查看:[《LinkedHashMap 源码详细分析(JDK1.8)》](https://www.imooc.com/article/22931)
+- **Hashtable:** 数组+链表组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的
+- **TreeMap:** 红黑树(自平衡的排序二叉树)
+
+## 如何选用集合?
+
+主要根据集合的特点来选用,比如我们需要根据键值获取到元素值时就选用Map接口下的集合,需要排序时选择TreeMap,不需要排序时就选择HashMap,需要保证线程安全就选用ConcurrentHashMap.当我们只需要存放元素值时,就选择实现Collection接口的集合,需要保证元素唯一时选择实现Set接口的集合比如TreeSet或HashSet,不需要就选择实现List接口的比如ArrayList或LinkedList,然后再根据实现这些接口的集合的特点来选用。
+
+## 公众号
+
+如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。
+
+**《Java面试突击》:** 由本文档衍生的专为面试而生的《Java面试突击》V2.0 PDF 版本[公众号](#公众号)后台回复 **"Java面试突击"** 即可免费领取!
+
+**Java工程师必备学习资源:** 一些Java工程师常用学习资源公众号后台回复关键字 **“1”** 即可免费无套路获取。
+
+
diff --git a/docs/java/LinkedList.md b/docs/java/collection/LinkedList.md
similarity index 99%
rename from docs/java/LinkedList.md
rename to docs/java/collection/LinkedList.md
index 983c1fae0d0..d26bc752267 100644
--- a/docs/java/LinkedList.md
+++ b/docs/java/collection/LinkedList.md
@@ -458,7 +458,7 @@ public class LinkedListDemo {
linkedList.add(3);
linkedList.removeFirstOccurrence(3); // 从此列表中移除第一次出现的指定元素(从头部到尾部遍历列表)
System.out.println("After removeFirstOccurrence(3):" + linkedList);
- linkedList.removeLastOccurrence(3); // 从此列表中移除最后一次出现的指定元素(从头部到尾部遍历列表)
+ linkedList.removeLastOccurrence(3); // 从此列表中移除最后一次出现的指定元素(从尾部到头部遍历列表)
System.out.println("After removeFirstOccurrence(3):" + linkedList);
/************************** 遍历操作 ************************/
diff --git "a/docs/java/jvm/JDK\347\233\221\346\216\247\345\222\214\346\225\205\351\232\234\345\244\204\347\220\206\345\267\245\345\205\267\346\200\273\347\273\223.md" "b/docs/java/jvm/JDK\347\233\221\346\216\247\345\222\214\346\225\205\351\232\234\345\244\204\347\220\206\345\267\245\345\205\267\346\200\273\347\273\223.md"
new file mode 100644
index 00000000000..ad0a6545287
--- /dev/null
+++ "b/docs/java/jvm/JDK\347\233\221\346\216\247\345\222\214\346\225\205\351\232\234\345\244\204\347\220\206\345\267\245\345\205\267\346\200\273\347\273\223.md"
@@ -0,0 +1,337 @@
+点击关注[公众号](#公众号)及时获取笔主最新更新文章,并可免费领取本文档配套的《Java面试突击》以及Java工程师必备学习资源。
+
+
+
+- [JDK 监控和故障处理工具总结](#jdk-监控和故障处理工具总结)
+ - [JDK 命令行工具](#jdk-命令行工具)
+ - [`jps`:查看所有 Java 进程](#jps查看所有-java-进程)
+ - [`jstat`: 监视虚拟机各种运行状态信息](#jstat-监视虚拟机各种运行状态信息)
+ - [` jinfo`: 实时地查看和调整虚拟机各项参数](#-jinfo-实时地查看和调整虚拟机各项参数)
+ - [`jmap`:生成堆转储快照](#jmap生成堆转储快照)
+ - [**`jhat`**: 分析 heapdump 文件](#jhat-分析-heapdump-文件)
+ - [**`jstack`** :生成虚拟机当前时刻的线程快照](#jstack-生成虚拟机当前时刻的线程快照)
+ - [JDK 可视化分析工具](#jdk-可视化分析工具)
+ - [JConsole:Java 监视与管理控制台](#jconsolejava-监视与管理控制台)
+ - [连接 Jconsole](#连接-jconsole)
+ - [查看 Java 程序概况](#查看-java-程序概况)
+ - [内存监控](#内存监控)
+ - [线程监控](#线程监控)
+ - [Visual VM:多合一故障处理工具](#visual-vm多合一故障处理工具)
+
+
+
+# JDK 监控和故障处理工具总结
+
+## JDK 命令行工具
+
+这些命令在 JDK 安装目录下的 bin 目录下:
+
+- **`jps`** (JVM Process Status): 类似 UNIX 的 `ps` 命令。用户查看所有 Java 进程的启动类、传入参数和 Java 虚拟机参数等信息;
+- **`jstat`**( JVM Statistics Monitoring Tool): 用于收集 HotSpot 虚拟机各方面的运行数据;
+- **`jinfo`** (Configuration Info for Java) : Configuration Info forJava,显示虚拟机配置信息;
+- **`jmap`** (Memory Map for Java) :生成堆转储快照;
+- **`jhat`** (JVM Heap Dump Browser ) : 用于分析 heapdump 文件,它会建立一个 HTTP/HTML 服务器,让用户可以在浏览器上查看分析结果;
+- **`jstack`** (Stack Trace for Java):生成虚拟机当前时刻的线程快照,线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合。
+
+### `jps`:查看所有 Java 进程
+
+`jps`(JVM Process Status) 命令类似 UNIX 的 `ps` 命令。
+
+`jps`:显示虚拟机执行主类名称以及这些进程的本地虚拟机唯一 ID(Local Virtual Machine Identifier,LVMID)。`jps -q` :只输出进程的本地虚拟机唯一 ID。
+
+```powershell
+C:\Users\SnailClimb>jps
+7360 NettyClient2
+17396
+7972 Launcher
+16504 Jps
+17340 NettyServer
+```
+
+`jps -l`:输出主类的全名,如果进程执行的是 Jar 包,输出 Jar 路径。
+
+```powershell
+C:\Users\SnailClimb>jps -l
+7360 firstNettyDemo.NettyClient2
+17396
+7972 org.jetbrains.jps.cmdline.Launcher
+16492 sun.tools.jps.Jps
+17340 firstNettyDemo.NettyServer
+```
+
+`jps -v`:输出虚拟机进程启动时 JVM 参数。
+
+`jps -m`:输出传递给 Java 进程 main() 函数的参数。
+
+### `jstat`: 监视虚拟机各种运行状态信息
+
+jstat(JVM Statistics Monitoring Tool) 使用于监视虚拟机各种运行状态信息的命令行工具。 它可以显示本地或者远程(需要远程主机提供 RMI 支持)虚拟机进程中的类信息、内存、垃圾收集、JIT 编译等运行数据,在没有 GUI,只提供了纯文本控制台环境的服务器上,它将是运行期间定位虚拟机性能问题的首选工具。
+
+**`jstat` 命令使用格式:**
+
+```powershell
+jstat -