【Java技术专题】「攻破技术盲区」带你攻破你很可能存在的Java技术盲点之技术功底指南(鲜为人知的技术)

简介: 【Java技术专题】「攻破技术盲区」带你攻破你很可能存在的Java技术盲点之技术功底指南(鲜为人知的技术)

Java.lang包经常进行更新,主要涉及基本类型的包装类、进程管理和线程类。本章节内容的主要要点和方向:



基本类型的包装类

技术盲点:基本类型的比较

通常对于基本类型的比较我们都是用的是 == 或者 equals方法进行处理,但是在基本类型的比较方面,Boolean、Byte、Short、Integer、Long 和 Character 类都添加了一个静态 compare 方法,用于比较两个基本类型值的大小。

例如,Long 类的 compare 方法可以用来比较两个 long 类型的值。这个 compare 方法主要作为“语法糖”存在,可以简化进行基本类型数值比较时的代码。

如果需要对两个 int 数值 x 和 y 进行比较,一般的做法是使用代码“Integer.valueOf(x).compareTo(Integer.valueOf(y))”,而其实可以直接使用“Integer.compare(x, y)”来实现同样的功能。

技术盲点:字符串内部化(string interning)

对于字符串内部化(string interning)技术,开发人员可能并不陌生。采用这种技术是常见的优化策略,可以提高字符串比较时的性能,是一种典型的空间换时间的做法。而 Java 也采用了这种技术

在 Java 中,包含相同字符的字符串字面量引用的是相同的内部对象。此外,String 类还提供了 intern 方法,用于返回与当前字符串内容相同但已经被包含在内部缓存中的对象引用。当对被内部缓存的字符串进行比较时,可以直接使用“==”操作符,而无需使用更加耗时的 equals 方法。

字符串内部化的示例

java

复制代码

public void stringIntern() {
  boolean test1= "test" == "test";
  boolean test2= (new String("test") == "test");
  boolean test3= (new String("test").intern() == "test");
}
  • 第一个字符串比较的结果为 true
  • 第二个字符串比较的结果为 false
  • 第三个字符串比较的结果为 true。

如果使用 equals 方法来进行第二个字符串的比较,结果也会是 true。

技术盲点:类型缓存机制(空间换时间)

String、Short和int、Byte等类型的扩展机制

根据Java语言规范,Java将字符串内部化机制扩展到了 -128 到 127 之间的数字。对于short类型和 int 类型在 -128 到 127 范围内的值,以及 char 类型在 \u0000 到 \u007f 范围内的值,它们对应的包装类对象始终指向相同的对象。因此,当通过“==”进行判断时,结果一定是 true。

valueOf方法的缓存触发

为了满足这个要求,Byte、Short 和 Integer 类的 valueOf 方法会对 -128 到 127 范围内的值进行内部缓存。而对于 Character 类,valueOf 方法会对 0 到 127 范围内的值进行内部缓存。

使用了内部缓存的示例

Integer 类的内部化的示例

java

复制代码

public void numberCache() {
  boolean value1 = Integer.valueOf(3) == Integer.valueOf(3);
  boolean value2 = Integer.valueOf(129) == Integer.valueOf(129);
}

由于第一个比较操作的数值在 -128 到 127 之间,所以 Integer 类的 valueOf 方法会返回同一个缓存对象,因此 value1 的值为 true。而第二个比较操作的数值超出了默认的缓存范围,所以 valueOf 方法会返回两个不同的对象,因此 value2 的值为 false。

调整内部缓存范围

如果希望缓存更多的值,可以通过 Java 虚拟机启动参数"java.lang.Integer.IntegerCache.high"进行设置。例如,通过使用"-Djava.lang.Integer.IntegerCache.high=256",可以将数值缓存的范围扩大至-128到256。当重新运行上面的代码时,会发现value2的值变为true,因为129位于修改后的缓存范围内。


进程使用技术实战

Java标准API提供了创建运行于底层操作系统上的进程的能力。只需提供正确的命令和相关参数,即可启动一个进程。启动进程后,Java程序可以向进程输入数据,并读取进程生成的输出数据。

技术盲点:进程输入输出

在Java程序中启动其他进程时,最重要的是处理输入和输出。通常的做法是将Java程序的内部运行结果作为输入传递给新创建的进程,然后等待进程执行完成。在获取进程输出的运行结果后,再继续后续处理。通过这种方式,底层操作系统上的其他进程可以与Java程序很好地集成。对于Java程序来说,进程的输入是通过输出流传递给进程的。程序向输出流中写入的数据会通过管道传递给进程。进程的输出对于Java程序来说是通过输入流获取的。

技术盲点:ProcessBuilder处理进程

通过读取输入流的内容,可以获得进程的输出。标准的创建新进程的过程是使用java.lang.ProcessBuilder类来设置新进程的属性,然后通过start方法来启动进程的执行。

ProcessBuilder类的start方法返回一个表示进程的java.lang.Process类的对象。通过Process类的getOutputStream方法,可以获取用于向进程写入数据的输出流;而通过getInputStreamgetErrorStream方法,可以分别获取包含进程正常执行和出错时输出内容的输入流。

创建进程

下面是一个创建进程的示例代码:

java

复制代码

public void startProcessNormal() throws IOException { 
  ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/c", "netstat", "-a"); 
  Process process = pb.start();
  InputStream input = process.getInputStream();
  Files.copy(input, Paths.get("netstat.txt"), StandardCopyOption.REPLACE_EXISTING);
}

以上示例代码展示了如何在Windows上启动命令行工具,并执行"netstat -a"命令,将结果保存到一个文件中。

进程的输入和输出的继承式处理方式

Java又有了两种对于进程的输入和输出的处理方式。

  • 继承方式,即即新创建进程的输入和输出与当前的 Java 进程相同。
  • 文件式,即将文件作为进程的输入来源和输出目的地

继承方式

以下是使用继承方式的示例代码:

java

复制代码

ProcessBuilder processBuilder = new ProcessBuilder("cmd.exe", "/c", "dir");
processBuilder.inheritIO();
Process process = processBuilder.start();
int exitCode = process.waitFor();
if (exitCode == 0) {
    System.out.println("进程执行成功");
} else {
    System.out.println("进程执行出错");
}

以上示例代码展示了如何启动一个进程,在Windows上使用命令行工具执行"dir"命令,并通过ProcessBuilder类的inheritIO方法将进程的输出设置为继承自父进程。运行结果会显示在Java程序默认的输出控制台中。

redirectOutput(Redirect.INHERIT)的方式

java

复制代码

public void dir() throws IOException  { 
  ProcessBuilder pb =new ProcessBuilder("cmd.exe", "/c", "dir");
  pb.redirectOutput(Redirect.INHERIT);
  pb.start();
}

上面启动的进程通过 Windows 上的命令行工具来执行 dir 命令,通过 ProcessBuilder 类的 redirectOutput 方法把进程的输出 设置为继承自父进程,运行的结果就是 dir 命令的输出内容,会显示在 Java 程序默认的输出控制台中。

文件方式

如果需要将进程的或输出更改为文件,可以使用ProcessBuilder类中的redirectInputOutput方法其他重载形式。下面是基于文件的示例。

java

复制代码

public void listProcesses() throws IOException { 
  ProcessBuilder pb = new ProcessBuilder("wmic", "process");
  File output = Paths.get("tasks.txt").toFile();
  pb.redirectOutput(output);
  pb.start();
}

上面展示了如将进的文件中。只需将一个java.io.File类对象作为Output方法的参数即可。这种方式当于管道方式读取输入流获取进程的输出,然后将其写入文件。通过标准API提供方式实现比自己编写实现要更简洁和可靠。


Thread类的特性分析

技术盲点:Thread是否可以clone以及参数校验

  • Thread类的 clone 方法改为始终抛出 NotSupportedException 异常。这因为对 Thread 类对象进行克隆是没有意义的。Java自身显式禁止了对 Thread 类对象的克隆操作。
  • Thread 类的 join 方法和 sleep 方法可以接收一个 long 类型的参数,表示等待的时间。然而,当这个参数值为负数时,并没有定义相应的处理方式。Java规定:如果这两个方法的等待时间参数的值为负数,则会抛出 IllegalArgumentException 异常。

技术盲点:Thread的线程的注意要点

在创建 Thread 类对象时,可以使用的参数包括:表示 Thread 类对象所在线程组的 java.lang.ThreadGroup 类的对象、表示需要运行的任务的 java.lang.Runnable 接口的现对象,以及表示线程名称的 String 类的对象。

  • ThreadGroup:如果传入的 ThreadGroup 类对象为 null,那么会先尝试调用当前配置好的安全管理器(java.lang.SecurityManager 类的对象)的 getThreadGroup 方法来获取 ThreadGroup 类对象;
  • Thread参数校验:如果没有配置安全管理器或 getThreadGroup 方法也返回 null,那么会使用当前线程线程组的 ThreadGroup 类对象。如果传入的 Runnable 接口的实现对象为 null,那么会调用 Thread 类对象本身的 run 方法。传入的线程名称为 null,会抛出 NullPointerException 异常。
  • setClassLoader:在调用 Thread 类的 setClassLoader 方法设置线程上下文类加载器时,如果传入的参数为 null,则表示使用系统类加载器。如果无使用系统类加载器,则使用启动类加载器。

同样地,如果当前线程的上下文类加载器是系统类加载器或启动类加载器,那么 getContextClassLoader 方法的返回值是 null


Objects对象操作

java.util 包中新增了一个用于操作对象的工具类java.util.ObjectsObjects 类中包含的都是静态方法,通过这些方法可以快速对对象进行操作对象的比较操作时,可以使用 Objectscompare 方法。

Objects的compare方法

在进行两个对象的比较操作时,可以使用 Objects 类的 compare 方法。一般来说,进行对象比较是先由 Java 类实现java.lang.Comparable 接口,再通过 compareTo 方法来进行比较。

Objects 类的 compare 方法的使用示例

一个简单的对 Long 对象进行比较的 Comparator接口的实现,以使用Objects类的compare` 方法的示例:

java

复制代码

private static class ReverseComparator implements Comparator<Long> {
  public int compare(Long num1, Long num2) {
    return num2.compareTo(num1);
  }
}
public void compare() {
  int value1 = Objects.compare(1L, 2L, new ReverseComparator());
}

通过使用类的 compare 方法,我们可以方便地对 Long 对象进行比较,而无需手动处理 null 值或实现比较逻辑。

集合的排序

如果要对集合中的元素进行排序,还会使用到 java.util.Comparator 接口的实现。Objects 类的 compare 方法可以通过特定的 `` 接口的实现对象来比较两个对象。

java

复制代码

import java.util.Comparator;
import java.util.Objects;
public class LongComparator implements Comparator<Long> {
    @Override
    public int compare(Long o1, Long o2) {
        return Objects.compare(o1, o2, Comparator.naturalOrder());
    }
}
public class Main {
    public static void main(String[] args)        
      Long a = Long.valueOf(10);
        Long b = Long.valueOf(5);
        LongComparator comparator = new LongComparator();
        int result = comparator.compare(a, b);
        System.out.println(result);: 1
    }
}

Objects的equals方法

判断对象相等的方式般是调用 Object 类的 equals 方法。例如,要判断两个对象 a 和 b 是否相等,可以使用代码a.equals(b)。Objects类的equals方法可以直接判断两个对象是否相等,如Objects.equals(a,)。该方法的一个好处是会对 null值进行处理。

Objects的equals的判断逻辑

如果直接调用一个对象的equals方法,需要先判断该对象是否为null,而使用 Objects类的equals方法则不需要。调用Objects类的equals方法时,两个参数的值都是null,则判断结果是 true;而如果只有一个参数为 null,则判断结果是 false;如果两个参数都不为 null,则调用第一个参数的 equals` 方法进行判断。

Objects的deepEquals的判断逻辑

Objects 类中与 equals 方法作用相似的方法是 deepEquals 方法,利用该方法也可以对两个对象进行相等性判断。

不同之在于,如果 deepEquals 方法两个参数是数组,则会调用 java.util 类的deepEquals方法进行比较。 Arrays 类的 deepEquals 方法在进行数组比较时,会考虑数组中的所有元素的相等性。在其他情况下,deepEquals 方法和 equals 方法的作用是相同的。

Objects 类的 equals 方法的使用示例

java

复制代码

public void equals() {
  boolean value1 = Objects.equals(new Object(), new Object()); 
  Object[] array1 = new Object[] {"Hello", 1, 1.0};
  Object[] array2 = new Object[] {"Hello", 1, 1.5};
  boolean value2 = Objects.deepEquals(array1, array2);
}

Objects的hashCode 方法

Objects 类中 hashCode 方法可以用来获取对象哈希值。如果参数为 null,则返回值是 0;否则返回值是参数对象的 hashCode 方法结果。

如果需要计算一组对象的哈希,可以使用 Objects 类的 hash 方法。Objects的 hash 方法的实现使用的是 Arrays 类中 hashCode 方法。

注意,调用 hash 方法传入单个对象作为的返回结果,并不相同于使用同的参数调用 hashCode 方法的结果。

Objects 类的 hash 和 hashCode 方法的使用示例

java

复制代码

public void hash() {
  int hashCode1 = Objects.hashCode("Hello");
  int hashCode2 = Objects.hash("Hello", "World");
  int hashCode3 = Objects.hash("Hello");
}

Objects的toString方法

Objects类还提供了一不同重载形式的toString方法,用于获取对象的字符串表示。当参数为null时,toString方法返回null";而在其他情况下,相当于调用参数对象的toString方法。希望在参数为null时返回特定内容作为提示信息,可以使用toString方法的另一种重载形式,该形式通过额外的参数来指定参数值为null时的返回结果。

Objects 类的 toString 方法的使用示例

java

复制代码

public void useToString() {
  String str1 = Objects.toString("Hello");
  String str2 = Objects.toString(null, " 空对象 ");
}
相关文章
|
2月前
|
存储 监控 安全
单位网络监控软件:Java 技术驱动的高效网络监管体系构建
在数字化办公时代,构建基于Java技术的单位网络监控软件至关重要。该软件能精准监管单位网络活动,保障信息安全,提升工作效率。通过网络流量监测、访问控制及连接状态监控等模块,实现高效网络监管,确保网络稳定、安全、高效运行。
79 11
|
2月前
|
XML Java 编译器
Java注解的底层源码剖析与技术认识
Java注解(Annotation)是Java 5引入的一种新特性,它提供了一种在代码中添加元数据(Metadata)的方式。注解本身并不是代码的一部分,它们不会直接影响代码的执行,但可以在编译、类加载和运行时被读取和处理。注解为开发者提供了一种以非侵入性的方式为代码提供额外信息的手段,这些信息可以用于生成文档、编译时检查、运行时处理等。
83 7
|
2天前
|
JavaScript 安全 Java
智慧产科一体化管理平台源码,基于Java,Vue,ElementUI技术开发,二开快捷
智慧产科一体化管理平台覆盖从备孕到产后42天的全流程管理,构建科室协同、医患沟通及智能设备互联平台。通过移动端扫码建卡、自助报道、智能采集数据等手段优化就诊流程,提升孕妇就诊体验,并实现高危孕产妇五色管理和孕妇学校三位一体化管理,全面提升妇幼健康宣教质量。
27 12
|
2月前
|
移动开发 前端开发 Java
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
JavaFX是Java的下一代图形用户界面工具包。JavaFX是一组图形和媒体API,我们可以用它们来创建和部署富客户端应用程序。 JavaFX允许开发人员快速构建丰富的跨平台应用程序,允许开发人员在单个编程接口中组合图形,动画和UI控件。本文详细介绍了JavaFx的常见用法,相信读完本教程你一定有所收获!
1317 1
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
|
1月前
|
监控 JavaScript 数据可视化
建筑施工一体化信息管理平台源码,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
智慧工地云平台是专为建筑施工领域打造的一体化信息管理平台,利用大数据、云计算、物联网等技术,实现施工区域各系统数据汇总与可视化管理。平台涵盖人员、设备、物料、环境等关键因素的实时监控与数据分析,提供远程指挥、决策支持等功能,提升工作效率,促进产业信息化发展。系统由PC端、APP移动端及项目、监管、数据屏三大平台组成,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
|
2月前
|
JavaScript 安全 Java
java版药品不良反应智能监测系统源码,采用SpringBoot、Vue、MySQL技术开发
基于B/S架构,采用Java、SpringBoot、Vue、MySQL等技术自主研发的ADR智能监测系统,适用于三甲医院,支持二次开发。该系统能自动监测全院患者药物不良反应,通过移动端和PC端实时反馈,提升用药安全。系统涵盖规则管理、监测报告、系统管理三大模块,确保精准、高效地处理ADR事件。
125 1
|
3月前
|
监控 前端开发 Java
【技术开发】接口管理平台要用什么技术栈?推荐:Java+Vue3+Docker+MySQL
该文档介绍了基于Java后端和Vue3前端构建的管理系统的技术栈及功能模块,涵盖管理后台的访问、登录、首页概览、API接口管理、接口权限设置、接口监控、计费管理、账号管理、应用管理、数据库配置、站点配置及管理员个人设置等内容,并提供了访问地址及操作指南。
|
3月前
|
JSON 前端开发 JavaScript
java-ajax技术详解!!!
本文介绍了Ajax技术及其工作原理,包括其核心XMLHttpRequest对象的属性和方法。Ajax通过异步通信技术,实现在不重新加载整个页面的情况下更新部分网页内容。文章还详细描述了使用原生JavaScript实现Ajax的基本步骤,以及利用jQuery简化Ajax操作的方法。最后,介绍了JSON作为轻量级数据交换格式在Ajax应用中的使用,包括Java中JSON与对象的相互转换。
74 1
|
3月前
|
SQL Java 数据库连接
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率。本文介绍了连接池的工作原理、优势及实现方法,并提供了HikariCP的示例代码。
85 3
|
3月前
|
Java 数据库连接 数据库
深入探讨Java连接池技术如何通过复用数据库连接、减少连接建立和断开的开销,从而显著提升系统性能
在Java应用开发中,数据库操作常成为性能瓶颈。本文通过问题解答形式,深入探讨Java连接池技术如何通过复用数据库连接、减少连接建立和断开的开销,从而显著提升系统性能。文章介绍了连接池的优势、选择和使用方法,以及优化配置的技巧。
79 1

热门文章

最新文章