在看Netty时,做者提到了Builder模式,曾经看过设计模式屡次,但都没什么感受,看过以后理解了,但很快就忘掉了,由于当时没有应用或深入的思考过,但Builder模式是第一个让我比较“深入”的设计模式。javascript
先看代码:前端
public class Item { private String name; private int price; private Date date; public Item(Builder builder) { this.name = builder.name; this.price = builder.price; this.date = builder.date; } public static class Builder { private String name; private int price = 0; private Date date = new Date(); public Builder(String name) { this.name = name; } public Builder price(int price) { this.price = price; return this; } public Builder date(Date date) { this.date = date; return this; } public Item build() { return new Item(this); } }// end of Builder /* Item getter & setter */ }
Builder模式应用于构造具备多参数的对象,这些参数的特色是比较灵活,能够自定义设置字段的值。这里已经很好理解了,不用再解释了。若是要构造Item对象:java
Item item = new Item.Builder("name").price(10).date(new Date()).build();
这种灵活的构造方式也能够用JavaBean的setter实现,即便用无参的构造函数,经过set方法赋值,但这种用法的缺点是在多线程并发时不能保证对象参数的一致性,不能确保线程安全。但使用Builder模式能够保证线程安全,所以Builder模式是既保证可读性,又保证安全性。web
以前遇到过一个JavaBean须要继承两个父类,两个父类分别存表明Jmx属性和Page属性。实现多继承的方式也很简单,中间加一层“代理”ProxyBean,持有两个父类对象,子类只需继承ProxyBean便可。ajax
JmxBean:数据库
public class JmxBean{ private Object jxm; public Object getJmx(){ return this.jxm; } public void setJmx(Object jmx){ this.jmx = jmx; } }
PageBean:json
public class PageBean{ private Object page; public Object getPage(){ return this.page; } public void setPage(Object page){ this.page = page; } }
ProxyBean:后端
public class Proxybean{ private JmxBean jmxBean; private PageBean pageBean; public Object getJmx(){ return this.jxmBean.getJmx(); } public void setJmx(Object jmx){ this.jmxBean.setJmx(jmx); } /* pageBean同理 */ }
同时又得出另外一个结论,序列化框架在序列化和反序列化时调用getter、setter方法,我之前一直不理解这是为何,而不是直接给属性。这个例子就是反例。固然如今想一想这种想法仍是很nc的。设计模式
优点:数组
1.静态工厂方法有名称。构造函数的参数有时并不能正确描述返回对象,因此使用带有具体名称的静态工厂方法能够避免。同时也可解决类中须要多个签名相同的构造函数。
2.没必要每次调用时建立一个新的对象。能够预先构造好实例,或将构造好的的实例缓存起来,避免建立没必要要的重复对象。若是程序常常请求建立相同的对象,而且建立对象代价很高,使用静态工厂方法能够提升性能。这么作能够确保类是单例或不可实例化的。使得不可变类不存在两个相同的实例,a==b <——> a.equals(b)。
3.能够返回原返回类型的任何子类类型对象。
4.简化代码。
劣势:
1.类不含有public或protected的构造函数,此类不能子类化。但这样能够更多地使用复合,即持有其余类的对象,而不是继承。
2.与其余静态方法没有任何区别。劣势在于,静态工厂方法并无在API中像构造函数同样标明出来,使用可能不方便。弥补的方法是使用惯用名称,例如:valueOf、getInstance、newInstance。
通常来讲,最好能重用对象而不是每次须要的时候建立一个功能相同的新对象,这个道理固然是很浅显的。例如String、不可变对象。固然在编码中主要对象的重用性以外,如下几点也很重要:
1. Calendar实例代价昂贵;
2. 延迟初始化,是不推荐的。即执行构造函数时可能建立了一些暂时不会调用到的对象、常量,将这些对象和常量在具体方法调用时再初始化。不推荐的缘由是,使方法实现变得复杂,且没法显著提升性能。我以为在工做中,尤为是新员工会常常会遇到这种问题,那么正确的方法如上所述;
3. 自动装箱会付出必定代价,要优先使用基本类型而不是装箱基本类型;
4.小对象建立和回收很是廉价,适当附加这种对象可提升程序可读性;
5.只有相似数据库链接池这种大对象须要对象池,JVM垃圾回收器性能优于轻量对象池。
失效的对象引用,例如实现一个相似栈的对象,这个对象本身管理内存(是出现内存泄漏的重要缘由),例如执行pop,但不将被pop位置的引用置为null,则出现内存泄漏。
1. Java中错误的操做会引发内存泄漏;
2. 清空对象引用是一种例外,而不是规范,消除失效引用的最好方法是结束其生命周期;
3. 若是类本身管理内存,应警戒内存泄漏问题;
4. 使用缓存时,能够将缓存对象做为key存入WeakHashMap(真的没见过,也脑补不出应该怎么用,感受很鸡肋),或将缓存对象存入LinkedHashMap,实现removeEldestEntry()做为淘汰策略。
尽量使每一个类或者类的成员不被外界访问到,在设计类和成员时,关注这个类的可见性范围,正确使用访问级别修饰符。private及包级私有(没有修饰符)都是一个类的实现中的一部分,通常不会影响他的API。当同一个包中的另外一个类真正须要访问一个成员时,才将private升级成为包级私有。
实例中不能包含公共域(或字段、属性)。即公有类中不能包含公有域。包中含有公共可变域是线程不安全的。类中有共有静态final域是错误的,客户端能够直接修改这种域的值。若是类是包级私有或私有的嵌套类,直接暴露域是没问题的。
其实看过Effective Java这一条才发现以前写的大部分代码都是不规范的,但没出问题的缘由一是由于本身对本身的系统熟悉,不会乱写,二是缺对乏类、成员可见性的意识。
不可变类是实例不能被修改的。jdk中String、基本类型的包装类、BigInteger、BigDecimal是不可变的。不可变类遵循如下五条规则:
不可变对象是线程安全的,不须要同步。所以能够被自由共享。除非有好的理由让类成为可变的,不然就应该是不可变的。若是类不能被作成不可变的,也应该尽量限制他的可变性。
专门设计用来继承的类,并有好的文档,在使用继承时很是安全,对于普通类进行跨包继承是很是危险的。继承打破了封装性,即超类随着版本变化会破坏子类,所以子类必须跟着超类一块儿变化。这也体现了专门用于继承类的优点。
书中介绍的一个例子是扩展HashSet,并重写了add() & addAll()方法,在执行这两个方法时作累加操做,记录集合中增长的数量。错误的代码此处就不写了。这样作有很是多问题,首先HashSet的addAll方法调用了add,若是调用子类的addAll方法就作了两次计数。这种“自用性”的实现细节首先须要开发者在开发以前清楚,而且jdk不保证其余的实现也是具备自用性的,不能保证随着jdk版本的升级不发生变化,所以这个子类的实现是错误的,同时也是脆弱的。
另外一个脆弱的缘由是,随着jdk版本的上升,超类可能会增长方法。而对于子类,设计子类每每是由于须要增长某些特殊的操做和限制,例如上面的例子中累加计数就是一种。若是超类增长方法,子类没有改变,那么就能够经过子类,将不符合限制的数据,没作特定操做的动做,直接经过父类提供的方法执行。而且超类新增的方法签名可能会与子类本身实现的方法签名冲突。
复合能够解决以上问题。复合composition,即不扩展示有的类,而是在新的类中增长私有域,它引用现有类的实例,现有类成为了新类中的一个组件。新类中能够调用现有类对象的方法,并返回结果,这被称为“转发”,forwarding,新类中的方法被称为“转发方法”。在书中提供的代码中,将复合和转发分红两个类,以下:
public class InstrumentSet<E> extends ForwardingSet<E> { private int count; public InstrumentSet(Set<E> s) { super(s); } @Override public boolean add(E e) { count++; return super.add(e); } @Override public boolean addAll(Collection<? extends E> c) { count += c.size(); return super.addAll(c); } public int count(){ return count; } } class ForwardingSet<E> implements Set<E> { private final Set<E> s; public ForwardingSet(Set<E> s) { this.s = s; } @Override public boolean add(E e) { // TODO Auto-generated method stub return s.add(e); } @Override public boolean addAll(Collection<? extends E> c) { // TODO Auto-generated method stub return s.addAll(c); } /* 其余实现省略 */ }
InstrumentSet将一个Set(构造函数传入的Set)包装成InstrumentSet,因此也称为包装类。
今天作了一次项目文件拷贝,全部代码的package都出了问题,因此想写个脚本批量替换。命令以下:
sed -i "" "s/package com\.hippoconsoleweb/package com\.pinganfu\.hippoconsoleweb/g" `grep package -rl *`
个人操做系统是MacOS,因此跟网上大部分的例子有点不一样,mac的sed -i 须要提供一个文件后缀,作备份,若是是上面的命令,直接会覆盖全部的文件。
sed -i:直接在文件中修改;
grep -rl *:递归遍历全部文件并打印出来
若是是CentOs,去掉-i 后面的第一个参数便可
忽然发觉对Java中clone的使用很模糊,在上一个项目中用过clone方法,但如今再看发现有些不懂。因而看书、百度。
在论坛中看到这样一句“Object的clone方法由于是protected的,因此不能直接调用”。这句话的结论是没错的,随随便便搞个对象obj.clone(),会报出method invisible的错误。当时以为虽然结论试验出来是没错的,但他说的好像有点怪怪的。因而本身作实验,(此处脑补代码)写一个父类,子类继承,父类中有一个protected方法,固然我是写在同一个.java文件中,这也是错误的开始。这样实验的结果一定是能够访问的,由于protected方法跟调用方法的类在同一个包中。做为初学者,在看到protected时每每只考虑到了继承关系,而忘记了包关系。把父类移到另外一个包中,一样报出了method invisible。最后再明确一下protected的做用域:子类或同一个包中的非子类。
再说说Cloneable接口。编码中咱们一般的作法是implements Cloneable,并实现:
@Override public Object clone(Object o){}
通常状况将clone方法定义为public,没有违反子类的访问修饰权限不能小于父类的 。CLoneable接口中并无定义任何方法,他是一个标志,相似于Serializable。但若是不实现Cloneable接口,在调用Object的clone方法时,会报出CloneNotSupportedException。
通常会在重写的clone方法中调用super.clone()。虽然调了父类的方法(其实就是Object的clone),但拷贝的并非父类,而是子类。网上有“专家”解释了为何会这样,算了我记不住就不转述了,反正我以为这不是特别难理解。
调用super.clone(),即所谓的浅拷贝。我之前一直错误的认为,浅拷贝是建立引用,而不是复制引用指向的对象。其实这种想法如今以为很愚蠢,很容易就能举出反例。浅拷贝将原有内存,原封不动复制到另外一块内存中。若是对象没有重写过equals和hashCodet方法,那么不管是==仍是equals都返回false,这点其实彻底能够说明浅拷贝的工做方式。引用不一样,对象也不一样。固然对象的不一样指的是内存,不是逻辑不一样。
这样的结论能够为深拷贝、浅拷贝提供基础。既然内存同样,那么拷贝的复杂对象中若是包含对其余对象的引用,也就是所谓的拷贝的是复杂对象,就会出现引用不一样但指向的对象相同这个问题。这也是深拷贝存在的缘由。这一段中不会再继续说明深拷贝,由于前面的结论已经搞清楚了clone的本质。
直接上代码了:
前端Js:
//migrationResultList是一个复杂对象数组,对象中包含多个字段 var migrationResultList = []; $.ajax({ type : "POST", url : "submitReduce", contentType: "application/json; charset=utf-8", data : JSON.stringify(migrationResultList), dataType : "json", success : function(data){ }, });
后端Java:
public @ResponseBody Map<String, Object> submitReduce(@RequestBody List<MigrationResult> migrationResultList) { return null; } //或者入参使用数组 public @ResponseBody Map<String, Object> submitReduce(@RequestBody MigrationResult[] migrationResultList) { return null; }