Java 9(2017年9月)

接口的私有函数

public interface Writable {
    String content();

    default void writeToFile(File file) {
        write(file.toPath());
    }

    default void writeToPath(Path path) {
        write(path);
    }
    // interface可以创建私有方法
    private void write(Path path) {
        try {
            Files.writeString(path, content());
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }
}
public class WritableImpl implements Writable{
    @Override
    public String content() {
        return "Hello World";
    }

    @Override
    public void writeToPath(Path path) {
        // implements不能调用 write(Path)
        System.out.println(content());
    }

}

构造器泛型/diamond运算符

        // Java 6
        List<Integer> result1 = new ArrayList<Integer>();

        // Java 7
        List<Integer> result2 = new ArrayList<>();
		
	// Java 9 支持匿名内部类了
	List<Integer> numbers = new ArrayList<>() {
  		public boolean add(Integer e) {
    		System.out.println("calling add");
    		return super.add(e);
  		}
	};

	numbers.add(1);

try-with-resources


class DbResource implements AutoCloseable {
  @Override
  public void close() {
    System.out.println("closing resource");
  }
}

private void doSomeWork() {
  work(getConnection(), getConnection());
}
// Java 8
private void work(DbResource dbResource1, DbResource dbResource2) {
  try (DbResource db1 = dbResource1; 
       DbResource db2 = dbResource2) {
    System.out.println("do some work");
  }   
  // do some work
  // closing resource
  // closing resource
}

private DbResource getConnection() {
  return new DbResource();
}
Java 9 
private void work(DbResource dbResource1,
 DbResource dbResource2) {
  try (dbResource1; dbResource2) {
	// 这里的变量默认变成了final 无法重新赋值
	// Variable used as a try-with-resources resource should be final or effectively final
	// dbResource1 = getConnection();
    	System.out.println("do some work");
  } 
}

Collection Factories

Java的9中ListSetMap接口中加入了of静态方法

Set<Integer> numbers = Set.of(1, 2, 3);
// [1, 2, 3]

List<String> cities = List.of("London", "Paris");
// [London, Paris]


numbers.add(4);
// java.lang.UnsupportedOperationException

List<Boolean> flags = List.of(false, true, null);
// java.lang.NullPointerException
Set<Long> numbers = Set.of(1L, 2L, 3L, 3L);
// java.lang.IllegalArgumentException: duplicate element: 3
Map<String, String> countryCapital = Map.of("FR", "Paris", "GB", "London", "ES", "Madrid");
String capitalOfSpain = countryCapital.get("ES");
// "Madrid"
Map<Integer, String> users = Map.of(1, null);
// java.lang.NullPointerException

Map<String, String> countryCapital = Map.of("FR", "Paris", "FR", "Paris", 
                                            "GB", "London", "ES", "Madrid");
// java.lang.IllegalArgumentException: duplicate key: FR  
Map<String, String> capitals = 
  Map.ofEntries(
      Map.entry("FR", "Paris"), Map.entry("GB", "London"), 
      Map.entry("ES", "Madrid"), Map.entry("IT", "Rome"),
      Map.entry("JP", "Tokyo"), Map.entry("IE", "Dublin"), 
      Map.entry("CH", "Bern"), Map.entry("DE", "Berlin"),
      Map.entry("AT", "Vienna"), Map.entry("PT", "Lisbon"), 
      Map.entry("LI", "Vaduz"), Map.entry("AD", "Andorra la Vella"));  

String capitalOfAustria = capitals.get("AT");
// "Vienna"
class User {
  private String name;
  User(String name) {
    this.name = name;
  }
  @Override
  public String toString() { return this.name; }
}

User userA = new User("Adam");
User userB = new User("Sarah");
List<User> users = List.of(userA, userB);
// [Adam, Sarah]


// changing name of userA  
userA.name = "John";
String firstUserName = users.get(0).name;
// John
// users contains: [John, Sarah] 

java.lang.Process

Java 9 新增java.lang.ProcessBuilderjava.lang.Process类,并添加了新接口java.lang.ProcessHandle
java.lang.ProcessHandl 提供了三种静态工厂方法来获取所有进程的句柄 (allProcesses()), 获取当前进程的句柄(current()), 以及获取特定进程的句柄 (of(long)).

ProcessHandle.allProcesses().forEach(ph -> {
  System.out.printf("PID: %d Info: %s\n", ph.pid(), ph.info());
});

long myPid = ProcessHandle.current().pid();

ProcessHandle.of(myPid).ifPresent(ph -> System.out.println(ph.info()));

java.lang.ProcessBuilder有一个新的方法startPipeline,用于创建新的进程Pipeline,该Pipeline将每个进程的输出直接发送给下一个进程执行。

 Path sampleFile = Paths.get("./sample.txt");
        Files.writeString(sampleFile, "Bat\nGoat\nApple\nDog\nFirst\nEat\nHide");
        File workingDir = Paths.get(".").toFile();
        ProcessBuilder cat = new ProcessBuilder()
                .command("cat", "sample.txt")
                .directory(workingDir);

        ProcessBuilder grep = new ProcessBuilder()
                .command("grep", "-v", "a")
                .directory(workingDir);

        ProcessBuilder sort = new ProcessBuilder()
                .command("sort", "-r")
                .directory(workingDir);

        List<Process> commands = ProcessBuilder.startPipeline(List.of(cat, grep, sort));

        Process last = commands.get(2);
        last.onExit().thenAccept(process -> {
            BufferedReader lineReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            lineReader.lines().forEach(System.out::println);
        }).join();


等效于

cat sample.txt | grep -v a | sort -r

java.util.Optional

Java 9java.util.Optional类添加了三个新方法。

ifPresentOrElse(Consumer,Runnable)

Optional<Boolean> flag = Optional.of(true);

flag.ifPresentOrElse(b -> System.out.printf("value is %b", b),
    () -> System.out.println("Optional has no value"));
// value is true

or(Supplier)

Optional<String> city1 = Optional.of("London");
Optional<String> city2 = Optional.of("Paris");

Optional<String> nextTrip = city1.or(() -> city2);
// Optional[London]

Optional<String> city3 = Optional.ofNullable(null);
nextTrip = city3.or(() -> city2);
// Optional[Paris]

stream()

  class User {
    private final String name;
    User(String name) {
      this.name = name;
    }
  }

  Optional<User> result = Optional.of(new User("Adam"));
  String name = result.stream().map(u -> u.name).collect(Collectors.joining());
  // "Adam"

  Optional<User> resultN = Optional.ofNullable(null);
  String nameN = resultN.stream().map(u -> u.name).collect(Collectors.joining());
  // ""

java.util.stream.Stream

新增了四个新方法

ofNullable(T)

String input = "John";
String output = Stream.ofNullable(input).map(String::toUpperCase).findFirst().orElse("");
// "John"

input = null;
output = Stream.ofNullable(input).map(String::toUpperCase).findFirst().orElse("");
// ""

iterate(T,Predicate,UnaryOperator)

//Infinite stream
Stream.iterate(1, i -> i += 1).limit(3).forEach(System.out::println);
// 1 2 3

//Java 9 iterate
Stream.iterate(1, i -> i < 4, i -> i += 1).forEach(System.out::println);
// 1 2 3

dropWhile(Predicate)

Stream.of(1, 2, 20, 3, 4, 15).dropWhile(n -> n < 10).forEach(System.out::println);
// 20 3 4 15

takeWhile(Predicate)

Stream.of(1, 2, 20, 3, 4, 15).takeWhile(n -> n < 10).forEach(System.out::println);
// 1 2

test.txt

:header:
  some header data
:body:
  the body
  more content
:footer:
  footer information
        Path input = Paths.get("./test.txt");
        Files.lines(input)
                .dropWhile(line -> !line.strip().equals(":body:"))
                .skip(1)
                .takeWhile(line -> !line.strip().equals(":footer:"))
                .map(String::strip)
                .collect(Collectors.toUnmodifiableList())
                .forEach(System.out::println);
            // the body
            // more content

java.util.Arrays

equals

int[] array1 = {1,2,3};
int[] array2 = {3,2,1};
int[] array3 = {1,2,3,4};

boolean isEqual = Arrays.equals(array1, array2);
// false

isEqual = Arrays.equals(array1, new int[]{1,2,3});
// true

isEqual = Arrays.equals(array1, array3);
// false

isEqual = Arrays.equals(array1, null);
// false
int[] array1 = {1,2,3};
int[] array3 = {1,2,3,4};

boolean isEqual = Arrays.equals(array1, 0, 3, array3, 0, 3);
// true
class User {
  private final String name;
  User(String name) {
    this.name = name;
  }
  String getName() { return this.name; }
}

User[] users1 = new User[]{new User("John"), new User("Sarah")};
User[] users2 = new User[]{new User("John"), new User("Sarah")};

boolean isEqual = Arrays.equals(users1, users2, Comparator.comparing(User::getName));
// true

compare

List<byte[]> numbers = new ArrayList<>();

numbers.add(new byte[]{9});
numbers.add(new byte[]{101});
numbers.add(new byte[]{1,2,3,4});
numbers.add(null);
numbers.add(new byte[]{7,7,7});
numbers.add(new byte[]{8,9});

numbers.sort((a, b) -> Arrays.compare(a, b));
/*
null
[1, 2, 3, 4]
[7, 7, 7]
[8, 9]
[9]
[101]
  */
Comparator<String> nullSafeStringComparator = Comparator
    .nullsFirst(String::compareToIgnoreCase);

class User implements Comparable<User> {
  private final String name;

  User(String name) {
    this.name = name;
  }

  String getName() {
    return this.name;
  }

  @Override
  public String toString() {
    return this.name;
  }

  @Override
  public int compareTo(User o) {
    return nullSafeStringComparator.compare(this.name, o.name);
  }
}

List<User[]> users = new ArrayList<>();

users.add(new User[] { new User("Walter"), new User("Sarah") });
users.add(new User[] { new User("Adam"), null, new User("Jane") });
users.add(new User[] { new User("Michael") });
users.add(null);

users.sort((a, b) -> Arrays.compare(a, b));

/*
null
[Adam, null, Jane]
[Michael]
[Walter, Sarah]
*/

mismatch

long[] numbers1 = new long[] { 1, 2, 3, 4 };
long[] numbers2 = new long[] { 1, 2, 5 };
int mismatchIndex = Arrays.mismatch(numbers1, numbers2);
// 2

mismatchIndex = Arrays.mismatch(new short[] { 1, 2 }, new short[] { 1, 2 });
// -1

// from and to index
mismatchIndex = Arrays.mismatch(
    new float[] { 1.1f, 2.2f, 3.3f }, 1, 3,
    new float[] { 5.5f, 2.2f, 3.3f }, 1, 3);
// -1
class User {
  private final String name;
  User(String name) {
    this.name = name;
  }
  String getName() { return this.name; }
}

User[] users1 = new User[]{new User("John"), new User("Sarah")};
User[] users2 = new User[]{new User("John"), new User("Anna")};

int mismatchIndex = Arrays.mismatch(users1, users2, Comparator.comparing(User::getName));
// 1

java.util.Objects

requireNonNullElse(T, T)

String userName = "Adam";
String output = Objects.requireNonNullElse(userName, "");
// "Adam"

userName = null;
output = Objects.requireNonNullElse(userName, "");
// ""

userName = "Sara";
output = Objects.requireNonNullElse(userName, null);
// "Sara"

userName = null;
output = Objects.requireNonNullElse(userName, null);
// java.lang.NullPointerException: defaultObj

requireNonNullElseGet(T, Supplier)

String userName = "Adam";
String output = Objects.requireNonNullElseGet(userName, () -> "<default>");
// "Adam"

userName = null;
output = Objects.requireNonNullElseGet(userName, () -> "<default>");
// <default>

output = Objects.requireNonNullElseGet(userName, () -> null);
// java.lang.NullPointerException: supplier.get()

output = Objects.requireNonNullElseGet(userName, null);
// java.lang.NullPointerException: supplier

checkIndex(int, int)

int index = 13;
int upperBound = 100;
index = Objects.checkIndex(index, upperBound);
// 13

index = Objects.checkIndex(-1, upperBound);
// java.lang.IndexOutOfBoundsException: Index -1 out of bounds for length 100

index = Objects.checkIndex(100, upperBound);
// java.lang.IndexOutOfBoundsException: Index 100 out of bounds for length 100

index = Objects.checkIndex(99, upperBound);
// 99

checkFromIndexSize(int, int, int)

int upperBound = 100;
int index = Objects.checkFromIndexSize(13, 20, upperBound);
// 13

index = Objects.checkFromIndexSize(-1, 20, upperBound);
// java.lang.IndexOutOfBoundsException: Range [-1, -1 + 20) out of bounds for length 100

index = Objects.checkFromIndexSize(100, 1, upperBound);
// java.lang.IndexOutOfBoundsException: Range [100, 100 + 1) out of bounds for length 100

index = Objects.checkFromIndexSize(90, 10, upperBound);
// 90

checkFromToIndex(int, int, int)

int upperBound = 100;
int index = Objects.checkFromToIndex(13, 20, upperBound);
// 13

index = Objects.checkFromToIndex(-1, 20, upperBound);
// java.lang.IndexOutOfBoundsException: Range [-1, 20) out of bounds for length 100

index = Objects.checkFromToIndex(100, 101, upperBound);
// java.lang.IndexOutOfBoundsException: Range [100, 101) out of bounds for length 100

index = Objects.checkFromToIndex(90, 100, upperBound);
// 90

Java 10(2018年3月)

Unmodifiable copies of collections

List,Set和Map接口中加入copyOf()方法

List<String> source = new ArrayList<>();
source.add("one");
source.add("two");

List<String> copy = List.copyOf(source);
// [one, two]

copy.add("three");
// java.lang.UnsupportedOperationException
Map<Integer, String> source = new HashMap<>();
source.put(1, "one");
source.put(2, "two");
    
Map<Integer, String> copy = Map.copyOf(source);

source.put(3, "three");
source.remove(1);
    
// source: {2=two, 3=three}
// copy: {2=two, 1=one}
class Counter {
    private int count;
    private Counter(int startCount) {
      this.count = startCount;
    }
    private void inc() { this.count++; }
    @Override
    public String toString() {
      return String.valueOf(this.count);
    }
}

List<Counter> source = new ArrayList<>();
Counter counter = new Counter(0);
source.add(counter);
    
List<Counter> copy = List.copyOf(source);
// [0]

counter.inc();

// source.get(0).count == 1
// copy.get(0).count == 1
Set<String> source = new HashSet<>();
source.add("one");
source.add(null);
   
Set<String> copy = Set.copyOf(source);
// java.lang.NullPointerException
Set<Integer> numbers = new HashSet<>();
numbers.add(1);
numbers.add(2);    
List<Integer> copy = List.copyOf(numbers);
// [1, 2]

Deque<Long> deque = new ArrayDeque();
deque.add(100L);
deque.add(200L);
Set<Long> copyOfDeque = Set.copyOf(deque);
// [100, 200]

Unmodifiable collection Collectors

Java 10中添加了四个新的Collector

  • toUnmodifiableList()
  • toUnmodifiableSet()
  • toUnmodifiableMap(Function,Function)
  • toUnmodifiableMap(Function,Function,BinaryOperator)
List<Integer> result = Stream.of(1, 2, 3, 4, 5).filter(n -> n % 2 == 0)
        .collect(Collectors.toUnmodifiableList());
// [2, 4]

result.add(6); // throws java.lang.UnsupportedOperationException
Set<Integer> numbers = Stream.of(1,2,2,3,3,4,4,4).filter(n -> n % 2 == 0)
        .collect(Collectors.toUnmodifiableSet());
// [4, 2]
Map<Integer,String> result = Stream.of("Paris", "London", "Rome")
        .collect(Collectors.toUnmodifiableMap(String::length, Function.identity()));
// {5=Paris, 6=London, 4=Rom}

Map<Integer,String> result = Stream.of("Paris", "London", "Rome", "Madrid")
        .collect(Collectors.toUnmodifiableMap(String::length, Function.identity()));
// java.lang.IllegalStateException: 
// Duplicate key 6 (attempted merging values London and Madrid)
Map<Integer, Set<String>> result = Stream.of("Paris", "London", "Rome", "Madrid")
        .collect(Collectors.toUnmodifiableMap(
            String::length,
            item -> new HashSet<>(Set.of(item)), 
            (existing, newValue) -> {
              existing.addAll(newValue);
              return existing;
            }));
    System.out.println(result);
    // {4=[Rom], 5=[Paris], 6=[Madrid, London]}

class Counter {
    private int count;
    private Counter(int startCount) {
      this.count = startCount;
    }
    private void inc() { this.count++; }
    @Override
    public String toString() {
      return String.valueOf(this.count);
    }
}

Counter zeroCounter = new Counter(0);
List<Counter> result = Stream.of(zeroCounter).collect(Collectors.toUnmodifiableList());
// [0]
zeroCounter.inc();
int c = result.get(0).count;  // 1

java.io.Reader

FileReader fr = new FileReader("test1.txt");
    FileWriter fw = new FileWriter("test2.txt");
    try (fr; fw) {
      fr.transferTo(fw);
    }
java.nio.file.Files.copy(Paths.get("test1.txt"), Paths.get("test2.txt"));

java.util.Optional

orElseThrow()

Optional<String> result = Optional.of("HelloWorld");
String value = result.orElseThrow(); // HelloWorld
   
result = Optional.ofNullable(null);
value = result.orElseThrow();
// java.util.NoSuchElementException: No value present

Java 11(2018年9月)

java.util.Collection

新增默认方法toArray(IntFunction)

List<String> cities = List.of("Rome", "Paris", "London");   
String[] citiesArray1 = cities.toArray(new String[cities.size()]);
Object[] citiesArray2 = cities.toArray();
   
//new in Java 11
String[] citiesArray3 = cities.toArray(String[]::new);

java.util.function.Predicate

java.util.function.Predicate<Integer> isEven = n -> n % 2 == 0;
java.util.function.Predicate<Integer> isOdd = Predicate.not(isEven);
    
List<Integer> evenNumbers = Stream.of(1,2,3,4,5,6).filter(isEven).collect(Collectors.toUnmodifiableList());
// [2, 4, 6]
    
List<Integer> oddNumbers = Stream.of(1,2,3,4,5,6).filter(isOdd).collect(Collectors.toUnmodifiableList());
// [1, 3, 5]

java.util.Optional

Optional<Integer> result = java.util.Optional.of(10);
boolean present = result.isEmpty(); // false

java.lang.String

isBlank()

boolean b = "Hello World".isBlank(); // false
b = "   \n   \t  ".isBlank(); // true

lines()

List<String> tokens = "Line 1\nLine 2\nLine 3".lines().collect(Collectors.toUnmodifiableList());
// [Line 1, Line 2, Line 3]

repeat(int)

String www = "World".repeat(3); // WorldWorldWorld

stripLeading()

String st = "\t\tHelloWorld\n\n".stripLeading(); // "HelloWorld\n\n"
st = "\t\tHelloWorld\n\n".stripTrailing(); // "\t\tHelloWorld"
st = "\t\tHelloWorld\n\n".strip(); // "HelloWorld"

java.nio.file.Files

Java 11向java.nio.file.Files该类引入了四个新方法。writeString(Path,CharSequence,Charset,OpenOption...)readString(Path, Charset)与两个重载变种两种方法writeString(Path,CharSequence,OpenOption...)readString(Path)。这些方法按照其名称的含义执行操作,将字符串写入文件,然后从文件读取字符串。

Path file1 = Paths.get("./test1.txt");
Files.writeString(file1, "Hello World");

Path file2 = Paths.get("./test2.txt");
Files.writeString(file2, "Hello World", StandardCharsets.UTF_8);
    
String input1 = Files.readString(file1); // Hello World
String input2 = Files.readString(file2, StandardCharsets.UTF_8); // Hello World

Lambda参数的局部变量语法

Predicate<Integer> predicate1 = n -> n % 2 == 0;
Predicate<Integer> predicate2 = (Integer n) -> n % 2 == 0;
Predicate<Integer> predicate3 = (var n) -> n % 2 == 0;
Predicate<Integer> predicate3 = (@Nonnull var n) -> n % 2 == 0;

Java 12(2019年3月)

CompactNumberFormat

java.text.NumberFormat shortFormat = NumberFormat.getCompactNumberInstance(Locale.ENGLISH, NumberFormat.Style.SHORT);
java.text.NumberFormat longFormat = NumberFormat.getCompactNumberInstance(Locale.ENGLISH, NumberFormat.Style.LONG);
    
String shortOutput = shortFormat.format(1_234); // 1K
String longOutput = longFormat.format(1_234); // 1 thousand
NumberFormat longFormat = NumberFormat.getCompactNumberInstance(Locale.ENGLISH, NumberFormat.Style.LONG);
longFormat.setRoundingMode(RoundingMode.UP);
String longOutput = longFormat.format(1_234); // 2 thousand
NumberFormat shortFormat = NumberFormat.getCompactNumberInstance(Locale.ENGLISH, NumberFormat.Style.SHORT);
NumberFormat longFormat = NumberFormat.getCompactNumberInstance(Locale.ENGLISH, NumberFormat.Style.LONG);

shortFormat.setMinimumFractionDigits(2);
longFormat.setMinimumFractionDigits(1);
    
String shortOutput = shortFormat.format(1_256); // 1.26K
String longOutput = longFormat.format(1_256); // 1.3 thousand
NumberFormat longFormat = NumberFormat.getCompactNumberInstance(Locale.GERMAN, NumberFormat.Style.LONG);
        
String input1 = "1,11 Tausend";
String input2 = "1,22 Million";
String input3 = "1,33 Milliarde";
    
Number n1 = longFormat.parse(input1); // 1110
Number n2 = longFormat.parse(input2); // 1220000
Number n3 = longFormat.parse(input3); // 1330000000

teeing Stream Collector

List<String> cities = List.of("Paris", "London", "Tokyo", "Madrid", "Rome");
List<Object> result = cities.stream().map(String::toUpperCase)
                        .collect(java.util.stream.Collectors.teeing(
                                    Collectors.counting(), 
                                    Collectors.toUnmodifiableList(), 
                                    (count, list) -> List.of(count, list))
                                );
    
long count = (long) result.get(0); // 5
List<String> citiesUC = (List<String>) result.get(1); // [PARIS, LONDON, TOKYO, MADRID, ROME]
List<String> cities = List.of("Paris", "London", "Tokyo", "Madrid", "Rome");

var stringLengthComparator = Comparator.comparing(String::length);

var minMaxCollector = Collectors.teeing(Collectors.minBy(stringLengthComparator),
        Collectors.maxBy(stringLengthComparator),
        (min, max) -> List.of(min.get(), max.get()));

var countListCollector = Collectors.teeing(Collectors.counting(),
        Collectors.toUnmodifiableList(), (count, list) -> List.of(count, list));

var result = cities.stream().map(String::toUpperCase).collect(
        Collectors.teeing(minMaxCollector, countListCollector, (minMax, countList) -> List
            .of(minMax.get(0), minMax.get(1), countList.get(0), countList.get(1))));

String min = (String) result.get(0); // ROME
String max = (String) result.get(1); // LONDON
long count = (long) result.get(2); // 5
List<String> list = (List<String>) result.get(3); // [PARIS, LONDON, TOKYO, MADRID, ROME]

java.nio.file.Files.mismatch

// file1.txt:Hello World
// file2.txt:Hello world

java.nio.file.Path file1 = java.nio.file.Paths.get("file1.txt");
java.nio.file.Path file2 = java.nio.file.Paths.get("file2.txt");
long diffPos = java.nio.file.Files.mismatch(file1, file2); // 6

java.lang.String

String basic = "10 PRINT \"Hello, World!\"\n" + 
               "20 END";
String indented = basic.indent(3);
/*
   10 PRINT "Hello, World!"
   20 END
*/  
String basic = " \t\t 10 PRINT \"Hello, World!\"\n" + 
                "\t20 END";
String indented = basic.indent(-3);
/*
 10 PRINT "Hello, World!"
20 END
*/
String hw = "Hello World";
int len2 = hw.transform(str -> str.length() * 2); // 22

String cities = "Tokyo;Madrid;London;Paris";
List<String> citiesList = cities.transform(str -> List.of(str.split(";")).stream()
                              .sorted().collect(Collectors.toUnmodifiableList()));
// [London, Madrid, Paris, Tokyo]

String text = "this is line 1\nthe second line";
String transformed = text.transform(str -> 
                      str.lines()
                      .map(line -> line.substring(0, 1).toUpperCase() + line.substring(1))
                      .collect(Collectors.joining("\n")));
//This is line 1\nThe second line

Java 13(2019年9月)

int i = switch (x) {
    case "1" -> 1;
    case "2" -> 2;
    default -> {
        int len = args[1].length();
        yield len;
    }
};
int i = switch (x) {
    case "1": yield 1;
    case "2": yield 2;
    default: {
        int len = args[1].length();
        yield len;
    }
};

Java 14(2020年3月)

Exact Arithmetic

int i = 2_147_483_647; // max value of Integer
i = i + 10;  // i == -2_147_483_639
int i = StrictMath.addExact(2_147_483_647, 10);
// java.lang.ArithmeticException: integer overflow
int i = 2_147_483_647;
i = StrictMath.decrementExact(i); // i--
i = StrictMath.incrementExact(i); // i++
i = StrictMath.negateExact(i); // i = -i

CompactNumberFormat

// Java 13
NumberFormat cnf = NumberFormat.getCompactNumberInstance(Locale.FRENCH, Style.LONG);
String output = cnf.format(1_000_000); // 1 million
output = cnf.format(2_000_000); // 2 million

// Java 14
NumberFormat cnf = NumberFormat.getCompactNumberInstance(Locale.FRENCH, Style.LONG);
String output = cnf.format(1_000_000); // 1 million
output = cnf.format(2_000_000); // 2 millions  <=======

Helpful NullPointerExceptions (JEP 358)

class Address {
     private final String city;
     private Address(String city) {
       this.city = city;
     }
   }
   class Person {
     private final Address address;
     private Person(Address address) {
       this.address = address;
     }     
   }
   Address a = new Address(null);
   Person p = new Person(a);
   String cityUpperCase = p.address.city.toUpperCase(); 
java.lang.NullPointerException: Cannot invoke "String.toUpperCase()" because "p.address.city" is null
  at ch.rasc.starter.M.go(M.java:26)
  at ch.rasc.starter.M.main(M.java:7)

Next: Java 15(2020年9月)

Q.E.D.


味无味处求吾乐,材不材间过此生。