Java谜题畅读版之字符谜题

来源:互联网 发布:邮票交易源码 编辑:程序博客网 时间:2024/06/05 14:19

谜题11:最后的笑声

public class LastLaugh{    public static void main(String[] args){        System.out.print("H"+"a");        System.out.print('H'+'a');    }}
你期待程序打出HaHa, 但是不是。
问题的关键在于:char的本质是16位无符号整数。所以结果它打印的是它打印的是Ha169.

谜题12:ABC

先看第一个:
public class NumberTest{    public static void main(String[] args){        char[] numbers = {'1', '2', '3'};        System.out.println(numbers);    }}
它打印的是123.
那么
public class ABC{    public static void main(String[] args){        String letters = "ABC";        char[] numbers = {'1', '2', '3'};        System.out.println(letters + " easy as " + numbers);    }}
会输出什么?
虽然char本质是16位整数,很多库方法对char做了特殊处理, 比如println会输出char的unicode字符,println字符数组会打印出所有的字符. String.valueOf和StringBuffer.append的char[]重载版本的行为也是类似的.
然而字符相加却会调用字符数组的toString函数,所以结果会打印出ABC easy as [C@16f0472一样的东西。

谜题13:畜牧场

这个程序会打印Animals are equal: true吗?
public class AnimalFarm{    public static void main(String[] args){        final String pig = "length: 10";        final String dog = "length: " + pig.length();        System.out. println("Animals are equal: "                            + pig == dog);    }}
这个程序有两个问题, 它只打印出false, 你应该能知道第一个问题出现在哪里。它相当于System.out.println(("Animals are equal: " + pig) == dog);
如果你把改成System.out.println("Animals are equal: " + (pig == dog)); 然后你想到了intern, 但是你运行程序, 并没有像你想的那样打印出Animals are equal: true.
String类型的变量,如果是通过字面量直接赋值,则相同的字面量共享一个copy;如果有+运算,比如"a"+"b",或者a+1,a+true,即String和原始类型的字面量直接相加,则在编译器直接把+运算做掉;如果是String加上一个变量,则不会在编译期优化;如果是String+Final变量,则在编译器就进行优化。如果是String+函数调用,则不会在编译期优化。
关于string常量表达式的处理和优化, 可以看看这里:www.blogjava.net/aoxj/archive/2009/11/10/165536.html

谜题14:转义字符的溃败

public class EscapeRout{    public static void main(String[] args){        // \u0022 是双引号的Unicode转义字符        System.out.println("a\u0022.length()+\u0022b".length());    }}
会不会输出26?因为a\u0022.length()+\u0022b总共有26个字符? 你会说不对, 因为\u连同后面的4个字符是一个unicode字符。所以应该是16.
程序会输出2.
记住一点: 编译器对unicode的解析优先于对符号的解析。所以程序等同于:System.out.println("a".length()+"b".length());

谜题15:令人晕头转向的Hello

/** * Generated by the IBM IDL-to-Java compiler, version 1.0 * from F:\TestRoot\apps\a1\units\include\PolicyHome.idl * Wednesday, June 17, 1998 6:44:40 o’clock AM GMT+00:00 */public class Test{    public static void main(String[] args){        System.out.print("Hell");        System.out.println("o world");    }}
这个程序输出什么? 如果你运行一下, 你会发现它不能通过编译。
问题处在\units。\u表示unicode的开始,后面紧跟4位16进制的数字。
所以如同14里面说的,编译器对unicode的解析优先于对符号的解析,哪怕是在注释中。

谜题16:行打印程序

public class LinePrinter{    public static void main(String[] args){    // Note: \u000A is Unicode representation of linefeed (LF)        char c = 0x000A;        System.out.println(c);       }}
同样这个程序不能通过编译,原因就不说了, 如果14 15你懂了, 这个你也懂的。

谜题17:嗯?

下面的是一个合法的Java程序吗?
\u0070\u0075\u0062\u006c\u0069\u0063\u0020\u0020\u0020\u0020
\u0063\u006c\u0061\u0073\u0073\u0020\u0055\u0067\u006c\u0079
\u007b\u0070\u0075\u0062\u006c\u0069\u0063\u0020\u0020\u0020
\u0020\u0020\u0020\u0020\u0073\u0074\u0061\u0074\u0069\u0063
\u0076\u006f\u0069\u0064\u0020\u006d\u0061\u0069\u006e\u0028
\u0053\u0074\u0072\u0069\u006e\u0067\u005b\u005d\u0020\u0020
\u0020\u0020\u0020\u0020\u0061\u0072\u0067\u0073\u0029\u007b
\u0053\u0079\u0073\u0074\u0065\u006d\u002e\u006f\u0075\u0074
\u002e\u0070\u0072\u0069\u006e\u0074\u006c\u006e\u0028\u0020
\u0022\u0048\u0065\u006c\u006c\u006f\u0020\u0077\u0022\u002b
\u0022\u006f\u0072\u006c\u0064\u0022\u0029\u003b\u007d\u007d
这个当然是合法的java程序。可以用javascript alert一下上面的字符串,即可得看到庐山真面目。对于这个题目,我思考的是,如何用java把它还原, Integer.parseInt可以做这个事情, 比如(char)Integer.parseInt("006F", 16) =='o; 反向转换更简单,比如我有char c = '啊'; 我想获得c的unicode形式, 则Integer.toString((int)c, 16);

谜题18:字符串奶酪

程序打印出什么?
public class StringCheese {    public static void main(String[] args) {        byte bytes[] = new byte[256];        for (int i = 0; i < 256; i++)             bytes[i] = (byte)i;        String str = new String(bytes);        for (int i = 0, n = str.length(); i < n; i++)             System.out.println((int)str.charAt(i) + " ");    }}
乍看, 会打印出0~255,在我机器上一运行, 会发现,0~127可以正常打印出来,后面的都是65533。
问题出在String str = new String(bytes); 当你用字节数组构造String的时候, 应该考虑编码问题,即程序使用什么编码来解析字符数组从而构造出String。如果你String str = new String(bytes, "iso-8859-1");则能够打印出0~255。在这里我感兴趣的是为什么127之后都出现的是65533。
我通过System.out.println(Charset.defaultCharset().name());得知我的机器的默认字符集是utf-8,对utf-8稍微了解的就知道,utf-8的字符的字节数是可变长的,如果一个byte的最高位是0, 则认为该字节是是一个单字节字符,也即和ascii部分是一致的。所以对于本例, 0~127满足utf-8单字节字符的要求, 可以被正确识别。但是如果最高位不是0, 则需要满足更多的规则,比如,两个字节的utf8的二进制应该满足110xxxxx 10xxxxxx. 三个字节的应该满足1110xxxx 10xxxxxx 10xxxxxx。如果不满足这些规则,则会用65533代替该字符,表示它无法识别, 建议阅读http://blog.csdn.net/sfdev/article/details/3770706.

谜题19:漂亮的火花 - 块注释符

下面的程序用一个方法对字符进行了分类,区分他们是letter还是operator。这个程序会打印出什么呢?
public class Classifier {    public static void main(String[] args) {        System.out.println(             classify('n') + classify('+') + classify('2'));    }    static String classify(char ch) {        if ("0123456789".indexOf(ch) >= 0)             return "NUMERAL ";        if ("abcdefghijklmnopqrstuvwxyz".indexOf(ch) >= 0)             return "LETTER ";        /* (Operators not supported yet)            if ("+-*/&|!=" >= 0)                 return "OPERATOR ";        */        return "UNKNOWN";    }}
只需要你把它拷贝到eclipse,就会发现它无法编译。
问题在于*/破坏了注释的结构。
另外需要注意的是:块注释不能嵌套。所以下面的代码也不能通过编译。
/*/* Add the numbers from 1 to n */int sum = 0;for (int i = 1; I <= n; i++)sum += i;*/

谜题20:我的类是什么?

你期待下面的程序打印出类路径,以/分割包名。
public class Me {    public static void main(String[] args){        System.out.println(             Me.class.getName().                replaceAll(".","/") + ".class");    }}
实际上他打印出///////////////////.class。
问题在于String.replaceAll接受了一个正则表达式作为它的第一个参数,而"."可以匹配任何单字符。
解决方法如下:
public class Me {    public static void main(String[] args){        System.out.println(            Me.class.getName().replaceAll("\\.","/") + ".class");    }}

谜题21:我的类是什么?II

import java.io.File;public class MeToo {    public static void main(String[] args){        System.out.println(MeToo.class.getName().            replaceAll("\\.", File.separator) + ".class");    }}
这个程序的错误和平台相关, 你能看出来吗?
如果你在windows下运行, replaceAll("\\.", File.separator)这句话就相当于:replaceAll("\\.", "\\").

谜题22:URL的愚弄

public class BrowserTest {    public static void main(String[] args) {        System.out.print("iexplore:");        http://www.google.com;        System.out.println(":maximize");            }}
这个程序有错吗?乍一看觉得肯定不能通过编译,事实上它可以运行。但是这个特性很少被用到. http:定义了一个label,这个label可以用来在多重循环的情况下break用的。后面的//www.google.com;是一个普通注释

谜题23:不劳无获

import java.util.Random;public class Rhymes {   private static Random rnd = new Random();   public static void main(String[] args) {      StringBuffer word = null;      switch(rnd.nextInt(2)) {          case 1:  word = new StringBuffer('P');          case 2:  word = new StringBuffer('G');          default: word = new StringBuffer('M');      }      word.append('a');      word.append('i');      word.append('n');      System.out.println(word);   }}
这个程序有3个问题, 你能看出几个?
问题1 rnd.nextInt(2)的取值范围为(0,1),而switch语句里面的却是case 1, case 2;
问题2 new StringBuffer('M')里面的参数是char,而char的本质是16位无符号整数,此时这个参数代表了该buffer初始长度;
问题3 case语句没有break, 所以就算你把程序修改成下面的样子,它只会输出Main, 原因不解释。
public class Rhymes {    private static Random rnd = new Random();    public static void main(String[] args) {        StringBuffer word = null;        switch(rnd.nextInt(2)) {            case 0: word = new StringBuffer("P");            case 1: word = new StringBuffer("G");            default: word = new StringBuffer("M");        }        word.append('a');        word.append('i');        word.append('n');        System.out.println(word);    }}





原创粉丝点击