Java问答:终极父类(3),java问答Java问答:终极父类(上)

Java问答:终极父类(下)

Java问答:终极父类(2)—上篇

Java问答:终极父类(2)—下篇

在之前关于 Object 的系列文章中,我们探讨了 finalize()、getClass()和hashCode()方法。在这篇文章中,我们将在Java8环境中继续讨论 toString() 方法,等待/唤醒方法和 Object 中的接口。

字符串形式的表现

Q1:toString() 方法实现了什么功能?

A1:toString() 方法将根据调用它的对象返回其对象的字符串形式,通常用于debug。

Q2:当 toString() 方法没有被覆盖的时候,返回的字符串通常是什么样子的?

A2:当 toString() 没有被覆盖的时候,返回的字符串格式是 类名@哈希值,哈希值是十六进制的。举例说,假设有一个 Employee 类,toString() 方法返回的结果可能是 Empoyee@1c7b0f4d。

Q3:能提供一个正确覆盖 toString() 方法的例子吗?

A3:见代码清单1:

public class Employee

{

private String name;

private int age;

public Employee(String name, int age)

{

this.name = name;

this.age = age;

}

@Override

public String toString()

{

return name + ": " + age;

}

}

代码清单1:返回一个非默认的字符串形式

代码清单1声明了 Employee 类,被私有修饰符修饰的 name 和 age 变量,构造器将其初始化。该类覆盖了 toString() 方法,并返回一个包含对象值和一个冒号的 String 对象。

字符串和 StringBuilder

当编译器遇到 name + ": " + age 的表达时,会生成一个 java.lang.StringBuilder 对象,并调用 append() 方法来对字符串添加变量值和分隔符。最后调用 toString() 方法返回一个包含各个元素的字符串对象。

Q4:如何得到字符串的表达形式?

A4:根据对象的引用,调用引用的 toString() 。例如,假设 emp 包含了一个 Employee 引用,调用 emp.toString() 就会得到这个对象的字符串形式。

Q5:System.out.println(o.toString()); 和 System.out.println(o) 的区别是什么?

A5:System.out.println(o.toString()); 和 System.out.println(o) 两者的输出结果中都包含了对象的字符串形式。区别是,System.out.println(o.toString()); 直接调用toString() 方法,而System.out.println(o) 则是隐式调用了 toString()。

等待和唤醒

Q6:wait(),notify() 和 notifyAll() 是用来干什么的?

A6:wait(),notify() 和 notifyAll() 可以让线程协调完成一项任务。例如,一个线程生产,另一个线程消费。生产线程不能在前一产品被消费之前运行,而应该等待前一个被生产出来的产品被消费之后才被唤醒,进行生产。同理,消费线程也不能在生产线程之前运行,即不能消费不存在的产品。所以,应该等待生产线程执行一个之后才执行。利用这些方法,就可以实现这些线程之间的协调。从本质上说,一个线程等待某种状态(例如一个产品被生产),另一个线程正在执行,知道产生了某种状态(例如生产了一个产品)。

Q7:不同的 wait() 方法之间有什么区别?

A7:没有参数的 wait() 方法被调用之后,线程就会一直处于睡眠状态,直到本对象(就是 wait() 被调用的那个对象)调用 notify() 或 notifyAll() 方法。相应的wait(long timeout)和wait(long timeout, int nanos)方法中,当等待时间结束或者被唤醒时(无论哪一个先发生)将会结束等待。

Q8:notify() 和 notifyAll() 方法有什么区别?

A8:notify() 方法随机唤醒一个等待的线程,而 notifyAll() 方法将唤醒所有在等待的线程。

Q9:线程被唤醒之后会发生什么?

A9:当一个线程被唤醒之后,除非本对象(调用 notify() 或 notifyAll() 的对象)的同步锁被释放,否则不会立即执行。唤醒的线程会按照规则和其他线程竞争同步锁,得到锁的线程将执行。所以notifyAll()方法执行之后,可能会有一个线程立即运行,也可能所有的线程都没运行。

Q10:为什么在使用等待、唤醒方法时,要放在同步代码中?

A10::将等待和唤醒方法放在同步代码中是非常必要的,这样做是为了避免竞争条件。鉴于要等待的线程通常在调用wait()之前会确认一种情况存在与否(通常是检查某一变量的值),而另一线程在调用notify()`之前通常会设置某种情况(通常是通过设置一个变量的值)。以下这种情况引发了竞争条件:

线程一检查了情况和变量,发现需要等待。

线程二设置了变量。

线程二调用了notify()。此时,线程一还没有等待,所以这次调用什么用都没有。

线程一调用了wait()。这下它永远不会被唤醒了。

Q11:如果在同步代码之外使用这些方法会怎么样呢?

A11:如果在同步代码之外使用了这些情况,就会抛出java.lang.IllegalMonitorStateException异常。

Q12:如果在同步代码中调用这些方法呢?

A12:当 wait() 方法在同步代码中被调用时,会根据同步代码中方法的优先级先后执行。在wait()方法返回值之前,该同步代码一直持有锁,这样就不会出现竞争条件了。在wait()方法可以接受唤醒之前,锁一直不会释放。

Q13:为什么要把wait()调用放在while循环中,而不是if判断中呢?

A13:为了防止假唤醒,可以在 stackoverflow上了解有关这类现象的更多信息——假唤醒真的会发生吗?。

Q14:能提供一个使用等待与唤醒方法的范例吗?

A14:见代码清单2:

public class WaitNotifyDemo

{

public static void main(String[] args)

{

class Shared

{

private String msg;

synchronized void send(String msg)

{

while (this.msg != null)

try

{

wait();

}

catch (InterruptedException ie)

{

}

this.msg = msg;

notify();

}

synchronized String receive()

{

while (msg == null)

try

{

wait();

}

catch (InterruptedException ie)

{

}

String temp = msg;

msg = null;

notify();

return temp;

}

}

final Shared shared = new Shared();

Runnable rsender;

rsender = new Runnable()

{

@Override

public void run()

{

for (int i = 0; i < 10; i++)

{

shared.send("A"+i);

try

{

Thread.sleep((int)(Math.random()*200));

}

catch (InterruptedException ie)

{

}

}

shared.send("done");

}

};

Thread sender = new Thread(rsender);

Runnable rreceiver;

rreceiver = new Runnable()

{

@Override

public void run()

{

String msg;

while (!(msg = shared.receive()).equals("done"))

{

System.out.println(msg);

try

{

Thread.sleep((int)(Math.random()*200));

}

catch (InterruptedException ie)

{

}

}

}

};

Thread receiver = new Thread(rreceiver);

sender.start();

receiver.start();

}

}

代码清单2:发送与接收信息

代码清单2声明了一个WaitNotifyDemo类。其中,main()方法有一对发送和接收信息的线程。

main()方法首先声明了Shard本地类,包含接收和发送信息的任务。Share声明了一个String类型的smg私有成员变量来存储要发送的信息,同时声明了同步的 send()和receive()方法来执行接收和发送动作。

发送线程调用的是send()。因为上一次调用send()的信息可能还没有被接收到,所以这个方法首先要通过计算this.msg != null的值来判断信息发送状态。如果返回值为true,那么信息处于被等待发送的状态,就会调用 wait() 。一旦信息被接收到,接受的线程就会给msg赋值为null并存储新信息,调用notify()唤醒等待的线程。

接收线程调用的是receive()因为可能没有信息处于被接收状态,这个方法首先会通过计算mas == null的值来验证信息有没有等待被接收的状态。如果表达式返回值为true,就表示没有信息等待被接收,此线程就要调用 wait() 方法。如果有信息发送,发送线程就会给 msg 分配值并且调用notify()唤醒接收线程。

编译(javac WaitNotifyDemo.java)并运行(java WaitNotifyDemo)源代码,将会看到以下输出结果:

A0

A1

A2

A3

A4

A5

A6

A7

A8

A9

Q15:我想更加深入的学习等待和唤醒的机制,能提供一些资源吗?

A15:可以在artima参考Bill Venners的书《Inside the Java Virtual Machine(深入理解 Java 虚拟机)》中第20章 wait().artima.com/insidejvm/ed2/threadsynch.html">Chapter 20: Thread Synchronization。

Object接口和Java8

Q16:在第一部分中提到过接口是不继承Object的。然而,我发现有些接口中声明了Object中的方法。比如java.util.Comparator接口有boolean.equals(Object.obj)。为什么呢?

A16:Java语言规范的9.6.3.4部分中清楚说明了,接口有相当于 Object 中成员那样的公共抽象成员。此外,如果接口中声明了Object中的成员函数(例如,声明的函数相当于覆盖 Object中的public方法),则认为是接口覆盖了他们,可以用 @Override注释。

为什么要在接口中声明非final的public Object方法(可能还带有 @Override)呢?举例来说,Comparator接口中就有boolean equals(Object obj)声明,这个方法在接口中声明就是为了此接口的特殊情况。

此外,这个方法只有在传入的类是一个比较规则相同的比较器的时候,才能返回 true。

因为这种情况是可选的,所以并不强制实现 Comparator。这取决于有没有equals,只有在遇到一个比较规则相同的比较器的时候才返回true的需求。尽管类并不要求覆盖equals,但是文档中却支持这样做来提高性能。

注意,不覆盖 Object.equals(Object)是安全的。但是,覆盖这个方发可能在一些情况下提高性能,比如让程序判断两个不同的比较器是不是用的相同规则。

Q17:哪一个equals()方法被覆盖了?是Object中的,还是接口中的?

A17:更早的文档中说,被覆盖的方法是在Object中的。

Q18:Java 8支持接口中的默认方法。可以在接口中默认实现Employee方法或者Object中的其他方法吗?

A18:不可以。Object中的任何public的非final方法都是不允许在接口中默认实现的。这个限制的基本原理在Brian Goetz的允许默认方法覆盖Object中的方法一文中有说明。

Q19:能提供更多关于接口中 Object 方法的学习资源吗?

A19:可以参考这篇接口继承了Object类吗?。

原文链接: javaworld 翻译: Wld5.com - 赖 信涛

译文链接: http://www.wld5.com/13256.html

[ 转载请保留原文出处、译者和译文链接。]

java 终极超类,Java问答:终极父类(3),java问答相关推荐

  1. 115个Java面试题和答案——终极列表(上)

    转载自  115个Java面试题和答案--终极列表(上) 本文我们将要讨论Java面试中的各种不同类型的面试题,它们可以让雇主测试应聘者的Java和通用的面向对象编程的能力.下面的章节分为上下两篇,第 ...

  2. 115个Java面试题和答案——终极列表(下)

    转载自   115个Java面试题和答案--终极列表(下) 第一篇讨论了面向对象编程和它的特点,关于Java和它的功能的常见问题,Java的集合类,垃圾收集器,本章主要讨论异常处理,Java小应用程序 ...

  3. Github下载热榜,阿里最新出品Java面试核心讲(终极版)

    程序员面试背八股,可以说是现在互联网开发岗招聘不可逆的形式了,其中最卷的当属Java!(网上动不动就是成千上百道的面试题总结)你要是都能啃下来,平时技术不是太差的话,面试基本上问题就不会太大. 这时候 ...

  4. 115 个 Java 面试题和答案——终极重点(下)

    题目:115 个 Java 面试题和答案--终极(下) 第一篇讨论了面向对象编程和它的特点,关于 Java 和它的功能的常见问题,Java 的集合类,垃圾收集器,本章主要讨论异常处理,Java 小应用 ...

  5. Java 8的新特性—终极版

    前言: Java 8 已经发布很久了,很多报道表明Java 8 是一次重大的版本升级.在Java Code Geeks上已经有很多介绍Java 8新特性的文章,例如Playing with Java ...

  6. 115个Java面试题和答案——终极列表

    开始! 目录 面向对象编程(OOP) 常见的Java问题 Java线程 Java集合类 垃圾收集器 面向对象编程(OOP) Java是一个支持并发.基于类和面向对象的计算机编程语言.下面列出了面向对象 ...

  7. 【三万粉丝终极福利】Python、C、Java三大语言学习路线和资源整理

    大家好,我是辣条. 今天给大家带来三万粉丝三大语言学习路线和资源整理,收藏就对了. 目录 C语言 学习路线 学习书籍 学习视频 资源推荐 Java语言 学习路线 学习书籍 学习视频 资源推荐 Pyth ...

  8. 关于Spring的69个面试问答和Top25个问答--终极列表,以及Spring相关知识

    Spring 框架越来越流行,很多大型系统用到,了解Spring的面试题有助简要短时间于了解对应的知识, 关于Spring的69个面试问答--终极列表 http://www.importnew.com ...

  9. 转:Java中子类是否可以继承父类的static变量和方法而呈现多态特性

    原文地址:Java中子类是否可以继承父类的static变量和方法而呈现多态特性 静态方法 通常,在一个类中定义一个方法为static,那就是说,无需本类的对象即可调用此方法,关于static方法,声明 ...

最新文章

  1. css控制非固定文本自动换行
  2. python3.7安装turtle步骤-Python3安装turtle问题
  3. ASP.NET 定时执行任务(定时器)
  4. cocos2dx集成友盟社会化分享图片崩溃问题
  5. 關於@Override
  6. 操作系统 | 用户态和内核态的切换(中断、系统调用与过程(库函数)调用)
  7. 什么是 NoSQL 数据库、NoSQL 与 SQL 的区别
  8. laravel 向模板中添加公共变量
  9. modem(2)---Android modem log查看
  10. 使用 android adb命令 录像 screenrecord 和 截屏 screencap
  11. 概率图模型和马尔可夫模型
  12. 激光导航AGV为何如此受企业青睐?
  13. 女孩假扮大学生跪地乞讨月入万元
  14. word2vec:基于层级 softmax 和负采样的 Skip-Gram
  15. Ai-WB1系列驱动4.0寸电阻触摸屏运行LVGL v8.3
  16. 新旧电脑安装win11系统【超简单教程】
  17. Oracle读书列表
  18. 房产行业php_phpwind房产系统打响社区垂直行业应用
  19. 台式电脑计算机硬盘清理,台式机的硬盘不够用怎么办 解决台式机电脑硬盘
  20. java提示程序包不存在,解决:Error:java xxxx 程序包不存在

热门文章

  1. git for c#, clone方法
  2. 浅谈Android系统开发中LOG的使用【转】
  3. 2010.2--netscreen ssg 140 恢复出厂设置的方法
  4. sqlmap绕过d盾_WEBSHELL免杀绕过WAF思路amp;方法(一)
  5. 兵团教师计算机水平考试免考条件,兵团职称计算机考试政策.doc
  6. android listview edittext 焦点冲突,Android开发之ListView+EditText-要命的焦点和软键盘问题解决办法...
  7. 简单介绍oracle重置序列的方法
  8. Python中scrapy下载保存图片
  9. 计算机ppt继续教育,泰州市专业技术人员继续教育公需科目PPT2010试卷及答案
  10. java别踩白块_java别踩白块(基础功能)