现在的位置: 首页 > 综合 > 正文

你是及格的java程序员吗(1)

2013年08月06日 ⁄ 综合 ⁄ 共 4258字 ⁄ 字号 评论关闭

写了很多年的java程序就一定是一个及格的java程序员吗?我不敢说自己就是一个合格的java程序员,勉强算及格吧。
我们从项目中最常用的String说起吧
1.关于replace和replaceAll
很多哥们对这两个兄弟的认识就是:replace只进行一次替换,replaceAll是全部替换。
replace是普通字符串替换,replaceAll是正则替换,这个要怪就怪Sun的工程师们给这两个方法取名取得不好,replaceAll如果改名叫做replaceRegex就好多了。
此外:replaceAll的第一个是正则表达式,第二个是替换字符串,这个函数的正确使用方式是:
对第一个参数使用:Pattern.quote()
对第二个参数使用:Matcher.quoteReplacement()
参考:《java puzzlers》Puzzle 20: What's My Class? Puzzle 21: What's My Class, Take 2
2.关于split
实际项目中,大家经常会用到把几个参数用一个特殊的字符拼到一块,然后用split拆分的情况,比如说:
String params = "aa|bb|cc|dd";
String paramArray[] = params.split("|");
看上去没错,实际上人家split也是用正则表达式来进行拆分的,而|在正则里面是有特殊意义的,所以这就出问题了。
既然replace可以有个双胞胎兄弟replaceAll,那么,split为什么就不可以有双胞胎姐妹splitAll或者说splitRegex呢?
此外,split还可以有第二个参数,split(regex, limit),这第二个参数是什么意义呢?
我们api文档怎么说:
The limit parameter controls the number of times the pattern is applied and therefore affects the length of the resulting array. If the limit n is greater than zero then the pattern will be applied at most n - 1 times, the array's length will be no greater
than n, and the array's last entry will contain all input beyond the last matched delimiter. If n is non-positive then the pattern will be applied as many times as possible and the array can have any length. If n is zero then the pattern will be applied as
many times as possible, the array can have any length, and trailing empty strings will be discarded.
如果limit<0,将会尽最大可能进行拆分
如果limit=0,将会尽最大可能进行拆分,但是会丢掉末尾的空字符串
如果limit>0,最多会进行limit-1次拆分,拆分出来的字符串数组的长度最大是limit。
"aa,bb,,,,,".split(",").length=2
"aa,bb,,,,,".split(",",0).length=2
"aa,bb,,,,,".split(",",-1).length=7
3.曾经遇见过synchronized(String)这种东西
这个不大好描述,还是直接看代码吧:
class Worker implements Runnable{
    private int count = 0;
    @Override
    public void run() {
        String lock = "mylock";
        synchronized(lock){
            try{Thread.sleep(100);}catch(Exception e){e.printStackTrace();}
            count++;
            System.out.println(Thread.currentThread().getName()+" count:"+count);
            count--;
            System.out.println(Thread.currentThread().getName()+" count:"+count);
        }
    }
}
public class Test1 {
    public static void main(String[] args) {
        Worker worker = new Worker();
        for(int i=0;i<10;i++){
            Thread t = new Thread(worker);
            t.start();
        }
    }
}
乍一看,锁对象是方法的内部变量,因此,根本起不到同步的作用,但是,实际上呢?运行一下代码就看出来了,人家确实是同步的!
因为,String的字面常量在一个虚拟机的常量池内部只会保留一份,因此,就算是内部变量,实际上也只有一份,因此是可以用来当锁使用的,
当然,可以这么做不等于一定就得这么做,反正我是没看出来非得这么使用的理由。如果想让上面的锁失效,也很简单:String lock = new String("mylock");这样的话,
每次都是new出来的新对象,肯定不是同一把锁,肯定就不会同步了!
下面是字节码:
(1)String lock = "mylock";的时候
stack=3, locals=5, args_size=1
         0: ldc           #3                  // String mylock
         2: astore_1      
         3: aload_1       
         4: dup           
         5: astore_2      
         6: monitorenter  
(2)String lock = new String("mylock");的时候
stack=3, locals=5, args_size=1
         0: new           #3                  // class java/lang/String
         3: dup           
         4: ldc           #4                  // String mylock
         6: invokespecial #5                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
         9: astore_1      
        10: aload_1       
        11: dup           
        12: astore_2      
        13: monitorenter  
可以参考:java language specification,3.10.5 String Literals这里面有最权威的例子。
不推荐使用String对象作为锁,如果非要使用,可以使用synchronized(str.intern())。
4.下面我们来看一个关于集合初始化的问题:
java里面所有的集合类基本上都有一个指定集合大小的构造函数,比如ArrayList,如果我们确切的知道List的元素数量是n个,我们在定义List的时候,可以这样:
List<String> list = new ArrayList<String>(n);这并不是说,用这个构造函数构造出来的List只能装n个元素,它只是说初始化的时候,可以装n个元素,当装不下的时候还是会进行扩容的。
指定初始化容量的好处是显而易见的,因为默认ArrayList是有10个元素,如果我们的List的元素比较少,只有5个元素,我们可以省掉一半的空间,如果元素比较多是1000个,
如果不指定初始化大小,那中间要经过12次扩容才可以放得下这1000个元素int newCapacity = oldCapacity + (oldCapacity >> 1);hashmap稍微有点特殊,因为它还有一个负载因子的概念,也就是说,HashMap不会等到把所有的桶都装满以后才进行扩容,而是达到一定的比例,就开始扩容,默认是0.75,因为HashMap扩容需要对里面所有的元素重新计算hashcode,因此,如果元素数目比较多,相对来说还是一件挺耗时的事情,如果可以的话,我们尽量减少扩容的次数。
下面是我写的一个用来计算HashMap初始化容量的一个类:
public class HashMapUtil {
    /**
     * 计算存储realCount个元素所需的hashmap的capacity,可以避免扩容
     *
     * @param realCount 实际要存储的元素的数目
     * */
    public static int capacity(int realCount){
        return capacity(realCount,0.75);
    }
    /**
     * 计算存储realCount个元素所需的hashmap的capacity,可以避免扩容
     *
     * @param realCount   实际要存储的元素的数目
     * @param loadbalance 负载因子
     * */
    public static int capacity(int realCount,double loadbalance){
        if(realCount <= 1){
            return 1;
        }
        int capacity = 1;
        while(capacity * loadbalance < realCount){
            capacity = capacity << 1;
        }
        return capacity;
    }
}
类似的还有StringBuffer和StringBuilder,如果在构造的时候就指定其大小,可以减少扩容的次数,提高效率。

抱歉!评论已关闭.