Java 高级特性

本篇笔记主要介绍了 Java 的六个高级特性:异常处理、IO、泛型、集合框架、多线程以及 Lambda 表达式与流式处理。在异常处理部分,详细讲解了异常的分类、捕获与处理机制;IO 部分介绍了文本和二进制的输入输出操作;泛型部分阐述了泛型的使用规则与限制;集合框架部分系统地介绍了 Java 常用的集合类;多线程部分包含了线程的创建、控制与同步机制;最后的 Lambda 与流式处理部分则介绍了 Java 的函数式编程特性。(由 claude-3.5-sonnet 生成摘要)

1. Ch13 Exception Handling and Text IO

1.1. Exceptions

  • 异常(exception)  是程序运行时发生的错误事件,可能导致程序无法正常运行。
  • 常见的异常:
    • ArithmeticException:除零错误。
    • InputMismatchException:输入数据类型不匹配。
    • NullPointerException:访问未初始化的对象。
  • 异常分类
    • 上半部分的 Exception 是程序级错误,可以捕获并处理。
      • 运行时异常(RuntimeException)通常由逻辑错误引发,属于 非检查型异常(unchecked exception)
        • 不需要强制捕获,通常由逻辑错误引发。
      • 非运行时异常(如  IOException 等)需要显式处理,属于 检查型异常(checked exception)
        • 编译器强制程序员检查并处理,即程序员应该使用使用 try-catch 块进行捕获这些异常并进行操作。
    • 下半部分的 Error 是系统级错误,由 JVM 检测,通常不可恢复,比如内存不足。
  • 抛出异常:用 throw 关键词抛出异常示例,异常示例通常使用 new 运算进行创建。
    • 语法:throw new TheException();
  • 声明异常:定义方法时用 throws 关键词声明可能抛出的异常,多个异常之间用逗号隔开。
    • 使用这种方法声明的异常是检查型异常,必须在调用该方法的代码中进行显示捕获,否则会报 CE
    • 语法:public void myMethod() throws IOException
    • 调用时,必须用:try { } catch(IOException ex) { } 来处理可能抛出的异常。

1.2. Exception Handling

  • try-catch 块捕获异常。
    • 语法:try { } catch(ExceptionType ex) { }
  • finally 块:
    • finally 块中的代码无论是否发生异常都会执行。
    • 使用 finally 块与在 try-catch 块之后跟语句的区别时:如果在 catch 块中抛出异常(或者 return 等情况),则 try-catch 块之后的代码不会执行,但是 finally 中的代码始终会执行
    • finally 关键字保证无论程序使用何种方式退出 try-catch 块,finally 块中的代码都会被执行。更具体的,finally 块会在以下情况发生之后执行:
      • try 块中的代码正常执行完毕。
      • 在 try 块中抛出异常。
      • 在 try 块中执行 return、break、continue。
      • catch 块中代码执行完毕。
      • 在 catch 块中抛出异常。
      • 在 catch 块中执行 return、break、continue
    • 通常使用 finally 块来释放资源。
  • 多个 catch 块的情况
    • 拥有多个 catch 块时,JVM 会由上而下来检测每个异常是否被捕获。
    • 不可达代码检测机制:如果先 catch 了一个异常,又 catch 了另一个异常的子类,则后者是不可达的。因为无论抛出什么异常,都会被先前的 catch 块捕获。编译器会检测这种不可达的 catch 块,并报 CE
      • 所以捕获子类异常的 catch 块要放在捕获父类异常的 catch 块之前
  • 捕获到异常 e 后:
    • 使用 e.getMessage() 获取异常信息。
    • 使用 e.printStackTrace() 打印异常调用堆栈。
    • 如果需要进一步抛出异常,建议带上 e 的异常信息:可以使用 Throwable(Throwable cause) 或者 Throwable(String message, Throwable cause) 的构造方法。

1.3. Assertions

  • 断言(assertions):用于验证程序中的某些假设是否成立。
  • 语法:
    • assert condition;,其中 condition 是一个布尔表达式。
    • assert condition : message;,其中 message 可以是基本数据类型或对象。
  • 当断言失败时,JVM 会抛出 AssertionError 异常。
  • 设计原则:
    • 断言仅用于开发阶段的内部检查。
    • 不应在公共方法中使用断言检查参数。
  • 断言默认在运行时禁用,需要在命令行手动使用 -enableassertions-ea 选项启用。

1.4. Text IO

  • File 类:提供文件和路径的抽象。
    • 常用方法
      • exists():检查文件是否存在。
      • createNewFile():创建新文件。
      • delete():删除文件。
  • 使用 Scanner 类读取数据。
    • 使用 new Scanner(Paths.get(path)) 从文件中创建 Scanner 对象,注意不能直接写字符串
    • 使用 new Scanner((new URL(url)).openStream()) 从网络资源中创建 Scanner 对象
    • 第二个参数(可选)用于指定编码,否则使用该系统的默认编码
  • 使用 PrintWriter 类向文件写入数据。
    • 支持 print(...)println(...)printf(...) 等方法。
    • 第二个参数(可选)用于指定编码,否则使用该系统的默认编码。

2. Ch14 Binary IO

2.1. Binary IO Basic

  • 几乎所有方法都需要显示捕捉 java.io.Exception 异常,故应显式声明或捕捉。
  • inputStream.read() 的返回值和 outputStream.write(int b) 都是 int,但实际上都是以 byte 为单位读写的。
    • 其中 inputStream.read() 在 EOF 时会返回 -1,这就是为什么需要用 int 而不是 byte 表示。
  • DataInputStream 在到达文件末尾后继续读取数据会抛出 EOFException 异常。
  • Reader 是读取字符流的抽象类,Writer 是写入字符流的抽象类。
    • InputStreamReader:字节流转字符流
    • OutputStreamWrite:字符流转字节流
    • BufferedReaderBufferedWriter:字符缓冲流
    • FileReaderInputStreamReader 类的直接子类,用来读取字符文件
    • FileWriterOutputStreamWriter 类的直接子类,用来写入字符文件

2.2. Object IO Stream

  • transient 关键字:在序列化时忽略(静态成员也会被忽略)。
  • 如果一个对象不止一次写入对象流,不会存储对象的多份副本。JVM 会将对象的内容和序号一起写入对象流。
  • 反序列化后的对象,不需要调用构造函数重新构造。
  • 序列前的对象与序列化后的对象是深复制。
  • 类需要显示地实现 Serializable 接口才能被序列化。(即使成员都可以序列化也需要这么做)
  • 如果数组中的所有元素都是可序列化的,这个数组就是可序列化的。
  • 24-25 秋冬期末考试还考察了子类/父类中只有一个实现了序列化接口在序列化/反序列化时的行为,读者可以自行查阅相关资料

2.3. Piped IO Stream

  • PipedOutputStream 和 PipedInputStream 的作用是让多线程可以通过管道进行线程间的通讯。PipedReader 和 PipedWriter 和这两个的区别也类似:操作字符而不是字节。
  • 我们在线程 A 中向 PipedOutputStream 中写入数据,这些数据会自动的发送到与 PipedOutputStream 对应的 PipedInputStream 中,进而存储在 PipedInputStream 的缓冲中;此时,线程 B 通过读取 PipedInputStream 中的数据。就可以实现,线程 A 和线程 B 的通信。

3. Ch16 Generics

  • 构造方法中不需要写泛型(如是 Stack() 而不是 Stack<E>()
  • 泛型方法若编译器可以推断则可省略,否则用类似 GenericMethodDemo.<Integer>print(integers) 的语法。
  • 有界泛型类型(bounded generic type)<E extends GeometricObject> 表示 E 必须是 GeometricObject 或其子类。
  • 泛型类型之间没有继承关系
    • IntegerNumber 有继承关系但是 GenericStack<Integer>GenericStack<Number> 没有
    • List<Integer> list = new List<Object>(); 报错且反之亦然
  • 三种通配
    • unbound wildcard<?> 适用于任何类型
    • bound wildcard<? extends T> 类型参数必须是 T 或其子类。
      • List<? extends Number> list = new ArrayList<Integer>();
      • 不能添加 NumberIntegerDouble 到 list 中
    • lower-bound wildcard<? super T> 表示类型参数必须是 T 或其父类。
      • List<? super Integer> list = new ArrayList<Number>();
      • 只能添加 IntegerInteger 的子类到 list 中
  • 泛型的限制
    • 不能创建泛型类型的实例
      • new E() 会 CE
    • 不能创建泛型数组
      • new E[10] 会 CE,应改为 (E[]) new Object[10]
    • 泛型类的静态上下文中不能使用类型参数
      • 泛型类的所有实例都有相同的运行时类,所以泛型类的静态变量和方法是被它的所有实例共享的。
      • 在静态方法、数据域或初始化语句中,为了类而引用泛型参数是非法的。
    • 泛型类不能继承 Throwable
      • class MyException<T> extends Exception {} 会 CE
      • 主要是为了 catch 的问题
  • ArrayList<String> 不是一个类,所以 ArrayList<String> list1 = new ArrayList<String>(); 后调用 list1 instanceof ArrayList<String> 是错误的。

4. Ch17 Java Collections Framework

  • Collection 接口关系图(P22
    • Map 是独立的接口,不继承自 Collection
    • ArrayListLinkedList 实现了 List 接口。
    • HashSetTreeSet 实现了 Set 接口。
    • HashMapTreeMap 实现了 Map 接口。
  • addAllremoveAllretainAll 类似于集合的并、差、交。
  • Set
    • HashSet
      • 通过 hashCodeequals 方法保证元素唯一性。
      • 集合元素可以是 null,但只能放入一个 null
    • LinkedHashSet
      • 额外使用双向链表维护元素的插入顺序(似乎访问也还会被提到最前,即遵循 LRU)。
    • TreeSet
      • TreeSetSortedSet 接口的唯一实现类
      • SortedSet 是 Set 的一个子接口。first()和 last()返回集合中的第一个和最后一个元素;headSet(toElement)和 tailSet(fromElement)返回集合中元素小于 toElement 和大于 fromElement 的那部分。
      • NavigableSet 扩展了 SortedSet,提供导航方法 lower(e),floor(e), ceiling(e)和 higher(e),分别返回小于、小于或等于、大于或等于、大于一个元素的元素,若没这样的元素,则返回 null。
      • pollFirst()和 pollLast()则分别删除和返回 TreeSet 中的第一个和最后一个元素。
  • List
    • ArrayList:基于动态数组。
      • 支持随机访问,访问速度快。插入和删除操作效率低(特别是中间位置)。
    • LinkedList:基于双向链表。
      • 插入和删除速度快。查询效率较低。
  • Map:HashMapTreeMapLinkedHashMap
    • 对于 HashMapLinkedHashMap,自定义对象作为 key 时需要重写 hashcode()equals() 方法
  • ArrayList 中迭代器的陷阱
    • 在迭代过程中,调用容器的删除方法,则会抛出异常
    • 迭代器内部会维护索引位置相关的数据(包含最近返回的元素的索引和下一个返回的元素的索引),在迭代过程中,容器不能发生结构性变化
  • List<String> a = new ArrayList<String>();List<String> a = new ArrayList<>(); 都是合法的写法。
  • Map  不能直接使用  Iterator  遍历,但可以通过  entrySet()keySet()  或  values()  方法间接获取可迭代的集合。 for (Map.Entry<String, Integer> entry : map.entrySet()) { System.out.println(entry.getKey() + ": " + entry.getValue()); }

5. Ch25 Multithreading

  • 创建线程
    • 实现 Runnable 接口并将实例传递给 Thread 构造函数
    • 继承 Thread 类:重写 run() 方法
  • 线程控制
    • 使用 run() 启动在当前线程创建,使用 start() 启动在新线程创建
    • Thread.yield():让出当前线程的 CPU 时间片。
    • Thread.sleep(long millis) 静态方法:当前线程休眠指定时间。
    • anotherThread.join():等待另一个线程结束。
    • 中断
      • InterruptedException:是 sleep 和 join 的必检异常,注意放在循环体中要手动 break。
      • 线程的生命周期
        • New(新建):线程对象被创建,但未调用 start() 方法。
        • Runnable(就绪):调用 start() 方法后,线程处于可运行状态,但可能未获得 CPU 时间。
        • Running(运行):线程获得 CPU 时间,正在执行任务。
        • Blocked(阻塞):线程等待某些资源(如锁或 IO)。
        • Terminated(终止):线程执行完毕或被中断。
      • isAlive():用于检查线程是否处于活动状态(Ready、Blocked 或 Running)
      • interrupt():设置中断标志。对于受阻塞(Object.wait()Thread.join()Thread.sleep())的线程,将其唤醒并抛出 InterruptedException;否则将暂时不起作用。
      • isInterrupted():检查线程的中断标志是否被设置。不会清除中断标志。
    • 优先级控制
      • setPriority(int newPriority):设置线程的优先级。
      • Thread.MAX_PRIORITY / Thread.MIN_PRIORITY / Thread.NORM_PRIORITY
  • 线程同步(thread synchronization)
    • 内存可见性问题:一个线程对共享变量的修改,可能不被另一个线程及时看到。因为每个线程都有自己的工作内存,而共享变量存储在主内存中。如果线程对共享变量的修改没有及时刷新到主内存就可能出现问题。
    • volatile 关键词:保证变量的可见性。
    • synchronized 关键词或 synchronized
      • 成员方法加 synchronized 相当于对 this 上锁,静态方法加 synchronized 相当于对 XXClass.class 上锁。
      • 功能
        • 保证可见性:释放锁时,所有的写入都会写回内存,获得锁后,会从内存中读最新数据。
        • 保证互斥性:同一时刻只有一个线程可以执行 synchronized 块中的代码。
      • 可重入性:对同一个线程,它在获得锁之后,在调用其他需要同样锁的代码时,可以直接调用。
  • wait() & notify()
    • 必须在 synchronized 方法或代码块中使用。
    • 用法
      • wait():当前线程等待,释放锁。
      • notify():唤醒一个等待线程。
      • notifyAll():唤醒所有等待线程。
    • 机制
      • 把当前线程放入条件等待队列,释放对象锁,阻塞等待,线程状态变为 WAITING 或 TIMED_WAITING。
      • 等待时间到或被其他线程调用 notify/notifyAll 从条件队列中移除,这是,要重新竞争对象锁
      • 如果能获得锁,线程变为 RUNNABLE,并从 wait 调用中返回
      • 否则,线程加入对象锁等待队列,线程变为 BLOCKED,只有在获得锁后才会从 wait 调用中返回。

6. Ch28 Lambda & Stream & RTTI

  • Lambda 表达式是一种特殊的匿名内部类。
  • 任何可以接受一个函数式接口(Functional Interface, FI)实例的地方,都可以使用 Lambda 表达式。
  • 函数式接口:只能有一个抽象方法。可以有多个静态方法和默认方法。接口中的方法默认是 public abstract
    • java.util.function 包中包含了多种函数式接口,例如:Function:接收参数并返回结果。Predicate:接收参数并返回布尔值。Consumer:接收参数但无返回值。Supplier:无参数但返回结果。
  • Stream 对象:实现了  java.util.stream.Stream  接口。
    • 生成流:stream()parallelStream()Stream.of("a1", "a2", "a3")
    • 常用方法:
      • filter:过滤满足条件的元素。
      • map:映射为新值
      • flatMap:映射成流并 flat 成一个
      • distinct:去重(基于 hashCodeequals
      • sorted:排序
      • limit(int n):只保留前至多 nn
      • skip(int n):跳过前 nn
    • 终端操作:
      • boolean allMatch(Predicate<? super T> predicate); 检查是否匹配所有元素。
      • boolean anyMatch(Predicate<? super T> predicate); 检查是否至少匹配一个元素。
      • boolean noneMatch(Predicate<? super T> predicate); 检查是否没有匹配所有元素。
      • Optional<T> findFirst(); 返回当前流中的第一个元素。
      • Optional<T> findAny(); 返回当前流中的任意元素。
      • long count(); 返回流中元素总数。
      • Optional<T> max(Comparator<? super T> comparator); 返回流中最大值。
      • Optional<T> min(Comparator<? super T> comparator); 返回流中最小值。
      • T reduce(T identity, BinaryOperator<T> accumulator); 可以将流中元素反复结合起来,得到一个值。返回 T。这是一个归约操作。
      • collect:转化为其他形式,如 .collect(Collectors.toSet())toList()
  • IntStreamgetAsInt()
  • RTTI
    • object.getClass()TheClass.class 来获取类对象。
    • clazz.isInstance(obj) 判断 obj 是不是当前类或当前类子类的实例
    • getSuperclass()getInterfaces()getModifiers()
    • Method 类
      • invoke()
      • instanceof 运算符

评论

TABLE OF CONTENTS

1. Ch13 Exception Handling and Text IO
1.1. Exceptions
1.2. Exception Handling
1.3. Assertions
1.4. Text IO
2. Ch14 Binary IO
2.1. Binary IO Basic
2.2. Object IO Stream
2.3. Piped IO Stream
3. Ch16 Generics
4. Ch17 Java Collections Framework
5. Ch25 Multithreading
6. Ch28 Lambda & Stream & RTTI