From f1d9128962625affecbab2084d7a105af0fd205f Mon Sep 17 00:00:00 2001 From: haiqiang <43314997+Goose9527@users.noreply.github.com> Date: Sun, 10 Mar 2019 08:52:06 +0800 Subject: [PATCH 01/17] =?UTF-8?q?fix=20grow()=E6=96=B9=E6=B3=95=E6=8F=8F?= =?UTF-8?q?=E8=BF=B0=E4=B8=8D=E5=A4=9F=E4=B8=A5=E8=B0=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Java相关/ArrayList-Grow.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Java相关/ArrayList-Grow.md b/Java相关/ArrayList-Grow.md index d763cb83..87c69519 100644 --- a/Java相关/ArrayList-Grow.md +++ b/Java相关/ArrayList-Grow.md @@ -145,7 +145,7 @@ } ``` -**int newCapacity = oldCapacity + (oldCapacity >> 1),所以 ArrayList 每次扩容之后容量都会变为原来的 1.5 倍!** 记清楚了!不是网上很多人说的 1.5 倍+1! +**int newCapacity = oldCapacity + (oldCapacity >> 1),所以在capacity是偶数的情况下,ArrayList 每次扩容之后容量都会变为原来的 1.5 倍,当capacity为奇数时(自己初始化容量时可能出现),第一次扩容容量变为oldCapacity + (oldCapacity - 1) / 2(在计算机运算的情况下减一除和直接除没有差别,对于整数会自动舍掉小数位),也就是说每次扩容相当于计算机中的1.5倍。 !** 记清楚了!不是网上很多人说的 1.5 倍+1!当然了,第一次扩容以后capacity会变为偶数,之后的每次扩容都会是原容量的1.5倍了。 > ">>"(移位运算符):>>1 右移一位相当于除2,右移n位相当于除以 2 的 n 次方。这里 oldCapacity 明显右移了1位所以相当于oldCapacity /2。对于大数据的2进制运算,位移运算符比那些普通运算符的运算要快很多,因为程序仅仅移动一下而已,不去计算,这样提高了效率,节省了资源   From 5053edca3ac5071f5e8f3db84f21d6acca795091 Mon Sep 17 00:00:00 2001 From: haiqiang <43314997+Goose9527@users.noreply.github.com> Date: Sun, 10 Mar 2019 19:50:05 +0800 Subject: [PATCH 02/17] 0.0.1 Snapshot --- Java相关/What's New in JDK8/README.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 Java相关/What's New in JDK8/README.md diff --git a/Java相关/What's New in JDK8/README.md b/Java相关/What's New in JDK8/README.md new file mode 100644 index 00000000..3206a31e --- /dev/null +++ b/Java相关/What's New in JDK8/README.md @@ -0,0 +1,24 @@ +JDK8新特性总结 +====== +总结了部分JDK8新特性,另外一些新特性可以通过Oracle的官方文档查看,毕竟是官方文档,各种新特性都会介绍,有兴趣的可以去看。
+[Oracle官方文档:What's New in JDK8](https://www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html) +----- +- [Java语言特性]() + - [Lambda表达式是一个新的语言特性,已经在JDK8中加入。它是一个可以传递的代码块,你也可以把它们当做方法参数。 + Lambda表达式允许您更紧凑地创建单虚方法接口(称为功能接口)的实例。]() + + - [方法引用为已经存在的具名方法提供易于阅读的Lambda表达式]() + + - [默认方法允许将新功能添加到库的接口,并确保与为这些接口的旧版本编写的代码的二进制兼容性。]() + + - [改进的类型推断。]() + + - [方法参数反射。]() + +- [流(stream)]() + - [新java.util.stream包中的类提供Stream API以支持对元素流的功能样式操作。流(stream)和I/O里的流不是同一个概念 + ,使用stream API可以更方便的操作集合。]() + +- [国际化]() + - 代办 +- 代办 From 2b204632274dc2119ab2d0d0a76e1ff3b83190e0 Mon Sep 17 00:00:00 2001 From: haiqiang <43314997+Goose9527@users.noreply.github.com> Date: Sun, 10 Mar 2019 21:27:08 +0800 Subject: [PATCH 03/17] =?UTF-8?q?Create=20Lambda=E8=A1=A8=E8=BE=BE?= =?UTF-8?q?=E5=BC=8F.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../What's New in JDK8/Lambda表达式.md | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 Java相关/What's New in JDK8/Lambda表达式.md diff --git a/Java相关/What's New in JDK8/Lambda表达式.md b/Java相关/What's New in JDK8/Lambda表达式.md new file mode 100644 index 00000000..0d1b8998 --- /dev/null +++ b/Java相关/What's New in JDK8/Lambda表达式.md @@ -0,0 +1,64 @@ +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表达式来实现,举个常用的例子: +
+``` + Thread thread = new Thread(() -> { + System.out.println("This is JDK8's Lambda!"); + }); +``` +这段代码和函数式接口有啥关系?我们回忆一下,Thread类的构造函数里是不是有一个以Runnable接口为参数的? +``` +public Thread(Runnable target) {...} + +/** + * Runnable Interface + */ +@FunctionalInterface +public interface Runnable { + public abstract void run(); +} +``` +到这里大家可能已经明白了,**Lambda表达式相当于一个匿名类或者说是一个匿名方法**。上面Thread的例子相当于 +``` + Thread thread = new Thread(new Runnable() { + @Override + public void run() { + System.out.println("Anonymous class"); + } + }); +``` +也就是说,上面的lambda表达式相当于实现了这个run()方法,然后当做参数传入(个人感觉可以这么理解,lambda表达式就是一个函数,只不过它的返回值、参数列表都 +由编译器帮我们推断,因此可以减少很多代码量)。 +
至此大家应该明白什么是函数式接口以及函数式接口和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.[方法引用]() From d1c564bb84bc4f6821f8c6ab1f38f1198c07be13 Mon Sep 17 00:00:00 2001 From: haiqiang <43314997+Goose9527@users.noreply.github.com> Date: Sun, 10 Mar 2019 21:27:51 +0800 Subject: [PATCH 04/17] Update README.md --- Java相关/What's New in JDK8/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Java相关/What's New in JDK8/README.md b/Java相关/What's New in JDK8/README.md index 3206a31e..b7612fa4 100644 --- a/Java相关/What's New in JDK8/README.md +++ b/Java相关/What's New in JDK8/README.md @@ -5,7 +5,7 @@ JDK8新特性总结 ----- - [Java语言特性]() - [Lambda表达式是一个新的语言特性,已经在JDK8中加入。它是一个可以传递的代码块,你也可以把它们当做方法参数。 - Lambda表达式允许您更紧凑地创建单虚方法接口(称为功能接口)的实例。]() + Lambda表达式允许您更紧凑地创建单虚方法接口(称为功能接口)的实例。](https://github.com/Goose9527/JavaGuide/blob/master/Java%E7%9B%B8%E5%85%B3/What's%20New%20in%20JDK8/Lambda%E8%A1%A8%E8%BE%BE%E5%BC%8F.md) - [方法引用为已经存在的具名方法提供易于阅读的Lambda表达式]() From cfbc8c45cf4ec466c8bd8e79a547e3018c3ec09a Mon Sep 17 00:00:00 2001 From: haiqiang <43314997+Goose9527@users.noreply.github.com> Date: Sun, 10 Mar 2019 22:44:54 +0800 Subject: [PATCH 05/17] =?UTF-8?q?Create=20JDK8=E6=8E=A5=E5=8F=A3=E8=A7=84?= =?UTF-8?q?=E8=8C=83.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../What's New in JDK8/JDK8接口规范.md | 164 ++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 Java相关/What's New in JDK8/JDK8接口规范.md diff --git a/Java相关/What's New in JDK8/JDK8接口规范.md b/Java相关/What's New in JDK8/JDK8接口规范.md new file mode 100644 index 00000000..0332ecce --- /dev/null +++ b/Java相关/What's New in JDK8/JDK8接口规范.md @@ -0,0 +1,164 @@ +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 +``` From 2fa0457b87a30b203064ffdf9551a01f41872d01 Mon Sep 17 00:00:00 2001 From: haiqiang <43314997+Goose9527@users.noreply.github.com> Date: Sun, 10 Mar 2019 22:45:36 +0800 Subject: [PATCH 06/17] =?UTF-8?q?Update=20Lambda=E8=A1=A8=E8=BE=BE?= =?UTF-8?q?=E5=BC=8F.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Java相关/What's New in JDK8/Lambda表达式.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Java相关/What's New in JDK8/Lambda表达式.md b/Java相关/What's New in JDK8/Lambda表达式.md index 0d1b8998..ab1c75be 100644 --- a/Java相关/What's New in JDK8/Lambda表达式.md +++ b/Java相关/What's New in JDK8/Lambda表达式.md @@ -15,13 +15,13 @@ JDK8--Lambda表达式 **函数式接口指的是有且只有一个抽象(abstract)方法的接口**
当需要一个函数式接口的对象时,就可以用Lambda表达式来实现,举个常用的例子:
-``` +```java Thread thread = new Thread(() -> { System.out.println("This is JDK8's Lambda!"); }); ``` 这段代码和函数式接口有啥关系?我们回忆一下,Thread类的构造函数里是不是有一个以Runnable接口为参数的? -``` +```java public Thread(Runnable target) {...} /** @@ -33,7 +33,7 @@ public interface Runnable { } ``` 到这里大家可能已经明白了,**Lambda表达式相当于一个匿名类或者说是一个匿名方法**。上面Thread的例子相当于 -``` +```java Thread thread = new Thread(new Runnable() { @Override public void run() { From e929c8ba24a8df808febea51ee00f7ce7aa35ac7 Mon Sep 17 00:00:00 2001 From: haiqiang <43314997+Goose9527@users.noreply.github.com> Date: Sun, 10 Mar 2019 22:51:13 +0800 Subject: [PATCH 07/17] =?UTF-8?q?Update=20Lambda=E8=A1=A8=E8=BE=BE?= =?UTF-8?q?=E5=BC=8F.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Java相关/What's New in JDK8/Lambda表达式.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/Java相关/What's New in JDK8/Lambda表达式.md b/Java相关/What's New in JDK8/Lambda表达式.md index ab1c75be..bf27300f 100644 --- a/Java相关/What's New in JDK8/Lambda表达式.md +++ b/Java相关/What's New in JDK8/Lambda表达式.md @@ -60,5 +60,3 @@ public interface Runnable { - ```Predicate {boolean test(T t);}``` 判断,接受一个T对象,返回一个布尔值。 - ```Supplier {T get();} 提供者(工厂)``` 返回一个T对象。 - 其他的跟上面的相似,大家可以看一下function包下的具体接口。 - -## 4.[方法引用]() From 36147197d7d5de1809b872d8a654719e26653df8 Mon Sep 17 00:00:00 2001 From: haiqiang <43314997+Goose9527@users.noreply.github.com> Date: Sun, 10 Mar 2019 22:52:06 +0800 Subject: [PATCH 08/17] =?UTF-8?q?Update=20Lambda=E8=A1=A8=E8=BE=BE?= =?UTF-8?q?=E5=BC=8F.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../What's New in JDK8/Lambda表达式.md | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/Java相关/What's New in JDK8/Lambda表达式.md b/Java相关/What's New in JDK8/Lambda表达式.md index bf27300f..a7ef7f93 100644 --- a/Java相关/What's New in JDK8/Lambda表达式.md +++ b/Java相关/What's New in JDK8/Lambda表达式.md @@ -60,3 +60,29 @@ public interface Runnable { - ```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) -> { + //!int x = 10; + //!x= 50; error + System.out.print(x); + }; + inner.printInt(30); + } +} +``` From 38e568cbe590866e2cf220bad0aafe9c34169df9 Mon Sep 17 00:00:00 2001 From: haiqiang <43314997+Goose9527@users.noreply.github.com> Date: Sun, 10 Mar 2019 22:58:23 +0800 Subject: [PATCH 09/17] =?UTF-8?q?Update=20Lambda=E8=A1=A8=E8=BE=BE?= =?UTF-8?q?=E5=BC=8F.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Java相关/What's New in JDK8/Lambda表达式.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Java相关/What's New in JDK8/Lambda表达式.md b/Java相关/What's New in JDK8/Lambda表达式.md index a7ef7f93..eb97bcc0 100644 --- a/Java相关/What's New in JDK8/Lambda表达式.md +++ b/Java相关/What's New in JDK8/Lambda表达式.md @@ -78,6 +78,7 @@ public class VaraibleHide { }; 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); @@ -85,4 +86,10 @@ public class VaraibleHide { inner.printInt(30); } } +输出 : +30 +20 ``` +lambda表达式和内部类一样,对外部变量捕获时,外部变量必须为final或者是最终变量(effectively final)的,也就是说这个变量初始化后就不能为它赋新值, +同时lambda不像内部类/匿名类,lambda表达式与外围嵌套块有着相同的作用域,因此对变量命名的有关规则对lambda同样适用。 +## 5.[方法引用]() From a771278b56db2dfb96e1d1a766dab00ef870eda1 Mon Sep 17 00:00:00 2001 From: haiqiang <43314997+Goose9527@users.noreply.github.com> Date: Sun, 10 Mar 2019 22:59:10 +0800 Subject: [PATCH 10/17] =?UTF-8?q?Create=20=E6=96=B9=E6=B3=95=E5=BC=95?= =?UTF-8?q?=E7=94=A8.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Java相关/What's New in JDK8/方法引用.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Java相关/What's New in JDK8/方法引用.md diff --git a/Java相关/What's New in JDK8/方法引用.md b/Java相关/What's New in JDK8/方法引用.md new file mode 100644 index 00000000..e8f2fcb9 --- /dev/null +++ b/Java相关/What's New in JDK8/方法引用.md @@ -0,0 +1,2 @@ +JDK8-Lambda-方法引用 +=== From 311b0bdc85e6a39e014c5457efc4a0d34bba0263 Mon Sep 17 00:00:00 2001 From: haiqiang <43314997+Goose9527@users.noreply.github.com> Date: Mon, 11 Mar 2019 09:53:20 +0800 Subject: [PATCH 11/17] =?UTF-8?q?Update=20JDK8=E6=8E=A5=E5=8F=A3=E8=A7=84?= =?UTF-8?q?=E8=8C=83.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Java相关/What's New in JDK8/JDK8接口规范.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Java相关/What's New in JDK8/JDK8接口规范.md b/Java相关/What's New in JDK8/JDK8接口规范.md index 0332ecce..ee1dd8c2 100644 --- a/Java相关/What's New in JDK8/JDK8接口规范.md +++ b/Java相关/What's New in JDK8/JDK8接口规范.md @@ -1,7 +1,6 @@ JDK8接口规范 === -在JDK8中引入了lambda表达式,因此出现了函数式接口的概念,为了保持向前兼容性(比如泛型也是为了保持兼容性而失去了在一些别的语言泛型拥有的功能), -Java接口规范发生了一些改变。 +在JDK8中引入了lambda表达式,出现了函数式接口的概念,为了在扩展接口时保持向前兼容性(比如泛型也是为了保持兼容性而失去了在一些别的语言泛型拥有的功能),Java接口规范发生了一些改变。。 --- ## 1.JDK8以前的接口规范 - JDK8以前接口可以定义的变量和方法 From 2d5923274436535adc33b9d02822b5b3de15eb5a Mon Sep 17 00:00:00 2001 From: haiqiang <43314997+Goose9527@users.noreply.github.com> Date: Mon, 11 Mar 2019 10:18:17 +0800 Subject: [PATCH 12/17] Update ArrayList-Grow.md --- Java相关/ArrayList-Grow.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Java相关/ArrayList-Grow.md b/Java相关/ArrayList-Grow.md index 87c69519..d763cb83 100644 --- a/Java相关/ArrayList-Grow.md +++ b/Java相关/ArrayList-Grow.md @@ -145,7 +145,7 @@ } ``` -**int newCapacity = oldCapacity + (oldCapacity >> 1),所以在capacity是偶数的情况下,ArrayList 每次扩容之后容量都会变为原来的 1.5 倍,当capacity为奇数时(自己初始化容量时可能出现),第一次扩容容量变为oldCapacity + (oldCapacity - 1) / 2(在计算机运算的情况下减一除和直接除没有差别,对于整数会自动舍掉小数位),也就是说每次扩容相当于计算机中的1.5倍。 !** 记清楚了!不是网上很多人说的 1.5 倍+1!当然了,第一次扩容以后capacity会变为偶数,之后的每次扩容都会是原容量的1.5倍了。 +**int newCapacity = oldCapacity + (oldCapacity >> 1),所以 ArrayList 每次扩容之后容量都会变为原来的 1.5 倍!** 记清楚了!不是网上很多人说的 1.5 倍+1! > ">>"(移位运算符):>>1 右移一位相当于除2,右移n位相当于除以 2 的 n 次方。这里 oldCapacity 明显右移了1位所以相当于oldCapacity /2。对于大数据的2进制运算,位移运算符比那些普通运算符的运算要快很多,因为程序仅仅移动一下而已,不去计算,这样提高了效率,节省了资源   From c336e45ead8b26b029891b2309994e4a11ceabf7 Mon Sep 17 00:00:00 2001 From: HaiQiang <540911185@qq.com> Date: Mon, 11 Mar 2019 10:28:27 +0800 Subject: [PATCH 13/17] Update --- {Java相关 => Java}/What's New in JDK8/JDK8接口规范.md | 0 {Java相关 => Java}/What's New in JDK8/Lambda表达式.md | 0 {Java相关 => Java}/What's New in JDK8/README.md | 0 {Java相关 => Java}/What's New in JDK8/方法引用.md | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename {Java相关 => Java}/What's New in JDK8/JDK8接口规范.md (100%) rename {Java相关 => Java}/What's New in JDK8/Lambda表达式.md (100%) rename {Java相关 => Java}/What's New in JDK8/README.md (100%) rename {Java相关 => Java}/What's New in JDK8/方法引用.md (100%) diff --git a/Java相关/What's New in JDK8/JDK8接口规范.md b/Java/What's New in JDK8/JDK8接口规范.md similarity index 100% rename from Java相关/What's New in JDK8/JDK8接口规范.md rename to Java/What's New in JDK8/JDK8接口规范.md diff --git a/Java相关/What's New in JDK8/Lambda表达式.md b/Java/What's New in JDK8/Lambda表达式.md similarity index 100% rename from Java相关/What's New in JDK8/Lambda表达式.md rename to Java/What's New in JDK8/Lambda表达式.md diff --git a/Java相关/What's New in JDK8/README.md b/Java/What's New in JDK8/README.md similarity index 100% rename from Java相关/What's New in JDK8/README.md rename to Java/What's New in JDK8/README.md diff --git a/Java相关/What's New in JDK8/方法引用.md b/Java/What's New in JDK8/方法引用.md similarity index 100% rename from Java相关/What's New in JDK8/方法引用.md rename to Java/What's New in JDK8/方法引用.md From 70257619d65aca5262008f42e09a964c53efe977 Mon Sep 17 00:00:00 2001 From: haiqiang <43314997+Goose9527@users.noreply.github.com> Date: Mon, 11 Mar 2019 10:51:18 +0800 Subject: [PATCH 14/17] =?UTF-8?q?Update=20Lambda=E8=A1=A8=E8=BE=BE?= =?UTF-8?q?=E5=BC=8F.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Java/What's New in JDK8/Lambda表达式.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Java/What's New in JDK8/Lambda表达式.md b/Java/What's New in JDK8/Lambda表达式.md index eb97bcc0..8351f9f8 100644 --- a/Java/What's New in JDK8/Lambda表达式.md +++ b/Java/What's New in JDK8/Lambda表达式.md @@ -8,7 +8,7 @@ JDK8--Lambda表达式
参数列表可以为空```()->{}```
可以加类型声明比如```(String para1, int para2) -> {return para1 + para2;}```我们可以看到,lambda同样可以有返回值.
在编译器可以推断出类型的时候,可以将类型声明省略,比如```(para1, para2) -> {return para1 + para2;}``` -
(lambda有点像动态类型语言,并且lambda在字节码层面是用invokedynamic实现的,而这条指令就是为了让JVM更好的支持运行在其上的动态类型语言) +
(lambda有点像动态类型语言语法。lambda在字节码层面是用invokedynamic实现的,而这条指令就是为了让JVM更好的支持运行在其上的动态类型语言) ## 3.函数式接口 在了解Lambda表达式之前,有必要先了解什么是函数式接口```(@FunctionalInterface)```
@@ -43,6 +43,11 @@ public interface Runnable { ``` 也就是说,上面的lambda表达式相当于实现了这个run()方法,然后当做参数传入(个人感觉可以这么理解,lambda表达式就是一个函数,只不过它的返回值、参数列表都 由编译器帮我们推断,因此可以减少很多代码量)。 +
Lambda也可以这样用 : +```java + Runnable runnable = () -> {...}; +``` +其实这和上面的用法没有什么本质上的区别。
至此大家应该明白什么是函数式接口以及函数式接口和lambda表达式之间的关系了。在JDK8中修改了接口的规范, 目的是为了在给接口添加新的功能时保持向前兼容(个人理解),比如一个已经定义了的函数式接口,某天我们想给它添加新功能,那么就不能保持向前兼容了, 因为在旧的接口规范下,添加新功能必定会破坏这个函数式接口[(JDK8中接口规范)]() @@ -77,6 +82,7 @@ public class VaraibleHide { } }; inner.printInt(30); + inner = (s) -> { //Variable used in lambda expression should be final or effectively final //!int x = 10; @@ -90,6 +96,8 @@ public class VaraibleHide { 30 20 ``` -lambda表达式和内部类一样,对外部变量捕获时,外部变量必须为final或者是最终变量(effectively final)的,也就是说这个变量初始化后就不能为它赋新值, -同时lambda不像内部类/匿名类,lambda表达式与外围嵌套块有着相同的作用域,因此对变量命名的有关规则对lambda同样适用。 -## 5.[方法引用]() +对于lambda表达式```java inner = (s) -> {System.out.print(x);};```,变量x并不是在lambda表达式中定义的,像这样并不是在lambda中定义或者通过lambda的参数列表()获取的变量成为自由变量,它是被lambda表达式捕获的。 +
lambda表达式和内部类一样,对外部自由变量捕获时,外部自由变量必须为final或者是最终变量(effectively final)的,也就是说这个变量初始化后就不能为它赋新值, +同时lambda不像内部类/匿名类,lambda表达式与外围嵌套块有着相同的作用域,因此对变量命名的有关规则对lambda同样适用。大家阅读上面的代码对这些概念应该 +不难理解。 +## 5.[方法引用](../方法引用.md) From 72050e60a14e28cdf0281020380a750b7e5bc450 Mon Sep 17 00:00:00 2001 From: haiqiang <43314997+Goose9527@users.noreply.github.com> Date: Mon, 11 Mar 2019 10:54:49 +0800 Subject: [PATCH 15/17] =?UTF-8?q?Rename=20JDK8=E6=8E=A5=E5=8F=A3=E8=A7=84?= =?UTF-8?q?=E8=8C=83.md=20to=20JDK8=E6=8E=A5=E5=8F=A3=E8=A7=84=E8=8C=83-?= =?UTF-8?q?=E9=9D=99=E6=80=81=E3=80=81=E9=BB=98=E8=AE=A4=E6=96=B9=E6=B3=95?= =?UTF-8?q?.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...8接口规范.md => JDK8接口规范-静态、默认方法.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Java/What's New in JDK8/{JDK8接口规范.md => JDK8接口规范-静态、默认方法.md} (100%) diff --git a/Java/What's New in JDK8/JDK8接口规范.md b/Java/What's New in JDK8/JDK8接口规范-静态、默认方法.md similarity index 100% rename from Java/What's New in JDK8/JDK8接口规范.md rename to Java/What's New in JDK8/JDK8接口规范-静态、默认方法.md From 98a085c9f0e1b458cb5aaf40d5aa0788ad9c4d0d Mon Sep 17 00:00:00 2001 From: haiqiang <43314997+Goose9527@users.noreply.github.com> Date: Mon, 11 Mar 2019 14:49:31 +0800 Subject: [PATCH 16/17] =?UTF-8?q?JDK8=20Lambda=E8=A1=A8=E8=BE=BE=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 0.0.1 SNAPSHOT --- Java/What's New in JDK8/Lambda表达式.md | 134 ++++++++++++++++++++- 1 file changed, 133 insertions(+), 1 deletion(-) diff --git a/Java/What's New in JDK8/Lambda表达式.md b/Java/What's New in JDK8/Lambda表达式.md index 8351f9f8..359c4714 100644 --- a/Java/What's New in JDK8/Lambda表达式.md +++ b/Java/What's New in JDK8/Lambda表达式.md @@ -100,4 +100,136 @@ public class VaraibleHide {
lambda表达式和内部类一样,对外部自由变量捕获时,外部自由变量必须为final或者是最终变量(effectively final)的,也就是说这个变量初始化后就不能为它赋新值, 同时lambda不像内部类/匿名类,lambda表达式与外围嵌套块有着相同的作用域,因此对变量命名的有关规则对lambda同样适用。大家阅读上面的代码对这些概念应该 不难理解。 -## 5.[方法引用](../方法引用.md) +## 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 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 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类型推断,同样对于这个特性需要斟酌使用。 From bcb8ba35822827c04cf10b1bbcb254a58fa43bd9 Mon Sep 17 00:00:00 2001 From: HaiQiang <540911185@qq.com> Date: Mon, 11 Mar 2019 14:55:53 +0800 Subject: [PATCH 17/17] =?UTF-8?q?Delete=20=E6=96=B9=E6=B3=95=E5=BC=95?= =?UTF-8?q?=E7=94=A8.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Java/What's New in JDK8/方法引用.md | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 Java/What's New in JDK8/方法引用.md diff --git a/Java/What's New in JDK8/方法引用.md b/Java/What's New in JDK8/方法引用.md deleted file mode 100644 index e8f2fcb9..00000000 --- a/Java/What's New in JDK8/方法引用.md +++ /dev/null @@ -1,2 +0,0 @@ -JDK8-Lambda-方法引用 -===