本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:Java API文档中文版是Java开发者的必备参考资料,全面介绍了Java SE平台的核心类库、接口和方法。涵盖基础语法、集合框架、网络编程、多线程、I/O流、反射、异常处理、JDBC、GUI开发等关键内容,并提供详细的API说明和使用示例。该文档适用于初学者入门学习和开发者日常开发参考,帮助提升Java程序的开发效率与质量。
Java API文档中文版

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(); // 调用方法
    }
}
执行逻辑分析
  1. new Person("Tom", 25) :调用构造方法,分配内存空间并初始化字段。
  2. 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());
    }
}

代码逻辑分析:

  1. 创建一个 ArrayList 实例,初始容量为10(默认值)。
  2. 添加三个字符串元素。
  3. 调用 remove() 方法删除一个元素。
  4. 打印删除前后的集合大小。

内部机制说明:

  • 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]

状态说明:

  1. New(新建) :线程对象被创建但尚未启动。
  2. Runnable(可运行) :线程已调用 start() ,等待 CPU 调度。
  3. Running(运行) :线程正在执行 run() 方法。
  4. Blocked / Waiting / Timed Waiting(阻塞/等待/超时等待)
    - 等待锁(Blocked)
    - 等待其他线程通知(Waiting)
    - 等待一段时间后恢复(Timed Waiting)
  5. 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的高级特性,敬请期待。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:Java API文档中文版是Java开发者的必备参考资料,全面介绍了Java SE平台的核心类库、接口和方法。涵盖基础语法、集合框架、网络编程、多线程、I/O流、反射、异常处理、JDBC、GUI开发等关键内容,并提供详细的API说明和使用示例。该文档适用于初学者入门学习和开发者日常开发参考,帮助提升Java程序的开发效率与质量。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐