最新在整体回归下 java 基础薄弱环节,以下为自己整理笔记,若有理解错误,请批评指正,谢谢。
java.lang.Object 为 java 所有类的基类,所以一般的类都可用重写或直接使用 Object 下方法,以下为逻辑结构图,没有画类图
(注: 以上绿色方法为 非 native 方法 粉色方法为 native 方法)
那么问题来了 :
本人理解: native 关键字标识的 java 方法为本地方法,底层是有 c/c++ 编写的程序编译后 dll 文件,java 加载 dll 文件后,
可用通过本地方法调用 dll 中函数,如有疑问可用参考 JNI 使用方式,看参考:http://blog.csdn.net/yangjiali014/article/details/1633017
以下为 Object 类对应 openjdk\jdk\src\share\native\java\lang\Object.c 的源码片段 1:
- 1 static JNINativeMethod methods[] = {
- 2 {
- "hashCode",
- "()I",
- (void * ) & JVM_IHashCode
- },
- 3 {
- "wait",
- "(J)V",
- (void * ) & JVM_MonitorWait
- },
- 4 {
- "notify",
- "()V",
- (void * ) & JVM_MonitorNotify
- },
- 5 {
- "notifyAll",
- "()V",
- (void * ) & JVM_MonitorNotifyAll
- },
- 6 {
- "clone",
- "()Ljava/lang/Object;",
- (void * ) & JVM_Clone
- },
- 7
- };
网友见解:http://stackoverflow.com/questions/4568972/what-is-a-native-object
本人理解:涉及调用系统及考虑性能,使用 c++ 或 c 实现更佳
网友见解:http://stackoverflow.com/questions/10578764/why-are-hashcode-and-getclass-native-methods
http://stackoverflow.com/questions/27224577/why-is-object-clone-native-in-java
现在按上图逐个方法讲讲理解:
java 代码 片段 2
- 1 private static native void registerNatives();
- 2 static {
- 3 registerNatives();
- 4
- }
对应 c++ 代码 片段 3
- 1 JNIEXPORT void JNICALL
- 2 Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls)
- 3 {
- 4 (*env)->RegisterNatives(env, cls,
- 5 methods, sizeof(methods)/sizeof(methods[0]));
- 6 }
object that represents the runtime class of this object.
- Class
来个简单例子 片段 4
- 1 package jdk;
- 2 3 public class ObjectTest {
- 4 5 public static void main(String[] args) {
- 6 A a = new B();
- 7 System.out.println(a.getClass());
- 8
- }
- 9
- }
- 10 11 class A {
- 12 static {
- 13 System.out.println("初始化a");
- 14
- }
- 15 16
- }
- 17 18 class B extends A {
- 19 static {
- 20 System.out.println("初始化b");
- 21
- }
- 22
- }
输出结果:
可以看到 getClass 返回的为 class B 非 A。
4、hashCode(): 返回一个整数的值依赖于内部表示堆上的对象的指针
方法说明:This is typically implemented by converting the internal address of the object into an integer, but this implementation technique is not required by the
一起看下 native 方法 位于 openjdk\hotspot\src\share\vm\prims\jvm.cpp 中
- 1 JVM_ENTRY(jint, JVM_IHashCode(JNIEnv* env, jobject handle))
- 2 JVMWrapper("JVM_IHashCode");
- 3 // as implemented in the classic virtual machine; return 0 if object is NULL
- 4 return handle == NULL ? 0 : ObjectSynchronizer::FastHashCode (THREAD, JNIHandles::resolve_non_null(handle)) ;
- 5 JVM_END
调用了 ObjectSynchronizer::FastHashCode 方法,位于 openjdk\hotspot\src\share\vm\runtime\synchronizer.cpp 中 第 530 行起
View Code
- 1 static inline intptr_t get_next_hash(Thread * Self, oop obj) {
- 2 intptr_t value = 0;
- 3
- if (hashCode == 0) {
- 4 // This form uses an unguarded global Park-Miller RNG,
- 5 // so it's possible for two threads to race and generate the same RNG.
- 6 // On MP system we'll have lots of RW access to a global, so the
- 7 // mechanism induces lots of coherency traffic.
- 8 value = os: :random();
- 9
- } else 10
- if (hashCode == 1) {
- 11 // This variation has the property of being stable (idempotent)
- 12 // between STW operations. This can be useful in some of the 1-0
- 13 // synchronization schemes.
- 14 intptr_t addrBits = intptr_t(obj) >> 3;
- 15 value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom;
- 16
- } else 17
- if (hashCode == 2) {
- 18 value = 1; // for sensitivity testing
- 19
- } else 20
- if (hashCode == 3) {
- 21 value = ++GVars.hcSequence;
- 22
- } else 23
- if (hashCode == 4) {
- 24 value = intptr_t(obj);
- 25
- } else {
- 26 // Marsaglia's xor-shift scheme with thread-specific state
- 27 // This is probably the best overall implementation -- we'll
- 28 // likely make this the default in future releases.
- 29 unsigned t = Self - >_hashStateX;
- 30 t ^= (t << 11);
- 31 Self - >_hashStateX = Self - >_hashStateY;
- 32 Self - >_hashStateY = Self - >_hashStateZ;
- 33 Self - >_hashStateZ = Self - >_hashStateW;
- 34 unsigned v = Self - >_hashStateW;
- 35 v = (v ^ (v >> 19)) ^ (t ^ (t >> 8));
- 36 Self - >_hashStateW = v;
- 37 value = v;
- 38
- }
- 39 40 value &= markOopDesc: :hash_mask;
- 41
- if (value == 0) value = 0xBAD;
- 42 assert(value != markOopDesc: :no_hash, "invariant");
- 43 TEVENT(hashCode: GENERATE);
- 44
- return value;
- 45
- }
- 46 //
- 47 intptr_t ObjectSynchronizer: :FastHashCode(Thread * Self, oop obj) {
- 48
- if (UseBiasedLocking) {
- 49 // NOTE: many places throughout the JVM do not expect a safepoint
- 50 // to be taken here, in particular most operations on perm gen
- 51 // objects. However, we only ever bias Java instances and all of
- 52 // the call sites of identity_hash that might revoke biases have
- 53 // been checked to make sure they can handle a safepoint. The
- 54 // added check of the bias pattern is to avoid useless calls to
- 55 // thread-local storage.
- 56
- if (obj - >mark() - >has_bias_pattern()) {
- 57 // Box and unbox the raw reference just in case we cause a STW safepoint.
- 58 Handle hobj(Self, obj);
- 59 // Relaxing assertion for bug 6320749.
- 60 assert(Universe: :verify_in_progress() || 61 ! SafepointSynchronize: :is_at_safepoint(), 62 "biases should not be seen by VM thread here");
- 63 BiasedLocking: :revoke_and_rebias(hobj, false, JavaThread: :current());
- 64 obj = hobj();
- 65 assert(!obj - >mark() - >has_bias_pattern(), "biases should be revoked by now");
- 66
- }
- 67
- }
- 68 69 // hashCode() is a heap mutator ...
- 70 // Relaxing assertion for bug 6320749.
- 71 assert(Universe: :verify_in_progress() || 72 ! SafepointSynchronize: :is_at_safepoint(), "invariant");
- 73 assert(Universe: :verify_in_progress() || 74 Self - >is_Java_thread(), "invariant");
- 75 assert(Universe: :verify_in_progress() || 76((JavaThread * ) Self) - >thread_state() != _thread_blocked, "invariant");
- 77 78 ObjectMonitor * monitor = NULL;
- 79 markOop temp,
- test;
- 80 intptr_t hash;
- 81 markOop mark = ReadStableMark(obj);
- 82 83 // object should remain ineligible for biased locking
- 84 assert(!mark - >has_bias_pattern(), "invariant");
- 85 86
- if (mark - >is_neutral()) {
- 87 hash = mark - >hash(); // this is a normal header
- 88
- if (hash) { // if it has hash, just return it
- 89
- return hash;
- 90
- }
- 91 hash = get_next_hash(Self, obj); // allocate a new hash code
- 92 temp = mark - >copy_set_hash(hash); // merge the hash code into header
- 93 // use (machine word version) atomic operation to install the hash
- 94 test = (markOop) Atomic: :cmpxchg_ptr(temp, obj - >mark_addr(), mark);
- 95
- if (test == mark) {
- 96
- return hash;
- 97
- }
- 98 // If atomic operation failed, we must inflate the header
- 99 // into heavy weight monitor. We could add more code here
- 100 // for fast path, but it does not worth the complexity.
- 101
- } else if (mark - >has_monitor()) {
- 102 monitor = mark - >monitor();
- 103 temp = monitor - >header();
- 104 assert(temp - >is_neutral(), "invariant");
- 105 hash = temp - >hash();
- 106
- if (hash) {
- 107
- return hash;
- 108
- }
- 109 // Skip to the following code to reduce code size
- 110
- } else if (Self - >is_lock_owned((address) mark - >locker())) {
- 111 temp = mark - >displaced_mark_helper(); // this is a lightweight monitor owned
- 112 assert(temp - >is_neutral(), "invariant");
- 113 hash = temp - >hash(); // by current thread, check if the displaced
- 114
- if (hash) { // header contains hash code
- 115
- return hash;
- 116
- }
- 117 // WARNING:
- 118 // The displaced header is strictly immutable.
- 119 // It can NOT be changed in ANY cases. So we have
- 120 // to inflate the header into heavyweight monitor
- 121 // even the current thread owns the lock. The reason
- 122 // is the BasicLock (stack slot) will be asynchronously
- 123 // read by other threads during the inflate() function.
- 124 // Any change to stack may not propagate to other threads
- 125 // correctly.
- 126
- }
- 1 public int hashCode() {
- 2 int h = hash;
- 3
- if (h == 0 && value.length > 0) {
- 4 char val[] = value;
- 5 6
- for (int i = 0; i < value.length; i++) {
- 7 h = 31 * h + val[i];
- 8
- }
- 9 hash = h;
- 10
- }
- 11
- return h;
- 12
- }
- 1 public boolean equals(Object obj) {
- 2
- return (this == obj);
- 3
- }
先比较 String 对象内存地址相同,若相同则返回 true,否则判断 String 对象对应字符的内容是否相等,若相等则返回 true
- 1 public boolean equals(Object anObject) {
- 2
- if (this == anObject) {
- 3
- return true;
- 4
- }
- 5
- if (anObject instanceof String) {
- 6 String anotherString = (String) anObject;
- 7 int n = value.length;
- 8
- if (n == anotherString.value.length) {
- 9 char v1[] = value;
- 10 char v2[] = anotherString.value;
- 11 int i = 0;
- 12
- while (n--!=0) {
- 13
- if (v1[i] != v2[i]) 14
- return false;
- 15 i++;
- 16
- }
- 17
- return true;
- 18
- }
- 19
- }
- 20
- return false;
- 21
- }
先看下方法的说明:Note that all arrays are considered to implement the interface Cloneable and that the return type of the clone method of an array type T[] is T[] where T is any reference or primitive type
一起看下 native 方法 位于 openjdk\hotspot\src\share\vm\prims\jvm.cpp 中 JVM_Clone
- JVM_ENTRY(jobject, JVM_Clone(JNIEnv* env, jobject handle))
- JVMWrapper("JVM_Clone");
- Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
- const KlassHandle klass (THREAD, obj->klass());
- JvmtiVMObjectAllocEventCollector oam;
- #ifdef ASSERT
- // Just checking that the cloneable flag is set correct
- if (obj->is_javaArray()) {
- guarantee(klass->is_cloneable(), "all arrays are cloneable");
- } else {
- guarantee(obj->is_instance(), "should be instanceOop");
- bool cloneable = klass->is_subtype_of(SystemDictionary::Cloneable_klass());
- guarantee(cloneable == klass->is_cloneable(), "incorrect cloneable flag");
- }
- #endif
- // Check if class of obj supports the Cloneable interface.
- // All arrays are considered to be cloneable (See JLS 20.1.5)
- if (!klass->is_cloneable()) {
- ResourceMark rm(THREAD);
- THROW_MSG_0(vmSymbols::java_lang_CloneNotSupportedException(), klass->external_name());
- }
- // Make shallow object copy
- const int size = obj->size();
- oop new_obj = NULL;
- if (obj->is_javaArray()) {
- const int length = ((arrayOop)obj())->length();
- new_obj = CollectedHeap::array_allocate(klass, size, length, CHECK_NULL);
- } else {
- new_obj = CollectedHeap::obj_allocate(klass, size, CHECK_NULL);
- }
- // 4839641 (4840070): We must do an oop-atomic copy, because if another thread
- // is modifying a reference field in the clonee, a non-oop-atomic copy might
- // be suspended in the middle of copying the pointer and end up with parts
- // of two different pointers in the field. Subsequent dereferences will crash.
- // 4846409: an oop-copy of objects with long or double fields or arrays of same
- // won't copy the longs/doubles atomically in 32-bit vm's, so we copy jlongs instead
- // of oops. We know objects are aligned on a minimum of an jlong boundary.
- // The same is true of StubRoutines::object_copy and the various oop_copy
- // variants, and of the code generated by the inline_native_clone intrinsic.
- assert(MinObjAlignmentInBytes >= BytesPerLong, "objects misaligned");
- Copy::conjoint_jlongs_atomic((jlong*)obj(), (jlong*)new_obj,
- (size_t)align_object_size(size) / HeapWordsPerLong);
- // Clear the header
- new_obj->init_mark();
- // Store check (mark entire object and let gc sort it out)
- BarrierSet* bs = Universe::heap()->barrier_set();
- assert(bs->has_write_region_opt(), "Barrier set does not have write_region");
- bs->write_region(MemRegion((HeapWord*)new_obj, size));
- // Caution: this involves a java upcall, so the clone should be
- // "gc-robust" by this stage.
- if (klass->has_finalizer()) {
- assert(obj->is_instance(), "should be instanceOop");
- new_obj = instanceKlass::register_finalizer(instanceOop(new_obj), CHECK_NULL);
- }
- return JNIHandles::make_local(env, oop(new_obj));
- JVM_END
隐含意思:数组类型默认可以直接克隆,而其他对象实现 clone 需要先实现 Cloneable 接口,否则抛出 CloneNotSupportedException 异常
问题 1:对象的创建有多中方式,类似 new 、getInstance、clone 等 clone 有什么好处?
问题 2:对象调用 clone 方法生成的对象 和 原对象是否还有什么关联关系?
问题 3 : 对象 clone 存在 "浅复制"、"深复制" 概念,怎么区分?
带着这 3 个问题,理解 Object clone 方法:
1、一般 native 方法比 java 中非 native 方法执行效率高 ,看示例 片段 9
- 1 package jdk;
- 2
- 3 import java.util.Calendar;
- 4 import java.util.Date;
- 5
- 6 public class ObjectCloneTest1 {
- 7
- 8 static final int N = 100000;
- 9
- 10 public static void main(String[] args) {
- 11
- 12 final Date date = new Date();
- 13
- 14 {
- 15
- 16 final long startTime = System.currentTimeMillis();
- 17 for (int i = 0; i < N; i++) {
- 18 Date date2 = (Date) date.clone();
- 19 }
- 20
- 21 final long endTime = System.currentTimeMillis();
- 22
- 23 System.out.println("clone:" + (endTime - startTime) + "ms");
- 24 }
- 25
- 26 {
- 27 final long startTime = System.currentTimeMillis();
- 28 for (int i = 0; i < N; i++) {
- 29 final Calendar cal = Calendar.getInstance();
- 30 cal.setTime(date);
- 31 final Date date2 = cal.getTime();
- 32 }
- 33
- 34 final long endTime = System.currentTimeMillis();
- 35 System.out.println("Calender.setTime:" + (endTime - startTime) + "ms");
- 36
- 37 }
- 38
- 39 }
- 40
- 41 }
2、clone 生成的新对象与原对象的关系,需要区别 2 个对象建是否存在相同的引用或对应的内存地址是否存在共用情况,若存在则 该次 clone 为 "浅复制",否则为 "深复制", 而且 Object 的 clone 方法是属于 "浅复制",看示例 片段 10
View Code
- 1 package jdk;
- 2
- 3
- 4 public class ObjectCloneTest2 {
- 5
- 6 public static void main(String[] args) {
- 7 Animal a1 = new Animal(1, "pig");
- 8 Animal a2 = (Animal) a1.clone();
- 9 System.out.println(a1.getName() == a2.getName() ? "浅复制" : "深复制");
- 10
- 11 System.out.println(a1);
- 12 a1.setAge(11);
- 13 a1.setName("big pig");
- 14 System.out.println(a1.age + ":" + a1.name);
- 15
- 16 System.out.println(a2);
- 17 System.out.println(a2.age + ":" + a2.name);
- 18
- 19 }
- 20
- 21 }
- 22
- 23 class Animal implements Cloneable{
- 24 int age;
- 25 String name;
- 26
- 27 Animal(int age, String name) {
- 28 this.age = age;
- 29 this.name = name;
- 30 }
- 31
- 32 public Animal clone() {
- 33 Animal o = null;
- 34
- 35 try {
- 36 o = (Animal) super.clone();
- 37 } catch (CloneNotSupportedException e) {
- 38 e.printStackTrace();
- 39 }
- 40
- 41 return o;
- 42 }
- 43
- 44 public int getAge() {
- 45 return age;
- 46 }
- 47
- 48 public void setAge(int age) {
- 49 this.age = age;
- 50 }
- 51
- 52 public String getName() {
- 53 return name;
- 54 }
- 55
- 56 public void setName(String name) {
- 57 this.name = name;
- 58 }
- 59 }
"深复制" 时,需要将共同关联的引用也复制完全看示例 片段 11
View Code
- 1 package jdk;
- 2 3 public class ObjectCloneTest3 {
- 4 5 public static void main(String[] args) {
- 6 Person p1 = new Person(10, "ll", new Race("yellow", "Asia"));
- 7 Person p2 = (Person) p1.clone();
- 8 System.out.println(p1.getRace() == p2.getRace());
- 9 System.out.println(p1.getTestArray() == p2.getTestArray());
- 10 11
- }
- 12 13
- }
- 14 15 class Person implements Cloneable {
- 16 int age;
- 17 String name;
- 18 Race race;
- 19 int[] testArray = {
- 1,
- 23,
- 5,
- 6,
- 0
- };
- 20 21 Person(int age, String name, Race race) {
- 22 this.age = age;
- 23 this.name = name;
- 24 this.race = race;
- 25
- }
- 26 27 public Person clone() {
- 28 Person o = null;
- 29 30
- try {
- 31 o = (Person) super.clone();
- 32 o.setRace(this.race.clone());
- 33 o.setTestArray(testArray.clone());
- 34
- } catch(CloneNotSupportedException e) {
- 35 e.printStackTrace();
- 36
- }
- 37 38
- return o;
- 39
- }
- 40 41 42 public int getAge() {
- 43
- return age;
- 44
- }
- 45 46 public void setAge(int age) {
- 47 this.age = age;
- 48
- }
- 49 50 public String getName() {
- 51
- return name;
- 52
- }
- 53 54 public void setName(String name) {
- 55 this.name = name;
- 56
- }
- 57 58 public Race getRace() {
- 59
- return race;
- 60
- }
- 61 62 public void setRace(Race race) {
- 63 this.race = race;
- 64
- }
- 65 66 public void setTestArray(int[] testArray) {
- 67 this.testArray = testArray;
- 68
- }
- 69 70 public int[] getTestArray() {
- 71
- return testArray;
- 72
- }
- 73 74
- }
- 75 76 class Race implements Cloneable {
- 77 String color; // 颜色
- 78 String distribution; // 分布
- 79 80 public Race(String color, String distribution) {
- 81 super();
- 82 this.color = color;
- 83 this.distribution = distribution;
- 84
- }
- 85 86 public Race clone() throws CloneNotSupportedException {
- 87
- return (Race) super.clone();
- 88
- }
- 89
- }
方法说明:
The
method for class
- toString
returns a string consisting of the name of the class of which the object is an instance, the at-sign character `
- Object
', and the unsigned hexadecimal representation of the hash code of the object. In other words, this method returns a string equal to the value of:
- @
默认返回对象的名称及引用地址,但一般被子类重写用于说明子类相关属性值描述
方法说明:Wakes up a single thread that is waiting on this object's monitor. If any threads are waiting on this object, one of them is chosen to be awakened. The choice is arbitrary and occurs at the discretion of the implementation. A thread waits on an object's monitor by calling one of the
methods.
- wait
方法说明:Wakes up all threads that are waiting on this object's monitor. A thread waits on an object's monitor by calling one of the
methods.
- wait
方法说明:Note that the
method, as it places the current thread into the wait set for this object, unlocks only this object; any other objects on which the current thread may be synchronized remain locked while the thread waits.
- wait
- if (nanos >= 500000 || (nanos != 0 && timeout == 0)) {
- timeout++;
- }
方法说明:The specified amount of real time has elapsed, more or less. If timeout is zero, however, then real time is not taken into consideration and the thread simply waits until notified.
- 1 public final void wait() throws InterruptedException {
- 2 wait(0);
- 3
- }
- 1 synchronized(obj) {
- 2
- while () 3 obj.wait(timeout);
- 4... // Perform action appropriate to condition
- 5
- }
View Code
- 1 package jdk;
- 2 3 import java.util.concurrent.ExecutorService;
- 4 import java.util.concurrent.Executors;
- 5 import java.util.concurrent.ThreadFactory;
- 6 import java.util.concurrent.atomic.AtomicInteger;
- 7 8 public class ObjectWaitTest1 {
- 9 static Object lock = new Object();
- 10 11 public static void main(String[] args) {
- 12 13 ExecutorService executeService = Executors.newCachedThreadPool(new MyThreadFactory());
- 14
- for (int i = 0; i < 10; i++) {
- 15 executeService.submit(() - >{
- 16 synchronized(lock) {
- 17
- try {
- 18 System.out.println(Thread.currentThread().getName() + " begin");
- 19 lock.wait(1000 * 30); // wiat 30s
- 20
- } catch(Exception e) {
- 21 e.printStackTrace();
- 22
- }
- 23 24 System.out.println(Thread.currentThread().getName() + " finish");
- 25
- }
- 26
- });
- 27
- }
- 28 29 executeService.shutdown();
- 30
- }
- 31 32 33 static class MyThreadFactory implements ThreadFactory {
- 34 35 private final AtomicInteger threadNumber = new AtomicInteger(1);
- 36 37 public Thread newThread(Runnable r) {
- 38
- return new Thread(r, "mythiread - " + threadNumber.getAndIncrement());
- 39
- }
- 40
- }
- 41
- }
执行 wait 过程中堆栈信息:
再来个生产消费者的示例 片段 15
View Code
- 1 package jdk;
- 2 3 import java.util.LinkedList;
- 4 import java.util.Queue;
- 5 import java.util.Random;
- 6 7 public class ObjectWaitTest2 {
- 8 static Object lock = new Object();
- 9 10 public static void main(String[] args) {
- 11 int maxSize = 10;
- 12 Queue buffer = new LinkedList < >();
- 13 14 Thread producer = new Producer(buffer, maxSize, "Producer");
- 15 Thread comsuner = new Comsumer(buffer, maxSize, "comsuner");
- 16 producer.start();
- 17 comsuner.start();
- 18
- }
- 19 20
- }
- 21 22 class Producer extends Thread {
- 23 private Queue queue;
- 24 private int maxSize;
- 25 26 27 public Producer(Queue queue, int maxSize, String name) {
- 28 super(name);
- 29 this.maxSize = maxSize;
- 30 this.queue = queue;
- 31
- }
- 32 33@Override 34 public void run() {
- 35
- while (true) {
- 36 37 synchronized(queue) {
- 38
- if (queue.size() == maxSize) {
- 39
- try {
- 40 System.out.println("Queue is full, Producer wair for Comsumer to task something from queue");
- 41 queue.wait();
- 42
- } catch(InterruptedException e) {
- 43 e.printStackTrace();
- 44
- }
- 45
- }
- 46 Random r = new Random();
- 47 int value = r.nextInt();
- 48 queue.add(value);
- 49 System.out.println("Producer value : " + value);
- 50 queue.notifyAll();
- 51
- }
- 52
- }
- 53
- }
- 54 55
- }
- 56 57 class Comsumer extends Thread {
- 58 private Queue queue;
- 59 private int maxSize;
- 60 61 public Comsumer(Queue queue, int maxSize, String name) {
- 62 super(name);
- 63 this.maxSize = maxSize;
- 64 this.queue = queue;
- 65
- }
- 66 67@Override 68 public void run() {
- 69
- while (true) {
- 70 71 synchronized(queue) {
- 72
- if (queue.isEmpty()) {
- 73
- try {
- 74 System.out.println("Queue is enpty, Comsumer wair for Producer to put something in queue");
- 75 queue.wait();
- 76
- } catch(InterruptedException e) {
- 77 e.printStackTrace();
- 78
- }
- 79
- }
- 80 System.out.println("Comsumer value : " + queue.remove());
- 81 queue.notifyAll();
- 82
- }
- 83
- }
- 84
- }
- 85 86
- }
Called by the garbage collector on an object when garbage collection determines that there are no more references to the object. A subclass overrides the
method to dispose of system resources or to perform other cleanup.
- finalize
来源: http://www.cnblogs.com/cq-home/p/6431426.html