Effective Java 读书笔记
来源:互联网 发布:淘宝联盟自己买自己的 编辑:程序博客网 时间:2024/04/30 03:00
1、当有多个参数时,使用构建器模式
常用的场景是,查询时,有多个参数。因此构建查询参数对象时,考虑使用Builder模式。
public class QueryBuilder { //查询的每个参数,不需要set方法 private String userName; private String mobile; private int cityId; private int pageNum; private int pageSize; //提供一个Builder实例 public static Builder newBuilder(){ return new Builder(); } //共有的public 静态static内部类 public static class Builder{ private String userName; private String mobile; private int cityId; private int pageNum; private int pageSize; //每个方法都是公有的 public Builder withUserName(String userName){ this.userName = userName; return this; } public Builder withMobile(String mobile){ this.mobile = mobile; return this; } public Builder withCityId(int cityId){ this.cityId = cityId; return this; } public Builder withPageNum(int pageNum){ this.pageNum = pageNum; return this; } public Builder withPageSize(int pageSize){ this.pageSize = pageSize; return this; } //最后提供一个build方法,返回查询类 public QueryBuilder build(){ return new QueryBuilder(this); } } //查询类构造方法私有,接收一个Builder参数 private QueryBuilder(Builder builder){ userName = builder.userName; mobile = builder.mobile; cityId = builder.cityId; pageNum = builder.pageNum; pageSize = builder.pageSize; //可以根据需要提供一个boolean hasQuery字段,用于判断是否有查询条件 //没有查询条件时,返回所有值 /*hasQuery = StringUtils.isNotEmpty(userName) || StringUtils.isNotEmpty(mobile) || cityId > 0; */ }} public String getUserName() { return userName; } public String getMobile() { return mobile; } public int getCityId() { return cityId; } public int getPageNum() { return pageNum; } public int getPageSize() { return pageSize; } public boolean isHasQuery() { return hasQuery; }
//使用QueryBuilder.Builder builder = QueryBuilder.newBuilder();QueryBuilder query = builder.withUserName("name1").withMobile("13456463216") .withCityId(1).withPageNum(0).withPageSize(10) .build();
2、使用私有构造器或者枚举类型强化Singleton属性
这里主要写一下Java的Sigleton模式吧,下文讨论的内容引入了其他博文
http://blog.csdn.net/cnyyx/article/details/7482735
http://my.oschina.net/alexgaoyh/blog/261106?fromerr=FT8qyEHA
方法1、静态成员直接初始化
public class Singleton { //私有化构造器,防止外部new Singleton() private Singleton(){} private static final Singleton instance = new Singleton(); public static Singleton getInstance(){ return instance; }}
这种方法在类加载的时候就会创建一个Singleton对象,不管该资源是否被请求,占用Jvm内存。
方法2根据lazy initialization思想,使用到时才初始化
public class Singleton { private Singleton(){} private static Singleton instance; public static synchronized Singleton getInstance(){ if(null == instance){ //@1 instance = new Singleton(); //@2 } return instance; }}
该方法加载了同步锁,可以防止多线程在执行getInstance方法得到2个对象,如果不加synchronized关键字,考虑线程A、B
A执行 @1 还未执行 @2
B执行 @1 还未执行 @2
将会得到2个对象
缺点:只有在第一次调用的时候才需要同步,一旦instance部位null了,系统依旧花费同步锁开销,有点得不偿失
方法3:在2的基础上,改进标注:尽量减少锁资源
private Singleton(){} private static Singleton instance; public static Singleton getInstance(){ if(null == instance){ //@1 synchronized (Singleton.class){ //@2 instance = new Singleton(); //@3 } } return instance; }
这种写法减少了锁开销,但是在如下情况,却创建了2个对象:
a:线程1执行到1挂起,线程1认为singleton为null
b:线程2执行到1挂起,线程2认为singleton为null
c:线程1被唤醒执行synchronized块代码,走完创建了一个对象
d:线程2被唤醒执行synchronized块代码,走完创建了另一个对象
所以看出这种写法,并不完美。
方法4:为了解决上述3的问题,引入双重检查锁定
private Singleton(){} private static Singleton instance; public static Singleton getInstance(){ if(null == instance){ //@1 synchronized (Singleton.class){//@2 if(null == instance){//@3 instance = new Singleton();//@4 } } } return instance; }
在同步锁代码块内部,再判断一次对象是否为null,为null才创建对象。这种写法已经接近完美:
a:线程1执行到1,已经进入synchronized的时候,线程挂起,线程1占有Singleton.class资源锁;
b:线程2执行到1,当它准备synchronized块时,因为Singleton.class被占用,线程2阻塞;
c:线程1被唤醒,判断出对象为null,执行完创建一个对象
d:线程2被唤醒,判断出对象不为null,不执行创建语句
如此分析,发现似乎没问题。
但是实际上并不能保证它在单处理器或多处理器上正确运行;
问题就出现在singleton = new Singleton()这一行代码。它可以简单的分成如下三个步骤:
mem= singleton();//1
instance = mem;//2
ctorSingleton(instance);//3
这行代码先在内存开辟空间,赋给singleton的引用,然后执行new 初始化数据,但是注意初始化是要消耗时间。如果此时线程3在执行步骤1的时候,发现singleton 为非null,就直接返回,那么线程3返回的其实是一个没构造完成的对象。
我们期望1,2,3 按照反序执行,但是实际jvm内存模型,并没有明确的有序指定。
这归咎于java的平台的内存模型允许“无序写入”。
方法5:在4的基础上引入volatile
private Singleton(){} private static volatile Singleton instance; public static Singleton getInstance(){ if(null == instance){ //@1 synchronized (Singleton.class){//@2 if(null == instance){//@3 instance = new Singleton();//@4 } } } return instance; }
Volatile 变量具有 synchronized 的可见性特性,但是不具备原子特性。这就是说线程能够自动发现 volatile 变量的最新值。
但是5的写法,虽然理论上似乎可以解决无序写入问题。实际上并非如此。
方法6: 为了实现慢加载,并且不希望每次调用getInstance时都必须互斥执行,最好并且最方便的解决办法如下:(通过内部类实现多线程环境中的单例模式)
private Singleton(){} private static class InstanceHolder{ private static final Singleton instance = new Singleton(); } public static Singleton getInstance(){ return InstanceHolder.instance; }
JVM内部的机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的。这样当我们第一次调用getInstance的时候,JVM能够帮我们保证instance只被创建一次,
并且会保证把赋值给instance的内存初始化完毕,这样我们就不用担心上面的问题(方法4)。此外该方法也只会在第一次调用的时候使用互斥机制,这样就解决了低效问题(方法2)。
最后instance是在第一次加载InstanceHolder类时被创建的,而InstanceHolder类则在调用getInstance方法的时候才会被加载,因此也实现了惰性加载。
方法7:另一种写法是采用枚举(因为枚举类型的构造函数天生是私有的, 而且外部也不能new一个枚举值)
public enum A{ INSTANCE; public void invoke(){...}}
3、通过私有构造器强化不可实例化的能力
有时候可能需要编写只包含静态方法和静态域的类,如工具类。最好给其提供一个私有构造器,否则编译器会为其生成一个默认的无参构造函数。
public class MobileUtils { private MobileUtils(){} /** * 隐藏手机号中间4位 * @param mobile * @return String 隐藏手机号中间4位之后的手机号 */ public static String hideMobile(String mobile) { if (StringUtils.isEmpty(mobile)) { return StringUtils.EMPTY; } if (mobile.length() < 8) { return StringUtils.EMPTY; } return mobile.substring(0, 3) + "****" + mobile.substring(7); }}
4、避免创建不必要的对象
对于同时提供了构造器和静态工厂方法,通常使用静态工程方法而不是构造器,以避免 创建不必要的对象,比如Boolean.valueOf(String)比new Boolean(String)要好,构造器每次 被调用时都要创建一个新的对象,静态工厂方法则没有这种要求,也不会这样做 下面是Boolean的部分源码,查看其valueOf方法
public static final Boolean TRUE = new Boolean(true); public static final Boolean FALSE = new Boolean(false); public static Boolean valueOf(String s) { return toBoolean(s) ? TRUE : FALSE; } private static boolean toBoolean(String name) { return ((name != null) && name.equalsIgnoreCase("true")); }
5、消除过期对象
这里只能说学习,主要看的是ArrayList吧,对不用的对象是如何处理的。
protected void removeRange(int fromIndex, int toIndex) { modCount++; int numMoved = size - toIndex; System.arraycopy(elementData, toIndex, elementData, fromIndex, numMoved); // clear to let GC do its work 就是这里! int newSize = size - (toIndex-fromIndex); for (int i = newSize; i < size; i++) { elementData[i] = null; } size = newSize; } public static native void arraycopy(Object src, int srcPos, Object dest, int destPos,int length);
6、覆盖equal时请遵守通用约定
简单总结一下:
1、是否是同一个对象的引用
2、类型转换
3、域的比较,在比较时,优先比较区分度大的域
注意:这里有个坑:equals方法的参数是Object,而不是自己定义的类。否则直接变成代码重载,而不是重写了!!
@Override的作用也可以看到了 书中36条:坚持使用Override注解
@Overridepublic boolean equals(Object anObject) { if (this == anObject) {//是否是同一个对象的引用 return true; } if (anObject instanceof String) { String anotherString = (String) anObject;//转换为该对象类型 int n = value.length; //管家field的值比较,先比较区分度比较大的! if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
7、clone方法
主要考虑Java 深拷贝和浅拷贝
浅拷贝(浅复制、浅克隆):被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象。深拷贝(深复制、深克隆):被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深拷贝把要复制的对象所引用的对象都复制了一遍。clone基本类型 深拷贝clone对象类型 浅拷贝,重写clone方法,将对象引入一一clone,即可实现深拷贝 // 改为深复制: Student2 student = (Student2) super.clone(); // 本来是浅复制,现在将Teacher对象复制一份并重新set进来 student.setTeacher((Teacher) student.getTeacher().clone());serialization序列化 深拷贝
8、Comparable的理解
类的比较,主要有两种方法,一种是类实现Comparable接口,重写compareTo方法;另外一种是在集合比较的时候,新建一个Comparator,重写compare方法。与书中hashCode讲解有点关系的部分是:hashCode影响:HashSet、HashMap、HashTable
compareTo影响:TreeSet、TreeMap、Collections、Arrays。
自我感觉关于整数比较时,尽量直接<>直接比较,通过减法有可能溢出!
另外,有点不太相关的内容,在比较时,返回-1、0、1表示大小关系,其实只要返回一个符号(正负)就行,看到了Long的signum方法
public static int signum(long i) { return (int) ((i >> 63) | (-i >>> 63)); }
直接通过移位,不需要比较操作符,一句话搞定!吊吊的!Long的rotate和reverse也很吊!
- 《Effective Java》读书笔记之一
- 《Effective Java》读书笔记
- Effective Java读书笔记
- Effective Java 读书笔记
- 《Effective Java》读书笔记
- 《Effective Java》读书笔记之一
- Effective java 读书笔记
- 《Effective in java》 读书笔记
- Effective Java读书笔记
- Effective java 读书笔记
- Effective Java读书笔记
- Effective Java读书笔记一
- Effective Java读书笔记二
- Effective Java读书笔记三
- Effective Java读书笔记四
- Effective Java读书笔记五
- Effective Java读书笔记六
- Effective Java读书笔记七
- 抢鲜版 Android studio 2.2 pre 版本,千万不要想用
- 为Druid监控配置访问权限(配置访问监控信息的用户与密码)
- cocos2d 了解
- group by +left join+count(1)查询实例
- std::string assign函数使用
- Effective Java 读书笔记
- 华为OJ——汽水瓶
- LADP
- iOS应用支持IPV6,就那点事儿
- 使控件和MFC窗口一起最大最小化
- 听韦东山老师公开课的理解
- web.xml详解
- Flume的体系结构介绍以及Flume入门案例(往HDFS上传数据)
- O(NlogN)复杂度选取出现次数超过一半的元素(递归版本)