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中List
,Set
和Map
接口中加入了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.ProcessBuilder
,java.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.