A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 淡然 中级黑马   /  2012-6-16 11:10  /  1806 人查看  /  10 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 淡然 于 2012-6-17 15:14 编辑

  1. class Book{
  2.         boolean checkOut=false;
  3.         Book(boolean checkOut){
  4.                 this.checkOut=checkOut;
  5.         }
  6.         
  7.         void checkIn(){
  8.                 checkOut=false;
  9.                 //System.out.println("Checked In!"); //去掉此行注解即可得出正确结果
  10.         }
  11.         
  12.         protected void finalize(){
  13.                 if(checkOut){
  14.                         System.out.println("Error: checked Out!");
  15.                 }
  16.         }

  17. }

  18. public class Test{
  19.         public static void main(String[] args){
  20.                 Book novel=new Book(true);
  21.                 novel.checkIn();
  22.                
  23.                 new Book(true);
  24.                
  25.                 System.gc();
  26.         }
  27. }
复制代码
垃圾回收器准备好释放对象占用的存储空间,将首先调用其finalize()方法,在下一次垃圾回收动作发生时才会真正回收对象占用的内存。上面这个程序出不了结果,只有把注释掉的那一行加上才能出结果。System.out.println("Checked In!"); 这行代码是怎么影响了程序的执行,求解析~~

评分

参与人数 1技术分 +1 收起 理由
黄奕豪 + 1 赞一个!

查看全部评分

10 个回复

倒序浏览
您在第24行调用了checkIn方法,备注释的代码是checkIn方法内的,没有任何问题。道理很简单注释了就不能输出了。您的困惑到底在哪里??建议去仔细了解一下System.out.println()是干嘛用的。
回复 使用道具 举报
本帖最后由 李盼 于 2012-6-16 11:48 编辑

首先你明白了finalize方法,也就是对象被垃圾回收器回收之前,这个方法一定要执行一次!

不注释 结果为:
Checked In!    novel对象打印的
Error: checked Out!    匿名对象打印的
原因是:novel对象初始化时checkOut为true,调用了checkIn方法,把checkOut改为了false,然后打印checked in!然后mian方法继续执行,new出一个新的匿名对象。然后调用垃圾回收站,这个时候novel对象的finalize方法一定要执行一次,执行的时候发现checkOut改为了false(在checkIn方法中改变的),所以不打印。而匿名对象也要调用一个finalize方法,因为匿名对象在创建时,checkOut为true,所以finalize执行的时候通过了判断,打印了Error: checked Out!

你注释掉那行代码,那么第一个对象调用checkIn方法的时候,改了checkOut的值,finalize执行的时候依然通过不了判断,依然不打印,只有匿名对象被回收之前调用的finalize打了Error: checked Out!
所以你注释掉,就只打印Error: checked Out!

评分

参与人数 1技术分 +1 收起 理由
黄奕豪 + 1 赞一个!

查看全部评分

回复 使用道具 举报
垃圾回收器要回收对象的时候,首先要调用这个类的finalize方法,一般的纯Java编写的Class不需要重新覆盖这个方法,因为Object已经实现了一个默认的,除非我们要实现特殊的功能。
不过用Java以外的代码编写的Class(比如JNI,C++的new方法分配的内存),垃圾回收器并不能对这些部分进行正确的回收,这时就需要我们覆盖默认的方法来实现对这部分内存的正确释放和回收(比如C++需要delete)。
总之,finalize相当于析构函数,他是垃圾回收器回收一个对象的时候第一个要调用的方法。不过由于Java的垃圾回收机制能自动为我们做这些事情,所以我们在一般情况下是不需要自己来手工释放(调用System.gc()方法)的。
调用System.gc()只是提醒java虚拟机回收没用的对象(没有被引用的对象),java虚拟机并不保证会马上回收,只是尽快回收对象。

你上面的程序中调用System.gc()的时候回收的是new Book(true);生成的对象,因为没有对象引用它,而这样Book novel=new Book(true);生成的对象有novel这个对象引用,所以在main方法结束时才会被垃圾回收器回收。把你的程序改成下面:

  1. class Book{
  2.         boolean checkOut=false;
  3.         String name;
  4.         Book(String name,boolean checkOut){
  5.                 this.checkOut=checkOut;
  6.                 this.name = name;
  7.         }
  8.         
  9.         void checkIn(){
  10.                 checkOut=false;
  11.                 System.out.println("Checked In!"); //去掉此行注解即可得出正确结果
  12.         }
  13.         
  14.         @Override
  15.         protected void finalize(){
  16.                 if(checkOut){
  17.                     System.out.println("Error: checked Out! name: " + name);
  18.                 }else{
  19.                         System.out.println("correct: checked Out! name: " + name);
  20.                 }
  21.         }

  22. }

  23. public class Test{
  24.         public static void main(String[] args){
  25.                 Book novel=new Book("one",true);
  26.                 novel.checkIn();
  27.                
  28.                 novel = null;
  29.                
  30.                 new Book("two",true);
  31.                
  32.                 System.gc();
  33.         }
  34. }
复制代码
加上
novel = null;
这一句垃圾回收器就会将 Book novel=new Book("one",true); 这一句new出来的对象也回收,否则只有在main结束后才会回收。你可以试一下,加上name属性
可以看到是哪一个对象被回收了。

评分

参与人数 1技术分 +1 收起 理由
黄奕豪 + 1 赞一个!

查看全部评分

回复 使用道具 举报
finalize()方法在三种情况下会被调用 :
1.所有对象被Garbage Collection时自动调用,比如运行System.gc()的时候.
2.程序退出时为每个对象调用一次finalize方法。
3.显式的调用finalize方法
除上述以外,正常情况下,当某个对象被系统收集为无用信息的时候,finalize()方法将被自动调用,但是JVM不保证finalize()一定被调用,所以说 ,finalize()的调用是不确定的。
class Book {
        boolean checkOut = false;
        Book(boolean checkOut) {
                this.checkOut = checkOut;
        }
        void checkIn() {
                checkOut = false;
                 System.out.println("Checked In!"); //并不影响程序的执行。
        }
        protected void finalize() {//Java语言允许程序员为任何方法添加finalize( )方法,该方法会在垃圾收集器交换回收对象之前被调用。
                if (checkOut) {
                        System.out.println("Error: checked Out!");
                }
        }
}
public class Test {
        public static void main(String[] args) {
                Book novel = new Book(true);
                novel.checkIn();//对象 novel调用了checkIn()方法,执行checkOut = false;调用finalize()方法但不符条件。
                new Book(true);//匿名对象 调用finalize()方法符合条件,执行方法。当对象被系统收集为无用信息的时候,finalize()方法将被自动调用,但调用是不确定的
                //System.gc();//gc 只能清除在堆上分配的内存(纯java语言的所有对象都在堆上使用new分配内存),而不能清除栈上分配的内存
        }
}
题外补充:一、gc()与finalize()的区别 :
gc 只能清除在堆上分配的内存(纯java语言的所有对象都在堆上使用new分配内存),而不能清除栈上分配的内存.如果某些对象被分配了栈上的内存区域,对这样的对象进行内存回收就用finalize().
二、finalize()的特点:
1.每一个对象都有一个finalize( )方法,这个方法是从Object类继承来的。 Java语言允许为任何方法添加finalize( )方法,该方法会在垃圾收集器交换回收对象之前被调用。但不要过分依赖该方法对系统资源进行回收和再利用,因为该方法调用后的执行结果是不可预知的。 通过以上对垃圾收集器特点的了解,你应该可以明确垃圾收集器的作用,和垃圾收集器判断一块内存空间是否无用的标准。简单地说,当你为一个对象赋值为null并且重新定向了该对象的引用者,此时该对象就符合垃圾收集器的收集标准。
2.finalize( )方法用来回收内存以外的系统资源,就像是文件处理器和网络连接器。该方法的调用顺序和用来调用该方法的对象的创建顺序是无关的。
3.每个对象只能调用finalize( )方法一次。如果在finalize( )方法执行时产生异常(exception),则该对象仍可以被垃圾收集器收集。
4.垃圾收集器跟踪每一个对象,收集那些不可到达的对象(即该对象没有被程序的任何"活的部分"所调用),回收其占有的内存空间。但在进行垃圾收集的时候,垃圾收集器会调用finalize( )方法,通过让其他对象知道它的存在,而使不可到达的对象再次"复苏"为可到达的对象。既然每个对象只能调用一次finalize( )方法,所以每个对象也只可能"复苏"一次。
5.finalize( )方法可以明确地被调用,但它却不能进行垃圾收集。
6.finalize( )方法可以被重载(overload),但只有具备初始的finalize( )方法特点的方法才可以被垃圾收集器调用。
7.子类的finalize( )方法可以明确地调用父类的finalize( )方法,作为该子类对象的最后一次适当的操作。但Java编译器却不认为这是一次覆盖操作(overriding),所以也不会对其调用进行检查。
8.当finalize( )方法尚未被调用时,System. runFinalization( )方法可以用来调用finalize( )方法,并实现相同的效果,对无用对象进行垃圾收集。
9.当一个方法执行完毕,其中的局部变量就会超出使用范围,此时可以被当作垃圾收集,但以后每当该方法再次被调用时,其中的局部变量便会被重新创建。
10.Java语言使用了一种"标记交换区的垃圾收集算法"。该算法会遍历程序中每一个对象的句柄,为被引用的对象做标记,然后回收尚未做标记的对象。
所谓遍历可以简单地理解为"检查每一个"。
回复 使用道具 举报
李盼 发表于 2012-6-16 11:47
首先你明白了finalize方法,也就是对象被垃圾回收器回收之前,这个方法一定要执行一次!

不注释 结果为:

你说得很对,但是我的程序出不来结果: Error: checked Out!   什么都打印不出来
回复 使用道具 举报
淡然 中级黑马 2012-6-17 09:51:12
7#
曾祥彬 发表于 2012-6-16 12:45
垃圾回收器要回收对象的时候,首先要调用这个类的finalize方法,一般的纯Java编写的Class不需要重新覆盖这 ...

还是出不来结果。我运行了你给出的代码,输出结果为:Checked In!
回复 使用道具 举报
淡然 发表于 2012-6-17 09:51
还是出不来结果。我运行了你给出的代码,输出结果为:Checked In!

不太懂你什么意思!你调用了checkIn的话肯定是会输出的啊!
回复 使用道具 举报
淡然 中级黑马 2012-6-17 10:24:34
9#
曾祥彬 发表于 2012-6-17 10:02
不太懂你什么意思!你调用了checkIn的话肯定是会输出的啊!

只输出 Checked In!  
回复 使用道具 举报
淡然 发表于 2012-6-17 10:24
只输出 Checked In!

你在
new Book("two",true);
后面加上
                try {
                        Thread.sleep(100);
                } catch (InterruptedException e) {
                        e.printStackTrace();
                }
然后多运行几次,应为我上面说了,finalize方法只是通知java虚拟机去回收没有用的对象,但是java虚拟机不一定会马上回收,所以你要多运行几次,每次的结果都会有可能不一样的。
回复 使用道具 举报
曾祥彬 发表于 2012-6-17 11:42
你在
new Book("two",true);
后面加上

懂了~~~~~~
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马