标签: java

使用lombok简化代码

lombok是一款可以精减java代码、提升开发人员生产效率的辅助工具,利用注解在编译期自动生成setter/getter/toString()/constructor之类的代码。减少了代码行数,不会看到一大堆的get、set干扰视线

官网地址:https://projectlombok.org/ 首页有一段几分钟的演示视频,看完就明白是怎么回事了。

@Data
public class Something {
....
}

加上Data注解以后就不用在类中添加get、set方法了

各种注解的详细用法,请参考:https://projectlombok.org/features/index.html

IDEA下使用时,可以通过插件的形式安装,插件下载地址:https://github.com/mplushnikov/lombok-intellij-plugin/releases

然后 Plugins -> Install plugin from disk… 选择下载的zip包安装,重启idea即可。

另外,还有一个关键设置:27612-20160217115233300-1759134314

图解java String的不变性

1. 声明一个字符串

String s = “abcd”;
s存储的字符串的引用,下面的箭头就是存储引用的意思1

2. 把这个字符串变量分配给另一个变量

String s2 = s;

s2存储相同的引用值

2

3. 连接字符串

s = s.concat(“ef”);
s现在存储新字符串对象的引用

3

总结

字符串一旦在内存中创建,就不能改变。我们应该知道字符串的所有方法都不能改变它自己,而是返回一个新的字符串。

如果我们想要一个可变的字符串,我们应该使用StringBuffer 或者 StringBuilder。否则会浪费gc时间,应为每次都会创建新的字符串对象。

Java设计模式教程(二)——责任链模式(Chain of Responsibility)

责任链模式(Chain of Responsibility)是行为模式之一,该模式构造一系列分别担当不同职责的类的对象来共同完成一个任务,这些类的对象之间像链条一样紧密相连,所以被称作责任链模式。

一、应用场景:

例1:比如客户要完成一个任务,这个任务包括a,b,c,d四个部分。 首先客户Client把任务交给A,A完成a部分之后,把任务交给B,B完成b部分,…,直到D完成d部分。
例2:web开发中的filter和interceptor

二、UML类图
责任链模式

抽象处理者(Handler)角色:定义一个请求的接口。如果需要可以定义个一个方法用来设定和返回下家对象的引用。
具体处理者(ConcreteHandler)角色:如果可以处理就处理请求,如果不能处理,就把请求传给下家,让下家处理。也就是说它处理自己能处理的请求且可以访问它的下家。 阅读详细 »

Java工程师应该读的几本书

《深入理解Java虚拟机:JVM高级特性与最佳实践》

深入理解Java虚拟机:JVM高级特性与最佳实践

《Java并发编程实战》

Java并发编程实战 阅读详细 »

google-perftools安装方法

google-perftools是用来查看堆外内存的一个工具。用来分析nio应用的内存泄露问题

https://github.com/gperftools/gperftools

安装gcc

yum install gcc gcc-c++  make

安装libunwind

wget http://ftp.yzu.edu.tw/nongnu/libunwind/libunwind-1.1.tar.gz
./configure
make
make install

安装google-perftools

wget -c -O gperftools-2.4.tar.gz  https://github.com/gperftools/gperftools/releases/download/gperftools-2.4/gperftools-2.4.tar.gz
./confighre
make
make install
vim /etc/ld.so.conf.d/usr_local_lib.conf 加入/usr/local/lib
/sbin/ldconfig

修改tomcat启动文件startenv.sh

加入export LD_PRELOAD=/usr/local/lib/libtcmalloc.so
加入export HEAPPROFILE=/tmp/test (导出的文件放在哪里)

heap文件分析

usr/local/bin/pprof –text /home/q/java/default/bin/java mem_6774.0001.heap

Java程序员们最常犯的3个集合错误

1.将数组转化为列表
将数组转化为一个列表时,程序员们经常这样做:

List list = Arrays.asList(arr);

Arrays.asList() 会返回一个ArrayList对象,ArrayList类是Arrays的一个私有静态类,而不是java.util.ArrayList 类,java.util.Arrays.ArrayList类有set()、get()、contains()方法,但是没有增加元素的方法,所以它的大 小是固定的,想要创建一个真正的ArrayList类,你应该这样做:

ArrayList arrayList = new ArrayList(Arrays.asList(arr));

ArrayList的构造方法可以接受一个集合类型,刚好它也是java.util.Arrays.ArrayList的超类。

2.判断一个数组是否包含一个值
程序员们经常这样做:

Set set = new HashSet(Arrays.asList(arr));
return set.contains(targetValue);

这段代码起作用,但是没有必要把一个数组转化成列表,转化为列表需要额外的时间。它可以像下面那样简单:

Arrays.asList(arr).contains(targetValue);

或者是:

 for (String s : arr) {
      if (s.equals(targetValue)) {
          return true;
      }
  }
  return false;

很显然第一种方法比第二种更容易读
3.在一个循环中删除一个列表中的元素
思考下面这一段在循环中删除多个元素的的代码:

  ArrayList list = new ArrayList(Arrays.asList("a", "b", "c", "d"));
        for (int i = 0; i < list.size(); i++) {
            list.remove(i);
        }
  System.out.println(list);

输出结果是:
[b,d]
在这个方法中有一个严重的错误。当一个元素被删除时,列表的大小缩小并且下标变化,所以当你想要在一个循环中用下标删除多个元素的时候,它并不会正常的生效。
你也许知道在循环中正确的删除多个元素的方法是使用迭代,并且你知道java中的foreach循环看起来像一个迭代器,但实际上并不是。考虑一下下面的代码:

ArrayList list = new ArrayList(Arrays.asList("a","b","c","d"));
  for(String s:list){
   if(s.equals("a")){
     list.remove(s);
   }
}

它会抛出一个ConcurrentModificationException异常。
相反下面的显示正常:

ArrayList list = new ArrayList(Arrays.asList("a", "b","c", "d"));
    Iterator iter = list.iterator();
    while (iter.hasNext()) {
        String s = iter.next();
        if (s.equals("a")) {
            iter.remove();
         }
    }

.next() 必须在.remove()之前调用。在一个foreach循环中,编译器会使.next()在删除元素之后被调用,因此就会抛出 ConcurrentModificationException异常,你也许希望看一下ArrayList.iterator()的源代码。

有趣的面试题

1.A、B两人分别在两座岛上。B生病了,A有B所需要的药。C有一艘小船和一个可以上锁的箱子。C愿意在A和B之间运东西,但东西只能放在箱子里。只要箱子没被上锁,C都会偷走箱子里的东西,不管箱子里有什么。如果A和B各自有一把锁和只能开自己那把锁的钥匙,A应该如何把东西安全递交给B?
答案:A把药放进箱子,用自己的锁把箱子锁上。B拿到箱子后,再在箱子上加一把自己的锁。箱子运回A后,A取下自己的锁。箱子再运到B手中时,B取下自己的锁,获得药物。

2.有一个软件公司,1/2的人是系统分析员,2/5的人是软件工程师,有1/4的人两者都是,问有多少人两者都不是?
1 – 1/2 – 2/5 + 1/4= 0.35

3.有25匹马,速度都不同,但每匹马的速度都是定值。现在只有5条赛道,无法计时,即每赛一场最多只能知道5匹马的相对快慢。问最少赛几场可以找出25匹马中速度最快的前3名?(百度2008年面试题) 阅读详细 »

java参数传递分析

先看基本类型作为参数传递的例子:

[java]
public static void main(String[] args) {
int param =100;
System.out.println("before change param=" + param);
changeInt(param);
System.out.println("after change param=" + param);
}
public static void changeInt(int i){
i = 1;
}
[/java]

基本类型作为参数传递时,是传递值的拷贝,无论你怎么改变这个拷贝,原值是不会改变的,输出的结果证明了这一点:
before change param = 100
after change param = 100

阅读详细 »

干掉程序中被注释的代码

程序中被注释掉的代码就像一个僵尸,空有一个躯体,但已经失去了生命。这种代码的存在有百害而无一利,越早删除越好。
删除被注释的代码的好处:

1、降低信噪比
本来阅读别人的代码就够费劲了,再加上注释就更不用说了。
2、造成歧义妨碍调试
3、影响关键词搜索
4、影响代码重构
如果打算要注释一段代码,先问问自己:

1、什么时候会取消注释?

2、是否能删掉它,需要时从版本控制里找回?

3、这种需要来回切换注释的功能可否通过配置实现?

4、重构时也需要重构这些注释掉的代码吗?

Java方法不应超过15行

大多数人都会说,方法不能太长,但也不能定死,要具体问题具体分析。再追问一下,有人会说,不超过200行,100行,50行,30行都有。另有人说,面向对象风格的可以短些,面向过程风格的可以长些。也有人说,一个方法不超过一屏幕就行(姑且不论显示器大小,字体大小和分辨率问题)。

先摘录一段Martin Fowler《重构》P110-P111 中的一段话:

人们有时会问我,一个函数多长才合适?在我看来,长度不是问题,关键在于函数名称和函数本体之间的语义距离(semantic distance)。如果提炼动作(extracting)可以强化代码的清晰度,那就去做,就算函数名称比提炼出来的代码还长也无所谓。

说了直白点,函数名是干什么,函数体是怎么干。当2者不是那么能容易分辨出来时,就需要提炼出函数来,让代码更好懂。

既然大师都说了,长度不是问题了吗,那干吗还要追究函数长度规范呢?
其实大师讲的是对于懂得编程之道的人而言,不用关心长度,因为小函数是必然的结果。但在开发实践中,适当的函数长度限制值,可以给予我们警示,为什么这个方法写长了?哪里不对劲了?
对于这个问题我的回答是:Java函数不应超过15行。
为什么定这个数呢?
一方面,这个是基于多年开发实践的总结。15行其实已经是一个比较宽松的标准,开发人员稍加培训就可以实际贯彻,而符合这个标准的函数也能达到良好的可读性。

另一方面,这是基于对函数复杂度和代码行数之间关联关系的认识。
我采用一个简单的数学公式:

方法复杂度 >= 代码行数^2 (该公式参考于 gerald m. weinberg 《质量·软件·管理(第1卷)——系统思维》)

以下为了简化,只以最低增长方式,平方数进行计算。
以一个函数的行数,从1行到20行为例,见下图:

java

当函数行数到达10行以上,函数复杂度就开始大幅度攀升。
当代码行数达到20行时(复杂度400),其复杂度已经是14行时(复杂度 196)的2倍。
因此我将代码规范定在15行。

下面我们运行此公式,来分析一下提炼小函数的好处。
例如,有1个100行的函数,分解为11(含自身)个10行的函数。
原复杂度= 100^2=10000。
分解后复杂度= 11×(10^2)= 1100。
分解后的复杂几乎是分解前的 1/9,几乎降低了1个数量级。这就解释了为什么长方法分解之后,可维护性大幅度提高。

也就无怪乎有同学表示维护1000行以上的函数,有种生不如死的感受。