Java的Object是所有引用类型的父类,定义的方法按照用途可以分为以下几种:

(1)构造函数

(2)hashCode() 和 equals() 函数用来判断对象是否相同

(3)wait()、wait(long)、wait(long,int)、notify()、notifyAll() 线程等待和唤醒

(4)toString()

(5)getClass() 获取运行时类型

(5)clone()

(6)finalize() 用于在垃圾回收。

这些方法经常会被问题到,所以需要记得。

由这几类方法涉及到的知识点非常多,我们现在总结一下根据这几个方法涉及的面试题。

1、对象的克隆涉及到的相关面试题目

涉及到的方法就是clone()。克隆就是为了快速构造一个和已有对象相同的副本。如果克隆对象,一般需要先创建一个对象,然后将原对象中的数据导入到新创建的对象中去,而不用根据已有对象进行手动赋值操作。

任何克隆的过程最终都将到达java.lang.Object 的clone()方法,而其在Object接口中定义如下

protected native Object clone() throws CloneNotSupportedException;

在面试中需要分清深克隆与浅克隆。克隆就是复制一个对象的复本。但一个对象中可能有基本数据类型,也同时含有引用类型。克隆后得到的新对象的基本类型的值修改了,原对象的值不会改变,这种适合shadow clone(浅克隆)。

如果你要改变一个非基本类型的值时,原对象的值却改变了,比如一个数组,内存中只copy地址,而这个地址指向的值并没有 copy。当clone时,两个地址指向了一个值。一旦这个值改变了,原来的值当然也变了,因为他们共用一个值。这就必须得用deep clone(深克隆)。举个例子如下:

public class ShadowClone implements Cloneable {

	private int a; // 基本类型
private String b; // 引用类型
private int[] c; // 引用类型
// 重写Object.clone()方法,并把protected改为public @Override
public Object clone() {
ShadowClone sc = null;
try {
sc = (ShadowClone) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return sc;
} public int getA() {
return a;
} public void setA(int a) {
this.a = a;
} public String getB() {
return b;
} public void setB(String b) {
this.b = b;
} public int[] getC() {
return c;
} public void setC(int[] c) {
this.c = c;
} public static void main(String[] args) throws CloneNotSupportedException{
ShadowClone c1 = new ShadowClone();
//对c1赋值
c1.setA(50) ;
c1.setB("test1");
c1.setC(new int[]{100}) ; System.out.println("克隆前c1: a="+c1.getA()+" b="+c1.getB()+" c="+c1.getC()[0]); ShadowClone c2 = (ShadowClone) c1.clone();
c2.setA(100) ;
c2.setB("test2");
int []c = c2.getC() ;
c[0]=500 ;
System.out.println("克隆前c1: a="+c1.getA()+ " b="+c1.getB()+" c[0]="+c1.getC()[0]);
System.out.println("克隆后c2: a="+c2.getA()+ " b="+c2.getB()+" c[0]="+c2.getC()[0]);
}
}

运行后打印如下信息:

克隆前c1:  a=50  b=test1 c=100
克隆后c1: a=50 b=test1 c[0]=500
克隆后c2: a=100 b=test2 c[0]=500

c1与c2对象中的c数组的第1个元素都变为了500。需要要实现相互不影响,必须进行深copy,也就是对引用对象也调用clone()方法,如下实现深copy:

@Override
public Object clone() {
ShadowClone sc = null;
try {
sc = (ShadowClone) super.clone();
sc.setC(b.clone());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return sc;
}

这样就不会相互影响了。 

另外需要注意,对于引用类型来说,并没有在clone()方法中调用b.clone()方法来实现b对象的复制,但是仍然没有相互影响,这是由于Java中的字符串不可改变。就是在调用c1.clone()方法时,有两个指向同一字符串test1对象的引用,当调用c2.setB("test2")语句时,c2中的b指向了自己的字符串test2,所以就不会相互影响了。

2、hashCode()和equals()相关面试题目

equals()方法定义在Object类内并进行了简单的实现,如下:

public boolean equals(Object obj) {
return (this == obj);
}

比较两个原始类型比较的是内容,而如果比较引用类型的话,可以看到是通过==符号来比较的,所以比较的是引用地址,如果要自定义比较规则的话,可以覆写自己的equals()方法。 String 、Math、还有Integer、Double等封装类重写了Object中的equals()方法,让它不再简单的比较引用,而是比较对象所表示的实际内容。其实就是自定义我们实际想要比较的东西。比如说,班主任要比较两个学生Stu1和Stu2的成绩,那么需要重写Student类的equals()方法,在equals()方法中只进行简单的成绩比较即可,如果成绩相等,就返回true,这就是此时班主任眼中的相等。
首先来看第1道面试题目,手写equals()方法,在手写时需要注意以下几点:

当我们自己要重写equals()方法进行内容的比较时,可以遵守以下几点:

(1)使用instanceof 操作符检查“实参是否为正确的类型”。
(2)对于类中的每一个“关键域”,检查实参中的域与当前对象中对应的域值。
  • 对于非float和double类型的原语类型域,使用==比较;
  • 对于float域,使用Float.floatToIntBits(afloat)转换为int,再使用==比较;
  • 对于double域,使用Double.doubleToLongBits(adouble) 转换为int,再使用==比较;
  • 对于对象引用域,递归调用equals()方法;
  • 对于数组域,调用Arrays.equals()方法。  

给一个字符串String实现的equals()实例,如下:

public boolean equals(Object anObject) {
if (this == anObject) { // 反射性
return true;
}
if (anObject instanceof String) { // 只有同类型的才能比较
String anotherString = (String) anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true; // 返回true时,表示长度相等,且字符序列中含有的字符相等
}
}
return false;
}

另外的高频面试题目就是equals()和hashCode()之间的相互关系。 

  • 如果两个对象是相等的,那么他们必须拥有一样的hashcode,这是第一个前提;
  • 如果两个对象有一样的hashcode,但仍不一定相等,因为还需要第二个要求,也就是equals()方法的判断。
我觉得如上2句的总结必须要有一个非常重要的前提,就是要在使用hashcode进行散列的前提下,否则谈不上equals()相等,hashcode一定相等这种说法。
对于使用hashcode的map来说,map判断对象的方法就是先判断hashcode是否相等,如果相等再判断equals方法是否返回true,只有同时满足两个条件,最后才会被认为是相等的。
Map查找元素比线性搜索更快,这是因为map利用hashkey去定位元素,这个定位查找的过程分成两步,内部原理中,map将对象存储在类似数组的数组的区域,所以要经过两个查找,先找到hashcode相等的,然后在再在其中按线性搜索使用equals方法,通过这2步来查找一个对象。 
另外还有在书写hashCode()方法时,为什么要用31这个数字? 例如String类的hashCode()的实现如下:
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value; for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}

循环中的每一步都对上一步的结果乘以一个系数31,选择这个数主要原因如下:

  • 奇数 乘法运算时信息不丢失;
  • 质数(质数又称为素数,是一个大于1的自然数,除了1和它自身外,不能被其他自然数整除的数叫做质数) 特性能够使得它和其他数相乘后得到的结果比其他方式更容易产成唯一性,也就是hashCode值的冲突概率最小;
  • 可优化为31 * i == (i << 5) - i,这样移位运算比乘法运算效率会高一些。

3、线程等待和唤醒相关面试题  

最常见的面试题就是sleep()与wait()方法的区别,这个问题很简单,调用sleep()方法不会释放锁,而调用wait()方法会阻塞当前线程并释放当前线程持有的锁。。
另外就是问wait()与notify()、notifyAll()方法相关的问题了,比如这几个方法为什么要定义在Object类中,一句话,因为Java中所有的对象都能当成锁,也就是监视器对象。
我们需要明白,调用这几个方法时,当前线程一定要持有锁,否则调用这几个方法会引起异常(也是一道面试题)。
有时候还需要书写生产者-消费者模式,我们就用wait()与notify()、notifyAll()方法写一个吧,如下:
// 仓库
class Godown {
public static final int max_size = 100; // 最大库存量
public int curnum; // 当前库存量 Godown(int curnum) {
this.curnum = curnum;
} // 生产指定数量的产品
public synchronized void produce(int neednum) {
while (neednum + curnum > max_size) {
try {
wait(); // 当前的生产线程等待,并让出锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 满足生产条件,则进行生产,这里简单的更改当前库存量
curnum += neednum;
System.out.println("已经生产了" + neednum + "个产品,现仓储量为" + curnum);
notifyAll(); // 唤醒在此对象监视器上等待的所有线程
} // 消费指定数量的产品
public synchronized void consume(int neednum) {
while (curnum < neednum) {
try {
wait(); // 当前的消费线程等待,并让出锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 满足消费条件,则进行消费,这里简单的更改当前库存量
curnum -= neednum;
System.out.println("已经消费了" + neednum + "个产品,现仓储量为" + curnum);
notifyAll(); // 唤醒在此对象监视器上等待的所有线程
}
}
在同步方法开始时都会测试,如果生产了过多或不够消费时,调用wait()方法阻塞当前线程并让锁。在同步方法最后都会调用notifyAll()方法,这算是给所有线程一个公平竞争锁的机会吧,他会唤醒在synchronized方法和wait()上阻塞等待的线程,因为他们都将当前对象做为锁对象。
 
 
 
 
 
 
 

Java面试系列第2篇-Object类中的方法的更多相关文章

  1. Java Object类中toString方法的重写

    Object类中的tostring方法的: 当我们输出一个对象时,实际是输出的是这个类中的tostring方法,是一个地址值,而不是类中的属性. 1 一:子类没有重写Object类中的toStrinn ...

  2. Java基础知识强化26:Object类之hashCode()方法、getClass()方法

    1. Object类的hashCode()方法,如下: public  int  hashCode():返回该对象的哈希码值,这个值和地址值有关,但是不是实际地址值(哈希码值是根据实际地址值转化过来的 ...

  3. Object类中通用方法之:toString()方法

    1.Java所有的对象都是Object类的实例,都可以直接调用该类中定义的方法,这些方法称为通用方法 2.toString()方法即为Object类中定义的通用方法之一 3.平时我们如果在控制台直接打 ...

  4. Java基础知识强化27:Object类之toString()方法

    1. Object类的toString()方法: public  String  toString():返回该对象的字符串表示 2. 案例演示: (1)Student类: package cn.itc ...

  5. Java面试系列第3篇-HashMap相关面试题

    HashMap是非线程安全的,如果想要用线程安全的map,可使用同步的HashTable或通过Collections.synchronizeMap(hashMap)让HashMap变的同步,或者使用并 ...

  6. Java深入研究【1、object类】

    一.概述Object类是所有Java类的祖先.每个类都使用 Object 作为超类.所有对象(包括数组)都实现这个类的方法. 参考英文:* Class {@code Object} is the ro ...

  7. 【Java深入研究】1、object类

    一.概述Object类是所有Java类的祖先.每个类都使用 Object 作为超类.所有对象(包括数组)都实现这个类的方法. 参考英文:* Class {@code Object} is the ro ...

  8. Java Object类的equals()方法

    所有类都从Object类中继承了equals方法,Object类中equals方法源代码如下: public boolean equals(Object obj)     {         retu ...

  9. object类的equals方法简介 & String类重写equals方法

    object类中equals方法源码如下所示 public boolean equals(Object obj) { return this == obj; } Object中的equals方法是直接 ...

随机推荐

  1. 深度学习中正则化技术概述(附Python代码)

    欢迎大家关注我们的网站和系列教程:http://www.tensorflownews.com/,学习更多的机器学习.深度学习的知识! 磐石 介绍 数据科学研究者们最常遇见的问题之一就是怎样避免过拟合. ...

  2. CVPR 2019细粒度图像分类竞赛中国团队DeepBlueAI获冠军 | 技术干货分享

    [导读]CVPR 2019细粒度图像分类workshop的挑战赛公布了最终结果:中国团队DeepBlueAI获得冠军.本文带来冠军团队解决方案的技术分享. 近日,在Kaggle上举办的CVPR 201 ...

  3. Redis在linux环境下的安装

    下载Redis安装包 wget http://download.redis.io/releases/redis-3.2.9.tar.gz 解压Redis安装包 tar -zxvf redis-3.2. ...

  4. Win10远程桌面发生身份验证错误,要求的函数不受支持

    昨儿个使用远程桌面,意外发的发现连不上测试环境了.身边的同事也有连不上的.一开始以为是远程机器可能出了问题,但是而后排查确认是自个儿机器问题.原因在与机器前天晚上自动升级了系统补丁,也有部分网友反映了 ...

  5. C#接口多继承方法重名问题

    最近实现一个功能需要继承两个接口,然而父类接口有这重名的方法,且方法实现一致.两个父接口均被多个子接口继承,并在类实例中实现.起初,我是通过new重名方法来实现我的功能调用.后被指正,在网上看了一个工 ...

  6. Spring中的设计模式:模板模式

    导读 模板模式在是Spring底层被广泛的应用,比如事务管理器的实现,JDBC模板的实现. 文章首发于作者的微信公众号[码猿技术专栏] 今天就来谈谈「什么是模板模式」.「模板模式的优缺点」.「模板模式 ...

  7. [bzoj1029]建筑抢修<贪心>

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1029 解析:这也算bzoj中比较简单的一道题,其实想通了就是非常的简单. 这题用贪心的方式 ...

  8. 小案例带你揭秘JS事件

    小案例带你揭秘JS事件 ### 什么是事件? 在js中一个事件的组成由那些呢? 谁触发事件:事件源 触发什么事件: 事件的类型 触发事件干什么事:事件处理函数 事件传播的过程 捕获阶段 就是从wind ...

  9. 文件输入输出实例&Ptask的编写

    前言 最近在写Ptask,顺便了解了如何进行文件读入输出.而在Ptask中最重要,也是最最容易出bug的地方就是文件操作.那么如何进行文件输入输出,在程序中起到重要作用呢? 输入 首先为了保证可以在控 ...

  10. Python第三方包之PrettyTable

    Python第三方包之PrettyTable 可以让我们将数据用表格的方式展示出来 安装方式 pip install PrettyTable 测试是否安装成功 使用方法与对比 增加一条数据 先简单的看 ...