document.write('
ABCD
[解析] 本题考查对JDK新特性的理解。
 对于选项A,JDK1.7增加了在switch语句中支持String的特性。所以,选项A正确。
 对于选项B,JDK1.7支持二进制字面量,写法为:int binary=0b1001。所以,选项B正确。
 对于选项C,JDK1.8增加了对Lambda表达式的支持。所以,选项C正确。
 对于选项D,JDK1.8通过使用关键字default可以给接口中的方法添加默认实现。所以,选项D正确。
 为了更好地说明JDK各个版本之间的差异,下面重点介绍JDK1.7与JDK1.8中一些其他的新特性。
 JDK1.7的部分新特性如下:
 1)switch可以接受String类型。随着Java语言的发展,在Java7中,switch开始支持String类型。以下是一段支持String类型
的示例代码:
public class Test {
public void test(String str) {
switch(str) {
case "computer":
System.out.println("computer");
break;
case "book":
System.out.println("book");
break;
case "iphone":
System.out.println("iphone");
break;
default:
System.out.println("default");
}
}
}
从本质上来讲,switch对字符串的支持,其实是对int类型值的匹配。它的实现原理如下:通过对case后面的String对象调用
hashCode()方法,得到一个int类型的hash值,然后用这个hash值来唯一标识这个case。当进行匹配的时候,首先调用这个字
符串的hashCode()方法,获取到一个hash值(int类型),用这个hash值来匹配所有的case,如果没有匹配成功,则说明不存
在,如果匹配成功,则会接着调用字符串的equals()方法进行匹配。由此可以看出,String变量不能为null,同时,switch的
case子句中使用的字符串也不能为null。 2)可以在catch代码块中捕获多个异常类型,如下面代码所示: try{ //可能会抛出
Exception1和Exception2异常的代码 } catch (Exception1 | Exception2 e){ //处理异常的代码 } 3)对数值字面量进行了改进。
①增加了二进制字面量的表示(0B001、0b111)。整数类型(例如byte、short、int与long等)也可以使用二进制数来表示。要指
定一个二进制字面量,可以给二进制数字添加前缀0b或者0B。相比于十六进制或者八进制,二进制字面量可以使数据之间的关
系更加清晰。 ②在数字中可以添加分隔符,例如123_456,下划线只能被用在数字中间,编译的时候这些下划线会被编译器去
掉。这样做的好处是避免了一些难以通过观察代码来发现的细微错误。例如数字10000000000和数字1000000000,不仔细看
很难发现两个数字中谁少了一个0或多了一个0,但对于10_000_000_000和1_000_000_000却不然。 4)使用泛型的时候增加了
类型推断机制。 在Java7以前,实例化一个HashMap对象的写法如下: Map<String, String> trades=new HashMap<
String, String>(); 而Java7引进了类型推断机制,因此,可以采用更加简洁的写法,如下所示: Map<String,String>
trades=new HashMap <>(); 5)增加了try-with-resources语句(try-with-resources语句是一个声明了一个或多个资源的try
语句。这里的一个资源指的是在使用完成后,必须关闭释放的对象。try-with-resources语句可以确保在该语句执行之后关闭
每个资源),确保了每个资源都在生命周期结束后被关闭,因此,在读取文件结束后,不需要显式地调用close方法。示例代码
如下: try (InputStream fis=new FileInputStream("input.txt");){ while(fis.read()!=-1) System.out.println(fis.read()); }
catch (Exception e){ e.printStackTrace(); } 同理,在使用JDBC访问数据库时,Statement对象也可以采用同样的写法。 6)增
加了fork/join框架用来增强对处理多核并行计算的支持,它的应用场景为:如果一个应用能被分解成多个子任务,并且组合多
个子任务的结果就能够获得最终的答案,在这种情况下,就可以使用fork/join框架。 具体而言,fork就是把一个大任务切分为
若干个子任务并行地执行,join就是合并这些子任务的执行结果,最后得到这个大任务的结果。例如,当需要计算1+2+…
+10000的值时,可以把这样一个大的任务分割成10个小的子任务,每个子任务分别对其中的1000个数进行求和,最终汇总这
10个子任务的结果即为最终结果。 以上讲解的都是JDK1.7的新特性,下面重点讲解JDK1.8的部分新特性。 1)增加了对
Lambda表达式的支持。Lambda表达式是一个匿名函数(指的是没有函数名的函数),它基于数学中的λ演算得名,直接对应于
其中的Lambda抽象。Lambda表达式可以表示闭包(注意和数学传统意义上的不同)。 Lambda表达式允许把函数作为一个方法
的参数。Lambda表达式的基本语法如下: (parameters)->expression 或 (parameters)->{statements;} Lambda的使用如
下例所示: Arrays.asList(1,7,2).forEach(i_->System.out.println(i)); 以上这种写法中,i的类型是由编译器推测出来的,当
然,也可以显式地指定类型,如下例所示: Arrays.asList(1,7,2).forEach((Integer i)->System.out.println(i)); 在Java8以前,
Java语言通过匿名函数的方法来代替Lambda表达式。 对于列表的排序,如果列表里面存放的是自定义的类,通常需要指定自
定义的排序方法,传统的写法如下:
import java.util.Arrays;
importjava.util.Comparator;
class Person
{
public Person(String name, int age)
{
this.name=name;
this.age=age;
}
private String name;
private int age;
public int getAge() {return age;}
public String getName() {return name;}
public String toString() {return name+":"+age; }
}
public class Test
{
public static void main(String[] args)
{
Person[] people={new Person("James", 25), new Person("Jack",21)};
//自定义类排序方法,通过年龄进行排序
Arrays.sort(people, new Comparato<Person>0
{
@Override
public int compare(Person a, Person b)
{
return a.getAge() -b.getAge();
}
});
for (Person p: people)
{
System.out.println(p);
}
}
}
采用Lambda表达式后,写法如下: Arrays.sort(people, (Person a, Person b)->a.getAge()-b.getAge()); 或
Arrays.sort(people,(a,b)->a.getAge()-b.getAge()); 显然,采用Lambda表达式后,代码会变得更加简洁。 Lambda表达式
是通过函数式接口(只有一个方法的普通接口)来实现的。函数式接口可以被隐式地转换为Lambda表达式。为了与普通的接口区
分开(普通接口中可能会有多个方法),JDK1.8新增加了一种特殊的注解@FunctionalInterface。下面给出一个函数式接口的定
义: @FunctionalInterface interface fun{ void f(); } 2)接口增加了方法的默认实现和静态方法。JDK1.8通过使用关键字
default可以给接口中的方法添加默认实现,此外,接口中还可以定义静态方法,示例代码如下: interface Inter8{ void f();
default void g(){ System.out.println("this is default method in interface"); } static void h(){ System.out.println("this is
static method in interface"); } } 那么,为什么要引入接口中方法的默认实现呢? 其实,这样做的最重要的一个目的就是为了实
现接口升级。在原有的设计中,如果想要升级接口,例如给接口中添加一个新的方法,会导致所有实现这个接口的类都需要被
修改,这给Java语言已有的一些框架进行升级带来了很大的麻烦。如果接口能支持默认方法的实现,那么可以给这些类库的升
级带来许多便利。例如,为了支持Lambda表达式,Collection中引入了foreach方法,可以通过这个语法增加默认的实现,从
而降低了对这个接口进行升级的代价,不需要对所有实现这个接口的类进行修改。 3)方法引用。方法引用指的是可以直接引用
Java类或对象的方法。它可以被看成是一种更加简洁易懂的Lambda表达式,使用方法引用后,上例中的排序代码就可以使用
下面更加简洁的方式来编写: Arrays.sort(people, Comparator.comparing(Person::getAge)); 方法引用共有下面4种形式:
①引用构造方法:ClassName::new。 ②引用类静态方法:ClassName::methodName。 ③引用特定类的任意对象方法:
ClassName::methodName。 ④引用某个对象的方法:instanceName::methodName。 下面给出一个使用方法引用的例
子:
import java.util.Arrays;
import java.util.Comparator;
import java.util.function.Supplier;
class Person {
private String name;
privateint age;
public Person() {}
public Person(String name,int age) {
this.name=name;
this.age=age;
}
publicstatic Person getInstance(final Supplier<Person>supplier){
return supplier.get();
}
publicvoid setAge(int age){ this.age=age;)
publicint getAge() {return age;}
public String getName() {return name;}
publicstaticint compareByAge(Person a, Person b){return b.age-a.age;)
public String toString() {return name+”:”+age;}
}
class CompareProvider{
publicint compareByAge(Person a, Person b){
return a.getAge()-b.getAge();
}
}
publicclass Test2{
publicstaticvoid main(String[] args){
//引用是构造方法
Person p1=Person.getInstance(Person::new);
p1.setAge(19);
System.out.println("测试引用构造方法: "+p1.getAge());
Person[] people={new Person("James", 25), new Person("Jack", 21)};
//引用特定类的任意对象方法
Arrays.sort(people, Comparator.comparing(Person::getAge));
System.out.println("测试引用特定类的任意对象方法:");
for(Personp: people){
System.out.println(p);
}
//引用类静态方法
Arrays.sort(people, Person::compareByAge);
System.out.println("测试引用类静态方法: ");
for (Personp:people){
System.out.println(p);
}
//引用某个对象的方法
Arrays.sort(people, new CompareProvider()::compareByAge);
System.out.println("测试引用某个对象的方法: ");
for (Personp: people){
System.out.println(p);
}
}
}
程序的运行结果为:
测试引用构造方法:19
测试引用特定类的任意对象方法:
Jack:21
James:25
测试引用类静态方法:
James:25
Jack:21
测试引用某个对象的方法:
Jack:21
James:25
4)注解(Annotation)。 ①JDK1.5中引入了注解机制,但是有一个限制:相同的注解在同一位置只能声明一次。JDK1.8引入了重
复注解机制后,相同的注解在同一个地方可以声明多次。 备注:注解为开发人员在代码中添加信息提供了一种形式化的方法,
它使得开发人员可以在某个时刻方便地使用这些数据(通过解析注解来使用这些数据)。注解的语法比较简单,除了@符号的使
用以外,它基本上与Java语言的语法一致,Java语言内置了三种注解方式,它们分别是:@Override(表示当前方法是覆盖父类
的方法)、@Deprecated(表示当前元素是不赞成使用的)、@SuppressWamings(表示关闭一些不当的编译器警告信息)。需要
注意的是,它们都定义在java.lang包中。 ②JDK1.8对注解进行了扩展。使得注解被使用的范围更广,例如可以给局部变量、
泛型和方法异常等提供注解。 5)类型推测。JDK1.8加强了类型推测机制,这种机制可以使得代码更为简洁,假如有如下类的定
义:
class List<E> {
static <Z> List<Z> nil() {...};
static <Z> List<Z> cons(Z head, List<Z> tail) {...};
E head() {...}
}
在调用时,可以使用下面的代码: List<Integer>1=List.nil(); //通过赋值的目标类型来推测泛型的参数 在Java7中,这种写法
将会产生编译错误,正确写法如下: List<Integer>1=List.<Integer>nil(); 同理,在调用cons方法时的写法为:
List.cons(5,List.nil()); //通过方法的第一个参数来推测泛型的类型 而不需要显式地指定类型:List.cons(5,List.<Integer>
nil()); 6)参数名字。JDK1.8通过在编译的时候增加-parameters选项,以及增加反射API与Parameter.getName()方法实现了获
取方法参数名的功能。 示例代码如下:
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
publicclass Test
{
publicstaticvoid main(String[] args) {
Method method;
try {
method=Test.class.getMethod("main",String[].class);
for(final Parameter parameter: method.getParameters()) {
System.out.println("Parameter: "+parameter.getName()); }
} catch (Exception e) {
e.printStackTrace();
}
}
}
如果使用命令javac Test.java来编译并运行以上程序,程序的运行结果为Parameter: args0。 如果使用命令javac Test.javaparameters来编译并运行以上程序,程序的运行结果为Parameter:
args。 7)新增Optional类。在使用Java语言进行编程时,
经常需要使用大量的代码来处理空指针异常,而这种操作往往会降低程序的可读性。JDK1.8引入了Optional类来处理空指针的
情况,从而增强了代码的可读性。下面给出一个简单的例子:
public class Test {
public static void main(String[] args) {
Optional<String> s1=Optional.of("Hello");
//判断是否有值
if(s1.isPresent())
System.out.println(s1.get());//获取值
Optional<String> s2=Optional.ofNullable(null);
if(s2.isPresent())
System.out.println(s2.get());
}
}
这里只是介绍了Optional简单的使用示例,读者如果想要了解更多相关内容,可以查看Java手册来详细了解Optional类的使用
方法。 8)新增Stream类。JDK1.8新增加了Stream类,从而把函数式编程的风格引入到Java语言中,Stream的API提供了非常
强大的功能,使用Stream后,可以写出更加强大、更加简洁的代码(例如可以代替循环控制语句)。示例代码如下:
import java.util.ArrayList;
import java.util.lterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
class Student
{
private String name;
private Integer age;
public Student(String name,int age)
{
this.name=name;
this.age=age;
}
public String getName() {return name;}
public Integer getAge() {return age;}
}
publicclass Test {
publicstaticvoid main(String[] args) {
List<Student>1=new ArrayList <>();
Ladd(new Student("Wang",10));
1.add(new Student("Li",13));
1.add(new Student("Zhang",10));
1.add(new Student("Zhao",15));
System.out,println("找出年龄为10的第一个学生: ");
Optional<Student> s=1.stream().filter(stu->stu.getAge().equals(10)).findFirst();
if(s.isPresent())
System.out.println(s.get().getName()+","+s.get().getAge());
System.out.println("找出年龄为10的所有学生: ");
List<Student> searchResult=1.stream().filter(stu->stu.getAge().equals(10)).collect (Collectors.toList());
for(Student stu:searchResult)
System.out.println(stu.getName()+","+stu.getAge());
System.out.println("对学生按年龄分组: ");
Map<Integer,List<Student>>map=1.stream().collect(Collectors.groupingBy(Student::getAge));
Iterator<Map.Entry<Integer,List<Student>>> iter=map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<Integer, List<Student>> entry=(Map.Entry<Integer, List<Student>>)
iter.next();
int age=entry.getKey();
System.out.print(age+":");
List<Student> group=entry.getValue();
for(Student stu:group)
System.out.print(stu.getName()+"");
System.out.println(); }}}
程序的运行结果为:
找出年龄为10的第一个学生:
Wang,10
找出年龄为10的所有学生:
Wang,10
Zhang,10
对学生按年龄分组:
10:Wang Zhang
13:Li
15:Zhao
此外,Stream类还提供了parallel、map、reduce等方法,这些方法用于增加对原生类并发处理的能力,有兴趣的读者可以参
考Java官方文档学习。 9)日期新特性。在JDK1.8以前,处理日期相关的类主要有如下三个: ①Calendar:实现日期和时间字
段之间转换,它的属性是可变的。因此,它不是线程安全的。 ②DateFormat:格式化和分析日期字符串。 ③Date:用来承载
日期和时间信息,它的属性是可变的。因此,它不是线程安全的。 这些API使用起来很不方便,而且有很多缺点,以如下代码
为例: Date date=new Date(2015,10,1); System.out.println(date); 在Date类传入参数中,月份为10月,但输出却为Mon
Nov 01 00:00:00 CST 3915。 JDK1.8对日期相关的API进行了改进,提供了更加强大的API。新的java.time主要包含了处理
日期、时间、日期/时间、时区、时刻(Instants)和时钟(Clock)等操作。下面给出一个使用示例:
import java.time.Clock;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Zoneld;
public class Test {
public static void main(String[] args) {
//Clock 类通过指定一个时区,可以获取到当前的时刻、日期与时间
Clock c=Clock.system(ZoneId.of("Asia/Shanghai"));//上海时区
System.out.println("测试 Clock: ");
System.out.println(c.millis());
System.out.println(c.instant());
//Instant 使用方法
System.out.println("测试 Instant: ");
Instant ist=Instant.now();
System.out.println(ist.getEpochSecond());//精确到秒
System.out.println(ist.toEpochMilli());//精确到毫秒
//LocalDate 以ISO-8601格式显示的日期类型,无时区信息
LocalDate date=LocalDate.now();
LocalDate dateFromClock=LocalDate.now(c);
System.out.println("测试LocalDate: ");
System.out.println(date);
System.out.println(dateFromClock);
//LocalYime是以ISO-8601格式显示时间类型,无时区信息
final LocalTime time=LocalTime.now();
final LocalTime timeFromClock=LocalYime.now(c);
System.out.println("测试LocalTime: ");
System.out.println(time);
System.out.println(timeFromClock);
//LocalDateTime 以ISO-8601格式显示的日期和时间
final LocalDateTime datetime=LocalDateTime.now();
final LocalDateTime datetimeFromClock=LocalDateTime.now(c);
System.out.println("测试LocalDateTime: ");
System.out.println(datetime);
System.out.println(datetimeFromClock);
}
}
程序的运行结果为:
测试Clock:
1445321400809
2015-10-20T06:10:00.809Z
测试Instant:
1445321400
1445321400821
测试LocaIDate:
2015-10-20
2015-10-20
测试LocalTime:
14:10:00.822
14:10:00.822
测试LocaIDateTime:
2015-10-20T14:10:00.822
2015-10-20T14:10:00.822
10)增加了调用JavaScript的引擎。JDK1.8增加API使得可以通过Java程序来调用JavaScript代码,使用示例如下:
import javax.script.*;
publicclass Test {
publicstaticvoid main(String[] args) throws ScriptException {
ScriptEngineManager manager=new ScriptEngineManager();
ScriptEngine engine=manager.getEngineByName("JavaScript");
System.out.println(engine.getClass().getName());
System.out.println(engine.eval("function f() {return \'Hello\'; };
f()+\'world!\';"));
}
}
程序的运行结果为:
jdk.nashorn.api.scripting.NashornScriptEngine
Hello world!
11)Base64。Base64编码是一种常见的字符编码,可用来作为电子邮件或Web Service附件的传输编码。JDK1.8把Base64编
码添加到了标准类库中。示例代码如下:
import java.nio.charset.StandardCharsets;
importjava.util.Base64;
public class Test {
public static void main(String[] args) {
String str="Hello world";
String encodStr=Base64.getEncoder().encodeToString(str.getBytes(StandardCharsets.UTF_8());
System.out.println(encodStr);
String decodStr=new String(Base64.getDecoder().decode(encodStr),StandardCharsets.UTF_8);
System.out.println(decodStr);
}
}
程序的运行结果为:
SGVsbG8gd29ybGQ=
Hello world
12)并行数组。JDK1.8增加了对数组并行处理的方法(parallelXxx),下面以排序为例介绍其用法。
import java.util.Arrays;
public class Test
{ public static void main(String[] args)
{ int[] arr={1,5,8,3,19,40,6};
Arrays.parallelSort(arr);
Arrays.stream(arr).forEach(i->System.out.print(i+" "));
System.out.println();
}
}

 

');