jdk1.8新特性
2.1 接口的默认和静态方法:
Java 8允许我们给接口添加一个非抽象的方法实现,只需要使用 default关键字即可,这个特征又叫做扩展方法。static的用法是直接用接口名去调方法即可。
当一个类继承父类又实现接口时,若后两者方法名相同,则优先继承父类中的同名方法,即“类优先”,如果实现两个同名方法的接口,则要求实现类必须手动声明默认实现哪个接口中的方法。默认方法(带default修饰的方法)的好处是可以为接口添加新的默认方法,而不会破坏接口的实现。
这样任何一个实现了带有默认方法接口的类都会自动继承默认方法实现,无需再写直接留白
public interface JDK8Interface {
// static修饰符定义静态方法
static void staticMethod() {
System.out.println("接口中的静态方法");
}
// default饰符定义默认方法
default void defaultMethod() {
System.out.println("接口中的默认方法");
}
}
2.2 Lambda 表达式:(例如: (x, y) -> { return x + y; } ;λ表达式有三部分组成:参数列表,箭头(->),以及一个表达式或语句块。)
最常用的就是在创建线程的时候
public class Demo {
public static void main(String[] args) {
new Thread(()->{
System.out.println("lambda");
}).start();
}
}
在Java 8 中你就没必要使用这种传统的匿名对象的方式了,Java 8提供了更简洁的语法,为引入Lambda表达式,Java8新增了java.util.funcion包,里面包含常用的函数接口,这是Lambda表达式的基础,Java集合框架也新增部分接口,以便与Lambda表达式对接。lambda表达式本质上是一段匿名内部类,也可以是一段可以传递的代码
Collections.sort(names, (String a, String b) -> {
return b.compareTo(a);
});
// 使用forEach()结合Lambda表达式迭代
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
list.forEach( str -> {
if(str.length()>3)
System.out.println(str);
});
2.3 方法与构造函数引用:
Java 8 允许你使用 :: 关键字来传递方法或者构造函数引用,上面的代码展示了如何引用一个静态方法,我们也可以引用一个对象的方法:
converter = something::startsWith;
String converted = converter.convert("Java");
System.out.println(converted);
2.4 函数式接口:
所谓的函数式接口,当然首先是一个接口,然后就是在这个接口里面只能有一个抽象方法。
你可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda 表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方法上进行声明)。
Thread t2 = new Thread(() -> System.out.println("t2"));
t2.start();
内置的函数式接口
//Consumer<T> - T作为输入,执行某种动作但没有返回值
Consumer<String> con = (x) -> {
System.out.println(x);
};
con.accept("hello world");
//Supplier<T> - 没有任何输入,返回T
Supplier<String> supp = () -> {
return "Supplier";
};
System.out.println(supp.get());
//Predicate<T> -T作为输入,返回的boolean值作为输出
Predicate<String> pre = (x) -> {
System.out.print(x);
return x.startsWith("op");
};
System.out.println(": " + pre.test("op, hello World"));
// Function<T, R> -T作为输入,返回的R作为输出
Function<String, String> function = (x) -> {
System.out.print(x + ": ");
return "Function";
};
System.out.println(function.apply("hello world"));
//BinaryOperator<T> -两个T作为输入,返回一个T作为输出,对于“reduce”操作很有用
BinaryOperator<String> bina = (x, y) -> {
System.out.print(x + " " + y);
return "BinaryOperator";
};
System.out.println(" " + bina.apply("hello ", "world"));
2.5 Annotation 注解:支持多重注解:
很多时候一个注解需要在某一位置多次使用。
@YourAnnotation
@YourAnnotation
public void test(){
//TODO
}
java 8之前也有重复使用注解的解决方案,但可读性不好。
public @interface MyAnnotation {
String role();
}
public @interface Annotations {
MyAnnotation[] value();
}
public class RepeatAnnotationUseOldVersion {
@Annotations({@MyAnnotation(role="Admin"),@MyAnnotation(role="Manager")})
public void doSomeThing(){
}
}
Java8的实现方式(由另一个注解来存储重复注解,在使用时候,用存储注解Authorities来扩展重复注解),可读性更强。
@Repeatable(Annotations.class)
public @interface MyAnnotation {
String role();
}
public @interface Annotations {
MyAnnotation[] value();
}
public class RepeatAnnotationUseOldVersion {
@MyAnnotation(role="Admin")
@MyAnnotation(role="Manager")
public void doSomeThing(){
}
}
2.6 新的日期时间 API:
Java 8新的Date-Time API (JSR 310)受Joda-Time的影响,提供了新的java.time包,可以用来替代
java.util.Date和java.util.Calendar。一般会用到Clock、LocaleDate、LocalTime、LocaleDateTime、ZonedDateTime、Duration这些类,对于时间日期的改进还是非常不错的。
2.7 Base64编码:
Base64工具类提供了三种BASE64编解码器:
基本:输出被映射到一组字符A-Za-z0-9+/,编码不添加任何行标,输出的解码仅支持A-Za-z0-9+/
URL:输出映射到一组字符A-Za-z0-9+_,输出是URL和文件
MIME:输出隐射到MIME友好格式。输出每行不超过76字符,并且使用’\r’并跟随’\n’作为分割。编码输出最后没有行分割
// 原始字符串
String initChars = "abc!@64编解码#-Adv://";
// 基础编码
String b64En = Base64.getEncoder().encodeToString(initChars.getBytes("utf-8"));
// 基础解码
byte[] b64De = Base64.getDecoder().decode(b64En);
String deChars = new String(b64De, "utf-8");
// Url编码
String b64UrlEn = Base64.getUrlEncoder().encodeToString(initChars.getBytes("utf-8"));
// Url解码
byte[] b64UrlDe = Base64.getUrlDecoder().decode(b64UrlEn);
String urlDeChars = new String(b64UrlDe, "utf-8");
// Mime编码
String b64MimeEn = Base64.getMimeEncoder().encodeToString(initChars.getBytes("utf-8"));
// Mime解码
byte[] b64MimeDe = Base64.getMimeDecoder().decode(b64MimeEn);
String mimeDeChars = new String(b64MimeDe, "utf-8");
2.8 JavaScript引擎Nashorn:
Nashorn允许在JVM上开发运行JavaScript应用,允许Java与JavaScript相互调用。
2.9 Stream的使用:
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
Stream 就好像一个高级的迭代器,但只能遍历一次,就好像一江春水向东流;在流的过程中,对流中的元素执行一些操作,比如“过滤掉长度大于 10 的字符串”、“获取每个字符串的首字母”等。
要想操作流,首先需要有一个数据源,可以是数组或者集合。每次操作都会返回一个新的流对象,方便进行链式操作,但原有的流对象会保持不变。
流的操作可以分为两种类型:
1)中间操作,可以有多个,每次返回一个新的流,可进行链式操作。
2)终端操作,只能有一个,每次执行完,这个流也就用光光了,无法执行下一个操作,因此只能放在最后。
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("3");
long count = list.stream().distinct().count();
System.out.println(count);
distinct() 方法是一个中间操作(去重),它会返回一个新的流(没有共同元素)。
Stream<T> distinct();
count() 方法是一个终端操作,返回流中的元素个数。
long count();
中间操作不会立即执行,只有等到终端操作的时候,流才开始真正地遍历,用于映射、过滤等。通俗点说,就是一次遍历执行多个操作,性能就大大提高了。
操作流
1)过滤
2)映射
3)匹配
4)组合
2.10 Optional:
以前一直不懂Optional有啥用,感觉太无语了,Java8还把它当做一个噱头来宣传,最近终于发现它的用处了,当然不用函数式编程的话,是没感觉的;
个人觉得Optional实现的功能,有很多替代方案,if-else、三目等都可以;但Optional是用于函数式的一个整体中的一环,让函数式更流畅
不使用Optional的版本,提变量,判空,必不可少,很繁琐
2.11 扩展注解的支持:
Java 8扩展了注解的上下文,几乎可以为任何东西添加注解,包括局部变量、泛型类、父类与接口的实现,连方法的异常也能添加注解。
2.12 并行(parallel)数组:
支持对数组进行并行处理,主要是parallelSort()方法,它可以在多核机器上极大提高数组排序的速度。
2.13 编译器优化:
Java 8将方法的参数名加入了字节码中,这样在运行时通过反射就能获取到参数名,只需要在编译时使用-parameters参数。
CurrentHashMap做了升级
在jdk1.8中对hashMap等map集合的数据结构优化。
原来的hashMap采用的数据结构是哈希表(数组+链表),hashMap默认大小是16,一个0-15索引的数组,如何往里面存储元素,首先调用元素的hashcode方法,计算出哈希码值,经过哈希算法算成数组的索引值,如果对应的索引处没有元素,直接存放,如果有对象在,那么比较它们的equals方法比较内容
如果内容一样,后一个value会将前一个value的值覆盖,如果不一样,在1.7的时候,后加的放在前面,形成一个链表,形成了碰撞,在某些情况下如果链表 无限下去,那么效率极低,碰撞是避免不了的
加载因子:0.75,数组扩容,达到总容量的75%,就进行扩容,但是无法避免碰撞的情况发生
在1.8之后,在数组+链表+红黑树来实现hashmap,当碰撞的元素个数大于8时 & 总容量大于64,会有红黑树的引入
除了添加之后,效率都比链表高,1.8之后链表新进元素加到末尾
ConcurrentHashMap (锁分段机制),concurrentLevel,jdk1.8采用CAS算法(无锁算法,不再使用锁分段),数组+链表中也引入了红黑树的使用