代码只有0和1!??

 System.out.println相信这句话没有谁会陌生,但是你真的了解这句话吗?先来看看下面的代码:

String s="abc"; 
    System.out.println(s); 
 s=null; 
    System.out.println(s);
s="null"; 
    System.out.println(s);
s=""; 
    System.out.println(s);
 s="  "; 
    System.out.println(s);

看第二条和第三条:他们会输出什么呢???null?还是什么都没有?这个留给你们自己去验证看看。当然重要的不是结果。而是如果你认为是输出null那理由是什么呢?如果什么都不输出,理由又是什么呢?这时你可能会分析,s是空的,里面什么都没有,当然不会输出任何东西。又或者说s为null那么输出肯定就是null。不管是哪种想法(当然不只是这两种想法),是不是觉得思路一下子打开了呢?当然想法归想法,怎么验证我们的想法对不对呢?毕竟对于这样的一个程序来说结果是确定的。当然验证也很简单,跑一下这段代码就完了。那么答案出来了,但是我们前面的分析却并不是那么的严谨。那么如何才能更加合理的分析呢?其实有一个很简单的方式,那就是源代码。任何的东西在源代码面前都是透明。下面是println的源码:

 1 public void println(String x) {
 2         synchronized (this) {
 3             print(x);
 4             newLine();
 5         }
 6     }
 7  public void print(String s) {
 8         if (s == null) {
 9             s = "null";
10         }
11         write(s);
12     }

好了,结果已经很明显了。源代码已经把所有定西都告诉你了。那么把输出语句升级一下:

JFrame jf=new JFrame();
jf.setTitle("这是标题 ");        
System.out.println(jf);

Student a=new Student();
System.out.println(a);

这两句话又会输出什么咧?如果你的想法和我以前一样,脑子里马上冒出了“地址”二字,你怕是和我一样中毒已深咯。下面是这两句的输出结果:

javax.swing.JFrame[frame0,0,0,0x0,invalid,hidden,layout=java.awt.BorderLayout,title=这是标题,resizable,normal,defaultCloseOperation=HIDE_ON_CLOSE,rootPane=javax.swing.JRootPane[,0,0,0x0,invalid,layout=javax.swing.JRootPane$RootLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=16777673,maximumSize=,minimumSize=,preferredSize=],rootPaneCheckingEnabled=true]
com.cbs.Student@52cc8049

那么在哪里看到了有地址这两个字呢?有人肯能会想第二句中的@52cc8049不是地址是什么?问题是你凭什么说它是地址呢?我说它是一串数字不行吗?或者它就是一串字符串。而且为何第一句和第二句的输出差别那么大呢?程序是不会骗人的,它既然输出了这样的结果,就意味着在代码的一句句的执行中,必然会存在这样的一句代码,造成了这样的输出结果。那么要找症结也就很容易了,只要找到那句输出的话就行了。还是上面的方法,源代码能够说明一切。

//第一层源码
public void println(Object x) {
        String s = String.valueOf(x);
        synchronized (this) {
            print(s);
            newLine();
        }
   }
//第二层源码
public static String valueOf(Object obj) {
        return (obj == null) ? "null" : obj.toString();
    }
//第三层源码
 public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

好,现在先看第一层。可以看到String s是由valueOf这个方法的返回值决定的。所以在往上走,可以看到一个判断,如果传入的对象为空,那么返回字符串“null”,否则调用Object类的toString方法。但是到这里还是没有看到我们想看到的东西,那么继续往上走。找到Object的toString方法,好了终于找到了我们要输出的这句话了。先是类名然后@最后是加上的类的hashCode。这时你可看看上诉三个三个方法的注解,你会发现并没有哪里提到地址这个词。

*Returns a string representation of the object. In general, the
* {@code toString} method returns a string that
* "textually represents" this object. The result should
* be a concise but informative representation that is easy for a
* person to read.
* It is recommended that all subclasses override this method.

这是toString方法注释的原话,显然并没有提过这里会输出对象的地址,只说过会得到一段hashCode。那么为什么第一句的输出又不一样呢?看注释的最后一句,我们都知道Java中的类都是Object类的子类,那么JFrame也不例外,所以说会不会是JFrame重写了toString方法呢?大家可以去找找JFrame的源码看看。现在你还认为打印对象输出的会是地址吗?那么终极一问,新建一A类的对象,下面的输出语句会输出什么(注意前提很重要!!)

A a=new A();
System.out.println(a);

首先想到的会是什么?hashCode?一串字符?然而,我只想说,只要给我重写toString的机会,我可以输出整个世界!你想要什么?数字、界面、游戏.......或许程序的世界只有0和1,但是程序员却拥有整个世界。其实现在这个问题就跟问你:树上有八只鸟,开了一枪,那么树上还有几只鸟?是一样的了。

瞎扯了半天,输出语句讲道理不就是输出吗?费的着在这研究那么久。我们都用过输出语句干嘛?单纯只是输出?有没有试过用它里排错。在编写代码的过程中,各种错误是无法避免的,遇到开发工具报错和异常的情况还好说,怕的就是编译没问题,但是始终的不到自己想要的结果。这时候就可以通过输出语句进行排错了。比如说:

for (int c = 0; c < arry[0].length; c++) 
    for (int r = arry.length - 1; r >= 0; r--) 
        if (arry[r][c] == 0) 
            for (int r1 = r - 1; r1 >= 0; r1--) 
                if (arry[r1][c] != 0) {
                    arry[r][c] = arry[r1][c];
                    arry[r1][c] = 0;
                    count++;                                
                    System.out.println("count: "+count);
                    break;
                    }

这里的输出语句就能反映很多问题,if条件是否已经执行,如果执行了,那么count的值又会是多少?如果有很多的输出语句,给语句加上编号,就可以确定发生问题的位置。如果发现这一层的逻辑没有问题,可以考虑用输出继续检测上一层的语句。编程无非就是如此,出问题了排错,不懂了研究研究源码。能够做到这些,大部分的问题也就都不是问题了。

ps:hashCode 数据结构中有哈希查找表的概念,实际上哈希表是通过一定的映射关系来确定元素的存储位置,来使得每个元素的平均查找长度尽可能的为0。哈希表的映射关系应该又叫哈希函数,hashCode并不一定就是地址,可能需要通过哈希函数来转换成元素的存储位置(地址)。

posted @ 2017-07-26 09:31  迷失的小菜包  阅读(2855)  评论(0编辑  收藏  举报