日期:2014-05-20  浏览次数:20700 次

吐槽一下JAVA:有人注意到这个问题了吗?
最近从C++转到JAVA,当然学JAVA只是业余爱好,主业仍是C/C++。

[color=#FF0000]一[/color]
我个人觉得JAVA的String类设计得不是很合理。
除了字符串常量在编译时能够确定这种情况下,可以将其放入常量池共享,其它情况基本都不太适合使用。
在运行时创建一个String对象时,如果常量池没有该字符串则创建两个,并将String对象与之关联。
我不明白,为什么非要在常量池中建立一个副本。
有人说,是为了字符串比较速度更快。
我却不以为然,如果你仔细分析就会发现不建立常量池与建立常量池来比较字符串的时间复杂度是一样的。

众所周知,String对象是不可变的,这就意味着如果要改变字符串就会产生新的字符串对象。
我看到书上充斥着大量的例子,它们都在使用String来处理可变的字符串,效率低到令人发指,就好像让你求一个catalan数,你不去总结公式,反而在那里一一穷举一样。

我觉得常量池能提高效率的唯一优势就是在于共享,如果不能共享,其反而弄巧成拙。事实上,有多少人在重复共享呢?

好吧,我找到了可变的字符串类StringBuilder(Buffer),我决定以后主要就使用它了。
它与String在处理字符串应用上一样十分强大,很棒,的确。

可当我从文件中读取字符串来处理时,我傻眼了,好像没有以StringBuilder作为参数的读取方法。你要么使用char[],要么使用String吧。String就算了吧,当我读个几M的文件,这货恐怕要新建一坨一坨的对象吧。
char[]也不错嘛,可是读完后,当我想处理时,我就崩溃了,我恐怕得自己实现所有的方法吧,要是这样,C++就要抽我的脸了!

好吧,我恐怕还得转到String或StringBuilder来,因为它们提供了大量现成的实用方法。这样,又从char[]复制一坨一坨的字符到String(Builder)对象。

大哥,效率啊,我在C/C++中读一遍文件,在JAVA中恐怕无形中要读2到3遍吧,更何况你还是解释型的。
设计者难道觉得设计成直接读到StringBuilder中会很显得极不优雅吗??


[b]二、[/b]
仁者见仁,智者见智的checked exception问题,虽然,这个争论很难达成一致结果,但我还是觉得C#和Python取消checked exception是好的做法。当我看到我的代码中处处充斥着try-catch这种效率极低的语句时或throws声明时,我觉得JAVA还真不优雅!!


[b]三、[/b]
for-each循环,我没有用过,好像只有在看书的时候,把书上的例子敲了一遍。但本能地想到,这货肯定又将对象复制了一遍,因为for-each是只读的,你不可能改变其元素。当对象很大很多时,创建对象的副本绝对是白痴做法。

[b]四、[/b]
泛型!
JAVA的泛型真是鸡肋,我真是佩服设计者能想出“擦除”这种投机取巧的方法。
可能是C++ Template给我的感觉是太优秀了,所以有点不适应JAVA这种绕着弯变戏法似的伪泛型。

[b]五、[/b]
之前一直用记事本,今天下了个eclipse,试用了下,功能还算强大,只不过默认设置不是很好,需要手动更改。
传说中eclipse比较慢,今日一见果然名不虚传,哥I5双核CPU都要一分钟才能打开,你让秒开的CB情何以堪啊!


好了,吐槽完了,这是我初学JAVA后临时的想法。JAVA虽然有瑕疵,但不掩瑜。总体来说,还算不错!
其实,只要是想听听大神们对String类的见解,其它的几条可以忽视了。

PS:本人小菜鸟一只,大神还请嘴下留情,谢谢!!

------解决方案--------------------
虽然现在的大多数JVM实现都采用的JIT形式

但是可以实现这样一种虚拟机:
读取class文件,直接将其编译成native代码,以后一直运行native代码,只是将编译期的动作推迟到了运行期的前期

jvm规范只是规定了它一定要能读懂和执行.class文件,换句话说,jvm的实现很自由,可以实现一个jvm和java编译器,将java代码编译成本地代码,jvm可以选择性的实现识别和执行这种本地代码,但必须要识别和执行class文件,当然,正如楼主所言,不能跨平台了。

GCJ这个编译器可以将java代码直接编译成本地代码,可以研究研究


最后,其实,我想表达的是,jvm的实现自由很大,哦了

探讨

难道不该说JIT?

引用:

引用:

更何况你还是解释型的
楼主需要继续好好读读

你敢说JAVA是编译型的??
虽然,我JAVA没你好,但我相信编译原理我比你懂得多。。。

不要说JIT,任何狭义的编译型语言都不能跨平台。。。

------解决方案--------------------
楼主所谓副本是指reference copy吧,string设计成不可变主要是数组长度本来就不可随便变的。String内部就是个char[],长度固定的,比如
char [5],你想加一位就要做个char[6]然后copy再加一位,string适合reference运算。StringBuilder又不是每次加一个字符串就copy一次,他是申请一段内存,当发现不够了,再扩大一次。
基本上是int newCapacity = value.length * 2 + 2;而且复制数组是调用c或者操作系统的copy方式,所以效率不是他负责的。

文件读取最基本的是字节流,就是char,你所说的什么string估计就是加了个修饰,自己做了个buffer拼接,而且读文件又不是java调用的,底层那块调用是交给操作系统的.
哥读写文件写过无数,从来没听说要读两遍的,不知道你怎么看书的。


foreach只是能让你能拿到对象的reference,谁你和说是复制一遍,你拿到reference了,当然可以改变对象内容,和foreach没半毛钱关系。

另外eclipse又不是存java开发的,底层那块都是用c++写的。


最后吐槽一下,楼主的学习能力,简直令人发指。呵呵
------解决方案--------------------
如果是new String,形如new String("avc");是有两个,因为new本身就是去要求一个新对象,没办法,另外一个就是缓存起来的

另外就是String#intern方法,两个具有相同码点序列的的String对象调用intern后得到的应该是同一个对象。那么对于这种呢:
String a = new String(args[0]);
boolean b = a == a.intern();

不同的jvm实现还真的不一样,hotspot跟jrocket就不一样,hotspot1.6与hotspot1.7之前的版本也不一样,究其原因是规范中并没有规定其行为。

这两个链接不得不看:
http://www.iteye.com/topic/774673

http://www.iteye.com/topic/1112592

探讨

“在运行时创建一个String对象时,如果常量池没有该字符串则创建两个”,说法貌似有误,哪里写了要创建两个?常量池中存的也是引用,总的来说,就是相当于缓存

------解决方案--------------------
java有很多设计不合理的地方,String那个常量池我个人是觉得用途不大

另外一点,class文件的结构也遭人诟病,有人觉得不够紧凑,结构也不太好,很多人做了这方面的研究,但由于要向前兼容,大都失败告终