Java提高学习之Object(5)
字符串形式的表现
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 = ; i < ; i++)
{
shared.send("A"+i);
try
{
Thread.sleep((int)(Math.random()*));
}
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()*));
}
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
)源代码,将会看到以下输出结果:
1
2
3
4
5
6
7
8
9
10
|
A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 |
Q15:我想更加深入的学习等待和唤醒的机制,能提供一些资源吗?
A15:可以在artima参考Bill Venners的书《Inside the Java Virtual Machine(深入理解 Java 虚拟机)》中第20章 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
中的方法一文中有说明。
Java提高学习之Object(5)的更多相关文章
- Java提高学习之Object类详解(1)
转自:http://www.importnew.com/10304.html 问:什么是Object类? 答:Object类存储在java.lang包中,是所有java类(Object类除外)的终极父 ...
- Java提高学习之Object(4)
哈希码 问: hashCode()方法是用来做什么的? 答: hashCode()方法返回给调用者此对象的哈希码(其值由一个hash函数计算得来).这个方法通常用在基于hash的集合类中,像java. ...
- Java提高学习之Object(3)
终止 问: finalize()方法是用来做什么的? 答: finalize()方法可以被子类对象所覆盖,然后作为一个终结者,当GC被调用的时候完成最后的清理工作(例如释放系统资源之类).这就是终止. ...
- Java提高学习之Object(2)
Equality 问:euqals()函数是用来做什么的? 答:equals()函数可以用来检查一个对象与调用这个equals()的这个对象是否相等. 问:为什么不用“==”运算符来判断两个对象是否相 ...
- java基础学习总结——Object类
一.Object类介绍
- 转载-java基础学习汇总
共2页: 1 2 下一页 Java制作证书的工具keytool用法总结 孤傲苍狼 2014-06-24 11:03 阅读:25751 评论:3 Java基础学习总结——Java对象的序列化和 ...
- Java提高篇(三二)-----List总结
前面LZ已经充分介绍了有关于List接口的大部分知识,如ArrayList.LinkedList.Vector.Stack,通过这几个知识点可以对List接口有了比较深的了解了.只有通过归纳总结的知识 ...
- java提高篇(四)-----理解java的三大特性之多态
面向对象编程有三大特性:封装.继承.多态. 封装隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据.对外界而已它的内部细节是隐藏的,暴露给外界的只是它的访问方法. 继承 ...
- Java提高篇(三二)-----List总结
前面LZ已经充分介绍了有关于List接口的大部分知识,如ArrayList.LinkedList.Vector.Stack,通过这几个知识点能够对List接口有了比較深的了解了.仅仅有通过归纳总结的知 ...
随机推荐
- python基础教程_学习笔记14:标准库:一些最爱——re
标准库:一些最爱 re re模块包括对正則表達式的支持,由于以前系统学习过正則表達式,所以基础内容略过,直接看python对于正則表達式的支持. 正則表達式的学习,见<Mastering Reg ...
- 如何确定Ubuntu下是否对某个CVE打了补丁
前些日子在月赛中,拿到了一台Ubuntu14.04的服务器,但并不是root权限,需要提权.我Google了一下,找到了CVE-2015-1318,CVE-2015-1328,CVE-2015 ...
- WorkFlow WF如何为一个集合赋值
今天刚刚开始学习WorkFlow.无奈WF网络上的学习资料实在太少. 刚刚学到Foreach控制流的使用,需要一个集合参数.经研究,静态赋值可以搞定.动态赋值还没. 首先添加一个List<int ...
- .Net 缓存依赖详解
缓存命名空间的讲解流程 16.1 System.Web.Caching简介 本节从缓存命名空间的总体简介和组成结构入手,从整体上对System.Web.Caching进行概述. 16.1.1 Sy ...
- 读书笔记--<<会说话的代码>>
三天看完一本书,说出来我都不信,不过我还真是史无前例的做到了, 现在分享一下我的收获,希望大家拍砖,共同讨论一下. <<会说话的代码>>一书是我们BJDP小组里的王洪亮老师的一 ...
- css层叠机制说明
css通过建立与文档的关联而实施效果.文档结构重要性不言而喻,对于建立良好的内容索引.提高可维护性.较好的可访问性:另,利于css选择器选择.继承机制. 概要地讲,层叠机制是处理对文档上应用样式时解决 ...
- python基础:列表生成式和生成器
列表生成式(List Comprehension) 列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式. 举个例子,要生成 list ...
- gridView行号的显示
我们在进行开发的时候,很多地方希望dataGridview或girdView显示行号,这里我来说一下两种的实现方法 在girdView中很简单很好实现,我在这里写一下代码,具体其他功能可以看其带的DE ...
- jQuery创建节点
注:摘自<锋利的jQuery(第二版)> 创建元素节点 例如要创建两个<li>元素节点,并且要把它们作为<ul>元素节点的子节点添加到DOM节点树上.完成这个任务需 ...
- listview及adapter
http://blog.csdn.net/shaojie519/article/details/6595720 http://blog.csdn.net/liuhe688/article/detail ...