Java API中文文档与核心开发指南
简介:Java API文档中文版是Java开发者的必备参考资料,全面介绍了Java SE平台的核心类库、接口和方法。涵盖基础语法、集合框架、网络编程、多线程、I/O流、反射、异常处理、JDBC、GUI开发等关键内容,并提供详细的API说明和使用示例。该文档适用于初学者入门学习和开发者日常开发参考,帮助提升Java程序的开发效率与质量。 
1. Java基础类库与核心语法
Java语言作为一门广泛应用于企业级开发的编程语言,其基础类库与核心语法构成了开发者技能体系的基石。本章将从开发环境搭建入手,系统讲解Java的基本数据类型、变量声明、运算符使用、流程控制语句(如if-else、for、while等)的编写逻辑,并通过示例代码展示其执行流程。我们将通过代码片段详细说明变量的声明与初始化、基本类型之间的自动与强制类型转换,以及表达式的运算优先级。
此外,本章还将介绍Java的命名规范、包(package)机制的使用,以及如何利用JDK自带的Java API文档快速查找类与方法信息。通过这些内容的学习,读者将建立起对Java程序结构的完整认知,为后续面向对象编程打下坚实基础。
2. Java面向对象核心特性
Java作为一门典型的面向对象编程(OOP)语言,其核心特性围绕类与对象展开。本章将深入剖析面向对象的核心概念,包括类的定义与对象的创建、封装、继承、多态、静态成员与访问控制等,帮助开发者构建清晰的面向对象思维模型。通过本章内容,开发者将掌握如何在实际项目中合理运用这些特性,提升代码的可维护性、扩展性与复用性。
2.1 类与对象的定义与使用
类(Class)是Java中面向对象编程的基础,它用于描述具有相同属性和行为的对象的模板。对象(Object)则是类的实例,代表着现实世界中具体的存在。
2.1.1 类的组成与结构
Java中的类主要由以下几部分构成:
- 字段(Field) :描述类的属性。
- 方法(Method) :描述类的行为。
- 构造方法(Constructor) :用于创建对象并初始化对象状态。
- 代码块(Code Block) :用于在类加载或对象创建时执行特定逻辑。
- 内部类(Inner Class) :定义在另一个类中的类。
public class Person {
// 字段
private String name;
private int age;
// 构造方法
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 方法
public void sayHello() {
System.out.println("Hello, my name is " + name + ", I am " + age + " years old.");
}
// Getter 和 Setter
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
代码解析
private String name;:表示一个私有字段,只能在本类中访问。public Person(String name, int age):构造方法用于初始化对象的状态。this.name = name;:this关键字用于区分同名的参数与字段。sayHello()方法:输出对象的信息。
结构图(Mermaid)
classDiagram
class Person {
-String name
-int age
+Person(String, int)
+void sayHello()
+String getName()
+void setName(String)
+int getAge()
+void setAge(int)
}
2.1.2 对象的创建与初始化
对象的创建通过 new 关键字完成,构造方法会自动调用以完成初始化。
public class Main {
public static void main(String[] args) {
Person person = new Person("Tom", 25); // 创建对象并初始化
person.sayHello(); // 调用方法
}
}
执行逻辑分析
new Person("Tom", 25):调用构造方法,分配内存空间并初始化字段。person.sayHello():调用对象的方法,输出信息。
对象生命周期示意图(Mermaid)
sequenceDiagram
participant JVM
participant Heap
participant Stack
JVM->>Heap: 分配内存空间
Heap-->>JVM: 返回内存地址
JVM->>Stack: 将引用存入栈中
Stack->>Heap: 调用对象方法
2.1.3 构造方法与this关键字
构造方法用于初始化对象,一个类可以有多个构造方法(重载), this 关键字用于访问当前对象的成员。
public class Person {
private String name;
private int age;
// 无参构造
public Person() {
this("Unknown", 0); // 调用另一个构造方法
}
// 带参构造
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
说明
this():用于调用同一类中的其他构造方法,必须放在第一行。- 构造方法重载允许以不同的方式创建对象,提升灵活性。
2.2 封装、继承与多态
封装、继承和多态是面向对象编程的三大支柱。它们分别对应着数据隐藏、代码复用和行为多态。
2.2.1 封装的概念与实现方式
封装是指将对象的内部细节隐藏起来,仅暴露必要的接口供外部访问。
public class BankAccount {
private double balance;
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
public void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
}
}
public double getBalance() {
return balance;
}
}
说明
private double balance;:防止外部直接修改余额。- 提供
deposit()和withdraw()方法来控制操作。 - 使用封装可以提高代码的安全性和可控性。
2.2.2 继承关系的建立与super关键字
继承允许子类继承父类的字段和方法, super 关键字用于调用父类的构造方法或方法。
class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
public void makeSound() {
System.out.println("Animal sound");
}
}
class Dog extends Animal {
public Dog(String name) {
super(name); // 调用父类构造方法
}
@Override
public void makeSound() {
System.out.println("Woof!");
}
}
说明
extends Animal:Dog类继承Animal类。super(name):调用父类构造方法。@Override:表示重写父类方法。
2.2.3 方法重写与多态的应用场景
多态是指同一接口的不同实现方式,通常通过继承和方法重写实现。
public class Main {
public static void main(String[] args) {
Animal a1 = new Dog("Buddy");
Animal a2 = new Cat("Whiskers");
a1.makeSound(); // 输出 Woof!
a2.makeSound(); // 输出 Meow
}
}
多态实现流程图(Mermaid)
graph TD
A[Animal a = new Dog()] --> B{运行时类型}
B -->|Dog| C[调用Dog.makeSound()]
B -->|Cat| D[调用Cat.makeSound()]
说明
- 多态允许将子类对象赋值给父类引用。
- 实际调用的方法取决于运行时对象类型。
- 多态提高了代码的灵活性和扩展性。
2.3 静态成员与访问控制
Java中的静态成员属于类本身而非对象,访问控制符决定了类成员的可见性。
2.3.1 static关键字的使用
static 关键字用于定义类级别的成员,包括静态字段、静态方法和静态代码块。
public class Counter {
private static int count = 0;
public Counter() {
count++;
}
public static int getCount() {
return count;
}
}
说明
static int count:所有对象共享同一个count变量。Counter.getCount():通过类名直接访问静态方法。- 静态方法不能访问非静态成员。
2.3.2 访问权限修饰符(public、private、protected)
Java提供了四种访问权限控制符:
| 修饰符 | 同一个类 | 同一个包 | 子类 | 不同包 |
|---|---|---|---|---|
| private | ✅ | ❌ | ❌ | ❌ |
| 默认(无) | ✅ | ✅ | ❌ | ❌ |
| protected | ✅ | ✅ | ✅ | ❌ |
| public | ✅ | ✅ | ✅ | ✅ |
说明
private:仅限本类访问。protected:允许子类访问。public:无限制访问。- 合理使用访问控制符可以提高代码的安全性。
2.3.3 默认权限与类成员的可见性
如果没有显式指定访问权限,Java使用默认权限(包访问权限)。
class DefaultClass {
void defaultMethod() {
System.out.println("Default method");
}
}
说明
defaultMethod()只能在同一个包中访问。- 默认权限适用于不需要暴露给外部的成员。
2.4 面向对象设计的实践应用
本节通过两个实际案例,演示如何将面向对象的核心特性应用于真实项目开发中。
2.4.1 设计一个简单的图书管理系统类模型
类图(Mermaid)
classDiagram
class Book {
-String title
-String author
-String isbn
+Book(String, String, String)
+String getTitle()
+String getAuthor()
}
class Library {
-List<Book> books
+void addBook(Book)
+void removeBook(String)
+List<Book> searchByAuthor(String)
}
Library "1" -- "0..*" Book : contains
代码示例
import java.util.ArrayList;
import java.util.List;
class Book {
private String title;
private String author;
private String isbn;
public Book(String title, String author, String isbn) {
this.title = title;
this.author = author;
this.isbn = isbn;
}
public String getTitle() {
return title;
}
public String getAuthor() {
return author;
}
public String getIsbn() {
return isbn;
}
}
class Library {
private List<Book> books = new ArrayList<>();
public void addBook(Book book) {
books.add(book);
}
public void removeBook(String isbn) {
books.removeIf(book -> book.getIsbn().equals(isbn));
}
public List<Book> searchByAuthor(String author) {
List<Book> result = new ArrayList<>();
for (Book book : books) {
if (book.getAuthor().equals(author)) {
result.add(book);
}
}
return result;
}
}
说明
Book类封装书籍信息。Library类管理书籍集合。- 使用了封装、集合、类关系等面向对象特性。
2.4.2 利用继承和多态实现图形绘制程序
类图(Mermaid)
classDiagram
class Shape {
<<abstract>>
+abstract double getArea()
}
class Circle {
-double radius
+double getArea()
}
class Rectangle {
-double width
-double height
+double getArea()
}
Shape <|-- Circle
Shape <|-- Rectangle
代码实现
abstract class Shape {
public abstract double getArea();
}
class Circle extends Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double getArea() {
return Math.PI * radius * radius;
}
}
class Rectangle extends Shape {
private double width;
private double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public double getArea() {
return width * height;
}
}
public class Main {
public static void main(String[] args) {
Shape[] shapes = {
new Circle(5),
new Rectangle(4, 6)
};
for (Shape shape : shapes) {
System.out.println("Area: " + shape.getArea());
}
}
}
输出结果
Area: 78.53981633974483
Area: 24.0
说明
abstract关键字定义抽象类和方法。- 子类实现具体逻辑,体现多态特性。
- 通过统一接口操作不同对象,提升扩展性。
3. Java集合框架(List、Set、Map等)
Java集合框架(Java Collections Framework)是Java中最核心的工具库之一,它提供了一组接口和类,用于存储、操作和管理一组对象。集合框架不仅简化了数据结构的使用,还提升了代码的可维护性和可扩展性。本章将深入讲解集合框架的核心接口与实现类,分析它们的内部机制与适用场景,并结合实践案例帮助开发者掌握高效使用集合的方式。
3.1 集合框架概述与接口体系
Java集合框架主要由两个根接口组成: Collection 和 Map 。其中, Collection 是集合的父接口,代表一组元素的集合;而 Map 则表示键值对的映射关系。
3.1.1 Collection与Map接口的关系
Java集合框架的类图如下所示(使用 Mermaid 流程图):
classDiagram
Collection <|-- List
Collection <|-- Set
Collection <|-- Queue
Map <|-- HashMap
Map <|-- TreeMap
Map <|-- Hashtable
List <|-- ArrayList
List <|-- LinkedList
Set <|-- HashSet
Set <|-- TreeSet
Queue <|-- PriorityQueue
如上图所示, Collection 接口是 List 、 Set 、 Queue 的父接口,分别用于表示有序可重复集合、无序不可重复集合、队列结构;而 Map 接口是 HashMap 、 TreeMap 、 Hashtable 等的父接口,用于表示键值对集合。
3.1.2 常用实现类的对比(ArrayList、LinkedList、HashSet、TreeSet、HashMap、TreeMap)
下面通过表格对比几个常用实现类的核心特性:
| 类名 | 类型 | 有序性 | 可重复 | 线程安全 | 数据结构 | 常用场景 |
|---|---|---|---|---|---|---|
| ArrayList | List | 是 | 是 | 否 | 动态数组 | 随机访问频繁,插入较少 |
| LinkedList | List | 是 | 是 | 否 | 双向链表 | 插入/删除频繁,访问较少 |
| HashSet | Set | 否 | 否 | 否 | 哈希表 | 快速查找、去重 |
| TreeSet | Set | 是 | 否 | 否 | 红黑树 | 需要排序的集合 |
| HashMap | Map | 否 | 是 | 否 | 哈希表+链表/红黑树 | 快速存取键值对 |
| TreeMap | Map | 是 | 是 | 否 | 红黑树 | 需要按键排序的键值对集合 |
从上表可以看出,不同实现类适用于不同的业务场景。例如:
- 当需要频繁进行插入和删除操作时,优先考虑
LinkedList。 - 当需要元素唯一性且不关心顺序时,使用
HashSet。 - 当需要对键进行排序时,使用
TreeMap。
3.2 List接口与常用实现
List 是一个有序、可重复的集合接口,常用于存储多个对象。Java 提供了多种 List 实现类,其中最常用的是 ArrayList 和 LinkedList 。
3.2.1 ArrayList的内部实现机制
ArrayList 内部使用一个动态数组来存储元素。当元素数量超过当前数组容量时,会自动进行扩容操作。
下面是一个 ArrayList 的简单使用示例:
import java.util.ArrayList;
public class ArrayListExample {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
list.add("C++");
System.out.println("Size before remove: " + list.size());
list.remove("Python");
System.out.println("Size after remove: " + list.size());
}
}
代码逻辑分析:
- 创建一个
ArrayList实例,初始容量为10(默认值)。 - 添加三个字符串元素。
- 调用
remove()方法删除一个元素。 - 打印删除前后的集合大小。
内部机制说明:
ArrayList使用数组实现,查询效率高(O(1))。- 插入或删除中间元素时需要移动后续元素,效率为 O(n)。
- 每次扩容时,默认扩容为原来的 1.5 倍。
3.2.2 LinkedList的链表特性与应用场景
LinkedList 是基于双向链表实现的 List ,每个节点保存前驱和后继节点的引用。其优点是插入和删除效率高,但随机访问效率低。
示例代码如下:
import java.util.LinkedList;
public class LinkedListExample {
public static void main(String[] args) {
LinkedList<String> linkedList = new LinkedList<>();
linkedList.addFirst("Java");
linkedList.addLast("Python");
linkedList.add(1, "C++");
System.out.println(linkedList);
}
}
输出结果:
[Java, C++, Python]
逻辑分析:
addFirst()和addLast()分别用于在头部和尾部添加元素。add(index, element)方法可以在指定位置插入元素。- 链表结构使得插入和删除操作无需移动大量元素,性能优于
ArrayList。
3.2.3 线程安全的List实现(Vector、Collections.synchronizedList)
在多线程环境中,为了保证线程安全,可以使用 Vector 或者通过 Collections.synchronizedList() 包装 ArrayList 。
示例代码:
import java.util.Collections;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;
public class ThreadSafeListExample {
public static void main(String[] args) {
List<String> vectorList = new Vector<>();
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
List<String> copyOnWriteList = new CopyOnWriteArrayList<>();
// 多线程操作略
}
}
线程安全类对比:
| 类名 | 线程安全 | 性能表现 | 使用建议 |
|---|---|---|---|
| Vector | 是 | 中等 | 已过时,推荐使用 Collections 或 CopyOnWriteArrayList |
| Collections.synchronizedList | 是 | 中等 | 适用于同步读写操作较多的场景 |
| CopyOnWriteArrayList | 是 | 写低读高 | 适用于读多写少的并发场景 |
3.3 Set接口与唯一性控制
Set 是一个不允许重复元素的集合接口。Java 提供了 HashSet 、 LinkedHashSet 和 TreeSet 等实现类。
3.3.1 HashSet与equals()、hashCode()的关系
HashSet 依赖 hashCode() 和 equals() 方法来判断元素是否重复。如果两个对象的 hashCode() 相同,并且 equals() 返回 true ,则认为它们是相同的。
示例代码:
import java.util.HashSet;
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
return name.hashCode() + age;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof Person)) return false;
Person other = (Person) obj;
return this.name.equals(other.name) && this.age == other.age;
}
@Override
public String toString() {
return name + " - " + age;
}
}
public class HashSetExample {
public static void main(String[] args) {
HashSet<Person> people = new HashSet<>();
people.add(new Person("Alice", 25));
people.add(new Person("Bob", 30));
people.add(new Person("Alice", 25)); // 重复元素
System.out.println(people);
}
}
输出结果:
[Alice - 25, Bob - 30]
关键点:
hashCode()决定对象在哈希表中的位置。equals()用于判断两个对象是否相等。- 若不重写这两个方法,将导致
HashSet无法正确去重。
3.3.2 TreeSet的排序机制与自定义排序
TreeSet 是一个基于红黑树的集合,可以实现自然排序或自定义排序。
示例代码(自定义排序):
import java.util.*;
class PersonComparator implements Comparator<Person> {
@Override
public int compare(Person o1, Person o2) {
return Integer.compare(o1.getAge(), o2.getAge());
}
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return name + " - " + age;
}
}
public class TreeSetExample {
public static void main(String[] args) {
TreeSet<Person> people = new TreeSet<>(new PersonComparator());
people.add(new Person("Alice", 25));
people.add(new Person("Bob", 20));
people.add(new Person("Charlie", 30));
for (Person p : people) {
System.out.println(p);
}
}
}
输出结果:
Bob - 20
Alice - 25
Charlie - 30
逻辑分析:
TreeSet默认使用自然排序(实现Comparable接口)。- 通过传入自定义
Comparator,可以实现按年龄排序。 - 底层使用红黑树,保证了插入和查找的时间复杂度为 O(log n)。
3.4 Map接口与键值对操作
Map 是一个用于存储键值对的接口,Java 提供了多种实现类,如 HashMap 、 TreeMap 、 LinkedHashMap 、 WeakHashMap 等。
3.4.1 HashMap的哈希算法与扩容机制
HashMap 是基于哈希表实现的,具有高效的存取性能。其核心原理是通过哈希函数将键映射到数组索引,冲突时使用链表或红黑树解决。
示例代码:
import java.util.HashMap;
public class HashMapExample {
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<>();
map.put("Java", 1);
map.put("Python", 2);
map.put("C++", 3);
System.out.println("Value for Java: " + map.get("Java"));
System.out.println("Size: " + map.size());
}
}
输出结果:
Value for Java: 1
Size: 3
内部机制:
- 使用
hashCode()和equals()来判断键是否重复。 - 默认负载因子为 0.75,当元素数量超过容量 × 负载因子时触发扩容。
- JDK 8 之后,链表长度超过 8 时转为红黑树以提高查找效率。
3.4.2 TreeMap的排序与红黑树结构
TreeMap 是一个有序的 Map 实现,底层使用红黑树结构,支持按键排序。
示例代码:
import java.util.TreeMap;
public class TreeMapExample {
public static void main(String[] args) {
TreeMap<Integer, String> treeMap = new TreeMap<>();
treeMap.put(3, "Three");
treeMap.put(1, "One");
treeMap.put(2, "Two");
System.out.println(treeMap);
}
}
输出结果:
{1=One, 2=Two, 3=Three}
特性:
- 键必须实现
Comparable接口或提供Comparator。 - 插入、查找、删除的时间复杂度为 O(log n)。
- 适用于需要按键排序的场景。
3.4.3 WeakHashMap与垃圾回收机制
WeakHashMap 是一种特殊的 Map ,其键为弱引用( WeakReference ),当键对象不再被引用时,会被垃圾回收器回收。
示例代码:
import java.lang.ref.WeakReference;
import java.util.WeakHashMap;
public class WeakHashMapExample {
public static void main(String[] args) throws InterruptedException {
WeakHashMap<Key, String> map = new WeakHashMap<>();
Key key = new Key("test");
map.put(key, "value");
System.out.println("Before GC: " + map.size());
key = null; // 释放强引用
System.gc();
Thread.sleep(1000); // 等待GC
System.out.println("After GC: " + map.size());
}
}
class Key {
private String name;
public Key(String name) {
this.name = name;
}
public String toString() {
return name;
}
}
输出结果:
Before GC: 1
After GC: 0
特点:
- 键被回收后,对应的键值对也会被自动移除。
- 常用于缓存系统或资源管理场景。
3.5 Java集合框架的实践应用
3.5.1 使用Map实现学生信息管理系统
通过 HashMap 实现一个简单的学生信息管理系统,支持添加、查询、删除功能。
import java.util.HashMap;
import java.util.Map;
class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String toString() {
return "Name: " + name + ", Age: " + age;
}
}
public class StudentManager {
private Map<String, Student> students = new HashMap<>();
public void addStudent(String id, Student student) {
students.put(id, student);
}
public Student getStudent(String id) {
return students.get(id);
}
public void removeStudent(String id) {
students.remove(id);
}
public static void main(String[] args) {
StudentManager manager = new StudentManager();
manager.addStudent("001", new Student("Alice", 20));
manager.addStudent("002", new Student("Bob", 22));
System.out.println(manager.getStudent("001"));
manager.removeStudent("001");
System.out.println(manager.getStudent("001"));
}
}
3.5.2 利用Set实现数据去重逻辑
使用 HashSet 实现一个数据去重程序:
import java.util.HashSet;
import java.util.Set;
public class DataDeduplication {
public static void main(String[] args) {
Set<Integer> uniqueData = new HashSet<>();
int[] data = {1, 2, 3, 2, 4, 1, 5};
for (int num : data) {
uniqueData.add(num);
}
System.out.println("Unique Data: " + uniqueData);
}
}
输出结果:
Unique Data: [1, 2, 3, 4, 5]
总结:
通过本章的学习,读者可以深入理解 Java 集合框架的结构、原理与实际应用方式。不同的集合类适用于不同的业务场景,合理选择可以显著提升程序性能与代码质量。
4. Java I/O流处理(InputStream、OutputStream、File、BufferedReader)
Java I/O流是Java编程中非常重要的基础模块,负责处理程序与外部资源之间的数据交互,如文件、网络、内存等。I/O流体系结构庞大,涵盖字节流、字符流、缓冲流、文件操作、NIO增强等多个方面。本章将深入解析Java I/O流的分类机制、流处理模型、文件操作实践及性能优化策略,帮助开发者构建高效、安全的数据读写能力。
4.1 Java I/O流的基本分类
Java I/O流按照处理数据的基本单位分为 字节流 和 字符流 ,同时根据流的功能分为 节点流 和 处理流 。理解这些分类有助于开发者在不同场景下选择合适的流类型。
4.1.1 字节流与字符流的区别
Java的I/O流体系中,字节流(InputStream/OutputStream)用于处理 二进制数据 ,如图片、音频、视频等;字符流(Reader/Writer)用于处理 文本数据 ,自动处理字符编码转换。
| 分类 | 基类 | 用途 | 编码支持 |
|---|---|---|---|
| 字节流 | InputStream / OutputStream | 处理二进制数据 | 无 |
| 字符流 | Reader / Writer | 处理文本数据 | 支持编码转换 |
示例:字节流与字符流读取文本文件
// 使用字节流读取文本文件(需要手动处理编码)
try (InputStream is = new FileInputStream("example.txt")) {
byte[] buffer = new byte[1024];
int length;
while ((length = is.read(buffer)) > 0) {
System.out.println(new String(buffer, 0, length, StandardCharsets.UTF_8));
}
} catch (IOException e) {
e.printStackTrace();
}
// 使用字符流读取文本文件(自动处理编码)
try (Reader reader = new FileReader("example.txt", StandardCharsets.UTF_8)) {
char[] buffer = new char[1024];
int length;
while ((length = reader.read(buffer)) > 0) {
System.out.println(new String(buffer, 0, length));
}
} catch (IOException e) {
e.printStackTrace();
}
逻辑分析 :
-FileInputStream是字节输入流,read()方法读取的是原始字节。
-FileReader是字符输入流,内部使用默认或指定编码(如 UTF-8)将字节转换为字符。
- 字符流更适用于文本处理,避免手动编码转换带来的错误。
4.1.2 节点流与处理流的组合使用
节点流(Node Stream)直接连接数据源或目标,如 FileInputStream ;处理流(Processing Stream)提供增强功能,如缓冲、压缩、对象序列化等,如 BufferedInputStream 。
流处理组合流程图(Mermaid)
graph TD
A[FileInputStream] --> B[BufferedInputStream]
B --> C[ObjectInputStream]
C --> D[反序列化对象]
示例:使用缓冲处理流提高性能
try (InputStream is = new BufferedInputStream(new FileInputStream("data.bin"))) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
// 处理buffer中的数据
}
} catch (IOException e) {
e.printStackTrace();
}
参数说明 :
-BufferedInputStream内部使用缓冲区减少磁盘访问次数,提高读取效率。
- 在处理大文件或频繁I/O操作时,建议始终使用缓冲流包装基础流。
4.2 文件操作与File类
Java中 File 类用于表示文件或目录路径,虽然它不直接处理文件内容,但提供了文件的创建、删除、重命名、目录遍历等功能。
4.2.1 文件的创建、读取与删除
File file = new File("test.txt");
// 创建文件
if (!file.exists()) {
file.createNewFile();
}
// 读取文件属性
System.out.println("文件大小:" + file.length());
System.out.println("是否可读:" + file.canRead());
// 删除文件
file.delete();
逻辑分析 :
-createNewFile()方法创建一个空文件,若文件已存在则返回 false。
-length()返回文件大小(单位为字节),适用于本地文件系统。
-delete()删除文件,但不抛出异常,需结合exists()判断是否存在。
4.2.2 文件目录遍历与过滤器使用
File dir = new File(".");
// 获取所有文件列表
File[] files = dir.listFiles((d, name) -> name.endsWith(".java"));
if (files != null) {
for (File f : files) {
System.out.println(f.getName());
}
}
参数说明 :
-listFiles()方法接受一个FilenameFilter接口,可自定义过滤条件。
- 本例中使用 Lambda 表达式过滤出.java结尾的文件。
4.3 字节流与缓冲流操作
字节流适用于处理二进制数据,而缓冲流则通过缓存机制优化性能,减少系统调用次数。
4.3.1 FileInputStream与FileOutputStream的使用
try (FileInputStream fis = new FileInputStream("input.bin");
FileOutputStream fos = new FileOutputStream("output.bin")) {
int data;
while ((data = fis.read()) != -1) {
fos.write(data);
}
} catch (IOException e) {
e.printStackTrace();
}
逻辑分析 :
-read()方法每次读取一个字节,返回 -1 表示读取结束。
- 逐字节写入效率较低,适用于小文件或特定格式解析。
4.3.2 BufferedInputStream与BufferedOutputStream的性能优化
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("input.bin"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("output.bin"))) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = bis.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
}
} catch (IOException e) {
e.printStackTrace();
}
参数说明 :
- 缓冲区大小建议为 8KB(8192 字节),这是大多数文件系统块大小的常见值。
- 使用缓冲流可显著减少I/O操作次数,提升复制大文件的性能。
4.4 字符流与编码处理
字符流处理文本数据,支持编码转换,适用于多语言环境。
4.4.1 InputStreamReader与OutputStreamWriter的转换机制
try (InputStreamReader isr = new InputStreamReader(new FileInputStream("utf8.txt"), StandardCharsets.UTF_8);
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk.txt"), "GBK")) {
char[] buffer = new char[1024];
int charsRead;
while ((charsRead = isr.read(buffer)) != -1) {
osw.write(buffer, 0, charsRead);
}
} catch (IOException e) {
e.printStackTrace();
}
逻辑分析 :
-InputStreamReader将字节流转换为字符流,并指定编码。
-OutputStreamWriter将字符流写入字节流,并指定目标编码。
- 可用于不同编码格式之间的文本转换。
4.4.2 BufferedReader与PrintWriter的文本处理
try (BufferedReader br = new BufferedReader(new FileReader("input.txt"));
PrintWriter pw = new PrintWriter(new FileWriter("output.txt"))) {
String line;
while ((line = br.readLine()) != null) {
pw.println(line.toUpperCase());
}
} catch (IOException e) {
e.printStackTrace();
}
参数说明 :
-BufferedReader提供readLine()方法逐行读取文本。
-PrintWriter提供便捷的格式化输出方法,如println()、printf()。
- 适合处理日志、配置文件等文本数据。
4.5 Java NIO简介与文件操作增强
Java NIO(New I/O)引入了 Path 和 Files 类,提供了更高效、灵活的文件操作接口。
4.5.1 Path与Files类的基本使用
Path path = Paths.get("example.txt");
// 判断文件是否存在
if (Files.exists(path)) {
System.out.println("文件存在");
}
// 读取整个文件内容
List<String> lines = Files.readAllLines(path);
lines.forEach(System.out::println);
逻辑分析 :
-Paths.get()构建路径对象,跨平台兼容。
-Files.readAllLines()一次性读取所有行,适用于小文件。
4.5.2 文件复制、移动与属性读取
Path source = Paths.get("source.txt");
Path target = Paths.get("target.txt");
// 文件复制
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
// 文件移动
Files.move(source, Paths.get("moved.txt"), StandardCopyOption.ATOMIC_MOVE);
// 获取文件属性
BasicFileAttributes attr = Files.readAttributes(source, BasicFileAttributes.class);
System.out.println("文件大小:" + attr.size());
System.out.println("创建时间:" + attr.creationTime());
参数说明 :
-StandardCopyOption.REPLACE_EXISTING允许覆盖已有文件。
-StandardCopyOption.ATOMIC_MOVE确保移动操作原子性。
-BasicFileAttributes提供丰富的元数据信息。
4.6 I/O流的实际应用案例
4.6.1 实现日志文件的自动归档与备份
public class LogArchiver {
public static void archiveLog(String logPath, String backupDir) throws IOException {
Path source = Paths.get(logPath);
Path targetDir = Paths.get(backupDir);
if (!Files.exists(targetDir)) {
Files.createDirectories(targetDir);
}
String backupFileName = "log_" + System.currentTimeMillis() + ".log";
Path target = targetDir.resolve(backupFileName);
Files.move(source, target, StandardCopyOption.REPLACE_EXISTING);
System.out.println("日志已归档至:" + target);
}
public static void main(String[] args) {
try {
archiveLog("app.log", "logs/backup/");
} catch (IOException e) {
e.printStackTrace();
}
}
}
功能说明 :
- 每次运行将当前日志文件移动到备份目录,并附加时间戳命名。
- 适用于定时任务中自动清理或归档日志。
4.6.2 文本文件的逐行读取与内容分析
public class TextAnalyzer {
public static void analyzeFile(String filePath) throws IOException {
Path path = Paths.get(filePath);
int lineCount = 0, wordCount = 0;
try (BufferedReader br = Files.newBufferedReader(path)) {
String line;
while ((line = br.readLine()) != null) {
lineCount++;
String[] words = line.split("\\s+");
wordCount += words.length;
}
}
System.out.println("总行数:" + lineCount);
System.out.println("总词数:" + wordCount);
}
public static void main(String[] args) {
try {
analyzeFile("sample.txt");
} catch (IOException e) {
e.printStackTrace();
}
}
}
逻辑分析 :
- 使用BufferedReader高效逐行读取。
- 使用正则\\s+拆分单词,统计词数。
- 适用于日志分析、词频统计等场景。
本章通过系统讲解Java I/O流的分类、流处理机制、文件操作与性能优化策略,结合实际案例演示了其在日志管理、文本处理等场景中的应用方式。下一章将深入探讨Java多线程与并发控制机制,进一步提升程序的性能与响应能力。
5. Java多线程与并发控制(Thread、Runnable、ExecutorService)
Java 多线程是构建高性能、并发应用程序的重要基础。随着现代处理器多核架构的普及,合理利用多线程可以显著提升程序的执行效率和资源利用率。本章将深入探讨 Java 中线程的创建、生命周期、调度机制、同步控制以及线程池等核心概念,并通过实际案例帮助读者掌握并发编程的关键技术。
5.1 多线程基础与线程创建
Java 提供了两种主要方式来创建线程:继承 Thread 类和实现 Runnable 接口。理解它们之间的区别对于掌握线程机制至关重要。
5.1.1 继承 Thread 类与实现 Runnable 接口的区别
Thread 类创建线程
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("Thread is running via Thread class.");
}
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start(); // 启动线程
}
}
代码解析:
MyThread继承Thread并重写run()方法。start()方法用于启动线程,JVM 会调用其run()方法。- 优点 :结构简单,适合单一任务。
- 缺点 :Java 不支持多重继承,若类已继承其他类,将无法再继承
Thread。
实现 Runnable 接口
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Thread is running via Runnable interface.");
}
public static void main(String[] args) {
Thread thread = new Thread(new MyRunnable());
thread.start();
}
}
代码解析:
MyRunnable实现Runnable接口并实现run()方法。- 通过将
Runnable实例传入Thread构造器创建线程。 - 优点 :更灵活,支持多线程任务共享同一个
Runnable实例。 - 适用场景 :任务逻辑与线程类解耦,推荐使用。
| 对比项 | Thread 类 | Runnable 接口 |
|---|---|---|
| 继承限制 | 不支持多重继承 | 支持实现多个接口 |
| 任务与线程耦合度 | 高 | 低 |
| 可扩展性 | 低 | 高 |
| 线程复用性 | 不易复用 | 易于复用 |
结论: 推荐使用 Runnable 接口实现线程任务,以提高代码灵活性与可维护性。
5.1.2 线程的生命周期与状态转换
Java 线程在其生命周期中会经历多个状态,这些状态由 Thread.State 枚举定义。
线程生命周期状态
graph TD
A[New] --> B[Runnable]
B --> C[Running]
C --> D[Blocked/Waiting]
D --> B
C --> E[Terminated]
状态说明:
- New(新建) :线程对象被创建但尚未启动。
- Runnable(可运行) :线程已调用
start(),等待 CPU 调度。 - Running(运行) :线程正在执行
run()方法。 - Blocked / Waiting / Timed Waiting(阻塞/等待/超时等待) :
- 等待锁(Blocked)
- 等待其他线程通知(Waiting)
- 等待一段时间后恢复(Timed Waiting) - Terminated(终止) :线程正常执行完毕或异常退出。
示例代码:获取线程状态
public class ThreadStateExample {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread is running.");
});
System.out.println("State after creation: " + t.getState()); // NEW
t.start();
Thread.sleep(100); // 等待线程进入运行状态
System.out.println("State during execution: " + t.getState()); // RUNNABLE
Thread.sleep(1100); // 等待线程结束
System.out.println("State after termination: " + t.getState()); // TERMINATED
}
}
逻辑分析:
getState()方法返回线程当前状态。- 线程启动后进入
RUNNABLE状态,执行sleep()后进入TIMED_WAITING。 - 线程执行完毕后变为
TERMINATED。
5.2 线程调度与优先级
线程调度是操作系统决定哪个线程获得 CPU 时间的过程。Java 通过线程优先级影响调度顺序。
5.2.1 线程的休眠与唤醒机制
Java 提供 Thread.sleep() 和 Object.wait() / notify() 实现线程休眠与唤醒。
使用 sleep() 让线程休眠
public class SleepExample {
public static void main(String[] args) {
Thread t = new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(500);
System.out.println("Thread is running: " + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
}
}
逻辑分析:
sleep(500)让线程休眠 500 毫秒。- 线程在每次循环中暂停 0.5 秒后继续执行。
使用 wait() 和 notify() 实现线程通信
public class WaitNotifyExample {
private static final Object lock = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (lock) {
System.out.println("Thread 1: Waiting...");
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 1: Resumed.");
}
}).start();
new Thread(() -> {
synchronized (lock) {
System.out.println("Thread 2: Notifying...");
lock.notify();
}
}).start();
}
}
逻辑分析:
wait()使当前线程释放锁并进入等待状态。notify()唤醒一个等待的线程,使其重新获取锁并继续执行。
5.2.2 线程优先级对调度的影响
Java 线程优先级范围为 1 到 10,默认为 5( Thread.NORM_PRIORITY )。
public class PriorityExample {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("Thread 1: " + i);
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("Thread 2: " + i);
}
});
t1.setPriority(Thread.MIN_PRIORITY); // 设置最低优先级
t2.setPriority(Thread.MAX_PRIORITY); // 设置最高优先级
t1.start();
t2.start();
}
}
逻辑分析:
setPriority()设置线程优先级。- 高优先级线程可能获得更多的 CPU 时间片,但不能保证绝对执行顺序。
5.3 线程同步与线程安全
多线程环境下,共享资源的访问可能导致数据不一致。Java 提供多种机制保证线程安全。
5.3.1 synchronized 关键字与锁机制
synchronized 是 Java 内建的同步机制,可用于方法或代码块。
public class SynchronizedCounter {
private int count = 0;
public synchronized void increment() {
count++;
}
public static void main(String[] args) throws InterruptedException {
SynchronizedCounter counter = new SynchronizedCounter();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Final count: " + counter.count); // 应为 2000
}
}
逻辑分析:
synchronized方法保证一次只有一个线程能执行increment()。- 如果不加同步,
count的值可能小于 2000。
5.3.2 volatile 关键字与内存可见性
volatile 保证变量在多线程间的可见性,适用于状态标志。
public class VolatileExample {
private volatile boolean running = true;
public void stop() {
running = false;
}
public static void main(String[] args) throws InterruptedException {
VolatileExample example = new VolatileExample();
Thread worker = new Thread(() -> {
while (example.running) {
// do work
}
System.out.println("Worker stopped.");
});
worker.start();
Thread.sleep(1000);
example.stop();
}
}
逻辑分析:
volatile保证变量修改对其他线程立即可见。- 如果不加
volatile,running的更新可能不会及时反映到其他线程中。
5.3.3 使用 ReentrantLock 进行显式锁控制
ReentrantLock 提供比 synchronized 更灵活的锁机制,支持尝试锁、超时等。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private Lock lock = new ReentrantLock();
private int count = 0;
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
ReentrantLockExample example = new ReentrantLockExample();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
example.increment();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
example.increment();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Final count: " + example.count);
}
}
逻辑分析:
lock()获取锁,unlock()释放锁。- 使用
try-finally确保即使发生异常也能释放锁。 - 支持尝试加锁、带超时的加锁等高级特性。
5.4 线程池与 ExecutorService
线程的频繁创建和销毁会影响性能,Java 提供 ExecutorService 来管理线程池。
5.4.1 固定大小线程池与缓存线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExample {
public static void main(String[] args) {
// 固定大小线程池
ExecutorService fixedPool = Executors.newFixedThreadPool(2);
for (int i = 0; i < 5; i++) {
final int taskNo = i;
fixedPool.submit(() -> {
System.out.println("Task " + taskNo + " is running by " + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
// 缓存线程池
ExecutorService cachedPool = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
final int taskNo = i;
cachedPool.submit(() -> {
System.out.println("Cached Task " + taskNo + " is running by " + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
fixedPool.shutdown();
cachedPool.shutdown();
}
}
逻辑分析:
newFixedThreadPool(2)创建固定大小为 2 的线程池,适合处理并发任务。newCachedThreadPool()创建可缓存线程池,适用于大量短期任务。submit()提交任务,线程池自动分配线程执行。
5.4.2 Future 与 Callable 的异步执行
Callable 支持返回值, Future 用于获取异步结果。
import java.util.concurrent.*;
public class FutureCallableExample {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executor = Executors.newSingleThreadExecutor();
Callable<Integer> task = () -> {
Thread.sleep(1000);
return 123;
};
Future<Integer> future = executor.submit(task);
System.out.println("Task submitted, waiting for result...");
Integer result = future.get(); // 阻塞直到结果返回
System.out.println("Result: " + result);
executor.shutdown();
}
}
逻辑分析:
Callable返回一个结果,适用于需要返回值的异步操作。Future.get()阻塞当前线程直到结果就绪。Future.isDone()可用于轮询任务是否完成。
5.5 多线程的实际应用
5.5.1 并发下载器的实现
模拟并发下载多个文件:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ConcurrentDownloader {
public static void main(String[] args) {
String[] urls = {
"http://example.com/file1",
"http://example.com/file2",
"http://example.com/file3"
};
ExecutorService pool = Executors.newFixedThreadPool(3);
for (String url : urls) {
pool.submit(new DownloadTask(url));
}
pool.shutdown();
}
static class DownloadTask implements Runnable {
private String url;
public DownloadTask(String url) {
this.url = url;
}
@Override
public void run() {
System.out.println("Downloading from " + url + " by " + Thread.currentThread().getName());
try {
Thread.sleep(2000); // 模拟下载
System.out.println("Download complete from " + url);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
逻辑分析:
- 使用线程池并发执行多个下载任务。
- 每个任务模拟下载耗时操作,提升整体效率。
5.5.2 使用线程池实现任务队列调度
任务队列是线程池的典型应用场景,常用于后台任务处理。
import java.util.concurrent.*;
public class TaskQueueScheduler {
public static void main(String[] args) {
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(10);
ExecutorService executor = new ThreadPoolExecutor(
2, 4, 60, TimeUnit.SECONDS, queue);
for (int i = 0; i < 15; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println("Processing Task " + taskId + " on " + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executor.shutdown();
}
}
逻辑分析:
- 使用
ThreadPoolExecutor自定义线程池参数。 - 任务队列缓存待处理任务,线程池根据负载动态调整线程数量。
- 防止任务丢失或系统资源耗尽。
至此,本章完整展示了 Java 多线程的核心概念、实现方式及实际应用,为构建高效并发程序打下坚实基础。后续章节将继续深入 Java 的网络编程和分布式系统开发。
6. Java网络编程(Socket、URL)
6.1 网络通信基础与协议概述
在Java中进行网络通信,开发者需要理解基本的网络协议和通信机制。Java通过 java.net 包提供了丰富的类和接口来支持网络编程,主要包括TCP/IP和UDP协议的实现。
6.1.1 TCP/IP与UDP协议的区别
TCP(Transmission Control Protocol)和UDP(User Datagram Protocol)是两种主要的传输层协议,它们在网络通信中扮演着不同的角色。
| 特性 | TCP | UDP |
|---|---|---|
| 连接方式 | 面向连接 | 无连接 |
| 可靠性 | 高,确保数据正确传输 | 不保证传输 |
| 传输速度 | 相对较慢 | 快 |
| 数据顺序 | 保证顺序 | 不保证顺序 |
| 应用场景 | HTTP、FTP、SMTP等 | 视频流、DNS、VoIP等 |
TCP适用于要求数据完整性和顺序性的应用,而UDP适用于实时性强、容忍部分数据丢失的场景。
6.1.2 IP地址与端口号的基本概念
IP地址是网络中设备的唯一标识符,分为IPv4和IPv6两种格式。端口号是用于区分不同应用程序的16位数字(0~65535),其中0~1023为系统端口。
Java中使用 InetAddress 类表示IP地址,使用 SocketAddress 及其子类表示套接字地址。
import java.net.InetAddress;
import java.net.UnknownHostException;
public class InetAddressExample {
public static void main(String[] args) {
try {
InetAddress address = InetAddress.getByName("www.baidu.com");
System.out.println("IP Address: " + address.getHostAddress());
System.out.println("Host Name: " + address.getHostName());
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
代码说明:
InetAddress.getByName()方法根据域名获取对应的IP地址。getHostAddress()返回IP地址字符串。getHostName()返回主机名。
该程序演示了如何通过Java获取指定域名的IP地址信息,是网络通信的基础操作之一。
6.2 基于Socket的TCP通信
Socket编程是Java网络通信的核心技术之一,常用于实现客户端与服务器之间的可靠数据传输。
6.2.1 服务器端Socket的创建与监听
服务器端通过 ServerSocket 类监听客户端的连接请求。
import java.io.*;
import java.net.*;
public class TCPServer {
public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(8888)) {
System.out.println("Server is listening on port 8888...");
while (true) {
Socket socket = serverSocket.accept(); // 等待客户端连接
System.out.println("New client connected");
// 启动线程处理客户端通信
new ServerThread(socket).start();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
class ServerThread extends Thread {
private Socket socket;
public ServerThread(Socket socket) {
this.socket = socket;
}
public void run() {
try (
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter output = new PrintWriter(socket.getOutputStream(), true);
) {
String clientMessage;
while ((clientMessage = input.readLine()) != null) {
System.out.println("Received: " + clientMessage);
output.println("Echo: " + clientMessage); // 回应客户端
}
} catch (IOException ex) {
ex.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
代码说明:
ServerSocket绑定到端口8888,监听客户端连接。accept()方法阻塞等待客户端连接。- 每个连接创建一个线程
ServerThread处理通信。 - 使用
BufferedReader读取客户端发送的消息,并用PrintWriter回传。
6.2.2 客户端Socket的连接与数据传输
客户端使用 Socket 类连接服务器并进行通信。
import java.io.*;
import java.net.*;
public class TCPClient {
public static void main(String[] args) {
try (Socket socket = new Socket("localhost", 8888)) {
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter output = new PrintWriter(socket.getOutputStream(), true);
BufferedReader consoleInput = new BufferedReader(new InputStreamReader(System.in));
String userInput;
while ((userInput = consoleInput.readLine()) != null) {
output.println(userInput); // 发送消息到服务器
System.out.println("Server response: " + input.readLine()); // 接收响应
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
代码说明:
- 客户端连接本地服务器的8888端口。
- 通过
PrintWriter发送用户输入的消息。 - 使用
BufferedReader接收服务器的回传消息。
6.2.3 多线程处理多个客户端连接
如上一节服务器端代码所示,每当有新客户端连接时,都会创建一个新的线程来处理该连接,实现并发通信。
逻辑分析:
- 每个客户端连接独立处理,互不干扰。
- 服务器端可同时服务多个客户端,提高并发处理能力。
- 线程资源需合理管理,避免内存溢出。
6.3 基于URL的数据访问
Java通过 URL 类和 HttpURLConnection 类实现了对网络资源的访问,广泛用于Web爬虫、API调用等场景。
6.3.1 使用URL类访问网络资源
import java.io.*;
import java.net.*;
public class URLReader {
public static void main(String[] args) {
try {
URL url = new URL("https://www.baidu.com");
BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
代码说明:
URL类表示一个网络资源地址。openStream()方法打开一个输入流,读取网页内容。- 使用
BufferedReader逐行读取HTML内容并输出。
6.3.2 HttpURLConnection的请求与响应处理
HttpURLConnection 用于处理HTTP请求与响应,支持GET、POST等方法。
import java.io.*;
import java.net.*;
public class HttpURLConnectionExample {
public static void main(String[] args) {
try {
URL url = new URL("https://jsonplaceholder.typicode.com/posts");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
int responseCode = connection.getResponseCode();
System.out.println("Response Code: " + responseCode);
if (responseCode == HttpURLConnection.HTTP_OK) {
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line;
StringBuilder response = new StringBuilder();
while ((line = reader.readLine()) != null) {
response.append(line);
}
reader.close();
System.out.println("Response Body: " + response.toString());
} else {
System.out.println("GET request failed.");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
代码说明:
- 设置请求方法为GET。
- 获取响应码判断请求是否成功。
- 若成功,读取响应体内容。
6.4 网络编程的实践应用
6.4.1 实现一个简单的即时通讯聊天程序
基于Socket通信,可以实现一个简单的即时通讯系统,支持多个客户端与服务器之间的消息转发。
架构图:
graph TD
A[Client 1] --> B[Server]
C[Client 2] --> B
D[Client N] --> B
B --> A
B --> C
B --> D
说明:
- 所有客户端连接到服务器。
- 服务器接收消息后,广播给所有其他客户端。
- 每个客户端通过独立线程处理接收和发送。
6.4.2 利用URL获取网页内容并解析HTML
使用 Jsoup 库可对HTML内容进行解析。
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
public class HtmlParser {
public static void main(String[] args) {
try {
Document doc = Jsoup.connect("https://www.baidu.com").get();
Element title = doc.select("title").first();
System.out.println("Page Title: " + title.text());
} catch (IOException e) {
e.printStackTrace();
}
}
}
代码说明:
- 使用
Jsoup.connect()发起GET请求。 select("title")选择HTML中的title标签。- 输出网页标题,实现基本的HTML解析功能。
本章通过Socket编程、URL访问和HTML解析三方面内容,详细讲解了Java网络编程的核心知识点,并结合代码示例与流程图说明了典型应用场景。下一章将继续深入Java的高级特性,敬请期待。
简介:Java API文档中文版是Java开发者的必备参考资料,全面介绍了Java SE平台的核心类库、接口和方法。涵盖基础语法、集合框架、网络编程、多线程、I/O流、反射、异常处理、JDBC、GUI开发等关键内容,并提供详细的API说明和使用示例。该文档适用于初学者入门学习和开发者日常开发参考,帮助提升Java程序的开发效率与质量。
更多推荐




所有评论(0)