java 入门教程

1. Java 概述

Java 是一种面向对象的编程语言,由 Sun Microsystems(现在的 Oracle)在 1995 年推出。Java 程序可以在任何支持 Java 虚拟机 (JVM) 的设备上运行。Java 的核心理念是“一次编写,到处运行”。

2. 基本语法

2.1 Java 程序结构

每个 Java 程序都由类 (class) 和方法 (method) 组成。以下是一个简单的 Java 程序示例:

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

2.2 注释

Java 支持三种类型的注释:

3. 数据类型

Java 的数据类型分为两大类:基本数据类型 (primitive types) 和引用数据类型 (reference types)。

3.1 基本数据类型

int number = 10;
float pi = 3.14f;
char letter = 'A';
boolean isJavaFun = true;

3.2 引用数据类型

引用数据类型包括类 (class), 接口 (interface), 数组 (array),以及枚举 (enum)。

4. 运算符

Java 提供了丰富的运算符,包括:

int a = 5;
int b = 10;
int sum = a + b; // 加法
boolean isEqual = (a == b); // 比较

5. 判断和循环

5.1 条件语句

if (a > b) {
    System.out.println("a is greater than b");
} else if (a < b) {
    System.out.println("a is less than b");
} else {
    System.out.println("a is equal to b");
}
 
switch (a) {
    case 1:
        System.out.println("a is 1");
        break;
    case 2:
        System.out.println("a is 2");
        break;
    default:
        System.out.println("a is not 1 or 2");
}

5.2 循环语句

for (int i = 0; i < 5; i++) {
    System.out.println(i);
}
 
int j = 0;
while (j < 5) {
    System.out.println(j);
    j++;
}
 
int k = 0;
do {
    System.out.println(k);
    k++;
} while (k < 5);

5.3 常用遍历方法

在 Java 中,遍历数组和字符串是常见的操作。下面详细介绍几种常用的遍历方法。

1. 遍历数组的方法

1.1 使用 for 循环

传统的 for 循环是遍历数组的常见方法:

int[] numbers = {1, 2, 3, 4, 5};
for (int i = 0; i < numbers.length; i++) {
    System.out.println(numbers[i]);
}

这里的 i 是数组的索引,通过 numbers[i] 获取数组元素。

1.2 使用增强型 for 循环(for-each 循环)

增强型 for 循环简化了数组的遍历,不需要使用索引:

int[] numbers = {1, 2, 3, 4, 5};
for (int number : numbers) {
    System.out.println(number);
}

这种方法直接获取数组中的每个元素,语法简洁。

2. 遍历字符串的方法

2.1 使用 for 循环

字符串可以看作是字符数组,可以用 for 循环逐个字符地遍历:

String text = "Hello";
for (int i = 0; i < text.length(); i++) {
    System.out.println(text.charAt(i));
}

charAt(i) 方法返回字符串中第 i 个字符。

2.2 使用增强型 for 循环(for-each 循环)

虽然增强型 for 循环不能直接用于 String,但可以将字符串转换为字符数组后进行遍历:

String text = "Hello";
for (char ch : text.toCharArray()) {
    System.out.println(ch);
}

toCharArray() 方法将字符串转换为字符数组,然后进行遍历。

2.3 使用 Stream API

同样地,可以使用 Stream API 来遍历字符串:

String text = "Hello";
text.chars().forEach(c -> System.out.println((char) c));

chars() 方法返回一个包含字符的 IntStream,需要将 int 类型转换为 char 类型。

3. 其他的遍历方法

3.1 使用迭代器(Iterator

对于集合类(如 ListSet 等),可以使用 Iterator 进行遍历:

List<String> list = Arrays.asList("A", "B", "C");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}

Iterator 提供了 hasNext()next() 方法,用于顺序访问集合中的元素。

3.2 使用 forEach 方法

Java 8 引入的 forEach 方法可以直接用于遍历集合和 Map

List<String> list = Arrays.asList("A", "B", "C");
list.forEach(System.out::println);
 
Map<Integer, String> map = new HashMap<>();
map.put(1, "One");
map.put(2, "Two");
map.forEach((key, value) -> System.out.println(key + " = " + value));

这种方法语法简洁,尤其适合使用 Lambda 表达式进行处理。

6. 数组

数组是相同数据类型的集合,可以存储固定大小的元素。

int[] numbers = new int[5];
numbers[0] = 1;
numbers[1] = 2;
// 其他元素初始化
 
int[] primes = {2, 3, 5, 7, 11};
System.out.println(primes[0]); // 输出第一个元素

Java 的 Lambda 表达式是一种简化代码的功能,主要用于表示匿名函数。它是 Java 8 引入的特性,旨在提供一种更简洁的方式来创建实现了某个接口的对象,特别是对于函数式接口。函数式接口是只包含一个抽象方法的接口。

Lambda 表达式的基本语法

基本语法:

(parameters) -> expression

或者

(parameters) -> { statements; }

示例

  1. 无参数

    Runnable r = () -> System.out.println("Hello, World!");
    r.run();  // 输出: Hello, World!
    

    这个 Lambda 表达式实现了 Runnable 接口,它没有参数,并且打印出 "Hello, World!"。

  2. 一个参数

    Consumer<String> printer = s -> System.out.println(s);
    printer.accept("Hello, Lambda!");  // 输出: Hello, Lambda!
    

    这个 Lambda 表达式实现了 Consumer 接口,它接收一个参数并打印它。

  3. 多个参数

    BinaryOperator<Integer> add = (a, b) -> a + b;
    System.out.println(add.apply(5, 3));  // 输出: 8
    

    这个 Lambda 表达式实现了 BinaryOperator 接口,它接受两个参数并返回它们的和。

  4. 多个语句

    Function<Integer, String> intToString = i -> {
        String result = "Number: " + i;
        return result;
    };
    System.out.println(intToString.apply(10));  // 输出: Number: 10
    

    这个 Lambda 表达式实现了 Function 接口,包含多个语句,返回一个字符串。

7. 面向对象编程

面向对象编程 (OOP) 是 Java 的核心概念,以下是几个重要的面向对象概念:

7.1 接口

接口是一种抽象类型,定义了类必须实现的方法。接口中的所有方法默认都是抽象的(没有方法体),且所有字段默认都是 public static final

interface Animal {
    void eat();
    void sleep();
}
 
class Dog implements Animal {
    @Override
    public void eat() {
        System.out.println("Dog eats");
    }
 
    @Override
    public void sleep() {
        System.out.println("Dog sleeps");
    }
}
 
public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.eat();
        dog.sleep();
    }
}

7.2 抽象类

抽象类是不能被实例化的类,可以包含抽象方法和具体方法。抽象方法必须在子类中实现。

abstract class Animal {
    abstract void makeSound();
 
    public void sleep() {
        System.out.println("Sleeping...");
    }
}
 
class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Bark");
    }
}
 
public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.makeSound();
        dog.sleep();
    }
}

7.3 继承

继承是指一个类(子类)继承另一个类(父类)的属性和方法。子类可以扩展或重写父类的方法。

class Animal {
    public void eat() {
        System.out.println("Animal eats");
    }
}
 
class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("Dog eats");
    }
}
 
public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.eat();
    }
}

7.4 多态

多态允许同一个接口在不同的实现中表现出不同的行为。它是通过方法重载和方法重写实现的。

class Animal {
    public void makeSound() {
        System.out.println("Some sound");
    }
}
 
class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Bark");
    }
}
 
class Cat extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Meow");
    }
}
 
public class Main {
    public static void main(String[] args) {
        Animal myDog = new Dog();
        Animal myCat = new Cat();
        myDog.makeSound();
        myCat.makeSound();
    }
}

8. 输入输出 (I/O)

Java 的 I/O 库提供了丰富的类和接口,用于文件操作、数据流操作、网络通信等。

8.1 Scanner 类

Scanner 类是 Java 5 引入的,用于从各种输入源读取数据,例如控制台输入、文件、字符串等。它提供了一系列方便的方法来解析基本类型和字符串。

下面给的示例代码都是从控制台获取输入

import java.util.Scanner;
 
public class ScannerExample {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
 
        System.out.print("请输入您的名字: ");
        String name = scanner.nextLine(); // 读取整行输入
        System.out.println("你好, " + name + "!");
 
        System.out.print("请输入您的年龄: ");
        int age = scanner.nextInt(); // 读取整数输入
        System.out.println("您 " + age + " 岁了!");
 
        scanner.close(); // 关闭 Scanner
    }
}
常用方法:

8.2 BufferedReader 类

BufferedReader 类用于从字符输入流中读取文本,提供了缓冲功能以提高读取效率。它通常与 InputStreamReader 一起使用,从标准输入或文件读取数据。

使用 BufferedReader 从终端读取数据
示例代码:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
 
public class BufferedReaderExample {
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
 
        System.out.print("请输入您的名字: ");
        String name = reader.readLine(); // 读取整行输入
        System.out.println("你好, " + name + "!");
 
        System.out.print("请输入您的年龄: ");
        int age = Integer.parseInt(reader.readLine()); // 读取整行输入并解析为整数
        System.out.println("您 " + age + " 岁了!");
 
        reader.close(); // 关闭 BufferedReader
    }
}
常用方法:

对比 Scanner 和 BufferedReader

相同点:

  1. 都可以用于从终端、文件等源读取输入。
  2. 都提供了读取整行输入的方法:nextLine()readLine()

不同点:

  1. 功能
    • Scanner 提供了更多解析输入数据的方法,如 nextInt(), nextDouble() 等,可以直接读取基本类型数据。
    • BufferedReader 主要用于读取字符串,需要手动解析基本类型数据。
  2. 性能
    • BufferedReader 通常性能更高,因为它使用缓冲机制,适合读取大量文本数据。
    • Scanner 在方便性上有优势,但性能可能稍逊色。
  3. 使用场景
    • Scanner 更适合处理交互式的终端输入,或者需要解析各种基本类型数据的场景。
    • BufferedReader 更适合读取大量文本数据,或者需要更高效的输入操作的场景。

8.3 输出

将数据输出到控制台。

1. System.out.print()

2. System.out.println()

3. System.out.printf()

8.4 常用格式说明符

总结

try-with-resources语句(通常被称为try语句)是Java 7引入的一个特性,它简化了资源管理,特别是用于自动关闭实现了AutoCloseable接口的资源,如InputStreamOutputStreamConnection等。try-with-resources语句保证在try块执行完毕后,即使发生异常,资源也会被自动关闭。

当然!在Java中,try-catch语句用于处理异常。异常是程序运行过程中发生的错误或不预期的情况,try-catch块允许你捕获和处理这些异常,以避免程序崩溃并提供更好的用户体验。

9、异常处理

1. 基本语法

try-catch语句的基本语法如下:

try {
    // 可能引发异常的代码
} catch (ExceptionType e) {
    // 异常处理代码
}

2. 示例

public class TryCatchExample {
    public static void main(String[] args) {
        try {
            int result = 10 / 0; // 这会引发 ArithmeticException
        } catch (ArithmeticException e) {
            System.out.println("发生了除以零的错误: " + e.getMessage());
        }
    }
}

在这个示例中,try块中的代码试图执行一个除以零的操作,导致ArithmeticException异常。catch块捕获这个异常,并输出错误消息。

3. 多个 catch

你可以有多个catch块来处理不同类型的异常:

public class MultiCatchExample {
    public static void main(String[] args) {
        try {
            int[] numbers = new int[5];
            numbers[10] = 10; // 这会引发 ArrayIndexOutOfBoundsException
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("数组索引越界错误: " + e.getMessage());
        } catch (Exception e) {
            System.out.println("发生了其他异常: " + e.getMessage());
        }
    }
}

在这个示例中,ArrayIndexOutOfBoundsException异常被单独捕获,而其他任何类型的异常都可以被通用的Exception捕获。

4. finally

finally块用于执行清理代码,无论是否发生异常,它都会执行。通常用于关闭文件、释放资源等操作。

public class FinallyExample {
    public static void main(String[] args) {
        try {
            System.out.println("尝试打开文件");
            // 模拟文件操作
        } catch (Exception e) {
            System.out.println("处理异常: " + e.getMessage());
        } finally {
            System.out.println("无论如何,都会执行的代码");
        }
    }
}

在这个示例中,无论try块中是否发生异常,finally块中的代码都会执行。

5. 多重异常处理

Java 7引入了多重异常捕获(多异常捕获),你可以在一个catch块中捕获多种异常类型,并用|分隔它们:

public class MultiExceptionCatchExample {
    public static void main(String[] args) {
        try {
            // 可能引发多种异常的代码
        } catch (IOException | SQLException e) {
            System.out.println("发生了 IO 或 SQL 异常: " + e.getMessage());
        }
    }
}

6. 重新抛出异常

catch块中,你可以选择重新抛出异常,以便在更高的层次处理它:

public class RethrowExceptionExample {
    public static void main(String[] args) {
        try {
            methodThatThrowsException();
        } catch (Exception e) {
            System.out.println("捕获异常: " + e.getMessage());
            throw e; // 重新抛出异常
        }
    }
 
    public static void methodThatThrowsException() throws Exception {
        throw new Exception("这是一个异常");
    }
}

在这个示例中,异常在catch块中被捕获并重新抛出,允许调用方法的代码进一步处理异常。

7. try-with-resources

try-with-resources语句在try块执行完毕后,会自动调用每个资源的close()方法。即使在try块中发生了异常,资源的close()方法也会被调用。因此,你不需要在finally块中显式地关闭资源,这样可以减少代码重复并提高代码的可读性。

try-with-resources语句的基本语法如下:

try (ResourceType resource = new ResourceType()) {
    // 使用资源的代码
} catch (ExceptionType e) {
    // 异常处理代码
}

示例

使用 try-with-resources 关闭文件

以下是一个使用try-with-resources读取文件内容的示例。BufferedReader实现了AutoCloseable接口,因此可以被用于try-with-resources语句中:

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
 
public class TryWithResourcesExample {
    public static void main(String[] args) {
        try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            System.out.println("读取文件时发生错误: " + e.getMessage());
        }
    }
}

在这个示例中,BufferedReader会在try块执行完成后自动关闭,无需显式调用close()方法。

多个资源

你也可以在try-with-resources语句中管理多个资源,这些资源会按照声明的顺序关闭:

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.zip.GZIPInputStream;
 
public class MultipleResourcesExample {
    public static void main(String[] args) {
        try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"));
             GZIPInputStream gzipInputStream = new GZIPInputStream(new FileInputStream("file.txt.gz"))) {
            // 使用 reader 和 gzipInputStream 进行操作
        } catch (IOException e) {
            System.out.println("发生了 IO 异常: " + e.getMessage());
        }
    }
}

在这个示例中,BufferedReaderGZIPInputStream会在try块执行完成后自动关闭。

自定义资源

如果你定义了一个自定义类,并希望它在try-with-resources中使用,那么这个类需要实现AutoCloseable接口(或者java.io.Closeable接口)。例如:

public class CustomResource implements AutoCloseable {
    @Override
    public void close() {
        System.out.println("CustomResource closed");
    }
 
    public void doSomething() {
        System.out.println("Doing something with CustomResource");
    }
 
    public static void main(String[] args) {
        try (CustomResource resource = new CustomResource()) {
            resource.doSomething();
        } catch (Exception e) {
            System.out.println("发生了异常: " + e.getMessage());
        }
    }
}

在这个示例中,CustomResource实现了AutoCloseable接口,因此可以在try-with-resources中使用,并在try块结束后自动关闭。

try-with-resources语句使得资源管理变得更加简单、安全,特别是当涉及到需要手动关闭的资源时。

10、Java 集合框架

String

1. 创建与初始化

2. 字符串操作

3. 查找与比较

4. 替换与转换

5. 分割与连接

6. 其他

List

List 是一个有序的集合,可以包含重复元素。常用实现类有 ArrayListLinkedList

ArrayList

ArrayList 是一个基于动态数组的数据结构,提供了快速的随机访问能力。它的主要特点是:

常用方法:

示例代码:

import java.util.ArrayList;
import java.util.List;
 
public class ArrayListExample {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("Apple");
        list.add("Banana");
        list.add("Cherry");
 
        System.out.println("List: " + list);
        System.out.println("Element at index 1: " + list.get(1));
 
        list.set(1, "Blueberry");
        System.out.println("Updated List: " + list);
 
        list.remove(0);
        System.out.println("List after removal: " + list);
    }
}
LinkedList

LinkedList 是一个基于双向链表的数据结构,提供了高效的插入和删除操作。它的主要特点是:

常用方法:

示例代码:

import java.util.LinkedList;
import java.util.List;
 
public class LinkedListExample {
    public static void main(String[] args) {
        List<String> list = new LinkedList<>();
        list.add("Apple");
        list.add("Banana");
        list.add("Cherry");
 
        System.out.println("List: " + list);
        System.out.println("Element at index 1: " + list.get(1));
 
        list.set(1, "Blueberry");
        System.out.println("Updated List: " + list);
 
        list.remove(0);
        System.out.println("List after removal: " + list);
    }
}

Set

Set 是一个不包含重复元素的集合。常用实现类有 HashSetTreeSet

HashSet

HashSet 基于哈希表实现,元素没有顺序。它的主要特点是:

常用方法:

示例代码:

import java.util.HashSet;
import java.util.Set;
 
public class HashSetExample {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        set.add("Apple");
        set.add("Banana");
        set.add("Cherry");
        set.add("Apple"); // 重复元素
 
        System.out.println("Set: " + set);
        System.out.println("Set contains 'Banana': " + set.contains("Banana"));
 
        set.remove("Banana");
        System.out.println("Set after removal: " + set);
    }
}
TreeSet

TreeSet 基于红黑树实现,元素是有序的。它的主要特点是:

常用方法:

示例代码:

import java.util.Set;
import java.util.TreeSet;
 
public class TreeSetExample {
    public static void main(String[] args) {
        Set<String> set = new TreeSet<>();
        set.add("Banana");
        set.add("Apple");
        set.add("Cherry");
 
        System.out.println("Set: " + set);
        System.out.println("Set contains 'Banana': " + set.contains("Banana"));
 
        set.remove("Banana");
        System.out.println("Set after removal: " + set);
    }
}

Map

Map 是一个键值对的集合,每个键最多只能关联一个值。常用实现类有 HashMapTreeMap

HashMap

HashMap 基于哈希表实现,键值对没有顺序。它的主要特点是:

常用方法:

示例代码:

import java.util.HashMap;
import java.util.Map;
 
public class HashMapExample {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("Apple", 1);
        map.put("Banana", 2);
        map.put("Cherry", 3);
 
        System.out.println("Map: " + map);
        System.out.println("Value for 'Banana': " + map.get("Banana"));
 
        map.remove("Banana");
        System.out.println("Map after removal: " + map);
    }
}
TreeMap

TreeMap 基于红黑树实现,键值对是有序的。它的主要特点是:

常用方法:

示例代码:

import java.util.Map;
import java.util.TreeMap;
 
public class TreeMapExample {
    public static void main(String[] args) {
        Map<String, Integer> map = new TreeMap<>();
        map.put("Banana", 2);
        map.put("Apple", 1);
        map.put("Cherry", 3);
 
        System.out.println("Map: " + map);
        System.out.println("Value for 'Banana': " + map.get("Banana"));
 
        map.remove("Banana");
        System.out.println("Map after removal: " + map);
    }
}

Queue

Queue 是一个先进先出的集合,常用实现类有 LinkedListPriorityQueue

LinkedList

LinkedList 实现了 Queue 接口,提供了基于链表的队列实现。它的主要特点是:

常用方法:

示例代码:

import java.util.LinkedList;
import java.util.Queue;
 
public class QueueExample {
    public static void main(String[] args) {
        Queue<String> queue = new LinkedList<>();
        queue.add("Apple");
        queue.offer("Banana");
        queue.add("Cherry");
 
        System.out.println("Queue: " + queue);
        System.out.println("Head of the queue: " + queue.peek());
 
        queue.remove();
        System.out.println("Queue after removal: " + queue);
 
        queue.poll();
        System.out.println("Queue after poll: " + queue);
    }
}
PriorityQueue

PriorityQueue 是一个基于优先级堆(最小堆或最大堆)的队列,元素按自然顺序或自定义顺序排序。它的主要特点是:

常用方法与 LinkedList 类似:

示例代码:

import java.util.PriorityQueue;
import java.util.Queue;
 
public class PriorityQueueExample {
    public static void main(String[] args) {
        Queue<String> queue = new PriorityQueue<>();
        queue.add("Banana");
        queue.offer("Apple");
        queue.add("Cherry");
 
        System.out.println("PriorityQueue: " + queue);
        System.out.println("Head of the queue: " + queue.peek());
 
        queue.remove();
        System.out.println("PriorityQueue after removal: " + queue);
 
        queue.poll();
        System.out.println("PriorityQueue after poll: " + queue);
    }
}

当然可以!让我们详细探讨一下Java集合框架的CollectionStream两个主要组件。

Stream

Stream是Java 8引入的一个新特性,它提供了一种功能性编程风格来处理集合数据。Stream并不存储数据,而是对数据进行操作的工具。

1. 创建 Stream

1. 从 Collection 创建 Stream

2. 从数组创建 Stream

3. 生成特定类型的 Stream

4. 创建字符流

2. Stream 操作

Stream支持多种操作,可以分为中间操作和终端操作:

在 Java 的 Stream API 中,Stream.sorted() 方法默认按照升序对流中的元素进行排序。你可以通过使用 Comparator 进行自定义排序来控制排序顺序,包括降序排序。

3. sorted

Stream.sorted() 方法在没有参数的情况下会使用元素的自然顺序(如果元素实现了 Comparable 接口),即默认进行升序排序。例如:

import java.util.Arrays;
import java.util.List;
 
public class StreamSortExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(5, 3, 8, 1, 2);
 
        // 默认升序排序
        numbers.stream()
               .sorted()  // 默认升序
               .forEach(System.out::println);  // 输出: 1, 2, 3, 5, 8
    }
}

降序排序

要实现降序排序,你可以使用 Comparatorreversed() 方法,或者使用 ComparatorreverseOrder() 方法。以下是如何进行降序排序的示例:

使用 Comparator.reversed()

import java.util.Arrays;
import java.util.List;
import java.util.Comparator;
 
public class StreamSortExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(5, 3, 8, 1, 2);
 
        // 降序排序
        numbers.stream()
               .sorted(Comparator.reverseOrder())  // 使用 reverseOrder() 实现降序
               .forEach(System.out::println);  // 输出: 8, 5, 3, 2, 1
    }
}

自定义排序(示例)

如果你需要对复杂对象进行降序排序,可以使用自定义的 Comparator

import java.util.Arrays;
import java.util.List;
import java.util.Comparator;
 
public class StreamSortExample {
    public static void main(String[] args) {
        List<Person> people = Arrays.asList(
            new Person("Alice", 30),
            new Person("Bob", 25),
            new Person("Charlie", 30),
            new Person("David", 25)
        );
 
        // 按年龄降序排序,如果年龄相同则按姓名升序排序
        people.stream()
              .sorted(Comparator.comparingInt(Person::getAge).reversed()
                                .thenComparing(Person::getName))
              .forEach(person -> System.out.println(person.getName() + ": " + person.getAge()));
    }
 
    static class Person {
        private String name;
        private int age;
 
        Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
 
        public String getName() {
            return name;
        }
 
        public int getAge() {
            return age;
        }
    }
}

总结

4. Stream 示例

import java.util.*;
import java.util.stream.Collectors;
 
public class StreamExample {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
        
        // 过滤出以 "A" 开头的名字
        List<String> filteredNames = names.stream()
            .filter(name -> name.startsWith("A"))
            .map(String::toUpperCase)
            .collect(Collectors.toList());
        System.out.println("Filtered Names: " + filteredNames);
        
        // 计算名字的总长度
        int totalLength = names.stream()
            .mapToInt(String::length)
            .sum();
        System.out.println("Total Length: " + totalLength);
        
        // 查找第一个以 "C" 开头的名字
        Optional<String> firstNameStartingWithC = names.stream()
            .filter(name -> name.startsWith("C"))
            .findFirst();
        firstNameStartingWithC.ifPresent(name -> System.out.println("First Name Starting with C: " + name));
    }
}

11、对文件的操作

Java中,输入输出流(I/O流)是处理数据输入和输出的关键机制。它们用于读取和写入数据,支持处理字节流和字符流。Java I/O流可以分为两大类:

  1. 字节流(Byte Streams):处理原始字节的数据流,如图片、音频、视频文件。
  2. 字符流(Character Streams):专门处理字符数据,如文本文件。

Java的输入输出流主要位于java.io包中。下面详细讲解如何使用Java的I/O流进行文件、控制台、网络等输入输出操作。


1. 字节流

字节流是以字节为单位进行数据的输入和输出。Java通过两个顶层抽象类来处理字节流:

1.1 InputStream

InputStream是读取字节数据的基础类,常用子类包括:

1.1.1 FileInputStream读取文件

FileInputStream用于从文件中读取字节数据。以下是读取文件的示例:

import java.io.FileInputStream;
import java.io.IOException;
 
public class FileInputStreamExample {
    public static void main(String[] args) {
        try (FileInputStream fis = new FileInputStream("example.txt")) {
            int byteData;
            while ((byteData = fis.read()) != -1) {
                System.out.print((char) byteData);  // 将字节数据转换为字符输出
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
1.1.2 BufferedInputStream缓冲读取

BufferedInputStream提供缓冲区,减少对文件的访问次数,提高读取效率。

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
 
public class BufferedInputStreamExample {
    public static void main(String[] args) {
        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("example.txt"))) {
            int byteData;
            while ((byteData = bis.read()) != -1) {
                System.out.print((char) byteData);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

1.2 OutputStream

OutputStream用于写入字节数据,常用子类包括:

1.2.1 FileOutputStream写入文件

FileOutputStream用于向文件中写入字节数据。如果文件不存在,它会创建文件。

import java.io.FileOutputStream;
import java.io.IOException;
 
public class FileOutputStreamExample {
    public static void main(String[] args) {
        try (FileOutputStream fos = new FileOutputStream("output.txt")) {
            String data = "Hello, FileOutputStream!";
            fos.write(data.getBytes());  // 将字符串转换为字节并写入文件
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
1.2.2 BufferedOutputStream缓冲写入

BufferedOutputStream提供缓冲区,可以提高写入效率。

import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
 
public class BufferedOutputStreamExample {
    public static void main(String[] args) {
        try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("output.txt"))) {
            String data = "Hello, BufferedOutputStream!";
            bos.write(data.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2. 字符流

字符流用于处理字符数据,专门设计为以字符为单位进行输入输出。Java通过两个顶层抽象类来处理字符流:

2.1 Reader

Reader类用于读取字符数据,常用子类包括:

2.1.1 FileReader读取字符文件

FileReader用于从文件中读取字符数据。

import java.io.FileReader;
import java.io.IOException;
 
public class FileReaderExample {
    public static void main(String[] args) {
        try (FileReader fr = new FileReader("example.txt")) {
            int charData;
            while ((charData = fr.read()) != -1) {
                System.out.print((char) charData);  // 输出字符数据
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
2.1.2 BufferedReader按行读取

BufferedReader不仅提供缓冲功能,还提供readLine()方法用于按行读取文件内容。

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
 
public class BufferedReaderExample {
    public static void main(String[] args) {
        try (BufferedReader br = new BufferedReader(new FileReader("example.txt"))) {
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println(line);  // 按行读取并输出
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2.2 Writer

Writer类用于写入字符数据,常用子类包括:

2.2.1 FileWriter写入字符文件

FileWriter用于向文件中写入字符数据。

import java.io.FileWriter;
import java.io.IOException;
 
public class FileWriterExample {
    public static void main(String[] args) {
        try (FileWriter fw = new FileWriter("output.txt")) {
            fw.write("Hello, FileWriter!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
2.2.2 BufferedWriter缓冲写入

BufferedWriter不仅提供缓冲区,还提供newLine()方法,用于写入换行符。

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
 
public class BufferedWriterExample {
    public static void main(String[] args) {
        try (BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"))) {
            bw.write("Hello, BufferedWriter!");
            bw.newLine();  // 写入换行符
            bw.write("This is a new line.");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

3. 转换流(Character Streams)

转换流用于在字节流和字符流之间进行转换,主要用于处理不同的字符编码。Java提供了两个主要的转换流类:

1. InputStreamReader

InputStreamReader是一个将字节流转换为字符流的桥梁。它读取来自字节流的数据,并根据指定的字符编码将其转换为字符数据。

InputStreamReader的构造方法

InputStreamReader(InputStream in)                  // 使用默认字符编码(通常是UTF-8或平台默认编码)
InputStreamReader(InputStream in, String charsetName) // 使用指定的字符编码

示例:使用InputStreamReader

以下是使用InputStreamReader从字节流中读取字符的示例:

import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.IOException;
 
public class InputStreamReaderExample {
    public static void main(String[] args) {
        try (FileInputStream fis = new FileInputStream("example.txt");
             InputStreamReader isr = new InputStreamReader(fis, "UTF-8")) {  // 指定字符编码为UTF-8
 
            int data;
            while ((data = isr.read()) != -1) {
                System.out.print((char) data);  // 输出读取的字符
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2. OutputStreamWriter

OutputStreamWriter是将字符流转换为字节流的桥梁。它接收字符数据,并根据指定的字符编码将其转换为字节流输出。

OutputStreamWriter的构造方法

OutputStreamWriter(OutputStream out)                  // 使用默认字符编码
OutputStreamWriter(OutputStream out, String charsetName) // 使用指定字符编码

示例:使用OutputStreamWriter

以下是使用OutputStreamWriter将字符数据写入字节流的示例:

import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.IOException;
 
public class OutputStreamWriterExample {
    public static void main(String[] args) {
        try (FileOutputStream fos = new FileOutputStream("output.txt");
             OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8")) {  // 指定字符编码为UTF-8
 
            osw.write("你好,世界!");  // 写入字符数据
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

3. 字符编码

当处理国际化字符或不同语言的文本时,指定正确的字符编码非常重要。例如,UTF-8是一种常用的字符编码,可以表示世界上大多数语言中的字符。使用转换流可以确保在不同字符编码之间进行正确的转换。

常见字符编码

4. InputStreamReader 和 OutputStreamWriter 的常见用途

  1. 读取文件内容并指定编码:当处理包含特殊字符的文件时,使用InputStreamReader可以正确处理字符数据。
  2. 写入文件内容并指定编码:使用OutputStreamWriter可以将字符流以特定编码方式写入文件,确保跨平台的字符一致性。
  3. 网络通信中的字符转换:在网络应用中,传输的数据往往是字节流,但客户端和服务器端可能使用不同的字符编码,因此需要使用转换流来处理。

5. 总结