从此
📄文章 #️⃣专题 🌐上网 📺 🛒 📱

Java编程开发技术

综合
原则
Java主打跨平台,也是Android应用的开发语言之一。
专题

Java综合

新特性有:结构化并发(JEP 453)、外部函数和内存API(JEP 424)等。

JDK20+: URI.create("https://congci.com").toURL(); 取代了过时的 new URL("https://congci.com");

Java开发工具IDE - IntelliJ IDEANetBeansEclipse

  断点续传方案 - Java RandomAccessFile 大文件分片上传及合并。

Java基础

免源文件执行Java代码:

  命令行输入: jshell
  填入: "x".repeat(3)
  退出: /exit
  用法: /help


JDK21+(JEP445)新版main函数最简写法:
  void main() { System.out.println("Hello, World!"); }

Linux创建Java主函数文件: dd of=/tmp/hi.java << EOF void main() { System.out.println(System.getProperty("java.version")); } EOF 启动单文件源码命令: java --enable-preview --source 21 /tmp/hi.java Gradle启用预览特性: application { mainClass = 'App' applicationDefaultJvmArgs = ['--enable-preview'] } compileJava { options.compilerArgs += ['--enable-preview'] } 孵化特性还需要添加模块儿 --add-modules jdk.incubator.concurrent 单jar包启动: java -jar ./app.jar com.jpkg.App 或MANIFEST.MF用Class-Path: other.jar指定额外*.jar 多jar包启动: java -cp ./app.jar;./guava-31.1-jre.jar com.jpkg.App
JDK21以前版本主函数完整写法:
// 若为Linux Shebang脚本(如java.sh)则起行必须写上#!....

#!/usr/bin/java --source 11
class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!"); 
    }
}

chmod +x java.sh
./java.sh
[或] java --release 11 java.sh  传参...

标准编译:
  javac hi.java
  [指定字节码版本/JDK9前用单横杠的-source]:javac --help输出最低支持为Java8(JDK9+为Java6)
  javac --release 11 hi.java
  java hi
  带包名则 java pkg/hi

  或 JDK11+支持同时编译加执行:java hi.java

JVM类文件版本(class file format major versions/UnsupportedClassVersionError) - https://en.wikipedia.org/wiki/Java_class_file#cite_ref-3

断言:
  用途 - Java默认禁止断言,不会执行assert行代码,也不会报错;生产期间可手动启用java -ea。
  语法 - assert result; 或 assert result : "移除失败";

特殊情况:
  gradle等工具对JDK新版本很敏感,若报错莫名其妙,可降级至主流JDK重测:
    曾遇到此类案例 - io.quarkus插件quarkusRun任务不支持JDK22;NetBeans运行的JDK和其运行的Gradle不兼容报错。


Windows PowerShell执行Java程序:
  & (gcm java.exe).Path --version

  Java打开命令行控制台并切换至指定目录:
        var cmd = {"cmd", "/c", "start", "powershell.exe", "-NoExit", "-Command", "Set-location", "D:\\"};
        new ProcessBuilder(cmd).start(); // 在PowerShell中执行命令、进入指定目录。
        // 接收命令输出流较复杂,故优先使用日志方式:
        new ProcessBuilder("ping", "localhost")
          //.redirectErrorStream(true).redirectOutput(ProcessBuilder.Redirect.appendTo(new File("x.log")))
          .start().onExit().thenAccept(p -> System.out.println("PID:" + p.pid() + "; EXIT CODE:" + p.exitValue())).join();



Java参数

  查看JVM参数(亲测Open Liberty、Gradle等均可用) - java.lang.management.ManagementFactory.getRuntimeMXBean().getInputArguments() // [-Dfile.encoding=UTF-8, -Duser.language=zh]

  生产用JVM参数(JDK 23起ZGenerational已成默认可不写/非容器[快退重启]可用 -XX:+HeapDumpOnOutOfMemoryError 来排查OOM)
    -XX:MaxRAMPercentage=95 -XX:+UseZGC -XX:+ZGenerational -XX:+CrashOnOutOfMemoryError

  [开发用JVM参数]
    -Xmx2048m -XX:+UseZGC --enable-preview

  JVM 内存占用百分比 -XX:MaxRAMPercentage默认跟-Xmx一样为总内存25%,推荐值为75%,会被显式参数-Xmx1024m覆盖。
  启用预览特性JVM参数 --enable-preview

  内存实际占用JVM参数 -XX:NativeMemoryTracking=summary
    启用后(性能损失约10%)查看命令(主看Total:committed=值) jcmd  VM.native_memory scale=MB

  GC内存占用减少一半(Java21起支持参二) -XX:+UseZGC -XX:+ZGenerational



Java语法、简写

  内存不足导致无法创建对象或执行System.gc()时才会触发Full GC,即使是“软引用(SoftReference)”对象也会被回收,而“弱引用(WeakReference)”则不进行Full GC也会被回收。
  垃圾回收器属于低优先级线程,故扫描时可能有延迟;System.gc()适用场合 - 在占内存较多的粗粒度操作之前调用。

  Java 22实用新特性 - 
    Unnamed Variables: try { } catch (NumberFormatException _) { }

  var arrays = {"a","b"}; // or new String[]{"a","b"};
  判断字符串是不是数字 - "123".chars().allMatch(Character::isDigit);

  Lambda:
    @FunctionalInterface interface Fi{ void x(String x); }
    Fi fi = (x) -> { System.out.println(x); }; fi.x("x"); 

  String Templates:
        var s = """
                {"text": "%s"}
                """.formatted("text value");
        System.out.println(s); // {"text": "text value"}

  遍历枚举:
    var e = NetworkInterface.getNetworkInterfaces(); // Guava 👇 入参必须判null。
    新迭代 - List.of("A", "B").iterator().forEachRemaining(System.out::println);
    传统迭代 - while(it.hasNext()) { System.out.println(it.next()); }
    if (e != null) { com.google.common.collect.Iterators.forEnumeration(e).forEachRemaining(System.out::println); }

  直接比对static常量会被IDE警告永不为true/false,故改写为:
    Objects.equals("com.example", BuildConfig.APPLICATION_ID);

  列表去重 - stringList.stream().distinct().toList();

  获取系统字体名:GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();


Lambda、Stream API

Stream API参考

  List转Map:Java Stream 转换List至Map写法 Collectors.toMap
    list.stream().collect(Collectors.toMap(User::getId, User::getName)); // 值为字段
    list.stream().collect(Collectors.toMap(User::getId, obj -> obj)); // 值为对象
    ...Collectors.toMap(User::Id, obj -> obj.Name != null ? obj.Name : "") // 值为null处理
    或 Collectors.toMap(User::Id, obj -> Optional.ofNullable(obj.Name()).orElse(""))
    // Key重复则取首个;覆盖式则Lambda返回值改为duplicateId
    list.stream().collect(Collectors.toMap(User::Id, User::Name, (firstId, duplicateId) -> firstId));

  ByteBuffer写入InputStream并可读:
    var bb = ByteBuffer.allocate(inputStream.available());
    while (inputStream.available() > 0) {
        bb.put((byte) inputStream.read());
    } // 该方式似乎是逐字节写入,可再套一层做成多字节缓冲方式。
    bb.flip(); // 从InputStream切换至OutputStream模式


特殊备忘

获取Java版本 - System.getProperty("java.version");
获取系统临时目录 - System.getProperty("java.io.tmpdir"); // Windows中默认取自“tmp”环境变量
设置系统临时目录 - java -Djava.io.tmpdir=C:\Users\x\AppData\Local\Temp\ -jar ... 路径不存在的方法 - Files.notExists(dirPath); 非自定义类的类加载器和getResource("")永远为null:new String().getClass().getClassLoader() 永不null:Thread.currentThread().getContextClassLoader()、CustomClass.class.getResource(""); 类型默认值: 等同C#的default(int)或return default; com.google.common.base.Defaults.defaultValue(Integer.TYPE); //will return 0 或 public static T defaultValue(Class cls) { return (T) java.lang.reflect.Array.get(Array.newInstance(cls, 1), 0); } 序列化: // Jackson使用OffsetDateTime必须引入jackson-datatype-jsr310库,不setDateFormat则为纯数字。 var om = new ObjectMapper().registerModules(new JavaTimeModule()) .setDateFormat(new StdDateFormat().withColonInTimeZone(true)); var jsonString = om.writeValueAsString(obj);


JDK FFM(Foreign Function & Memory API)取代JNA和JNI之系统调用C库实例(JDK22正式支持):
  var nl = Linker.nativeLinker();
  var mh = nl.downcallHandle(nl.defaultLookup().find("_getpid").get(),
    FunctionDescriptor.of(JAVA_INT)); //or FunctionDescriptor.ofVoid(...)
  // mh.invokeExact(Arena.openConfined().allocateUtf8String("x"));
  var r = (int) mh.invokeExact(); System.out.println(r);

  纯Java获取Process ID (PID):ProcessHandle.current().pid();
  
  如果库文件规模过大,可采用jextract方式生成Java待调用的胶水代码:
    jextract 抽取C库头文件生成 Foreign Function & Memory API 绑定类

  库主要分为两类:静态库和动态库(共享库)。
   1. windows中静态库是以 .lib 为后缀的文件,动态库是以 .dll 为后缀的文件。
   2. linux中静态库是以 .a 为后缀的文件,动态库是以 .so为后缀的文件。
   3. mac中静态库是以.a为后缀的文件,动态库是以.dylib为后缀的文件。




虚拟线程(Virtual Threads)/协程:

Thread.startVirtualThread(() -> { Thread.currentThread().isVirtual(); });
Thread.ofVirtual().name("可传递自定义数据").start(() -> System.out.println());

结构化并发(Structured Concurrency)

  多任务并发时,只要其中之一完成,就关闭所有任务线程:
        try (var sts = new StructuredTaskScope.ShutdownOnSuccess()) {
            sts.fork(() -> findUser()); sts.fork(() -> fetchOrder());
            sts.joinUntil(Instant.now().plusSeconds(60)).result();
        } catch (InterruptedException | TimeoutException | ExecutionException ex) {
            System.err.println(ex);
        }

    老版本方式:Executors.newCachedThreadPool().invokeAny(List.of(() -> UUID.randomUUID()));

Java兼容性

JDK 21(ClassVersion 65)、JDK 20(ClassVersion 64)、JDK 19(ClassVersion 63)

        Apache NetBeans:
          IDE v20首选JDK 20,若用JDK 21+则警告“Unsupported Java Runtime”,但不影响使用;
          该IDE配置的Gradle,若不支持高版本JDK则报错;
          注意 - 版本发布日总会延误20天!
          gradle.properties可指定IDE所用JDK之外的版本:netbeans.hint.jdkPlatform=JDK_20
          【临时Bug】NB20在compileJava任务启用--enable-preview会飘红,暂注释之。
        

Gradle 8.1 最高兼容 JDK 20;Gradle 8.0 最高兼容 JDK 19

Android Studio Dolphin 2021.3.1 项目 最高兼容 JDK 11

jpackage - 打包java程序为系统安装包("app-image", "exe", "msi", "rpm", "deb", "pkg", "dmg")

Java程序打包




GraalVM Native Image

Java原生应用专题



Java语法

Java语言关键词、保留词列表
方法内抛出异常:throw new Exception("抛出一个异常");
方法体声明异常:void method() throws Exception, ArithmeticException { }

单例模式 代码模板

综合

HTML转义 - guava 库
com.google.common.html.HtmlEscapers.htmlEscaper().escape(s)
  EQ 就是 EQUAL等于 
  NE 就是 NOT EQUAL不等于 
  GT 就是 GREATER THAN大于  
  LT 就是 LESS THAN小于 
  GE 就是 GREATER THAN OR EQUAL 大于等于 
  LE 就是 LESS THAN OR EQUAL 小于等于


Java Web App | 网站应用

Java Servlet、JAX-RS(RESTful)、Spring、JSF等网页开发专题


Java Web Server | HTTP网站服务监听

java -m jdk.httpserver 默认端口为-p 8000
或 C:\Progra~1\Java\jdk-21\bin\jwebserver.exe -p 8000
可选参数:默认为当前目录,可指定其他目录 -d D:\

Java 网络

Java HttpClient实例 Java Socket 网络编程详解


Java Swing | 桌面GUI程序

JOptionPane.showMessageDialog(null, "对话框文字");

关闭所有extends WindowAdapter覆写了windowClosing方法的窗口:
        var we = new WindowEvent(this, WindowEvent.WINDOW_CLOSING);
        Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(we);

JDBC

  JDBC(Java Database Connectivity) - 关系型数据库管理技术入门

        // 若用了mariadb-java-client,则必须修改URL协议为 jdbc:mariadb://...
        String url = "jdbc:mysql://127.0.0.1:3306/db-name";
        String user = "root", password = "1";
        try (var conn = DriverManager.getConnection(url, user, password);
                Statement sta = conn.createStatement()) {
            // 或 参数化 var ps = conn.prepareStatement(sql);
            // ps.setString(1, name); // index从1数起
            ResultSet rs = sta.executeQuery("select 'Hi'"); // ps.executeUpdate(); 
            if(rs.next()){ System.out.println(rs.getObject(1)); } // Statement关闭时会连带关闭ResultSet
        } catch (SQLException ex) { System.err.println(ex); }

        // 今日统计 - select COUNT(ID) AS count FROM article WHERE TO_DAYS(AccessTime)=TO_DAYS(NOW())

        // 或 Spring 6.1 JdbcClient API:jdbcClient.sql(sql).param(grade).param(state).query(new StudentRowMapper()).list();
数据映射: MyBatis将数据库类型 TIMESTAMP(3) 与Java类型 OffsetDateTime 对应。 数据库类型TIMESTAMP存储的是UTC时间戳,写入和读取均会自动转换。

Java Optional类

参考文章
    对象嵌对象判null传统写法:
    if (p!= null && p.getLocation() != null && p.getLocation().getCity() != null) {
        return p.getLocation().getCity().toLowerCase();
    }
    简写:
    Optional.ofNullable(p).map(Person::getLocation)
    .map(Location::getCity).map(String::toLowerCase);
    

 

Gradle & Maven

    Gradle兼容Java版本列表 - https://docs.gradle.org/current/userguide/compatibility.html
    说明 - 兼容表的“Support for running Gradle”列是指Gradle自身JDK支持最高版本。
    Gradle顶级对象可用成员 - https://docs.gradle.org/current/dsl/org.gradle.api.Project.html
    NetBeans支持设置Gradle执行环境与IDE不同的Java Runtime了,安装JDK21,添加zip版JDK20。
    目录 gradle/wrapper/ 为可选文件夹,对Gradle不敏感者无需配置。
    

其他:
InputStream、FilterInputStream类没有实现reset方法,故无法实现流的复用,可通过BufferedInputStream和ByteArrayInputStream来实现复用。
FileInputStream直接读取;BufferedInputStream是分批读取,通常套在FileInputStream外改善性能;ByteArrayInputStream则是全部读取。