zl程序教程

您现在的位置是:首页 >  其它

当前栏目

Objects, Immutability, and Switch Expressions 40-48

and 40 switch Objects 48 Expressions
2023-06-13 09:13:55 时间

本文为《Java Coding Problems》40-48题,问题涉及Objects, Immutability, and Switch Expressions (共18题)。

40. 函数式和过程式的判空

问题:null的判断。

思路:使用object != null,或者Objects.isNullObjects.nonNull

代码如下:

// 判断列表中是否有null (过程式)
public boolean hasNull(List<Integer> integerList) {
        if (integerList == null) {
            return false;
        }

        for (Integer integer : integerList) {
            if (integer == null) {
                return true;
            }
        }

        return false;
    }
}

// 判断列表中是否有null (函数式)
public boolean hasNull(List<Integer> integerList) {
    return Objects.nonNull(integerList) &&
            integerList.stream()
                .anyMatch(Objects::isNull);
}

41. 检查null引用,并抛出自定义NullPointerException

问题:检查null引用,并抛出自定义NullPointerException

思路:使用if检查,使用throw new NullPointerException抛出异常,或者使用Objects.requireNonNull

代码如下:

public void nullTest() {
    String name = null;
    if (name == null) {
        throw new NullPointerException("Name cannot be null!");
    }
    // 等价于if (name == null) throw new ...
    Objects.requireNonNull(name, "Name cannot be null!");
}

42. 检查null引用,并抛出特定异常

问题:检查null引用,并抛出特定异常。

思路:使用if检查,然后抛出异常。

43. 检查null引用,并设置默认值

问题:检查null饮用,并在为null的时候设置默认值。

思路:使用if检查,并设置默认值,或使用Objects.requireNonNullElseGet

代码如下:

public void nullTestSetDefaultValue() {
    String name = null;
    if (name == null) {
        name = "default name";
    }
    // 等价于if (name == null) name = ...
    name = Objects.requireNonNullElse(name, "default name");
}

44. 检查索引范围是否在[0, length)范围内

问题:检查给定索引是否在[0, length)范围内。

思路:使用Objects.checkIndex检查(JDK9)。

45. 检查索引段是否在[0, length)范围内

问题:检查索引段是否在[0, length)范围内。

思路:使用Objects.checkFromToIndex检查(JDK9)。

46. 说明equalshashCode如何工作

问题:说明equalshashCode在java中如何工作

思路:说明。

用途说明:

equalshashCode是每个类都有的方法。Equals用于比较对象,hashCode用于生成类的hash值。

默认实现:

equals默认实现是检查两个对象是否是同一个(内存地址相同),内部使用==实现。hashCode默认实现返回对象的内存地址。

重写实现

equals实现需要满足5个性质:

  • 自反性:对象总是等于自身。 p1.equals(p1)总是返回true
  • 对称性:p1.equals(p2)p2.equals(p1)结果相同。
  • 传递性:如果p1.equals(p2)p2.equals(p3),那么p1.equals(p3)
  • 一致性:两个对象如果相等,那么只要两个对象没改动,他们会一直相等。
  • 不等于Null: 所有对象与null,不相等。 代码如下:
@Override
public boolean equals(Object obj) {
    if (this == obj) {
        // 自反性
        return true;
    }

    if (obj == null) {
        // 不等于Null
        return false;
    }

    if (getClass() != obj.getClass()) {
        // 类型相同
        return false;
    }
    
    // 其他属性比较
    // ...

    return true;
}

hashCode实现需要满足:

  • 两个相等的对象,必须返回相同的hash code。
  • 只要对象不发生改变,必须返回相同的hash code。 因此:
  • 当重写equals方法,必须重写hashCode方法。
  • 在两个方法中必须使用相同的属性集。

47. 解释不可变对象

问题:解释不可变对象。

思路:说明。

  • 基本类型是不可变的。
  • 一些常用类,比如StringPatternLocalDate是不可变的。
  • 数组是不可变的。
  • Collections可以是可变的,不可修改的,或不可变的。 不可变对象在多线程环境中常用,因为它不会引起并发中的一些常见问题。

48. 不可变字符串

问题:字符串是不可变的,这样设计有什么好处?

思路:说明。

不可变的优势

字符串常量池:

在创建字符串常量时,所有的字符串常量会被保存在字符串常量池中,代码如下:

public static void internString() {
    String t1 = "Hello";
    String t2 = "Hello";
    String t3 = "Hello";

    if (t1 == t2 && t2 == t3) {
        // t1, t2, t3指向常量池中的同一个对象
        System.out.println("All String points to same memory address");
    }
    String t4 = new String("Hello");
    if (t3 != t4) {
        // 通过new String创建的对象在堆中
        System.out.println("Not interned");
    }

    t4 = t4.intern();
    if (t3 == t4) {
        // 手动调用intern产生一个指向常量池对象的引用
        System.out.println("Interned");
    }
}

安全:

字符串不可变可以防止很多攻击。在使用Class.forName时不会遭受String改变的问题。

线程安全:

字符串可以在多个线程中共享,是线程安全的。

hash code缓存:

可以缓存hash code。

不可变的缺点

无法继承

无法通过继承为String添加更多功能。

安全问题

如果通过String保存敏感数据,那这些数据可能长时间存在于字符串常量池中,存在安全隐患。

内存溢出

字符串常量池不大,可能导致内存溢出错误。