对JVM平台来说,java.lang包中的类至关重要,因为这个包中的很多类我们都经常用到,如:
- Object类(java.lang.Object);
- String类(java.lang.String);
- 基本数据类型包装类(java.lang包中的Integer、Long、Short、Char、Float和Double);
- 异常和错误(java.lang.Exception和java.lang.Error)。
下图是这些类之间的层级关系:
Object类
java.lang包中的Object类是其他所有类的基类;在JVM中,只有它没有父类。在Java语言中,没有显式地继承其他类的类都隐式地继承java.lang.Object类。
- Object类的重要方法
java.lang.Object类中最常用的方法如下表所示
方法名 | 返回类型 | 描述 |
---|---|---|
toString() | String | 返回对象的文本描述 |
equals(Object object) | boolean | 指出传入的对象是否与当前对象相等,它使用多种规则来判断相等性 |
hashCode() | int | 计算当前对象的散列值 |
- Object(以及每个JVM对象)提供的重要方法之一是toString(),它返回当前对象实例的文本描述。
- Oracle提供的默认实现是返回全限定类名以及十六进制格式的hashCode()结果,但建议在自定义类中重写这个方法,以提供更易于理解的描述。
- 集合API大量地使用了方法equals()和hashCode()。
- 有关java.lang.Object类的完整方法列表,请参阅API文档。
String类(java.lang.String)
java.lang.String类表示JVM中的字符串。这是一种不可修改的对象,这意味着修改String对象时不会影响原始对象,而是生成一个包含修改后内容的新字符串。字符串在内部都是使用UTF-16编码存储的。
基本类型包装器类(java.lang包中的Integer、Long、Short、Char、Float、Double类)
并非所有的Java API都能够使用JVM的内置基本数据类型。如果需要的是基本类型包装类,而你传递的是基本数据类型变量,编译器将自动创建指定包装类的实例。反之亦然,即在需要的是基本数据类型变量,而你传递的是包装类对象时,编译器将把包装类对象的值赋给基本数据类型变量。这个过程被称为自动装箱(autoboxing)。
- 自动装箱示例
下面的代码将一个基本类型int值赋给一个java.lang.Integer引用变量:
int primitiveInt = 42;
Integer wrappedInteger = primitiveInt;
这将创建一个包装了int值42的Integer对象,与显式地创建一个Integer实例等效:
Integer wrappedInteger = new Integer(42);
下面的代码将两个Integer实例传递给一个将两个int值作为参数的API,结果符合预期:
System.out.println("Hello world".substring(new Integer(0),
new Integer(5)));
这将打印Hello。
异常和错误(java.lang.Exception和java.lang.Error)
JVM开发人员必须知道JVM是如何管理运行阶段错误的。鉴于所有的语言都有自己的运行阶段错误处理机制,这里不介绍错误是如何处理的,而只说说发生运行阶段错误时的情况。
在方法中发生运行阶段错误时,将创建并引发一个异常或Error对象。在Java语言中,这是使用关键字throw实现的。下面是一个引发通用异常Exception的示例:
throw new Exception("Oops!");
Java有很多继承Exception或Error类的内置类。创建自定义异常类时,应考虑可重用哪个既有的异常类。例如,如果方法要求传入的引用不能为空,应在传入的参数为空时引发异常java.lang.NullPointerException;所有Java API在用户将空引用传递给不支持它的方法时都这样做。
注意:如果你仔细看了文章开头的类层级关系图,将发现Exception和Error都继承了Throwable类。Throwable是可引发的对象,但通常使用Exception和Error的子类,因为它们使用起来更方便。
- Exception和Error类的不同之处如下。
在程序很可能能够妥善地处理错误并继续运行时引发异常。
发现根本没有想到的问题时引发错误。很多错误都是由JVM本身引发的。
异常或错误(以下简称异常)被引发时,JVM将查看引发异常的方法。如果它包含能够处理错误的错误处理程序,就把控制权交给错误处理程序。如果这个方法不能处理任何错误(或当前错误),就检查方法调用方是否包含错误处理程序。这个过程将不断重复,直到找到能够处理当前错误且不引发新错误的方法,或者进入第一个方法调用。在第二种情况下,JVM实例将崩溃并生成类似于下面的栈跟踪:
Exception in thread "main" java.lang.Exception: Oops
at ExceptionDemo.method3(ExceptionDemo.java:37)
at ExceptionDemo.method2(ExceptionDemo.java:33)
at ExceptionDemo.method1(ExceptionDemo.java:29)
at ExceptionDemo.main(ExceptionDemo.java:25)
很多语言都在生成的Java字节码中包含源代码文件名和行号,这样可以在栈跟踪中包含源代码行号,让栈跟踪更容易理解。
Java有严格的异常引发规则,因此并非每个类都能够引发所有的异常;