85、Java实用类与机制详解

Java实用类与机制详细解析

Java实用类与机制详解

1. BitSet类

1.1 概述

BitSet类可用于创建动态增长的位向量。实际上,位集是一个从0到 Integer.MAX_VALUE 索引的布尔位向量,初始时所有位都为 false 。这些位可以单独设置、清除或检索。BitSet仅使用足够的存储空间来保存已设置的最高索引位,超出该索引的位被视为 false

1.2 构造函数

构造函数 描述
public BitSet(int size) 创建一个新的位集,具有足够的初始存储空间,可显式表示从0到 size - 1 的位,所有位初始为 false
public BitSet() 创建一个具有默认初始存储空间的新位集,所有位初始为 false

1.3 位操作方法

  • 单个位操作

    • public void set(int index) :将指定索引的位设置为 true
    • public void clear(int index) :将指定索引的位设置为 false
    • public void flip(int index) :将指定索引的位设置为其当前值的补码,即 true 变为 false false 变为 true
    • public boolean get(int index) :返回指定索引的位的值。
  • 范围位操作 :上述方法都有处理位范围的重载形式,这些重载方法接受一个起始索引和一个结束索引,对该范围内的所有位进行设置、清除、翻转或返回操作。

1.4 逻辑操作方法

方法 描述
public void and(BitSet other) 对当前位集和另一个位集进行逻辑与操作,并将结果存储在当前位集中。
public void andNot(BitSet other) 清除当前位集中在另一个位集中被设置的所有位。
public void or(BitSet other) 对当前位集和另一个位集进行逻辑或操作,并将结果存储在当前位集中。
public void xor(BitSet other) 对当前位集和另一个位集进行逻辑异或操作,并将结果存储在当前位集中。

1.5 示例代码

public class WhichChars {
    private BitSet used = new BitSet();
    public WhichChars(String str) {
        for (int i = 0; i < str.length(); i++)
            used.set(str.charAt(i));    // set bit for char
    }
    public String toString() {
        String desc = "[";
        for (int i = used.nextSetBit(0);
             i >= 0;
             i = used.nextSetBit(i+1) ) {
            desc += (char) i;
        }
        return desc + "]";
    }
}

使用示例:

public class Main {
    public static void main(String[] args) {
        WhichChars wc = new WhichChars("Testing123");
        System.out.println(wc); // 输出: [ 123Teginst]
    }
}

1.6 流程图

graph TD;
    A[创建WhichChars对象] --> B[遍历字符串];
    B --> C[设置对应位为true];
    C --> D[生成字符串表示];
    D --> E[输出结果];

2. Observer/Observable机制

2.1 概述

Observer/Observable类型为任意数量的Observer对象提供了一个协议,用于监视任意数量的Observable对象的更改和事件。Observable对象继承自 Observable 类,该类提供了维护一个Observer对象列表的方法,这些Observer对象希望了解Observable对象的更改。所有在“感兴趣”列表中的对象都必须实现 Observer 接口。

2.2 Observer接口

Observer 接口只包含一个方法:

public void update(Observable obj, Object arg)

当Observable对象 obj 有更改或事件要报告时,会调用此方法。 arg 参数用于传递一个任意对象,以向观察者描述更改或事件。

2.3 Observable类方法

方法 描述
protected void setChanged() 标记此对象已更改, hasChanged 方法现在将返回 true ,但不通知观察者。
protected void clearChanged() 表示此对象不再更改或已通知所有观察者最后一次更改, hasChanged 方法现在将返回 false
public boolean hasChanged() 返回“更改”标志的当前值。
public void notifyObservers(Object arg) 通知所有观察者发生了某些事情,然后清除“更改”标志。
public void notifyObservers() 等同于 notifyObservers(null)
public void addObserver(Observer o) 将观察者 o 添加到观察者列表中(如果它不在列表中)。
public void deleteObserver(Observer o) 从观察者列表中删除观察者 o
public void deleteObservers() 删除观察者列表中的所有观察者。
public int countObservers() 返回观察者列表中的观察者数量。

2.4 示例代码

import java.util.*;

// 定义Observable类
public class Users extends Observable {
    private Map<String, UserState> loggedIn = 
        new HashMap<String, UserState>();
    public void login(String name, String password)
        throws BadUserException
    {
        if (!passwordValid(name, password))
            throw new BadUserException(name);
        UserState state = new UserState(name);
        loggedIn.put(name, state);
        setChanged();
        notifyObservers(state);
    }
    public void logout(UserState state) {
        loggedIn.remove(state.name());
        setChanged();
        notifyObservers(state);
    }
    // ...
}

// 定义Observer类
public class Eye implements Observer {
    Users watching;
    public Eye(Users users) {
        watching = users;
        watching.addObserver(this);
    }
    public void update(Observable users, Object whichState)
    {
        if (users != watching)
            throw new IllegalArgumentException();
        UserState state = (UserState) whichState;
        if (watching.loggedIn(state))   // user logged in
            addUser(state);             // add to my list
        else
            removeUser(state);          // remove from list
    }
    // ...
}

2.5 流程图

graph TD;
    A[用户登录/注销] --> B[Users对象设置更改标志];
    B --> C[Users对象通知观察者];
    C --> D[Eye对象调用update方法];
    D --> E[根据用户状态更新显示];

3. Random类

3.1 概述

Random类用于创建管理独立伪随机数序列的对象。如果不关心序列的具体内容,并且希望将其作为双精度值序列,可以使用 java.lang.Math.random 方法。

3.2 构造函数

构造函数 描述
public Random() 创建一个新的随机数生成器,其种子将根据当前时间初始化。
public Random(long seed) 使用指定的种子创建一个新的随机数生成器,具有相同初始种子的两个Random对象将返回相同的伪随机数序列。

3.3 常用方法

方法 描述
public boolean nextBoolean() 返回一个均匀分布的伪随机布尔值。
public int nextInt() 返回一个在 Integer.MIN_VALUE Integer.MAX_VALUE 之间均匀分布的伪随机整数值。
public int nextInt(int ceiling) 返回一个至少为零且小于 ceiling 的伪随机整数值。
public long nextLong() 返回一个在 Long.MIN_VALUE Long.MAX_VALUE 之间均匀分布的伪随机长整数值。
public void nextBytes(byte[] buf) 用随机字节填充数组 buf
public float nextFloat() 返回一个在0.0f(包含)和1.0f(不包含)之间均匀分布的伪随机浮点值。
public double nextDouble() 返回一个在0.0(包含)和1.0(不包含)之间均匀分布的伪随机双精度值。
public double nextGaussian() 返回一个均值为0.0,标准差为1.0的高斯分布伪随机双精度值。

3.4 示例代码

import java.util.Random;

public class RandomExample {
    public static void main(String[] args) {
        Random random = new Random();
        int randomInt = random.nextInt(10); // 生成0到9之间的随机整数
        double randomDouble = random.nextDouble(); // 生成0到1之间的随机双精度数
        System.out.println("Random int: " + randomInt);
        System.out.println("Random double: " + randomDouble);
    }
}

3.5 流程图

graph TD;
    A[创建Random对象] --> B[调用随机数生成方法];
    B --> C[获取随机数];
    C --> D[输出随机数];

4. Scanner类

4.1 概述

Scanner类可帮助读取格式化数据文件,它使用正则表达式定位所需数据,并使用解析器将其转换为已知类型。例如,它能使用本地化数字解析来读取人类可能输入的值。

4.2 使用方法

Scanner类有两种主要使用方法,且可以根据需要合理混合使用:
- 读取值流 :直接要求Scanner返回流中的下一个值。
- 面向行读取 :逐行处理输入。

4.2.1 读取值流

以下是一个使用Scanner读取值流的示例代码:

static double sumStream(Readable source) throws IOException {
    Scanner in = new Scanner(source);
    double result = 0.0;
    while (in.hasNext()) {
        if (in.hasNextDouble())
            result += in.nextDouble();
        else
            in.next();
    }
    IOException ex = in.ioException();
    if (ex != null)
        throw ex;
    return result;
}

操作步骤如下:
1. 创建Scanner对象并传入可读源。
2. 初始化结果变量。
3. 使用 hasNext() 方法检查是否还有下一个标记。
4. 使用 hasNextDouble() 方法检查下一个标记是否为双精度值,如果是则读取并累加。
5. 若不是双精度值,则使用 next() 方法读取并忽略。
6. 检查是否有 IOException ,若有则抛出。
7. 返回结果。

Scanner还支持扫描值到 java.math.BigInteger java.math.BigDecimal 对象。同时,它的许多方法可以接受一个模式来指示如何匹配输入,有字符串和 java.util.regex.Pattern 两种重载形式。

4.2.2 面向行读取

以下是一个面向行读取逗号分隔值(CSV)表格的示例代码:

static final int CELLS = 4;
public static List<String[]> readCSVTable(Readable source)
    throws IOException {
    Scanner in = new Scanner(source);
    List<String[]> vals = new ArrayList<String[]>();
    String exp = "^(.*),(.*),(.*),(.*)";
    Pattern pat = Pattern.compile(exp, Pattern.MULTILINE);
    while (in.hasNextLine()) {
        String line = in.findInLine(pat);
        if (line != null) {
            String[] cells = new String[CELLS];
            MatchResult match = in.match();
            for (int i = 0; i < CELLS; i++)
                cells[i] = match.group(i+1);
            vals.add(cells);
            in.nextLine();  // skip newline
        }
        else {
            throw new IOException("input format error");
        }
    }
    IOException ex = in.ioException();
    if (ex!= null)
        throw ex;
    return vals;
}

操作步骤如下:
1. 创建Scanner对象并传入可读源。
2. 初始化存储结果的列表。
3. 编译正则表达式模式。
4. 使用 hasNextLine() 方法检查是否还有下一行。
5. 使用 findInLine() 方法查找匹配的行。
6. 若找到匹配行,使用 match() 方法获取匹配结果,并提取每个单元格的值。
7. 将提取的单元格值添加到结果列表中,并使用 nextLine() 方法跳过换行符。
8. 若未找到匹配行,抛出 IOException
9. 检查是否有 IOException ,若有则抛出。
10. 返回结果列表。

4.3 Scanner与StreamTokenizer对比

Scanner和StreamTokenizer在功能上有一些重叠,但操作模式不同:
| 对比项 | Scanner | StreamTokenizer |
| — | — | — |
| 匹配基础 | 正则表达式 | 字符类 |
| 灵活性 | 可根据正则表达式匹配标记,但处理某些简单任务可能较困难 | 基本逐字符处理输入,可控制字符类,但灵活性不如Scanner |
| 处理注释 | 需要显式处理,较复杂 | 内置处理注释行的能力 |
| 处理CSV文件 | 更改分隔符较简单 | 需要仔细操作字符类,概念上较奇怪 |
| 处理灵活格式文件 | 处理灵活格式较困难 | 适合处理自由格式文件 |

4.4 示例代码

以下是一个处理注释的示例代码:

Scanner in = new Scanner(source);
Pattern COMMENT = Pattern.compile("#.*");
String comment;
// ...
while (in.hasNext()) {
    if (in.hasNext(COMMENT)) {
        comment = in.findWithinHorizon(COMMENT, 0);
        in.nextLine();
    }
    else {
        // process other tokens
    }
}

操作步骤如下:
1. 创建Scanner对象并传入可读源。
2. 编译注释正则表达式模式。
3. 使用 hasNext() 方法检查是否还有下一个标记。
4. 使用 hasNext(COMMENT) 方法检查下一个标记是否为注释。
5. 若为注释,使用 findWithinHorizon() 方法查找并跳过注释,然后使用 nextLine() 方法跳过换行符。
6. 若不是注释,处理其他标记。

4.5 流程图

graph TD;
    A[创建Scanner对象] --> B[选择读取方式];
    B --> C{读取值流?};
    C -- 是 --> D[读取下一个值];
    D --> E{是否为所需类型?};
    E -- 是 --> F[处理值];
    E -- 否 --> D;
    C -- 否 --> G[读取下一行];
    G --> H{是否匹配模式?};
    H -- 是 --> I[处理匹配行];
    H -- 否 --> J[抛出异常];
    F --> K{还有下一个值?};
    K -- 是 --> D;
    K -- 否 --> L[结束读取];
    I --> M{还有下一行?};
    M -- 是 --> G;
    M -- 否 --> L;

4.6 本地化支持

Scanner类默认在当前区域设置下工作,但可以使用 useLocale() 方法更改区域设置,并使用 locale() 方法获取当前使用的区域设置。类型特定的数字扫描方法(如 hasNextDouble nextInt )支持本地化,能识别特定区域设置的数字格式。

5. 总结

本文详细介绍了Java中的BitSet、Observer/Observable、Random和Scanner等实用类和机制。BitSet类用于处理位向量,可进行位操作和逻辑操作;Observer/Observable机制提供了一种观察者模式,用于监视对象的更改和事件;Random类用于生成伪随机数;Scanner类可帮助读取格式化数据文件,支持多种使用方法和本地化。通过学习这些类和机制,可以提高Java编程的效率和灵活性。在实际应用中,应根据具体需求选择合适的类和方法,并注意处理可能出现的异常。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值