标签:# Java_New_Future

Java 20~21 新特性

Java 20没有发布重要的新特性,本文以Java21版本为主。 Java 21 是新的LTS版本,其中发布了众多新的特性。 字符串模板(预览版) 使用官方的STR、FMT 支持通过STR、FMT来实现字符串拼接,可以自定义模板处理器组织字符串输出形式。 import java.time.LocalDate; import java.time.format.DateTimeFormatter; import static java.util.FormatProcessor.FMT; /** * @author imyzt * @date 2023/12/19 * @description 模板字符串 */ public class TemplateString { public static void main(String[] args) { String str = "world"; String result = STR."hello \{str}"; System.out.println(result); System.out.println(STR); String name = "yzt"; String[] blogAddress = {"imyzt.top", "blog.imyzt.top"}; String text = FMT.""" My name is \{name} My blog address is \{blogAddress[0].toUpperCase()}, \{blogAddress[1].toLowerCase()}"""; System.out.println(text); System.out.println(STR."\{Math.random()}"); System.out.println(STR."\{Integer.MAX_VALUE}"); System.out.println(STR."\{index++}, \{++index}"); //hello world //java.lang.StringTemplate$$Lambda/0x00000001260457f0@33c7353a //My name is yzt //My blog address is IMYZT.TOP, blog.imyzt.top //0.9361799484353136 //2147483647 //现在的时间是: 2023-12-20 //0, 2 } } 自定义StringTemplate import java.util.Iterator; /** * @author imyzt * @date 2023/12/20 * @description 自定义StringTemplate */ public class CustomTemplate { public static void main(String[] args) { var INTER = StringTemplate.Processor.of((StringTemplate st) -> { StringBuilder sb = new StringBuilder(); Iterator<String> fragments = st.fragments().iterator(); for (Object value : st.values()) { sb.append(fragments.next()); sb.append(value); } sb.append(fragments.next()); return sb.toString(); }); String name = "yzt"; int index = 0; String text = INTER.""" { "name":\{name}, "index":\{++index} } """; System.out.println(text); //{ //"name":yzt, //"index":1 //} } } Record Patterns 记录模式更强大了,支持直接在表达式中创建和使用。还支持嵌套 /** * @author imyzt * @date 2023/12/19 * @description 记录模式 */ public class RecordPatterns { public static void main(String[] args) { Object p = new Point(1, 2); if (p instanceof Point(int x, int y)) { System.out.println(x + y); } Object w = new Window(new Point(1, 2), 3); if (w instanceof Window(Point(int x, int y), int z)) { System.out.println(x + y + z); } } } record Point(int x, int y) {} record Window(Point p, int z) {} ScopedValue ScopedValue是一个隐藏的方法参数,只有方法可以访问ScopedValue,它可以让两个方法传递参数时无需声明形参(对于Web项目中传递用户信息是非常场景的操作) ThreadLocal的问题 通常对于上述场景,都会采用ThreadLocal来解决,但是由于ThreadLocal在设计上的瑕疵,导致一直有以下问题: 内存泄露,在使用完ThreadLocal之后,若没有调用remove方法,会出现内存泄漏。 增加开销,在具有继承关系的线程中,子线程需要为父线程中ThreadLocal里面的数据分配内存 不是不可变对象,在方法中可以随意调用set方法篡改。 随着虚拟线程的到来,内存泄漏问题不用担心了,因为虚拟线程会很快的终止,此时会自动删除ThreadLocal中的数据,这样就不用调用remove方法了。 但是虚拟线程的数量是非常多的,假如有上百万个虚拟线程都要拷贝一份ThreadLocal中的变量(问题2),内存将会被严重的浪费掉。 示例 示例代码中通过模拟一个“送礼物”的场景,来演示ScopedValue。 在 giveGift 方法中,将礼物送出,是500元 在 receiveMiddleMan 方法模拟中间人抽成的场景,抽成后送出仅 200元 在 receiveGift 方法中 ,对 ScopedValue 进行获取,得到的就是200元 但整个过程中,中间人 receiveMiddleMan 在送出前后获取到的信息都是 500元,这正是 Scope 的含义,修改只在本作用域(方法)中生效。 /** * @author imyzt * @date 2023/12/20 * @description ScopedValue */ public class ScopedValueTest { private static final ScopedValue<String> GIFT = ScopedValue.newInstance(); public static void main(String[] args) { ScopedValueTest test = new ScopedValueTest(); test.giveGift(); //中间人开始: 500 //收礼物: 200 //中间人结束: 500 } private void giveGift() { ScopedValue.where(GIFT, "500").run(this::receiveMiddleMan); } private void receiveMiddleMan() { System.out.println(STR."中间人开始: \{GIFT.get()}"); ScopedValue.where(GIFT, "200").run(this::receiveGift); System.out.println(STR."中间人结束: \{GIFT.get()}"); } private void receiveGift() { System.out.println(STR."收礼物: \{GIFT.get()}"); } } Switch 又升级了,支持when关键字了 switch 可谓是升级升级再升级,我还是在用 if/else 啊。 从12开始,一路升级。 支持 -> 表达式(Java 12) 支持返回值(Java 12) case支持单行写多个条件(Java 12) 新增yield关键字返回switch的值(Java 12) 引入模式匹配(Java 15) 这回又新增了一个when关键字(都快改成SQL了) (Java 21) Java switch升级之路 /** * @author imyzt * @date 2023/12/20 * @description Switch 又升级了~, 这次支持when关键字了 */ public class SwitchFuture { void main() { var str = "yes"; var result = switch (str) { case null -> "空对象"; case String s when "yes".equals(s) -> { System.out.println("确定"); yield "字符串的Yes"; } case String s when "no".equals(s) -> { System.out.println("取消"); yield "字符串的No"; } default -> "default"; }; System.out.println(result); //确定 //字符串的Yes } } 简化了main方法(注意看上一个章节的main方法) Class声明和强制的public访问修饰符是必须的。当用在外部组件定义良好的接口封装代码单元时,它们很有用。但在这个小例子中,它们毫无意义。 String[]参数主要用于将代码与外部组件(在本例中为操作系统的shell,接收命令传入的参数)连接。它在这里很神秘且无用,尤其是它从未被使用过。 static修饰符是Java类和对象模型的一部分。对于新手来说,这不仅是神秘的,而且是有害的:要添加更多可以调用和使用的方法或字段,学​​生必须要么将它们全部声明(传播一种既不常见也不是好习惯的用法),或者就要面对是否有static修饰的区别问题,并学习如何实例化对象。 未命名模式和变量(Go:和我有点像) /** * @author imyzt * @date 2023/12/21 * @description 像golang学习, 支持不使用的变量不命名了 */ public class UnnamedVariable { void main() { List<String> list = new ArrayList<>(); list.add("first"); list.add("last"); try { System.out.println(STR."first -> \{list.getFirst()}"); System.out.println(STR."last -> \{list.getLast()}"); System.out.println("try"); } catch (Exception _) { System.out.println("异常了, 但是我没有 Exception"); } } } 有序集合 Sequenced Collections Sequenced Collections引入了三个新接口: SequencedCollection SequencedMap SequencedSet 接口都附带了一些新方法,可以提高操作集合的效率。比如:java.util.SequencedCollection#reversed等等。 集合补充了获取收尾元素的方法(hutool:当我不存在?) List<String> list = new ArrayList<>(); list.add("first"); list.add("last"); System.out.println(STR."first -> \{list.getFirst()}"); System.out.println(STR."last -> \{list.getLast()}"); 加了一些工具类 StringBuilder中的repeat方法 补充java.lang.Character#isEmoji 等等 /** * @author imyzt * @date 2023/12/21 * @description 加了些新方法 */ public class SomethingExample { void main() { System.out.println(STR."repeat => \{new StringBuffer().repeat("*", 10)}"); //repeat => ********** var happy = "你开心吗? 😄"; System.out.println(STR."isEmoji => \{happy.codePoints().anyMatch(Character::isEmoji)}"); //isEmoji => true } }
Read More ~

Java 19 新特性

Java 19 最核心的特性就是虚拟线程(Virtual Threads) 简介 该特性在Java19中是预览特性,虚拟线程是用户态下的线程,和go语言中的goroutines,Erlang中的processes类似,虚拟线程并非比线程快,而是提高了应用的吞吐量,相比于传统的线程是由操作系统调度来看,虚拟线程是我们自己程序调度的线程。虚拟线程的出现并没有修改Java原有的并发模型,也不会替代原有的线程,虚拟线程主要作用是提高服务器端的吞吐量(主要解决IO密集型而非CPU密集型任务) 吞吐量的瓶颈 服务器应用程序的伸缩性受利特尔法则(Little's Law)的制约,与下面三点有关: 延迟:请求处理的耗时 并发量:同一时刻处理的请求数量 吞吐量:单位时间内处理的数据数量 如果一个服务处理延迟是50ms,处理10个并发请求,则吞吐量是200请求/秒(10/0.05),如果吞吐量要达到2000请求/秒,则处理的并发请求数量是100. 如果按照1个请求一个线程来看,要想提高吞吐量,线程数量也要增加。 Java中的线程在操作系统线程(OS Thread)进行了一层封装,而操作系统重线程是重量级资源,在硬件配置确定的前提下,不能无限制创建线程。 与虚拟地址可以映射到物理内存类似,Java将大量的虚拟线程映射到少量的操作系统线程上,虚拟线程的生命周期短暂,不会有很深的栈调用,一个虚拟线程的生命周期只运行一个任务,因此可以大量创建虚拟线程,且无需池化。 虚拟线程的应用场景 在服务器端的应用程序中,虚拟线程能够明显提高应用的吞吐量: 至少几千的并发任务量 任务是IO密集型 平台线程和虚拟线程 平台线程(platform thread):指Java中的线程,比如通过new Thread()创建的线程。 虚拟线程并不会直接分配给CPU执行,而是通过调度器分配给平台线程,平台线程再被调度器管理。Java中的虚拟线程的调度器采用了工作窃取的模式进行FIFO的操作,调度器的并行书默认是jvm获取的处理器数量(Runtime.getRuntime().availableProcessors()),调度器并非分时(time sharing)的。在使用虚拟线程编写程序时,不能控制虚拟线程合适分配给平台线程,也不能控制平台线程合适分配给CPU。 以前任务和平台线程的关系: 在使用虚拟线程之后,任务-虚拟线程-调度器-平台线程的关系,1个平台线程可以被调度器分配不同的虚拟线程: 携带器 调度器将虚拟线程挂载到平台线程之后,该平台线程叫做虚拟线程的携带器,调度器并不维护虚拟线程和携带器的关联关系,因此在一个虚拟线程的生命周期可以被分配到不同的携带器,即虚拟线程运行了一小段代码后,可能会脱离携带器,此时其他的虚拟线程会被分配到这个携带器上。 携带器和虚拟线程是相互独立的,比如: 虚拟线程不能使用携带器的标识,Thread.current()方法获取的是虚拟线程本身。 两者有各自的栈空间。 两者不能访问对方的ThreadLocal变量 在程序的执行过程中,虚拟线程遇到阻塞的操作是大部分情况下会被解除挂载,阻塞结束后,虚拟线程会被调度器重新挂载到携带器上,因此虚拟线程会频繁的挂载和解除挂载,这并不会导致操作系统线程的阻塞。 有些阻塞操作并不会导致虚拟线程接触挂载,这样会同时阻塞携带器和操作系统线程,例如:操作系统基本的文件操作,Java的Object.wait()方法,下面两种情况下不会导致虚拟线程的解除挂载: 执行synchronized同步代码(会导致携带器阻塞,所以建议使用ReentrantLock替换掉Synchronized) 执行本地方法或外部函数 虚拟线程和平台线程api的区别 从内存空间上来说,虚拟线程的栈空间可以看做是一个大块的站对象,他被存储在Java堆中,相比于单独存储对象,堆中存储虚拟线程会造成一些空间的浪费,这点在后续的Java版本中应该会得到改善,当然也有一些好处,就是可以重复利用这部分栈空间,不用多次申请开辟新的内存地址,虚拟线程的栈空间最大可以达到平台线程的栈空间容量。 虚拟线程并不是GC root,其中的引用不会出现Stop World,当虚拟线程阻塞之后比如BlockingQueue.take(),平台线程既不能获取到虚拟线程,也不能获取到queue队列,这样该平台线程可能被回收掉,虚拟线程在运行或阻塞时不会被GC 通过Thread构造方法创建的线程都是平台线程 虚拟线程是守护线程,不能通过setDaemon方法改为非守护线程 虚拟线程的优先级是默认的5,不能被修改,将来版本可能允许修改 虚拟线程不支持stop(), suspend(),resume()方法 创建虚拟线程的方式 Java中创建的虚拟线程本质都是通过Thread.Builder.OfVirtual对象进行创建的,有如下三种方式: Thread.startVirtualThread()直接创建一个虚拟线程 Thread.ofVirtual().name("virtual-thread-").unstarted(r) ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor() 示例代码 虚拟线程池 import java.time.Duration; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.stream.IntStream; /** * @author imyzt * @date 2023/12/18 * @description VirtualThread 1 */ public class VirtualThread { public static void main(String[] args) throws InterruptedException { // 通过线程池创建虚拟线程池 try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) { IntStream.range(0, 10000000).forEach(i -> { executor.submit(() -> { try { Thread.sleep(Duration.ofSeconds(1)); System.out.println("执行任务: " + i); } catch (InterruptedException e) { e.printStackTrace(); } }); }); } catch (Exception e) { e.printStackTrace(); } } } 创建虚拟线程 import java.util.concurrent.TimeUnit; /** * @author imyzt * @date 2023/12/18 * @description VirtualThread 2 */ public class CreateVirtualThread { public static void main(String[] args) throws InterruptedException { Runnable r = () -> System.out.println(Thread.currentThread().getName() + " --- 执行了"); // 创建虚拟线程, 方式1 Thread.startVirtualThread(r); Thread virtualThread = Thread.ofVirtual().name("virtual-thread-").unstarted(r); virtualThread.start(); System.out.println("是虚拟线程吗? " + virtualThread.isVirtual()); Thread platformThread = Thread.ofPlatform().priority(0).daemon(true).name("platform-thread-").unstarted(r); platformThread.start(); System.out.println("是虚拟线程吗? " + platformThread.isVirtual()); // --- 执行了 //virtual-thread- --- 执行了 //是虚拟线程吗? true //platform-thread- --- 执行了 //是虚拟线程吗? false // 主线程休眠 TimeUnit.SECONDS.sleep(1); } } 其它 除了提出虚拟线程外,还提出来新的并发编程模型结构化并发,
Read More ~

Java 17~18 新特性

特性列表 版本 特性 Java 17(LTS) 1. switch 类型推断 2. 伪随机数变化 Java 18 1. 使用UTF-8编码 2. jwebserver 3. Object、Thread部分方法标记废弃 4. 增加@snippet注解 Java 17 switch 类型推断,伪随机数变化 import java.util.random.RandomGenerator; import java.util.random.RandomGeneratorFactory; /** * @author imyzt * @date 2023/12/17 * @description switch 类型推断 */ public class SwitchFuture { public static void main(String[] args) { Animal animal = new Cat(); switch (animal) { case Cat c -> c.say(); case Dog d -> d.say(); case null -> System.out.println("null"); default -> System.out.println("default"); } /* ➜ java17 git:(master) ✗ java -version openjdk version "21.0.1" 2023-10-17 LTS OpenJDK Runtime Environment Corretto-21.0.1.12.1 (build 21.0.1+12-LTS) OpenJDK 64-Bit Server VM Corretto-21.0.1.12.1 (build 21.0.1+12-LTS, mixed mode, sharing) ➜ java17 git:(master) ✗ java SwitchFuture.java 汪汪汪 */ RandomGeneratorFactory<RandomGenerator> factory = RandomGeneratorFactory.getDefault(); RandomGenerator randomGenerator = factory.create(); randomGenerator.ints(10).forEach(System.out::println); //261824154 //540138312 //-1600972486 //-467718820 //-660092685 //-1149689401 //-46916737 //2110685130 //-1910355456 //-814203516 } } class Animal { } class Cat extends Animal { void say() { System.out.println("汪汪汪"); } } class Dog extends Animal { void say() { System.out.println("喵喵喵"); } } Java 18 默认使用UTF-8字符编码 从jdk18开始,默认使用UTF-8字符编码,如果需要修改为其他字符,可以使用命令 -Dfile.encoding=UTF-8来指定。 简单web服务器 ➜ java17 git:(master) ✗ jwebserver 默认情况下绑定到环回。如果要表示所有接口,请使用 "-b 0.0.0.0" 或 "-b ::"。 为 127.0.0.1 端口 8000 上的 /xxxxxxxxx目录/java17 及子目录提供服务 URL http://127.0.0.1:8000/ 127.0.0.1 - - [17/12月/2023:10:38:18 +0800] "GET / HTTP/1.1" 200 - 127.0.0.1 - - [17/12月/2023:10:38:18 +0800] "GET /favicon.ico HTTP/1.1" 404 - 127.0.0.1 - - [17/12月/2023:10:38:20 +0800] "GET /SwitchFuture.java HTTP/1.1" 200 - 将被废弃的方法 java.lang.Object#finalize java.lang.Thread#stop 新增@snippet展示代码 之前需要用<code>标签框代码,不方便,现在通过@snippet还是框代码
Read More ~

Java 10~16 新特性

主要特性 版本 特性 Java 10 1. var声明局部变量 Java 11 (LTS) 1. 字符串补充工具方法 2. lambda 类型推断 Java 12 1. switch 箭头表达式 Java 13 1. switch 支持返回值,增加yield关键字 2. 多行字符串 Java 14 1. instanceof优化 2. NPE优化 3. record类型 Java 15 1. scale密封类 Java 16 1. 包装类构造方法警告 2. DateTimeFormatter增加匹配方式 3. InvocationHandler新增方法 Java 10 var 声明局部变量 /** * @author imyzt * @date 2023/12/16 * @description Var */ public class VarDemo { public static void main(String[] args) { var str = "hello world"; var num = 10; System.out.println(str); System.out.println(num); } } Java 11 字符串补充工具方法, lambda 类型推断 /** * @author imyzt * @date 2023/12/16 * @description String 补充工具方法 */ public class StrUtil { public static void main(String[] args) { //Unicode空白字符 char c = '\u2000'; String str = c + "abc" + c; // 去除普通空白字符 System.out.println(str.trim()); // 去除Unicode空白字符 System.out.println(str.strip()); // 去除前面的空白字符 System.out.println(str.stripLeading()); // 去除后面的空白字符 System.out.println(str.stripTrailing()); // abc  //abc //abc  // abc // 判空 System.out.println(" ".isBlank()); // 支持直接定义常量使用format System.out.println("%s_%s".formatted("a", "b")); // 重复字符串 System.out.println("abc".repeat(3)); //true //a_b //abcabcabc // lambda 类型推断 // java11前 MyFunc s1 = (String a, Integer b) -> a + b; MyFunc s2 = (a, b) -> a + b; // java11后, 支持类型推断(作用不大) MyFunc s3 = (var a, var b) -> a + b; } } @FunctionalInterface interface MyFunc { String foo(String a, Integer b); } Java 12 switch 箭头表达式 /** * @author imyzt * @date 2023/12/16 * @description switch */ public class SwitchFuture { public static void main(String[] args) { int month = LocalDate.now().getMonthValue(); // java12 前 switch switch (month) { case 3: case 4: case 5: System.out.println("spring"); break; case 6: case 7: case 8: System.out.println("summer"); break; case 9: case 10: case 11: System.out.println("fall"); break; case 12: case 1: case 2: System.out.println("winter"); break; default: System.out.println("err"); } // java12 之后的switch switch (month) { case 3,4,5 -> System.out.println("spring"); case 6,7,8 -> System.out.println("summer"); case 9,10,11 -> System.out.println("fall"); case 12,1,2 -> System.out.println("winter"); default -> System.out.println("err"); } } } Java 13 switch 箭头表达式支持返回值,多行字符串,增加yield关键字作为switch多行时的返回值 import java.time.LocalDate; /** * @author imyzt * @date 2023/12/16 * @description switch 返回值 */ public class SwitchFuture { public static void main(String[] args) { int month = LocalDate.now().getMonthValue(); // java13 之后的switch String str = switch (month) { case 3, 4, 5 -> "spring"; case 6, 7, 8 -> "summer"; case 9, 10, 11 -> "fall"; case 12, 1, 2 -> "winter"; default -> { System.out.println("err"); yield "err"; } }; System.out.println(str); // winter String strline = """ 第一行 第二行 第三行"""; System.out.println(strline); //第一行 //第二行 //第三行 } } Java 14 instanceof 优化 /** * @author imyzt * @date 2023/12/16 * @description instanceof 优化 */ public class InstanceofFuture { public static void main(String[] args) { // Java 14之前 Object o = "str"; if (o instanceof String) { String str = (String) o; System.out.println(str); } // Java 14之后 if (o instanceof String str) { System.out.println(str); } } } NPE优化,在链式调用时, 如果有空指针, 可以明确是哪个变量空指针 /** * @author imyzt * @date 2023/12/16 * @description 友好的NPE提示 */ public class NpeFuture { public static void main(String[] args) { C c = new C(); // 在链式调用时, 如果有空指针, 可以明确是哪个变量空指针 String name = c.b.a.name; System.out.println(name); // Cannot read field "a" because "c.b" is null } } class A { public String name; } class B { public A a; } class C { public B b; } record模式 /** * @author imyzt * @date 2023/12/16 * @description record模式 */ public class RecordFuture { public static void main(String[] args) { Student student = new Student("yzt", 25); System.out.println(student); // Student[name=yzt, age=25] student.study(); // good good study! } } record Student(String name, Integer age) { public void study() { System.out.println("good good study!"); } } Java 15 scale密封类 需要使用sealed声明, 使用permits指定 继承密封类,必须指定自己为final(Dog),或选择继续将自己指定为密封类(Cat) /** * @author imyzt * @date 2023/12/16 * @description scale, permits */ public class ScaleFuture { } /** * 只希望Dog和Cat能继承, 需要使用sealed声明, 使用permits指定 */ sealed class Animal permits Dog, Cat { } /** * 继承密封类,必须指定自己为final(Dog), 或继续将自己指定为密封类(Cat) */ final class Dog extends Animal { } /** * 将自己指定为密封类, 并且通过permits指定只有Cat2能够继承 */ sealed class Cat extends Animal permits Cat2 { } final class Cat2 extends Cat { } Java 16 包装类型的构造方法被标记过期 /** * @author imyzt * @date 2023/12/17 * @description Integer */ public class IntegerFuture { public static void main(String[] args) { // 'Integer(int)' is deprecated and marked for removal Integer i = new Integer(2); System.out.println(i); // 不建议这样编写,因为数字在常量池中, 会和其他毫不相干的地方使用同一个锁对象 synchronized (i) { System.out.println(1); } // time format // All letters 'A' to 'Z' and 'a' to 'z' are reserved as pattern letters. The following pattern letters are defined: System.out.println(DateTimeFormatter.ofPattern("B").format(LocalDateTime.now())); //2 //1 //凌晨 } } DateTimeFormatter 官方文档 Symbol Meaning Presentation Examples G era text AD; Anno Domini; A u year year 2004; 04 y year-of-era year 2004; 04 D day-of-year number 189 M/L month-of-year number/text 7; 07; Jul; July; J d day-of-month number 10 g modified-julian-day number 2451334 Q/q quarter-of-year number/text 3; 03; Q3; 3rd quarter Y week-based-year year 1996; 96 w week-of-week-based-year number 27 W week-of-month number 4 E day-of-week text Tue; Tuesday; T e/c localized day-of-week number/text 2; 02; Tue; Tuesday; T F day-of-week-in-month number 3 a am-pm-of-day text PM B period-of-day text in the morning h clock-hour-of-am-pm (1-12) number 12 K hour-of-am-pm (0-11) number 0 k clock-hour-of-day (1-24) number 24 H hour-of-day (0-23) number 0 m minute-of-hour number 30 s second-of-minute number 55 S fraction-of-second fraction 978 A milli-of-day number 1234 n nano-of-second number 987654321 N nano-of-day number 1234000000 V time-zone ID zone-id America/Los_Angeles; Z; -08:30 v generic time-zone name zone-name Pacific Time; PT z time-zone name zone-name Pacific Standard Time; PST O localized zone-offset offset-O GMT+8; GMT+08:00; UTC-08:00 X zone-offset 'Z' for zero offset-X Z; -08; -0830; -08:30; -083015; -08:30:15 x zone-offset offset-x +0000; -08; -0830; -08:30; -083015; -08:30:15 Z zone-offset offset-Z +0000; -0800; -08:00 p pad next pad modifier 1 ' escape for text delimiter '' single quote literal ' [ optional section start ] optional section end # reserved for future use { reserved for future use } reserved for future use InvocationHandler新增方法 该接口补充了新方法 java.lang.reflect.InvocationHandler#invokeDefault,可以调用父接口中的default方法。 @CallerSensitive public static Object invokeDefault(Object proxy, Method method, Object... args) throws Throwable { Objects.requireNonNull(proxy); Objects.requireNonNull(method); return Proxy.invokeDefault(proxy, method, args, Reflection.getCallerClass()); }
Read More ~

Java 9 新特性

主要特性 接口中支持定义 private 方法 try-with-resource 方式优化 不可以使用 “_”下划线命名变量 @Deprecated 注解支持指定废弃版本(since), 以及标记未来版本是否删除(forRemoval) String字符串的变化 模块化 jshell 接口定义private方法 import java.util.concurrent.TimeUnit; /** * @author imyzt * @date 2023/12/16 * @description java 9 在接口中可以定义 private 方法, 只可在本接口中调用 */ public interface InterfaceFuture { void foo(); /** * java 8 */ default void foo1() throws InterruptedException { sleep(); } /** * java 8 */ static void foo2() throws InterruptedException { sleep2(); } /** * java 9 */ private void sleep() throws InterruptedException { TimeUnit.SECONDS.sleep(1); } /** * java 9 */ private static void sleep2() throws InterruptedException { TimeUnit.SECONDS.sleep(1); } } try-with-resource 方式优化 package top.imyzt.jdk.features.jdk9; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; /** * @author imyzt * @date 2023/12/16 * @description 1. try 代码块简化, 2. 不可以再使用 "_"(下划线)命名变量 */ public class TryWithResource { public static void main(String[] args) throws FileNotFoundException { // java 8 try (FileInputStream fis1 = new FileInputStream(""); FileOutputStream fos1 = new FileOutputStream("")) { fis1.read(); } catch (Exception e) { } // java 9 // 可以将变量写到 try 代码块中, 让代码块更简洁 FileInputStream fis2 = new FileInputStream(""); FileOutputStream fos2 = new FileOutputStream(""); try (fis2; fos2) { } catch (Exception e) { } // java 9 运行报错 As of Java 9, '_' is a keyword, and may not be used as an identifier // String _ = "123"; } /** * `@Deprecated` 注解支持指定废弃版本(since), 以及标记未来是否废弃(forRemoval) */ @Deprecated(since = "9", forRemoval = true) private void test() { } } @Deprecated 注解升级 例如java.lang.Object#finalize方法就在 Java9 中被标记废弃,并且在未来可能会被删除。 @Deprecated(since="9", forRemoval=true) protected void finalize() throws Throwable { } String字符串的变化 在 Java9 之前的版本中,String内部使用char数组存储,对于使用英语的人来说,一个字符用一个byte就能存储,使用char存储字符会浪费一半的内存空间,因此在 Java9 中将String内部的char数组改成了byte数组,这样就节省了一半的内存占用。 char c = 'a'; // 2个字节 byte b = 97; // 1个字节 String中增加了2个成员变量 static final boolean COMPACT_STRINGS;:判断是否压缩,默认为true,如果=false,则不压缩,使用UTF-16编码。 private final byte coder;:用来区分使用的字符编码 LATIN1,值为0,存储英文 UTF-16,值为1,存储中文,或夹杂中文的英文(为了区分字符边界,如果英文使用1byte,则不利于字符串的sub等操作,也无法计算长度) @Native static final byte LATIN1 = 0; @Native static final byte UTF16 = 1; public int length() { // 如果开启压缩,`coder()=0`,不进行位移,返回value数组长度 // 如果开启压缩,`coder()=1`,右移1=除以2,因为中文存储1个字符占用2个byte的空间 return value.length >> coder(); } byte coder() { // 如果开启压缩,则`coder=LATIN1=0`,否则`coder=UTF16=1` return COMPACT_STRINGS ? coder : UTF16; } byte数组如何存储中文?通过源码 java.lang.StringUTF16#toBytes(char[], int, int) 可以看到,1个中文会被存储到byte数组中的两个元素上 ,即存储一个中文,byte数组长度为2,存储2个中文,byte数组长度为4. StringUTF16 部分源码截取,高八位和低八位分别存储,占用2个byte数组的空间: static final int HI_BYTE_SHIFT; static final int LO_BYTE_SHIFT; static { // CPU架构大小端 if (isBigEndian()) { HI_BYTE_SHIFT = 8; LO_BYTE_SHIFT = 0; } else { HI_BYTE_SHIFT = 0; LO_BYTE_SHIFT = 8; } } @IntrinsicCandidate // intrinsic performs no bounds checks static void putChar(byte[] val, int index, int c) { assert index >= 0 && index < length(val) : "Trusted caller missed bounds check"; index <<= 1; val[index++] = (byte)(c >> HI_BYTE_SHIFT); val[index] = (byte)(c >> LO_BYTE_SHIFT); } @IntrinsicCandidate // intrinsic performs no bounds checks static char getChar(byte[] val, int index) { assert index >= 0 && index < length(val) : "Trusted caller missed bounds check"; index <<= 1; return (char)(((val[index++] & 0xff) << HI_BYTE_SHIFT) | ((val[index] & 0xff) << LO_BYTE_SHIFT)); } 模块化 Java8 和之前的版本中,主要源代码是放在 rt.jar 中的,但是其中很多包是我们平时不会用到的,比如 java.awt 如果全载入到内存中,会造成一定的浪费。 所以在 Java9 开始,将 rt.jar 分成了不同的模块,一个模块下可以包含多个包,模块之间存在依赖关系。其中 java.base 为基础模块(包含java.lang,java.util..),不依赖其他模块。 模块与包类似,只不过一个模块下可以包含多个包。 Java8文档 Java9文档 .jar包中含有.class文件,配置文件。 .jmod除了上述文件外,还包含navite library,legal licenses等。 两者主要区别是.jmod主要用在编译器和链接期,并非运行期,对于开发者来说,运行期任然需要使用jar包。 模块化的优点 精简JVM运行所需加载的class类,提升加载速度。 对包更精细的控制,提高安全。 关键字 exports: 声明导出包,正常可使用,反射可以使用。 opens:声明导出包,只有反射可以使用,正常编写代码编译无法通过,报错 Package 'package_name' is declared in module 'develop', which does not export it to module 'test' requires:声明依赖包。 示例工程 创建项目 创建模块 develop 、 test 在模块 develop 下创建 Cat.java, Apple.java,创建 module-info.java,声明 export develop package top.imyzt.learing.jdkfuture.dev1; /** * @author imyzt * @date 2023/12/16 * @description Cat */ public class Cat { public void eat() { System.out.println("吃鱼"); } } package top.imyzt.learing.jdkfuture.dev2; /** * @author imyzt * @date 2023/12/16 * @description Apple */ public class Apple { public Apple() { System.out.println("Apple Constructor"); } } module develop { // 导出包 exports top.imyzt.learing.jdkfuture.dev1; // 导出包, 只能通过反射访问 opens top.imyzt.learing.jdkfuture.dev2; } 在模块 test 下创建 Test.java,创建 module-info.java,声明 requie develop package main.top.imyzt.learing.jdkfuture.test; import top.imyzt.learing.jdkfuture.dev1.Cat; // Package 'top.imyzt.learing.jdkfuture.dev2' is declared in module 'develop', which does not export it to module 'test' // import top.imyzt.learing.jdkfuture.dev2.Apple; /** * @author imyzt * @date 2023/12/16 * @description 描述信息 */ public class Test { public static void main(String[] args) throws Exception { Cat cat = new Cat(); cat.eat(); Class<?> clazz = Class.forName("top.imyzt.learing.jdkfuture.dev2.Apple"); clazz.getDeclaredConstructor().newInstance(); } } module test { requires develop; } 项目结构图 源代码 示例项目 jshell 作用不大,主要对于初学者可以学习语法。 ➜ jshell | 欢迎使用 JShell -- 版本 21.0.1 | 要大致了解该版本, 请键入: /help intro jshell> System.out.println("hello world"); hello world jshell> int a = 1; a ==> 1 jshell> int b = 2; b ==> 2 jshell> System.out.println(a+b); 3 jshell> /exit | 再见
Read More ~

Java 8 Guide E4

本篇接着上篇来, 继续学习java8新特性 Java 8 简明教程 Map putIfAbsent 代码展示 Map<String, Object> hashMap = new LinkedHashMap<>(); hashMap.put("val_1", 1); hashMap.put("val_2", 2L); hashMap.put("val_3", null); System.out.println(hashMap); Object val_1 = hashMap.putIfAbsent("val_1", 111); System.out.println(val_1); Object val_3 = hashMap.putIfAbsent("val_3", 3); System.out.println(val_3); Object val_4 = hashMap.putIfAbsent("val_4", 4); System.out.println(val_4); System.out.println(hashMap); 输出: {val_1=1, val_2=2, val_3=null} 1 null null {val_1=1, val_2=2, val_3=3, val_4=4} 对比 在之前如果我们存储一个key时, 需要判断是否存在, 不存在时添加. 我们需要先判断再添加. 在jdk1.8之后, 我们只需要 putIfAbsent(k,v) 方法就可以完成. if (!hashMap.containsKey("val_3")) { hashMap.put("val_3", "val_3"); } 方法详解 hashMap.putIfAbsent("val_1", 111);, 存在key时, 如果 value != null, 不替换, 返回原本存储的 value hashMap.putIfAbsent("val_3", 3);, 存在key时, 如果 value == null, 替换value, 返回 null hashMap.putIfAbsent("val_4", 4);, 不存在key时, 直接put进去.
Read More ~

Java 8 Guide E3

本篇接着上篇来, 继续学习java8新特性 Java 8 简明教程 Stream 介绍 Stream (流) 操作算是对集合的大大增强, 很多之前很复杂需要很多代码量的逻辑在此终结. 一个Collection(集合)可以通过调用 stream() 和 parallelStream() 两个方法获得 stream stream()方法返回一个 顺序流, 所有的操作必须等待上一步的完成. parallelStream()方法返回一个 并行流, 操作会更高效, 考虑业务使用不同的流. 流的操作分为 中间操作 和 终止操作 中间操作: 对流的处理还在进行中, 不会中指当前流. 可以返回一个流进行其他操作. 就像StringBuffer的append()一样 终止操作: 对流的处理将会终结, 当前流将不能继续使用. 中间操作 Filter Stream<T> filter(Predicate<? super T> predicate); Filter接收一个 Predicate<? super T> 接口的参数进行过滤操作 Sorted Stream<T> sorted(Comparator<? super T> comparator); Stream<T> sorted(); sorted()方法自然排序. 即调用对象本身的compareTo()方法排序. sorted(Comparator<? super T> comparator); 接受一个 Comparator对象进行排序 Map <R> Stream<R> map(Function<? super T, ? extends R> mapper); IntStream mapToInt(ToIntFunction<? super T> mapper); LongStream mapToLong(ToLongFunction<? super T> mapper); DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper); <U> Stream<U> mapToObj(IntFunction<? extends U> mapper); map有上述几个方法, 可以转换为指定的类型, 方法参数都是接受一个 xxxFunction 的转换器. 下面有一个演示. list.stream() .filter(s -> s.startsWith("1")) .sorted(String::compareTo) .mapToInt(Integer::valueOf) .mapToObj(String::valueOf) .mapToLong(Long::valueOf); 终止操作 Match boolean anyMatch(Predicate<? super T> predicate); boolean allMatch(Predicate<? super T> predicate); boolean noneMatch(Predicate<? super T> predicate); Match方法有三个, 都接受一个 Predicate 条件来决定返回true or false Count long count(); count()计算集合大小, 这个方法很简单. Reduce 有了Map, 那肯定是有Reduce, 不过Reduce是一个终止操作. 它接收一个 xxxOperator 的操作函数. <U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner); T reduce(T identity, BinaryOperator<T> accumulator); Optional<T> reduce(BinaryOperator<T> accumulator); 下面有一个操作的演示. list.stream() .sorted((s1, s2) -> s1.compareTo(s2) > 0 ? -1 : 0) .reduce((i, j) -> i + "#" + j) .ifPresent(System.out::println); System.out.println("---------------------------"); final String join = "#"; String joinReduce = list.stream() .reduce(join, (s1, s2) -> s1 + join + s2); System.out.println(joinReduce); 所有操作的代码 public static void main(String[] args) { ArrayList <String> list = new ArrayList <>(); list.add("11"); list.add("12"); list.add("13"); list.add("14"); Comparator<String> comparator = String::compareTo; Predicate<String> predicate = s -> s.startsWith("1"); IntUnaryOperator intUnaryOperator = i -> i + 1; ToIntFunction<String> toIntFunction = Integer::valueOf; IntConsumer consumer = System.out::println; list.stream() .filter(predicate) .sorted(comparator) .mapToInt(toIntFunction) .map(intUnaryOperator) .forEach(consumer); boolean anyMatch = list.stream() .filter(s -> s.startsWith("1")) .sorted(String::compareTo) .mapToInt(Integer::valueOf) .mapToObj(String::valueOf) .mapToLong(Long::valueOf) .anyMatch(i -> i > 10); System.out.println(anyMatch); long count = list.stream().count(); System.out.println(count); list.stream() .sorted((s1, s2) -> s1.compareTo(s2) > 0 ? -1 : 0) .reduce((i, j) -> i + "#" + j) .ifPresent(System.out::println); System.out.println("---------------------------"); final String join = "#"; String joinReduce = list.stream() .reduce(join, (s1, s2) -> s1 + join + s2); System.out.println(joinReduce); }
Read More ~

Java 8 Guide E2

本篇接着上篇来, 继续学习java8新特性 Java 8 简明教程 自定义一个函数式接口 代码展示 /** * @author imyzt * @date 2019/1/17 18:23 * @description 自定义函数式接口 */ public class FunctionInterfaceDemo { public static void main(String[] args) { Person<Boolean> person = Objects::nonNull; Boolean aaa = person.say("aaa"); System.out.println("传入的信息是否不为空: " + aaa); Person<String> person1 = (msg) -> "Hello "+msg; String say = person1.say("World"); System.out.println(say); } } @FunctionalInterface interface Person<T extends Object> { T say(String msg); } 讲解 代 码中定义了一个接口 Person, 接口上使用了 @FunctionalInterface 注解, 接口泛型Object是一个约束, 在这里只是演示. 实际场景中可以写需要被约束的父类. 使 用的时候, 实例 person 的泛型是 Boolean 布尔类型, 所以在表达式的右边, 也就是具体的实现上我调用了Objects的nonNull方法. :: 表达式可以直接引用对象的方法. 实 例 person1 的泛型是 String 字符串类型, 所以在表达式的右边可以定义对 say(String msg) 中msg的操作, 然后调用say()方法时就会执行上述操作. 结果输出 传入的信息是否不为空: true Hello World 内置函数式接口 Java8内置了很多函数式接口便于我们使用; 当然,我们也可以自定义函数式接口通过**@FunctionalInterface**注解声明. java.util.function下的接口最多支持到二元运算。有了这些接口,我们就可以省去创建接口的功夫,而直接使用lambda了。 Nilary 零元,Unary 一元,Binary 二元,Ternary 三元,Quaternary 四元。对于一个算子来说,一个参数,就是一元运算;2个参数就是二元运算。 Predicate Predicate是一个布尔类型的函数,该函数只有一个输入参数。 Predicate接口包含了多种默认方法,用于处理复杂的逻辑动词(and, or,negate) 摘抄自 http://blog.didispace.com/books/java8-tutorial/ch1.html Predicate<String> predicate = (a) -> a.startsWith("a"); Predicate<String> predicate2 = Objects::nonNull; boolean test = predicate.negate().and(predicate2).test("abc"); System.out.println(test); Consumer Consumer 消费target type。参数 T,无返回值(void)。 Consumer可以理解为一个消费者, 因为他没有返回值, 只接收一个参数, 按照重写的方法执行完成后即完成操作. Consumer<String> consumer = System.out::println; consumer.accept("hello world"); Function Function 对target type做转换。参数T,返回R。 Predicate<String> predicate = s -> null != s && s.startsWith(v26); Function<String, String> function = s -> predicate.test(pageInfo) ? sdkHttpKit.ipPort().append(s).toString() : sdkHttpKit.uri().append("face/meta/").append(URLUtil.encode(meta)).toString(); String url = function.apply(pageInfo); Supplier Supplier 供应target,可以理解为target的factory。无参,返回T。 Supplier 可以理解为工厂类, 通过下面的演示可以看出它能够创建对象.不过我看了别人的博客, 理解为这个方法可以将耗时的操作放进去. 在没有调用get()方法之前, 是不会执行的.可以降低系统运行时间. Main.java Supplier<People> supplier = People::new; // 在需要的时候可以调用get()方法获得一个新的People对象. People people = supplier.get(); people.hello(); 输出: hello People.java class People { void hello() { System.out.println("hello"); } } UnaryOperator UnaryOperator 一元运算。继承Function接口。参数T,返回T。 UnaryOperator 可以理解为对参数T进行处理. UnaryOperator<String> unaryOperator = s -> s + "___"; System.out.println(unaryOperator.apply("hello")); 输出: hello___ BinaryOperator BinaryOperator 二元运算。参数 T,返回R。 BinaryOperator为UnaryOperator的二元运算, 它接收两个T参数, 返回的也是T参数, 可以理解对两个参数的条件操作 BiPredicate<String, String> biPredicate = (s1, s2) -> s1.startsWith("s1") && s2.endsWith("s2"); BinaryOperator<String> binaryOperator = // 当条件满足时, 执行三目运算的第一个操作. 否则第二个操作执行. (s1, s2) -> biPredicate.test(s1, s2) ? s2 + "_" + s1 : s1 + "_" + s2; System.out.println(binaryOperator.apply("s1" , "s2")); 输出: s2_s1 Comparator 这个可以用来做比较. 在上一篇已经有使用过了. 此处添加一点演示代码 Supplier<People> supplier = People::new; People people1 = supplier.get(); people1.setPeopleId(10); People people2 = supplier.get(); people2.setPeopleId(100); Comparator<People> comparator1 = Comparator.comparing(People::getPeopleId); System.out.println(comparator1.compare(people2, people1)); Comparator<People> comparator2 = (p1, p2) -> p1.getPeopleId() > p2.getPeopleId() ? 1 : 0; System.out.println(comparator2.compare(people1, people2)); 第一个输出: 1 第二个输出: 0 Binary Consumer、Function、Predicate分别还对应了Bi的函数式接口。 它们的操作与对应的一元运算也是大同小异, 只是参数多了一个. 就不细讲了 对应的也就是二元运算函数。分别是 BiConsumer<T, U> 接收T对象和U对象,不返回值 BiPredicate<T, U> 接收T对象和U对象,返回boolean BiFunction<T, U, R> 接收T对象和U对象,返回R对象 总结 接口一大堆, 我所理解的 Consumer, BiConsumer: 消费 Predicate, BiPredicate: 条件判断 Function, BiFunction, UnaryOperator, BinaryOperator: 参数处理 Supplier: 工厂
Read More ~

Java 8 Guide E1

文章系学习Java8新特性 教程地址: Java8简明教程 在接口中定义默认方法 通过static修饰, 该方法不可被重写. 通过default修饰, 该方法不仅仅默认实现, 还可以被实现类重写 示例代码 /** * Created by imyzt on 2019/1/17 10:59 * 可以在接口中定义默认方法 */ public interface Formula { double calculate(int a); default double sqrt(int a) { return Math.sqrt(a); } static int abs (int b) { return Math.abs(b); } } class Main { public static void main(String[] args) { Formula formula = new Formula() { @Override public double calculate(int a) { return sqrt(a * 1000); } }; double calculate = formula.calculate(1); System.out.println(calculate); //31.622776601683793 double sqrt = formula.sqrt(1000); System.out.println(sqrt); //31.622776601683793 int abs = Formula.abs(1000); System.out.println(abs); //1000 } } Lambda表达式 Lambda表达式是java8最大的更新了, 使得一些操作十分简单明了. 不使用lambda表达式进行集合排序 List<String> names = Arrays.asList("peter", "anna", "mike", "xenia"); Collections.sort(names, new Comparator <String>() { @Override public int compare(String a, String b) { return b.compareTo(a); } }); 使用Lambda表达式进行集合排序 Collections.sort(names, (String a, String b) -> { return b.compareTo(a); }); 不过,还可以省去返回值的表述 Collections.sort(names, (String a, String b) -> b.compareTo(a)); Lambda可以自动识别类型, 因此我们可以删除掉类型声明 Collections.sort(names, (a, b) -> b.compareTo(a)); 当然, 也可以将具体动作简化 Collections.sort(names, Comparator.reverseOrder()); 最后, java8的jdk集合中实现了sort方法, 因此我们可以用最后这句话来实现集合的排序 names.sort(Comparator.reverseOrder());
Read More ~