总而言之,如果不能阻止或者处理来自更底层的异常,一般的做法就是进行异常转译,异常转译就是高层捕获底层异常进行处理,或者把它转化层高层相同业务逻辑的异常.
简单来说对于异常可能出现的情况进行尽可能的声明,这样让调用你的人才能知道要怎么来使用对应的方法
简单来说,对于可能出现业务逻辑异常处应当做好对应的日志记录,这样才能更好的跟踪有些我们无法预料捕获而是由程序帮我们抛出的异常信息
总而言之,当异常出现的时候肯定存在某个对象出现了某个问题,那么此时当我们捕获异常的时候需要让这个对象保持它出现异常的状态,不应该去改变它的结构,从而破坏了原子性,最终导致异常无法重现
简单来说,不要让catch代码块当中什么事情都不做,应该要么捕获异常打印日志,要么记录异常改变状态.总需要在这里做点什么,不能放空
当开启多线程去访问同一个共享的数据资源时,需要使用synchronized修饰.笔者推荐BlockingQueue也是可以作为一个保证消费顺序的队列
不要试图在所有的方法当中都加上synchronized修饰,而且使用synchronized修饰的方法操作的逻辑应该越短越好
从JDK1.5开始,Java平台发布了Exceutor Framework,它创建了一个工作队列.只需要一行代码即可实现
//单线程执行器
ExecutorService executorService = Executors.newSingleThreadExecutor();
//多线程执行器,自定义线程的数量
ExecutorService executorService = Executors.newFixedThreadPool(10);
//线程执行完成之后,需要关闭当前线程,但并不是退出JVM
executorService.shutdown();
总的来说使用Exceutor Framework会比以往的Thread执行要方便的的多,具体可以参照书籍学习
当我们要去保证线程的顺序执行时,并发工具帮我们提供了很多队列帮助我们让线程的执行顺序保持一致且快速.如果一定要使用wait()方法,那么需要在while循环中使用,保证线程的等待是有条件的,如果是需要使用notify方法,那么优先使用notifyAll方法,避免死锁
我们需要对一个类是否是线程安全的进行对应的文档注释,提供以下几种可选类型
延迟初始化是消耗启动性能来换回运行性能,只有当我们创建的对象确实很大,创建多个可能影响总体运行效率.那么此时建议使用双重检查,代码如下
public class DoubleChecked {
private volatile Client client;
private Client getFieldType(){
Client result = client;
if(result == null){
synchronized (this){
result = client;
if(result == null){
client = result = buildFiled();
}
}
}
return result;
}
private Client buildFiled() {
return new Client();
}
}
class Client {
}
程序依赖于线程调度器会导致程序不够健壮也不具有移植性,线程任务的优先级可以提升工作服务的质量,但是却不能修复原本不能正常运行的程序
public class PersonSingleton implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private PersonSingleton(String name) {
this.name = name;
};
private static PersonSingleton person = null;
public static synchronized PersonSingleton getInstance() {
if (person == null){
return person = new PersonSingleton("cgl");
}
return person;
}
private Object writeReplace() throws ObjectStreamException {
System.out.println("1 write replace start");
return this;//可修改为其他对象
}
private void writeObject(java.io.ObjectOutputStream out) throws IOException {
System.out.println("2 write object start");
out.defaultWriteObject();
//out.writeInt(1);
}
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
System.out.println("3 read object start");
in.defaultReadObject();
//int i=in.readInt();
}
private Object readResolve() throws ObjectStreamException {
System.out.println("4 read resolve start");
return PersonSingleton.getInstance();//不管序列化的操作是什么,返回的都是本地的单例对象
}
public static void main(String[] args) throws Exception {
FileOutputStream out = new FileOutputStream(new File("D://person.dat"));
ObjectOutputStream op = new ObjectOutputStream(out);
op.writeObject(PersonSingleton.getInstance());
op.close();
FileInputStream in = new FileInputStream(new File("D://person.dat"));
ObjectInputStream oi = new ObjectInputStream(in);
Object person = oi.readObject();
in = new FileInputStream(new File("D://person.dat"));
oi = new ObjectInputStream(in);
PersonSingleton person1 = (PersonSingleton) oi.readObject();
System.out.println("sington person hashcode:" + person.hashCode());
System.out.println("sington person1 hashcode:" + person1.hashCode());
System.out.println("singleton getInstance hashcode:" + PersonSingleton.getInstance().hashCode());
System.out.println("singleton person equals:" + (person == PersonSingleton.getInstance()));
System.out.println("person equals1:" + (person1 == person));
}
}
编写readObject注意事项
当实现序列化接口时使用枚举来替代数组,错误做法
private String[] HOBBIES = {"CLIMB","READ","SONG"};
正确做法
enum HOBBIES{
CLIMB,READ,SONG
}
代码示例
/**
* 1. 序列化Person时, 会调用调用writeReplace()生成一个PersonProxy对象, 然后对此对象进行序列化 (不是对Person类对象进行序列化,
* 由序列化文件的内容可以得知, 可以查看序列化生成的文件, 文件中内容为如下图 (代码之后的图)
* 2. 反序列化时, 会调用PersonProxy的readResolve()方法生成一个Person对象,
* 最后返回此对象的拷贝 (通过PersonProxy类的readResolve方法和main方法中的输出可以看出)
* 3. 因此, Person类的序列化工作完全交给PersonProxy类, 正如此模式的名称所表达的一样
*/
public class Person implements Serializable {
private final String name;
private final String hobby;
private final int age;
public Person(String name, String hobby, int age) {
System.out.println("Person(String name, String hobby, int age)");
//约束条件
if(age < 0 || age > 200) {
throw new IllegalArgumentException("非法年龄");
}
this.name = name;
this.hobby = hobby;
this.age = age;
}
public String getName() {
return name;
}
public String getHobby() {
return hobby;
}
public int getAge() {
return age;
}
private static class PersonProxy implements Serializable {
private final String name;
private final String hobby;
private final int age;
public PersonProxy(Person original) {
System.out.println("PersonProxy(Person original)");
this.name = original.getName();
this.hobby = original.getHobby();
this.age = original.getAge();
}
private Object readResolve() {
System.out.println("PersonProxy.readResolve()");
Person person = new Person(name, hobby, age);
System.out.println("resolveObject: " + person);
return person;
}
}
private Object writeReplace() {
System.out.println("Person.writeReplace()");
return new PersonProxy(this); //readObject的时候是调用, PersonProxy的readResolve()
}
//此方法不会执行,
private void writeObject(ObjectOutputStream out) {
System.out.println("Person.writeObject()");
}
//防止攻击者伪造数据, 企图违反约束条件 (如: 违反年龄约束)
private Object readObject(ObjectInputStream in) throws InvalidObjectException {
System.out.println("Person.readObject()");
throw new InvalidObjectException("Proxy required");
}
}
测试用例
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
Person person = new Person("张三", "足球" ,25);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("serial.ser"));
out.writeObject(person);
out.flush();
out.close();
Thread.sleep(1000);
ObjectInputStream in = new ObjectInputStream(new FileInputStream("serial.ser"));
Person deserPerson = (Person) in.readObject();
System.out.println("main: " + person);
in.close();
if(person == deserPerson) {
System.out.println("序列化前后是同一个对象");
} else {
//程序会走这一段, 反序列化会创建对象, 但是不会执行类的构造方法, 而是使用输入流构造对象
System.out.println("序列化前后不是同一个对象, 哈哈哈");
}
}
本书完结