常用类的概述和使用
1. java.lang 包中的常用类
1. Object 类
- 概述:
- 所有类的根类,所有类都继承自 Object。
- 常用方法:
- toString():返回对象的字符串表示。
- equals():比较两个对象是否相等。
- hashCode():返回对象的哈希值。
- 示例:
Object obj1 = new Object();
Object obj2 = new Object();
System.out.println(obj1.toString());
System.out.println(obj1.equals(obj2)); // false
2. String 类
- 概述:
- 表示不可变的字符串。
- 常用方法:
- length():返回字符串长度。
- substring():截取子字符串。
- equals():比较两个字符串是否相等。
- split():根据分隔符拆分字符串。
- 示例:
String str = "Hello, Java!";
System.out.println(str.length()); // 12
System.out.println(str.substring(0, 5)); // Hello
String[] parts = str.split(", ");
System.out.println(parts[1]); // Java!
3. Math 类
- 概述:
- 提供数学运算的方法,全部是静态方法。
- 常用方法:
- Math.pow():计算幂。
- Math.sqrt():计算平方根。
- Math.random():生成 0 到 1 之间的随机数。
- 示例:
System.out.println(Math.pow(2, 3)); // 8.0
System.out.println(Math.sqrt(16)); // 4.0
System.out.println(Math.random()); // 0.0 到 1.0
4. System 类
- 概述:
- 提供系统级别的操作。
- 常用方法:
- System.out.println():打印输出。
- System.currentTimeMillis():返回当前时间戳。
- System.gc():建议 JVM 进行垃圾回收。
- 示例:
System.out.println("当前时间戳:" + System.currentTimeMillis());
2. java.util 包中的常用类
1. ArrayList 类
- 概述:
- 动态数组,可自动调整大小。
- 常用方法:
- add():添加元素。
- get():获取元素。
- size():获取元素数量。
- 示例:
import java.util.ArrayList;
ArrayList<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
System.out.println(list.get(0)); // Java
System.out.println(list.size()); // 2
2. HashMap 类
- 概述:
- 基于键值对的哈希表。
- 常用方法:
- put():添加键值对。
- get():根据键获取值。
- containsKey():判断是否包含某个键。
- 示例:
HashMap<String, Integer> map = new HashMap<>();
map.put("Java", 1);
map.put("Python", 2);
System.out.println(map.get("Java")); // 1
System.out.println(map.containsKey("C++")); // false
3. Collections 类
- 概述:
- 对集合进行操作的工具类。
- 常用方法:
- sort():对列表排序。
- max():获取集合中的最大值。
- min():获取集合中的最小值。
- 示例:
import java.util.ArrayList;
import java.util.Collections;
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list, 5, 2, 8, 1);
Collections.sort(list);
System.out.println(list); // [1, 2, 5, 8]
3. java.io 包中的常用类
1. File 类
- 概述:
- 用于操作文件和目录。
- 常用方法:
- exists():检查文件是否存在。
- createNewFile():创建新文件。
- isDirectory():判断是否是目录。
- 示例:
import java.io.File;
File file = new File("test.txt");
if (!file.exists()) {
file.createNewFile();
}
System.out.println(file.isDirectory()); // false
2. BufferedReader/BufferedWriter 类
- 概述:
- 高效的文件读写类。
- 常用方法:
- BufferedWriter.write():写入文件。
- BufferedReader.readLine():按行读取文件内容。
- 示例:
import java.io.*;
BufferedWriter writer = new BufferedWriter(new FileWriter("test.txt"));
writer.write("Hello, World!");
writer.close();
BufferedReader reader = new BufferedReader(new FileReader("test.txt"));
System.out.println(reader.readLine()); // Hello, World!
reader.close();
3. InputStream/OutputStream 类
- 概述:
- 用于处理字节流。
- 常用方法:
- read():读取一个字节。
- write():写入一个字节。
- 示例:
import java.io.*;
FileOutputStream fos = new FileOutputStream("test.bin");
fos.write(65); // 写入字节 A
fos.close();
FileInputStream fis = new FileInputStream("test.bin");
System.out.println((char) fis.read()); // A
fis.close();
4. java.time 包中的常用类
1. LocalDate/LocalDateTime 类
- 概述:
- 用于日期和时间操作的现代类。
- 常用方法:
- now():获取当前日期/时间。
- plusDays():增加天数。
- getYear():获取年份。
- 示例:
import java.time.LocalDate;
import java.time.LocalDateTime;
LocalDate date = LocalDate.now();
System.out.println(date); // 当前日期
LocalDateTime dateTime = LocalDateTime.now();
System.out.println(dateTime); // 当前日期和时间
5. java.net 包中的常用类
1. URL 类
- 概述:
- 用于表示和解析 URL。
- 常用方法:
- getHost():获取主机名。
- getProtocol():获取协议名。
- 示例:
import java.net.URL;
URL url = new URL("https://www.google.com");
System.out.println(url.getHost()); // www.google.com
System.out.println(url.getProtocol()); // https
2. Socket 类
- 概述:
- 用于实现客户端与服务器的通信。
- 常用方法:
- getOutputStream():获取输出流。
- getInputStream():获取输入流。
- 示例:
import java.net.Socket;
Socket socket = new Socket("localhost", 8080);
socket.getOutputStream().write("Hello".getBytes());
socket.close();
6. 总结
| 包名 | 常用类 | 功能概述 |
|---|---|---|
| java.lang | String、Math | 字符串操作、数学运算 |
| java.util | ArrayList、HashMap | 集合框架 |
| java.io | File、BufferedReader | 文件和流操作 |
| java.time | LocalDate | 日期和时间 |
| java.net | URL、Socket | 网络通信 |
String 类的概述和使用
String 是 Java 中的一个核心类,位于 java.lang 包中,用于表示字符串。字符串在 Java 中是不可变的(immutable),每次对字符串的修改都会生成一个新的字符串对象,而不会改变原来的字符串内容。
String 类的特点
-
不可变性(Immutable):
- 一旦创建,字符串的内容不能改变。
- 例如:修改字符串时,会生成一个新的字符串对象,而原字符串保持不变。
String str = "Hello"; str = str + " World"; // 创建了一个新字符串 "Hello World","Hello" 依然存在 -
存储在字符串池(String Pool)中:
- 如果两个字符串的内容相同,且用字符串字面量(
"")方式创建,它们会共享同一个对象。 - 使用
new String()创建的字符串不会进入字符串池,而是会在堆内存中创建新对象。
- 如果两个字符串的内容相同,且用字符串字面量(
-
实现了
CharSequence接口:- 可以作为其他需要
CharSequence的类(如StringBuilder或正则表达式)的输入。
- 可以作为其他需要
String 的常见操作方法
1. 创建字符串
String str1 = "Hello"; // 字符串常量
String str2 = new String("World"); // 通过构造方法创建(不推荐)
2. 获取字符串长度
String str = "Hello World";
System.out.println(str.length()); // 输出:11
3. 字符串比较
- 使用
equals()比较内容是否相同(区分大小写)。 - 使用
equalsIgnoreCase()忽略大小写比较。 - 使用
==比较引用地址是否相同。
String str1 = "hello";
String str2 = "hello";
String str3 = new String("hello");
System.out.println(str1.equals(str2)); // true
System.out.println(str1 == str2); // true(共享常量池中的字符串)
System.out.println(str1 == str3); // false(不同对象)
System.out.println(str1.equals(str3)); // true
4. 查找字符或子字符串
charAt(int index):获取指定索引的字符。indexOf(String str):查找子字符串的索引(从前往后)。lastIndexOf(String str):查找子字符串的索引(从后往前)。
String str = "Hello World";
System.out.println(str.charAt(1)); // 输出:e
System.out.println(str.indexOf("o")); // 输出:4
System.out.println(str.lastIndexOf("o")); // 输出:7
5. 字符串截取
- 使用
substring()方法从指定索引截取字符串。
String str = "Hello World";
System.out.println(str.substring(6)); // 输出:World
System.out.println(str.substring(0, 5)); // 输出:Hello
6. 字符串替换
replace(char oldChar, char newChar):替换字符。replaceAll(String regex, String replacement):替换符合正则表达式的内容。
String str = "Hello World";
System.out.println(str.replace('o', 'a')); // 输出:Hella Warld
System.out.println(str.replaceAll("o", "123")); // 输出:Hell123 W123rld
7. 转换大小写
String str = "Hello World";
System.out.println(str.toUpperCase()); // 输出:HELLO WORLD
System.out.println(str.toLowerCase()); // 输出:hello world
8. 去除空格
trim():去掉字符串两端的空格。- 注意:
trim()不会去掉中间的空格。
String str = " Hello World ";
System.out.println(str.trim()); // 输出:"Hello World"
9. 分割字符串
- 使用
split()按照某种规则分割字符串,返回一个字符串数组。
String str = "apple,banana,orange";
String[] fruits = str.split(",");
for (String fruit : fruits) {
System.out.println(fruit);
}
// 输出:
// apple
// banana
// orange
10. 判断字符串是否为空
isEmpty():判断字符串是否为空串(长度为 0)。isBlank():判断字符串是否为空或仅包含空白字符(Java 11 以上)。
String str1 = "";
String str2 = " ";
System.out.println(str1.isEmpty()); // true
System.out.println(str2.isEmpty()); // false
System.out.println(str2.isBlank()); // true
11. 拼接字符串
- 使用
+或concat()方法。
String str1 = "Hello";
String str2 = "World";
System.out.println(str1 + " " + str2); // 输出:Hello World
System.out.println(str1.concat(" ").concat(str2)); // 输出:Hello World
String 常用的高级操作
1. 反转字符串
可以使用 StringBuilder 提供的 reverse() 方法。
String str = "Hello";
String reversed = new StringBuilder(str).reverse().toString();
System.out.println(reversed); // 输出:olleH
2. 字符串的不可变性示例
String str1 = "Java";
String str2 = str1; // 引用 str1 的地址
str1 = str1 + " Programming"; // str1 被指向新的对象
System.out.println(str1); // 输出:Java Programming
System.out.println(str2); // 输出:Java
3. 检查是否包含子字符串
String str = "Hello World";
System.out.println(str.contains("World")); // true
System.out.println(str.contains("Java")); // false
4. 检查字符串前缀和后缀
startsWith(String prefix):检查是否以某字符串为前缀。endsWith(String suffix):检查是否以某字符串为后缀。
String str = "Hello World";
System.out.println(str.startsWith("Hello")); // true
System.out.println(str.endsWith("World")); // true
String 类与字符串池的关系
- 字符串池(String Pool)是 Java 内存中的一部分,用于存储字符串字面量。
- 使用
new String()创建的字符串不会放入字符串池中,而是直接在堆内存中分配。
String str1 = "Java";
String str2 = "Java";
String str3 = new String("Java");
System.out.println(str1 == str2); // true(引用相同)
System.out.println(str1 == str3); // false(堆内存中的对象)
System.out.println(str1.equals(str3)); // true(内容相同)
常见的面试题
1. 为什么 String 是不可变的?
- 安全性:字符串不可变可以防止内容被意外修改(如在多线程中)。
- 缓存效率:字符串常量池可以复用相同内容的字符串对象。
- HashCode 缓存:不可变字符串的 HashCode 可以缓存起来,避免多次计算。
2. String 和 StringBuilder / StringBuffer 的区别
| 特性 | String | StringBuilder | StringBuffer |
|---|---|---|---|
| 是否可变 | 不可变 | 可变 | 可变 |
| 是否线程安全 | 否 | 否 | 是 |
| 性能 | 慢(频繁创建对象) | 快(单线程) | 慢(线程安全机制) |
可变字符串类和日期相关类
可变字符串类
Java 提供了两种用于表示可变字符串的类:StringBuilder 和 StringBuffer。
StringBuilder 类
StringBuilder是一个可变的字符序列类。- 线程不安全,但性能较高,适合单线程环境。
- 常用于需要频繁修改字符串内容的场景。
常见方法
StringBuilder sb = new StringBuilder("Hello");
sb.append(" World"); // 追加内容
System.out.println(sb); // "Hello World"
sb.insert(5, ","); // 插入内容
System.out.println(sb); // "Hello, World"
sb.delete(5, 6); // 删除内容
System.out.println(sb); // "Hello World"
sb.reverse(); // 反转字符串
System.out.println(sb); // "dlroW olleH"
StringBuffer 类
StringBuffer类与StringBuilder类类似,但它是线程安全的。- 适合多线程环境,但性能稍逊于
StringBuilder。
StringBuffer sb = new StringBuffer("Hello");
sb.append(" World");
System.out.println(sb); // "Hello World"
日期相关类
Java 提供了多个日期和时间相关的类。
java.util.Date 类
- 表示特定的时间点。
- 许多方法已被废弃,推荐使用
java.time包中的类。
使用示例
import java.util.Date;
public class Main {
public static void main(String[] args) {
Date date = new Date();
System.out.println(date); // 当前日期和时间
}
}
java.util.Calendar 类
- 提供了比
Date更强大的功能。 - 可以用来处理日期的加减。
使用示例
import java.util.Calendar;
public class Main {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
calendar.set(2024, Calendar.DECEMBER, 16);
System.out.println(calendar.getTime());
calendar.add(Calendar.DAY_OF_MONTH, 5); // 日期加 5 天
System.out.println(calendar.getTime());
}
}
java.time 包
- 从 Java 8 开始引入,提供了更强大和灵活的日期时间 API。
- 推荐使用此包中的类处理日期和时间。
LocalDate、LocalTime 和 LocalDateTime
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.LocalDateTime;
public class Main {
public static void main(String[] args) {
LocalDate date = LocalDate.now(); // 当前日期
System.out.println(date);
LocalTime time = LocalTime.now(); // 当前时间
System.out.println(time);
LocalDateTime dateTime = LocalDateTime.now(); // 当前日期时间
System.out.println(dateTime);
}
}
日期格式化
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class Main {
public static void main(String[] args) {
LocalDateTime dateTime = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formatted = dateTime.format(formatter);
System.out.println(formatted); // 例如:2024-12-16 14:30:00
}
}
集合类库
Java 提供了一整套集合类,用于存储和操作数据。这些类位于 java.util 包中,主要分为三大类:List、Set 和 Map。
1. List 接口
概述
List是一个有序的集合,允许存储重复的元素。- 常用实现类包括
ArrayList和LinkedList。
ArrayList 示例
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
list.add("Cherry");
for (String fruit : list) {
System.out.println(fruit);
}
}
}
LinkedList 示例
import java.util.LinkedList;
public class Main {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
list.add("Dog");
list.add("Cat");
list.add("Rabbit");
System.out.println(list.getFirst()); // Dog
System.out.println(list.getLast()); // Rabbit
}
}
2. Set 接口
概述
Set是一个无序的集合,且不允许存储重复的元素。- 常用实现类包括
HashSet、LinkedHashSet和TreeSet。
HashSet 示例
import java.util.HashSet;
public class Main {
public static void main(String[] args) {
HashSet<String> set = new HashSet<>();
set.add("One");
set.add("Two");
set.add("Three");
set.add("Two"); // 重复元素不会被添加
for (String element : set) {
System.out.println(element);
}
}
}
TreeSet 示例
import java.util.TreeSet;
public class Main {
public static void main(String[] args) {
TreeSet<Integer> set = new TreeSet<>();
set.add(10);
set.add(5);
set.add(20);
System.out.println(set); // [5, 10, 20] 自动排序
}
}
3. Map 接口
概述
Map是一个键值对的集合。- 常用实现类包括
HashMap、LinkedHashMap和TreeMap。
HashMap 示例
import java.util.HashMap;
public class Main {
public static void main(String[] args) {
HashMap<Integer, String> map = new HashMap<>();
map.put(1, "One");
map.put(2, "Two");
map.put(3, "Three");
System.out.println(map.get(2)); // Two
for (Integer key : map.keySet()) {
System.out.println(key + ": " + map.get(key));
}
}
}
TreeMap 示例
import java.util.TreeMap;
public class Main {
public static void main(String[] args) {
TreeMap<String, Integer> map = new TreeMap<>();
map.put("Apple", 3);
map.put("Banana", 2);
map.put("Cherry", 5);
System.out.println(map); // {Apple=3, Banana=2, Cherry=5} 按键排序
}
}
4. 集合工具类
Collections 类
- 提供了操作集合的静态方法,例如排序、查找、替换等。
示例
import java.util.ArrayList;
import java.util.Collections;
public class Main {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(3);
list.add(1);
list.add(2);
Collections.sort(list); // 排序
System.out.println(list); // [1, 2, 3]
Collections.reverse(list); // 反转
System.out.println(list); // [3, 2, 1]
Collections.shuffle(list); // 随机打乱
System.out.println(list);
}
}
Arrays 类
- 提供了操作数组的静态方法,例如排序和转换为列表。
示例
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] array = {3, 1, 2};
Arrays.sort(array); // 排序
System.out.println(Arrays.toString(array)); // [1, 2, 3]
String[] strArray = {"Apple", "Banana", "Cherry"};
System.out.println(Arrays.toString(strArray));
}
}
异常机制和 File 类
1. 异常机制
异常(Exception)是 Java 程序中发生的不正常事件。异常机制提供了一种优雅的方式来处理程序运行时可能发生的错误。
异常分类
- 受检异常(Checked Exception)
- 必须在代码中明确处理,例如:
IOException、SQLException。
- 必须在代码中明确处理,例如:
- 运行时异常(Runtime Exception)
- 在编译时不强制要求处理,例如:
NullPointerException、IndexOutOfBoundsException。
- 在编译时不强制要求处理,例如:
- 错误(Error)
- 严重错误,不建议捕获,例如:
OutOfMemoryError。
- 严重错误,不建议捕获,例如:
异常的关键字
try:尝试执行可能抛出异常的代码块。catch:捕获异常并处理。finally:无论是否发生异常都会执行的代码块。throw:显式抛出异常。throws:声明方法可能抛出的异常。
示例:基本异常处理
public class Main {
public static void main(String[] args) {
try {
int result = 10 / 0; // 可能抛出 ArithmeticException
} catch (ArithmeticException e) {
System.out.println("捕获到异常: " + e.getMessage());
} finally {
System.out.println("无论是否发生异常,都会执行。");
}
}
}
自定义异常
可以通过继承 Exception 或 RuntimeException 来创建自定义异常。
class MyException extends Exception {
public MyException(String message) {
super(message);
}
}
public class Main {
public static void main(String[] args) {
try {
throw new MyException("自定义异常");
} catch (MyException e) {
System.out.println("捕获到自定义异常: " + e.getMessage());
}
}
}
2. File 类
java.io.File 类是 Java 中用于操作文件和目录的类。
常见构造方法
File(String pathname):根据路径名创建 File 对象。File(String parent, String child):根据父路径和子路径创建 File 对象。File(File parent, String child):根据父 File 对象和子路径创建 File 对象。
常用方法
| 方法 | 描述 |
|---|---|
exists() | 判断文件或目录是否存在。 |
createNewFile() | 创建一个新文件。 |
mkdir() | 创建单级目录。 |
mkdirs() | 创建多级目录。 |
delete() | 删除文件或空目录。 |
getName() | 返回文件或目录名。 |
getAbsolutePath() | 返回绝对路径。 |
isFile() | 判断是否是文件。 |
isDirectory() | 判断是否是目录。 |
示例:创建和删除文件
import java.io.File;
import java.io.IOException;
public class Main {
public static void main(String[] args) {
File file = new File("example.txt");
try {
if (file.createNewFile()) {
System.out.println("文件创建成功: " + file.getName());
} else {
System.out.println("文件已存在。");
}
} catch (IOException e) {
System.out.println("发生错误: " + e.getMessage());
}
if (file.delete()) {
System.out.println("文件已删除: " + file.getName());
} else {
System.out.println("删除文件失败。");
}
}
}
示例:目录操作
import java.io.File;
public class Main {
public static void main(String[] args) {
File dir = new File("testDir");
if (dir.mkdir()) {
System.out.println("目录创建成功: " + dir.getName());
} else {
System.out.println("目录已存在。");
}
if (dir.delete()) {
System.out.println("目录已删除: " + dir.getName());
} else {
System.out.println("删除目录失败。");
}
}
}
示例:遍历目录内容
import java.io.File;
public class Main {
public static void main(String[] args) {
File dir = new File("."); // 当前目录
String[] files = dir.list();
if (files != null) {
for (String file : files) {
System.out.println(file);
}
}
}
}
6. IO 流
Java 的 IO(Input/Output)流用于处理数据的输入和输出操作。IO 流提供了对文件、网络、内存等数据源的读写功能。
1. IO 流的分类
Java IO 流分为以下两种维度的分类:
按数据类型划分
-
字节流(Byte Stream)
- 处理二进制数据。
- 父类:
InputStream(输入流)、OutputStream(输出流)。 - 常用子类:
FileInputStream、FileOutputStream。
-
字符流(Character Stream)
- 处理文本数据。
- 父类:
Reader(输入流)、Writer(输出流)。 - 常用子类:
FileReader、FileWriter。
按数据流向划分
- 输入流(Input Stream)
- 用于从数据源读取数据。
- 输出流(Output Stream)
- 用于向目标写入数据。
2. 常用的 IO 类
| 类名 | 作用 |
|---|---|
FileInputStream | 以字节为单位读取文件内容。 |
FileOutputStream | 以字节为单位向文件中写入数据。 |
BufferedInputStream | 提高输入流的效率,加入缓冲功能。 |
BufferedOutputStream | 提高输出流的效率,加入缓冲功能。 |
FileReader | 以字符为单位读取文件内容。 |
FileWriter | 以字符为单位向文件中写入数据。 |
BufferedReader | 提高字符输入流的效率,支持逐行读取功能。 |
BufferedWriter | 提高字符输出流的效率。 |
3. 字节流示例
文件复制(字节流)
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class ByteStreamExample {
public static void main(String[] args) {
try (FileInputStream fis = new FileInputStream("source.txt");
FileOutputStream fos = new FileOutputStream("destination.txt")) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}
System.out.println("文件复制成功!");
} catch (IOException e) {
e.printStackTrace();
}
}
}
4. 字符流示例
文件读取(字符流)
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CharStreamExample {
public static void main(String[] args) {
try (FileReader reader = new FileReader("source.txt");
FileWriter writer = new FileWriter("destination.txt")) {
char[] buffer = new char[1024];
int charsRead;
while ((charsRead = reader.read(buffer)) != -1) {
writer.write(buffer, 0, charsRead);
}
System.out.println("文件复制成功!(字符流)");
} catch (IOException e) {
e.printStackTrace();
}
}
}
5. 缓冲流示例
使用 BufferedReader 和 BufferedWriter
import java.io.*;
public class BufferedStreamExample {
public static void main(String[] args) {
try (BufferedReader reader = new BufferedReader(new FileReader("source.txt"));
BufferedWriter writer = new BufferedWriter(new FileWriter("destination.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
writer.write(line);
writer.newLine();
}
System.out.println("文件复制成功!(缓冲流)");
} catch (IOException e) {
e.printStackTrace();
}
}
}
6. 常见注意事项
-
关闭流资源
- 使用
try-with-resources语法可以自动关闭流。
- 使用
-
选择合适的流
- 二进制数据(如图片、音频):使用字节流。
- 文本数据:使用字符流。
-
性能优化
- 大文件读写时建议使用缓冲流(
BufferedInputStream、BufferedOutputStream)。
- 大文件读写时建议使用缓冲流(
多线程
多线程是指在一个程序中同时执行多个线程,用于提高程序的并发性和运行效率。Java 提供了多种方式来创建和管理线程。
1. 线程的基本概念
-
线程
- 线程是程序执行的最小单位,每个线程都有独立的运行路径。
-
进程
- 进程是程序运行的基本单位,一个进程可以包含多个线程。
-
多线程的优点
- 提高程序运行效率。
- 更好地利用多核处理器。
- 提升用户体验(例如,界面不卡顿)。
2. 创建线程的三种方式
方法一:继承 Thread 类
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("线程运行中:" + Thread.currentThread().getName());
}
public static void main(String[] args) {
MyThread thread1 = new MyThread();
MyThread thread2 = new MyThread();
thread1.start();
thread2.start();
}
}
说明:
- 重写
Thread类的run()方法,定义线程任务。 - 使用
start()方法启动线程。
方法二:实现 Runnable 接口
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("线程运行中:" + Thread.currentThread().getName());
}
public static void main(String[] args) {
Thread thread1 = new Thread(new MyRunnable());
Thread thread2 = new Thread(new MyRunnable());
thread1.start();
thread2.start();
}
}
说明:
- 实现
Runnable接口并重写run()方法。 - 通过
Thread构造器传入Runnable对象。
方法三:使用 Callable 和 Future
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
return "线程返回结果:" + Thread.currentThread().getName();
}
public static void main(String[] args) {
Callable<String> callable = new MyCallable();
FutureTask<String> futureTask = new FutureTask<>(callable);
Thread thread = new Thread(futureTask);
thread.start();
try {
System.out.println(futureTask.get()); // 获取线程返回结果
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
说明:
Callable可以返回值,并且支持抛出异常。- 使用
FutureTask包装Callable,并通过get()方法获取结果。
3. 常用线程操作
-
线程睡眠(
sleep)Thread.sleep(1000); // 当前线程休眠 1 秒 -
线程优先级(
setPriority)thread.setPriority(Thread.MAX_PRIORITY); // 设置线程优先级为最高 -
线程合并(
join)thread.join(); // 等待线程执行完毕 -
线程中断(
interrupt)thread.interrupt(); // 中断线程
4. 线程同步
多线程可能会引发数据竞争问题,线程同步用于确保线程安全。
使用 synchronized 关键字
public class SynchronizedExample {
private int count = 0;
public synchronized void increment() {
count++;
}
public static void main(String[] args) {
SynchronizedExample example = new SynchronizedExample();
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
example.increment();
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
example.increment();
}
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("最终计数值:" + example.count);
}
}
说明:
synchronized保证同一时间只有一个线程可以执行关键代码块。
使用 ReentrantLock
import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
private int count = 0;
private final ReentrantLock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
LockExample example = new LockExample();
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
example.increment();
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
example.increment();
}
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("最终计数值:" + example.count);
}
}
说明:
ReentrantLock提供了比synchronized更灵活的锁机制。
5. 线程池
线程池是 Java 提供的一种管理线程的机制,可以减少线程创建和销毁的开销。
示例:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
final int task = i;
executor.submit(() -> {
System.out.println("正在执行任务:" + task + " - 线程:" + Thread.currentThread().getName());
});
}
executor.shutdown();
}
}
说明:
- 使用
Executors创建线程池,提交任务后线程池自动管理线程。
网络编程
Java 网络编程主要通过 Socket 和 ServerSocket 类来实现客户端和服务器之间的通信。下面是一些基本的示例。
1. 网络编程基本概念
- 客户端-服务器模型:客户端向服务器发送请求,服务器接受并返回响应。
- IP 地址和端口号:客户端和服务器通过 IP 地址和端口号进行连接。
- Socket:用于建立客户端与服务器之间的连接。包括
ServerSocket(服务器端)和Socket(客户端)。
2. 创建简单的客户端-服务器程序
2.1 服务器端代码
import java.io.*;
import java.net.*;
public class Server {
public static void main(String[] args) {
try {
// 创建 ServerSocket,绑定端口
ServerSocket serverSocket = new ServerSocket(12345);
System.out.println("服务器启动,等待客户端连接...");
// 等待客户端连接
Socket clientSocket = serverSocket.accept();
System.out.println("客户端连接成功!");
// 获取输入流
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
String message = in.readLine();
System.out.println("客户端消息: " + message);
// 向客户端发送消息
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
out.println("你好,客户端!");
// 关闭连接
clientSocket.close();
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.2 客户端代码
import java.io.*;
import java.net.*;
public class Client {
public static void main(String[] args) {
try {
// 创建 Socket 并连接服务器
Socket socket = new Socket("localhost", 12345);
// 向服务器发送消息
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
out.println("你好,服务器!");
// 获取服务器响应
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String response = in.readLine();
System.out.println("服务器回应: " + response);
// 关闭连接
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
3. 关键类介绍
-
Socket:用于客户端与服务器之间的通信。
Socket(String host, int port):连接到指定主机和端口。getInputStream()和getOutputStream():用于获取输入输出流。
-
ServerSocket:服务器端的监听器。
ServerSocket(int port):指定端口。accept():等待并接受客户端连接。
-
InputStream/OutputStream:用于读取和写入字节数据。
-
BufferedReader/PrintWriter:用于处理文本流的读写。
4. 网络编程注意事项
- 异常处理:网络编程中,网络连接和数据传输容易发生异常,需要注意捕获和处理异常。
- 线程处理:服务器通常需要为每个客户端创建一个线程来处理通信。可以使用线程池来优化性能。
5. 多线程服务器示例
import java.io.*;
import java.net.*;
public class MultiThreadedServer {
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(12345);
System.out.println("服务器启动,等待客户端连接...");
while (true) {
Socket clientSocket = serverSocket.accept();
System.out.println("客户端连接成功!");
// 为每个客户端创建一个线程进行处理
new ClientHandler(clientSocket).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
class ClientHandler extends Thread {
private Socket clientSocket;
public ClientHandler(Socket socket) {
this.clientSocket = socket;
}
@Override
public void run() {
try {
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
String message = in.readLine();
System.out.println("客户端消息: " + message);
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
out.println("你好,客户端!");
clientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
反射机制
Java 的反射机制是一种 运行时动态获取类的信息(如类的属性、方法、构造器)并对其进行操作的机制。通过反射,可以在程序运行时加载类、创建对象、调用方法和访问字段,而不需要在编译期确定它们。
反射是由 java.lang.reflect 包中的类实现的,比如 Class、Field、Method、Constructor 等。
一、反射的常见应用场景
- 运行时加载类(动态加载第三方库)。
- 运行时访问和修改私有字段、调用私有方法。
- 编写通用框架(如 Spring、MyBatis)。
- 工具类(如 JSON 序列化/反序列化)。
- 动态代理。
二、反射的基本用法
1. 获取 Class 对象
通过反射机制操作类的前提是获取该类的 Class 对象。获取方式如下:
// 方式一:通过类名
Class<?> clazz1 = MyClass.class;
// 方式二:通过对象实例
MyClass myObject = new MyClass();
Class<?> clazz2 = myObject.getClass();
// 方式三:通过类的完全限定名(全路径)
Class<?> clazz3 = Class.forName("com.example.MyClass");
2. 获取类的构造器并创建对象
import java.lang.reflect.Constructor;
public class ReflectionExample {
public static void main(String[] args) throws Exception {
// 获取 Class 对象
Class<?> clazz = Class.forName("java.util.ArrayList");
// 获取无参构造器
Constructor<?> constructor = clazz.getConstructor();
// 使用构造器创建对象
Object obj = constructor.newInstance();
System.out.println("创建的对象是: " + obj.getClass().getName());
}
}
3. 获取类的字段并访问/修改
import java.lang.reflect.Field;
class Person {
private String name = "John";
}
public class ReflectionFieldExample {
public static void main(String[] args) throws Exception {
Person person = new Person();
Class<?> clazz = person.getClass();
// 获取私有字段
Field field = clazz.getDeclaredField("name");
// 取消访问修饰符检查
field.setAccessible(true);
// 获取字段值
System.out.println("修改前的字段值: " + field.get(person));
// 修改字段值
field.set(person, "Alice");
System.out.println("修改后的字段值: " + field.get(person));
}
}
4. 获取类的方法并调用
import java.lang.reflect.Method;
class Calculator {
public int add(int a, int b) {
return a + b;
}
}
public class ReflectionMethodExample {
public static void main(String[] args) throws Exception {
Calculator calculator = new Calculator();
Class<?> clazz = calculator.getClass();
// 获取方法
Method method = clazz.getMethod("add", int.class, int.class);
// 调用方法
Object result = method.invoke(calculator, 5, 3);
System.out.println("调用结果: " + result);
}
}
5. 获取类的全部信息
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectionDetails {
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("java.util.ArrayList");
// 打印类名
System.out.println("类名: " + clazz.getName());
// 打印所有字段
System.out.println("字段:");
for (Field field : clazz.getDeclaredFields()) {
System.out.println(field.getName());
}
// 打印所有构造器
System.out.println("构造器:");
for (Constructor<?> constructor : clazz.getConstructors()) {
System.out.println(constructor);
}
// 打印所有方法
System.out.println("方法:");
for (Method method : clazz.getMethods()) {
System.out.println(method.getName());
}
}
}
三、反射的优缺点
优点
- 灵活性:运行时动态操作,适用于编写通用代码或框架。
- 无侵入性:可以访问和操作类的私有成员。
缺点
- 性能开销:反射比直接调用性能稍差,需尽量避免频繁使用。
- 安全风险:通过反射可以访问私有字段和方法,可能导致安全问题。
- 复杂性:代码的可读性和可维护性降低。
四、总结
Java 的反射机制是强大的工具,在框架开发和工具类中经常使用。但需要谨慎对待,避免滥用反射带来的性能和安全问题。