必威Java String 扫盲。Core Java 总结(字符和字符串类问题)

1.有关概念

负有代码都在本土编译运行测试,环境也
Windows7 32员机器 + eclipse Mars.2 Release (4.5.2)

1.开篇

今日整治代码的下突然见到String
和StringBuilder引发了片思想,我们还晓得彼此都是对准字符串的保管,
并且二者都提供多对准字符串增删改查等等的效果。但是重来都未建议当改动大(复杂)的字符串用String
来处理。这个是胡吧。查找了部分资料和翻译看了源码,整理了产,如果出哪里说错的,望大神出来指点

2016-10-17 整理

2.什么是字符串

字符串是什么,通俗易懂的来说就是是出于多独字符(0-9,a-z,A-Z,符号)组成的一致名目繁多内容。Java是透过char的数组进行管制即同样失误字符的。

  • 字符,字符串类问题
  • 正则表达式问题
  • Java字符编码问题
  • 字符串内存问题

2.String

1.定义:

TheStringclass represents character strings. All string literals in Java
programs, such as”abc”, are implemented as instances of this
class.Strings are constant; their values cannot be changed after they
are created.
StringApi

直接翻译看Java
api可以找到String类的享有信息,String是独八九不离十即是复合类型(不是明媒正娶项目),但是String
这个近乎是只常量类,只要创造了
String,他的值是未曾办法改变之(所以一律担当对为此String来操作字符串会里面开辟一个初的字符串出来)。而这边的常量不是乘的上上下下String
而是说之String的字面量。

String a = "abc"            //这里的"abc"会被放到.class 文件中的常量池中
String b=new String("abc")  //String的另外一种情况,b指向的并不是常量池,是堆

2.常量

简述String和StringBuffer、StringBuilder的区别?

于初级的一个问题,而初级题目还要是除了高端职位外,笔试题海量筛人的首选,但是当经题目,还是入选了自之笔记,因为它们能够延长的Java字符串的题目太多矣……另一样栽延伸的高端问法就是套路你,因为这个书100%碰头回复多线程的知识点:前者是线程安全之,后者是非线程安全的。然后他们即咨询啊是线程,或者说过程以及线程的界别,为什么有线程?它怎么落实之线程安全,接下稍微高端的即使引入Java并发的题材……呵呵。闲言少叙,看几乎单此类不同难度之文山会海题材。

1.常量的基本概念

说了这般多常量,常量到底是啊呢?就是简简单单通过字面的诠释,创建了未能够改变为不见面改变之计量。由于是着重出口String所以基本常量的说就是未多说了,找了一如既往篇雅好的篇章,大家好看下
介绍了java中的常量池

下面的代码有了哟?

String s = "abcd";
s = s.concat("ef");

String是final的,字符串对象中是故final的字符数组存储的,故String是产生字面量这无异于说法之,这是另外门类所未曾底特点(除原生类型)。另外,java中也起字符串常量池这个说法,用来囤字符串字面量。可以写一个贪图表示:

必威 1String
类的操作本质是起了新的 String
对象,给丁假象:好像是字符串被改成了貌似。

2.String 常量

鉴于String
有2栽申明的艺术,这2种植方式表面上看起来一样,其实私下所举行的操作都非一样,所以有必要将出去记录下(来咯大量的事例来咯):

 1 String s1 = "Hello";
 2 String s2 = "Hello";
 3 String s3 = "Hel" + "lo";
 4 String s4 = "Hel" + new String("lo");
 5 String s5 = new String("Hello");
 6 String s6 = s5.intern();
 7 String s7 = "H";
 8 String s8 = "ello";
 9 String s9 = s7 + s8;
10           
11 System.out.println(s1 == s2);  // true
12 System.out.println(s1 == s3);  // true
13 System.out.println(s1 == s4);  // false
14 System.out.println(s1 == s9);  // false
15 System.out.println(s4 == s5);  // false
16 System.out.println(s1 == s6);  // true

String和StringBuffer、StringBuilder三者的类图(或者选择题:类的涉嫌)是何许的?

咱俩先使记住:

必威 2

String、StringBuffer、StringBuilder 都实现了 CharSequence
接口,内部还是因此一个char数组实现,虽然她都同字符串相关,但是其处理机制不同。

  • String:是不足更改的,也就算是创建后虽不可知在修改了。
  • StringBuffer:是一个只是变换字符串序列,它与 String
    一样,在内存中保留的都是一个平稳的字符串序列(char
    类型的数组),不同点是 StringBuffer 对象的价都是可变的。
  • StringBuilder:与 StringBuffer
    类基本相同,都是不过转移字符串序列,不同点是 StringBuffer
    是线程安全的,StringBuilder 是线程不安全的。所以StringBuilder效率更胜,因为钉的获得和自由会带来开销。

无论是创造StringBuffer 还是
StringBuilder对象,都是默认创建一个容量为16之字符数组。区别就是具备的办法中,比如append,前者有synchronized关键字修饰。

必威 3

 

刚刚入门的同伙看到这些自然头晕了,不着急慢慢一修说明是为何:
  1. s1==s2
    十分好掌握,由于==是判地址(不是判断值,判断地址地址地址重要之业务说其三举),
    当编译的当儿,系统活动在常量池内部存入了”Hello”这个价值,由于常量池有复用的法力,自然就拿此常量的地方被了s2这个引用。
  2. s1==s3,这个实际跟11
    差不多注意一点网老聪明了,常量池会自动优化拼接,拼接完发现同就是将本的常量地址直接被了s3所以返回true
  3. s1==s4,s4有一些属常量池有一些编译的时候系统从无理解凡是啥,所以只好等到运行的当儿把常量池里面的得下然后带在新的字符串在堆种开辟了平片新的半空中存放,千万不要问我存在堆的乌,因为自吗不亮堂拍!!鬼知道存哪了阿但是自然是初的均等块。没毛病的~~
  4. s1==s9, 这个好风趣阿,为什么会不同呢,因为系统在编译的常常他才略知一二s7
    s8凡独变量,他杀根不晓里面来甚,他致完地址便忘记了讨好!!只好等交运行的时到s7
    s8内得到值然后每当堆种开辟新的上空。
  5. s4 == s5 不多讲.
    6.s1 == s6,肯定起正入门的人咨询s5.intern();
    这个是啥,这个就是将堆着之值放到常量池里面,同理常量池里面有复用的效应放上的时光发现一律就是直把本来的地点以出来~~所以还是一样的

还有局部状什么“+”号拼接啦,上面推荐的文章还起,不多说了
方的事例,图片就未打了,因为小编好懒的巴结不愿意画图

StringBuffer、StringBuilder,两者的toString()方法是哪些回到的字符串类型?

必威 4必威 5

虽然StringBuffer使用了缓存,但是本质上都一样,每次toString()都会创建一个新的String对象,而不是使用底层的字符数组,StringBuffer/StringBuilder的存在是为了高效的操作字符串(字符数组)的状态,但是当我们使用toString()的时候一定是一个稳定的状态,具有确切的行为。

解析

2.String 源码分析

说了String在对字符串进行改动的时候会创造一个新的String,由于好奇背后怎么落实之,小编就无在了一个事例:

        String a = new String("abca");
        String b=a.replace("a","e");
        System.out.println(a);     //abca
        System.out.println(b);    //ebce

针对~就是无用了一个a.replace()来开分析,不知底此method是干嘛的伴儿自行去api网站看~
断别问我
a的值怎么不转移,因为告诉你们字面量是常量不会见变换无会见变所以需要一个新的String来保存结果

    public String replace(CharSequence var1, CharSequence var2) {
        return Pattern.compile(var1.toString(), 16).matcher(this).replaceAll(Matcher.quoteReplacement(var2.toString()));
    }

实质上是办法充分简单,就辣么一行,但是及时一行将他详细看还是可以看看成千上万东西的,首先java在轮换字符串的时段用之凡正则表达式
哎是正则表达式咧(sjsu的小伙伴你们46b lab会教)附上链接:
正则表达式

先是创建了一个正则表达式的规则,没有错就是过上要改的字符,然后以创建了一个matcher,matcher(this)是用来存放在匹配的结果(参数代表要配合的字符串,String的话语当然就是友好我去匹配了)
发出无出配合到,有没出找到相应之情节等等 附上链接:
Matcher

这里重点说一下这二个
matcher.find(); //部分匹配,通俗啊点讲就是查找这个字符串里面有没有匹配到内容,然后定位到剩下匹配到的内容
matcher.matches(); //全部匹配,就是把整串东西和规则进行匹配

Matcher.quoteReplacement(var2.toString()) //去除转义字符"\"和"$"

重点看replaceAll的实现:

public String replaceAll(String var1) {
        this.reset();
        boolean var2 = this.find();
        if(!var2) {
            return this.text.toString();
        } else {
            StringBuffer var3 = new StringBuffer();

            do {
                this.appendReplacement(var3, var1);
                var2 = this.find();
            } while(var2);

            this.appendTail(var3);
            return var3.toString();
        }
    }

就同一错简单说一下:
要find()没有结果的话语一直归text(String的字面量),如果出配合到
那就算证实要替换了,那么这里是第一了,java开辟了一个新的StringBuffer!!(暂时理解呢一个初的char[]).然后把一个连贯一个的拿字符赋值上去,然后匹配的地方赋值新的值,就可以看出,String在召开替换的操作的时刻的确开辟了一个初的长空,而且看这段代码也足以见到为何替换了2只a
因为他会直接寻找找找直到终极 懂吧~~

replace我重点看了其它的法门扫了产呢大都用到了正则表达式啦,有趣味之伴儿可以看String其它的实现方式

String和StringBuffer、StringBuilder三者的使用状况

必威 6必威 7

使用 String 类的场景:在字符串不经常变化的场景中可以使用 String 类,例如常量的声明、少量的变量运算。

使用 StringBuffer 类的场景:在频繁进行字符串运算(如拼接、替换、删除等),并且运行在多线程环境中,则可以考虑使用 StringBuffer,例如 XML 解析、HTTP 参数解析和封装。

使用 StringBuilder 类的场景:在频繁进行字符串运算(如拼接、替换、和删除等),并且运行在单线程的环境中,则可以考虑使用 StringBuilder,如 SQL 语句的拼装、JSON 封装等。

解析

3.StringBuilder

开业便称了StringBuilder也是故来保管字符串,但是他的不过老区别就是是可转中的值,他不是常量,直接上主要代码:

public final class StringBuilder extends AbstractStringBuilder implements Serializable, CharSequence {
    static final long serialVersionUID = 4383685877147921099L;

    public StringBuilder() {
        super(16);
    }

    public StringBuilder(int var1) {
        super(var1);
    }

    public StringBuilder(String var1) {
        super(var1.length() + 16);
        this.append(var1);
    }

    public StringBuilder(CharSequence var1) {
        this(var1.length() + 16);
        this.append(var1);
    }

StringBuilder继承了AbstractStringBuilder然后引入了样子和char数列的接口

StringBuilder a= new StringBuilder("abc");

/*对应的构造方法*/ 
public StringBuilder(String var1) {
        super(var1.length() + 16);
        this.append(var1);
    }

我们一直看AbstractStringBuilder的构造方法因为StringBuilder的构造方法也从未开啊事阿:

 AbstractStringBuilder(int var1) {
        this.value = new char[var1];
    }

足看得出直接说明了一个char的数组但是关键的是外的大小是本来的轻重缓急+16,这个是怎么也,因为说了Stringbuilder是可转原的值所以可以在char[]里面加加更多之东西.当StringBuilder
对象的Length(字符串的尺寸)属性值超过Capacity属性的长短时,StringBuilder
对象中会另行组织一个字符数组Capacity属性会化新的大小

返回StringBuilder里面:

/*对应的构造方法*/ 
public StringBuilder(String var1) {
        super(var1.length() + 16);
        this.append(var1);
    }
==>
    public StringBuilder append(String var1) {
        super.append(var1);
        return this;
    }
==>
public AbstractStringBuilder append(String var1) {
        if(var1 == null) {
            return this.appendNull();
        } else {
            int var2 = var1.length();
            this.ensureCapacityInternal(this.count + var2);
            var1.getChars(0, var2, this.value, this.count);
            this.count += var2;
            return this;
        }
    }

append是往char[]数组里面加东西,分析一下,首先看下有没有发价过来没有一直归,然后一旦产生价,获取长度,然后针对长进行判定
出没有发出过容量

 private void ensureCapacityInternal(int var1) {
        if(var1 - this.value.length > 0) {
            this.value = Arrays.copyOf(this.value, this.newCapacity(var1));
        }

    }

便比如前说之逾了,会发一个新的极致酷空间,看一下value是什么

char[] value;

接下来就是朝这数组里面放内容了,把count(字符串的轻重)给改了
把Stringbuilder的append的措施分析了,其它方法可以好失去研究下都不难很轻了解的

String和StringBuffer、StringBuilder三者的属性分析

必威 8必威 9

在性能方面,由于 String 类的操作是产生新的 String 对象,而 StringBuilder 和 StringBuffer 只是一个字符数组的扩容而已,所以 String 类的操作要远慢于 StringBuffer 和 StringBuilder。简要的说, String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象, 因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象。所以经常改变内容的字符串最好不要用 String ,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后, JVM 的 GC 就会开始工作,那速度是一定会相当慢的。而如果是使用 StringBuffer 类则结果就不一样了,每次结果都会对 StringBuffer 对象本身进行操作,而不是生成新的对象,再改变对象引用。所以在一般情况下我们推荐使用 StringBuffer,如果没有同步问题,推进直接使用StringBuilder ,特别是字符串对象经常改变的情况下。而在某些特别情况下, String 对象的字符串拼接其实是被 JVM 解释成了 StringBuffer 对象的拼接。而在解释的过程中,自然速度会慢一些。

解析

结尾

先是坏写技术整理,如果有描绘错的地方为大家指出自身可以尽早改掉以免误人子弟~哈哈
我以为我莫说错啦~就是叫部分入门的弟子伴扫扫盲 刚好今天打点及这些了
有趣味的翻译了下源码啦

下面这些话语执行会发什么事情? 

String m = "hello,world";
String n = "hello,world";
String u = new String(m);
String v = new String("hello,world");

必威 10必威 11

Java堆中会分配一个长度11的char数组,并在字符串常量池分配一个由这个char数组组成的字符串,然后由栈中引用变量m指向这个字符串。

用n去引用常量池里边的同一个字符串,所以和m引用的是同一个对象。

生成一个新的字符串,但新字符串对象内部的字符数组引用着m内部的字符数组。

同样会生成一个新的字符串,但内部的字符数组引用常量池里边的字符串内部的字符数组,意思是和u是同样的字符数组。

解析

假若下一个贪图来代表的话,情况便盖是这般的(使用虚线只是表示双方其实没什么特别之关联):

必威 12

敲定就是是,m和n是和一个靶,但m,u,v都是殊的对象,但还用了平等的字符数组,并且用equal判断的语也会见回true。

如下代码的实行结果是啊?

class Workout {
    private static String m = "hello,world";
    private static String n = "hello,world";
    private static String u = new String(m);
    private static String v = new String("hello,world");

    public static void main(String[] args) throws Exception {
        test1();
    }

    public static void test1() throws Exception {
        Field f = m.getClass().getDeclaredField("value");
        f.setAccessible(true);
        char[] cs = (char[]) f.get(m);
        cs[0] = 'H';
        String p = "Hello,world";

        System.out.println(p.equals(m));
        System.out.println(p.equals(n));
        System.out.println(p.equals(u));
        System.out.println(p.equals(v));
    }
}

必威 13必威 14

执行全部是true,说明反射起作用了,即String作为一直标榜的不可变对象,竟然被修改了!可以看到,经常说的字符串是不可变的,其实和其他的final类还是没什么区别,还是引用不可变的意思。 虽然String类不开放value,但同样是可以通过反射进行修改,只是通常没人这么做而已。 即使是涉及JDK自己的 ”修改” 的方法,都是通过产生一个新的字符串对象来实现的,例如replace、toLower、concat等。 这样做的好处就是让字符串是一个状态不可变类,在多线程操作时没有后顾之忧。

解析

关押下String类的第一源码:

必威 15

来一个final类型的char数组value,它是能够于反射攻击的!全部出口true,也作证了前头的诠释是天经地义的,存在字符串常量池,且新目标可以,直接引用的常量池也好,内部的char数组都是即时一个。如果情节一律的语句。即:字符串常量通常是以编译的早晚便规定好的,定义在接近的方法区里,也就是说,不同之类似,即祭了一样的字符串,还是属于不同之靶子。所以才用通过引用字符串常量来压缩相同之字符串的数据。

脚这些话执行会发出啊工作? 

String m = "hello,world";
String u = m.substring(2,10);
String v = u.substring(4,7);

必威 16必威 17

m,u,v是三个不同的字符串对象,但引用的value数组其实是同一个。 同样可以通过上述反射的代码进行验证。

解析

必威 18

尽管发了初的字符串对象,但是引用的字符串常量池还是本来的,

下面这些言辞执行会来什么事情? 

String m = "hello,";
String u = m.concat("world");
String v = new String(m.substring(0,2));

必威 19必威 20

注意:字符串操作时,可能需要修改原来的字符串数组内容或者原数组没法容纳的时候,就会使用另外一个新的数组,例如replace,concat, + 等操作。对于String的构造方法,对于字符串参数只是引用部分字符数组的情况(count小于字符数组长度),采用的是拷贝新数组的方式,是比较特别的,不过这个构造方法也没什么机会使用到。

解析

必威 21

可窥见,m,u,v内部的字符数组连无是和一个。且单独看 m.substring(0,2);
 产生的“he”字符串引用的字符数组是常量池里之“hello,”。但是在String构造方法里,采用的是拷贝新数组的主意,然后v来引用,这里非常独特。别忘了,world也以字符串常量池里,常量池中的字符串通常是透过字面量的法有的,就比如上述m语句那样。
并且他们是以编译的时光便准备好了,类加载的时,顺便就于常量池变迁。

瞩目:在JDK7,substring()方法会创建一个新的字符数组,而不是使用已有些。

正如代码的履行结果是什么?

        String m = "hello,world";
        String u = m + ".";
        String v = "hello,world.";
        String q = "hello,world.";

        System.out.println(u.equals(v));
        System.out.println(u == v);
        System.out.println(q == v);

必威 22必威 23

true,false,true

答案

哪怕是字符串的情节是一致的,都无能够担保是与一个字符串数组,u和v虽然是平等内容的字符串,但里边的字符数组不是和一个。画成图的语句就是这般的:

必威 24

因m引用的字符数组长度固定,多一个”.”,原数组无法包容,会以另外一个初的字符数组,也就是u引用新的靶子,没有坐常量池。

如下代码的尽结果是啊?

        final String m = "hello,world";
        String u = m + ".";
        String v = "hello,world.";
        String q = "hello,world.";

        System.out.println(u.equals(v));
        System.out.println(u == v);
        System.out.println(q == v);

必威 25必威 26

true
true
true

如果让m声明为final,u和v会变成同一个对象。这应该怎么解释?这其实都是编译器搞的鬼,因为m是显式final的,常量和常量连接, u直接被编译成”hello,world.”了。

解析

画成图的言语虽是如此的:

必威 27

下程序的周转结果是?

必威 28

必威 29必威 30

false


String str1 = "hello";
这里的str1指的是方法区(java7中又把常量池移到了堆中)的字符串常量池中的“hello”,编译时期就知道的;

String str2 = "he" + new String("llo");
这里的str2必须在运行时才知道str2是什么,所以它是指向的是堆里定义的字符串“hello”,所以这两个引用是不一样的,如果用str1.equal(str2),那么返回的是True;因为两个字符串的内容一样。

和上个问题类型,因为,编译器没那么智能,它不知道"he" + new String("llo")的内容是什么,所以才不敢贸然把"hello"这个对象的引用赋给str2. 

new String("llo")外边包着一层外衣呢,如果语句改为:"he"+"llo"这样就是true了。

解析

脚这些话执行会有啊工作? 

        String m = "hello,world";
        String u = m.substring(0,2);
        String v = u.intern();

必威 31必威 32

上面我们已经知道m,u虽然是不同的对象,但是使用的是同一个value字符数组,但intern方法会到常量池里边去寻找字符串”he”,如果找到的话,就直接返回该字符串, 否则就在常量池里边创建一个并返回,所以v使用的字符数组和m,n不是同一个。

解析

画成图的语句就是如此的:

必威 33

脚这些话执行后,JVM以后会回收m,n么? 

String m = "hello,world";
String n = m.substring(0,2);
m = null;
n = null;

必威 34必威 35

字面量字符串,因为存放在常量池里边,被常量池引用着,是没法被GC的。

解析

画成图的言辞虽是如此的:

必威 36

通过地方几乎独书写的剖析,对于Java字符串,像substring、split等措施赢得的结果尚且是引用原字符数组的,如果某字符串很特别,而且未是当常量池里是的,当您以substring等措施以到平有点片新字符串之后,长期保留之言辞(例如用于缓存等),会导致原来的怪字符数组意外无法被GC的问题。如果这样的大字符串对象比较多,且每个都吃substring等艺术切割了,那么这些老目标都无法被GC,必然会内存浪费。关于这题目,常见的解决办法就是采取new
String(String
original)。在String构造方法里,采用的是拷贝新数组的点子来吃引用。

请简述 equal 和 ==的区别?

必威 37必威 38

也是初级的,但是想得满分不太容易,能拉开档次,之前看网上有的解释说,前者比较内容,后者比较地址,其实这是不严谨的,作为一个宣称掌握Java的程序员,实在不应该,equal方法是Object这个超级根类里的,默认是实现的==,只有在重写(比如字符串操作里)后,按照Java的设计规范,equal才被重写为了比较对象的内容。故,应该分类别(重写否),不同环境和不同数据类型(对象还是基本类型)下进行分析。

== 用于比较两个对象的时候,是来check 两个引用是否指向了同一块内存,比较的是地址,比较基本类型,比较的是数值大小。

equals() 是Object的方法,默认情况下,它与== 一样,比较的地址。但是当equal被重载之后,根据设计,equal 会比较对象的value。而这个是java希望有的功能。String 类就重写了这个方法,比较字符串内容。

解析

上述几只问题得出结论

  • 别时候,比较字符串内容都应当采取equals方法

  • 修改字符串操作,应该使用StringBuffer,StringBuilder

  • 足下intern方法被运行时出的字符串复用常量池中之字符串

  • 字符串操作可能会见复用原字符数组,在好几情况或造成内存泄露的题目,split,subString等办法。要小心。

下哪段程序能够正确的落实GBK编码字节约流至UTF-8编码字节流的易:

必威 39

必威 40必威 41

操作步骤就是先解码再编码,用new String(src,"GBK")解码得到字符串,用getBytes("UTF-8")得到UTF8编码字节数组

解析

于Java语言中,下列关于字符集编码(Character set encoding)和国际化(i18n)的问题,哪些是是的?

必威 42

必威 43必威 44

Java内部默认使用Unioncode编码,即不论什么语言都是一个字符占两个字节,Java的class文件编码为UTF-8,而虚拟机JVM编码为UTF-16。UTF-8编码下,一个中文占3个字节,一个英文占1个字节,Java中的char默认采用Unicode编码,所以Java中char占2个字节。B 也是不正确的,不同的编码之间是可以转换的,必须太绝对了。C 是正确的。Java虚拟机中通常使用UTF-16的方式保存一个字符。D 也是正确的。ResourceBundle能够依据Local的不同,选择性的读取与Local对应后缀的properties文件,以达到国际化的目的。

解析

言辞:char foo=’中’;  是否是?(假设源文件为GB2312编码存储,并且以javac – encoding GB2312发令编译)

必威 45必威 46

这在java中是正确的,在C语言中是错误的,java的char类型默认占两个字节。这种写法是正确的,此外java还可以用中文做变量名。因为java内部都是用unicode的,所以java其实是支持中文变量名的,比如string 世界 = "我的世界";这样的语句是可以通过的。综上,java中采用GB2312或GBK编码方式时,一个中文字符占2个字节,而char是2个字节,所以是对的。

解析

盖下Java代码用打印出什么?

必威 47

必威 48必威 49

由于replaceAll方法的第一个参数是一个正则表达式,而"."在正则表达式中表示任何字符,所以会把前面字符串的所有字符都替换成"/"。

如果想替换的只是“.”的话,正则表达式那里就要写成“\\.”或者是“[.]”。前者将“.”转义为“.”这个具体字符,后者则匹配“[]”中的任意字符,“.”就代表具体字符“.”。

输出  ///////MyClass.class

解析

因下Java代码用打印出什么?Test1是本类名。完整,类名是com.dashuai.Test1

System.out.println(Test1.class.getName().replaceAll("\\.", File.separator) + ".class");

必威 50必威 51

这个程序根据底层平台的不同会显示两种行为中的一种。如果在类UNIX 上运行,那么该程序将打印com/dashuai/Test1.class,这是正确的。但是在Windows 上运行,那么该程序将抛出异常:

Exception in thread "main" java.lang.IllegalArgumentException: character to be escaped is missing
at java.util.regex.Matcher.appendReplacement(Matcher.java:809)
at java.util.regex.Matcher.replaceAll(Matcher.java:955)
at java.lang.String.replaceAll(String.java:2223)
at wangdashuai.Test1.main(Test1.java:25)

在Windows 上出了什么错呢?事实证明,String.replaceAll 的第二个参数不是一个普通的字符串,而是一个替代字符串(replacement string),就像在java.util.regex 规范中所定义的那样。在Linux平台,是正斜杠,在win下士反斜杠。在替代字符串中出现的反斜杠会把紧随其后的字符进行转义,从而导致其被按字面含义而处理。

修改:5.0 + 版本提供了解决方案。该方法就是

String.replace(CharSequence, CharSequence),它做的事情和String.replaceAll 相同,但是它将模式和替代物都当作字面含义的字符串处理。

System.out.println(Test1.class.getName().replace(".", File.separator) + ".class");



小结:在使用不熟悉的类库方法时一定要格外小心。当你心存疑虑时,就要求助于Javadoc。还有就是正则表达式是很棘手的:它所引发的问题趋向于在运行时刻而不是在编译时刻暴露出来。还要记住,replaceAll,会把模式当做正则表达式,而replace不会。

解析

 

Java中因故正则表达式截取字符串中第一单冒出的英文左括号之前的字符串。比如:北京市(海淀区)(朝阳区)(西城区),截取结果也:北京市。正则表达式为()

必威 52必威 53

做这个题,如果想做对,必须知道和理解正则表达式的贪婪匹配。


String str="abcaxc";
Patter p="ab*c";
贪婪匹配:正则表达式一般趋向于最大长度匹配,也就是所谓的贪婪匹配。如上面使用模式p匹配字符串str,结果就是匹配到:abcaxc

非贪婪匹配:就是匹配到结果就好,尽量少的匹配字符。如上面使用模式p匹配字符串str,结果就是匹配到:abc

正则表达式默认是贪婪模式;在量词后面直接加上一个问号?就是非贪婪模式匹配。

量词:
{m,n}:m到n个
*:任意多个(0个或者多个)
+:一个或者多个
?:0或一个

(?=Expression) 顺序匹配Expression

正确答案:“.*?(?=\\()”

”(?=\\()”  就是顺序匹配正括号,前面的.*?是非贪婪匹配的意思, 表示找到最小的就可以了

解析

动Java写一个术,判断一个ip地址是否中

必威 54必威 55

    private static boolean isIp(String ip) {
        String ipString = "^(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[1-9])\\."
                + "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\."
                + "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\."
                + "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)$";
        Pattern pattern = Pattern.compile(ipString);
        Matcher matcher = pattern.matcher(ip);

        return matcher.matches();
    }

解析

Java中哪用正则表达式判断一个网址地址是否管用,写起正则表达式即可?

必威 56必威 57

String urlString = "(^(([hH][tT]{2}[pP])|([hH][tT]{2}[pP][sS]))://(([a-zA-Z0-9-~]+).)+([a-zA-Z0-9-~\\/]+)$)";

解析

顾:以上编程答案就是个人解答,不唯。

下面这长长的告词一共创建了有些个目标?

String s=“a”+”b”+”c”+”d”;

必威 58必威 59

只创建了一个String对象。System.out.println(s== “abcd”);打印的结果为true。

javac编译可以对字符串常量直接相加的表达式进行优化,不必要等到运行期去进行加法运算处理,而是在编译时去掉其中的加号,直接将其编译成一个这些常量相连的结果。

解析

若是改变成为 String s = a+b+c+d+e; 又是几只了?

即上面的每个字符串 “a”、”b”、”c”、”d”、”e”用5只变量代替。

必威 60必威 61

Java编译器,自动生成一个StringBuilder对象,对字符串变量进行拼接操作。使用append方法,分别加入a,b,c,d,e。然后调用toString()方法返回。

看append方法源码:

    public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();
        int len = str.length();
        ensureCapacityInternal(count + len);
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }

而ensureCapacityInternal方法内部使用了Arrays.copyOf()方法,该方法内部又调用了本地方法System.arraycopy(),该本地方法没有产生新对象,但是在Arrays.copyOf()内部其他地方还产生了一个新对象new char[newLength],源码如下:
    public static char[] copyOf(char[] original, int newLength) {
        char[] copy = new char[newLength];
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }

而getchars方法内部直接调用了本地方法System.arraycopy();没有产生新对象。

看StringBuilder默认构造器:
    public StringBuilder() {
        super(16);
    }
我们的字符串长度不会超过16,不会扩容。如果题目出现了总长度超过16,则会出现如下的再次分配的情况:
    /**
     * This implements the expansion semantics of ensureCapacity with no
     * size check or synchronization.
     */
    void expandCapacity(int minimumCapacity) {
        int newCapacity = value.length * 2 + 2;
        if (newCapacity - minimumCapacity < 0)
            newCapacity = minimumCapacity;
        if (newCapacity < 0) {
            if (minimumCapacity < 0) // overflow
                throw new OutOfMemoryError();
            newCapacity = Integer.MAX_VALUE;
        }
        value = Arrays.copyOf(value, newCapacity);
    }


在看最后的返回过程,调用了toString()方法,此时产生一个String新对象。
    @Override
    public String toString() {
        // Create a copy, don't share the array
        return new String(value, 0, count);
    }

故,一共产生三个对象,一个StringBuilder对象,一个String对象,一个new char[]对象。

解析

Java里String a = new String(“abc”);一共能创几单String对象?

实在这个题材,问的十分单调,不严谨,如果不幸在笔试遇到了,没道,照在标准答案:两独或一个目标,”abc”本身很成一个靶,放在字符串池(缓冲区),new的时刻再生成一个,结果是2只。如果前就利用了“abc”,那么”abc”本身便不再生成对象,就是1只。一个字符串常量池里的,一个new出来的,但是如果面试中遇了,可以品尝的和面试官沟通是题材之题材。请圈业JVM开发的
R大神的解答:

请别再以“String s = new String(“xyz”);创建了聊只String实例”来面试了吧

StringBuffer sb = new StringBuffer(“abc”); 创建了几只String对象?

必威 62必威 63

这种问题,一定是问创建多少string对象,否则涉及其他对象,就太多了,不好说。看字节码:

Code:
stack=3, locals=2, args_size=1
0: new #16 // class java/lang/StringBuffer
3: dup 
4: ldc #18 // String 123
6: invokespecial #20 // Method java/lang/StringBuffer."<init>":(Ljava/lang/String;)V
9: astore_1 
10: return

答案很明显。ldc:从常量池加载string,如果常量池中有“123”,就不创建。而new指令 :创建了一个buffer对象。

解析

Java里对密码等敏感信息先采取字符数组还是字符串,为什么?

必威 64必威 65

虽然String加载密码之后可以把这个变量扔掉,但是字符串并不会马上被GC回收(考虑常量池,即使没有常量池引用,GC也不一定立即回收它),一但进程在GC执行到这个字符串之前被dump,dump出的的转储中就会含有这个明文的字符串。那如果去“修改”这个字符串,比如把它赋一个新值,那么是不是就没有这个问题了?答案是否定的,因为String本身是不可修改的,任何基于String的修改函数都是返回一个新的字符串,原有的还会在内存里。对于char[]来说,可以在抛弃它之前直接修改掉它里面的内容,密码就不会存在了。但是如果什么也不做直接交给gc的话,也会存在上面一样的问题。

解析

以下Java代码用打印出什么?

        System.out.print("H"+"a");
        System.out.print('H'+'a');

必威 66必威 67

打印的是Ha169。

第一个对System.out.print 的调用打印的是Ha:它的参数是表达式"H"+"a",显然它执行的是一个字符串连接。

第二个对System.out.print 的调用,'H'和'a'是字符型字面常量,这两个操作数都不是字符串类型的,所以 + 操作符执行的是加法而不是字符串连接。编译器在计算常量表达式'H'+'a'时,是通过我们熟知的拓宽原始类型转换将两个具有字符型数值的操作数('H'和'a')提升为int 数值而实现的(类似的还有byte,short,char类型计算的时候都是自动提升为int)。从char 到int 的拓宽原始类型转换是将16 位的char 数值零扩展到32 位的int。对于'H',char 数值是72,而对于'a',char 数值是97(需要记一下,0的asc码是48,A是65,a是97,这些常用的),因此表达式'H'+'a'等价于int常量72 + 97=169。



修改为打印Ha,可以使用类库:

StringBuffer sb = new StringBuffer();
sb.append('H');
sb.append('a');
System.out.println(sb);



很丑陋。其实我们还是有办法去避免这种方式所产生的拖沓冗长的代码。 你可以通过确保至少有一个操作数为字符串类型,来强制 + 操作符去执行一个字符串连接操作,而不是一个加法操作。这种常见的惯用法用一个空字符串("")作为一个连接序列的开始

System.out.println("" + 'H' + 'a');

解析

坐下Java代码用打印出什么?

System.out.print("2 + 2 = " + 2 + 2);

必威 68必威 69

2 + 2 = 22

因为进行的是字符串连接,不是数值加法计算。

修改:
        int a = 2 + 2;
        System.out.print("2 + 2 = " + a);

解析

必威 70必威 71

这几道题说明,使用字符串连接操作符要格外小心。+ 操作符当且仅当它的操作数中至少有一个是String 类型时,才会执行字符串连接操作;否则,它执行的就是加法。如果要连接的没有一个数值是字符串类型的,那么你可以有几种选择:
• 预置一个空字符串;
• 将第一个数值用String.valueOf 显式地转换成一个字符串;
• 使用一个字符串缓冲区StringBuilder等;
• 或者如果你使用的JDK 5.0,可以用printf 方法,类似c语言。

小结

因为下Java代码用打印出必威什么?

        char[] numbers = {'1', '2', '3'};
        System.out.println(numbers);

必威 72必威 73

尽管char 是一个整数类型,但是许多类库都对其进行了特殊处理,因为char数值通常表示的是字符而不是整数。例如,将一个char 数值传递给println 方法会打印出一个Unicode 字符而不是它的数字代码。字符数组受到了相同的特殊处理:println 的char[]重载版本会打印出数组所包含的所有字符,而String.valueOf和StringBuffer.append的char[]重载版本的行为也是类似的。

解析

为下Java代码用打印出什么?

        String letters = "ABC";
        char[] numbers = {'1', '2', '3'};
        System.out.println(letters + " easy as " + numbers);

必威 74必威 75

打印的是诸如 ABC easy as [C@1db9742 之类的东西。尽管char 是一个整数类型,但是许多类库都对其进行了特殊处理,然而,字符串连接操作符在这些方法中没有被定义。该操作符被定义为先对它的两个操作数执行字符串转换,然后将产生的两个字符串连接到一起。对包括数组在内的对象引用的字符串转换定义如下:

如果引用为null,它将被转换成字符串"null"。否则,该转换的执行就像是不用任何参数调用该引用对象的toString 方法一样;

但是如果调用toString 方法的结果是null,那么就用字符串"null"来代替。

那么,在一个非空char 数组上面调用toString 方法会产生什么样的行为呢?

数组是从Object 那里继承的toString 方法,规范中描述到:“返回一个字符串,它包含了该对象所属类的名字,'@'符号,以及表示对象散列码的一个无符号十六进制整数”。有关Class.getName 的规范描述到:在char[]类型的类对象上调用该方法的结果为字符串"[C"。将它们连接到一起就形成了在我们的程序中打印出来的那个字符串。



有两种方法可以修改这个程序。可以在调用字符串连接操作之前,显式地将一个数组转换成一个字符串:

String letters = "ABC";
char[] numbers = {'1', '2', '3'};
System.out.println(letters + " easy as " + String.valueOf(numbers));



可以将System.out.println 调用分解为两个调用,以利用println 的char[]重载版本:
System.out.print(letters + " easy as ");
System.out.println(numbers);

解析

以下Java代码用打印出什么?

        String letters = "ABC";
        Object numbers = new char[] { '1', '2', '3' };
        System.out.print(letters + " easy as ");
        System.out.println(numbers);

必威 76必威 77

打印ABC easy as [C@1db9742这样的字符串,因为它调用的是println 的Object 重载版本,而不是char[]重载版本。



总之,记住:

char 数组不是字符串。要想将一个char 数组转换成一个字符串,就要调用String.valueOf(char[])方法。某些类库中的方法提供了对char 数组的类似字符串的支持,通常是提供一个Object 版本的重载方法和一个char[]版本的重载方法,而之后后者才能产生我们想要的行为。。

解析

坐下Java代码用打印出什么?

        final String pig = "length: 10";
        final String dog = "length: " + pig.length();
        System.out.println("Animals are equal: " + pig == dog);

必威 78必威 79

分析可能会认为它应该打印出Animal are equal: false。对吗?

运行该程序,发现它打印的只是false,并没有其它的任何东西。它没有打印Animal are equal: 。+ 操作符,不论是用作加法还是字符串连接操作,它都比 == 操作符的优先级高。因此,println 方法的参数是按照下面的方式计算的:

System.out.println(("Animals are equal: " + pig) == dog);

这个布尔表达式的值当然是false,它正是该程序打印的输出。避免此类错误的方法:在使用字符串连接操作符时,当不能确定你是否需要括号时,应该选择稳妥地做法,将它们括起来。



小结:

字符串连接的优先级不应该和加法一样。这意味着重载 + 操作符来执行字符串连接是有问题的。

解析

因为下Java代码打印26对么,如果不对,为什么?

        System.out.println("a\u0022.length() + \u0022b".length());

必威 80必威 81

对该程序的一种很肤浅的分析会认为它应该打印出26,因为在由两个双引号"a\u0022.length()+\u0022b"标识的字符串之间总共有26 个字符。稍微深入一点的分析会想到 \u0022 是Unicode 转义字符,其实它是双引号的Unicode 转义字符,肯定不会打印26。



如果提示你Unicode 转义字符是双引号,打印什么?

有人说打印16,因为两个Unicode 转义字符每一个在源文件中都需要用6个字符来表示,但是它们只表示字符串中的一个字符。因此这个字符串应该比它的外表看其来要短10 个字符。



其实如果运行,它打印的既不是26 也不是16,是2。理解这个题的关键是要知道:Java 对在字符串字面常量中的Unicode 转义字符没有提供任何特殊处理。编译器在将程序解析成各种符号之前,先将Unicode转义字符转换成为它们所表示的字符。因此,程序中的第一个Unicode转义字符将作为一个单字符字符串字面常量("a")的结束引号,而第二个Unicode 转义字符将作为另一个单字符字符串字面常量("b")的开始引号。程序打印的是表达式"a".length()+"b".length(),即2。



可能的情况是该程序员希望将两个双引号字符置于字符串字面常量的内部。使用Unicode 转义字符你是不能实现这一点的,但是你可以使用转义字符序列来实现。表示一个双引号的转义字符序列是一个反斜杠后面紧跟着一个双引号(\”)。如果将最初的Unicode 转义字符用转义字符序列来替换,那么它将打印出16:

System.out.println("a\".length() + \"b".length());

解析

必威 82必威 83

在字符串和字符字面常量中要优先选择的是转义字符序列,而不是Unicode 转义字符。Unicode 转义字符可能会因为它们在编译序列中被处理得过早而引起混乱。不要使用Unicode 转义字符来表示ASCII 字符。在字符串和字符字面常量中,应该使用转义字符序列。

小结

为下Java代码用打印出什么?

/**
* 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 Test1 {
    public static void main(String[] args) {
        System.out.print("Hell");
        System.out.println("o world");
    }
}

必威 84必威 85

通不过编译。问题在于注释的第三行,它包含了字符\units。这些字符以反斜杠(\)以及紧跟着的字母u 开头的,而它(\u)表示的是一个Unicode 转义字符的开始。而这些字符后面没有紧跟四个十六进制的数字,因此,这个Unicode 转义字符是错误的,而编译器则被要求拒绝该程序。即使是出现在注释中也是如此。

Javadoc注释中要小心转移字符,要确保字符\u 不出现在一个合法的Unicode 转义字符上下文之外,即使在注释中也是如此。在机器生成的代码中要特别注意此问题。

解析

必威 86必威 87

除非确实是必需的,否则就不要使用Unicode 转义字符。它们很少是必需的。

小结

以下Java代码运行将会见冒出什么问题?

        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) + " ");
        }

必威 88必威 89

首先,byte 数组从0 到255 每一个可能的byte 数值进行了初始化,然后这些byte 数值通过String 构造器被转换成了char 数值。最后,char 数值被转型为int 数值并被打印。打印出来的数值肯定是非负整数,因为char 数值是无符号的,因此,你可能期望该程序将按顺序打印出0 到255 的整数。

如果你运行该程序,可能会看到这样的序列。但是在运行一次,可能看到的就不是这个序列了。如果在多台机器上运行它,会看到多个不同的序列,这个程序甚至都不能保证会正常终止,它的行为完全是不确定的。这里的罪魁祸首就是String(byte[])构造器。有关它的规范描述道:“在通过解码使用平台缺省字符集的指定byte 数组来构造一个新的String 时,该新String 的长度是字符集的一个函数,因此,它可能不等于byte 数组的长度。当给定的所有字节在缺省字符集中并非全部有效时,这个构造器的行为是不确定
的”。

到底什么是字符集?从技术角度上讲,字符集是一个包,包含了字符、表示字符的数字编码以及在字符编码序列和字节序列之间来回转换的方式。转换模式在字符集之间存在着很大的区别:某些是在字符和字节之间做一对一的映射,但是大多数都不是这样。ISO-8859-1 是唯一能够让该程序按顺序打印从0 到255 的整数的缺省字符集,它更为大家所熟知的名字是Latin-1[ISO-8859-1]。J2SE 运行期环境(JRE)的缺省字符集依赖于底层的操作系统和语言。如果你想知道你的JRE 的缺省字符集,并且你使用的是5.0 或更新的版本,那么你可以通过调用java.nio.charset.Charset.defaultCharset()来了解。如果你使用的是较早的版本,那么你可以通过阅读系统属性“file.encoding”来了解。

修改:

当你在char 序列和byte 序列之间做转换时,你可以且通常是应该显式地指定字符集。除了接受byte 数字之外,还可以接受一个字符集名称的String 构造器就是专为此目的而设计的。如果你用下面的构造器去替换在最初的程序中的String 构造器,那么不管缺省的字符集是什么,该程序都保证能够按照顺序打印从0 到255的整数:
String str = new String(bytes, "ISO-8859-1");

这个构造器声明会抛出UnsupportedEncodingException 异常,因此你必须捕获它,或者更适宜的方式是声明main 方法将抛出它,要不然程序不能通过编译。尽管如此,该程序实际上不会抛出异常。Charset 的规范要求Java 平台的每一种实现都要支持某些种类的字符集,ISO-8859-1 就位列其中。

小结:

每当你要将一个byte 序列转换成一个String 时,你都在使用某一个字符集,不管你是否显式地指定了它。如果你想让你的程序的行为是可预知的,那么就请你在每次使用字符集时都明确地指定。

解析

盖下Java代码用打印出什么?

必威 90

必威 91必威 92

如果回答打印LETTER UNKNOWN NUMERAL,那么你就掉进陷阱里面了。程序连编译都通不过。因为注释在包含了字符*/的字符串内部就结束了,字面常量在注释中没有被特殊处理。更一般地讲,注释内部的文本没有以任何方式进行特殊处理。因此,块注释不能嵌套。

总之,块注释不能可靠地注释掉代码段,应该用单行的注释序列来代替。

解析

因为下Java代码用打印出什么?

        System.out.print("iexplore:");
        http://www.google.com;
        System.out.println(":maximize");

必威 93必威 94

正确运行并打印 iexplore::maximize。在程序中间出现的URL是一个语句标号(statement label),后面跟着一行行尾注释(end-of-line comment)。在Java中很少需要标号,这多亏了Java 没有goto 语句(作为保留字)。

Java中很少被人了解的特性:”实际上就是你可以在任何语句前面放置标号。这个程序标注了一个表达式语句,它是合法的,但是却没什么用处。”



注意:令人误解的注释和无关的代码会引起混乱。要仔细地写注释,并让它们跟上时代;要切除那些已遭废弃的代码。还有就是如果某些东西看起来奇怪,以至于不像对的,那么它极有可能就是错的。

解析

因下Java代码用打印出什么?

public class Test1 {
    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);
    }
}

必威 95必威 96

乍一看,这个程序可能会在一次又一次的运行中,以相等的概率打印出Pain,Gain 或 Main。看起来该程序会根据随机数生成器所选取的值来选择单词的第一个字母:0 选M,1 选P,2 选G。

它实际上既不会打印Pain,也不会打印Gain。也许更令人吃惊的是,它也不会打印Main,并且它的行为不会在一次又一次的运行中发生变化,它总是在打印ain。

一个bug 是所选取的随机数使得switch 语句只能到达其三种情况中的两种。Random.nextInt(int)的规范描述道:“返回一个伪随机的、均等地分布在从0(包括)到指定的数值(不包括)之间的一个int 数值”。这意味着表达式rnd.nextInt(2)可能的取值只有0和1,Switch语句将永远也到不了case 2 分支,这表示程序将永远不会打印Gain。nextInt 的参数应该是3 而不是2。这是一个相当常见的问题。

第二个bug 是在不同的情况(case)中没有任何break 语句。不论switch 表达式为何值,该程序都将执行其相对应的case 以及所有后续的case。因此,尽管每一个case 都对变量word 赋了一个值,但是总是最后一个赋值胜出,覆盖了前面的赋值。最后一个赋值将总是最后一种情况(default),即 M。这表明该程序将总是打印Main,而从来不打印Pain或Gain。在switch的各种情况中缺少break语句是非常常见的错误。

最后一个bug 是表达式new StringBuffer('M')可能没有做你希望它做的事情。你可能对StringBuffer(char)构造器并不熟悉,这很容易解释:它压根就不存在。StringBuffer 有一个无参数的构造器,一个接受一个String 作为字符串缓冲区初始内容的构造器,以及一个接受一个int 作为缓冲区初始容量的构造器。在本例中,编译器会选择接受int 的构造器,通过拓宽原始类型转换把字符数值'M'转换为一个int 数值77。换句话说,new StringBuffer('M')返回的是一个具有初始容量77 的空的字符串缓冲区。该程序余下的部分将字符a、i 和n 添加到了这个空字符串缓冲区中,并打印出该缓冲区那总是ain 的内容。为了避免这类问题,不管在什么时候,都要尽可能使用熟悉的惯用法和API。如果你必须使用不熟悉的API,那么请仔细阅读其文档。在本例中,程序应该使用常用的接受一个String 的StringBuffer 构造器。



修改:

public class Test1 {

    private static Random rnd = new Random();

    public static void main(String[] args) {

        StringBuffer word = null;

        switch (rnd.nextInt(3)) {

        case 1:

            word = new StringBuffer("P");

            break;

        case 2:

            word = new StringBuffer("G");

            break;

        default:

            word = new StringBuffer("M");

            break;

        }

        word.append('a');

        word.append('i');

        word.append('n');

        System.out.println(word);

    }

}

尽管这个程序订正了所有的bug,它还是显得过于冗长了。下面是一个更优雅的版本:

private static Random rnd = new Random();

public static void main(String[] args) {

  System.out.println("PGM".charAt(rnd.nextInt(3)) + "ain");

}

下面是一个更好的版本。尽管它稍微长了一点,但是它更加通用: 

public class Test1 {

    public static void main(String[] args) {

        String a[] = { "Main", "Pain", "Gain" };

        System.out.println(randomElement(a));

    }

    private static Random rnd = new Random();

    private static String randomElement(String[] a) {

        return a[rnd.nextInt(a.length)];

    }

}

总结一下:首先,要当心Java随机数产生器的特点。其次,牢记在 switch 语句的每一个 case中都放置一条 break 语句。第三,要使用常用的惯用法和 API,并且当模糊的时候,一定要参考相关的文档。第四,一个 char 不是一个 String,而是更像一个 int

解析

 

相关文章