先序: 学习编程语言要先学个轮廓,刚开始只用学核心的部分,一些细节、不常用的内容先放着,现用现查即可;把常用的东西弄熟练了在慢慢补充。
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!"); }}
public class HelloWorld
:定义一个名为 HelloWorld
的公共类。public static void main(String[] args)
:主方法,是程序的入口点。System.out.println("Hello, World!");
:打印一行文本到控制台。 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. 遍历数组的方法
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
)
对于集合类(如 List
、Set
等),可以使用 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; }
parameters: 这是 Lambda 表达式的输入参数,可以省略类型声明。->: 这是 Lambda 运算符,将参数与表达式或语句块分隔开。expression: 这是一个单一的表达式,Lambda 表达式将其返回。statements: 如果 Lambda 表达式包含多个语句,则需要用 {}
包括起来。 示例
无参数
Runnable r = () -> System.out.println("Hello, World!");r.run(); // 输出: Hello, World!
这个 Lambda 表达式实现了 Runnable
接口,它没有参数,并且打印出 “Hello, World!”。
一个参数
Consumer<String> printer = s -> System.out.println(s);printer.accept("Hello, Lambda!"); // 输出: Hello, Lambda!
这个 Lambda 表达式实现了 Consumer
接口,它接收一个参数并打印它。
多个参数
BinaryOperator<Integer> add = (a, b) -> a + b;System.out.println(add.apply(5, 3)); // 输出: 8
这个 Lambda 表达式实现了 BinaryOperator
接口,它接受两个参数并返回它们的和。
多个语句
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 }}
常用方法:
nextLine()
: 读取一整行输入,返回一个字符串。nextLine().charAt(0)
: 读取一行字符串中的第一个,一般用这个读取char类型变量。nextInt()
: 读取一个整数。nextDouble()
: 读取一个双精度浮点数。nextBoolean()
: 读取一个布尔值。hasNext()
: 检查是否有下一个输入。hasNextLine()
: 检查是否有下一行输入。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 }}
常用方法:
readLine()
: 读取一整行输入,返回一个字符串。close()
: 关闭 BufferedReader。 对比 Scanner 和 BufferedReader
相同点:
都可以用于从终端、文件等源读取输入。都提供了读取整行输入的方法:nextLine()
和 readLine()
。 不同点:
功能:
Scanner
提供了更多解析输入数据的方法,如 nextInt()
, nextDouble()
等,可以直接读取基本类型数据。BufferedReader
主要用于读取字符串,需要手动解析基本类型数据。 性能:
BufferedReader
通常性能更高,因为它使用缓冲机制,适合读取大量文本数据。Scanner
在方便性上有优势,但性能可能稍逊色。 使用场景:
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");
输出:HelloWorld
3. System.out.printf()
用法: System.out.printf(formatString, arguments);
示例:// 使用格式化字符串输出内容,类似于 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 yearsHeight: 1.75 meters
8.4 常用格式说明符
整数:%d
: 十进制整数%x
: 十六进制整数 浮点数: %f
: 浮点数%.2f
: 浮点数,保留两位小数 字符串: %s
: 字符串 字符: %c
: 单个字符 百分比: %%
: 输出百分号 %
总结
System.out.print()
: 用于输出内容,不换行。System.out.println()
: 用于输出内容并换行。System.out.printf()
: 用于格式化输出内容。 try-with-resources
语句(通常被称为try
语句)是Java 7引入的一个特性,它简化了资源管理,特别是用于自动关闭实现了AutoCloseable
接口的资源,如InputStream
、OutputStream
、Connection
等。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
: 实现了AutoCloseable
或java.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()); } }}
在这个示例中,BufferedReader
和GZIPInputStream
会在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. 创建与初始化
String()
:创建一个空字符串。String(String original)
:创建一个新的字符串,内容为指定的 String
。 2. 字符串操作
concat(String str)
:连接指定字符串到当前字符串末尾。substring(int beginIndex)
:从指定索引开始,返回子字符串。substring(int beginIndex, int endIndex)
:返回从 beginIndex
到 endIndex
之间的子字符串。 3. 查找与比较
indexOf(String str)
:返回指定子字符串第一次出现的索引。lastIndexOf(String str)
:返回指定子字符串最后一次出现的索引。contains(CharSequence sequence)
:判断当前字符串是否包含指定字符序列。equals(Object anObject)
:比较两个字符串的内容是否相等。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)
:判断字符串是否匹配给定的正则表达式。 List
List
是一个有序的集合,可以包含重复元素。常用实现类有 ArrayList
和 LinkedList
。
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); }}
LinkedList
LinkedList
是一个基于双向链表的数据结构,提供了高效的插入和删除操作。它的主要特点是:
常用方法:
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); }}
Set
Set
是一个不包含重复元素的集合。常用实现类有 HashSet
和 TreeSet
。
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); }}
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); }}
Map
Map
是一个键值对的集合,每个键最多只能关联一个值。常用实现类有 HashMap
和 TreeMap
。
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); }}
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); }}
Queue
Queue
是一个先进先出的集合,常用实现类有 LinkedList
和 PriorityQueue
。
LinkedList
LinkedList
实现了 Queue
接口,提供了基于链表的队列实现。它的主要特点是:
常用方法:
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); }}
PriorityQueue
PriorityQueue
是一个基于优先级堆(最小堆或最大堆)的队列,元素按自然顺序或自定义顺序排序。它的主要特点是:
常用方法与 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集合框架的Collection
和Stream
两个主要组件。
Stream
Stream
是Java 8引入的一个新特性,它提供了一种功能性编程风格来处理集合数据。Stream
并不存储数据,而是对数据进行操作的工具。
1. 创建 Stream
1. 从 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();
2. 从数组创建 Stream
数组: 使用Arrays.stream()
方法将数组转换为Stream
。
String[] array = {"One", "Two", "Three"};Stream<String> streamFromArray = Arrays.stream(array);
3. 生成特定类型的 Stream
Stream.of()
: 创建包含指定元素的Stream
。
Stream<String> streamOfValues = Stream.of("A", "B", "C");
4. 创建字符流
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 }}
降序排序
要实现降序排序,你可以使用 Comparator
的 reversed()
方法,或者使用 Comparator
的 reverseOrder()
方法。以下是如何进行降序排序的示例:
使用 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()
或 Comparator
的 reversed()
方法进行降序排序。自定义排序:可以结合使用 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流可以分为两大类:
字节流(Byte Streams):处理原始字节的数据流,如图片、音频、视频文件。字符流(Character Streams):专门处理字符数据,如文本文件。Java的输入输出流主要位于java.io
包中。下面详细讲解如何使用Java的I/O流进行文件、控制台、网络等输入输出操作。
1. 字节流
字节流是以字节为单位进行数据的输入和输出。Java通过两个顶层抽象类来处理字节流:
InputStream
:所有字节输入流的基类。OutputStream
:所有字节输出流的基类。 1.1 InputStream
InputStream
是读取字节数据的基础类,常用子类包括:
FileInputStream
:从文件中读取字节数据。ByteArrayInputStream
:从内存中的字节数组读取数据。BufferedInputStream
:提供缓冲功能,提高读取效率。 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
用于写入字节数据,常用子类包括:
FileOutputStream
:向文件中写入字节数据。ByteArrayOutputStream
:将数据写入内存中的字节数组。BufferedOutputStream
:提供缓冲功能,提高写入效率。 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通过两个顶层抽象类来处理字符流:
Reader
:所有字符输入流的基类。Writer
:所有字符输出流的基类。 2.1 Reader
Reader
类用于读取字符数据,常用子类包括:
FileReader
:从文件中读取字符。BufferedReader
:提供缓冲功能,并且提供按行读取的能力。 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
类用于写入字符数据,常用子类包括:
FileWriter
:向文件中写入字符数据。BufferedWriter
:提供缓冲功能,并且提供按行写入的能力。 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提供了两个主要的转换流类:
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
可以将字符流以特定编码方式写入文件,确保跨平台的字符一致性。网络通信中的字符转换:在网络应用中,传输的数据往往是字节流,但客户端和服务器端可能使用不同的字符编码,因此需要使用转换流来处理。