用静态工厂替代构造函数
1、关于可以重复使用对象以及命名的优势理解,但是其中可以返回原返回类型的任何类型,而该返回可以是非公有的。
如下面的例子,unmodifiableSet本身对外声明的返回类型是set接口,但是实际返回的是UnmodifiableSet这样一个非共有的类型。
也就是其实对于静态的工厂函数可以返回一个接口,具体的返回可以是该接口的任何子类,甚至是非公有,这样通过返回类型就明确是什么类型,但是又不关系具体到底是哪个实现类,足够灵活。
public static <T> Set<T> unmodifiableSet(Set<? extends T> s) {
return new UnmodifiableSet<>(s);
}
/**
* @serial include
*/
static class UnmodifiableSet<E> extends UnmodifiableCollection<E>
implements Set<E>, Serializable {
private static final long serialVersionUID = -9215047833775013803L;
UnmodifiableSet(Set<? extends E> s) {super(s);}
public boolean equals(Object o) {return o == this || c.equals(o);}
public int hashCode() {return c.hashCode();}
}
2、java8中允许接口中存在静态方法以及静态成员,但是不能是private的,直接通过接口实现调用。
//接口
public interface Eat {
public static void getGirl() {
System.out.println("111");
}
}
//接口实现类
public class EatImpl implements Eat{
}
public class Main {
public static void main(String[] args) {
Eat.getGirl();//可以——接口调用
EatImpl.getGirl();//不可以
}
}
注:
(1)实现类并没用继承该静态的函数或者静态成员
(2)在java8中很多接口也存在静态方法,比如Compator接口中的comparing()
(3)接口中不能存在私有的成员以及私有的方法—接口是约束,实现类无法看到
(4)java9中可以存在私有的静态方法—允许该接口中其他静态方法使用,但还是不能有私有的成员。
3、对于服务提供api用于提供服务的静态方法,可能由于该提供者并没有注册而不能提供服务。
和服务提供框架(SPF)相关,存在四个组件:服务、服务提供者、服务注册、服务提供api:
//服务
public interface Service {
public void service();
}
//服务提供者
public interface Provider {
public Service getService();
}
public class ServiceManager {
static Map<String,Provider> map=new HashMap<>();
//服务注册
public static void registerProvider(String name,Provider provider){
map.put(name,provider);
}
//服务提供api
//(1)服务实现类 (2)提供者实现类 (3)提供者注册 (4)提供服务
public static Service getService(String name){
Provider provider = map.get(name);
return provider.getService();
}
}
当前实现一个服务,并提供服务仅仅需要:
(1)服务实现类
(2)提供者实现类
(3)提供者注册
(4)提供服务
即在没有任何服务提供者注册时候,是无法提供服务的,因此对于提供服务的getService()这个静态方法,可能由于没有相应的服务注册者注册,而无法提供服务。
服务提供者框架实现了服务提供者注册即可提供服务,而获取服务也直接可以通过服务提供者名字实现灵活获取。
4、类如果不含共有的或者受保护的构造器就无法子类化
这个的意思是,其实静态工厂方法的本质是,通过返回这个函数返回值的子类,实现灵活性。但是由于子类继承父类,子类的构造函数会默认调用父类的无参构造函数,如果没有就需要显示调用同参的父类构造函数,因此如果父类的构造函数不是共有的或者protected那么就无法实例化子类,那么静态工厂方法就没有意义。
使用构建器方式
一般当构造对象的时候涉及多个参数,一般情况下会采用“重叠”的方式构建对象,即通过不同个数的构造器,创建不同的对象;或者通过javaBean的方式,先构建没有参数的对象,再通过setter的方式填充参数,但是此种方式会造成不一致,即这边还没有构建完成,那边就使用就会不一致。
通过构造器的方式进行构建,即先构建Buillder,而Builder中的参数和实际需要构建的对象的参数是一一对应的:
(1)首先,通过必要参数使用Builder的构造器构造Builder
(2)通过函数,基于需要,填充参数
(3)build对象
public class Subject {
int id;
String name;
String teacherName;
int score;
static class Builder{
int bId;
String bName;
String bTeacherName;
int bScore;
public Builder(int id,String name){
bId=id;
bName=name;
}
public Builder buildTeacherName(String teacherName){
bTeacherName=teacherName;
return this;
}
public Builder buildScore(int score){
bScore=score;
return this;
}
public Subject build(){
return new Subject(this);
}
}
private Subject(Builder builder){
this.id=builder.bId;
this.name=builder.bName;
this.teacherName=builder.bTeacherName;
this.score=builder.bScore;
}
}
new Subject.Builder(1,"java").buildScore(100).buildTeacherName("jjw").build();
通过构建器的方式保证了可读性,也保证了一致性,因为当仅当build的时候,该对象才存在。
用私有构造器
1、创建单例都需要将构造函数私有化,通过将域公有化或者通过静态方法返回单例对象。
2、单例的序列化需要实现readResolve()方法保证单例对象的唯一性:
public class Singleton implements Serializable{
private static final Singleton instance=new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
private Object readResolve(){
return instance;
}
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
ByteArrayOutputStream byteArrayOs = new ByteArrayOutputStream();
ObjectOutputStream objectOs = new ObjectOutputStream(byteArrayOs);
// 对象写入流中
objectOs.writeObject(Singleton.getInstance());
ObjectInputStream objectIs = new ObjectInputStream(new ByteArrayInputStream(byteArrayOs.toByteArray()));
System.out.println(objectIs.readObject() == Singleton.getInstance());
}
上面的例子,当仅仅当readResolve()存在才会是true。
3、也可以通过枚举类返回唯一对象。
避免使用终结方法和清除方法
1、通过cleaner注册对象以及对应的State的run()方法,当对象可以进行垃圾回收时,Cleaner 类的对象会自动得到通知,执行State中的run方法——但是不能保证一定执行以及执行时机。
public class CleanerTest {
public static void main(String args[]) {
System.out.println("nhooo");
//创建Cleaner
Cleaner cleaner = Cleaner.create();
if(true) {
CleanerTest myObject = new CleanerTest();
//向cleaner注册某个对象以及State,当前面对象进行垃圾回收的时候就会执行state中的run方法
cleaner.register(myObject, new State()); // register cleaner }
for(int i = 1; i <= 10000; i++) {
String[] largeObject = new String[1000];
try {
Thread.sleep(1);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
private static class State implements Runnable {
public void run() {
System.out.print("Cleaning action");
}
}
}
try-with-resource
首先被自动关闭的资源需要实现Closeable或者AutoCloseable接口,因为只有实现了这两个接口才可以自动调用close()方法去自动关闭资源,将要关闭的外部资源在try()中创建,catch()捕获处理异常。
try(FileInputStream inputStream=new FileInputStream(new File("test"))){
System.out.println(inputStream.read());
}catch (IOException e){
throw new RuntimeException(e.getMessage(),e);
}
将inputStream在使用完毕后关闭,就放在try中。