├── src └── main │ ├── resources │ ├── newFile.txt │ ├── file_1.txt │ └── file_2.txt │ └── java │ └── com │ └── company │ ├── common │ ├── FlightNotFoundException.java │ ├── Utils.java │ ├── FlightSchedule.java │ └── Flight.java │ ├── java17to20 │ ├── java19 │ │ └── notes.txt │ ├── java20 │ │ └── notes.txt │ ├── java17 │ │ ├── impl │ │ │ ├── Vehicle.java │ │ │ ├── Car.java │ │ │ └── Truck.java │ │ ├── subclassing │ │ │ ├── Lizard.java │ │ │ ├── Dog.java │ │ │ └── Mammal.java │ │ └── notes.txt │ └── java18 │ │ ├── doc.html │ │ ├── HttpHeadDemo.java │ │ ├── notes.txt │ │ ├── CharSetUpdate.java │ │ └── Utf8ByDefault.java │ ├── java11to16 │ ├── java13 │ │ └── notes.txt │ ├── java16 │ │ ├── StreamApi.java │ │ ├── records │ │ │ ├── Product.java │ │ │ └── RecordsDemo.java │ │ ├── patterns │ │ │ ├── PatternMatchingForInstanceof.java │ │ │ └── Book.java │ │ └── DateTimeFormatterApi.java │ ├── java12 │ │ ├── NewFilesMethod.java │ │ ├── CompactNumberFormatting.java │ │ └── NewStringMethod.java │ ├── java11 │ │ ├── httpclient │ │ │ ├── HttpClientBasicExample.java │ │ │ └── BasicLinkValidator.java │ │ ├── NewStringMethods.java │ │ └── NewFilesMethods.java │ ├── java15 │ │ └── TextBlocks.java │ └── java14 │ │ └── SwitchExpressions.java │ └── java8to10 │ ├── java9 │ ├── IThing.java │ ├── more_features.txt │ ├── CollectionFactoryMethods.java │ ├── process │ │ ├── ProcessId.java │ │ └── DestroyProcess.java │ ├── OptionalApi.java │ └── StreamApi.java │ └── java10 │ ├── OptionalApi.java │ └── LocalVarInference.java ├── .gitignore └── pom.xml /src/main/resources/newFile.txt: -------------------------------------------------------------------------------- 1 | some str -------------------------------------------------------------------------------- /src/main/resources/file_1.txt: -------------------------------------------------------------------------------- 1 | Lorem Ipsum -------------------------------------------------------------------------------- /src/main/resources/file_2.txt: -------------------------------------------------------------------------------- 1 | Lorem Ipsum -------------------------------------------------------------------------------- /src/main/java/com/company/common/FlightNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.company.common; 2 | 3 | public class FlightNotFoundException extends RuntimeException { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/com/company/java17to20/java19/notes.txt: -------------------------------------------------------------------------------- 1 | Either preview or incubator features: 2 | https://www.oracle.com/news/announcement/oracle-releases-java-19-2022-09-20/ -------------------------------------------------------------------------------- /src/main/java/com/company/java17to20/java20/notes.txt: -------------------------------------------------------------------------------- 1 | Either preview or incubator features: 2 | https://www.oracle.com/news/announcement/oracle-releases-java-20-2023-03-21/ -------------------------------------------------------------------------------- /src/main/java/com/company/java17to20/java17/impl/Vehicle.java: -------------------------------------------------------------------------------- 1 | package com.company.java17to20.java17.impl; 2 | 3 | public sealed interface Vehicle permits Car, Truck { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea 8 | *.iws 9 | *.iml 10 | *.ipr -------------------------------------------------------------------------------- /src/main/java/com/company/java11to16/java13/notes.txt: -------------------------------------------------------------------------------- 1 | Nothing interesting: 2 | - API update to ByteBuffer 3 | - Update to localization (support for new chars and emojis) 4 | - GC updates -------------------------------------------------------------------------------- /src/main/java/com/company/common/Utils.java: -------------------------------------------------------------------------------- 1 | package com.company.common; 2 | 3 | public class Utils { 4 | 5 | public static void print(Object s) { 6 | System.out.println(s); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/company/java17to20/java17/subclassing/Lizard.java: -------------------------------------------------------------------------------- 1 | package com.company.java17to20.java17.subclassing; 2 | 3 | // not permitted to extend Mammal 4 | public class Lizard /* extends Mammal */ { 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/company/java17to20/java17/impl/Car.java: -------------------------------------------------------------------------------- 1 | package com.company.java17to20.java17.impl; 2 | 3 | // must be final or non-sealed. Otherwise, won't compile 4 | public final class Car implements Vehicle { 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/company/java17to20/java17/subclassing/Dog.java: -------------------------------------------------------------------------------- 1 | package com.company.java17to20.java17.subclassing; 2 | 3 | // must be final or non-sealed or won't compile 4 | public final class Dog extends Mammal { 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/company/java17to20/java17/impl/Truck.java: -------------------------------------------------------------------------------- 1 | package com.company.java17to20.java17.impl; 2 | 3 | // must be final or non-sealed. Otherwise, won't compile 4 | public non-sealed class Truck implements Vehicle { 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/company/java17to20/java17/subclassing/Mammal.java: -------------------------------------------------------------------------------- 1 | package com.company.java17to20.java17.subclassing; 2 | 3 | // allow Dog. This sends a message that "Lizard" shouldn't extend it 4 | public sealed class Mammal permits Dog { 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/company/java17to20/java18/doc.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Title of the document 5 | 6 | 7 | 8 | This page can be served with Java's Simple Web Server using the "jwebserver" command 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/main/java/com/company/java8to10/java9/IThing.java: -------------------------------------------------------------------------------- 1 | package com.company.java8to10.java9; 2 | 3 | public interface IThing { 4 | 5 | double getPrice(); 6 | 7 | default double getPriceWithVat() { 8 | return calculateThing() * 1.2; 9 | } 10 | 11 | // private methods now allowed 12 | // use case - to be used in default methods 13 | private double calculateThing() { 14 | return 1; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/company/java11to16/java16/StreamApi.java: -------------------------------------------------------------------------------- 1 | package com.company.java11to16.java16; 2 | 3 | import java.util.List; 4 | import java.util.stream.Stream; 5 | 6 | public class StreamApi { 7 | 8 | public static void main(String[] args) { 9 | 10 | List ints = Stream.of(1, 2, 3) 11 | .filter(n -> n < 3) 12 | .toList(); // new, instead of the verbose .collect(Collectors.toList()) 13 | 14 | ints.forEach(System.out::println); 15 | 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/company/common/FlightSchedule.java: -------------------------------------------------------------------------------- 1 | package com.company.common; 2 | 3 | import java.util.List; 4 | 5 | public class FlightSchedule { 6 | 7 | 8 | public static List getFlights() { 9 | return List.of( 10 | // date format DD-MM-YYYY 11 | new Flight("Boston", "New York", "17-10-2023"), 12 | new Flight("Chicago", "New York", "15-10-2023"), 13 | new Flight("Houston", "Phoenix", "16-10-2023"), 14 | new Flight("Phoenix", "Detroit", "15-10-2023") 15 | ); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/company/java8to10/java9/more_features.txt: -------------------------------------------------------------------------------- 1 | 1) JShell: 2 | - A Read-Eval-Print Loop (REPL) tool to execute code snippets 3 | - Type "jshell" on the command line. Press Ctrl + D or type "/exit" to exit. 4 | 5 | ============================================ 6 | C:\>jshell 7 | | Welcome to JShell -- Version 17.0.1 8 | | For an introduction type: /help intro 9 | 10 | jshell> /exit 11 | | Goodbye 12 | ============================================ 13 | 14 | 15 | 2) Modular System: 16 | - Allows creating modular JAR files to create more efficient and secure applications. 17 | - The JVM could run with only those modules and APIs which are required by the application. 18 | 19 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.company 8 | Java-9-onwards 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 18 13 | 18 14 | UTF-8 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/main/java/com/company/java11to16/java16/records/Product.java: -------------------------------------------------------------------------------- 1 | package com.company.java11to16.java16.records; 2 | 3 | /** 4 | * params are called "Components" 5 | * want more fields - must add into the signature 6 | * Extending not allowed, implementing interfaces IS allowed 7 | */ 8 | public record Product(String name, int price) { 9 | 10 | // static fields allowed, but not non-static 11 | static String country = "US"; 12 | 13 | // constructor with all fields is generated 14 | 15 | // can add validation 16 | public Product { 17 | if(price < 0) { 18 | throw new IllegalArgumentException(); 19 | } 20 | } 21 | 22 | // possible to override auto-generated methods like toString() 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/company/java11to16/java16/patterns/PatternMatchingForInstanceof.java: -------------------------------------------------------------------------------- 1 | package com.company.java11to16.java16.patterns; 2 | 3 | import java.util.Set; 4 | 5 | import static com.company.common.Utils.print; 6 | 7 | public class PatternMatchingForInstanceof { 8 | 9 | public static void main(String[] args) { 10 | 11 | Object o = new Book("Harry Potter", Set.of("Jon Doe")); 12 | 13 | // old way 14 | if (o instanceof Book) { 15 | Book book = (Book) o; 16 | print("The book's author(s) are " + book.getAuthors()); 17 | } 18 | 19 | // new way 20 | if (o instanceof Book book) { 21 | print("The book's author(s) are " + book.getAuthors()); 22 | } 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/company/java17to20/java18/HttpHeadDemo.java: -------------------------------------------------------------------------------- 1 | package com.company.java17to20.java18; 2 | 3 | import java.io.IOException; 4 | import java.net.URI; 5 | import java.net.http.HttpClient; 6 | import java.net.http.HttpRequest; 7 | import java.net.http.HttpResponse; 8 | 9 | import static com.company.common.Utils.print; 10 | 11 | public class HttpHeadDemo { 12 | 13 | /** 14 | * HEAD() convenience method added 15 | */ 16 | public static void main(String[] args) throws IOException, InterruptedException { 17 | HttpRequest head = HttpRequest.newBuilder(URI.create("https://api.github.com/")) 18 | .HEAD() 19 | .build(); 20 | 21 | var response = HttpClient.newHttpClient().send(head, HttpResponse.BodyHandlers.ofString()); 22 | 23 | print(response); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/company/java8to10/java10/OptionalApi.java: -------------------------------------------------------------------------------- 1 | package com.company.java8to10.java10; 2 | 3 | import com.company.common.Flight; 4 | import com.company.common.FlightNotFoundException; 5 | import com.company.common.FlightSchedule; 6 | 7 | import java.util.Optional; 8 | 9 | import static java.util.Comparator.comparing; 10 | 11 | public class OptionalApi { 12 | 13 | /** 14 | * new .orElseThrow() 15 | */ 16 | public static void main(String[] args) { 17 | 18 | Optional earliestFlight = FlightSchedule.getFlights() 19 | .stream() 20 | .filter(f -> "Boston".equals(f.from())) 21 | .filter(f -> "San Francisco".equals(f.to())) 22 | .min(comparing(Flight::date)); 23 | 24 | earliestFlight.orElseThrow(FlightNotFoundException::new); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/company/java17to20/java18/notes.txt: -------------------------------------------------------------------------------- 1 | Other additions: 2 | 3 | 1) Better code snippets in JavaDoc: https://docs.oracle.com/en/java/javase/18/code-snippet/index.html 4 | 5 | 6 | 2) Simple Web Server 7 | 8 | https://docs.oracle.com/en/java/javase/19/docs/specs/man/jwebserver.html 9 | 10 | Simplest way to get started: 11 | 1) open the terminal in this package (java18) 12 | 2) run "java -version" and make sure it is at least java 18 13 | 3) run the "jwebserver" command 14 | 15 | It should output: 16 | 17 | Binding to loopback by default. For all interfaces use "-b 0.0.0.0" or "-b ::". 18 | Serving path/to/your/subdirectory and subdirectories on 127.0.0.1 port 8000 19 | URL http://127.0.0.1:8000/ 20 | 21 | The HTML page is now served at: http://127.0.0.1:8000/java18/doc.html 22 | 23 | IP address, port and other parameters may be changed 24 | -------------------------------------------------------------------------------- /src/main/java/com/company/java11to16/java12/NewFilesMethod.java: -------------------------------------------------------------------------------- 1 | package com.company.java11to16.java12; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Files; 5 | import java.nio.file.Paths; 6 | 7 | import static com.company.common.Utils.print; 8 | 9 | public class NewFilesMethod { 10 | 11 | static String filePath = System.getProperty("user.dir") + "/src/main/resources/"; 12 | static String file_1 = filePath + "file_1.txt"; 13 | static String file_2 = filePath + "file_2.txt"; 14 | 15 | public static void main(String[] args) throws IOException { 16 | 17 | // Finds and returns the position of the first mismatched byte in the content of two files, 18 | // or -1L if there is no mismatch 19 | long result = Files.mismatch(Paths.get(file_1), Paths.get(file_2)); 20 | 21 | print(result); // -1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/company/java17to20/java18/CharSetUpdate.java: -------------------------------------------------------------------------------- 1 | package com.company.java17to20.java18; 2 | 3 | import java.io.FileWriter; 4 | import java.io.IOException; 5 | import java.nio.charset.Charset; 6 | import java.nio.charset.StandardCharsets; 7 | 8 | public class CharSetUpdate { 9 | 10 | public static void main(String[] args) throws IOException { 11 | 12 | // Before - this might throw an 'UnsupportedCharsetException' if the specified charset not present on the system 13 | Charset charSet = Charset.forName("windows-1252"); 14 | FileWriter writer = new FileWriter("out.txt", charSet); 15 | 16 | // Now - added .forName() overload that accepts a fallback 17 | // use 'UTF_8' if 'windows-1252' no present on the system 18 | Charset charSetWithFallback = Charset.forName("windows-1252", StandardCharsets.UTF_8); 19 | 20 | 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/company/java11to16/java12/CompactNumberFormatting.java: -------------------------------------------------------------------------------- 1 | package com.company.java11to16.java12; 2 | 3 | import java.text.NumberFormat; 4 | import java.util.Locale; 5 | 6 | import static com.company.common.Utils.print; 7 | import static java.text.NumberFormat.Style; 8 | import static java.text.NumberFormat.getCompactNumberInstance; 9 | 10 | public class CompactNumberFormatting { 11 | 12 | public static void main(String[] args) { 13 | NumberFormat fmt = getCompactNumberInstance(Locale.ENGLISH, Style.SHORT); 14 | print(fmt.format(100000)); // 100k 15 | print(fmt.format(1000000)); // 1M 16 | 17 | fmt = getCompactNumberInstance(Locale.FRENCH, Style.LONG); 18 | print(fmt.format(1000)); // 1 millier 19 | print(fmt.format(1000000)); // 1 million 20 | 21 | fmt = getCompactNumberInstance(Locale.forLanguageTag("RU"), Style.LONG); 22 | print(fmt.format(1000)); 23 | print(fmt.format(2000)); 24 | print(fmt.format(5000)); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/company/java11to16/java12/NewStringMethod.java: -------------------------------------------------------------------------------- 1 | package com.company.java11to16.java12; 2 | 3 | import java.util.Arrays; 4 | 5 | import static com.company.common.Utils.print; 6 | 7 | public class NewStringMethod { 8 | 9 | /** 10 | * indent(int i) and transform(String s) were added 11 | */ 12 | public static void main(String[] args) { 13 | 14 | print("hi"); 15 | print("hi".indent(5)); // also takes negative nums 16 | 17 | // transform - orderly transformation of date, 18 | // as opposed to writing code inside out 'words(clean(""))' 19 | String[] result = "some text $whatever$" 20 | .transform(NewStringMethod::clean) 21 | .transform(String::toUpperCase) 22 | .transform(NewStringMethod::words); 23 | 24 | print(Arrays.toString(result)); 25 | } 26 | 27 | public static String clean(String s) { 28 | return s.replaceAll("\\$", ""); 29 | } 30 | public static String[] words(String s) { 31 | return s.split(" "); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/company/java11to16/java11/httpclient/HttpClientBasicExample.java: -------------------------------------------------------------------------------- 1 | package com.company.java11to16.java11.httpclient; 2 | 3 | import java.net.URI; 4 | import java.net.http.HttpClient; 5 | import java.net.http.HttpRequest; 6 | import java.net.http.HttpResponse; 7 | 8 | import static com.company.common.Utils.print; 9 | 10 | public class HttpClientBasicExample { 11 | 12 | /** 13 | * Create a client, send a GET Request, print Response info 14 | */ 15 | public static void main(String... args) throws Exception { 16 | HttpClient client = HttpClient.newHttpClient(); 17 | 18 | HttpRequest request = 19 | HttpRequest.newBuilder(URI.create("https://github.com/")) 20 | .GET() // default, may be omitted 21 | .build(); 22 | 23 | HttpResponse response = 24 | client.send(request, HttpResponse.BodyHandlers.ofString()); 25 | 26 | print("Status code was: " + response.statusCode()); 27 | 28 | print(response.headers().map()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/company/common/Flight.java: -------------------------------------------------------------------------------- 1 | package com.company.common; 2 | 3 | import java.time.LocalDate; 4 | import java.time.format.DateTimeFormatter; 5 | 6 | public class Flight { 7 | 8 | private final String from; 9 | private final String to; 10 | private final LocalDate date; 11 | 12 | public Flight(String from, String to, String date) { 13 | this.from = from; 14 | this.to = to; 15 | this.date = parseInputDate(date); 16 | } 17 | 18 | private static LocalDate parseInputDate(String date) { 19 | return LocalDate.parse(date, DateTimeFormatter.ofPattern("dd-MM-yyyy")); 20 | } 21 | 22 | public String from() { 23 | return from; 24 | } 25 | 26 | public String to() { 27 | return to; 28 | } 29 | 30 | public LocalDate date() { 31 | return date; 32 | } 33 | 34 | @Override 35 | public String toString() { 36 | return "Flight{" + 37 | "from='" + from + '\'' + 38 | ", to='" + to + '\'' + 39 | ", date=" + date + 40 | '}'; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/company/java11to16/java11/NewStringMethods.java: -------------------------------------------------------------------------------- 1 | package com.company.java11to16.java11; 2 | 3 | import java.util.function.Predicate; 4 | import java.util.stream.Stream; 5 | 6 | import static com.company.common.Utils.print; 7 | 8 | public class NewStringMethods { 9 | 10 | public static void main(String[] args) { 11 | 12 | // repeat 13 | print("hello ".repeat(3)); 14 | 15 | // instead of "".equals(text.trim()) 16 | // or instead of str.trim().isEmpty() 17 | // https://stackoverflow.com/questions/3598770/check-whether-a-string-is-not-null-and-not-empty 18 | print(" ".isBlank()); 19 | 20 | // strip() slightly different from trim() 21 | print("text \u2005".strip()); 22 | 23 | // lines() 24 | // instead of ugly BufferedReader or other older solutions 25 | Stream linesFromString = "1\n 2 \n 3".lines(); 26 | linesFromString.forEach(System.out::print); 27 | 28 | // predicate::not 29 | linesFromString 30 | .filter(Predicate.not(String::isBlank)) 31 | .forEach(System.out::println); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/company/java17to20/java18/Utf8ByDefault.java: -------------------------------------------------------------------------------- 1 | package com.company.java17to20.java18; 2 | 3 | import java.io.FileWriter; 4 | import java.io.IOException; 5 | import java.nio.charset.StandardCharsets; 6 | 7 | public class Utf8ByDefault { 8 | 9 | // https://openjdk.org/jeps/400 - Platform Default Encoding 10 | 11 | public static void main(String[] args) throws IOException { 12 | 13 | // Problem: 14 | // 1) On Windows, use the below FileWriter to write characters outside the ASCII table, e.g. some exotic Unicode chars, without explicitly specifying a char set 15 | // 2) Copy or transfer the file to a UNIX-based OS, like a Mac, and read the file using the default char encoding of the system 16 | // 3) Likely result - garbled output 17 | // Hence the problem - unpredictable behavior. 18 | FileWriter writer = new FileWriter("out.txt"); 19 | 20 | // Solution before Java 18: always specify the charset, (and good luck not forgetting it!) 21 | FileWriter writer2 = new FileWriter("out.txt", StandardCharsets.UTF_8); 22 | 23 | // Solution since Java 18: UTF-8 is now default, so no need to specify the Char set 24 | 25 | 26 | } 27 | 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/company/java11to16/java11/NewFilesMethods.java: -------------------------------------------------------------------------------- 1 | package com.company.java11to16.java11; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Files; 5 | import java.nio.file.Path; 6 | import java.nio.file.Paths; 7 | import java.nio.file.StandardOpenOption; 8 | 9 | import static com.company.common.Utils.print; 10 | 11 | public class NewFilesMethods { 12 | 13 | static String filePath = System.getProperty("user.dir") + "/src/main/resources/"; 14 | static String file_1 = filePath + "file_1.txt"; 15 | 16 | /** 17 | * Files.readString() and .writeString() 18 | */ 19 | public static void main(String[] args) throws IOException { 20 | 21 | // reading files is much easier now 22 | // not to be used with huge files 23 | Path path = Paths.get(file_1); 24 | String content = Files.readString(path); 25 | print(content); 26 | 27 | 28 | Path newFile = Paths.get(filePath + "newFile.txt"); 29 | if(!Files.exists(newFile)) { 30 | Files.writeString(newFile, "some str", StandardOpenOption.CREATE); 31 | } else { 32 | Files.writeString(newFile, "some str", StandardOpenOption.TRUNCATE_EXISTING); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/company/java11to16/java16/DateTimeFormatterApi.java: -------------------------------------------------------------------------------- 1 | package com.company.java11to16.java16; 2 | 3 | import java.time.LocalDateTime; 4 | import java.time.format.DateTimeFormatter; 5 | import java.time.format.DateTimeFormatterBuilder; 6 | import java.time.format.TextStyle; 7 | import java.util.Locale; 8 | import java.util.Map; 9 | 10 | import static com.company.common.Utils.print; 11 | 12 | public class DateTimeFormatterApi { 13 | 14 | static Map map = Map.of( 15 | TextStyle.FULL, Locale.US, 16 | TextStyle.SHORT, Locale.FRENCH, 17 | TextStyle.NARROW, Locale.GERMAN 18 | ); 19 | 20 | public static void main(String[] args) { 21 | 22 | for (var entry : map.entrySet()) { 23 | 24 | LocalDateTime now = LocalDateTime.now(); 25 | DateTimeFormatter formatter = new DateTimeFormatterBuilder() 26 | .appendPattern("yyyy-MM-dd hh:mm ") 27 | .appendDayPeriodText(entry.getKey()) // at night, du soir, abends, etc. 28 | .toFormatter(entry.getValue()); 29 | 30 | String formattedDateTime = now.format(formatter); 31 | print(formattedDateTime); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/company/java8to10/java10/LocalVarInference.java: -------------------------------------------------------------------------------- 1 | package com.company.java8to10.java10; 2 | 3 | import java.io.BufferedReader; 4 | import java.net.http.HttpClient; 5 | import java.util.List; 6 | 7 | public class LocalVarInference { 8 | 9 | /** 10 | * Allowed: only as a local variable 11 | * Not allowed: anywhere else (class field, method param, etc.) 12 | * User var responsibly! 13 | * 14 | * Use: 15 | * - when it's clear what the type is (string, int) 16 | * - to shorten very long ugly types 17 | * 18 | * Don't use: 19 | * - returned value is unclear (var data = service.getData();) 20 | */ 21 | 22 | public static void main(String[] args) { 23 | 24 | // allowed, but brings little benefit 25 | var b = "b"; 26 | var c = 5; // int 27 | var d = 5.0; // double 28 | var httpClient = HttpClient.newHttpClient(); 29 | 30 | // one hell of an inference :) 31 | var list = List.of(1, 2.0, "3"); 32 | 33 | // the benefit becomes more evident with types with long names 34 | var reader = new BufferedReader(null); 35 | // vs. 36 | BufferedReader reader2 = new BufferedReader(null); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/company/java11to16/java16/patterns/Book.java: -------------------------------------------------------------------------------- 1 | package com.company.java11to16.java16.patterns; 2 | 3 | import java.util.Objects; 4 | import java.util.Set; 5 | 6 | public class Book { 7 | 8 | private final String title; 9 | private final Set authors; 10 | 11 | public Book(String title, Set authors) { 12 | this.title = title; 13 | this.authors = authors; 14 | } 15 | 16 | public String getTitle() { 17 | return title; 18 | } 19 | 20 | public Set getAuthors() { 21 | return authors; 22 | } 23 | 24 | @Override 25 | public boolean equals(Object o) { 26 | if (this == o) return true; 27 | if (o == null || getClass() != o.getClass()) return false; 28 | Book book = (Book) o; 29 | return Objects.equals(getTitle(), book.getTitle()) && Objects.equals(getAuthors(), book.getAuthors()); 30 | } 31 | 32 | @Override 33 | public int hashCode() { 34 | return Objects.hash(getTitle(), getAuthors()); 35 | } 36 | 37 | @Override 38 | public String toString() { 39 | return "Book{" + 40 | "title='" + title + '\'' + 41 | ", authors=" + authors + 42 | '}'; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/company/java11to16/java16/records/RecordsDemo.java: -------------------------------------------------------------------------------- 1 | package com.company.java11to16.java16.records; 2 | 3 | import static com.company.common.Utils.print; 4 | 5 | /** 6 | * Record are data-only immutable classes (thus have specific use cases) 7 | * They are a restricted (specialized) form of a class (like enums) 8 | * Not suitable for objects that are meant to change state, etc. 9 | *

10 | * Records are NOT: 11 | * - Boilerplate reduction mechanism 12 | *

13 | * Records generate constructors, getters, fields; equals, hashCode, toString 14 | *

15 | * Use cases: 16 | * - to model immutable data 17 | * - to hold read-only data in memory 18 | * - DTOs - Data Transfer Objects 19 | */ 20 | public class RecordsDemo { 21 | 22 | public static void main(String[] args) { 23 | Product p1 = new Product("milk", 50); 24 | Product p2 = new Product("milk", 50); 25 | 26 | print(p1.price()); // without "get" prefix 27 | print(p1); // auto-generated toString() - Product[name=milk, price=50] 28 | 29 | print(p1 == p2); // false - different objects 30 | print(p1.equals(p2)); // true - values of fields (milk, 50) are compared by the auto-generated equals()/hashCode() 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/company/java8to10/java9/CollectionFactoryMethods.java: -------------------------------------------------------------------------------- 1 | package com.company.java8to10.java9; 2 | 3 | import java.util.*; 4 | 5 | import static java.util.Map.entry; 6 | 7 | public class CollectionFactoryMethods { 8 | 9 | /** 10 | * New factory .of() methods 11 | */ 12 | public static void main(String[] args) { 13 | 14 | // new factory methods - create unmodifiable (!) collections 15 | // i.e. set(), add() and other modifying methods throw UnsupportedOperationException 16 | List list = List.of(1, 2, 3); 17 | Set set = Set.of("a", "b"); 18 | Map map = Map.of("key1", "val1", "key2", "val2"); 19 | 20 | 21 | // Map.of() has 10 overloaded methods accepting up to 10 Key-Value pairs 22 | // To create a map with 11+ elements: 23 | Map test2 = Map.ofEntries( 24 | entry("key1", "val1"), 25 | entry("key2", "val2") 26 | // etc. 27 | ); 28 | 29 | 30 | // older ways 31 | List listOld = Arrays.asList(1, 2, 3); 32 | 33 | Map mapOld = new HashMap<>(); 34 | mapOld.put("key1", "val1"); 35 | mapOld.put("key2", "val2"); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/company/java8to10/java9/process/ProcessId.java: -------------------------------------------------------------------------------- 1 | package com.company.java8to10.java9.process; 2 | 3 | import java.time.Instant; 4 | import java.util.Comparator; 5 | 6 | import static com.company.common.Utils.print; 7 | 8 | public class ProcessId { 9 | 10 | /** 11 | * The new Process API that allows you to manage and control operating system processes 12 | */ 13 | public static void main(String... args) { 14 | 15 | long currentProcessId = ProcessHandle.current().pid(); 16 | System.out.printf("pid: %s ", currentProcessId); 17 | 18 | print("================================"); 19 | 20 | ProcessHandle.allProcesses() 21 | .map(ProcessHandle::info) 22 | .sorted(Comparator.comparing(info -> 23 | info.startInstant().orElse(Instant.MAX))) 24 | .limit(10) 25 | .forEach(ProcessId::printInfo); 26 | 27 | } 28 | 29 | private static void printInfo(ProcessHandle.Info info) { 30 | if (info.startInstant().isPresent() && info.command().isPresent() && info.user().isPresent()) { 31 | print("Started at: " + info.startInstant().get() + 32 | ", Command: " + info.command().get() + 33 | ", By user: " + info.user().get() 34 | ); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/main/java/com/company/java8to10/java9/OptionalApi.java: -------------------------------------------------------------------------------- 1 | package com.company.java8to10.java9; 2 | 3 | import com.company.common.Flight; 4 | import com.company.common.FlightSchedule; 5 | 6 | import java.util.Optional; 7 | 8 | import static com.company.common.Utils.print; 9 | import static java.util.Comparator.comparing; 10 | 11 | public class OptionalApi { 12 | 13 | /** 14 | * Two new methods on Optional 15 | * ifPresentOrElse(if present - do this, else do that) 16 | * or(substitute optional of the same type) 17 | */ 18 | public static void main(String[] args) { 19 | 20 | // get me the earliest flight 21 | Optional earliestFlight = 22 | FlightSchedule.getFlights() 23 | .stream() 24 | .filter(f -> "Boston".equals(f.from())) 25 | .filter(f -> "San Francisco".equals(f.to())) 26 | .min(comparing(Flight::date)); 27 | 28 | 29 | earliestFlight.ifPresentOrElse(x -> print("Flight found: " + earliestFlight), 30 | () -> print("Didn't find any flights for your search")); 31 | 32 | 33 | Optional alternative = earliestFlight.or( 34 | () -> Optional.of(new Flight("A", "B", "17-10-2023")) 35 | ); 36 | print(alternative.get()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/company/java17to20/java17/notes.txt: -------------------------------------------------------------------------------- 1 | https://openjdk.org/jeps/409 2 | 3 | A sealed class or interface can be extended or implemented only by those classes and interfaces permitted to do so. 4 | 5 | Benefits: 6 | 1) help enforce a well-defined and limited set of possible implementations - communicates INTENTION 7 | 2) Better security - help prevent unexpected or unauthorized subclassing and behavior from third-party code 8 | 9 | Rules: 10 | 1. A sealed class uses "permits" to allow other classes to subclass it. 11 | 2. A child class MUST either be final, sealed or non-sealed. (or code won't compile) 12 | 3. A permitted child class MUST extend the parent sealed class. Permitting without using the permit is now allowed. 13 | 4. The classes specified by permits must be located near the superclass: 14 | either in the same module (if the superclass is in a named module) (see Java 9 modularity) 15 | or in the same package (if the superclass is in the unnamed module). 16 | 17 | More on point 4: 18 | The motivation is that a sealed class and its (direct) subclasses are tightly coupled, since they must be compiled and maintained together. 19 | In a modular world, this means "same module"; in a non-modular world, the best approximation for this is "same package". 20 | If you use modules, you get some additional flexibility, because of the safety boundaries modules give you. -------------------------------------------------------------------------------- /src/main/java/com/company/java8to10/java9/process/DestroyProcess.java: -------------------------------------------------------------------------------- 1 | package com.company.java8to10.java9.process; 2 | 3 | import static com.company.common.Utils.print; 4 | 5 | public class DestroyProcess { 6 | 7 | /** 8 | * On Windows: start the native Notepad app and run this program 9 | * For other Operating systems: open an equivalent app and change the .filter() line below 10 | */ 11 | public static void main(String... args) { 12 | 13 | // 1) Find and print the process 14 | ProcessHandle notePadHandle = 15 | ProcessHandle.allProcesses() 16 | .filter( 17 | h -> h.info().command().map(s -> s.contains("Notepad.exe")).orElse(false) 18 | ) 19 | .findFirst() 20 | .orElseThrow(() -> new RuntimeException("No matching handle found")); 21 | 22 | print(notePadHandle.info()); 23 | 24 | // 2) Set up a listener in advance - if the process exits - inform us! 25 | notePadHandle.onExit() // Returns an async CompletableFuture (Promise) 26 | .thenAccept(h -> print("Notepad.exe was killed by Java!")); 27 | 28 | //3) End the process 29 | boolean shutdown = notePadHandle.destroy(); 30 | 31 | // to avoid a race condition, make the current thread wait for the CompletableFuture before this program exits 32 | notePadHandle.onExit().join(); 33 | print("Shutdown: " + shutdown); 34 | } 35 | } -------------------------------------------------------------------------------- /src/main/java/com/company/java11to16/java11/httpclient/BasicLinkValidator.java: -------------------------------------------------------------------------------- 1 | package com.company.java11to16.java11.httpclient; 2 | 3 | import java.io.IOException; 4 | import java.net.URI; 5 | import java.net.http.HttpClient; 6 | import java.net.http.HttpRequest; 7 | import java.net.http.HttpResponse; 8 | import java.net.http.HttpResponse.BodyHandlers; 9 | import java.util.Optional; 10 | 11 | public class BasicLinkValidator { 12 | 13 | private static HttpClient client; 14 | 15 | public static void main(String[] args) throws IOException { 16 | 17 | client = HttpClient.newHttpClient(); 18 | 19 | String links = """ 20 | https://github.com/andrejs-ps 21 | https://httpstat.us/404 22 | https://httpstat.us/500 23 | """; 24 | 25 | var invalidResponses = links.lines() 26 | .map(BasicLinkValidator::getResponse) 27 | .filter(Optional::isPresent) 28 | .map(Optional::get) 29 | .filter(r -> r.statusCode() >= 400) 30 | .toList(); 31 | 32 | invalidResponses.forEach(r -> 33 | System.out.printf("%s responded with %s\n", r.uri(), r.statusCode()) 34 | ); 35 | } 36 | 37 | private static Optional> getResponse(String link) { 38 | HttpRequest request = HttpRequest.newBuilder(URI.create(link)).build(); 39 | try { 40 | var response = client.send(request, BodyHandlers.discarding()); 41 | return Optional.of(response); 42 | } catch (IOException | InterruptedException e) { 43 | return Optional.empty(); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/company/java8to10/java9/StreamApi.java: -------------------------------------------------------------------------------- 1 | package com.company.java8to10.java9; 2 | 3 | import java.util.List; 4 | import java.util.function.Predicate; 5 | 6 | public class StreamApi { 7 | 8 | /** 9 | * New Stream API methods - takeWhile() and dropWhile() 10 | * They are like filter(), but with stop conditions 11 | */ 12 | public static void main(String[] args) { 13 | 14 | // filter (from Java 8) - filter through the entire stream 15 | List list = List.of(1, 2, 3, 7, 9, 2, 6); 16 | Predicate p = i-> i < 5; 17 | list.stream() 18 | .filter(p) 19 | .forEach(System.out::print); // 1232 20 | 21 | System.out.println(); 22 | 23 | // takeWhile - like filter(), but exit the stream as soon as the predicate does NOT match the 1st time 24 | list.stream() 25 | .takeWhile(p) 26 | .forEach(System.out::print); // 123 27 | 28 | System.out.println(); 29 | 30 | // dropWhile - remove elements while the given predicate returns true 31 | // and stops removing (but keeps going) when the predicate returns false for the 1st time 32 | list.stream() 33 | .dropWhile(p) 34 | .forEach(System.out::print); // 7926 35 | 36 | System.out.println(); 37 | // trick to extract a chunk of string 38 | String chunk = """ 39 | Lorem ipsum 40 | ========== 41 | This is the text 42 | That we care about 43 | ========== 44 | Lorem ipsum 45 | """; 46 | // task: extract the text between "==========" 47 | String separator = "=========="; 48 | chunk.lines() 49 | .dropWhile(l -> !l.contains(separator)) 50 | .skip(1) 51 | .takeWhile(l -> !l.contains(separator)) 52 | .forEach(System.out::println); 53 | } 54 | 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/company/java11to16/java15/TextBlocks.java: -------------------------------------------------------------------------------- 1 | package com.company.java11to16.java15; 2 | 3 | import static com.company.common.Utils.print; 4 | 5 | /** 6 | * Use cases for TextBlocks (What's New in Java 15 > Text Blocks in Practice) 7 | * - Blocks of text using markdown 8 | * - Testing, defining hard-coded JSON strings 9 | * - Simple templating 10 | */ 11 | public class TextBlocks { 12 | 13 | public static void main(String[] args) { 14 | oldStyle(); 15 | emptyBlock(); 16 | jsonBlock(); 17 | jsonMovedEndQuoteBlock(); 18 | jsonMovedBracketsBlock(); 19 | } 20 | 21 | private static void oldStyle() { 22 | print("******** Old style ********"); 23 | 24 | String text = "{\n" + 25 | " \"name\": \"John Doe\",\n" + 26 | " \"age\": 45,\n" + 27 | " \"address\": \"Doe Street, 23, Java Town\"\n" + 28 | "}"; 29 | print(text); 30 | } 31 | 32 | private static void emptyBlock() { 33 | print("******** Empty Block ********"); 34 | String text = """ 35 | """; 36 | print("|" + text + "|"); 37 | } 38 | 39 | private static void jsonBlock() { 40 | print("******** JSON Block ********"); 41 | 42 | String text = """ 43 | { 44 | "name": "John Doe", 45 | "age": 45, 46 | "address": "Doe Street, 23, Java Town" 47 | } 48 | """; // <-- no indentation if char is aligned with first " 49 | print(text); 50 | } 51 | 52 | private static void jsonMovedEndQuoteBlock() { 53 | print("******** Json Moved End Quote Block ********"); 54 | 55 | String text = """ 56 | { 57 | "name": "John Doe", 58 | "age": 45, 59 | "address": "Doe Street, 23, Java Town" 60 | } 61 | """; 62 | print(text); 63 | } 64 | 65 | private static void jsonMovedBracketsBlock() { 66 | print("******** Json Moved Brackets Block ********"); 67 | 68 | String text = """ 69 | { 70 | "name": "John Doe", 71 | "age": 45, 72 | "address": "Doe Street, 23, Java Town" 73 | } 74 | """; // <-- indented by 2 spaces as it is aligned with third " 75 | print(text); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/company/java11to16/java14/SwitchExpressions.java: -------------------------------------------------------------------------------- 1 | package com.company.java11to16.java14; 2 | 3 | import static com.company.common.Utils.print; 4 | 5 | /** 6 | * "Switch Expressions" (SE) instead of "Switch Statements" (SS) 7 | * (Both can be used, but SE is better than SS) 8 | */ 9 | public class SwitchExpressions { 10 | 11 | public static void main(String[] args) { 12 | 13 | oldStyleWithBreak(FruitType.APPLE); 14 | 15 | withSwitchExpression(FruitType.PEAR); 16 | 17 | switchExpressionWithReturn(FruitType.KIWI); 18 | 19 | switchWithYield(FruitType.PINEAPPLE); 20 | } 21 | 22 | // Old style is more verbose and error-prone (forgotten "break;" causes the switch to fall through) 23 | private static void oldStyleWithBreak(FruitType fruit) { 24 | print("==== Old style with break ===="); 25 | switch (fruit) { 26 | case APPLE, PEAR: 27 | print("Common fruit"); 28 | break; 29 | case PINEAPPLE, KIWI: 30 | print("Exotic fruit"); 31 | break; 32 | default: 33 | print("Undefined fruit"); 34 | } 35 | } 36 | 37 | private static void withSwitchExpression(FruitType fruit) { 38 | print("==== With switch expression ===="); 39 | switch (fruit) { 40 | case APPLE, PEAR -> print("Common fruit"); 41 | case PINEAPPLE -> print("Exotic fruit"); 42 | default -> print("Undefined fruit"); 43 | } 44 | } 45 | 46 | private static void switchExpressionWithReturn(FruitType fruit) { 47 | print("==== With return value ===="); 48 | 49 | // or just "return switch" right away 50 | String text = switch (fruit) { 51 | case APPLE, PEAR -> "Common fruit"; 52 | case PINEAPPLE -> "Exotic fruit"; 53 | default -> "Undefined fruit"; 54 | }; 55 | print(text); 56 | } 57 | 58 | /** 59 | * "Yield" is like "return" but with an important difference: 60 | * "yield" returns a value and exits the switch statement. Execution stays within the enclosing method 61 | * "return" exits the switch and the enclosing method 62 | */ 63 | // https://stackoverflow.com/questions/58049131/what-does-the-new-keyword-yield-mean-in-java-13 64 | private static void switchWithYield(FruitType fruit) { 65 | print("==== With yield ===="); 66 | String text = switch (fruit) { 67 | case APPLE, PEAR -> { 68 | print("the given fruit was: " + fruit); 69 | yield "Common fruit"; 70 | } 71 | case PINEAPPLE -> "Exotic fruit"; 72 | default -> "Undefined fruit"; 73 | }; 74 | print(text); 75 | } 76 | 77 | public enum FruitType {APPLE, PEAR, PINEAPPLE, KIWI} 78 | } --------------------------------------------------------------------------------