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("HelloWorld!");
}
}
  • public class HelloWorld :定义一个名为 HelloWorld 的公共类。
  • public static void main(String[] args) :主方法,是程序的入口点。
  • System.out.println("HelloWorld!"); :打印一行文本到控制台。

2.2 注释

Java 支持三种类型的注释:

  • 单行注释:使用 //
  • 多行注释:使用 /* ... */
  • 文档注释:使用 /** ... */

3.数据类型

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

3.1 基本数据类型

  • 整形byte , short , int , long
  • 浮点型float , double
  • 字符型char
  • 布尔型boolean
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 语句:用于条件判断
  • switch 语句:用于多分支选择
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 循环:用于固定次数的循环
  • while 循环:用于条件控制的循环
  • do-while 循环:至少执行一次的循环
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. 使用 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] 获取数组元素。

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

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

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

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

② 遍历字符串的方法

  1. 使用 for 循环

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

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

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

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

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

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

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

  3. 使用 stream API

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

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

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

③ 其他的遍历方法

  1. 使用迭代器(Iterator

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

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

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

  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; }
  • parameters: 这是 Lambda 表达式的输入参数,可以省略类型声明。
  • ->: 这是 Lambda 运算符,将参数与表达式或语句块分隔开。
  • expression: 这是一个单一的表达式,Lambda 表达式将其返回。
  • statements: 如果 Lambda 表达式包含多个语句,则需要用 {} 包括起来。

示例

  1. 无参数

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

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

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

    Function<Integer,String> intToString = i -> {
    String result = "Number:" + i;
    return result;
    };
    System.out.println(inToString.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 vid sleep() {
System.out.println("Sleeping...");
}
}

class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Brak");
}
}

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
}
}

常用方法:

  • nextLine():读取一整行输入,返回一个字符串。
  • nextLine().charAt(0):读取一行字符串中的第一个,一般用这个读取 char 类型变量。
  • nextInt():读取一个整数。
  • nexrtDouble():读取一个双精度浮点数。
  • nextBoolean():读取一个布尔值。
  • hasNext():检查是否有下一个输入。
  • hasNextLine():检查是否有下一行输入。
  • close():关闭 Scanner。

8.2 BufferedReader 类

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

使用 Buffered 从终端读取数据

示例代码:

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
}
}

常用方法:

  • readLine():读取一整行输入,返回一个字符串。
  • 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()

    • 示例:

      // 输出内容到控制台,末尾没有换行
      System.out.print("Hello");
      System.out.print("World");
    • 输出:

      HelloWorld
  2. System.out.println()

    • 示例:

      // 输出内容到控制台,并在内容后自动添加换行符。
      System.out.println("Hello");
      System.out.println("World");
    • 输出:

      Hello
      World
  3. System.out.printf()

    • 用法:System.out.printf(formatString,agruments);

    • 示例:

      //  使用格式化字符串输出内容,类似于 C 语言中的 `printf`。允许你使用格式说明符控制输出的格式。
      int age = 25;
      double height = 1.75;
      System.out.printf("Age: %d years\n", age);
      System.out.printf("Height: %.2f meters\n", height);
    • 输出:

      Age: 25 years
      Height: 1.75 meters

8.4 常用格式说明符

  1. 整数
    • %d :十进制整数
    • %x :十六进制整数
  2. 浮点数
    • %f :浮点数
    • %.2f :浮点数,保留两位小数
  3. 字符串
    • %s :字符串
  4. 字符
    • %c :单个字符
  5. 百分比
    • %% :输出百分号 %

总结

  • System.out.print() :用于输出内容,不换行。
  • System.out.println() :用于输出内容并换行。
  • System.out.printf() :用于格式化输出内容。

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) {
    // 异常处理代码
    }
    • try 块:包含可能会英法异常的代码。如果 try 块中的代码引发了异常,异常会被 catch 块捕获。
    • 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) {
    // 异常处理代码
    }
    • ResourceType: 实现了AutoCloseablejava.io.Closeable接口的资源类型。
    • resource: 在try块中使用的资源对象。
    • try: 包含使用资源的代码。
    • catch: 处理在try块中可能发生的异常。

    示例

    使用 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());
    }
    }
    }

    自定义资源

    如果你定义了一个自定义类,并希望它在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 集合框架

10.1 String

  1. 创建与初始化
    • String():创建一个空字符串。
    • String(String original):创建一个新的字符串,内容为指定的 String
  2. 字符串操作
    • concat(String str) :连接指定字符串到当前字符串末尾。
    • substring(int beginIndex) :从指定索引开始,返回子字符串。
    • substring(int beginIndex,int endIndex) :返回从 beginIndexendIndex 之间的子字符串。
  3. 查找与比较
    • indexOf(String str) :返回指定子字符串第一次出现的索引。
    • lastIndexOf(String str) :返回指定子字符串最后一次出现的索引。
    • contains(CharSequence sequence) :判断当前字符串是否包含指定字符序列。
    • equals(Object andObject) :比较两个字符串的内容是否相等。
    • equalsIgnoreCase(String anotherString) :忽略大小写比较两个字符串的内容是否相等。
    • compareTo(String anotherString) :按字典顺序比较两个字符串。
  4. 替换与转换
    • replace(char oldChar,char newChar) :替换字符串中的所有指定字符为新字符。
    • replaceAll(String regex,String replacement) :用正则表达式匹配并替换匹配的部分。
    • toLowerCase() :将字符串转换为小写。
    • toUpperCase() :将字符串转换为大写。
    • trim() :去除字符串首尾的空白字符。
  5. 分割与连接
    • split(String regex) :根据正则表达式分割字符串,返回字符串数组。
    • join(CharSequence delimiter,CharSequence... elements) :使用指定的分隔符连接多个字符序列。
  6. 其他
    • charAt(int index) :返回指定索引处的字符。Java 的 String 不可以通过 str[0] 这样的方式访问
    • length() :返回字符串的长度。
    • isEmpty() :判断字符串石佛偶为空(长度为 0 )。
    • toCharArray() :将字符串转换为字符数组。
    • startsWith(String prefix) :判断字符串是否以指定的前缀开始。
    • endsWith(String suffix) :判断字符串是否以指定的后缀结束。
    • matches(String regex) :判断字符串是否匹配给定的正则表达式。

10.2 List

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

  1. ArrayList

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

    • 动态调整数组大小:当元素超过数组容量时,ArrayList 会自动扩展。
    • 访问元素速度快:由于底层是数组。通过索引访问元素的时间复杂度为 O(1) 。
    • 插入和删除操作相对较慢:插入或删除元素时,可能需要移动数组中的其他元素,时间复杂度为 O(n) 。

    常用方法

    • add(E e) :添加元素到列表末尾。
    • get(int index) :获取指定索引位置的元素。
    • set(int index,E element) :替换指定索引位置的元素。
    • remove(int index) :移除指定索引位置的元素。
    • size() :返回列表中元素的数量。

    示例代码

    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);
    }
    }
  2. LinkedList

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

    • 链表结点:每个元素都是一个结点,包含元素值以及指向前一个和后一个结点的指针。
    • 插入和删除速度快:插入和删除元素只需要调整指针,时间复杂度为 O(1) 。
    • 访问元素速度慢:由于需要从头开始遍历链表,通过所以访问元素的时间复杂度为 O(n) 。

    常用方法

    • add(E e): 添加元素到列表末尾。
    • get(int index): 获取指定索引位置的元素。
    • set(int index, E element): 替换指定索引位置的元素。
    • remove(int index): 移除指定索引位置的元素。
    • size(): 返回列表中元素的数量。

    示例代码

    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);
    }
    }

10.3 Set

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

  1. HashSet

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

    • 无序集合:元素没有特定的顺序。
    • 不允许重复元素:如果添加重复元素,HashSet 会忽略它。
    • 高效的插入、删除和查找操作:时间复杂度为 O(1)。

    常用方法

    • add(E e): 添加元素到集合中。
    • remove(Object o): 从集合中移除指定元素。
    • contains(Object o): 检查集合是否包含指定元素。
    • size(): 返回集合中元素的数量。

    示例代码

    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);
    }
    }
  2. TreeSet

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

    • 有序集合:元素按照自然顺序或自定义顺序排序。
    • 不允许重复元素:如果添加重复元素,TreeSet 会忽略它。
    • 较高的插入、删除和查找操作性能:时间复杂度为 O(log n) 。

    常用方法

    • add(E e): 添加元素到集合中。
    • remove(Object o): 从集合中移除指定元素。
    • contains(Object o): 检查集合是否包含指定元素。
    • size(): 返回集合中元素的数量。

    示例代码

    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);
    }
    }

10.4 Map

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

  1. HashMap

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

    • 无序集合:键值对没有特定的顺序。
    • 不允许重复键:如果添加重复键,HashMap 会覆盖旧值。
    • 高效的插入、删除和查找操作:时间复杂度为 O(1) 。

    常用方法

    • put(K key, V value): 添加键值对到映射中。
    • get(Object key): 获取指定键的值。
    • remove(Object key): 从映射中移除指定键值对。
    • containsKey(Object key): 检查映射是否包含指定键。
    • size(): 返回映射中键值对的数量。

    示例代码

    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);
    }
    }
  2. TreeMap

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

    • 有序集合:键值对按照键的自然顺序或自定义顺序排序。
    • 不允许重复键:如果添加重复键,TreeMap 会覆盖旧值。
    • 较高的插入、删除和查找操作性能:时间复杂度为 O(log n)。

    常用方法

    • put(K key, V value): 添加键值对到映射中。
    • get(Object key): 获取指定键的值。
    • remove(Object key): 从映射中移除指定键值对。
    • containsKey(Object key): 检查映射是否包含指定键。
    • size(): 返回映射中键值对的数量。

    示例代码

    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);
    }
    }

10.5 Queue

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

  1. LinkedList

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

    • 双向链表:可以作为队列(FIFO)和双端队列(Deque)使用。
    • 高效的插入和删除操作:时间复杂度为 O(1)。

    常用方法

    • add(E e): 将指定元素插入此队列的末尾。
    • offer(E e): 将指定元素插入此队列的末尾,如果成功则返回 true,如果队列已满则返回 false(一般不检查队列是否满)。
    • remove(): 检索并移除此队列的头部元素。
    • poll(): 检索并移除此队列的头部元素,如果此队列为空,则返回 null
    • element(): 检索但不移除此队列的头部元素。
    • peek(): 检索但不移除此队列的头部元素,如果此队列为空,则返回 null

    示例代码

    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);
    }
    }
  2. PriorityQueue

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

    • 无界优先级队列:元素按照优先级排序,不一定是先进先出(FIFO)。
    • 默认最小堆:自然顺序为最小堆,可自定义比较器实现最大堆。
    • 高效的插入和删除操作:时间复杂度为 O(log n)。

    常用方法与 LinkedList 类似

    • add(E e): 将指定元素插入此队列。
    • offer(E e): 将指定元素插入此队列。
    • remove(): 检索并移除此队列的头部元素。
    • poll(): 检索并移除此队列的头部元素,如果此队列为空,则返回 null
    • element(): 检索但不移除此队列的头部元素。
    • peek(): 检索但不移除此队列的头部元素,如果此队列为空,则返回 null

    示例代码

    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两个主要组件。

10.6 Stream

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

  1. 创建Stream

    ① 从 Collection 创建 Stream

    • List :使用 stream() 方法创建 Stream 。

      List<String> list = Arrays.asList("A", "B", "C");
      Stream<String> streamFromList = list.stream();
    • Set :使用 stream() 方法创建 Stream 。

      Set<String> set = new HashSet<>(Arrays.asList("X", "Y", "Z"));
      Stream<String> streamFromSet = set.stream();
    • Queue :使用 Stream() 方法创建 Stream 。

      Queue<String> queue = new LinkedList<>(Arrays.asList("1", "2", "3"));
      Stream<String> streamFromQueue = queue.stream();

    ② 从数组创建 Stream

    • 数组: 使用Arrays.stream()方法将数组转换为Stream

      String[] array = {"One", "Two", "Three"};
      Stream<String> streamFromArray = Arrays.stream(array);

    ③ 生成特定类型的 Stream

    • Stream.of(): 创建包含指定元素的Stream

      Stream<String> streamOfValues = Stream.of("A", "B", "C");

    ④ 创建字符流

    • Files.lines(): 从文件中创建字符流。

      Path path = Paths.get("file.txt");
      try (Stream<String> lines = Files.lines(path)) {
      lines.forEach(System.out::println);
      } catch (IOException e) {
      e.printStackTrace();
      }
  2. Stream 操作

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

    中间操作: 返回一个新的Stream,可以链式调用,常用的中间操作有:

    • filter(Predicate<T> predicate): 过滤流中的元素。
    • map(Function<T, R> mapper): 将元素映射成另一种形式。
    • sorted(): 对流进行排序。
    • distinct(): 去重操作。

    终端操作: 触发对Stream的处理并生成结果,常用的终端操作有:

    • forEach(Consumer<T> action): 遍历每个元素并执行操作。
    • collect(Collector<T, A, R> collector): 将Stream收集到集合中。
    • reduce(T identity, BinaryOperator<T> accumulator): 对流中的元素进行归约操作。
    • count(): 计算流中元素的数量。

    在 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;
    }
    }
    }

    ③ 总结

    • 默认排序Stream.sorted() 默认按照升序对元素进行排序。
    • 降序排序:使用 Comparator.reverseOrder()Comparatorreversed() 方法进行降序排序。
    • 自定义排序:可以结合使用 Comparator 的不同方法来实现更复杂的排序需求,例如同时按多个字段排序。
  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 流进行文件、控制台、网络等输入输出操作。

11.1 字节流

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

  • InputStream:所有字节输入流的基类。
  • OutputStream:所有字节输出流的基类。
  1. InputStream

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

    • FileInputStream:从文件中读取字节数据。
    • ByteArrayInputStream:从内存中的字节数组读取数据。
    • BufferedInputStream:提供缓冲功能,提高读取效率。

    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();
    }
    }
    }

    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();
    }
    }
    }
  2. OutputStream

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

    • FileOutputStream:向文件中写入字节数据。
    • ByteArrayOutputStream:将数据写入内存中的字节数组。
    • BufferedOutputStream:提供缓冲功能,提高写入效率。

    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();
    }
    }
    }

    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();
    }
    }
    }

11.2 字符流

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

  • Reader:所有字符输入流的基类。
  • Writer:所有字符输出流的基类。
  1. Reader

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

    • FileReader:从文件中读取字符。
    • BufferedReader:提供缓冲功能,并且提供按行读取的能力。

    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();
    }
    }
    }

    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. Writer

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

    • FileWriter:向文件中写入字符数据。
    • BufferedWriter:提供缓冲功能,并且提供按行写入的能力。

    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();
    }
    }
    }

    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();
    }
    }
    }

11.3 转换流(Character Streams)

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

  • InputStreamReader:将字节流转换为字符流(从输入字节流读取数据,并将其转换为字符)。
  • OutputStreamWriter:将字符流转换为字节流(将字符数据写出时转换为字节)。
  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();
    }
    }
    }
    • 工作原理FileInputStream以字节的形式读取文件内容,而InputStreamReader将字节数据转换为字符数据。
  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();
    }
    }
    }
    • 工作原理OutputStreamWriter接收字符数据,并根据UTF-8编码转换为字节,最后通过FileOutputStream将其写入文件中。
  3. 字符编码

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

    常见字符编码

    • UTF-8:可变长度字符编码,适合国际化。
    • ISO-8859-1:一种单字节编码,仅能表示西欧语言的字符。
    • GBK:中文字符编码。
  4. InputStreamReader 和 OutputStreamWriter 的常见用途

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

    • InputStreamReaderOutputStreamWriter 是Java中用于在字节流和字符流之间转换的工具,解决了字节流处理字符时的编码问题。
    • 通过这些转换流,程序可以方便地读取和写入不同字符编码的文本文件,确保文本内容在不同平台之间的兼容性。