Effective java 系列之避免过度同步和不要使用原生态类型,优先考虑泛型
避免过度同步(67):在一个被同步的方法或代码块中,不要调用哪些被设计成被覆盖的方法或者是由客户端以函数对象的形式提供的方法(21)。
有点拗口,书上提供的创建者与观察者模式,add方法太多,看得眼花缭乱,重新写了一个例子:
package com.example.demo.effective;
public interface Believer {
void confide();
}
package com.example.demo.effective; import com.google.common.collect.Lists; import java.util.List; /**
* 上帝
*/
public class God {
/**
* 天国
*/
private List<Believer> believers = Lists.newArrayList(); public void addBeliever(Believer be){
synchronized (believers){
believers.add(be);
}
} public void removeBeliever(Believer be){
synchronized (believers){
believers.remove(be);
}
} public void getBeliever(Believer be){
synchronized (believers){
//选一个大主教
System.out.println(believers.get(0));
}
} /**
* 倾听每位信徒倾述
*/
public void listenEveryBeliever(){
synchronized (believers){
for(Believer be : believers){
be.confide();
}
}
} }
测试:
@Test
public void test1(){
God god = new God();
god.addBeliever(new Believer() {
@Override
public void confide() {
System.out.println("I love my son....");
}
});
god.listenEveryBeliever();
}
I love my son....
没问题,再看下面:
@Test
public void test2(){
God god = new God(); god.addBeliever(new Believer() {
@Override
public void confide() {
System.out.println("I love my son....");
}
}); //现在出现一个亵神者,放弃信仰了
god.addBeliever(new Believer() {
@Override
public void confide() {
god.removeBeliever(this);
}
});
god.listenEveryBeliever();
}
结果,在上帝做倾听信徒时,居然出现了渎神者,大家都知道,集合在循环时,时不可以增减元素的。

在继续看,死锁了....
@Test
public void test3(){
God god = new God(); god.addBeliever(new Believer() {
@Override
public void confide() {
System.out.println("I love my son....");
}
}); //现在出现一个亵神者,放弃信仰了
god.addBeliever(new Believer() {
@Override
public void confide() {
ExecutorService executor = Executors.newSingleThreadExecutor();
try {
executor.submit(()->{god.getBeliever(this);}).get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}finally {
executor.shutdown();
}
}
});
god.listenEveryBeliever();
}
这里死锁,分析下:god.listenEveryBeliever(),主线程锁住天堂,上帝开始倾听信徒倾述,但此时有一个信徒倾述时另启一个线程,并在此等待主线程执行结束,当然永远也等不到....
所以正确的姿势:原则:不要在同步块中,调用可能被覆盖的方法
/**
* 倾听每位信徒倾述
*/
public void listenEveryBeliever(){ List<Believer> believersBat = null;
synchronized (believers){
//快照副本
believersBat = Lists.newArrayList(believers);
}
for(Believer be : believersBat){
be.confide();
}
}
请不要在新代码中使用原生态类型(23)
jdk1.5版本增加了泛型,如果还是再用低于1.5的jdk,不说了。
泛型的好处:1、表述性 2、安全性(主要体现在编译阶段的泛型检查,有些错误不必要等到运行时才发现)
@Test
public void test1(){
//运行时才发现类型异常,追悔吧...
List list = Lists.newArrayList("adc");
getFirstEle1(list);//java.lang.String cannot be cast to java.lang.Integer
} @Test
public void test2(){
List list = Lists.newArrayList("adc");
getFirstEle2(list);//adc
} @Test
public void test3(){
//结合test2() List和List<Object>主要区别:
//1、List 时原生态类型,逃避了类型检查。List<Object>则明确告知编译器
//2、List<String> 时List的字类,而不是List<Object>子类
List<String> list = Lists.newArrayList("adc");
//getFirstEle2(list);//编译报错
} @Test
public void test4(){
List<String> list = Lists.newArrayList("adc");
getFirstEle3(list);//adc
} @Test
public void test5(){
List<? extends String> list1 = Lists.newArrayList("adc");
List<String> list2 = Lists.newArrayList("ap"); //<? extends E> 是 Upper Bound(上限) 的通配符,用来限制元素的类型的上限
//list2 元素类型String,list1元素类型继承String,大类型->小类型,报错
//list1.addAll(list2);
list2.addAll(list1);
System.out.println(list2); //[ap, adc]
} @Test
public void test6(){
// <? super E> 是 Lower Bound(下限) 的通配符 ,用来限制元素的类型下限
List<? super String> list1 = Lists.newArrayList("adc");
List<String> list2 = Lists.newArrayList("ap");
list1.addAll(list2);
System.out.println(list1); //[ap, adc]
} /**
* List 原生态类型
* @param list
*/
public static void getFirstEle1(List list){
int i = (int) list.get(0);
System.out.println(i);
} /**
* List<Object> 从表述上,时所有Object元素都ok
* @param list
*/
public static void getFirstEle2(List<Object> list){
Object i = (Object) list.get(0);
System.out.println(i);
} /**
* <?> 可接受任意泛型,多定义在变量中
* @param list
*/
public static void getFirstEle3(List<?> list){
System.out.println(list.get(0));
} /**
* <T> 表示方法要用到泛型参数,多用在类和方法上
* @param list
* @param <T>
*/
public static <T> void getFirstEle4(List<T> list){
T t = list.get(0);
System.out.println(t);
}
私有构造器强化不可实例化
使用场景:通常用在工具类,因为他们都不希望被实例化。
public class MyUtils {
private MyUtils(){
//不管外部还是内部,该类实例化就会抛异常
throw new AssertionError();
}
public static String method(){
return "";
}
}
枚举类型:
通常程序中需要用到大量的常量,优雅的使用枚举将对代码可读性、健壮性、维护性有显著效果
enum代替int常量,用实例域代替序数
package com.example.demo.effective.enumDemo;
public enum StatusEnum {
APPLY(1),ACTIVE(2),FREEZE(3),FREE(4);
private final int index;
StatusEnum(int size){
this.index = size;
}
public void method(){
//System.out.println(this.ordinal()); 位置顺序变动,会乱套
System.out.println(index);
}
}
用EnumSet代替位域
// num << x,相当于num * 2^x,
//|或运算符:即两个二进制数同位中,只要有一个为1则结果为1,若两个都为1其结果也为1
public static final int STYLE_BOLD = 1 << 0;
public static final int STYLE_ITALIC = 1 << 1;
public static final int STYLE_UNDERLINE = 1 << 2;
public static final int STYLE_STRIKETHROUGH = 1 << 3;
@Test
public void test3(){
System.out.println(STYLE_UNDERLINE|STYLE_STRIKETHROUGH);//
} @Test
public void test4(){
//用EnumSet代替位域
//位域表示法允许利用位操作,有效地执行先 union(联合)和 intersection(交集)这样的集合操作 如:test3()
EnumSet<StatusEnum> status1 = EnumSet.of(StatusEnum.APPLY, StatusEnum.ACTIVE);
System.out.println(status1); //确定,EnumSet可变,用unmodifiableSet 封装
Set<StatusEnum> unmodifiableStyle = Collections.unmodifiableSet(status1);
unmodifiableStyle.add(StatusEnum.FREE); //运行时:UnsupportedOperationException
}
Effective java 系列之避免过度同步和不要使用原生态类型,优先考虑泛型的更多相关文章
- Effective Java 第三版——78. 同步访问共享的可变数据
Tips 书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code 注意,书中的有些代码里方法是基于Java 9 API中的,所 ...
- Effective java 系列之更优雅的关闭资源-try-with-resources
背景: 在Java编程过程中,如果打开了外部资源(文件.数据库连接.网络连接等),我们必须在这些外部资源使用完毕后,手动关闭它们.因为外部资源不由JVM管理,无法享用JVM的垃圾回收机制,如果我们不在 ...
- Effective java 系列之异常转译
异常转译:当位于最上层的子系统不需要关心底层的异常细节时,常见的作法时捕获原始异常,把它转换一个新的不同类型的异常,在将新异常抛出. 通常方法捕获底层异常,然后抛高层异常. public static ...
- Effective Java 第三版——89. 对于实例控制,枚举类型优于READRESOLVE
Tips 书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code 注意,书中的有些代码里方法是基于Java 9 API中的,所 ...
- Effective Java 第三版——79. 避免过度同步
Tips 书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code 注意,书中的有些代码里方法是基于Java 9 API中的,所 ...
- Effective.Java第78-90条(同步相关)
78. 同步访问共享的可变数据 为了在线程之间进行可靠的通信,也为了互斥访问,同步是必要的. 不共享可变的数据.要么共享不可变的数据,要么压根不共享.换句话说,将可变数据限制在单线程中. 当多个线程 ...
- Effective Java通俗理解(下)
Effective Java通俗理解(上) 第31条:用实例域代替序数 枚举类型有一个ordinal方法,它范围该常量的序数从0开始,不建议使用这个方法,因为这不能很好地对枚举进行维护,正确应该是利用 ...
- 《Effective Java(中文第二版)》【PDF】下载
<Effective Java(中文第二版)>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230382186 Java(中文第二版)& ...
- Effective Java 第三版——48. 谨慎使用流并行
Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...
随机推荐
- Codeforces Round #424 (Div. 2, rated, based on VK Cup Finals) Problem E (Codeforces 831E) - 线段树 - 树状数组
Vasily has a deck of cards consisting of n cards. There is an integer on each of the cards, this int ...
- python---02.while循环 格式化输出 运算符 编码
一.while循环语句 1.while 条件:(如果条件是真, 则直接执⾏循环体. 然后再次判断条件. 直到条件是假. 停⽌循环) 循环体(break continue) 2. break: 立刻跳 ...
- Windows下配置Jenkins 实现自动发布maven项目至tomcat(svn+maven+tomcat)
Jenkins安装完成之后,需要我们对其配置,然后才可以实现自动部署项目. 前提 防火墙开放tomcat的8080端口 Linux(CentOS):firewall-cmd --zone=public ...
- Derek解读Bytom源码-P2P网络 地址簿
作者:Derek 简介 Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomBlockchain/bytom ...
- 【C#】委托的发展
"用事件去处理程序, 进而解决问题" ---- 委托的目的 委托早在C#2的时候就已经初具模型, 但是并不是特别灵活, 制止C#3才在代码中被广泛使用. C#4中泛型委托, C#5 ...
- Bash 和 Zsh 开启 vi-mode
Bash 和 Zsh 开启 vi-mode bash 有两种操作模式,分别是 emacs 和 vi . 在 bash 中 set -o vi # 临时开启 vi 模式 vi ~/.bashrc # 在 ...
- Mysql视图、触发器、事务、储存过程、函数
一.视图 什么是视图 视图是有一张表或多张表的查询结果构成的一张虚拟表 为什么使用视图 当我们在使用多表查询时 我们的sql语句可能会非常的复杂,如果每次都编写一遍sql'的话无疑是一件麻烦的事情,这 ...
- 抗性基因数据库CARD介绍
随着抗生素药物的发现及使用,越来越多的耐药菌株由此产生.而耐药菌株的发展则会增加疾病治疗的难度和成本,因此耐药微生物的研究则显得尤为重要.目前,通过对耐药基因的鉴定挖掘能够一定程度上帮助我们揭开耐药机 ...
- P3110 [USACO14DEC]驮运Piggy Back
传送门 做过次短路后,再来做这题感觉轻松不少. 这题看着就像最短路模板题. 思路: 虽说题目看起来比较水,但是码起来还是有点难度的.(对我这个蒟蒻来说) 这道题,跟"路障"一题差不 ...
- vs2013 报错error C1083: 无法打开包括文件:“gl\glew.h”: No such file or directory\
vs报错诸如如无法打开“gl\xxx.h”时, 解决方法: 1.去http://glew.sourceforge.net/下载相关文件,2.在下载下来的文件里找到xxx.h,将其复制到vs的相关目录下 ...