类
1 | class Box |
this
Java定义了this关键字,可以在任何方法中使用this引用当前对象。也就是说,this总是引用调用方法的对象。可以在允许使用当前类对象引用的任何地方使用this。
1 | class Box |
finalize
有时,对象销毁时需要执行一些动作。例如,如果对象包含一些非Java资源,比如文件句柄或字符字体,那么我们可能希望确保这些资源在对象销毁之前释放。为了处理这种情况,Java提供了一种称为“终极”(finalization)的机制。通过使用终结机制,可以定义当前对象即将被垃圾回收器回收时发生的特定动作。
为了给类添加终结器(finalizer),可以简单地定义finalize()方法。当即将回收类的对象时,Java运行时会调用该方法。在finalize()方法内部,可以指定在销毁对象之前必须执行的那些动作。垃圾回收器周期性地运行,检查那些不再被任何运行时状态或通过其他引用对象间接引用的对象。在释放资源之前,Java运行时为对象调用finalize()方法。
只会在即将进行垃圾回收之前调用finalize()方法,理解这一点很重要。例如,当对象超出其作用域时不会调用该方法。这意味着不知道什么时候会执行finalize()方法,甚至也不知道是否会执行finalize()方法,这与C++的析构函数是不一样的。所以,程序应当提供释放对象所使用的系统资源等内容的一些其他方法。对于常规的程序操作而言,不应该依赖于finalize()方法。
1 | protected void finalize() |
static
有时可能希望定义能够独立于类的所有对象进行使用的成员。在正常的情况下,只有通过组合类的对象才能访问类的成员。但是,也可以创建能够由类本身使用的成员,而不需要通过对特定实例的引用。为了创建这种成员,需要在成员声明的前面使用关键字static。如果成员被声明为静态的,就可以在创建类的任何对象之前访问该成员,并且不需要使用任何对象引用。方法和变量都可以声明为静态的。main()方法是最常见的静态成员的例子。main()方法被声明为静态的,因为需要在创建所有对象之前调用该方法。
被声明为静态的实例变量,在本质上是全局变量。当声明类的对象时,不会生成静态变量的副本。相反,类的所有实例共享相同的静态变量。
声明为静态的方法有几个限制:
- 它们只能直接调用其他静态方法
- 它们只能直接方法静态数据
- 它们不能以任何方式引用this或super关键字
1 | class Math |
继承
使用extends关键字将类的定义集成到另一个类。
1 | class A |
super
有时希望创建只有自己猜知道实现细节的超类,对于这种情况,子类就不能直接访问或初始化这些变量。当子类需要引用它的直接超类时,都可以使用关键字super。
super有两种用法:
- 用于调用超类的构造函数
- 用于访问超类中被子类的某个成员隐藏的成员
1 | class Box |
final
在继承的时候,使用final可以禁止方法的重写以及阻止类被继承。
将方法声明为final,有时可以提高性能:编译器可以自由地内联对这类方法的调用,因为编译器知道这些方法不能被子类重写,当调用小的final方法的时,Java编译器通常可以复制子例程的字节码,直接和调用方法的编译代码内联到一起,从而可以消除方法调用所需要的开销。内联是final方法才有的一个选项。通常,Java在运行时动态分析对方法的调用,这称为后期绑定。但是,因为final方法不能被重写,所有对final方法的调用可以在编译时解析,这称为早期绑定。
重载方法
在同一个类中定义两个或多个共享相同名称的方法,只要它们的参数声明不同即可。
1 | class Test |
方法重写
在类层次中,如果子类的一个方法和超类的某个方法具有相同的名称和类型签名,那么称子类中的方法重写了超类中相应的那个方法。当在子类调用被重写的方法时,总是调用由子类定义的版本,由超类定义的版本会被隐藏。
如果希望访问超类中重写的方法,可以通过super完成该操作。
重写方法为Java支持运行时多态奠定了基础。多态是面向对象编程的本质特征之一,理由之一是:多态允许一般类规定对所有继承类都通用的方法,并且允许子类定义所有这些方法或其中部分方法的特定实现。重现方法是Java实现“一个接口,多种方法”这一多态特性的另一种方式。
成功应用多态的部分关键是,要理解正是超类和子类形成了具体化成都从更少到更多的层次。使用正确的话,超类提供了子类可以直接使用的所有元素,另外还定义了派生类必须实现的方法。这允许子类可以灵活地定义自己的方法,但是仍然强制使用一致的接口。因此通过联合使用继承和重新方法这两个特性,超类可以定义被所有子类使用的方法的一般形式。
动态的、运行时多态是面向对象设计实现代码重用和健壮性的最强大机制之一。现有代码库能够调用新类实例的方法而不需要重新编译,同时还能保持清晰的抽象接口,这种能力是一种非常强大的工具。
1 | class Box |
抽象类
在某些情况下,希望定义这样一种超类,声明已知抽象内容的接口,而不提供每个方法的完整实现,这些细节的实现由子类来自己处理。
可以通过abstract类型修饰符,要求特定的方法必须由子类重写。这些方法有时被称作子类责任(subclass responsibility)。
任何包含一个或多个抽象方法的类都必须被声明为抽象的。为了声明抽象类,只需要简单地在类声明的开头、在class关键字的前面使用关键字abstract。
1 | abstract class Box |
可变长度参数
可变长度参数通过三个句点(…)表示。语法“…”只是告诉编译器将要使用可变长度参数,并且将这些参数存到指定的数组里面。
使用可变长度参数也可以具有“常规”参数,但是,可变长度参数必须是方法最后声明的参数。对于抽象类不存在对象,不能使用new运算符直接实例化抽象类。
1 | public static void TestVarargs(int a, int b, String c, int ...vals) |
Object
有一个特殊的类,即Object,该类是由Java定义的。所有其他类都是Object的子类。也就是说,Object是所有其他类的超类。这意味着Object类型的引用变量可以引用任何其他类的对象。此外,因为数组也是作为类实现的,所以Object类型的变量也可以引用任何数组。
- Object类的方法
方法 | 用途 |
---|---|
Object clone() | 创建一个和将要复制的对象完全相同的新对象 |
boolean equals(Object object) | 判断一个对象是否和另外一个对象相等 |
void finalize() | 在回收不再使用的对象之前调用 |
Class<?>getClass() | 在运行时获取对象所属的类 |
int hashCode() | 返回与调用对象相关联的散列值 |
void notify() | 恢复执行在调用对象上等待的某个线程 |
void notifyAll() | 恢复执行在调用对象上等待的所有线程 |
String toString() | 返回一个描述对象的字符串 |
void wait() | 等待另一个线程的执行 |
void wait(long milliseconds) | 等待另一个线程的执行 |
void wait(long milliseconds, int nanoseconds) | 等待另一个线程的执行 |
如果尝试为没有实现Cloneable接口的类调用clone()方法,会抛出CloneNotSupportedException异常。当进行复制时,不会调用被复制对象的构造函数。复制品是原来对象的精确副本。
Class
Class类封装了类或接口的运行时状态。Class类型的对象是加载类时自动创建的,不能显式地声明Class对象。通常,通过Object类定义的getClass()方法来获取Class对象。Class是泛型类。
- Class类定义的方法
方法 | 描述 |
---|---|
static Class<?> forName(String name) | 返回给定全名的Class对象 |
static Class<?> forName(String name, boolean how, ClassLoader ldr) | 返回给定全名的Class对象。对象使用由ldr指定的加载器加载。如果how为true,就初始化对象;否则不进行初始化 |
A getAnnotation(Class annoType) | 返回一个Annotation对象,该对象包含与调用对象的annoType相关联的注解 |
Annotation[] getAnnotations() | 获得与调用对象关联的所有注解,并将它们存储在元素为Annotation对象的数组中。返回对这个数组的引用 |
A[] getAnnotationsByType(Class annoType) | 返回一个与调用对象关联的annoType注解数组(包含重复注解)(JDK8新增) |
Class<?>[] getClasses() | 为调用对象所属类的所有共有类和接口成员都返回一个Class对象 |
ClassLoader getClassLoader() | 返回加载类或接口的ClassLoader对象 |
Constructor |
返回一个Constructor对象,表示调用对象所属类的构造函数,调用对象拥有paramTypes指定的参数类型 |
Constructor<?>[] getConstructors() | 为调用对象所属类的每个共有构造函数都获取一个Constructor对象,并将它们存储在数组中。返回对这个数组的引用 |
Annotation[] getDeclaredAnnotations() | 为调用对象声明的所有注解都获取一个Annotation对象,并将它们存储在数组中。返回对这个数组的引用(忽略继承的注解) |
A[] getDeclaredAnnotationsByType(Class annoType) | 返回一个与调用对象关联的非继承的annoType注解数组(包括宠物注解)(JDK8新增) |
Constructor<?>[] getDeclaredConstructors() | 为调用对象所属类声明的每个构造函数获取一个Constructor对象,并将它们存储在数组中。返回对这个数组的引用(忽略超类的构造函数) |
Field[] getDeclaredFields() | 为调用对象所属类或接口声明的每个域变量都获取一个Field对象,并将它们存储在数组中。返回对这个数组的引用(忽略继承的域变量) |
Method[] getDeclaredMethods() | 为调用对象所属类或接口声明的每个方法都获取一个Method对象,并将它们存储在数组中。返回对这个数组的引用(忽略继承的方法) |
Field getField(String fieldName) | 返回一个Field对象,该对象表示在调用对象所属类或接口中由fieldName指定的共有域变量 |
Field[] getFields() | 为调用对象所属类或接口的所有共有域变量获取一个Field对象,并将它们存储在数组中。返回对这个数组的引用 |
Class<?>[] getInterfaces() | 该方法返回一个数组。如果调用对象表示一个类,那么返回该数组的元素为这个类实现的接口;如果调用对象表示一个接口,那么返回数组的元素为这个接口扩展的接口 |
Method getMethod(String methName, Class<?> …paramTypes) | 返回一个Method对象,该对象表示调用对象所属类或接口中的一个方法,该方法的名称为methName,并且参数类型由paramTypes指定 |
Method[] getMehod() | 为调用对象所属类或接口中的每个公有方法获取一个Method对象,并将它们存储在数组中。返回对这个数组的引用 |
String getName() | 返回调用对象所属类或接口的全名 |
ProtectionDomain getProtectionDomain() | 返回与调用对象关联的保护域 |
Class<? super T> getSuperclass() | 返回调用对象所属类型的超类。如果调用对象代表的类型是Object或者不是类,就返回null |
boolean isInterface() | 如果调用对象的类型是接口,就返回true;否则返回false |
T newInstance() | 创建调用对象所属类型的一个实例(也就是一个新的对象)。这相当于为new运算符使用类的默认构造函数。返回这个新的对象。如果调用对象代码的类型是抽象的、不是类或者没有默认构造函数,那么该方法是把 |
String toString() | 返回调用对象所属类型或接口的字符串表示形式 |
ClassLoader
抽象类ClassLoader定义了加载类的方法。应用程序可以创建扩展了ClassLoader类的子类,实现其中的方法。通过扩展ClassLoader类,可以采用某些其他方式加载类,而不是采用Java运行时系统的常规方式。但是,在正常情况下不需要这么做。