NVIDIA 初创加速计划,免费加速您的创业启动 了解详情
写点什么

Java 新特性完整指南:Switch 模式匹配

  • 2023-07-18
    北京
  • 本文字数:10257 字

    阅读完需:约 34 分钟

Java新特性完整指南:Switch模式匹配

Switch 语句由选择器表达式和包含 case 标签switch 块组成;对选择器表达式进行求值,并切换到与求值结果相匹配的 case 标签所对应的执行路径。


在原来 switch 语句中,case…:标签语法采用的是穿透语义(fall-through semantics)。Java 14 增加了对新标签语法case ...-> 的支持,该语法采用的是非穿透语义。


Java 14 还增加了对 switch 表达式的支持。Switch 表达式的计算结果为单个值。该版本还引入了yield语句,用于显式地生成一个值。


支持 switch 表达式(另有一篇文章进行了详细讨论)是指可以将 switch 用于需要表达式(如赋值语句)的实例。

问题


即使有了 Java 14 所做的功能增强,switch 语句的使用还是有一些限制:


  1. Switch 选择器表达式只支持特定类型,即基本整型数据类型byteshortcharint;对应的装箱形式ByteShortCharacterIntegerString类;枚举类型。

  2. Switch 选择器表达式的计算结果只能与常量做相等比较。在匹配 case 标签和常量值时只对一个值进行检查。

  3. null 值的处理方式与其他值不同。

  4. 错误处理方式不统一。

  5. 枚举的作用域不是很合理。

解决方案


为了克服这些限制,人们已经提出并实现了一种实用、便捷的解决方案:switch 语句模式匹配和表达式。这个解决方案解决了上面提到的所有问题。


Switch 模式匹配是在 JDK 17 中引入的,JDK 18、19 和 20 对其做了改进,JDK 21 最终将其完成。


模式匹配从以下几个方面克服了传统 switch 语句的局限性:


  1. 选择器表达式的类型可以是整型基本类型(不包括long类型),也可以是任何引用类型。

  2. 除了常量之外,case 标签还可以包含模式。不同于常量 case 标签只能应用于一个值,模式 case 标签可以应用于多个值。引入了一个新的 case 标签case p,其中p是一个模式。

  3. Case 标签可以包含null

  4. Case 标签后面有一个可选的when子句,可用于条件模式匹配或受保护模式匹配。带有 when 的 case 标签被称为受保护 case 标签。

  5. 枚举常量的 case 标签可以限定。当使用枚举常量时,选择器表达式不一定要是枚举类型。

  6. 引入MatchException,在模式匹配中实现更统一的错误处理。

  7. 传统的 switch 语句和穿透语义也支持模式匹配。模式匹配的一个好处是方便面向数据的编程,例如提高复杂数据查询的性能。

什么是模式匹配?


模式匹配是一个功能强大的特性,它扩展了程序中控制流结构的功能。除了可以匹配传统上支持的常量外,该特性还允许选择器表达式与多个模式进行匹配。Switch 语句的语义并没有变化;与 switch 选择器表达式的值进行匹配的 case 标签可能包含模式,如果选择器表达式的值与一个 case 标签模式匹配成功,就会选中控制流中那个 case 标签所对应的执行路径。唯一的增强是,选择器表达式既可以是基本整型(不包括 long 类型),也可以是任何引用类型。除了常量之外,case 标签还可以包含模式。此外,还有一个新增功能是,case 标签支持 null 和限定枚举常量。


以下是 switch 块中 switch 标签的语法:


SwitchLabel:  case CaseConstant { , CaseConstant }  case null [, default]  case Pattern  default
复制代码


模式匹配既可以用于具有穿透语义的传统case…:标签语法,也可以用于非穿透语义的case…->标签语法。尽管如此,必须注意的是,一个 switch 块不能同时使用这两种类型的 case 标签。


得益于这些修改,模式匹配让开发人员可以实现更复杂的控制流结构,为代码逻辑的处理提供了更丰富的方法。

环境设置


要运行本文中的示例代码,唯一的先决条件是安装 Java 20 或 Java 21。与 Java 20 相比,Java 21 只做了一项增强,即在 case 标签中支持限定枚举常量。可以通过以下命令查看 Java 版本:


java --versionjava version "20.0.1" 2023-04-18Java(TM) SE Runtime Environment (build 20.0.1+9-29)Java HotSpot(TM) 64-Bit Server VM (build 20.0.1+9-29, mixed mode, sharing)
复制代码


因为在 Java 20 中,switch 模式匹配是一个预览特性,所以必须使用以下语法运行javacjava命令:


javac --enable-preview --release 20 SampleClass.javajava --enable-preview  SampleClass
复制代码


但是,也可以使用源码启动器直接运行它,命令行如下:


java --source 20 --enable-preview Main.java
复制代码


还有一个jshell选项,但也需要启用预览功能:


jshell --enable-preview
复制代码

一个简单的模式匹配示例


我们从一个简单的模式匹配示例开始,其中,switch 表达式的选择器表达式类型是引用类型Collection ;case 标签包含case p形式的模式。


import java.util.Collection;import java.util.LinkedList;import java.util.Stack;import java.util.Vector;
public class SampleClass { static Object get(Collection c) {
return switch (c) { case Stack s -> s.pop(); case LinkedList l -> l.getFirst(); case Vector v -> v.lastElement(); default -> c; }; }
public static void main(String[] argv) {
var stack = new Stack<String>(); stack.push("firstStackItemAdded"); stack.push("secondStackItemAdded"); stack.push("thirdStackItemAdded");
var linkedList = new LinkedList<String>();
linkedList.add("firstLinkedListElementAdded"); linkedList.add("secondLinkedListElementAdded"); linkedList.add("thirdLinkedListElementAdded");
var vector = new Vector<String>();
vector.add("firstVectorElementAdded"); vector.add("secondVectorElementAdded"); vector.add("thirdVectorElementAdded");
System.out.println(get(stack)); System.out.println(get(linkedList)); System.out.println(get(vector)); }}
复制代码


编译并运行这个 Java 应用程序,输出如下:


thirdStackItemAddedfirstLinkedListElementAddedthirdVectorElementAdded
复制代码

模式匹配支持所有引用类型


在上面给出的示例中,选择器表达式的类型是Collection类类型。但是,选择器表达式的类型可以是任何引用类型。因此,case 标签模式可以是与选择器表达式的值兼容的任何引用类型。例如,下面是经过修改的SampleClass类,它使用了Object类型的选择器表达式,而 case 标签模式除了之前使用的StackLinkedListVector等引用类型外,还包括一个记录模式和一个数组引用类型的模式。


import java.util.LinkedList;import java.util.Stack;import java.util.Vector;
record CollectionType(Stack s, Vector v, LinkedList l) {}
public class SampleClass { static Object get(Object c) { return switch (c) { case CollectionType r -> r.toString(); case String[] arr -> arr.length; case Stack s -> s.pop(); case LinkedList l -> l.getFirst(); case Vector v -> v.lastElement(); default -> c; }; }
public static void main(String[] argv) {
var stack = new Stack<String>(); stack.push("firstStackItemAdded"); stack.push("secondStackItemAdded"); stack.push("thirdStackItemAdded");
var linkedList = new LinkedList<String>();
linkedList.add("firstLinkedListElementAdded"); linkedList.add("secondLinkedListElementAdded"); linkedList.add("thirdLinkedListElementAdded");
var vector = new Vector<String>();
vector.add("firstVectorElementAdded"); vector.add("secondVectorElementAdded"); vector.add("thirdVectorElementAdded");
var r = new CollectionType(stack, vector, linkedList); System.out.println(get(r)); String[] stringArray = {"a", "b", "c"};
System.out.println(get(stringArray)); System.out.println(get(stack)); System.out.println(get(linkedList)); System.out.println(get(vector));
}}
复制代码


这次的输出如下:


CollectionType[s=[firstStackItemAdded, secondStackItemAdded, thirdStackItemAdded], v=[firstVectorElementAdded, secondVectorElementAdded, thirdVectorElementAdded], l=[firstLinkedListElementAdded, secondLinkedListElementAdded, thirdLinkedListElementAdded]]3thirdStackItemAddedfirstLinkedListElementAddedthirdVectorElementAdded
复制代码

Null case 标签


传统上,如果选择器表达式的计算结果为空,则 switch 语句在运行时会抛出NullPointerException。选择器表达式为空不是编译时问题。下面这个简单的应用程序有一个匹配所有 case 标签的default ,我们通过它演示下选择器表达式为空如何导致运行时异常NullPointerException


import java.util.Collection;
public class SampleClass { static Object get(Collection c) { return switch (c) { default -> c; }; }
public static void main(String[] argv) { get(null); }}
复制代码


我们可以在 switch 块外面显式地检测空值,并仅在值非空时执行 switch,但这涉及到添加 if-else 代码。在新的模式匹配特性中,Java 增加了对null的支持。下面这个应用程序中的 switch 语句使用case null来检测选择器表达式的值是否为空。


import java.util.Collection;
public class SampleClass { static void get(Collection c) {
switch (c) { case null -> System.out.println("Did you call the get with a null?"); default -> System.out.println("default"); } }
public static void main(String[] argv) { get(null); }}
复制代码


在运行时,应用程序输出如下:


你在调用get方法时使用了null参数?
复制代码


case null可以与case default合并,如下所示:


import java.util.Collection;
public class SampleClass { static void get(Collection c) { switch (c) { case null, default -> System.out.println("Did you call the get with a null?"); } }
public static void main(String[] argv) { get(null); }}
复制代码


但是,case null 不能与任何其他 case 标签合并。例如,下面的类将 case null 与一个模式为Stack s的 case 标签做了合并:


import java.util.Collection;import java.util.Stack;
public class SampleClass { static void get(Collection c) { switch (c) { case null, Stack s -> System.out.println("Did you call the get with a null?"); default -> System.out.println("default"); } }
public static void main(String[] args) { get(null); }}
复制代码


该类将产生如下编译时错误:


SampleClass.java:11: error: 非法case标签合并          case null, Stack s -> System.out.println("Did you call the get with a null?");
复制代码

带有 when 子句的受保护模式


有时,开发人员可能会使用与布尔表达式计算结果做匹配的条件式 case 标签模式。这时,when子句就派上用场了。该子句会计算布尔表达式,形成所谓的“受保护模式”。如下所示,代码中的第一个 case 标签使用when子句判断Stack是否为空。


import java.util.Stack;import java.util.Collection;
public class SampleClass { static Object get(Collection c) { return switch (c) { case Stack s when s.empty() -> s.push("first"); case Stack s2 -> s2.push("second"); default -> c; }; }}
复制代码


对应的代码在-> 右侧,只有在 Stack 为空时才会执行。

对于带有模式的 case 标签,顺序很重要


在使用带模式的 case 标签时,开发人员必须确保不会因为顺序产生任何与类型或子类型层次结构相关的问题。这是因为,与常量 case 标签不同,case 标签中的模式使得选择器表达式可以匹配多个包含模式的 case 标签。Switch 模式匹配特性会匹配第一个模式与选择器表达式值相同的标签。


如果一个 case 标签模式的类型是在它之前出现的另一个 case 标签模式的类型的子类型,则会发生编译时错误,因为后一个 case 标签将被识别为不可访问代码。


下面是一个演示程序,你可以编译并运行它,其中类型为Object的 case 标签模式控制了后续类型为Stack的代码标签模式。


import java.util.Stack;
public class SampleClass { static Object get(Object c) { return switch (c) { case Object o -> c; case Stack s -> s.pop(); }; }}
复制代码


在编译这个类时,会产生以下错误信息:


SampleClass.java:12: error: 该case标签为它前面的case标签所控制        case Stack s  -> s.pop();             ^
复制代码


像下面这样对调两个 case 标签的顺序就可以修复这个编译时错误:


public class SampleClass {    static Object get(Object c) {        return switch (c) {            case Stack s  -> s.pop();            case Object o  -> c;        };    }}
复制代码


类似地,如果 case 标签包含的模式与前面出现的无条件/非保护模式 case 标签具有相同的引用类型,则会导致编译类型的错误,就像下面的类这样:


import java.util.Stack;import java.util.Collection;
public class SampleClass { static Object get(Collection c) { return switch (c) { case Stack s -> s.push("first"); case Stack s2 -> s2.push("second"); }; }}
复制代码


上述代码在编译时会产生以下错误:


SampleClass.java:13: error: 该case标签为它前面的case标签所控制        case Stack s2 -> s2.push("second");             ^
复制代码


为了避免这种错误,case 标签的顺序应该直观、可读。应该首先列出常量标签,然后是case null标签、受保护的模式标签和非受保护类型的模式标签。default case 标签可以与case null 标签合并,也可以单独作为最后一个 case 标签。下面的类演示了正确的排序:


import java.util.Collection;import java.util.Stack;import java.util.Vector;
public class SampleClass { static Object get(Collection c) { return switch (c) { case null -> c; //case label null case Stack s when s.empty() -> s.push("first"); // 受保护case标签 case Vector v when v.size() > 2 -> v.lastElement(); // 受保护case标签 case Stack s -> s.push("first"); // 非受保护case标签 case Vector v -> v.firstElement(); // 非受保护case标签 default -> c; }; }}
复制代码

模式匹配可用于传统的 switch 语句和穿透语义


模式匹配特性与它是 switch 语句还是 switch 表达式无关。模式匹配也与使用穿透语义的case…:标签还是使用非穿透语义的case…->标签无关。在下面的示例中,模式匹配与 switch 语句而不是与 switch 表达式一起使用。case 标签使用了具有穿透语义的case…:。第一个 case 标签中的when子句使用了一个受保护的模式。


import java.util.Stack;import java.util.Collection;
public class SampleClass { static void get(Collection c) { switch (c) { case Stack s when s.empty(): s.push("first"); break; case Stack s : s.push("second"); break; default : break; } }}
复制代码

模式变量的作用域


模式变量是出现在 case 标签模式中的变量。模式变量的作用域仅限于出现在->箭头右侧的块、表达式或 throw 语句。请看下面的演示代码,default 中使用了来自它前面的 case 标签的模式变量。


import java.util.Stack;
public class SampleClass { static Object get(Object c) { return switch (c) { case Stack s -> s.push("first"); default -> s.push("first"); }; }}
复制代码


上述代码会产生以下编译错误:


import java.util.Collection;SampleClass.java:13: error: cannot find symbol        default -> s.push("first");                   ^  symbol:   variable s  location: class SampleClass
复制代码


出现在受保护 case 标签模式中的模式变量,其作用域包括 when 子句,如下所示:


import java.util.Stack;import java.util.Collection;
public class SampleClass { static Object get(Collection c) { return switch (c) { case Stack s when s.empty() -> s.push("first"); case Stack s -> s.push("second"); default -> c; }; }}
复制代码


由于模式变量的作用域有限,所以相同的模式变量名可以跨多个 case 标签使用。前面的例子说明了这一点,其中模式变量s用在了两个不同的 case 标签中。


当处理具有穿透语义的 case 标签时,模式变量的作用域扩展到了:右侧的一组语句。这就是为什么在上一节中,使用模式匹配和传统的 switch 语句,可以在两个 case 标签中使用相同的模式变量名。但是,具有穿透语义的 case 标签声明模式变量会导致编译时错误。关于这一点,下面的类可以证明:


import java.util.Stack;import java.util.Vector;import java.util.Collection;
public class SampleClass { static void get(Collection c) { switch (c) { case Stack s : s.push("second"); case Vector v : v.lastElement(); default : System.out.println("default"); } }}
复制代码


第一个语句组中缺少break; 语句,如果第二个语句组中的模式变量v未初始化,则 switch 可能会在第二个语句组处失败。上述类会产生如下编译时错误:


SampleClass.java:12: error: 非法穿透到模式        case Vector v  : v.lastElement();             ^
复制代码


只需在第一个语句组中添加break; 语句就可以修复这个错误:


import java.util.Stack;import java.util.Vector;import java.util.Collection;
public class SampleClass { static void get(Collection c) { switch (c) { case Stack s : s.push("second"); break; case Vector v : v.lastElement(); default : System.out.println("default"); } }}
复制代码

每个 case 标签只能有一个模式


无论是类型为case…:的 case 标签,还是类型为case…->的 case 标签,都不允许在单个 case 标签中组合使用多个模式,否则会导致编译时错误。也许不太明显,在单个 case 标签中组合使用多个模式会导致非法穿透,如下所示:


import java.util.Stack;import java.util.Vector;import java.util.Collection;
public class SampleClass { static Object get(Collection c) { return switch (c) { case Stack s, Vector v -> c; default -> c; }; }}
复制代码


上述代码会产生以下编译时错误:


SampleClass.java:11: error: 非法穿透到模式        case Stack s, Vector v -> c;                      ^
复制代码

一个 switch 块中只能有一个匹配所有的 case 标签


无论是 switch 语句还是 switch 表达式,在一个 switch 块中包含多个匹配所有的 case 标签都会导致编译时错误。匹配所有的 case 标签是指:


  1. 一个带有模式、可以无条件匹配选择器表达式的 case 标签

  2. default case 标签请看下面的演示类:


import java.util.Collection;
public class SampleClass { static Object get(Collection c) { return switch (c) { case Collection coll -> c; default -> c; }; }}
复制代码


编译这个类会产生以下错误信息:


SampleClass.java:13: error: switch同时具有无条件模式和default标签        default -> c;        ^
复制代码

类型覆盖的穷尽性


穷尽性意味着 switch 块必须处理选择器表达式所有可能的值。穷尽性要求只有在下列一项或多项适用的情况下才能满足:


a) 使用模式 switch 表达式/语句;


b) 使用case null


c) 选择器表达式不属于以下遗留类型:charbyteshortintCharacterByteShortIntegerString或枚举类型。


为了实现穷尽性,如果子类型不多的话,则可以为选择器表达式类型的每个子类型添加 case 标签。然而,如果子类型众多,这种方法可能会很啰嗦;例如,为Object类型的选择器表达式的每个引用类型添加 case 标签,甚或为Collection类型的选择器表达式的每个子类型添加 case 标签,都是不可行的。


为了演示穷尽性要求,请看下面这个类:


import java.util.Collection;import java.util.Stack;import java.util.LinkedList;import java.util.Vector;
public class SampleClass { static Object get(Collection c) { return switch (c) { case Stack s -> s.push("first"); case null -> throw new NullPointerException("null"); case LinkedList l -> l.getFirst(); case Vector v -> v.lastElement(); }; }}
复制代码


该类会产生以下编译时错误消息:


SampleClass.java:10: error: switch表达式未涵盖所有可能的输入值                return switch (c) {                       ^
复制代码


如下所示,只需要增加一个 default case 标签就可以解决这个问题:


import java.util.Collection;import java.util.Stack;import java.util.LinkedList;import java.util.Vector;
public class SampleClass { static Object get(Collection c) { return switch (c) { case Stack s -> s.push("first"); case null -> throw new NullPointerException("null"); case LinkedList l -> l.getFirst(); case Vector v -> v.lastElement(); default -> c; }; }}
复制代码


像下面这样,如果匹配所有的 case 标签所带有的模式可以无条件匹配选择器表达式,那么就可以满足穷尽性要求,但它无法显式地处理任何子类型。


import java.util.Collection;
public class SampleClass { static Object get(Collection c) { return switch (c) { case Collection coll -> c; }; }}
复制代码


default case 标签可用于满足穷尽性,但如果选择器表达式可能的取值非常少,有时候可以避免使用它。例如,如果选择器表达式的类型为java.util.Vector,只需提供一个子类java.util.Stack的 case 标签模式就可以避免。类似地,如果选择器表达式是密封类类型,则只有在密封类类型的permits子句中声明的类需要由 switch 块处理。

Switch case 标签中的泛型记录模式


Java 20 增加了对 switch 语句/表达式中泛型记录模式类型参数推断的支持。作为一个例子,考虑以下泛型记录:


record Triangle<S,T,V>(S firstCoordinate, T secondCoordinate,V thirdCoordinate){};
复制代码


在下面的 switch 块中,推断出的 record 模式如下:


Triangle<Coordinate,Coordinate,Coordinate>(var f, var s, var t): static void getPt(Triangle<Coordinate, Coordinate, Coordinate> tr){        switch (tr) {           case Triangle(var f, var s, var t) -> …;           case default -> …;        }}
复制代码

使用 MatchException 进行错误处理


Java 19 引入了java.lang.Runtime类的一个新的子类,旨在用更统一的方式处理模式匹配期间的异常。这个名为java.lang.MatchException的新类是一个预览 API。MatchException 不是专门为 switch 中的模式匹配而设计的,而是为所有模式匹配语言结构而设计的。当模式匹配最终未能匹配提供的任何模式时,在运行时可能就会抛出 MatchException。关于这一点,请看下面的应用程序。该应用程序的 case 标签中有一个 record 模式,而它所要匹配的 record 声明了一个除数为 0 的访问器方法。


record DivisionByZero(int i) {    public int i() {        return i / 0;    }}


public class SampleClass {
static DivisionByZero get(DivisionByZero r) { return switch(r) { case DivisionByZero(var i) -> r; };
}
public static void main(String[] argv) {
get(new DivisionByZero(42)); }}
复制代码


示例应用程序编译通过,没有错误,但运行时会抛出MatchException异常:


Exception in thread "main" java.lang.MatchException: java.lang.ArithmeticException: / by zero        at SampleClass.get(SampleClass.java:7)        at SampleClass.main(SampleClass.java:14)Caused by: java.lang.ArithmeticException: / by zero        at DivisionByZero.i(SampleClass.java:1)        at SampleClass.get(SampleClass.java:1)        ... 1 more
复制代码

小结


本文介绍了 Java 新增的对 switch 控制流结构模式匹配的支持。其主要改进是 switch 的选择器表达式可以是任何引用类型,并且 switch 的 case 标签可以包含模式,包括条件模式匹配。而且,如果你不愿意更新整个代码库,那么模式匹配也支持使用传统的 switch 语句及其穿透语义。


原文链接:

https://www.infoq.com/articles/pattern-matching-for-switch/

2023-07-18 08:003908

评论

发布
暂无评论
发现更多内容

小白科普丨何为树、二叉树和森林

华为云开发者联盟

开发 华为云 企业号 2 月 PK 榜 华为云开发者联盟

【计算讲谈社】第十六讲|当我们在谈目标时,究竟在谈什么?

大咖说

软件测试/测试开发 | app自动化测试(Android)—Capability 使用进阶

测试人

软件测试 自动化测试 测试开发 appium app自动化测试

基于ModelArts进行流感患者密接排查

华为云开发者联盟

人工智能 华为云 行人检测 企业号 2 月 PK 榜 华为云开发者联盟

前端培训机构毕业后该注意什么?

小谷哥

开源!MatrixBench:实时物联网场景的数据压测“兵法秘籍”

YMatrix 超融合数据库

开源 物联网 超融合数据库 YMatrix MatrixBench

Grafana组件升级和离线镜像源

TiDB 社区干货传送门

监控 版本升级

为啥要对jvm做优化

华为云开发者联盟

开发 华为云 企业号 2 月 PK 榜 华为云开发者联盟

前端技术培训学习的就业怎么样?

小谷哥

软件测试/测试开发 | app自动化测试(Android)—参数化用例

测试人

软件测试 自动化测试 测试开发 appium app自动化测试

DR Auto-Sync 的 ACID 恢复功能简介和长期断网应急处理方案

TiDB 社区干货传送门

管理与运维 数据库架构设计

看板:自我管理的高效工具!

敏捷开发

项目管理 软件开发 看板

DR Auto-Sync 搭建和灾难恢复手册

TiDB 社区干货传送门

管理与运维 数据库架构设计

TiDB 的数据加载性能调优方案

TiDB 社区干货传送门

性能调优 应用适配

墨天轮《2022年中国数据库行业年度分析报告》正式发布,精彩抢先看

墨天轮

数据库 Serverless 云原生 国产数据库 HTAP

【ha知识两问】ha软件是什么?ha软件用途有哪些?

行云管家

高可用 ha 日志审计 双机热备

2023年知名堡垒机厂商及价格简单说明

行云管家

网络安全 信息安全 数据安全 堡垒机

品牌不得不投放户外LED广告的原因

Dylan

LED显示屏 户外LED显示屏 led显示屏厂家

云小课|GaussDB如何进行性能调优

华为云开发者联盟

数据库 后端 华为云 企业号 2 月 PK 榜 华为云开发者联盟

PingCAP 黄东旭万字长文剖析数据库发展新趋势:脱离应用开发者的数据库,不会成功

TiDB 社区干货传送门

数据库前沿趋势

机房搬迁更改集群IP

TiDB 社区干货传送门

Cloud + TiDB 技术解读

TiDB 社区干货传送门

【Nacos配置管理】一文带你搞懂Nacos配置管理模块

石臻臻的杂货铺

nacos

Apipost预执行脚本使用教程

徐天

TiDB Operator--K8S集群基础环境配置

TiDB 社区干货传送门

实践案例 集群管理 管理与运维 安装 & 部署 扩/缩容

ChatGPT3.5 !微软最新官宣整合OpenAI的14个产品细节,改变从视频会议Teams开始

B Impact

Apipost产品介绍

徐天

构建工具tsup入门第一部分

小鑫同学

前端 编译 工具链

通过Jmeter对TiDB数据库进行压测

TiDB 社区干货传送门

监控 性能调优 实践案例 故障排查/诊断 安装 & 部署

通过Jmeter批量向TiDB数据库插入数据

TiDB 社区干货传送门

性能调优 实践案例 管理与运维 安装 & 部署 数据库连接

webhook告警配置

TiDB 社区干货传送门

Java新特性完整指南:Switch模式匹配_编程语言_Deepak Vohra_InfoQ精选文章