前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >跨越Java时代的桥梁:一位程序员的自我革新之旅

跨越Java时代的桥梁:一位程序员的自我革新之旅

原创
作者头像
花花Binki
发布2024-09-29 00:16:32
1351
发布2024-09-29 00:16:32
举报
文章被收录于专栏:金三银四加速季岚的工作随笔
封面
封面

前言

作为一名出身于二本院校的程序员,我的Java语法基础主要来源于大学时期的教材。然而,近期我在阅读一些前沿书籍时,意外发现了许多新颖的语法技巧和应用。为了记录这一学习过程,并与同行分享我的心得,特此撰写此篇。

逻辑计算篇

位运算符

提起运算符,大多人的脑海里第一联想到的是加减乘除,有逻辑电路基础的,会想起||,&&等符号。笔者在算法的学习中,看到了一些神奇的用法。

位运算,常见的就是与(&)和或(|)。Java 语言中,他们用来做多种条件(结果为boolean值)的组合判断。如果两边的类型是int类型,就会产生一些奇妙的效果。比如用下面的方式判断奇偶:

代码语言:java
复制
n & 1 // 偶数:0。奇数:1

在现代计算机中,位运算与加减运算相当,但对比乘除与取模,还是要快上一些。

排序时想要交换两个数字,又不申请额外空间?试试如下操作:

代码语言:java
复制
int a = 34;
int b = 67;
a = a ^ b;  // a = 97
b = a ^ b;  // b = 34
a = a ^ b;  // a = 67

当然,位运算的成员还有很多,异或(^)左移(<<)右移(>>)等。你会在HashMap#hash 方法中见到妙用。

被遗忘的label

while 是一个很常用的循环语句,工作中常常用break,continue来处理一些中断。Java 中还提供了另一种,见下例:

代码语言:java
复制
      outer:
      while (true) {
    	  // doSomething
    	  while(true) {
    		  // doSomething
    		  if (2 > 1) break outer;
    	  }
      }

代码第 1 行定义了一个outer的label,第二层循环的break后面跟这这个label。代码运行到这里,不仅不会报错,命中条件后,还会回到定义的位置继续执行。

第一次遇到以为是什么新特性,在官网查了半天,原来在初始版本就拥有该特性。至于为什么被人遗忘,一是场景较少,使用有局限。二是多层循环很难理解。

不断革新的switch

JDK23 的JEP 455,JDK21的JEP 441,以及之前的n多预览版,都可以看到switch的身影。Java核心技术卷的描述也变了。先看一段示例:

在Java中,JEP是Java Enhancement Proposal(Java增强提案)的缩写。JEP是一种用于提议改进Java平台的新特性和功能的文档。这些提案由Java平台的开发者、用户和社区成员提出,并经过Java平台的维护者和领导者进行审查和批准。

代码语言:java
复制
    	String key = "a";
    	String realKey = switch(key) {
    		case "a" -> "a+";
    		case "b" -> "b+";
    		case "c" -> "c+";
    		case "d" -> "d+";
    		default -> "S";
		};

根据官方描述(JEP325)这种语法,是模仿C/C++的,叫fall-through semantics (直通语义)。而经常使用的switch现在被叫做fall-through variant(直通式变体),这下成“守旧派”了。

结合后面提到的Record类,又有了新用法。

代码语言:java
复制
record Person(String name, int age) {}

class SwitchExample {
    public static void main(String[] args) {
        Person person = new Person("Alice", 30);

        switch (person) {
            case Person p when p.age() < 18:
                System.out.println("未成年人");
                break;
            case Person p when p.age() >= 18 && p.age() < 60:
                System.out.println("成年人");
                break;
            case Person p when p.age() >= 60:
                System.out.println("老年人");
                break;
            default:
                System.out.println("未知");
        }
    }
}

还有针对对象类型的JEP406,和上面类似,这里就不展开介绍。值得注意的是,代码还是需要手动判断是否为null,否者会抛出NPE。

数据类型

字符串

在写AI应用时,会遇到多行文本的情况,在以前的情况下,会采用手动加入换行符来实现。而在Java 13的JEP355中,可以这样书写:

代码语言:java
复制
// HTML代码
String html = """
              <html>
                  <body>
                      <p>Hello, world</p>
                  </body>
              </html>
              """;
// SQL
String query = """
               SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`
               WHERE `CITY` = 'INDIANAPOLIS'
               ORDER BY `EMP_ID`, `LAST_NAME`;
               """;

如果你只是书写上换行,而不是真正的换行,结尾可以添加\。不过还存在一个问题,如果想动态的拼接参数,还需要自己实现替换。官方在这个特性中对比了其他语言:

其他语言的实现
其他语言的实现

Java 集众家特性得到如下的语法:

代码语言:java
复制
		String name = "name";
		String phone = "1111";
		String address = "address";
		
		String json = STR."""
				"name":    "\{name}",
				"phone":   "\{phone}",
				"address": "\{address}"
				""";
		System.out.println(json);

更详细的用法可以查看java.lang.StringTemplate

Record 类

提出是在JDK14,正式引入是在16,以下是对正式版的解读。 一个自带get方法的类,这听起来就像是像是引入了lombok。除了上述的方法,它还自带:

  • 一个包含所有属性的构造函数
  • equals() 和 hashCode() 方法
  • toString() 方法

正如这个词的意思一样,记录,非常适合作为DTO来使用。我在Spring AI的Ollama包下,也发现了它

org.springframework.ai.ollama.api.OllamaApi#Message

代码语言:java
复制
	/**
	 * Chat message object.
	 *
	 * @param role The role of the message of type {@link Role}.
	 * @param content The content of the message.
	 * @param images The list of base64-encoded images to send with the message.
	 * 				 Requires multimodal models such as llava or bakllava.
	 */
	@JsonInclude(Include.NON_NULL)
	public record Message(
			@JsonProperty("role") Role role,
			@JsonProperty("content") String content,
			@JsonProperty("images") List<String> images,
			@JsonProperty("tool_calls") List<ToolCall> toolCalls) {

这些配置类希望一开始就设置好属性,不允许再更改,Record也非常适合。

除了不可变之外,相较于普通类还有些特性:

  • 记录类不能被继承,但可以实现接口
  • 记录类的构造函数和方法调用可以使用类型推断,与switch配合。

还有一点,取得属性时,比如name,请使用name(),而不是getName()

包定义

如果Spring项目尝试过从JDK 8 升级到JDK 17,可能会遇到module-info.java的相关报错,一般是升级相关依赖就好,那么怎样理解这个类呢。参考JDK 9的JEP261与Java 核心技术卷。

Spring Boot 3支持的最低版本是JDK 17。

众所周知,封装,继承,多态是面向对象的三大特性。在Java类与类中,封装体现在访问修饰符上,比如Public,Private等。这些是为了单个工程间的隔离,如果在多模块(module)下,自带的限制,比如public,可能不想那么”publicer“。于是就有了这么一个模块化配置。

在JDK 9以后的更新中,官方又根据此特性,将以前的宽松封装替换为了强封装。比如下方的图例中,明显的jre没了(实际上放到了lib下)

JDK 17 与 JDK 8 包结构对比(左 17)
JDK 17 与 JDK 8 包结构对比(左 17)

具体module-info里包含了什么呢?以java.sql举例:

代码语言:java
复制
// 定义一个名为java.sql的模块,该模块包含Java数据库连接(JDBC)的核心API。
module java.sql {
    // 需要java.logging模块,并且是传递性的,意味着任何依赖java.sql的模块也需要java.logging。
    requires transitive java.logging;
    // 需要java.transaction.xa模块,并且是传递性的,用于支持XA事务。
    requires transitive java.transaction.xa;
    // 需要java.xml模块,并且是传递性的,用于处理XML数据。
    requires transitive java.xml;

    // 导出java.sql包,使得其他模块可以使用这个包中的公共类和接口。
    exports java.sql;
    // 导出javax.sql包,提供额外的与数据库交互的接口和类。
    exports javax.sql;

    // 声明java.sql.Driver为服务提供者接口,允许第三方实现自己的数据库驱动。
    uses java.sql.Driver;
}

向量API

JDK 21刚问世的时候,做过一次解读,那时以为向量是为数学运算服务。现在看来,对也不对。

面对LLM的幻觉问题,RAG技术诞生了。如果想要用得好,向量数据库作为存储服务,起到了很大的作用。向量的作用不仅如此,这里不过多展开。

向量的关键字为Vector,这和原链表Vector同名。向量API包路径为jdk.incubator.vector

具体用法见下代码:

代码语言:java
复制
import jdk.incubator.vector.VectorSpecies;
import jdk.incubator.vector.FloatVector;

public class MethodDemo {
    // 定义一个静态常量SPECIES,表示浮点数向量的种类
    static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_PREFERRED;

    /**
     * 对输入的三个浮点数数组进行向量计算。
     *
     * @param a 第一个浮点数数组
     * @param b 第二个浮点数数组
     * @param c 结果数组,存储计算后的值
     */
    void vectorComputation(float[] a, float[] b, float[] c) {
        int i = 0;
        // 计算循环的上限,确保不会超出数组a的长度
        int upperBound = SPECIES.loopBound(a.length);
        // 使用SIMD指令集并行处理数组a和b的前部分
        for (; i < upperBound; i += SPECIES.length()) {
            // 从数组a和b中创建FloatVector对象va和vb
            var va = FloatVector.fromArray(SPECIES, a, i);
            var vb = FloatVector.fromArray(SPECIES, b, i);
            // 计算va的平方加上vb的平方,然后取负值,结果存储在vc中
            var vc = va.mul(va)
                       .add(vb.mul(vb))
                       .neg();
            // 将vc的值存入数组c的相应位置
            vc.intoArray(c, i);
        }
        // 处理剩余的数组元素,这些元素无法使用SIMD指令集并行处理
        for (; i < a.length; i++) {
            c[i] = (a[i] * a[i] + b[i] * b[i]) * -1.0f;
        }
    }
}

代码主要来自官方文档,补全了报包引入和注释。旧版本的Eclipse无法自动引入。

笔者Eclipse版本为2023-12,JDK21版本是2023年9月19正式发布。

one more thing

  • 笔者如何发现这些特性? 找到新特性主要来自于OpenJDK官方网站,以及经典书籍的最新版。
相关书籍
相关书籍
  • 看不懂英文如何处理?

可以借助AI,比如和元宝说

解读 【link】link为你要阅读的网页

  • ZGC和虚拟线程为什么没有提到? 实力有限,后续有精力会更新。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 逻辑计算篇
    • 位运算符
      • 被遗忘的label
        • 不断革新的switch
        • 数据类型
          • 字符串
            • Record 类
              • 包定义
                • 向量API
                • one more thing
                相关产品与服务
                向量数据库
                腾讯云向量数据库(Tencent Cloud VectorDB)是一款全托管的自研企业级分布式数据库服务,专用于存储、检索、分析多维向量数据。该数据库支持多种索引类型和相似度计算方法,单索引支持千亿级向量规模,可支持百万级 QPS 及毫秒级查询延迟。腾讯云向量数据库不仅能为大模型提供外部知识库,提高大模型回答的准确性,还可广泛应用于推荐系统、自然语言处理等 AI 领域。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档