找回密码
 会员注册
查看: 35|回复: 0

Java23新特性来袭(附示例):super不再是构造函数第一条语句

[复制链接]

2

主题

0

回帖

7

积分

新手上路

积分
7
发表于 2024-9-3 16:28:10 | 显示全部楼层 |阅读模式
前言Java23自2024年6月6日起进入所谓的“Rampdown第一阶段”,因此该版本中将不再包含任何JDK增强提案(JEP)。因此,功能集已修复。只会纠正错误,并且如有必要,将进行微小改进。目标发布日期为2024年9月17日。您可以在此处下载最新的抢先体验版本。Java23的亮点:Markdown文档注释:最后,我们可以用Markdown编写JavaDoc。使用Patterns、instanceof和switch中的基本数据类型primitivetypes将变量与基元类型进行匹配(预览)。使用模块导入声明(importmodule)导入整个模块。直接使用print()、println()和readln():不需带System.in和System.out的输入和输出,带隐式声明的类和实例main方法。在调用super(...)之前,允许定义其它语句(即super不再是构造函数第一条语句)使用灵活的构造函数体初始化构造函数中的字段。此外,Java21和Java22中引入的许多其他功能也正在进入新一轮预览,无论是否有细微变化。Java21中引入并在Java22中重新引入的字符串模板是个例外:它们不再包含在Java23中。根据GavinBierman的说法,大家一致认为需要修改设计,但对于如何实际进行修改存在分歧。因此,语言开发人员决定花更多时间来修改设计,并在以后的Java版本中以完全修改的形式呈现该功能。与往常一样,我对所有JEP和其他更改都使用原始英文。1.Markdown文档注释–JEP467为了格式化JavaDoc注释,我们一直必须使用HTML。这在1995年无疑是一个不错的选择,但如今,Markdown比HTML更受欢迎,更适合用于编写文档。JDK增强提案467允许我们从Java23开始在Markdown中编写JavaDoc注释。以下示例Math.ceilMod(...)以常规符号显示了该方法的文档:/***Returnstheceilingmodulusofthe{@codelong}and{@codeint}arguments.* *Theceilingmodulusis{@coder=x–(ceilDiv(x,y)*y)},*hastheoppositesignasthedivisor{@codey}oriszero,and*isintherangeof{@code-abs(y)*Therelationshipbetween{@codeceilDiv}and{@codeceilMod}issuchthat:**{@codeceilDiv(x,y)*y+ceilMod(x,y)==x}** *Forexamples,see{@link#ceilMod(int,int)}.**@paramxthedividend*@paramythedivisor*@returntheceilingmodulus{@codex–(ceilDiv(x,y)*y)}*@throwsArithmeticExceptionifthedivisor{@codey}iszero*@see#ceilDiv(long,int)*@since18*/12345678910111213141516171819202122该示例包含格式化的代码、段落标记、项目符号列表、链接以及JavaDoc特定信息,例如@param和@return。要使用Markdown,我们需要用三个斜杠(///)开始JavaDoc注释的所有行。Markdown中的相同注释如下所示:///Returnstheceilingmodulusofthe`long`and`int`arguments.//////Theceilingmodulusis`r=x–(ceilDiv(x,y)*y)`,///hastheoppositesignasthedivisor`y`oriszero,and///isintherangeof`-abs(y)已被空行替换。枚举项以连字符引入。链接用标记[…],而不是{@link…}。JavaDoc特定的细节(例如@param和@return)保持不变。支持以下文本格式:///**Thistextisbold.**///*Thistextisitalic.*///_Thisisalsoitalic._///`Thisissourcecode.`//////```///Thisisablockofsourcecodex.///```//////Indentedtext///isalsorenderedasacodeblock.//////~~~///Thisisalsoablockofsourcecode///~~~123456789101112131415支持枚举列表和编号列表:///Thisisabulletedlist:///–One///–Two///–Three//////Thisisanumberedlist:///1.One///1.Two///1.Three123456789您还可以显示简单的表格:///|Binary|Decimal|///|--------|---------|///|00|0|///|01|1|///|10|2|///|11|3|123456您可以按照如下方式集成到其他程序元素的链接:///Links:///–einModul:[java.base/]///–einPaket:[java.lang]///–eineKlasse:[Integer]///–einFeld:[Integer#MAX_VALUE]///–eineMethode:[Integer#parseInt(String,int)]123456如果链接文本和链接目标不同,可以将链接文本放在前面的方括号中:///Links:///–[einModul][java.base/]///–[einPaket][java.lang]///–[eineKlasse][Integer]///–[einFeld][Integer#MAX_VALUE]///–[eineMethode][Integer#parseInt(String)]123456最后但同样重要的一点是,如果在代码或代码块中使用JavaDoc标签(例如@param、@throws等),则不会对其进行评估。2.Java23中的新预览功能Java23引入了两个新的预览功能。您不应在生产代码中使用这些功能,因为它们仍可能发生变化(或者,就像字符串模板的情况一样,可能会在短时间内再次被删除)。javac您必须通过VM选项在命令中明确启用预览功能–enable-preview--source23。对于该java命令来说,–enable-preview这就足够了。2.1模块导入声明(预览)–JEP476从Java1.0开始,java.lang包中的所有类都会自动导入到每个.java文件中。这就是为什么我们可以不加语句地使用诸如Object、String、Integer、Exception、Thread等类import。我们也一直能够导入完整的软件包。例如,导入java.util.*意味着我们不必单独导入List、Set、Map、ArrayList、HashSet和HashMap等类。JDK增强提案476现在允许我们导入完整的模块-更准确地说,是模块导出的包中的所有类。例如,我们可以按如下方式导入完整的java.base模块,并使用此模块中的类(在示例List、Map、Collectors、Stream中),无需进一步导入:importmodulejava.base;publicstaticMap>groupByFirstLetter(String...values){returnStream.of(values).collect(Collectors.groupingBy(s->Character.toUpperCase(s.charAt(0))));}123456要使用importmodule,导入类本身不需要位于模块中。2.1.1模糊的类名如果有两个导入的类具有相同的名称,例如以下示例中的Date,则会发生编译器错误:importmodulejava.base;importmodulejava.sql;...Datedate=newDate();//Compilererror:"referencetoDateisambiguous"...123456解决方案很简单:我们还必须直接导入所需的Date类:importmodulejava.base;importmodulejava.sql;importjava.util.Date;//⟵Thisresolvestheambiguity...Datedate=newDate();...12345672.1.2过渡导入如果导入的模块传递性地导入另一个模块,那么我们也可以使用传递性导入模块的导出包的所有类,而无需显式导入。例如,java.sql模块以传递方式导入java.xml模块:modulejava.sql{...requirestransitivejava.xml;...}12345因此,在以下示例中,我们不需要SAXParserFactory和SAXParser的任何显式导入,也不需要java.xml`模块的显式导入:importmodulejava.sql;...SAXParserFactoryfactory=SAXParserFactory.newInstance();SAXParsersaxParser=factory.newSAXParser();...1234562.1.3JShell中的自动模块导入JShell会自动导入10个常用包。此JEP将使JShell在将来导入完整的java.base模块。通过调用JShell一次不使用–enable-preview和一次使用–enable-preview,然后输入/imports命令,可以很好地演示这一点:$jshell|WelcometoJShell--Version23-ea|Foranintroductiontype:/helpintrojshell>/imports|importjava.io.*|importjava.math.*|importjava.net.*|importjava.nio.file.*|importjava.util.*|importjava.util.concurrent.*|importjava.util.function.*|importjava.util.prefs.*|importjava.util.regex.*|importjava.util.stream.*jshell>/exit|Goodbye$jshell--enable-preview|WelcometoJShell--Version23-ea|Foranintroductiontype:/helpintrojshell>/imports|importjava.base12345678910111213141516171819202122232425当启动JShell而不启用预览(--enable-preview)时,您将看到十个导入的包;当使用--enable-preview启动它时,您将只看到java.base模块的导入。2.1.4隐式声明类中的自动模块导入java.base从Java23开始,隐式声明的类也会自动导入完整的模块。2.2模式中的原始类型、instanceof和switch(预览)–JEP455使用instanceof和switch,我们可以检查某个对象是否属于某种类型,如果是,则将该对象绑定到该类型的变量,执行特定的程序路径,并在该程序路径中使用新变量。例如,以下代码块(自Java16起已获准使用)检查对象是否为至少包含5个字符的字符串,如果是,则以大写形式打印。如果对象是整数,则计算该数字的平方并打印。否则,按原样打印对象。if(objinstanceofStrings&s.length()>=5){System.out.println(s.toUpperCase());}elseif(objinstanceofIntegeri){System.out.println(i*i);}else{System.out.println(obj);}1234567从Java21开始,我们可以使用switch更清楚地做到这一点:switch(obj){caseStringswhens.length()>=5->System.out.println(s.toUpperCase());caseIntegeri->System.out.println(i*i);casenull,default->System.out.println(obj);}12345然而,到目前为止,这只适用于对象。instanceof根本不能与基元数据类型一起使用,只在可以将基元类型byte、short、char和int的变量与常量匹配的范围内进行切换,例如:intx=...switch(x){case1,2,3->System.out.println("Low");case4,5,6->System.out.println("Medium");case7,8,9->System.out.println("High");}123456得益于JDK增强提案455,所有基元类型现在也可以在Java23的模式匹配中使用,包括instanceof和switch。JDK增强提案455在Java23中引入了两个更改:首先,所有基元类型现在都可以在switch表达式和语句中使用,包括long、float、double和boolean。其次,我们还可以在模式匹配中使用所有原始类型,包括instanceof和switch。在这两种情况下,即对于通过long、float、double和boolean进行切换,以及与原始变量进行模式匹配,切换(与所有新的切换功能一样)必须是详尽的,即涵盖所有可能的情况。2.2.1来自Java23:模式匹配中的原始类型使用原始模式时,确切的含义与使用对象时不同,因为原始类型没有继承:是一个基本类型的变量(即byte、short、int、long、float、double、char或boolean),B是这些基本类型之一。然后,如果a的精确值也可以存储在B类型的变量中,则B的实例结果为真。为了帮助你更好地理解这是什么意思,这里有一个简单的例子:intvalue=...if(valueinstanceofbyteb){System.out.println("b="+b);}1234代码应按如下方式读取:如果变量值的值也可以存储在字节变量中,则将此值分配给字节变量b并打印出来。例如,对于值=5,情况就是这样,但对于值=1000则不然,因为字节只能存储-128到127之间的值。与对象一样,对于基本类型,您还可以使用&直接在check实例中添加进一步的检查。例如,以下代码仅打印正字节值(即1到127):intvalue=...if(valueinstanceofbyteb&b>0){System.out.println("b="+b);}1234让我们为value分配一个具体值,并将其与所有数值基元类型进行比较(不允许将数值类型与布尔值进行比较,这会导致编译器错误):intvalue=65;if(valueinstanceofbyteb)System.out.println(value+"instanceofbyte:"+b);if(valueinstanceofshorts)System.out.println(value+"instanceofshort:"+s);if(valueinstanceofinti)System.out.println(value+"instanceofint:"+i);if(valueinstanceoflongl)System.out.println(value+"instanceoflong:"+l);if(valueinstanceoffloatf)System.out.println(value+"instanceoffloat:"+f);if(valueinstanceofdoubled)System.out.println(value+"instanceofdouble:"+d);if(valueinstanceofcharc)System.out.println(value+"instanceofchar:"+c);12345678此代码打印以下内容:65instanceofbyte:6565instanceofshort:6565instanceofint:6565instanceoflong:6565instanceoffloat:65.065instanceofdouble:65.065instanceofchar:A1234567因此,值65可以与所有其他基本类型(布尔值除外)一起存储。您可以看到,作为float和double,值显示为一位小数,作为char,显示为字符“A”(其ASCII码为65)。如果我们将值更改为100000,我们将得到以下输出:100000instanceofint:100000100000instanceoflong:100000100000instanceoffloat:100000.0100000instanceofdouble:100000.01234因此,值100000可以存储在int、long、float和double类型的变量中,但不能存储在byte、short和char类型的变量。它们的数字范围最多只能达到12732767和65535。当值=16_777_217时,它变得有趣:16777217instanceofint:1677721716777217instanceoflong:1677721716777217instanceofdouble:1.6777217E7123所以16777217可以存储在int、long和double中,但不能存储在float中?确实如此!运行以下代码:floatf=16_777_217;System.out.printf("f=%.1f%n",f);12结果出乎意料:f=16777216.01这是因为浮点型浮点精度有限,例如可以存储16.777.216、16.777.218和16.777.220,但不能存储介于两者之间的值16.777.217和16.777.219。下面是一个在instanceof之前使用浮点数的示例:floatvalue=3.5F;if(valueinstanceofbyteb)System.out.println(value+"instanceofbyte:"+b);if(valueinstanceofshorts)System.out.println(value+"instanceofshort:"+s);if(valueinstanceofinti)System.out.println(value+"instanceofint:"+i);if(valueinstanceoflongl)System.out.println(value+"instanceoflong:"+l);if(valueinstanceoffloatf)System.out.println(value+"instanceoffloat:"+f);if(valueinstanceofdoubled)System.out.println(value+"instanceofdouble:"+d);if(valueinstanceofcharc)System.out.println(value+"instanceofchar:"+c);12345678此代码块打印以下内容:3.5instanceoffloat:3.53.5instanceofdouble:3.512当然,因为一个有小数点的数字当然只能用浮点数和双精度表示。如果我们将值设置为100000.0F,结果如下:100000.0instanceofint:100000100000.0instanceoflong:100000100000.0instanceoffloat:100000.0100000.0instanceofdouble:100000.01234浮点数100000.0也可以存储在int或a中,只要它没有小数点。布尔值只能与boolean进行比较;布尔值与另一种类型的任何比较都会导致编译器错误。然而,这对我们没有多大帮助,因为布尔值与布尔值的比较总是得到true。2.2.2带switch的基本类型模式我们不仅可以在instanceof中使用原始模式,还可以在switch中使用:doublevalue=...switch(value){casebyteb->System.out.println(value+"instanceofbyte:"+b);caseshorts->System.out.println(value+"instanceofshort:"+s);casecharc->System.out.println(value+"instanceofchar:"+c);caseinti->System.out.println(value+"instanceofint:"+i);caselongl->System.out.println(value+"instanceoflong:"+l);casefloatf->System.out.println(value+"instanceoffloat:"+f);casedoubled->System.out.println(value+"instanceofdouble:"+d);}12345678910以下是一些值的示例以及相应值将导致的输出:0可以表示为字节(-128到127)。10000可以表示为短(-32768到32767)。50000可以表示为一个char(0到65535)。1000000可以表示为整数(-2147483648到2147483647)。1000000000000可以表示为一个长(大约负9万亿到正9万亿)。0.125可以表示为浮点数。另一方面,0.126只能表示为双精度。同样,对于switch中的原始类型模式,我们可以使用“guards”,即用when将布尔表达式附加到模式上:doublevalue=...switch(value){case1->...case2->...caseintiwheni...caseinti->...default->...}123456782.2.3支配型和支配型对于具有原始类型的切换,我们必须遵守支配和被支配类型的原则,就像对象类型一样。支配类型是指可以表示支配类型的所有值以及更多值的类型。例如,byte以int为主,因为每个字节也可以存储为int。看看以下代码(在以下示例中,我使用了Java22中未命名的变量_finalize):doublevalue=...switch(value){caseint_->System.out.println(value+"instanceofint");casebyte_->System.out.println(value+"instanceofbyte");casedouble_->System.out.println(value+"instanceofdouble");}123456在这种情况下,case字节标签永远不会匹配,因为每个字节也是一个int,因此已经由caseint标签处理。一般来说,支配类型必须始终列在支配类型之前。因此,以下内容是可以的:doublevalue=...switch(value){casebyte_->System.out.println(value+"instanceofbyte");caseint_->System.out.println(value+"instanceofint");casedouble_->System.out.println(value+"instanceofdouble");}123456switch的疲劳检查与Java21中添加的所有开关功能一样,以下规则适用:如果您使用其中一个新功能,则switch必须详尽无遗。这就是为什么前面的例子也包含一个casedouble。以下行为是不允许的:doublevalue=...switch(value){casebyte_->System.out.println(value+"instanceofbyte");caseint_->System.out.println(value+"instanceofint");}12345此开关不完整,因此无效,例如,没有标签与值3.5匹配。编译器将返回消息“switch语句未覆盖所有可能的输入值”以下switch将正常运行:shortvalue=...switch(value){casebyte_->System.out.println(value+"instanceofbyte");caseint_->System.out.println(value+"instanceofint");}12345虽然这里没有caseshort,但有一个caseint标签,每个可能的short值都与之匹配。为了总结对这一新特性的描述,我们不禁要问,在哪里可以合理地使用原始模式。我还没有遇到任何用例,JEP示例也没有真正说服我。如果你知道一个好的用例,请通过评论功能告诉我。3.重新提交的预览和孵化器功能Java23中再次推出了7个预览和孵化器功能,其中3个与Java22相比没有变化:3.1流收集器(第二预览版)–JEP473自从Java8引入StreamAPI以来,Java社区一直抱怨中间流操作的范围有限。诸如“窗口”或“折叠”之类的操作非常缺乏,而且反复被要求。JDK开发人员没有屈服于社区的压力而提供这些功能,而是有一个更好的想法:他们实现了一个API,他们和所有其他Java开发人员可以使用它来自己实现中间流操作。这个新API被称为“流收集器”。它首次由JDK增强提案461在Java22中引入,并由JDK增强提案473在Java23中第二次作为预览版未经更改地呈现,以便收集来自社区的进一步反馈。例如,通过以下代码,我们可以实现并使用中间流操作“map”作为流收集器:publicGatherermapping(Functionmapper){returnGatherer.of(Integrator.ofGreedy((state,element,downstream)->{RmappedElement=mapper.apply(element);returndownstream.push(mappedElement);}));}publicListtoLengths(Listwords){returnwords.stream().gather(mapping(String::length)).toList();}1234567891011121314您可以在有关流收集器的主要文章中确切了解流收集器的工作原理、有哪些限制,以及我们是否最终会得到期待已久的“窗口”和“折叠”操作。3.2隐式声明的类和实例主要方法(第三个预览)-JEP477当Java开发人员编写他们的第一个程序时,它通常看起来像这样(直到现在)publicclassHelloWorld{publicstaticvoidmain(String[]args){System.out.println("Helloworld!");}}12345Java初学者会同时面临许多新概念:通过classes.使用可见性修饰符public.使用静态方法.使用未使用的方法参数.和System.out。如果我们可以摆脱所有这些并专注于基本要素,那不是很好吗-就像下面的截图所示?这正是“隐式声明的类和实例主方法”所实现的!从Java23开始,以下代码是一个有效且完整的Java程序:voidmain(){println("Helloworld!");}123这是如何实现的?指定类不再是强制性的。如果省略类规范,编译器将生成隐式类。方法main()不必是public或static,也不必有参数。隐式类自动导入新类java.io.IO,其中包含静态方法print(…)、println(…)和readln(…)。有关更多详细信息、示例、需要遵守的限制以及main()重载多个方法时会发生什么情况,请参阅有关Javamain()方法的主要文章。这里描述的更改最初在Java21中以“未命名类和实例主方法”的名称发布。在Java22中,该功能的一些过于复杂的方面得到了简化,并且该功能已重命名为当前名称。在Java23中,JDK增强提案477添加了自动导入的java.io.IO类,因此最终System.out也可以省略,这在Java22的第二个预览版中还无法实现。请注意,该功能在Java23中仍处于预览阶段,必须使用VM选项激活–enable-preview。3.3结构化并发(第三次预览)–JEP480结构化并发是一种现代方法,通过虚拟线程将任务分成几个子任务以并行执行。结构化并发为并行任务的开始和结束提供了清晰的结构,并有序地处理错误。如果不再需要某些子任务的结果,则可以干净地取消这些子任务。使用结构化并发的一个例子是实现一种race()方法,该方法启动两个任务并返回完成的任务的结果,而另一个任务则自动取消:publicstaticRrace(Callabletask1,Callabletask2)throwsInterruptedException,ExecutionException{try(varscope=newStructuredTaskScope.ShutdownOnSuccess()){scope.fork(task1);scope.fork(task2);scope.join();returnscope.result();}}123456789您可以在有关结构化并发的主要文章中找到更详细的描述、更多用例和大量示例。结构化并发是Java21中的预览功能,并在Java22中再次呈现,没有任何变化。Java23中也没有变化(由JDK增强提案480指定)——JDK开发人员希望在最终确定该功能之前获得进一步的反馈。3.4作用域值(第三次预览)–JEP481作用域值可用于将值传递给远程方法调用,而无需将它们作为参数循环遍历调用链的所有方法。经典的例子是用户登录到要为其执行特定用例的web服务器。作为此类用例的一部分,许多方法都需要访问用户信息。使用ScopedValues,我们可以设置一个上下文,在该上下文中,所有方法都可以访问用户对象,而无需将其作为参数传递给所有这些方法。以下代码使用ScopedValue.where(…)创建上下文:publicclassServer{publicfinalstaticScopedValueLOGGED_IN_USER=ScopedValue.newInstance();...privatevoidserve(Requestrequest){...UserloggedInUser=authenticateUser(request);ScopedValue.where(LOGGED_IN_USER,loggedInUser).run(()->restAdapter.processRequest(request));...}}1234567891011现在,run(…)方法中调用的方法,以及它直接或间接调用的所有方法,例如调用堆栈中称为deep的存储库方法,都可以按如下方式访问用户:publicclassRepository{...publicDatagetData(UUIDid){Datadata=findById(id);UserloggedInUser=Server.LOGGED_IN_USER.get();if(loggedInUser.isAdmin()){enrichDataWithAdminInfos(data);}returndata;}...}123456789101112任何曾经使用过ThreadLocal变量的人都会认识到这种相似性。但是,与ThreadLocals相比,ScopedValues有几个优点。您可以在关于范围值的主文章中找到这些优势和全面的介绍。作用域值与Java21中的结构化并发一起作为预览功能引入,并在Java22中发送到第二轮预览,没有任何更改。在Java23中,JDK增强建议481将ScopedValue类的以下两个静态方法合并为一个://Java22:publicstaticRgetWhere(ScopedValuekey,Tvalue,Supplierop)publicstaticRcallWhere(ScopedValuekey,Tvalue,Callableop)123这些方法的不同之处仅在于,一个Supplier被传递给getWhere(…)(一个具有不声明异常的get()方法的函数接口),一个Callable被传递给callWhere(…)(一种具有声明抛出异常的call()方法)。假设我们想在作用域值的上下文中调用以下方法,其中SpecificException是一个已检查的异常:ResultdoSomethingSmart()throwsSpecificException{...}123在Java22中,我们必须按如下方式调用此方法://Java22:try{Resultresult=ScopedValue.callWhere(USER,loggedInUser,this::doSomethingSmart);}catch(Exceptione){//⟵CatchinggenericException...}123456由于Callable.call()抛出了通用的Exception,我们必须捕获Exception,即使被调用的方法抛出了更具体的异常。在Java23中,现在只有一种callWhere(…)方法:publicstaticRcallWhere(ScopedValuekey,Tvalue,ScopedValue.CallableOpop)throwsX12现在将a传递给方法,而不是aSupplier或a。这是一个函数式接口,定义如下:CallableScopedValue.CallableOp@FunctionalInterfacepublicstaticinterfaceScopedValue.CallableOp{Tcall()throwsX}1234这个新接口包含一个可能抛出的异常作为类型参数X。这允许编译器识别调用可能抛出哪种异常callWhere(…)——我们可以直接SpecificException在catch块中处理://Java23:try{Resultresult=ScopedValue.callWhere(USER,loggedInUser,()->doSomethingSmart());}catch(SpecificExceptione){//⟵CatchingSpecificException...}123456如果doSomethingSmart()没有抛出异常或者抛出了RuntimeException,我们可以省略catch块://Java23:Resultresult=callWhere(USER,loggedInUser,this::doSomethingSmart);12Java23中的这一变化使代码更具表现力,且更不容易出错。3.5灵活的构造函数主体(第二次预览)–JEP482假设您有如下类:publicclassConstructorTestParent{privatefinalinta;publicConstructorTestParent(inta){this.a=a;printMe();}voidprintMe(){System.out.println("a="+a);}}123456789101112并且我们假设您有第二个扩展该类的类:publicclassConstructorTestChildextendsConstructorTestParent{privatefinalintb;publicConstructorTestChild(inta,intb){super(a);this.b=b;}}12345678现在,您要确保在调用父类super构造函数之前,构造函数ConstructorTestChild中的a和b不为负数。以前不允许在构造函数之前进行相应的检查。这就是为什么我们不得不使用如下的扭曲方法:publicclassConstructorTestChildextendsConstructorTestParent{privatefinalintb;publicConstructorTestChild(inta,intb){super(verifyParamsAndReturnA(a,b));this.b=b;}privatestaticintverifyParamsAndReturnA(inta,intb){if(a
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2024-12-27 12:57 , Processed in 0.418571 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表