Objects类位于Java.util包,这个类与Object类的名称很相像,Java语言定义Objects类是为了让程序员能够以更加合理的方式操作对象。Objects类中定义的方法很多,这些方法可以分为两大类,分别是防止空指针异常的方法和数据验证的方法。
11.7.1防止空指针异常的方法
实际开发过程中,如果程序员没有及时判断某个对象是否为空,这有可能导致在调用该对象的方法时出现空指针异常而致使程序停止运行,例如:
Object o = null;
System.out.println(o.toString());
以上代码中,o对象调用toString ()方法把自身转换为一个字符串。由于o是一个空对象,因此在执行toString()方法时会出现空指针异常从而导致程序无法继续运行。Objects类中提供了很多方法,这些方法与Object类中的部分方法有相同的执行效果,但这些方法能够在空对象出现的情况下采取特殊的处理方式,而不是抛出空指针异常。这些不会导致空指针异常的方法就属于防止空指针的方法。下面的表11-7展示了Objects类中所定义的防止空指针异常的方法。
表11-7 Objects类中防止空指针的方法
方法 | 作用 |
boolean equals(Object a, Object b) | 判断a、b两个对象是否相同 |
int compare(T a, T b, Comparator<? super T> c) | 按规则比较a、b两个对象的先后关系 |
boolean deepEquals(Object a, Object b) | 深度比较a、b两个对象是否相同 |
int hashCode(Object o) | 获得对象o的哈希码 |
int hash(Object... values) | 以多个对象为参数生成哈希码 |
String toString(Object o) | 把对象o转换为字符串 |
String toString(Object o, String nullDefault) | 把对象o转换为字符串,o为null返回nullDefault |
下面的【例11_22】展示了Objects类防止空指针方法的运行效果。
【例11_22 Objects类防止空指针方法的运行效果】
Exam11_22.java
import java.util.Objects;
public class Exam11_22 {
public static void main(String[] args) {
Object o = new Object();
System.out.println("空对象与非空对象比较1:"+Objects.equals(o, null));
System.out.println("空对象与非空对象比较2:"+Objects.equals(null, o));
System.out.println("非空对象的哈希码1:"+Objects.hashCode(o));
System.out.println("非空对象的哈希码2:"+o.hashCode());
System.out.println("空对象的哈希码:"+Objects.hashCode(null));
System.out.println("非空对象转为字符串1:"+Objects.toString(o));
System.out.println("非空对象转为字符串2:"+o.toString());
System.out.println("空对象转为字符串默认值:"+Objects.toString(null));
System.out.println("设定空对象转为字符串的值:"+Objects.toString(null, "对象为空"));
}
}
【例11_22】中,对非空对象o求哈希码以及转换为字符串的操作都采用了两种方式完成。一种是调用Objects类所定义的静态方法完成,而另一种是调用Object类所定义的方法完成。【例11_22】的运行结果如图11-22所示。
图11-22【例11_22】运行结果
从图11-22可以看出:对于非空对象求哈希码以及转换为字符串的操作,使用Objects类所定义的方法和使用Object类所定义的方法完成都有相同的效果。而对于空对象而言,求哈希码以及转换字符串的操作不会出现空指针异常,而是采取了特殊的处理方式。在默认情况下,以数字0作为空对象的哈希码,而以“null”当做空对象转换为字符串的结果。如果程序员不希望以“null”当做空对象转换为字符串的结果,还可以自行设置,例如语句①就把空对象转换为字符串的结果设置为“对象为空”。而调用Objects类的equals()方法对两个对象进行比较时,无论是否有空对象,也无论空对象作为方法的第几个参数都不会出现空指针异常。
Objects类中除了提供有不产生空指针异常的equals()、hashCode()和toString()方法外,还提供了一个用于比较数组元素的deepEquals()方法,这个方法能够深入数组内部比较两个数组的每一对元素是否对应相同。下面的【例11_23】展示了deepEquals()方法的运行效果。
【例11_23 deepEquals()方法的使用】
Exam11_23.java
import java.util.Objects;
public class Exam11_23 {
public static void main(String[] args) {
String[] s1 = new String[]{"1","2","3"};
String[] s2 = new String[]{"1","2","3"};
System.out.println("使用equals()方法比较s1与s2:"+ Objects.equals(s1,s2));
System.out.println("使用deepEquals()方法比较s1与s2:"+ Objects.deepEquals(s1,s2));
System.out.println("使用equals()方法比较s1[0]与s2[0]:"+ Objects.equals(s1[0],s2[0]));
System.out.print("使用deepEquals()方法比较s1[0]与s2[0]:");
System.out.println(Objects.deepEquals(s1[0],s2[0]));
}
}
本例中创建了两个元素完全相同的String类数组s1和s2,之后分别用equals()方法和deepEquals()方法比较s1和s2以及s1和s2的首个元素。【例11_23】的运行结果如图11-23所示。
图11-23【例11_23】运行结果
从图11-23可以看出:使用equals()方法比较数组s1和s2的结果为false,这是因为数组也是对象,equals()方法在比较数组时只判断这两个数组是不是同一个对象,并不会深入到数组内部去比较每一对元素是否对应相同。而deepEquals()方法则不同,它会深入数组内部依次比较对应位置上的两个元素是否都相同,因此deepEquals()方法比较s1和s2的结果为true。如果被比较的不是数组而是普通对象,则equals()方法和deepEquals()方法的运行效果完全相同。本例中分别使用equals()方法和deepEquals()比较s1和s2的首个元素,比较的结果都是true。
本小节没有讲解表11-7中的compare()和hash()方法,compare()方法需要用到Comparator接口作为参数,因此这个方法将在11.9小节中讲解,而hash()方法将在集合相关章节中讲解。
11.7.2数据验证的方法
数据验证的方法能够验证数据是否合理,这些方法并不仅仅能够验证出数据是否合理,并且还能检验出数据在不合理的情况下会产生怎样的异常。下面的表11-8展示会了Objects类中所定义的数据验证方法。
表11-8 Objects类中参数验证的方法
方法 | 作用 |
int checkFromIndexSize(int fromIndex, int size, int length) | 判断区间[fromIndex, fromIndex + size)是否在[0,length)范围内 |
int checkFromToIndex(int fromIndex, int toIndex, int length) | 判断区间[fromIndex,toIndex)是否在 [0,length)范围内 |
int checkIndex(int index, int length) | 判断index是否在[0,length)范围内 |
boolean isNull(Object obj) | 判断obj是否为空 |
boolean nonNull(Object obj) | 判断obj是否不为空 |
<T> T requireNonNull(T obj) | 若obj为空则抛空指针异常 |
<T> T requireNonNull(T obj, String message) | 若obj为空则抛空指针异常,message为程序员自定义的异常信息 |
<T> T requireNonNullElse(T obj, T defaultObj) | obj不为空则返回obj,obj为空返回defaultObj,二者均为空抛出空指针异常 |
<T> T requireNonNullElseGet(T obj, Supplier supplier) | obj不为空则返回obj,obj为空返回supplier所提供的对象 |
这些数据验证的方法常常用来检验方法的实际参数是否合理。请看下面的【例11_24】
【例11_24检验数据合理性】
Exam11_24.java
import java.util.Objects;
public class Exam11_24 {
public static void main(String[] args) {
String str1 = null;
String str2 = "ABCDE";
int[] array = {1,2,3,4,5};
//检验str1是否为空
Objects.requireNonNull(str1);
str1.charAt(2);//①
//检验2和8是否是截取字符串str2的合理参数
Objects.checkFromToIndex(2,8,str2.length());
str2.substring(2,8);//②
//检验10是否是数组array的合理下标
Objects.checkIndex(10,array.length);
int a = array[10];//③
}
}
【例11_24】的语句①调用了str1的charAt()方法,在调用这个方法之前先检验了str1是否为空对象。语句②调用str2的substring()方法截取字符串,在调用方法前先检验了参数2和8是否在合理范围内。语句③取出数组array下标为10的元素,在取出之前也检验了下标值是否合理。通过观察程序不难发现,语句①会因str1为空而抛出空指针异常,而语句②和③都会因参数数值不合理导致抛出数组越界异常。而这三条语句之前的检验就会提前发现数据的不合理性,并且以抛出异常的形式通知程序员语句①、②、③运行不会成功。虽然在不检验数据合理性的情况下语句①、②、③也会抛出同样的异常,但这样的检验也是有意义的,这是因为在语句①、②、③真正执行前就已经发现了数据的不合理性,这样就不会造成关键代码运行产生错误的结果。需要提醒各位读者:【例11_24】的程序代码中能够造成3个异常,但因为一旦抛出异常程序就会中止运行,所以实际运行程序时只会出现1个异常。
本文字版教程还配有更详细的视频讲解,小伙伴们可以点击这里观看。