做网站用什么语言高效,重庆网润集团有限公司,站长工具2023最新国产,海南建设大厅网站1. Java8的新特性 1.1. Lambda表达式和函数式接口 最简单的Lambda表达式可以用逗号分隔的参数列表、-符号和功能语句块来表示。示例如下#xff1a; Arrays.asList( a, b, d ).forEach( e - System.out.println( e ) ); 请注意到…1. Java8的新特性 1.1. Lambda表达式和函数式接口 最简单的Lambda表达式可以用逗号分隔的参数列表、-符号和功能语句块来表示。示例如下 Arrays.asList( a, b, d ).forEach( e - System.out.println( e ) ); 请注意到编译器会根据上下文来推测参数的类型或者你也可以显示地指定参数类型只需要将类型包在括号里。举个例子 Arrays.asList( a, b, d ).forEach( ( String e ) - System.out.println( e ) ); Lambda表达式可能会引用类的成员或者局部变量会被隐式地转变成final类型下面两种写法的效果是一样的 String separator ,;
//separator ;;;//该行编译时会报错从lambda 表达式引用的本地变量必须是最终变量或实际上的最终变量
Arrays.asList( a, b, d ).forEach(
( String e ) - System.out.print( e separator ) ); 和 final String separator ,;
Arrays.asList( a, b, d ).forEach(( String e ) - System.out.print( e separator ) ); Lambda表达式可能会有返回值编译器会根据上下文推断返回值的类型。如果lambda的语句块只有一行不需要return关键字。下面两个写法是等价的 Arrays.asList( a, b, d ).sort( ( e1, e2 ) - e1.compareTo( e2 ) ); 和 Arrays.asList( a, b, d ).sort( ( e1, e2 ) - {int result e1.compareTo( e2 );return result;
} ); Java语言的设计者们思考了很多如何让现有的功能和lambda表达式友好兼容。于是就有了函数接口这个概念。函数接口是一种只有一个方法的接口函数接口可以隐式地转换成lambda表达式。 java.lang.Runnable 和java.util.concurrent.Callable是函数接口两个最好的例子。但是在实践中函数接口是非常脆弱的只要有人在接口里添加多一个方法那么这个接口就不是函数接口了就会导致编译失败。Java 8提供了一个特殊的注解FunctionalInterface来克服上面提到的脆弱性并且显示地表明函数接口的目的java里所有现存的接口都已经加上了FunctionalInterface。让我们看看一个简单的函数接口定义 FunctionalInterface
public interface Functional {void method();
} 我们要记住默认的方法和静态方法下一节会具体解释不会违反函数接口的约定例子如下 FunctionalInterface
public interface FunctionalDefaultMethods {void method();default void defaultMethod() {}
} 函数式接口的重要属性是我们能够使用lambda实例化它们。下面是实例化Runnable函数式接口的一个例子。 Runnable r () -{ System.out.println(Running!); } 新版本向 java.util.function包中添加了很多新的函数式接口。下面是一些例子 FunctionT, R——将T作为输入返回R作为输出 PredicateT——将T作为输入返回一个布尔值作为输出 ConsumerT——将T作为输入不返回任何内容 SupplierT——没有输入返回T BinaryOperatorT——将两个T作为输入返回一个T作为输出 1.2. 接口的默认方法和静态方法 Java 8增加了两个新的概念在接口声明的时候默认和静态方法。默认方法允许我们在接口里添加新的方法而不会破坏实现这个接口的已有类的兼容性也就是说不会强迫实现接口的类实现默认方法。 默认方法和抽象方法的区别是抽象方法必须要被实现默认方法不是。作为替代方式接口可以提供一个默认的方法实现所有这个接口的实现类都会通过继承得到这个方法如果有需要也可以重写这个方法让我们来看看下面的例子 private interface Defaulable {// Interfaces now allow default methods, the implementer may or// may not implement (override) them.default String notRequired() {return Default implementation;}
}private static class DefaultableImpl implements Defaulable {}private static class OverridableImpl implements Defaulable {Overridepublic String notRequired() {return Overridden implementation;}
} 接口Defaulable使用default关键字声明了一个默认方法notRequired()类DefaultableImpl实现了Defaulable接口没有对默认方法做任何修改。另外一个类OverridableImpl重写类默认实现提供了自己的实现方法。注意接口不能为Object类中的任何方法提供默认的实现。 Java 8 的另外一个有意思的新特性是接口里可以声明静态方法并且可以实现。例子如下 private interface DefaulableFactory {// Interfaces now allow static methodsstatic Defaulable create( Supplier Defaulable supplier ) {return supplier.get();}
} 下面是把接口的静态方法和默认方法放在一起的示例::new 是构造方法引用后面会有详细描述 public static void main( String[] args ) {Defaulable defaulable DefaulableFactory.create( DefaultableImpl::new );System.out.println( defaulable.notRequired() );defaulable DefaulableFactory.create( OverridableImpl::new );System.out.println( defaulable.notRequired() );
} 控制台的输出如下 Default implementation
Overridden implementation 1.3. 方法引用 方法引用提供了一个很有用的语义来直接访问类或者实例的已经存在的方法或者构造方法。结合Lambda表达式方法引用使语法结构紧凑简明。不需要复杂的引用。 下面我们用Car 这个类来做示例Car这个类有不同的方法定义。让我们来看看java 8支持的4种方法引用。 public static class Car {public static Car create( final Supplier Car supplier ) {return supplier.get();} public static void collide( final Car car ) {System.out.println( Collided car.toString() );}public void follow( final Car another ) {System.out.println( Following the another.toString() );}public void repair() {System.out.println( Repaired this.toString() );}
} 第一种方法引用是构造方法引用语法是Class::new 对于泛型来说语法是ClassT ::new请注意构造方法没有参数: final Car car Car.create( Car::new );
final List Car cars Arrays.asList( car ); 第二种方法引用是静态方法引用语法是Class::static_method请注意这个静态方法只支持一个类型为Car的参数。 cars.forEach( Car::collide ); 第三种方法引用是类实例的方法引用语法是Class::method请注意方法没有参数。 cars.forEach( Car::repair ); 最后一种方法引用是引用特殊类的方法语法是instance::method请注意只接受Car类型的一个参数。 final Car police Car.create( Car::new );
cars.forEach( police::follow ); 运行这些例子我们将会在控制台得到如下信息Car的实例可能会不一样 Collided com.javacodegeeks.java8.method.references.MethodReferences$Car7a81197d
Repaired com.javacodegeeks.java8.method.references.MethodReferences$Car7a81197d
Following the com.javacodegeeks.java8.method.references.MethodReferences$Car7a81197d 2. Java 8 库的新特性 2.1. Optional 著名的NullPointerException 是引起系统失败最常见的原因。很久以前Google Guava项目引入了Optional作为解决空指针异常的一种方式不赞成代码被null检查的代码污染期望程序员写整洁的代码。受Google Guava的鼓励Optional 现在是Java 8库的一部分。 Optional 只是一个容器它可以保存一些类型的值或者null。它提供很多有用的方法所以没有理由显式地检查null。 让我们看看两个Optional 用法的小例子一个是允许为空的值另外一个是不允许为空的值。 Optional String fullName Optional.ofNullable( null );
System.out.println( Full Name is set? fullName.isPresent() );
System.out.println( Full Name: fullName.orElseGet( () - [none] ) );
System.out.println( fullName.map( s - Hey s ! ).orElse( Hey Stranger! ) ); 如果Optional实例有非空的值方法 isPresent() 返回true否则返回false。方法orElseGet提供了回退机制当Optional的值为空时接受一个方法返回默认值。map()方法转化Optional当前的值并且返回一个新的Optional实例。orElse方法和orElseGet类似但是它不接受一个方法而是接受一个默认值。上面代码运行结果如下 Full Name is set? false
Full Name: [none]
Hey Stranger! 让我们大概看看另外一个例子。 Optional String firstName Optional.of( Tom );
System.out.println( First Name is set? firstName.isPresent() );
System.out.println( First Name: firstName.orElseGet( () - [none] ) );
System.out.println( firstName.map( s - Hey s ! ).orElse( Hey Stranger! ) );
System.out.println(); 输出如下 First Name is set? true
First Name: Tom
Hey Tom! 2.2. Stream 新增加的Stream API (java.util.stream)引入了在Java里可以工作的函数式编程。这是目前为止对java库最大的一次功能添加希望程序员通过编写有效、整洁和简明的代码能够大大提高生产率。 list转map 常用方式 public MapLong, String getIdNameMap(ListAccount accounts) {return accounts.stream().collect(Collectors.toMap(Account::getId, Account::getUsername));
} 收集成实体本身map public MapLong, Account getIdAccountMap(ListAccount accounts) {return accounts.stream().collect(Collectors.toMap(Account::getId, account - account));
} account - account是一个返回本身的lambda表达式其实还可以使用Function接口中的一个默认方法代替使整个方法更简洁优雅 public MapLong, Account getIdAccountMap(ListAccount accounts) {return accounts.stream().collect(Collectors.toMap(Account::getId, Function.identity()));
} 重复key的情况 代码如下 public MapString, Account getNameAccountMap(ListAccount accounts) {return accounts.stream().collect(Collectors.toMap(Account::getUsername, Function.identity()));
} 这个方法可能报错java.lang.IllegalStateException: Duplicate key因为name是有可能重复的。toMap有个重载方法可以传入一个合并的函数来解决key冲突问题 public MapString, Account getNameAccountMap(ListAccount accounts) {return accounts.stream().collect(Collectors.toMap(Account::getUsername, Function.identity(), (key1, key2) - key2));
} 这里只是简单的使用后者覆盖前者来解决key重复问题。 指定具体收集的map toMap还有另一个重载方法可以指定一个Map的具体实现来收集数据 public MapString, Account getNameAccountMap(ListAccount accounts) {return accounts.stream().collect(Collectors.toMap(Account::getUsername, Function.identity(), (key1, key2) - key2, LinkedHashMap::new));
} Stream API让集合处理简化了很多我们后面会看到不仅限于Java集合类。让我们从一个简单的类Task开始来看看Stream的用法。 public class Streams {private enum Status {OPEN, CLOSED};private static final class Task {private final Status status;private final Integer points;Task( final Status status, final Integer points ) {this.status status;this.points points;}public Integer getPoints() {return points;}public Status getStatus() {return status;}Overridepublic String toString() {return String.format( [%s, %d], status, points );}
}
} Task类有一个分数的概念或者说是伪复杂度其次是还有一个值可以为OPEN或CLOSED的状态.让我们引入一个Task的小集合作为演示例子 final Collection Task tasks Arrays.asList(new Task( Status.OPEN, 5 ),new Task( Status.OPEN, 13 ),new Task( Status.CLOSED, 8 )
); 第一个问题是所有的开放的Task的点数是多少在java 8 之前通常的做法是用foreach迭代。但是Java8里头我们会用Stream。Stream是多个元素的序列支持串行和并行操作。 // Calculate total points of all active tasks using sum()
final long totalPointsOfOpenTasks tasks.stream().filter( task - task.getStatus() Status.OPEN ).mapToInt( Task::getPoints ).sum();
System.out.println( Total points: totalPointsOfOpenTasks ); 控制台的输出将会是 Total points: 18 上面代码执行的流程是这样的首先Task集合会被转化为Stream表示然后filter操作会过滤掉所有关闭的Task接下来使用Task::getPoints 方法取得每个Task实例的点数mapToInt方法会把Task Stream转换成Integer Stream最后使用Sum方法将所有的点数加起来得到最终的结果。 在我们看下一个例子之前我们要记住一些关于Stream的说明。Stream操作被分为中间操作和终点操作。 中间操作返回一个新的Stream。这些中间操作是延迟的执行一个中间操作比如filter实际上不会真的做过滤操作而是创建一个新的Stream当这个新的Stream被遍历的时候它里头会包含有原来Stream里符合过滤条件的元素。 终点操作比如说forEach或者sum会遍历Stream从而产生最终结果或附带结果。终点操作执行完之后Stream管道就被消费完了不再可用。在几乎所有的情况下终点操作都是即时完成对数据的遍历操作。 Stream的另外一个价值是Stream创造性地支持并行处理。让我们看看下面这个例子这个例子把所有task的点数加起来。 // Calculate total points of all tasks
final double totalPoints tasks.stream().parallel().map( task - task.getPoints() ) // or map( Task::getPoints ).reduce( 0, Integer::sum );
System.out.println( Total points (all tasks): totalPoints ); 这个例子跟上面那个非常像除了这个例子里使用了parallel()方法 并且计算最终结果的时候使用了reduce方法。 输出如下 Total points (all tasks): 26.0 经常会有这个一个需求我们需要按照某种准则来对集合中的元素进行分组。Stream也可以处理这样的需求下面是一个例子 // Group tasks by their status
final Map Status, List Task map tasks.stream().collect( Collectors.groupingBy( Task::getStatus ) );
System.out.println( map ); 控制台的输出如下 {CLOSED[[CLOSED, 8]], OPEN[[OPEN, 5], [OPEN, 13]]} 按照某种准则来对集合中的元素进行分组并统计每组个数 // Group tasks by their status
final Map Status, Long map tasks.stream().collect( Collectors.groupingBy( Task::getStatus, Collectors.counting()) ); System.out.println( map ); 按照某种准则来对集合中的元素进行分组并统计每组里某个字段的平均值 // Group tasks by their status
final Map Status, Long map tasks.stream().collect( Collectors.groupingBy( Task::getStatus, Collectors.averagingInt(Task::getPoints)) ); System.out.println( map ); 让我们来计算整个集合中每个task分数或权重的平均值来结束task的例子。 // Calculate the weight of each tasks (as percent of total points)
final Collection String result tasks.stream() // Stream String .mapToInt( Task::getPoints ) // IntStream.asLongStream() // LongStream.mapToDouble( points - points / totalPoints ) // DoubleStream.boxed() // Stream Double .mapToLong( weigth - ( long )( weigth * 100 ) ) // LongStream.mapToObj( percentage - percentage % ) // Stream String.collect( Collectors.toList() ); // List String
System.out.println( result ); 控制台输出如下 [19%, 50%, 30%] 最后就像前面提到的Stream API不仅仅处理Java集合框架。像从文本文件中逐行读取数据这样典型的I/O操作也很适合用Stream API来处理。下面用一个例子来应证这一点。 final Path path new File( filename ).toPath();
try( Stream String lines Files.lines( path, StandardCharsets.UTF_8 ) ) {lines.onClose( () - System.out.println(Done!) ).forEach( System.out::println );
} Stream的方法onClose 返回一个等价的有额外句柄的Stream当Stream的close方法被调用的时候这个句柄会被执行。 流可以是无限的、有状态的可以是顺序的也可以是并行的。在使用流的时候你首先需要从一些来源中获取一个流执行一个或者多个中间操作然后执行一个最终操作。中间操作包括filter、map、flatMap、peel、distinct、sorted、limit和substream。终止操作包括forEach、toArray、reduce、collect、min、max、count、anyMatch、allMatch、noneMatch、findFirst和findAny。 java.util.stream.Collectors是一个非常有用的实用类。该类实现了很多归约操作例如将流转换成集合和聚合元素。 Stream有串行和并行两种串行Stream上的操作是在一个线程中依次完成而并行Stream则是在多个线程上同时执行。 下面的例子展示了是如何通过并行Stream来提升性能 首先我们创建一个没有重复元素的大表 int max 1000000;
ListString values new ArrayList(max);
for (int i 0; i max; i) {UUID uuid UUID.randomUUID();values.add(uuid.toString());
} 然后我们计算一下排序这个Stream要耗时多久 串行排序 long t0 System.nanoTime();
long count values.stream().sorted().count();
System.out.println(count);
long t1 System.nanoTime();
long millis TimeUnit.NANOSECONDS.toMillis(t1 - t0);
System.out.println(String.format(sequential sort took: %d ms, millis));
// 串行耗时: 899 ms 并行排序 long t0 System.nanoTime();
long count values.parallelStream().sorted().count();
System.out.println(count);
long t1 System.nanoTime();
long millis TimeUnit.NANOSECONDS.toMillis(t1 - t0);
System.out.println(String.format(parallel sort took: %d ms, millis));
// 并行排序耗时: 472 ms 上面两个代码几乎是一样的但是并行版的快了50%之多唯一需要做的改动就是将stream()改为parallelStream()。 2.3. 日期时间APIJSR310 新的java.time包包含了所有关于日期、时间、日期时间、时区、Instant跟日期类似但精确到纳秒、duration持续时间和时钟操作的类。设计这些API的时候很认真地考虑了这些类的不变性从java.util.Calendar吸取的痛苦教训。如果需要修改时间对象会返回一个新的实例。 ClockClock使用时区来访问当前的instant, date和time。Clock类可以替换 System.currentTimeMillis() 和 TimeZone.getDefault(). LocalDateLocalDate只保存有ISO-8601日期系统的日期部分有时区信息 LocalTimeLocalTime只保存ISO-8601日期系统的时间部分没有时区信息。 LocalDateTimeLocalDateTime类合并了LocalDate和LocalTime它保存有ISO-8601日期系统的日期和时间但是没有时区信息。 ZonedDateTime如果您需要一个类持有日期时间和时区信息可以使用ZonedDateTime它保存有ISO-8601日期系统的日期和时间而且有时区信息。 DurationDuration持有的时间精确到纳秒。它让我们很容易计算两个日期中间的差异。 2.4. Base64 对Base64的支持最终成了Java 8标准库的一部分非常简单易用 package com.javacodegeeks.java8.base64;import java.nio.charset.StandardCharsets;
import java.util.Base64; public class Base64s {
public static void main(String[] args) {
final String text Base64 finally in Java 8!;
final String encoded Base64
.getEncoder()
.encodeToString( text.getBytes( StandardCharsets.UTF_8 ) );
System.out.println( encoded );
final String decoded new String(
Base64.getDecoder().decode( encoded ),
StandardCharsets.UTF_8 );
System.out.println( decoded );
}
} 控制台输出的编码和解码的字符串 QmFzZTY0IGZpbmFsbHkgaW4gSmF2YSA4IQ
Base64 finally in Java 8! 新的Base64API也支持URL和MINE的编码解码 (Base64.getUrlEncoder() / Base64.getUrlDecoder(), Base64.getMimeEncoder() / Base64.getMimeDecoder()). 2.5. 并行数组 Java 8新增加了很多方法支持并行的数组处理。最重要的大概是parallelSort()这个方法显著地使排序在多核计算机上速度加快。下面的小例子演示了这个新的方法parallelXXX的行为。 import java.util.Arrays;
import java.util.concurrent.ThreadLocalRandom; public class ParallelArrays {public static void main( String[] args ) {long[] arrayOfLong new long [ 20000 ];Arrays.parallelSetAll( arrayOfLong,index - ThreadLocalRandom.current().nextInt( 1000000 ) );Arrays.stream( arrayOfLong ).limit( 10 ).forEach(i - System.out.print( i ) );System.out.println();Arrays.parallelSort( arrayOfLong );Arrays.stream( arrayOfLong ).limit( 10 ).forEach(i - System.out.print( i ) );System.out.println();}
} 这一小段代码使用parallelSetAll() 方法填充这个长度是2000的数组然后使用parallelSort() 排序。这个程序输出了排序前和排序后的10个数字来验证数组真的已经被排序了。示例可能的输出如下请注意这些数字是随机产生的 Unsorted: 591217 891976 443951 424479 766825 351964 242997 642839 119108 552378Sorted: 39 220 263 268 325 607 655 678 723 793 2.6. 并发 在新增Stream机制与lambda的基础之上在java.util.concurrent.ConcurrentHashMap中加入了一些新方法来支持聚集操作。同时也在java.util.concurrent.ForkJoinPool类中加入了一些新方法来支持共有资源池common pool。 新增的java.util.concurrent.locks.StampedLock类提供一直基于容量的锁这种锁有三个模型来控制读写操作它被认为是不太有名的java.util.concurrent.locks.ReadWriteLock类的替代者。 在java.util.concurrent.atomic包中还增加了下面这些类 DoubleAccumulatorDoubleAdderLongAccumulatorLongAdder 3. 新的工具 3.1. 类依赖分析工具jdeps Jdeps是一个功能强大的命令行工具它可以帮我们显示出包层级或者类层级java类文件的依赖关系。它接受class文件、目录、jar文件作为输入默认情况下jdeps会输出到控制台。 作为例子让我们看看现在很流行的Spring框架的库的依赖关系报告。为了让报告短一些我们只分析一个jar: org.springframework.core-3.0.5.RELEASE.jar. jdeps org.springframework.core-3.0.5.RELEASE.jar 这个命令输出内容很多我们只看其中的一部分这些依赖关系根绝包来分组如果依赖关系在classpath里找不到就会显示not found. 4. JVM的新特性 JVM内存永久区已经被metaspace替换JEP 122。JVM参数 -XX:PermSize 和 –XX:MaxPermSize被XX:MetaSpaceSize 和 -XX:MaxMetaspaceSize代替。 5. 参考资料 http://ifeve.com/java-8-features-tutorial/ https://blog.chou.it/2014/03/java-8-new-features/ http://www.infoq.com/cn/news/2013/08/everything-about-java-8# http://www.importnew.com/17313.html转载于:https://www.cnblogs.com/junzi2099/p/7942980.html