Java 中文官方教程 2022 版(四十)(3)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: Java 中文官方教程 2022 版(四十)

Java 中文官方教程 2022 版(四十)(2)https://developer.aliyun.com/article/1488166

创建一个事件迭代器

第三步是创建一个事件迭代器:

XMLEventReader r = factory.createXMLEventReader
                       (filename, new FileInputStream(filename));
while (r.hasNext()) {
    XMLEvent e = r.nextEvent();
    System.out.println(e.toString());
}

获取事件流

最后一步是获取底层事件流:

public final static String getEventTypeString(int eventType) {
    switch (eventType) {
        case XMLEvent.START_ELEMENT:
            return "START_ELEMENT";
        case XMLEvent.END_ELEMENT:
            return "END_ELEMENT";
        case XMLEvent.PROCESSING_INSTRUCTION:
            return "PROCESSING_INSTRUCTION";
        case XMLEvent.CHARACTERS:
            return "CHARACTERS";
        case XMLEvent.COMMENT:
            return "COMMENT";
        case XMLEvent.START_DOCUMENT:
            return "START_DOCUMENT";
        case XMLEvent.END_DOCUMENT:
            return "END_DOCUMENT";
        case XMLEvent.ENTITY_REFERENCE:
            return "ENTITY_REFERENCE";
        case XMLEvent.ATTRIBUTE:
            return "ATTRIBUTE";
        case XMLEvent.DTD:
            return "DTD";
        case XMLEvent.CDATA:
            return "CDATA";
        case XMLEvent.SPACE:
            return "SPACE";
    }
    return "UNKNOWN_EVENT_TYPE," + eventType;
}

返回输出

当你运行事件示例时,EventParse 类被编译,XML 流被解析为事件并返回到 STDOUT。例如,Author 元素的一个实例被返回为:

<[’http://www.publishing.org’]::Author>
    Dhirendra Brahmachari
</[’http://www.publishing.org’]::Author>

请注意,在这个示例中,事件包括一个包含命名空间的开标签和闭标签,两者都包含元素的内容作为字符串返回在标签内。

同样,一个 Cost 元素的一个实例被返回如下:

<[’http://www.publishing.org’]::Cost currency=’INR’>
    11.50
</[’http://www.publishing.org’]::Cost

在这种情况下,currency 属性和值在事件的开标签中返回。

运行事件示例

  1. 要编译和运行事件示例,在终端窗口中,转到 INSTALL_DIR/jaxp-version/samples/ 目录并输入以下内容:
javac -classpath ../lib/jaxp-ri.jar stax/event/*.java
  1. BookCatalogue.xml 文件运行 EventParse 示例,使用以下命令。
java stax/event/EventParse stax/data/BookCatalogue.xml
  1. EventParse 将打印出由 BookCatalogue.xml 文件定义的所有元素的数据。

过滤器示例

位于 INSTALL_DIR/jaxp-version/samples/stax/filter/ 目录中,MyStreamFilter.java 演示了如何使用 StAX 流过滤器 API 过滤应用程序不需要的事件。在这个示例中,解析器过滤掉除了 StartElementEndElement 之外的所有事件。

实现 StreamFilter 类

MyStreamFilter 类实现了 javax.xml.stream.StreamFilter

public class MyStreamFilter implements javax.xml.stream.StreamFilter {
    // ...
}

创建一个输入工厂

下一步是创建一个 XMLInputFactory 实例。在这种情况下,还在工厂上设置了各种属性:

XMLInputFactory xmlif = null ;
try {
    xmlif = XMLInputFactory.newInstance();
    xmlif.setProperty(
        XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES,
        Boolean.TRUE);
    xmlif.setProperty(
        XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES,
        Boolean.FALSE);
    xmlif.setProperty(
        XMLInputFactory.IS_NAMESPACE_AWARE,
        Boolean.TRUE);
    xmlif.setProperty(
        XMLInputFactory.IS_COALESCING,
        Boolean.TRUE);
} 
catch (Exception ex) {
    ex.printStackTrace();
}
System.out.println("FACTORY: " + xmlif);
System.out.println("filename = "+ filename);

创建过滤器

下一步是实例化一个文件输入流并创建流过滤器:

FileInputStream fis = new FileInputStream(filename);
XMLStreamReader xmlr = xmlif.createFilteredReader(
                           xmlif.createXMLStreamReader(fis), 
                           new MyStreamFilter());
int eventType = xmlr.getEventType();
printEventType(eventType);
while (xmlr.hasNext()) {
    eventType = xmlr.next();
    printEventType(eventType);
    printName(xmlr,eventType);
    printText(xmlr);
    if (xmlr.isStartElement()) {
        printAttributes(xmlr);
    }
    printPIData(xmlr);
    System.out.println("-----------------------");
}

捕获事件流

下一步是捕获事件流。这与事件示例中的方式基本相同。

过滤流

最后一步是过滤流:

public boolean accept(XMLStreamReader reader) {
    if (!reader.isStartElement() && !reader.isEndElement())
        return false;
    else
        return true;
}

返回输出

当你运行过滤器示例时,MyStreamFilter 类被编译,XML 流被解析为事件并返回到 STDOUT。例如,一个 Author 事件被返回如下:

EVENT TYPE(1):START_ELEMENT
HAS NAME: Author
HAS NO TEXT
HAS NO ATTRIBUTES
-----------------------------
EVENT TYPE(2):END_ELEMENT
HAS NAME: Author
HAS NO TEXT
-----------------------------

同样,一个 Cost 事件被返回如下:

EVENT TYPE(1):START_ELEMENT
HAS NAME: Cost
HAS NO TEXT
HAS ATTRIBUTES:
 ATTRIBUTE-PREFIX:
 ATTRIBUTE-NAMESP: null
ATTRIBUTE-NAME:   currency
ATTRIBUTE-VALUE: USD
ATTRIBUTE-TYPE:  CDATA
-----------------------------
EVENT TYPE(2):END_ELEMENT
HAS NAME: Cost
HAS NO TEXT
-----------------------------

查看 迭代器 API 和 读取 XML 流 以获取更详细的 StAX 事件解析讨论。

运行过滤器示例

  1. 要编译和运行过滤器示例,在终端窗口中,转到 INSTALL_DIR/jaxp-version/samples/ 目录并输入以下内容:
javac -classpath ../lib/jaxp-ri.jar stax/filter/*.java
  1. java.endorsed.dirs 系统属性设置为指向 samples/lib 目录的情况下,对 BookCatalogue.xml 文件运行 MyStreamFilter 示例,使用以下命令。
java -Djava.endorsed.dirs=../lib stax/filter/MyStreamFilter -f stax/data/BookCatalogue.xml
  1. MyStreamFilter 将打印出由 BookCatalogue.xml 文件定义的事件作为 XML 流。

读写示例

位于INSTALL_DIR/jaxp-version/samples/stax/readnwrite/目录中,EventProducerConsumer.java演示了如何同时将 StAX 解析器用作生产者和消费者。

StAX XMLEventWriter API 扩展自XMLEventConsumer接口,并被称为事件消费者。相比之下,XMLEventReader是一个事件生产者。StAX 支持同时读取和写入,因此可以顺序地从一个 XML 流中读取并同时写入到另一个流中。

读写示例展示了如何使用 StAX 生产者/消费者机制同时读取和写入。该示例还展示了如何修改流以及如何动态添加新事件,然后写入到不同的流中。

创建一个事件生产者/消费者

第一步是实例化一个事件工厂,然后创建一个事件生产者/消费者的实例:

XMLEventFactory m_eventFactory = XMLEventFactory.newInstance();
public EventProducerConsumer() {
    // ...
    try {
        EventProducerConsumer ms = new EventProducerConsumer();
        XMLEventReader reader = XMLInputFactory.newInstance().
        createXMLEventReader(new java.io.FileInputStream(args[0]));
        XMLEventWriter writer = 
            XMLOutputFactory.newInstance().createXMLEventWriter(System.out);
    }
    // ...
}

创建一个迭代器

下一步是创建一个迭代器来解析流:

while (reader.hasNext()) {
    XMLEvent event = (XMLEvent)reader.next();
    if (event.getEventType() == event.CHARACTERS) {
        writer.add(ms.getNewCharactersEvent(event.asCharacters()));
    }
    else {
        writer.add(event);
    }
}
writer.flush();

创建一个写入器

最后一步是创建一个流写入器,形式为一个新的Character事件:

Characters getNewCharactersEvent(Characters event) {
    if (event.getData().equalsIgnoreCase("Name1")) {
        return m_eventFactory.createCharacters(
                   Calendar.getInstance().getTime().toString());
    }
    // else return the same event
    else {
        return event;
    }
}

返回输出

运行读写示例时,EventProducerConsumer类被编译,并且 XML 流被解析为事件并写回到STDOUT。输出是示例 XML 文档中描述的BookCatalog.xml文件的内容。

运行读写示例

  1. 要编译和运行读写示例,在终端窗口中,转到INSTALL_DIR/jaxp-version/samples/目录并输入以下内容:
javac -classpath ../lib/jaxp-ri.jar stax/readnwrite/*.java
  1. BookCatalogue.xml文件上运行EventProducerConsumer示例,使用以下命令。
java stax/readnwrite/EventProducerConsumer stax/data/BookCatalogue.xml
  1. EventProducerConsumer将打印出BookCatalogue.xml文件的内容。

写入器示例

位于INSTALL_DIR/jaxp-version/samples/stax/writer/目录中,CursorWriter.java演示了如何使用 StAX 游标 API 编写 XML 流。

创建输出工厂

第一步是创建一个XMLOutputFactory的实例:

XMLOutputFactory xof =  XMLOutputFactory.newInstance();

创建一个流写入器

下一步是创建一个XMLStreamWriter的实例:

XMLStreamWriter xtw = null;

写入流

最后一步是写入 XML 流。请注意,在写入最终的EndDocument后,流会被刷新并关闭:

xtw = xof.createXMLStreamWriter(new FileWriter(fileName));
xtw.writeComment("all elements here are explicitly in the HTML namespace");
xtw.writeStartDocument("utf-8","1.0");
xtw.setPrefix("html", "http://www.w3.org/TR/REC-html40");
xtw.writeStartElement("http://www.w3.org/TR/REC-html40","html");
xtw.writeNamespace("html", "http://www.w3.org/TR/REC-html40");
xtw.writeStartElement("http://www.w3.org/TR/REC-html40", "head");
xtw.writeStartElement("http://www.w3.org/TR/REC-html40", "title");
xtw.writeCharacters("Frobnostication");
xtw.writeEndElement();
xtw.writeEndElement();
xtw.writeStartElement("http://www.w3.org/TR/REC-html40", "body");
xtw.writeStartElement("http://www.w3.org/TR/REC-html40", "p");
xtw.writeCharacters("Moved to");
xtw.writeStartElement("http://www.w3.org/TR/REC-html40", "a");
xtw.writeAttribute("href","http://frob.com");
xtw.writeCharacters("here");
xtw.writeEndElement();
xtw.writeEndElement();
xtw.writeEndElement();
xtw.writeEndElement();
xtw.writeEndDocument();
xtw.flush();
xtw.close();

返回输出

运行写入器示例时,CursorWriter类被编译,并且 XML 流被解析为事件并写入到名为dist/CursorWriter-Output的文件中:

<!--all elements here are explicitly in the HTML namespace-->
<?xml version="1.0" encoding="utf-8"?>
<html:html >
<html:head>
<html:title>Frobnostication</html:title></html:head>
<html:body>
<html:p>Moved to <html:a href="http://frob.com">here</html:a>
</html:p>
</html:body>
</html:html>

在实际的dist/CursorWriter-Output文件中,该流是连续写入的,没有任何换行符;这里添加了换行符以便更容易阅读清单。在这个示例中,与事件示例中的对象流一样,命名空间前缀被添加到 HTML 标签的开头和结尾。虽然 StAX 规范不要求添加这个前缀,但是当输出流的最终范围不明确时,这是一个良好的实践。

运行写入器示例

  1. 要编译和运行 Writer 示例,在终端窗口中,转到 INSTALL_DIR/jaxp-version/samples/ 目录,并输入以下内容:
javac -classpath \
    ../lib/jaxp-ri.jar stax/writer/*.java
  1. 运行 CursorWriter 示例,指定输出应写入的文件名。
java stax/writer/CursorWriter -f *output_file*
  1. CursorWriter 将创建一个包含 返回输出 中显示的数据的相应名称的输出文件。

更多信息

docs.oracle.com/javase/tutorial/jaxp/stax/info.html

有关 StAX 的更多信息,请参见:

有关使用 StAX 的一些有用文章,请参见:

课程:JAXP 1.5 和新属性

原文:docs.oracle.com/javase/tutorial/jaxp/properties/index.html

本课程重点介绍了 JAXP 1.5 中引入的新属性。

JAXP 1.5 被添加到了 7u40 和 JDK 8 版本中。你可以从java.net下载当前的JDK 8 快照

背景

原文:docs.oracle.com/javase/tutorial/jaxp/properties/backgnd.html

JAXP 安全处理功能对 XML 处理器施加资源限制,以抵御某些类型的拒绝服务攻击。 但是,它并不限制获取外部资源的方式,这在尝试安全处理 XML 文档时也是有用的。 当前的 JAXP 实现支持特定于实现的属性,可用于强制执行此类限制,但需要一种标准方法来实现。

JAXP 1.5 添加了三个新属性以及它们对应的系统属性,允许用户指定可以或不可以允许的外部连接类型。属性值是协议列表。 JAXP 处理器通过将协议与列表中的协议进行匹配来检查给定的外部连接是否被允许。 如果连接在列表中,则处理器将尝试建立连接,否则将拒绝连接。

JAXP 1.5 已经集成到 7u40 和 JDK8 中。

外部资源

原文:docs.oracle.com/javase/tutorial/jaxp/properties/resources.html

XML、Schema 和 XSLT 标准支持以下需要外部资源的构造。JDK XML 处理器的默认行为是建立连接并按照指定的方式获取外部资源。

  • 外部 DTD:引用外部文档类型定义(DTD),示例:
  • 外部实体引用:引用外部数据,语法:
    通用实体引用如下:
<?xml version="1.0" standalone="no" ?>
<!DOCTYPE doc [<!ENTITY otherFile SYSTEM "otherFile.xml">]>
<doc>
    <foo>
    <bar>&otherFile;</bar>
    </foo>
</doc>
  • 外部参数实体,语法 。例如:
<?xml version="1.0" standalone="no"?>
    <!DOCTYPE doc [
      <!ENTITY % foo SYSTEM "http://www.example.com/student.dtd"<
      %foo;
    ]>
  • XInclude:在 XML 文档中包含外部信息集
  • 使用 schemaLocation 属性、importinclude 元素引用 XML Schema 组件。示例:schemaLocation="http://www.example.com/schema/bar.xsd"
  • 使用 importinclude 元素合并样式表:语法:
  • xml-stylesheet 处理指令:用于在 xml 文档中包含样式表,语法:
  • XSLT document() 函数:用于访问外部 XML 文档中的节点。例如,新属性

    原文:docs.oracle.com/javase/tutorial/jaxp/properties/properties.html

    JAXP 1.5 定义了三个新属性,用于调节 XML 处理器是否解析上述外部资源。这些属性是:

    • javax.xml.XMLConstants.ACCESS_EXTERNAL_DTD
    • javax.xml.XMLConstants.ACCESS_EXTERNAL_SCHEMA
    • javax.xml.XMLConstants.ACCESS_EXTERNAL_STYLESHEET

    这些 API 属性具有相应的系统属性和 jaxp.properties。

    ACCESS_EXTERNAL_DTD

    名称http://javax.xml.XMLConstants/property/accessExternalDTD

    定义:限制对外部 DTD、外部实体引用到指定协议的访问。

    :参见属性的值

    默认值all,允许连接到所有协议。

    系统属性javax.xml.accessExternalDTD

    ACCESS_EXTERNAL_SCHEMA

    名称http://javax.xml.XMLConstants/property/accessExternalSchema

    定义:限制对由schemaLocation属性、Import 和 Include 元素设置的外部引用协议的访问。

    :参见属性的值

    默认值all,允许连接到所有协议。

    系统属性javax.xml.accessExternalSchema

    ACCESS_EXTERNAL_STYLESHEET

    名称http://javax.xml.XMLConstants/property/accessExternalStylesheet

    定义:限制对由样式表处理指令、文档函数、Import 和 Include 元素设置的外部引用协议的访问。

    :参见属性的值

    默认值all,允许连接到所有协议。

    系统属性javax.xml.accessExternalStylesheet

    ${java.home}/lib/jaxp.properties

    这些属性可以在jaxp.properties中指定,以定义所有使用 Java Runtime 的应用程序的行为。格式为property-name=[value][,value]*。例如:

    javax.xml.accessExternalDTD=file,http

    属性名称与系统属性相同:javax.xml.accessExternalDTDjavax.xml.accessExternalSchemajavax.xml.accessExternalStylesheet

    属性的值

    所有属性的值格式相同。

    :由逗号分隔的协议列表。协议是 URI 的 scheme 部分,或者在 JAR 协议的情况下,由冒号分隔的"jar"加上 scheme 部分。协议定义为:

    scheme = alpha *( alpha | digit | "+" | "-" | "." )

    其中 alpha = a-z 和 A-Z。

    以及 JAR 协议:

    jar[:scheme]

    协议不区分大小写。值中由Character.isSpaceChar定义的任何空格将被忽略。协议的示例包括filehttpjar:file

    默认值:默认值是实现特定的。在 JAXP 1.5 RI、Java SE 7u40 和 Java SE 8 中,默认值为all,授予所有协议的权限。

    授予所有访问:关键字all授予所有协议的权限。例如,在jaxp.properties中设置javax.xml.accessExternalDTD=all将允许系统像以前一样工作,无限制地访问外部 DTD 和实体引用。

    拒绝任何访问:空字符串,即"",表示不授予任何协议权限。例如,在jaxp.properties中设置javax.xml.accessExternalDTD=""将指示 JAXP 处理器拒绝任何外部连接。

    范围和顺序

    原文:docs.oracle.com/javase/tutorial/jaxp/properties/scope.html

    javax.xml.XMLConstants#FEATURE_SECURE_PROCESSING(FSP)是包括 DOM、SAX、Schema Validation、XSLT 和 XPath 在内的 XML 处理器的必需功能。当设置为true时,建议实现启用由上述新属性定义的访问限制。为了兼容性,尽管对于 DOM、SAX 和 Schema Validation,默认情况下 FSP 为 true,但 JAXP 1.5 不会启用新的限制。

    对于 JDK 8,建议将新的accessExternal*属性在 FSP 被明确设置时设置为空字符串。这仅在通过 API 设置 FSP 时才会发生,例如factory.setFeature(FSP, true)。尽管对于 DOM、SAX 和 Schema Validation,默认情况下 FSP 为 true,但 JDK 8 并不将其视为“明确”设置,因此默认情况下不会设置限制。

    jaxp.properties文件中指定的属性会影响 JDK 或 JRE 的所有调用,并将覆盖其默认值,或者可能已经由 FEATURE_SECURE_PROCESSING 设置的值。

    当设置系统属性时,将仅影响一个调用,并将覆盖默认设置或在 jaxp.properties 中设置的设置,或者可能已经由 FEATURE_SECURE_PROCESSING 设置的设置。

    通过 JAXP 工厂或SAXParser指定的 JAXP 属性优先于系统属性,jaxp.properties文件以及javax.xml.XMLConstants#FEATURE_SECURE_PROCESSING

    新的 JAXP 属性在以下情况下对其试图限制的相关构造没有影响:

    • 当存在解析器并且解析器返回的源不为 null 时。这适用于可能设置在 SAX 和 DOM 解析器上的实体解析器,StAX 解析器上的 XML 解析器,SchemaFactory 上的 LSResourceResolver,验证器或 ValidatorHandler,或者转换器上的 URIResolver。
    • 当通过调用SchemaFactorynewSchema方法显式创建模式时。
    • 当不需要外部资源时。例如,以下功能/属性由参考实现支持,并可用于指示处理器不加载外部 DTD 或解析外部实体。
    http://apache.org/xml/features/disallow-doctype-decl true
    http://apache.org/xml/features/nonvalidating/load-external-dtd false
    http://xml.org/sax/features/external-general-entities false
    http://xml.org/sax/features/external-parameter-entities false

    与安全管理器的关系

    原文:docs.oracle.com/javase/tutorial/jaxp/properties/security.html

    在尝试连接之前,将首先检查 JAXP 属性,无论是否存在SecurityManager。这意味着即使SecurityManager授予权限,连接也可能被阻止。例如,如果 JAXP 属性被设置为禁止 http 协议,它们将有效地阻止任何连接尝试,即使应用程序具有SocketPermission

    为了限制连接,SecurityManager可以被视为处于较低级别。在评估 JAXP 属性之后,权限将被检查。例如,如果一个应用程序没有SocketPermission,即使 JAXP 属性被设置为允许 http 连接,也会抛出SecurityException

    当存在SecurityManager时,JAXP FEATURE_SECURE_PROCESSING被设置为 true。这种行为不会启用新的限制。

    JDK 中的属性设置

    原文:docs.oracle.com/javase/tutorial/jaxp/properties/propSettings.html

    以下表格显示了 JDK 中新属性的默认值和行为。

    访问属性的值 默认值 设置 FSP(a) jaxp.properties 系统属性 API 属性
    7u40 all 无更改 覆盖 覆盖 覆盖
    JDK8 all 更改为 “” 覆盖 覆盖 覆盖

    (a) 设置 FSP 意味着明确使用 JAXP 工厂的 setFeature 方法设置 FEATURE_SECURE_PROCESSING。

    (b) 7u40 和 JDK8 之间唯一的行为差异是,在 7u40 中设置 FSP 不会更改 accessExternal* 属性,但在 JDK8 中会将值设置为空字符串。在 JDK8 中,设置 FSP 被视为选择加入。

    © 表中从左到右的顺序反映了覆盖顺序。例如,如果通过 API 设置了 accessExternal 属性,则会覆盖其他可能已通过其他方式设置的属性。

    Java 中文官方教程 2022 版(四十)(4)https://developer.aliyun.com/article/1488170

相关文章
|
11天前
|
Java 开发工具 Android开发
Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)
Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)
|
2月前
|
Java 开发者 UED
【实战宝典】Java异常处理大师级教程:throws关键字,让异常声明成为你的专属标签!
【实战宝典】Java异常处理大师级教程:throws关键字,让异常声明成为你的专属标签!
48 3
|
5天前
|
Java 数据库连接 编译器
Kotlin教程笔记(29) -Kotlin 兼容 Java 遇到的最大的“坑”
Kotlin教程笔记(29) -Kotlin 兼容 Java 遇到的最大的“坑”
|
8天前
|
Java 编译器 Android开发
Kotlin教程笔记(28) -Kotlin 与 Java 混编
本系列教程笔记详细讲解了Kotlin语法,适合希望深入了解Kotlin的开发者。对于需要快速学习Kotlin的小伙伴,推荐查看“简洁”系列教程。本篇笔记重点介绍了Kotlin与Java混编的技巧,包括代码转换、类调用、ProGuard问题、Android库开发建议以及相互调用时的注意事项。
14 3
|
10天前
|
Java 编译器 Android开发
Kotlin教程笔记(28) -Kotlin 与 Java 混编
Kotlin教程笔记(28) -Kotlin 与 Java 混编
18 3
|
11天前
|
安全 Java 编译器
Kotlin教程笔记(27) -Kotlin 与 Java 共存(二)
Kotlin教程笔记(27) -Kotlin 与 Java 共存(二)
|
17天前
|
消息中间件 存储 JSON
rabbitmq基础教程(ui,java,springamqp)
本文提供了RabbitMQ的基础教程,包括如何使用UI创建队列和交换机、Java代码操作RabbitMQ、Spring AMQP进行消息发送和接收,以及如何使用不同的交换机类型(fanout、direct、topic)进行消息路由。
13 0
rabbitmq基础教程(ui,java,springamqp)
|
18天前
|
存储 前端开发 Java
Java后端如何进行文件上传和下载 —— 本地版(文末配绝对能用的源码,超详细,超好用,一看就懂,博主在线解答) 文件如何预览和下载?(超简单教程)
本文详细介绍了在Java后端进行文件上传和下载的实现方法,包括文件上传保存到本地的完整流程、文件下载的代码实现,以及如何处理文件预览、下载大小限制和运行失败的问题,并提供了完整的代码示例。
114 1
|
9天前
|
安全 Java 编译器
Kotlin教程笔记(27) -Kotlin 与 Java 共存(2)
Kotlin教程笔记(27) -Kotlin 与 Java 共存(2)
|
9天前
|
Java 开发工具 Android开发
Kotlin教程笔记(26) -Kotlin 与 Java 共存(1)
Kotlin教程笔记(26) -Kotlin 与 Java 共存(1)