理解Java-Object类

Java中的Object有哪些公用方法

1.clone方法

保护方法, 创建并返回此对象的一个副本, 用于实现对象的浅复制, 只有实现了Cloneable接口才可以调用该方法,否则抛出CloneNotSupportedException异常;
JAVA里除了8种基本类型传参数是值传递,其他的类对象传参数都是引用传递,我们有时候不希望在方法里将参数改变,这时就需要在类中复写clone方法.

2.getClass方法

final方法,返回一个对象的运行时类。

3.toString方法

返回该对象的字符串表示, 该方法用得比较多,一般子类都有覆盖。

4.finalize方法

该方法用于释放资源。当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。子类重写 finalize 方法,以配置系统资源或执行其他清除。

在启用某个对象的 finalize 方法后,将不会执行进一步操作,直到 Java 虚拟机再次确定尚未终止的任何线程无法再通过任何方法访问此对象,其中包括由准备终止的其他对象或类执行的可能操作,在执行该操作时,对象可能被丢弃。

对于任何给定对象,Java 虚拟机最多只调用一次 finalize 方法。

5.equals方法

该方法是非常重要的一个方法。一般equals和==是不一样的,但是在Object中两者是一样的。子类一般都要重写这个方法。

6.hashCode方法

该方法用于哈希查找,可以减少在查找中使用equals的次数,重写了equals方法一般都要重写hashCode方法。这个方法在一些具有哈希功能的Collection中用到。

一般必须满足obj1.equals(obj2)==true。可以推出obj1.hashCode()==obj2.hashCode(),但是hashCode相等不一定就满足equals。不过为了提高效率,应该尽量使上面两个条件接近等价。

如果不重写hashCode(),在HashSet中添加两个equals的对象,会将两个对象都加入进去。

7.wait方法

wait方法就是使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。wait()方法一直等待,直到获得锁或者被中断。wait(long timeout)设定一个超时间隔,如果在规定时间内没有获得锁就返回。

调用该方法后当前线程进入睡眠状态,直到以下事件发生。

(1)其他线程调用了该对象的notify方法。

(2)其他线程调用了该对象的notifyAll方法。

(3)其他线程调用了interrupt中断该线程。

(4)时间间隔到了。

此时该线程就可以被调度了,如果是被中断的话就抛出一个InterruptedException异常。

8.notify方法

该方法唤醒在该对象上等待的某个线程。

9.notifyAll方法

该方法唤醒在该对象上等待的所有线程。

面试延伸

Java是值传递还是引用传递

Java是指传递, 对于基本类型传递的是值, 对于引用类型传递的是指针的地址

  • 值传递:方法调用时,实际参数把它的值传递给对应的形式参数,方法执行中形式参数值的改变不影响实际参数的值。

  • 引用传递:也称为传地址, 方法调用时实际参数的引用(地址,而不是参数的值)被传递给方法中相对应的形式参数,在方法执行中对形式参数的操作实际上就是对实际参数的操作,方法执行中形式参数值的改变将会影响实际参数的值.

阐述final、finally、finalize的区别

  • final:修饰符(关键字)有三种用法:(1)修饰类:表示该类不能被继承;(2)修饰方法:表示方法不能被重写;(3)修饰变量:表示变量只能一次赋值以后值不能被修改(常量)

  • finally:通常放在try…catch…的后面构造总是执行代码块(try{}里的return语句,其后finally{}里的代码会方法返回给调用者前执行),这就意味着程序无论正常执行还是发生异常,这里的代码只要JVM不关闭都能执行,可以将释放外部资源的代码写在finally块中。

  • finalize:Object类中定义的方法,Java中允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作(如关闭连接、关闭文件)。这个方法一般不会显示的调用, 通常是由垃圾收集器在销毁对象时调用的,通过重写finalize()方法可以整理系统资源或者执行其他清理工作。

说明: http://www.iteye.com/topic/484934

equals与==的区别

  • == 比较的是变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,即是否是指相同一个对象。比较的是真正意义上的指针操作.

  • equals 比较的是两个对象的内容是否相等,由于所有的类都是继承自java.lang.Object类的,所以适用于所有对象,而equals()可以返回true或者false主要取决于重写equals方法的实现逻辑.

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) {
String a = new String("ab"); // a 为一个引用
String b = new String("ab"); // b为另一个引用,对象的内容一样
String aa = "ab"; // 放在常量池中
String bb = "ab"; // 从常量池中查找
if (aa == bb) // true
System.out.println("aa==bb");
if (a == b) // false,非同一对象
System.out.println("a==b");
if (a.equals(b)) // true
System.out.println("aEQb");
if (42 == 42.0) { // true
System.out.println("true");
}
}

对象值相同(x.equals(y)为true),但却可以有不同的hash值?

如果两个对象满足equals为true,既 x.equals(y)==true, 那么他的哈希码(hash code)必然相同.

如果两个对象的hashCode相同, 它们并不一定相同.

如何解决Hash冲突

通过构造性能良好的哈希函数,可以减少冲突,但一般不可能完全避免冲突,因此解决冲突是哈希法的另一个关键问题。创建哈希表和查找哈希表都会遇到冲突,两种情况下解决冲突的方法应该一致。下面以创建哈希表为例,说明解决冲突的方法。常用的解决冲突方法有以下四种:

开放定址法

又称为开放散列法

基本思想是: 当关键字key的哈希地址p=H(key)出现冲突时,以p为基础,产生另一个哈希地址p1,如果p1仍然冲突,再以p为基础,产生另一个哈希地址p2, …;直到找出一个不冲突的哈希地址pi ,将相应元素存入其中.

这种方法有一个通用的再散列函数形式: Hi = ( H(key) + di ) % m 其中 i=1,2,…,n

说明: H(key)为哈希函数, m为表长, di称为增量序列, 增量序列的取值方式不同,相应的再散列方式也不同, 主要有以下三种:

线性探测再散列

di i=1,2,3,…,m-1

这种方法的特点是: 冲突发生时,顺序查看表中下一单元,直到找出一个空单元或查遍全表。

二次探测再散列

di = 12,-12,22,-22,…,k2,-k2 (k<=m/2)

这种方法的特点是: 冲突发生时,在表的左右进行跳跃式探测,比较灵活。

伪随机探测再散列

di = 伪随机数序列.

示例说明

例如: 已知哈希表长度m=11,哈希函数为:H(key) = key % 11,则H(47)=3,H(26)=4,H(60)=5,假设下一个关键字为69,则H(69)=3,与47冲突.

用线性探测再散列处理冲突: 下一个哈希地址为H1=(3 + 1) % 11 = 4,仍然冲突,再找下一个哈希地址为H2= (3 + 2) % 11 = 5,还是冲突,继续找下一个哈希地址为H3=(3 + 3)% 11 = 6,此时不再冲突,将69填入5号单元。

用二次探测再散列处理冲突: 下一个哈希地址为H1=(3 + 12)% 11 = 4,仍然冲突,再找下一个哈希地址为H2= (3 - 12) % 11 = 2,此时不再冲突,将69填入2号单元。

用伪随机探测再散列处理冲突: 设伪随机数序列为: 2,5,9,……..,则下一个哈希地址为H1=(3 + 2)% 11 = 5,仍然冲突,再找下一个哈希地址为H2=(3 + 5)% 11 = 8,此时不再冲突,将69填入8号单元。

再哈希法

基本思想: 同时构造多个不同的哈希函数: Hi=RH1(key) i=1, 2, …, k

当哈希地址Hi=RH1(key)发生冲突时,再计算Hi=RH2(key)…, 直到冲突不再产生。这种方法不易产生聚集,但增加了计算时间.

链地址法

基本思想: 将所有哈希地址相同的记录都链接在同一单链表中; 并将单链表的头指针存在哈希表的第i个单元中,因而查找、插入和删除主要在同义词链中进行.

链地址法适用于经常进行插入和删除的情况.

建立公共溢出区

基本思想: 将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表

参考