计算机系统应用教程网站

网站首页 > 技术文章 正文

Java 7新特性深度解析:提升效率与功能

btikc 2025-02-28 15:00:17 技术文章 3 ℃ 0 评论

一、Switch中添加对String类型的支持

Switch语句可以使用原始类型或枚举类型。

Java引入了另一种类型,可以在switch语句中使用:字符串类型。

public class switchAddString {

public static void main(String[] args) {

String s = "a";

switch (s) {

case "a":

System.out.println("a");

break;

case "b":

System.out.println("b");

break;

default:

System.out.println("default");

}

}

}

编译器在编译时的处理情形

  1. 仅有一个casedefault,则直接转换为if…else…
  2. 有多个case。先将String转换为hashCode,然后相应的进行处理。

二、数字字面量的改进

1.数字中可加入分隔符

  • Java7中支持在数字量中间添加’_’作为分隔符。
  • 下划线仅仅能在数字中间。
  • 编译时编译器自己主动删除数字中的下划线。


  • 2.Java7添加二进制表示。

public static void main(String[] args) {

int i = 10;

System.out.println("i=" + i);

// 二进制

int j = 0b1010;

// 十六进制

int k = 0x1234;

// 1,000,000

int l = 1_000_000;

// 1,000,000

int m = 1__000_000;

System.out.println("j=" + j);

System.out.println("k=" + k);

System.out.println("l=" + l);

System.out.println("m=" + m);

}

三、异常处理(捕获多个异常)

1.catch子句能够同一时候捕获多个异常

  • 使用’|'切割,多个类型,一个对象e 。

2.try-with-resources语句

  • Java7之前须要在finally中关闭socket、文件、数据库连接等资源。
  • Java7引入try-with-resources,用于确保资源在使用后能够正确地关闭。
  • 在使用try-with-resources时,你可以在 try 关键字后面的括号中声明一个或多个资源。
  • 这些资源必须实现 AutoCloseable 接口(Java 7引入的接口,它具有一个 close() 方法用于释放资源)。

public static void main(String[] args) {

// 捕获多个异常

try {

int a = 10;

int b = 0;

System.out.println("a/b=" + (a / b));

} catch (ArithmeticException | NullPointerException e) {

e.printStackTrace();

}

// try-with-resources,在 try 关键字后面的括号中声明一个或多个资源,每个资源用逗号分隔

String fileName = "example.txt";

try (

// 使用try-with-resources声明BufferedReader资源

BufferedReader reader = new BufferedReader(new FileReader(fileName));

// 需要声明多个资源,我们只需在括号中用逗号分隔

BufferedReader reader1 = new BufferedReader(new FileReader("file1.txt"));

BufferedReader reader2 = new BufferedReader(new FileReader("file2.txt"))

) {

// 读取文件内容

String line;

while ((line = reader.readLine()) != null) {

System.out.println(line);

}

// 这里不需要显式调用reader.close(),因为try-with-resources会自动处理

} catch (IOException e) {

// 处理可能发生的IOException

e.printStackTrace();

}

// 在try代码块执行完毕后,reader会被自动关闭

}

四、增强泛型推断

1.菱形操作符

  • 在实例化泛型类时,可以使用菱形操作符 <> 来省略类型参数。
  • 编译器会根据上下文推断类型参数。
  • 简化代码,减少冗余,提高代码的可读性。

2.泛型实例化类型推断

  • 当泛型类的构造函数的参数包含泛型类型时,Java 7 能够推断泛型的类型。
  • 这允许你在实例化泛型类时省略类型参数,只在构造函数参数中指定类型即可。

public static void main(String[] args) {

// Java7之前

ArrayList arrayList = new ArrayList();

// Java7之后

ArrayList arrayList2 = new ArrayList<>();

// 泛型类

class Generic {

// 构造函数参数为泛型类型

public Generic(T t) {

}

}

// 使用泛型实例化类型推断

Generic generic = new Generic<>("abc");

}

五、NIO2.0(AIO)新IO的支持

Java 7 引入了 NIO 2.0(New I/O),其中包含对异步 I/O(AIO)的支持,这是一个显著的新特性,特别是在处理非阻塞 I/O 操作时非常有用。具体来说,NIO 2.0 的 AIO 支持通过引入 AsynchronousFileChannel 类来实现异步文件 I/O 操作。

AsynchronousFileChannel 类:

  • 允许进行异步文件读取和写入操作。
  • 相比于传统的阻塞 I/O,异步 I/O 可以在读写数据的同时执行其他操作,从而提高系统的效率和性能。

public static void main(String[] args) throws Exception {

// 异步读取文件

Path path = Paths.get("file.txt");

AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(

path, StandardOpenOption.READ);

// 分配缓冲区

ByteBuffer buffer = ByteBuffer.allocate(1024);

// 读取文件

long position = 0;

// 异步读取数据,用于检查读取操作的状态和获取读取的结果

Future operation = fileChannel.read(buffer, position);

while (!operation.isDone()) {

// 等待读取完成

Thread.sleep(1000);

}

// 读取完成,将缓冲区数据翻转

buffer.flip();

// 读取数据

byte[] data = new byte[buffer.limit()];

// 将缓冲区数据复制到data中

buffer.get(data);

System.out.println(new String(data));

// 关闭文件

fileChannel.close();

}


2.AsynchronousServerSocketChannel 和 AsynchronousSocketChannel:

  • 用于支持异步的网络编程。
  • 这些类允许你创建异步服务器端和客户端,并进行异步的网络数据读取和写入操作。


  1. 在高并发环境下处理 I/O 操作变得更为高效和灵活。
public static void main(String[] args) throws Exception {
        // 创建异步通道
        AsynchronousServerSocketChannel serverChannel =
                AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(8080));
        
        // 接受连接
        serverChannel.accept(null, new CompletionHandler() {
            /**
             * 接受连接完成时调用此方法。
             * @param clientChannel
             * @param attachment
             */
            @Override
            public void completed(AsynchronousSocketChannel clientChannel, Void attachment) {
                // 继续接受连接
                serverChannel.accept(null, this);

                // 读取数据
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                // 异步读取数据
                clientChannel.read(buffer, buffer, new CompletionHandler() {

                    /**
                     * 当读取操作完成时调用此方法。
                     *
                     * @param result 读取操作的结果,通常为读取的字节数,但在此处未使用
                     * @param attachment 包含读取数据的ByteBuffer对象
                     */
                    @Override
                    public void completed(Integer result, ByteBuffer attachment) {
                        // 处理读取完成
                        attachment.flip();
                        // 读取数据
                        byte[] data = new byte[attachment.limit()];
                        // 将数据复制到数组中
                        attachment.get(data);
                        System.out.println(new String(data));
                    }

                    /**
                     * 读取操作失败时调用此方法。
                     * @param exc
                     * @param attachment
                     */
                    @Override
                    public void failed(Throwable exc, ByteBuffer attachment) {
                        // 处理读取失败
                    }
                });
            }

            /**
             * 接受连接失败时调用此方法。
             * @param exc
             * @param attachment
             */
            @Override
            public void failed(Throwable exc, Void attachment) {
                // 处理接受连接失败
            }
        });

        // 程序继续执行其他操作
        Thread.sleep(Long.MAX_VALUE);
    }


六、SR292与InvokeDynamic
SR-292(Small Ranges):

  • SR-292 是 Java 7 中引入的一个改进,主要针对 switch 语句的性能优化。
  • 在早期的 Java 版本中,switch 语句的效率问题在于它通过逐个比较每个 case 条件来确定执行的分支,如果分支很多,这个过程可能会很慢。
    SR-292 引入了一种优化,即当 switch 语句的 case 常量之间的距离非常小(称为 “small ranges”),Java 编译器会使用一种更有效的查找方式,而不是简单的逐个比较。
    • 这种方式可以显著提高 switch 语句的执行速度,特别是在处理密集的条件分支时。

    public static void main(String[] args) {

    int month = 3;

    String monthName;

    // Java 7 的 SR-292 特性可以在一些情况下优化这样的 switch 语句,尤其是在 case 常量的范围较小时

    switch (month) {

    case 1:

    monthName = "January";

    break;

    case 2:

    monthName = "February";

    break;

    case 3:

    monthName = "March";

    break;

    case 4:

    monthName = "April";

    break;

    case 5:

    monthName = "May";

    break;

    case 6:

    monthName = "June";

    break;

    default:

    monthName = "Unknown";

    break;

    }

    System.out.println("Month: " + monthName);

    }

    2.InvokeDynamic(动态方法调用):

    • InvokeDynamicJava 7 引入的另一个重要特性,它是 Java 虚拟机(JVM)层面的改进,旨在支持更灵活和高效的动态语言实现。
    • 允许 Java 代码中的方法调用在运行时动态解析,并且可以绑定到相应的方法实现。
    • 用来优化字节码生成和方法调用的性能。
    • Java 8Lambda 表达式依赖于 InvokeDynamic 来生成相应的字节码。

    public static void main(String[] args) throws Throwable {

    // 创建动态调用

    MethodHandles.Lookup lookup = MethodHandles.lookup();

    // 创建方法句柄,使用lookup对象来查找Math.class中的静态方法sqrt,该方法接受一个double参数并返回一个dou* 第一个参数是`lookup`对象。

    MethodHandle mh = lookup.findStatic(Math.class, "sqrt",

    MethodType.methodType(double.class, double.class));

    // 创建动态调用

    // 第一个参数是`lookup`对象

    // 第二个参数是方法名,这里使用`"apply"`,因为它是`Function`接口中唯一的方法。

    // 第三个参数是`Function`接口的签名(即`Function.class`的类型)。

    // 第四个参数是`mh`的泛型类型签名。

    // 第五个参数是我们要调用的方法句柄(即`mh`)。

    // 第六个参数是`mh`的类型签名,表示我们要调用的方法的实际类型。

    CallSite sqrt = LambdaMetafactory.metafactory(

    lookup, "apply",

    MethodType.methodType(Function.class),

    mh.type().generic(),

    mh,

    mh.type()

    );

    // 调用动态调用

    MethodHandle factory = sqrt.getTarget();

    // 调用工厂方法,得到一个Function对象,使用了强制类型转换,因为factory.invoke()返回的是一个Object

    Function sqrtFunc = (Function) factory.invoke();

    double result = sqrtFunc.apply(16.0);

    System.out.println("Square root of 16: " + result);

    }

    七、Path接口

    在 Java 7 之前,通常使用 java.io.File 类来处理文件路径,Java 7 引入了 java.nio.file.Path 接口,它是 Java 中操作文件和目录路径的抽象表示。 Path 接口提供了更多功能和更强大的操作能力。

    Path 接口的一些主要特性和用法

    1. 路径表示
    • Path 接口可以表示文件系统中的路径,可以是文件或目录。它不仅仅是一个字符串,而是一个真正的对象,提供了丰富的方法来操作路径。

    2.创建路径

    可以使用Paths 类的静态方法来创建 Path 对象

    Path path = Paths.get("/path/to/file.txt");

    路径操作

    Path 接口提供了多种方法来获取路径的信息

    • toString():将路径转换为字符串表示。
    • getFileName():获取路径中的文件名部分。
    • getParent():获取路径中的父路径。
    • getRoot():获取路径的根部分。
    • getNameCount():获取路径中的名称元素的数量。
    • subpath(int beginIndex, int endIndex):获取指定范围内的子路径


    • 4.路径解析

    resolve() 方法可以用于解析相对路径或者连接两个路径,返回一个新的路径对象。

    1. 检查路径属性
      可以使用 Files 类的静态方法来检查文件或目录的属性,例如是否存在、是否可读、是否可写等。

    6.文件操作

    • Files 类结合 Path 接口提供了丰富的文件操作功能,包括读取文件内容、写入文件、复制、移动、删除等。

    7.路径迭代

    Path接口支持迭代,可以方便地遍历路径的各个部分。

    for (Path element : path) {

    System.out.println(element);

    }

    1. 相对路径和绝对路径
    1. Path 接口可以表示相对路径和绝对路径,并提供了方法来转换和处理这两种路径。
    
    public static void main(String[] args) {
            // 创建一个 Path 对象
            Path path = Paths.get("/path/to/file.txt");
    
            // 获取文件名
            Path fileName = path.getFileName();
            System.out.println("File Name: " + fileName);
    
            // 获取父路径
            Path parent = path.getParent();
            System.out.println("Parent Path: " + parent);
    
            // 获取路径的根部分
            Path root = path.getRoot();
            System.out.println("Root of the path: " + root);
    
            // 获取路径的元素数量
            int nameCount = path.getNameCount();
            System.out.println("Number of elements in the path: " + nameCount);
    
            // 遍历路径的每个元素
            System.out.println("Elements in the path:");
            for (int i = 0; i < nameCount; i++) {
                System.out.println("Element " + i + ": " + path.getName(i));
            }
    
            // 路径解析示例
            Path resolvedPath = path.resolve("subdir");
            System.out.println("Resolved Path: " + resolvedPath);
    
            // 检查文件或目录的属性
            // 判断文件是否存在
            boolean exists = Files.exists(path);
            System.out.println("Exists: " + exists);
    
            // 判断文件是否是目录
            boolean isReadable = Files.isReadable(path);
            System.out.println("Readable: " + isReadable);
    
            // 判断文件是否是可写
            boolean isWritable = Files.isWritable(path);
            System.out.println("Writable: " + isWritable);
    
            // 读取文件内容
            Path filePath = Paths.get("/path/to/file.txt");
            List lines = null;
            try {
                lines = Files.readAllLines(filePath, StandardCharsets.UTF_8);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            for (String line : lines) {
                System.out.println(line);
            }
    
            // 写入文件内容
            Path newFilePath = Paths.get("/path/to/newfile.txt");
            String content = "Hello, Java 7!";
            try {
                Files.write(newFilePath, content.getBytes());
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
    
            // 复制文件
            Path copiedFilePath = Paths.get("/path/to/copiedfile.txt");
            try {
                Files.copy(filePath, copiedFilePath, StandardCopyOption.REPLACE_EXISTING);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
    
            // 移动文件
            Path targetPath = Paths.get("/path/to/targetdir/movedfile.txt");
            try {
                Files.move(filePath, targetPath, StandardCopyOption.REPLACE_EXISTING);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
    
            // 删除文件
            try {
                Files.delete(filePath);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

    fork/join计算框架

    Java 7 引入了 Fork/Join 框架,是一种并行计算框架,专门用于解决分而治之的问题。主要用于执行递归式地将问题划分为更小子问题,并行执行这些子问题的计算,然后合并结果的任务。

    使用 Fork/Join 框架的基本步骤:

    1. 定义任务类 (RecursiveTaskRecursiveAction):RecursiveTask: 用于有返回值的任务。RecursiveAction: 用于无返回值的任务。
    2. 重写 compute() 方法:在任务类中,需要实现 compute() 方法来定义任务的具体执行逻辑。通常会判断是否需要进一步拆分任务,执行子任务的计算,最终将子任务的结果合并或处理。
    3. 创建 Fork/Join 池:使用 ForkJoinPool 类来管理并发执行的任务。通常可以通过 ForkJoinPool.commonPool() 方法来获取默认的线程池,也可以根据需要创建自定义的线程池。

    4.提交任务:将任务提交给 ForkJoinPool 来执行。

    使用 Fork/Join 框架来计算数组的总和Demo

    import java.util.concurrent.*;

    // 继承 RecursiveTask 来实现有返回值的任务

    class SumTask extends RecursiveTask {

    // 阈值,控制任务拆分的粒度

    private static final int THRESHOLD = 10;

    private int[] array;

    private int start;

    private int end;

    public SumTask(int[] array, int start, int end) {

    this.array = array;

    this.start = start;

    this.end = end;

    }

    @Override

    protected Long compute() {

    if (end - start <= THRESHOLD) {

    // 如果任务足够小,直接计算结果

    long sum = 0;

    for (int i = start; i < end; i++) {

    sum += array[i];

    }

    return sum;

    } else {

    // 否则,拆分任务为更小的子任务

    int mid = (start + end) / 2;

    SumTask leftTask = new SumTask(array, start, mid);

    SumTask rightTask = new SumTask(array, mid, end);

    // 异步执行左边的子任务

    leftTask.fork();

    // 同步执行右边的子任务

    long rightResult = rightTask.compute();

    // 获取左边子任务的结果

    long leftResult = leftTask.join();

    // 合并子任务的结果

    return leftResult + rightResult;

    }

    }

    }

    public class ForkJoinDemo {

    public static void main(String[] args) {

    int[] array = new int[100];

    for (int i = 0; i < array.length; i++) {

    array[i] = i;

    }

    // 创建 Fork/Join 线程池

    ForkJoinPool forkJoinPool = ForkJoinPool.commonPool();

    // 创建任务并提交给 Fork/Join 线程池

    SumTask task = new SumTask(array, 0, array.length);

    long result = forkJoinPool.invoke(task);

    // 输出计算结果

    System.out.println("Sum: " + result);

    }

    }

    本文暂时没有评论,来添加一个吧(●'◡'●)

    欢迎 发表评论:

    最近发表
    标签列表