Java8简介
摘自官文文档
Oracle的Java SE8包含了两个产品:Java SE开发套件(JDK)8和Java SE运行环境(JRE)8。
JDK8是JRE8的超集,它包含了JRE8中所含有的一切,以及开发applet和应用程序所需的编译器和调试器等工具。JRE8提供了运行用java程序语言编写的applets和程序所需要的类库,java虚拟机(jvm)和其他组件。需要注意的是,JRE包含Java SE规范不需要的组件,包括标准和非标准Java组件。
以下概念图说明了Oracle Java SE产品的组件:
Java概念图
Java7,Java8新增功能
摘自官方文档
Java7新增功能
- Binary Literals——在Java7中,整型(
byte
,short
,int
andlong
)也可以使用二进制数字进行表示。要指定二进制表示,只需在数字添加0b
或者0B
前缀。示例代码:
// An 8-bit 'byte' value:
byte aByte = (byte)0b00100001;
// A 16-bit 'short' value:
short aShort = (short)0b1010000101000101;
// Some 32-bit 'int' values:
int anInt1 = 0b10100001010001011010000101000101;
int anInt2 = 0b101;
int anInt3 = 0B101; // The B can be upper or lower case.
// A 64-bit 'long' value. Note the "L" suffix:
long aLong = 0b1010000101000101101000010100010110100001010001011010000101000101L;
- Underscores in Numeric Literals——任意数量的下划线‘_’可以出现在数字文本中的任意位置。这个特性可以让你对数字文本切割分组,这样可以提升代码的可读性。示例代码:
long creditCardNumber = 1234_5678_9012_3456L;
long socialSecurityNumber = 999_99_9999L;
float pi = 3.14_15F;
long hexBytes = 0xFF_EC_DE_5E;
long hexWords = 0xCAFE_BABE;
long maxLong = 0x7fff_ffff_ffff_ffffL;
byte nybbles = 0b0010_0101;
long bytes = 0b11010010_01101001_10010100_10010010;
注意!
你不能在如下场景下使用:
- 不能在文字的开头或结尾使用下划线!
- 不能与浮点数中的小数点相邻!
- 不能在L或F后缀之前使用!
- 不能在预期是一串数字的位置上使用!
示例代码:
float pi1 = 3_.1415F; // Invalid; cannot put underscores adjacent to a decimal point
float pi2 = 3._1415F; // Invalid; cannot put underscores adjacent to a decimal point
long socialSecurityNumber1
= 999_99_9999_L; // Invalid; cannot put underscores prior to an L suffix
int x1 = _52; // This is an identifier, not a numeric literal
int x2 = 5_2; // OK (decimal literal)
int x3 = 52_; // Invalid; cannot put underscores at the end of a literal
int x4 = 5_______2; // OK (decimal literal)
int x5 = 0_x52; // Invalid; cannot put underscores in the 0x radix prefix
int x6 = 0x_52; // Invalid; cannot put underscores at the beginning of a number
int x7 = 0x5_2; // OK (hexadecimal literal)
int x8 = 0x52_; // Invalid; cannot put underscores at the end of a number
int x9 = 0_52; // OK (octal literal)
int x10 = 05_2; // OK (octal literal)
int x11 = 052_; // Invalid; cannot put underscores at the end of a number
- Strings in switch Statements——可以在
switch
表达式中使用String
类型。示例代码:
public String getTypeOfDayWithSwitchStatement(String dayOfWeekArg) {
String typeOfDay;
switch (dayOfWeekArg) {
case "Monday":
typeOfDay = "Start of work week";
break;
case "Tuesday":
case "Wednesday":
case "Thursday":
typeOfDay = "Midweek";
break;
case "Friday":
typeOfDay = "End of work week";
break;
case "Saturday":
case "Sunday":
typeOfDay = "Weekend";
break;
default:
throw new IllegalArgumentException("Invalid day of the week: " + dayOfWeekArg);
}
return typeOfDay;
}
- Type Inference for Generic Instance Creation——你可以使用一组空类型参数(<>)替换调用泛型类的构造函数所需的类型参数,只要编译器可以从上下文中推断出类型参数。示例代码:
//Java7之前
Map> myMap = new HashMap>();
//Java7
Map> myMap = new HashMap<>();
/*Java SE 7 supports limited type inference for generic instance creation;
you can only use type inference if the parameterized type of the constructor is
obvious from the context.*/
List list = new ArrayList<>();
list.add("A");
// The following statement should fail since addAll expects
// Collection extends String>
list.addAll(new ArrayList<>());
/*Note that the diamond often works in method calls;
however, it is suggested that you use the diamond primarily for variable declarations.*/
// The following statements compile:
List extends String> list2 = new ArrayList<>();
list.addAll(list2);
- Improved Compiler Warnings and Errors When Using Non-Reifiable Formal Parameters with Varargs Methods——Java7编译器会在那些有一个
non-reifiable
类型可变参数的方法或者构造器声明点生成一个警告。Java7采用编译选项-Xlint:varargs
和这些注解@SafeVarargs
,@SuppressWarnings({"unchecked", "varargs"})
来压制这个警告。
原文:The Java SE 7 complier generates a warning at the declaration site of a varargs method or constructor with a non-reifiable varargs formal parameter. Java SE 7 introduces the compiler option -Xlint:varargs and the annotations @SafeVarargs and @SuppressWarnings({"unchecked", "varargs"}) to suppress these warnings.
non-reifiable
类型是指在运行期无法完全获得的类型,例如ArrayList
原文:Most parameterized types, such as ArrayList
示例代码:
public class ArrayBuilder {
public static void addToList (List listArg, T... elements) {
for (T x : elements) {
listArg.add(x);
}
}
@SuppressWarnings({"unchecked", "varargs"})
public static void addToList2 (List listArg, T... elements) {
for (T x : elements) {
listArg.add(x);
}
}
@SafeVarargs
public static void addToList3 (List listArg, T... elements) {
for (T x : elements) {
listArg.add(x);
}
}
// ...
}
public class HeapPollutionExample {
// ...
public static void main(String[] args) {
// ...
ArrayBuilder.addToList(listOfStringLists, stringListA, stringListB);
ArrayBuilder.addToList2(listOfStringLists, stringListA, stringListB);
ArrayBuilder.addToList3(listOfStringLists, stringListA, stringListB);
// ...
}
}
- The
try-with-resources
Statement——try-with-resources
表达式是一个定义了一个或多个资源的try
表达式。一个资源指的是一个在程序结束时必须要调用close
方法进行关闭的对象。try
-with-resource表达式可以确保每个资源都会在表达式最后进行关闭。任何一个实现了java.lang.AutoCloseable
或者java.io.Closeable
接口的对象都可以看做一个资源。这些类,java.io.InputStream
,OutputStream
,Reader
,Writer
,java.sql.Connection
,Statement
,ResultSet
都可以在try-with-resources
表达式中被当做资源进行使用,因为它们都实现了AutoCloseable
接口。示例代码:
//定义一个资源
static String readFirstLineFromFile(String path) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
}
}
//定义多个资源
public static void writeToFileZipFileContents(String zipFileName, String outputFileName)
throws java.io.IOException {
java.nio.charset.Charset charset = java.nio.charset.StandardCharsets.US_ASCII;
java.nio.file.Path outputFilePath = java.nio.file.Paths.get(outputFileName);
// Open zip file and create output file with try-with-resources statement
try (
java.util.zip.ZipFile zf = new java.util.zip.ZipFile(zipFileName);
java.io.BufferedWriter writer = java.nio.file.Files.newBufferedWriter(outputFilePath, charset)
) {
// Enumerate each entry
for (java.util.Enumeration entries = zf.entries(); entries.hasMoreElements();) {
// Get the entry name and write it to the output file
String newLine = System.getProperty("line.separator");
String zipEntryName = ((java.util.zip.ZipEntry)entries.nextElement()).getName() + newLine;
writer.write(zipEntryName, 0, zipEntryName.length());
}
}
}
- Catching Multiple Exception Types and Rethrowing Exceptions with Improved Type Checking——一个简单
catch
块能够处理多种类型的异常。此外,与早期版本的Java相比,编译器会对重新抛出的异常执行更加精确的分析。这使你可以在方法声明的throws子句中指定更加具体的异常类型。示例代码:
//Handling More Than One Type of Exception
catch (IOException|SQLException ex) {
logger.log(ex);
throw ex;
}
// Rethrowing Exceptions with More Inclusive Type Checking
public void rethrowException(String exceptionName)
throws FirstException, SecondException {
try {
// ...
}
catch (Exception e) {
throw e;
}
}
Java8新增功能
- Lambda Expressions——使你能够封装单个行为单位并将其传递给其他代码。当一个进程结束或者遇到错误时,如果你想集合中的每一个元素都执行某一操作,你可以使用
lambda
表达式。以下几种特征支持使用lambda
表达式:- Method References
- Default Methods
- New and Enhanced APIs That Take Advantage of Lambda Expressions and Streams in Java SE 8
示例代码:
public class Person {
public enum Sex {
MALE, FEMALE
}
String name;
LocalDate birthday;
Sex gender;
String emailAddress;
public int getAge() {
// ...
}
public Calendar getBirthday() {
return birthday;
}
public static int compareByAge(Person a, Person b) {
return a.birthday.compareTo(b.birthday);
}}
//Reference to a Static Method
Arrays.sort(rosterAsArray, Person::compareByAge);
//Reference to an Instance Method of a Particular Object
class ComparisonProvider {
public int compareByName(Person a, Person b) {
return a.getName().compareTo(b.getName());
}
public int compareByAge(Person a, Person b) {
return a.getBirthday().compareTo(b.getBirthday());
}
}
ComparisonProvider myComparisonProvider = new ComparisonProvider();
Arrays.sort(rosterAsArray, myComparisonProvider::compareByName);
//Reference to a Constructor
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 rosterSetLambda = transferElements(roster, () -> { return new HashSet<>(); });
// or
Set rosterSet = transferElements(roster, HashSet::new);
// or
Set rosterSet = transferElements(roster, HashSet::new);
- Improved Type Inference——Java编译器利用目标类型来推断泛型方法调用的类型参数。表达式的目标类型是Java编译器所期望的数据类型,具体取决于表达式在哪里出现。举个例子,在Java7中,Java编译器通过赋值语句的目标类型来进行推断,而在Java8中,通过上下文中使用的目标类型进行类型推断。示例代码:
List stringList = new ArrayList<>();
stringList.add("A");
//在java7中这段会编译出错
stringList.addAll(Arrays.asList());
你可以在以下的Java教程中获取更多相关的信息:
1. Target Typing in Lambda Expressions
2. Type Inference
- Annotations on Java Types——可以将注释运用在Java类型的任何地方。与可插拔类型系统结合使用,可以对代码进行更强大的类型校验。示例代码:
//Class instance creation expression
new @Interned MyObject();
//Type cast
myString = (@NonNull String) str;
//implements clause
class UnmodifiableList implements
@Readonly List<@Readonly T> { ... }
//Thrown exception declaration
void monitorTemperature() throws
@Critical TemperatureException { ... }
这种形式的注释称为类型注释。更多信息请查阅 Type Annotations and Pluggable Type Systems。
- Repeating Annotations——在一个定义或类型上允许多次使用同一个注释。查阅更多资料可参考 Repeating Annotations in the new Annotations lesson in the Java Tutorial。示例代码:
@Schedule(dayOfMonth="last")
@Schedule(dayOfWeek="Fri", hour="23")
public void doPeriodicCleanup() { ... }
@Alert(role="Manager")
@Alert(role="Administrator")
public class UnauthorizedAccessException extends SecurityException { ... }
- Method Parameter Reflection——你可以通过使用 java.lang.reflect.Executable.getParameters方法来获取任一方法或构造函数形参的名字。(由于 Method类和Constructor类继承了Executable类,所以它们继承了
Executable.getParameters
方法。)然而.class
文件默认是不存储形参的名称。为了实际上能够让.class
文件存储形参的名称并且通过Reflection API
获取到形参的名称,我们需要在javac
编译源文件时加上-parameters
的选项。你可以参考 Obtaining Names of Method Parameters教程。示例代码:
public class Annotation {
private static final String fmt = "%24s: %s%n";
private String name;
private String method;
public Annotation(String name, String method){
this.name = name;
this.method = method;
}
public static void main(String[] args) {
Class extends Annotation> clazz = Annotation.class;
Constructor>[] constructors = clazz.getConstructors();
for(Constructor> constructor : constructors){
for(Parameter p : constructor.getParameters()){
printParameter(p);
}
}
}
public static void printParameter(Parameter p) {
System.out.format(fmt, "Parameter class", p.getType());
System.out.format(fmt, "Parameter name", p.getName());
System.out.format(fmt, "Modifiers", p.getModifiers());
System.out.format(fmt, "Is implicit?", p.isImplicit());
System.out.format(fmt, "Is name present?", p.isNamePresent());
System.out.format(fmt, "Is synthetic?", p.isSynthetic());
}
}
输出结果: