JAVA 核心类库

265 阅读20分钟

常用类的概述和使用

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.langString、Math字符串操作、数学运算
java.utilArrayList、HashMap集合框架
java.ioFile、BufferedReader文件和流操作
java.timeLocalDate日期和时间
java.netURL、Socket网络通信

String 类的概述和使用

String 是 Java 中的一个核心类,位于 java.lang 包中,用于表示字符串。字符串在 Java 中是不可变的(immutable),每次对字符串的修改都会生成一个新的字符串对象,而不会改变原来的字符串内容。


String 类的特点

  1. 不可变性(Immutable)

    • 一旦创建,字符串的内容不能改变。
    • 例如:修改字符串时,会生成一个新的字符串对象,而原字符串保持不变。
    String str = "Hello";
    str = str + " World"; // 创建了一个新字符串 "Hello World","Hello" 依然存在
    
  2. 存储在字符串池(String Pool)中

    • 如果两个字符串的内容相同,且用字符串字面量("")方式创建,它们会共享同一个对象。
    • 使用 new String() 创建的字符串不会进入字符串池,而是会在堆内存中创建新对象。
  3. 实现了 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 的区别

特性StringStringBuilderStringBuffer
是否可变不可变可变可变
是否线程安全
性能慢(频繁创建对象)快(单线程)慢(线程安全机制)

可变字符串类和日期相关类

可变字符串类

Java 提供了两种用于表示可变字符串的类:StringBuilderStringBuffer

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 包中,主要分为三大类:ListSetMap


1. List 接口

概述

  • List 是一个有序的集合,允许存储重复的元素。
  • 常用实现类包括 ArrayListLinkedList

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 是一个无序的集合,且不允许存储重复的元素。
  • 常用实现类包括 HashSetLinkedHashSetTreeSet

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 是一个键值对的集合。
  • 常用实现类包括 HashMapLinkedHashMapTreeMap

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 程序中发生的不正常事件。异常机制提供了一种优雅的方式来处理程序运行时可能发生的错误。

异常分类

  1. 受检异常(Checked Exception)
    • 必须在代码中明确处理,例如:IOExceptionSQLException
  2. 运行时异常(Runtime Exception)
    • 在编译时不强制要求处理,例如:NullPointerExceptionIndexOutOfBoundsException
  3. 错误(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("无论是否发生异常,都会执行。");
        }
    }
}

自定义异常

可以通过继承 ExceptionRuntimeException 来创建自定义异常。

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 流分为以下两种维度的分类:

按数据类型划分

  1. 字节流(Byte Stream)

    • 处理二进制数据。
    • 父类:InputStream(输入流)、OutputStream(输出流)。
    • 常用子类:FileInputStreamFileOutputStream
  2. 字符流(Character Stream)

    • 处理文本数据。
    • 父类:Reader(输入流)、Writer(输出流)。
    • 常用子类:FileReaderFileWriter

按数据流向划分

  1. 输入流(Input Stream)
    • 用于从数据源读取数据。
  2. 输出流(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. 缓冲流示例

使用 BufferedReaderBufferedWriter

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. 常见注意事项

  1. 关闭流资源

    • 使用 try-with-resources 语法可以自动关闭流。
  2. 选择合适的流

    • 二进制数据(如图片、音频):使用字节流。
    • 文本数据:使用字符流。
  3. 性能优化

    • 大文件读写时建议使用缓冲流(BufferedInputStreamBufferedOutputStream)。

多线程

多线程是指在一个程序中同时执行多个线程,用于提高程序的并发性和运行效率。Java 提供了多种方式来创建和管理线程。


1. 线程的基本概念

  1. 线程

    • 线程是程序执行的最小单位,每个线程都有独立的运行路径。
  2. 进程

    • 进程是程序运行的基本单位,一个进程可以包含多个线程。
  3. 多线程的优点

    • 提高程序运行效率。
    • 更好地利用多核处理器。
    • 提升用户体验(例如,界面不卡顿)。

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 对象。

方法三:使用 CallableFuture

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. 常用线程操作

  1. 线程睡眠(sleep

    Thread.sleep(1000); // 当前线程休眠 1 秒
    
  2. 线程优先级(setPriority

    thread.setPriority(Thread.MAX_PRIORITY); // 设置线程优先级为最高
    
  3. 线程合并(join

    thread.join(); // 等待线程执行完毕
    
  4. 线程中断(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 网络编程主要通过 SocketServerSocket 类来实现客户端和服务器之间的通信。下面是一些基本的示例。

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 包中的类实现的,比如 ClassFieldMethodConstructor 等。


一、反射的常见应用场景

  1. 运行时加载类(动态加载第三方库)
  2. 运行时访问和修改私有字段、调用私有方法
  3. 编写通用框架(如 Spring、MyBatis)
  4. 工具类(如 JSON 序列化/反序列化)
  5. 动态代理

二、反射的基本用法

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

三、反射的优缺点

优点

  1. 灵活性:运行时动态操作,适用于编写通用代码或框架。
  2. 无侵入性:可以访问和操作类的私有成员。

缺点

  1. 性能开销:反射比直接调用性能稍差,需尽量避免频繁使用。
  2. 安全风险:通过反射可以访问私有字段和方法,可能导致安全问题。
  3. 复杂性:代码的可读性和可维护性降低。

四、总结

Java 的反射机制是强大的工具,在框架开发和工具类中经常使用。但需要谨慎对待,避免滥用反射带来的性能和安全问题。