我与java的邂逅

这是一篇对java语言的感悟和总结

总想写点什么,但又不知道从哪开始。从在大学接触java、自学java到毕业后工作中使用java已经有7年了。想写点自己的一些感悟和从技术上对java的一些总结。对于一个非计算机专业的码农,是接触java后才让我决定从事软件开发(后来的互联网开发)的道路。

回忆一下我是怎么认识java的?当时还在学校实验室画着电路图、写着汇编、写着C,突然老师让我写一个在浏览器中通过串口与单片机通信的程序。当时傻了,以前都是用VB来写串口的上位机程序。于是到处查资料,当时java做web开发已经很流行了,API上有CommPort可进行串口通信的抽象类,最重要的他还有个JNI,可以调用我写的C程序,就这样与java邂逅了。

在接触java前,没有接触过真正面向对象的语言,在学习java的过程中,被这种面向对象的思想吸引了。决定系统性的学习java语言,一进去就拔不出来了,哈哈,单纯语言来说,java其实很简单,但是java的框架太多,java延伸出去的技术又有很多,java的方方面面很广,就连IDE也有很多。虽然说java语言不难,但是要写出简洁、优雅而且性能高的java代码绝对要经历长期的实践和学习,对本质的知识有深刻的了解,比如jvm中对象的内存怎么分配的、字符串和集合怎么优化、jvm的参数怎么优化、垃圾收集器怎么选择、怎么合理的使用线程、什么场合使用并发编程、自己用NIO2实现Socket还是选择成熟的框架(Netty、mina)等,没有一个简单,在真实的项目开发尤其现在的服务端开发遇到的问题会更多。我从来都不相信现在市面上的“一周掌握某语言、一月掌握某技术、七天七XX”等。学习技术是一个漫长的过程,不仅是理论的更要去实践,还必须有自己的思想。

2015.3.29 String的个人理解
很多资料都说进行字符串拼接的时候尽量使用StringBuilder,这样会比使用“+”要节约开销,因为”+”会重新分配内存空间给新的String,而StringBuilder则是分配连续的内存保存,这样可以减少内存碎片同时可以减少垃圾回收频率。

但是这并不是绝对的,我们来看下下面这段代码:

String s = "a" + "b" + "c"

在JDK1.7及以前版本中,常量都会在永久代的常量池上分配一块内存,”a”、”b”、”c”都是常量,但是编译器的编译的时候会帮我们优化,编译器会认为常量不可变了,就可以进行叠加操作:

StringBuilder sTmp = new StringBuilder();
sTmp.append("a").append("b").append("c");
sTmp.toString()

编译器优化后类似于以上的代码,所以说在编译阶段就完成赋值的操作使用”+”并不会比使用StringBuilder有更大的开销。

谈了字符串的拼接再来谈下equals()方法,谈到equals()就不得不谈hashCode()。先看一下这段代码:

String a = "hi";
String b = "hi";
a.equals(b);
a == b;

上面将返回2个true。先来讲讲==,==在java中比较2个对象的内存地址是不是一样的,或是否指向同一个内存地址;a和b都是常量,而且是字符串相等的常量,所以指向的字符串”hi”存于常量池中只有一个”hi”对象,==返回true;equals()则是先判断==,如果不是同一个对象则遍历每个char;java中String内部value是一个char数组,只是在C语言的char[]上加了一个语法糖隐藏了this指针则变成了面向对象语言。讲到了常量池的内存分配,则必须讲一下intern()方法,先是判断在常量池中是否存在字符串,存在则返回常量池中的地址,不存在则在常量池中创建再返回地址;但是1.7以后有所变化,常量池中存的只是字符串在堆中的引用。
hashCode()让字符串中每个字符参与算法计算出当前字符串在内存中的一个范围(桶),桶可以有链表或其他数据结构形成,再用equals()来遍历桶判断字符串是否在当前桶中。hashCode()是一个native方法,native方法时会有一定开销的。我以前将一个对象的某个属性参与了hashCode的重写,将对象创建及初始化完成后,在运行过程中改变此属性的值,导致了找不到该对象的情况出现,恍然大悟啊!

StringBuilder和集合的扩容

jdk源码中,StringBuilder是按2倍或者字符串最新长度来扩容的。
List则按3/2+1的方案来扩容。
Map有一个基数因子,当达到初始化长度的3/4时开始扩容,也是按原来的2倍。

sosop hou 29 March 2015
blog comments powered by Disqus