本文共 23264 字,大约阅读时间需要 77 分钟。
java9快来了,必须得梳理一下java8了。
官方文档:
接口里也可以写方法体了,实现该接口的类不再强制实现该方法,只需要在方法签名增加default签名并实现方法体,如:
接口:
public interface Compute { default Integer add(Integer x,Integer y) { return x+y; } Integer minus(Integer x,Integer y);}
实现:
public class ComputeImpl implements Compute { @Override public Integer minus(Integer x, Integer y) { return x-y; } public static void main(String[] args) { ComputeImpl c=new ComputeImpl(); System.out.println(c.add(2, 1)); System.out.println(c.minus(22, 1)); }}
接口可以定义静态方法,通过接口调用。实现类不需实现,也无法在实现类中直接调用。
public interface Compute { default Integer add(Integer x,Integer y) { return x+y; } Integer minus(Integer x,Integer y); static void test() { System.out.println("static method test()"); }}
调用时:
Compute.test();
注意:缺省方法或静态方法引入,让java拥有了C++类似的多重继承的能力,所以同样会存在继承导致的歧义。
java7及以前,在内部类中访问外部变量,需要外部变量定义为final。java8中final关键字不是必须的了,但是需要确保不会修改该变量,否则仍旧会编译错误。
对于外部来的静态成员和字段,可以任意访问。
lambda中同内部类相同。
lambda是一个语法糖,编写代码时可以提高编码效率,实际编译后的结果是一样的。形如:
(Class1 p1,Class2 p2...pn) -> { statement1; statement2; //... statementn;}
它相当于jdk7及以前的内部类:
new Interface1(){ @Override public Class1 abstractMethod(Class1 p1,Class2 p2...pn){ statement1; statement2; //... statementn; }}
(Class1 p1,Class2 p2…pn) 为lambda头,或称为参数列表,即接口中方法的参数列表。
-> 为 lambda运算符。
{statementn…;} 为 lambda体,即接口中方法实现的代码块。
由于jdk在编译时会有代码检查,java8 针对lambda增强了代码检查时的推测能力,他可以根据上下文对变量类型、代码块关键字进行一些推断,因此提供了lambda简化的能力。
参数列表中的变量类型可以省略,jdk会根据接口中的参数进行匹配。
如果参数只有一个,参数列表的括号可以省略。
如果方法体中的语句只有一行,那么大括号和分号可以省略。需要注意,如果方法体有返回值,return关键字也一起省略。
除了精简了代码之外,lambda中如果引用this,那么this指针是包装类,即lambda被调用的类;而内部类中,则是指内部类。
要用lambda形式使用接口,需要接口为一种特殊的接口类型,即函数式接口。所谓函数接口,是只有一个抽象方法的接口。
所谓函数式接口中的抽象方法,并不是抽象类中的抽象方法,它不需要abstract关键修饰,是指jdk7及以前的没有方法体的方法。
函数式接口中除了唯一的未实现方法之外,可以有其他有函数体的default方法。
只要符合以前上条件,就是一个合法的函数式接口。java8中提供了一个注解@FunctionalInterface
,将它标注在接口定义上面,可以在编译阶段校验,如果接口中定义了第二个抽象方法会编译失败。该注解不是必需的。
@FunctionalInterfacepublic interface Compute { default Integer add(Integer x,Integer y) { return x+y; } Integer minus(Integer x,Integer y);}
接口定义:
public interface ICompute { public void print();}
定义使用该接口的方法:
private void hello(ICompute c){ c.print();}
测试用例1——传统内部类方式
@Test public void testAnonymousInnerClass(){ int i=12; hello(new ICompute() { @Override public void print() { System.out.println(i);//jdk7会报错,要求i为final;java8则不报错,假定你不会修改,如果修改就会报错 System.out.println("Hello anonymous inner class."); } }); }
测试用例2——lambda
hello(()-> System.out.println("Hello lambda"));
接口定义:
public interface FunctionalInterfaceVoidArg { public void sayHi(String str);}
定义使用该接口的方法:
private void sayHi(String str,FunctionalInterfaceVoidArg object) { object.sayHi(str);}
测试用例:
@Testpublic void testLambdaVoidHasArg(){ sayHi("peter",x-> System.out.println("Hello "+x));}
接口定义:
public interface FunctionalInterfaceReturn { public String generateTime();}
定义使用该接口的方法:
private String getTime(FunctionalInterfaceReturn fi){ return fi.generateTime();}
测试用例:
@Testpublic void testLambdaReturn(){ String str=getTime(()->{ return String.valueOf(System.currentTimeMillis()); }); System.out.println(str); //省略大括号和分号的简化lambda str=getTime(()->new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); System.out.println(str);}
① Predicate
接收一个参数,返回一个boolen值,用于条件判断。
//使用lambdaPredicatepre=(x)-> x.startsWith("t_");boolean isTable=pre.startsWith("t_users");//使用方法引用Predicate notNull=Objects::nonNull;Predicate isNull=Objects::isNull;Predicate isEmpty=String::isEmpty;Predicate notEmpty=isEmpty.negate();
② Comparator
这是一个早已存在的接口,在java8中实现了一些默认方法。
package java.util.function;@FunctionalInterfacepublic interface Supplier{ T get();}
适用于无入参,有返回值的场景。如生产者。
典型的应用是List.forEach(Supplier s);
Arrays.asList("aa","bb","cc").forEach((x)->System.out.println(x));
package java.util.function;@FunctionalInterfacepublic interface Consumer{ void accept(T t);}
适用于有一个入参,无返回值的场景。如消费者。
package java.util.function;@FunctionalInterfacepublic interface Function{ R apply(T t);}
适用于一个类型的入参,返回一个类型的场景。
package java.util.function;@FunctionalInterfacepublic interface UnaryOperatorextends Function { static UnaryOperator identity() { return t -> t; }}
事实上,他就是Function<T,R>
的一种特殊情况,即Function<T,T>
。
package java.util.function;@FunctionalInterfacepublic interface Predicate{ boolean test(T t);}
它类似于Function<T, Boolean>
,但并未继承Function,用于条件判断,类似于guava中的Predicate。
java8在包java.util.function
中提供了很多类型,如果不能满足可以查看javadocs,查找合适的类型,或自定义。
在需要函数参数的方法中,我们可以把另一个同类型的方法直接传入,这称为方法引用的绑定。类似于C语言中的函数指针。
lambda表达式可以替代方法引用;或者说方法引用是lambda的一种特例,方法引用不可以控制传递参数。
private Person construntorRef(Suppliersup){ Person p=sup.get(); return p;}@Testpublic void testConstructorRef(){ Person p=construntorRef(Person::new); System.out.println(p);}
需要有无参的构造器。
private static void print(String s){ System.out.println(s); } @Test public void testStaticRef(){ Arrays.asList("aa","bb","cc").forEach(TestMethodReference::print); }
so easy,只要静态方法的参数列表和FI需要的参数一致就可以。
@Test public void testMemberMethodRef(){ Arrays.asList("aa","bb","cc").forEach(System.out::println); }
so easy,只要成员方法的参数列表和FI需要的参数一致就可以。
@Testpublic void testClassMemberMethodRef(){ String[] strs={ "zzaa","xxbb","yycc"}; Arrays.sort(strs,String::compareToIgnoreCase);//OK System.out.println(Arrays.asList(strs)); File[] files = new File("C:").listFiles(File::isHidden); // OK}
��,前方高能,请关掉耳机的音乐,认真思考,小心行事。
传统的java开发中,是不允许使用类名去调用成员方法的,这是一个基本原则,那么这里的这种写法就有点不太容易理解了。还是用实例说明:
用到的内部类:
import lombok.Data;@Datapublic static class Person{ private String name; private Integer age; public int mycompare(Person p1){ return p1.getAge()-this.getAge(); } public void print(){ System.out.println(this); } public void println(Person p){ System.out.println(p); } public int compareByAge(Person a,Person b){ return a.getAge().compareTo(b.getAge()); }}public static class APerson{ public void print(){ System.out.println(this); } public void println(Person p){ System.out.println(p); }}
测试代码:
@Test public void testClassMemberMethodRef2() { // R apply(T t);//要求一个参数 Functionupperfier1 = String::toUpperCase; UnaryOperator upperfier2 = (x) -> x.toUpperCase();//这里没有参数,即0个 /* * 小结:如果方法引用表达式 "String::toUpperCase" 可以用lambda表达式中参数的指定成员方法(这个成员方法的参数比FI要求的参数少一个改类型的参数)改写, * 那么就可以使用 "类的实例方法"来表示方法引用。 * * 或者说:如果lambda表达式的lambda体中使用的方法是参数匹配的方法,那么方法引用表达式就用"类引用对象的实例方法"。 * * lambda的参数是方法的主体。 */ class Print { public void println(String s) { System.out.println(s); } } // void accept(T t); Consumer sysout1 = new Print()::println; Consumer sysout2 = (x) -> new Print().println(x); /* * 小结:如果方法引用表达式 "new Print()::println" 可以用lambda表达式中参数的具体对象的参数匹配的成员方法改写, * 那么就用 "对象的实例方法"来表示方法引用。 * * 或者说:如果lambda表达式的lambda体中使用的方法来操作lambda的参数,那么方法引用表达式就用"对象的实例方法"。 * * lambda的参数是方法的参数。 */ //有一个更让人易混淆的例子,可以用上面的规则来验证,Arrays.sort(T t,Comparator c) class Person { public int com1(Person p) { return 1; } public int com2(Person p1, Person p2) { return 1; } } // int compare(T o1, T o2);//需要两个参数 Person【】 ps = { new Person(), new Person() }; Arrays.sort(ps, Person::com1); Arrays.sort(ps, (x,y)->x.com1(y)); Arrays.sort(ps, new Person()::com2); Arrays.sort(ps, (x,y)->new Person().com2(x, y)); //按照以上规则验证应该能说明清楚。 /* * 但是一个接口为什么有两种写法?缺省的lambda会匹配FI方法,即"int compare(T o1, T o2);" * 从上面的lambda表达式来分析,默认的使用lambda应该是: */ Comparator comparator1 = new Person()::com2; /* * 下面的方式又是怎么回事呢? */ Comparator comparator2 = Person::com1; System.out.println(comparator2); /* * 任一个两个参数的FI接口方法(int compare(T o1, T o2)),都可以用引用减少一个参数的方法(int o1 .compare(T o2))来代替,而引用对象本身作为另一个隐含参数,那么方法引用的对象用类名,表示类的任意对象。 还是有点乱?我们来换一个角度来看一下: 首先,我们需要的是int compare(T o1, T o2)是两个参数; 其次,先不考虑::前缀是类还是对象,你给了我一个compare(T o2),少一个参数?怎么办? lambda机制为了解决这个问题,它使用::前面的类名new一个对象,当做需要的缺少的那个参数,这就是类的实例方法。 */ }
小结一下:
首先明确此处需要的方法参数列表,此处标记参数个数为N,那么:1. 如果传入的方法是一个类型的静态方法,而且参数匹配,使用“类的静态方法引用”;这应该不难理解。2. 如果传入的方法是一个实例的成员方法,而且参数匹配,使用“实例的成员方法”;这也应该不难理解。3. 如果传入的方法是一个类型T的实例的成员方法,而且参数为N-1个,缺少了一个T类型的参数,那么就使用“T类型的实例方法”。
前面的例子,FI的两个参数是同一个类型,如果类型不同呢?省略了哪个参数呢?
是按照位置省略了第一个,亦或者是省略了最后一个?
还是按照类型自动去对应,而不关心第几个呢?
这个时候,我们能想到的办法可能是去看源码,但是一般看代码没有个把礼拜甚至更长,毛都看不出来。我们还是用一个例子来分析一下吧。
定义一个FI接口:
package com.pollyduan.fi;public interface TestInterface { //随便什么名字,lambda并不关心,因为FI只有一个接口,并且根据参数来匹配 public void anyStringAsName(TestBean1 bean1,TestBean2 bean2);}
编写两个用于参数的类:
TestBean1.java
package com.pollyduan.fi;public class TestBean1 { public void expect1(TestBean1 bean1){ } public void expect2(TestBean2 bean2){ } public void test1(TestInterface i){ }}
TestBean2.java
package com.pollyduan.fi;public class TestBean2 { public void expect1(TestBean1 bean1){ } public void expect2(TestBean2 bean2){ } public void test1(TestInterface i){ }}
二者区别不大。
编写测试类:
package com.pollyduan.fi;public class TestFIMain { public static void main(String[] args) { TestBean1 bean1=new TestBean1(); bean1.test1(TestBean1::expect1);//① bean1.test1(TestBean1::expect2);//② ok bean1.test1(TestBean2::expect1);//③ bean1.test1(TestBean2::expect2);//④ TestBean2 bean2=new TestBean2(); bean2.test1(TestBean1::expect1);//⑤ bean2.test1(TestBean1::expect2);//⑥ ok bean2.test1(TestBean2::expect1);//⑦ bean2.test1(TestBean2::expect2);//⑧ }}
测试方法中,除了标记OK的行正确,其他都报错。
分析:
首先我们要明确FI需要的参数列表是:(TestBean1,TestBean2)
我们先看①行,我们传入的”::”前导的类是TestBean1,而expect1方法匹配的是TestBean1类型的入参bean1,也就是说省略了TestBean2类型的参数bean2,FI中的最后一个参数。即便我们使用类TestBean1去new一个对象,也找不到TestBean2,因此这个错误。
我们先看②行,我们传入的”::”前导的类是TestBean1,而expect2方法匹配的是TestBean2类型的入参bean2,也就是说省略了TestBean1类型的参数bean1,那么lambda就可以使用”::”前导的TestBean1构建一个对象,作为第一个参数,从而匹配FI的接口方法。ok。
我们先看③行,我们传入的”::”前导的类是TestBean2,而expect1方法匹配的是TestBean1类型的入参bean1,也就是说省略了TestBean2类型的参数bean2,FI的最后一个参数。按照第二步的分析,我们用”::”前导的类TestBean2去new一个对象,应该可以凑足两个参数。实际测试会发现这不灵。这就证明了只能省略第一个参数,而且,用”::”前导的类也必须是第一个参数的类型。
同第一步类似,第④行代码,找不到TestBean1的参数,有错误可以理解。
至于⑤~⑧,只是替换了外层的test1的主体,没有任何区别。这证明了,lambda的匹配与外层是什么鬼没有任何关系,它只关心外层需要的FI的参数列表。
请不要看下一步,在这里停下来冷静的思考一下,如果我们把TestInterface中FI方法的参数位置换一下,即public void anyStringAsName(TestBean2 cat,TestBean1 dog);
,结果应该是哪两行正确呢?认真思考一下,实在想不明白跑一下测试用例,也许对理解更有帮助。
如果想明白了用这个思路验证一下:参照参数列表(TestBean2,TestBean1)
,可以确定只可以省略第一个参数即TestBean2,那么”::”前导类必须是TestBean2,用于自动创建对象;而未省略的参数是TestBean1,那么方法名为expect1,结果为xxx(TestBean2::expect1)
,即③和⑦,你答对了吗?
Listlist = Arrays.asList(new String[] { "xxx", "aaa", "zzz", "eee", "yyy", "ccc" });
Streamstream1 = list.stream();Stream stream2 = list.stream().parallel();
Stream类有几个工厂方法可以创建Stream:
Stream
事实上我们很少直接操作Stream对象,而是作为中间结果,最终要关闭流返回数据。
查看Stream实现,其中返回非Stream对象的方法,即为关闭流返回数据的方法。
Object[] toArray(); A[] toArray(IntFunction generator);T reduce(T identity, BinaryOperatoraccumulator);Optional reduce(BinaryOperator accumulator); U reduce(U identity,BiFunction accumulator,BinaryOperator combiner); R collect(Supplier supplier,BiConsumer accumulator,BiConsumer combiner); R collect(Collector collector);Optional min(Comparator comparator);Optional max(Comparator comparator);long count();boolean anyMatch(Predicate predicate);boolean allMatch(Predicate predicate);boolean noneMatch(Predicate predicate);Optional findFirst();Optional findAny();
Stream对象打开后,关闭前,是我们要对数据做处理的主要途径。
将流转换成List。
@Testpublic void testStreamToList() { Listlist1 = list.stream().collect(Collectors.toList());}
@Testpublic void testStreamSort() { System.out.println(list); // list.sort((x, y) -> x.compareTo(y)); Listlist2 = list.stream().parallel() .sorted(Comparator.comparing(String::toLowerCase)) .collect(Collectors.toList()); System.out.println(list2);}
@Testpublic void testStreamFilter() { System.out.println(list); Listlist2 = list.stream() .filter(x->!x.equals("zzz")) .collect(Collectors.toList()); System.out.println(list2);}
转换大小写
Listlist...;list.stream().map(String::toUpperCase).forEach(System.out::println);
提取局部信息:
Field[] fields = entityClass.getDeclaredFields();String fieldNames = Arrays.asList(fields).stream().map(x -> x.getName()).collect(Collectors.joining(","));
提取局部信息到到Map
Mappeoples = persons.stream().collect(Collectors.toMap(Person::getName, Person::getAge));
list.forEach(x->System.out.println(x));list.forEach(System.out::println);
一个参数:
@Testpublic void testStreamReduce1() { System.out.println(list); BinaryOperatoraccumulator=BinaryOperator.maxBy((x,y)->x.compareTo(y)); Optional max = list.stream() .reduce(accumulator); System.out.println(max);}
两个参数:
@Testpublic void testStreamReduce2() { System.out.println(list); Optionalmax = list.stream() .reduce((x,y)->x+y); System.out.println(max);}
三个参数:
@Testpublic void testStreamReduce3() { System.out.println(list); StringBuilder joining = list.stream() .reduce(new StringBuilder(), (x, e) -> x.append(e).append(","), (u, t) -> u.append(t) ); System.out.println(joining);}
参数1:返回的类型,此处传入一个初始对象;
参数2:一个Funcion(x,e),x为处理的数据中间结果,e为当前元素。 参数3:同2。这个例子有点复杂,打点日志分析一下:
@Testpublic void testStreamReduce3A() { System.out.println(list); StringBuilder joining = list.stream() .reduce(new StringBuilder(), (x, e) -> { System.out.println("2:t="+x+"; u="+e); x.append(",").append(e); return x; }, (x, e) -> { System.out.println("3:t="+x+"; u="+e); //x.append(",").append(e); return x; } ); System.out.println(joining);}
你会发现第三个FI并没有执行。stream加上.parallel()
,并行处理,就看到第三个FI执行了。但是结果有点意外,乱了。
list中有6个元素,2打印6次正常;而3只打印5次。而且每运行一次,3的打印结果都是变化的。
2:t=; u=ccc2:t=; u=eee2:t=; u=aaa2:t=; u=xxx2:t=,ccc,eee,aaa; u=zzz2:t=,ccc; u=yyy3:t=,ccc,eee,aaa,xxx,zzz,yyy; u=,ccc,eee,aaa,xxx,zzz,yyy3:t=,ccc,eee,aaa,xxx,zzz,yyy; u=,ccc,eee,aaa,xxx,zzz,yyy3:t=,ccc,eee,aaa,xxx,zzz; u=,ccc,eee,aaa,xxx,zzz,yyy3:t=,ccc,eee,aaa,xxx,zzz,yyy; u=,ccc,eee,aaa,xxx,zzz,yyy3:t=,ccc,eee,aaa,xxx,zzz,yyy; u=,ccc,eee,aaa,xxx,zzz,yyy
@Testpublic void testStreamCount() { long count = list.stream().count(); System.out.println(count);}
@Testpublic void testStreamDistinct() { Listlist2 = list.stream().distinct().collect(Collectors.toList()); System.out.println(list2);}
@Testpublic void testStreamAllMatch() { boolean b = list.stream().allMatch(x->x.matches("a*")); System.out.println("AllMath:"+b); b = list.stream().anyMatch(x->x.matches("a*")); System.out.println("anyMatch:"+b); b = list.stream().noneMatch(x->x.matches("a*")); System.out.println("noneMatch:"+b);}
Map不支持stream(),不过java8也为它增加了附加检查的方法,如:
putIfAbsent() computeIfPresent() getOrDefault() merge()Mapmap=...;map.forEach((key,value)->System.out.pritnln("key="+key+";value="+value))
java8 的 Date Time API来自jsr310(),这个specification的主导者中jota-time的作者Stephen Colebourne就是四分之一。
除了改为线程安全,基本上很大程度上延续了java自己的设计风格。而joda-time一些很方便的特性,如Property、toString(fmt) 都没有引入。
LocalDateTime 与Calendar相比改变有限
LocalDate 抄过来的 LocalTime 抄过来的 Instance 抄过来的 DateTimeFormatter 节操,还不如SimpleDateFormat,你怎么不去屎?int LocalDateTime.getDayOfMonth()
和DayOfWeek dateTime.getDayOfWeek()
什么玩意儿? @Test public void testClock(){ Clock clock = null; clock=Clock.system(ZoneId.of("Europe/Paris")); System.out.println(clock.millis()); clock=Clock.systemDefaultZone(); long millis = clock.millis(); System.out.println(millis); System.out.println(System.currentTimeMillis()); Instant instant = clock.instant(); Date legacyDate = Date.from(instant); System.out.println(legacyDate.getTime()); } @Test public void testTimezone(){ System.out.println(ZoneId.getAvailableZoneIds()); ZoneId zone1 = ZoneId.of("Europe/Berlin"); System.out.println(zone1.getRules()); ZoneId zone2=ZoneId.of("Asia/Shanghai"); System.out.println(zone2.getRules()); } @Test public void testLocalDate(){ //LocalDate是不可变的 LocalDate today = LocalDate.now(); System.out.println(today); LocalDate tomorrow = today.plus(1, ChronoUnit.DAYS); DayOfWeek dayOfWeek = tomorrow.getDayOfWeek(); System.out.println(dayOfWeek); System.out.println(dayOfWeek.getValue()); } @Test public void testLocalTime(){ LocalTime now1 = LocalTime.now(); System.out.println(now1); LocalTime now2=now1.plusMinutes(10); System.out.println(now2); LocalTime now3=now1.plus(2, ChronoUnit.HOURS); System.out.println(now3); } @Test public void testLocalDateTime(){ LocalDateTime dateTime=LocalDateTime.now(); System.out.println(dateTime); dateTime=LocalDateTime.of(2017, 2, 22, 13, 23); System.out.println(dateTime); } @Test public void testFormater(){ //DateTimeFormatter是线程安全的,SimpleDateFormat不是 LocalDateTime dateTime=LocalDateTime.now(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); System.out.println(formatter.format(dateTime)); }
欣赏一下joda-time:
System.out.println(DateTime.now().toString("yyyy-MM-dd HH:mm:ss"));//2017-02-21 18:26:49//计算两个时间时间隔了多少天DateTime dateTime=new DateTime(2008,12,8,0,0);Duration duration=new Duration(dateTime,DateTime.now());System.out.println(duration.getStandardDays());//Interval可以获得两个时间每个单位的时间差DateTime dt1 = new DateTime(2008, 12, 8, 8, 31);DateTime dt2 = new DateTime(2017, 2, 21, 17, 21);Interval interval = new Interval(dt1.getMillis(), dt2.getMillis());Period p = interval.toPeriod();System.out.println(p.toString());System.out.println("years:" + p.getYears()+";months:"+p.getMonths() +";days:"+p.getDays()+";hours:"+p.getHours() +";minutes:"+p.getMinutes()+";seconds:"+p.getSeconds() +";mills:"+p.getMillis());//链式代码DateTime dateTime=new DateTime(2017,2,21,0,0);System.out.println(dateTime.dayOfMonth() .setCopy(28)//穿越到2017-02-28 .minusYears(9)//穿越到9年前 .dayOfMonth() .withMaximumValue()//穿越到那年那月的最后一天,那天是29日 .dayOfWeek() //.get()//29日那天是星期五 .setCopy(1)//(不管29日是星期几)穿越到29日那天所在的星期一 //Hello,我来过 2008-02-25T00:00:00.000+08:00
java8之前一个注解在一个位置只能标注一次,java8允许标注多次。
注解定义:
@Target( ElementType.TYPE )@Retention( RetentionPolicy.RUNTIME )public @interface Filters { Filter[] value();}@Target( ElementType.TYPE )@Retention( RetentionPolicy.RUNTIME )@Repeatable( Filters.class )public @interface Filter { String value();}
在java8 中标注:
@Filter( "filter1" )@Filter( "filter2" )public void sayHi(){}
编译器隐式地增加了一个Filters注解。查询注解可以看到:
@com.pollyduan.annotation.Filters(value=[@com.pollyduan.annotation.Filter(value=filter1), @com.pollyduan.annotation.Filter(value=filter2)])
在这之前实现同一目标需要这么做:
@Filters({ @Filter("filter1"),@Filter("filter2")})public void sayHi(){}
实际上这是一致的,也就是说java8的重复注解是一个语法糖,编译器会自动包装成之前的格式。
Base64在java8中转正了,不需要再使用sun.misc.BASE64Encoder
和sun.misc.BASE64Decoder
了, 而是java.util.Base64
,这是一个工厂类,它用来创建两个内部实现类:
Encoder encoder = Base64.getEncoder();String str="abcd";String enc=encoder.encodeToString(str.getBytes());System.out.println(enc);System.out.println(new String(Base64.getDecoder().decode(enc)));
新的try…cache可以自动关闭在try表达式中打开的对象,而无需开发者手动关闭。
多个流对象打开语句,用分号分隔,不是逗号。
try(ObjectInputStream in=new ObjectInputStream(new FileInputStream("p1.obj"))){ System.out.println(Person.class.hashCode()); Person person=(Person)in.readObject(); System.out.println(person.staticString); } catch (Exception e) { e.printStackTrace(); }}
不再需要:
finally{ in.close();}
Java8 更新了JavaScript引擎,从jdk6引入的Mozilla Rhino
引擎改为使用nashorn
。
测试一下,首先编写一个js文件my.js。
function myfun(str){ return str+new Date();}
java调用js:
package com.pollyduan.nashron;import java.io.FileReader;import javax.script.ScriptEngine;import javax.script.ScriptEngineManager;public class NashornTest { public static void main(String[] args) throws Exception { ScriptEngine nashorn = new ScriptEngineManager().getEngineByName("js"); nashorn.eval(new FileReader("src/main/resources/my.js")); Object ret = nashorn.eval("myfun(\"Hello,It\'s \")"); System.out.println(ret); }}
在java6、7自动使用rhino
引擎,在java8则自动使用nashorn
引擎。
java8中,建议显示指定使用nashorn
:
ScriptEngine nashorn = new ScriptEngineManager().getEngineByName("nashorn");
如果项目曾经使用过rhino
,在移植到java8时,需要引入依赖并指定engineName为rhino
。
cat.inspiracio rhino-js-engine 1.7.7.1
同时java修改:
ScriptEngine nashorn = new ScriptEngineManager().getEngineByName("rhino");
java8,节操洒了一地。很多的改变违背了面向对象的初衷,多重继承、函数指针都有了,你要变成c–语言吗?
有些改变是积极的,不过效果差强人意。像date time api。
诸如Predicate、Optional、stream,从guava借鉴而来,也是不错的。
转载地址:http://vlzxx.baihongyu.com/