文章目录
API
方法说明
指一些已经预先定义好的,暴露给外界使用的方法或者工具
Object成员方法
隐式继承
getClass方法
方法的声明
public final native Class<?> getClass();
说明:
-
首先它的访问权限修饰符是
public
,可以任意访问,没有访问权限问题。 -
final
修饰它,表示它无法被重写。 -
native
表示该方法是一个本地方法,指的是Java调用其它语言(主要是C/C++)的实现来完成功能的方法。本地方法不需要方法体,我们也不会考虑它的实现细节。(该方法的作用可以通过查阅API文档了解) -
Class<?>是返回值类型,表示该方法需要返回一个Class对象。
上述<?>表示泛型,关于泛型后面会详细讲。
作用:通过一个本地方法的实现,去获取Class对象。
Class对象
具体来说,某个类的运行时类对象,就是该类的Class对象
。
某个类的Class对象当中,包含了该类的所有类型信息(比如类名是啥,有哪些方法、变量、构造器等)
于是程序员在程序的运行时期,只需要获取该类的Class对象,就能够获取类型信息了。
作用
getClass()
方法只是获取Class对象,该方法不负责创建Class对象。- 某个类的类加载在一次程序运行过程中,仅有一次。
所以某个类的运行时类对象(Class对象)也必然是唯一的!!! - 可以判断两个引用所指向的对象的类型是否相同
toString方法
源码:
public String toString{}{
getClass().getName() + '@' + Integer.toHexString(hashCode());
}
说明:
getClass().getName()
:全类名
Integer.toHexString(hashCode())
:十六进制地址值
作用:
返回该对象的字符串表示。
重写toString的方法:
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
注意事项:
-
toString()
方法可以快速自动生成,仍然使用IDEA快捷键Alt + Insert
完成。 -
toString()
方法,普遍来说就是为了完成打印成员变量取值的,不要在里面写一些奇怪的代码。 -
如果类中有(自定义)引用数据类型成员变量,也需要重写它的
toString
方法,不然就会打印地址值了。
equals方法
源码:
public boolean equals(Object obj) {
return (this == obj);
}
说明:
参数需要传入一个对象
默认实现:判断当前这个对象是否跟参数对象一样
只有当两个引用完全指向同一个对象时,方法才会返回true,否则都会返回false。
重写equals方法
equals 方法在非空对象引用上实现相等关系:
- 自反性:对于任何非空引用值 x,x.equals(x) 都应返回 true。
- 对称性:对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才应返回 true。
- 传递性:对于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true,并且 y.equals(z) 返回 true,那么 x.equals(z) 应返回 true。
- 一致性:对于任何非空引用值 x 和 y,多次调用 x.equals(y) 始终返回 true 或始终返回 false,前提是对象上 equals 比较中所用的信息没有被修改。
- 排他性:当比对的不是同种类型的对象或者是一个null时,默认返回false
@Override
public boolean equals(Object o) {
// 自反性
if (this == o) return true;
// 排他性
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
// 比较成员变量的值
if (age != student.age) return false;
return name != null ? name.equals(student.name) : student.name == null;
}
重写equals方法的注意事项
-
在实现排他性时,实际上有两种选择:
-
使用
getClass
方法比较。 这个比较是比较苛刻的,只有在完全是同一个类型时才会返回true -
使用
instanceof
比较。
引用 instanceof 类名
表示判断引用所指向的对象的具体类型,是否是后面类名的对象或者子类对象,如果是就返回true,如果不 是就返回false。
这个比较的条件就比较宽松了,可以允许传入子类对象。(当子类对象的父类成员和父类对象相同时,equals方法仍然返回true)
@Override public boolean equals(Object o) { // 自反性 if (this == o) return true; // 排他性 //if (o == null || getClass() != o.getClass()) return false; if(o == null) return false; // 类型扩大,可以判断子类 if(!(o instanceof Student)) return false; Student student = (Student) o; // 比较成员变量的值 if (age != student.age) return false; return name != null ? name.equals(student.name) : student.name == null; }
-
-
equals方法也是可以用快捷键自动生成的,使用快捷键
alt + insert
。而且可以选择在实现排它性时的方式。 -
浮点数比较特殊,它具有规格化和非规格化的区别,还有非数(NaN),无穷大,无穷小很多特殊的概念,正常情况下,如果仅仅比较数值,用==比较相等是够用的。但为了避免因浮点数特殊值,而出现的错误。实际开发中,从严谨角度出发,浮点数的比较仍然建议使用,对应包装类型的
compare
方法去比较浮点数的大小:Float.compare(float a,float b)
Double.compare(double a,doublet b)
这两个方法在,a < b时返回-1(负数),在a>b时,返回1(正数),只有在两个浮点数相等时,才会返回0
-
如果类中有引用数据类型成员变量,需要去调用它们的equals方法完成比较。这就意味着还需要重写这个类的equals方法。
-
财务金额上的运算是不推荐使用浮点数的,会出现精度问题。推荐使用
BigDecimal
这个类完成运算。
eg:
// 不会出现精度问题
public static void main(String[] args) {
BigDecimal b1 = new BigDecimal("1.0");
BigDecimal b2 = new BigDecimal("0.9");
BigDecimal sub = b1.subtract(b2);
System.out.println(sub);
BigDecimal addNumber = b1.add(b2);
System.out.println(addNumber);
}
hashCode方法
映射不要求元素一一对应,允许出现多对一,但绝不允许一对多。
方法的声明
public native int hashCode();
很显然,它是一个本地方法,这个方法也没有任何参数,返回值是一个int类型整数。
方法的作用
- 返回该对象的哈希码值。
- 支持此方法是为了提高哈希表(例如
java.util.Hashtable
提供的哈希表)的性能。
默认实现
实际上,由 Object
类定义的 hashCode
方法确实会针对不同的对象返回不同的整数。
方法的重写
-
在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是将对象进行 equals比较时所用的信息没有被修改。
-
如果根据
equals(Object)
方法,两个对象是相等的,那么对这两个对象中的每个对象调用hashCode方法都必须生成相同的整数结果。 -
如果根据
equals(java.lang.Object)
方法,两个对象不相等,那么对这两个对象中的任一对象上调用hashCode方法不要求一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。 -
hashCode
方法和equals
方法是息息相关的,要重写必须一起重写
。- 一起重写后,必须符合equals方法返回true,那么这两个对象的哈希值必须相同的规则。
- 这是因为equals是判断对象相等的,对象相等后,就相当于是上述映射定义中,集合A中的同一个元素,那么它们哈希映射的结果必然相同。这是映射定义强制要求的。
- 哈希冲突是允许存在(不允许也不可能)的,但是我们要清楚,设计出更好的哈希算法,尽量避免哈希冲突,可以提升哈希表性能。
如何重写
使用快捷键Alt + Insert
为什么要同时重写hashCode和equals方法
首先hashCode
方法中规定:
- 两个对象相等,哈希值一定相等。
- 两个对象不相等,哈希值不要求完全不相等。
那么这里就很明显存在一个问题:
如何判断对象相等? ----> 答:依赖类中的equals
方法!
finalize方法
方法的声明
protected void finalize() throws Throwable { }
说明:
-
首先注意访问权限是protected,这说明在不重写访问权限的情况下,仅能够在自身类中创建自身对象,才能调用这个方法。
-
其次它的方法体是空的,功能没有实现,这种设计的目的是为了让子类自己去重写该方法的实现。这种做法比起抽象方法的强制子类实现,要更加灵活,而且可以用在普通类中。
-
最后,它的方法声明中有一个
throws Throwable
, 这是方法抛出异常列表的声明。 -
Java当中的
finalize
方法就是"模仿"析构函数设计的,finalize方法也会在对象销毁时自动被调用,
测试finalize方法
public class Demo {
public static void main(String[] args) {
// 匿名对象,理论上很快成为垃圾对象
new Student();
// 通知GC进行垃圾回收
System.gc();
}
}
class Student{
@Override
protected void finalize() throws Throwable {
System.out.println("模拟释放资源");
}
}
在Java中,所有的资源释放,都必须依赖程序员手动完成,所以以后大家做I/O操作,网络操作,数据库操作等需要占用系统资源的操作时,一定不要忘记在用完后,释放系统资源!
clone方法
方法的声明
clone方法的方法声明为:
protected native Object clone() throws CloneNotSupportedException;
这里需要注意的地方有:
-
首先注意访问权限,它的访问权限是
protected
。这意味着:- 一般来说,只能在子类当中,创建子类自身对象才能够调用该方法(方法调用位置,肯定不是同包)。
- 让一个类自身克隆自身,一般都没有多大意义,所以建议在子类中重写方法访问权限。
-
它是一个本地
native
方法,没有方法体。(依赖本地方法实现创建对象,不同于new对象) -
返回值类型是
Object
。这里,也建议在子类方法中重写这个返回值类型。
-
throws CloneNotSupportedException
是方法抛出异常的声明,
方法的作用
Object
类当中的clone
方法默认实现,就是得到一个独立的,和原先对象成员一致的新对象。
使用clone方法的步骤
- 重写
clone
方法,修改权限 - 把返回值类型修改成自身类型
- 要求实现
clone
方法的类,实现cloneable
接口
cloneable
是一个空接口,起到标记的作用
像Cloneable这种没有声明定义任何成员的,一个空接口,它其实就起到一个标记的作用,称之为"标记接口"。
浅克隆
概念:
如果类中有引用数据类型的成员变量,那么clone方法的使用就要格外注意了:
- Java当中,Object类的clone方法的默认实现是完全直接拷贝一份成员变量。
- 对于基本数据类型的成员变量来说,没有任何问题,直接拷贝值。
- 但对于引用数据类型而言,拷贝的是引用。这意味着克隆后的引用和原先的引用指向同一个对象。
- 这样的话,使用任何一个引用去修改对象的状态,都会互相影响,这样的两个对象就不是完全独立的了。
像以上Object类当中的clone方法的实现,直接拷贝一份成员变量,不管引用数据类型成员变量引用,所指向的对象。我们称之为"浅克隆"。
代码演示:
@Override
protected User clone() throws CloneNotSupportedException{
return (User)super.clone();
}
深克隆
概念:
如果能够让引用数据类型成员变量之间也能相互独立,克隆后获取真正独立的两个对象。我们称之为"深度克隆"。
- 将引用指向的对象,也克隆一份。
- 然后让克隆后的引用指向它。
代码演示:
@Override
protected User clone() throws CloneNotSupportedException{
// 1. 深克隆在浅拷贝的基础上进行修改
User cloneUser = (User)super.clone();
// 2. 把Address这个引用再clone一份
Address cloneAddress = this.address.clone();
// 3.赋值给克隆出的User对象的address,就是将拷贝引用指向拷贝对象
cloneUser.address = cloneAddress;
return cloneUser;
}