静态工厂方法优点:
缺点:
静态工厂方法惯用名称:
Date d = Date.from(instant);
Set faceCards = EnumSet.of(JACK,QUEEN,KING);
BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
4)instance/getInstance:返回的实例是通过方法的(如有)参数来描述的,但是不能说与参数具有同样的值,如
StackWalker luke = StackWalker.getInstance(Instance);
Object newArray = Array.newInstance(ClassObject,arrayLen);
FileStore fs = Files.getFileStore(path);
BufferedReader br = Files.newBufferedReader(path);
List litany = Collections.list(legacyLitany);
如果类的构造器或者静态工厂中具有多个参数(遇到参数不固定的时候),构造器无法解决问题,原因:
设计这种类时,使用建造者(Builder)模式:不直接生成想要的对象,而是让客户端利用所有必要的参数调用构造器(或者静态工厂),得到一个builder对象。然后客户端在builder对象上调用类似于setter的方法,来设置每个相关的可选参数。最后,客户端调用无参的build方法来生成通常是不可变的对象。这个builder通常是它构建的类的静态成员类。
Builder优点:
2)使用类结构(继承父类extends),这里摘录两个例子:
①通过Builder操作接口返回的Model里的属性值,得到想要的结果,代码来自项目的ComeBackModel类
public class AMoled{
boolean aBack;
public boolean isABack() {
return aBack;
}
AMoled(){
}
public static AMoledBuilder builder() {
return new AMoledBuilder ();
}
public static final class AMoledBuilder {
String aNum1;
String aNum2;
private AMoledBuilder () {
}
public AMoledBuilder withNum1(String aNum1) {
this.aNum1= aNum1;
return this;
}
public AMoledBuilder withNum2(String aNum2) {
this.aNum2 = aNum2;
return this;
}
public AMoledbuild() {
AMoled aMoled= new AMoled();
// 判断是否相等,把值true/false返回给AMoled
aMoled.aBack= (this.aNum1!= null && this.aNum1.equals(this.aNum2));
return aMoled;
}
}
}
②一个类的参数不固定,有必选和可选
public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;
private NutritionFacts(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
public static class Builder{
private final int servingSize;
private final int servings;
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;
public Builder(int servingSize,int servings){
this.servings=servings;
this.servingSize=servingSize;
}
public Builder calories(int val){
calories = val;
return this;
}
public Builder fat(int val){
fat = val;
return this;
}
public Builder sodium(int val){
sodium = val;
return this;
}
public Builder carbohydrate(int val){
carbohydrate = val;
return this;
}
public NutritionFacts build(){
return new NutritionFacts(this);
}
}
}
Singleton是指仅仅被实例化一次的类,用来代表无状态的对象,如函数,或者那些本质上唯一的系统组件。使类成为Singleton会使它的客户端测试变得十分困难。
两种常见的实现方法步骤1)保持构造器私有 2)导出公有的静态成员,以便允许客户端能够访问该类的唯一实例。
/**
* ClassName: Elvis
* Description:
* date: 4/22/2019 1:33 PM
*
*/
public class Elvis {
/**
* 方法一:公有静态成员是一个public final域
* 优势:①公有的静态域是final的,所以该域总是包含相同的对象引用
* ②简单
* 建议使用
*/
public static final Elvis INSTANCE = new Elvis();
/**
* 方法二:公有的成员是个静态工厂方法
* 优势:①不改变API的前提下可以改变该类是否为Singleton,工厂返回该类的唯一实例,但是容易被修改,
* 如改成每个调用该方法的线程返回一个唯一的实例
* ②可以编写一个泛型Singleton工厂
* ③通过方法引用作为提供者,如Elvis::instance2就是一个Supplier
* 除非满足以上,否则还是使用方法一
*/
private static final Elvis INSTANCE2 = new Elvis();
public static Elvis getInstance2(){
return INSTANCE2;
}
// 其它共用的代码
private Elvis(){
// 私有的构造器
}
// otherMethod
public void leaveTheBuilding(){
}
}
/**
* 将上述方法实现的Singleton类变成可序列化的
* ①实现Serialzable
* ②声明所有实例域都是瞬时的,并提供一个readResolve方法,否则每次反序列化一个序列号的实例时,都会创建一个新的实例
* 在原来的类上修改
*/
public class Elvis implements Serializable{
// 添加readResolve方法
private Object readResolve(){
return INSTANCE/INSTANCE2;
}
}
实现Singleton的第三种方法(最佳):声明一个包含单个元素的枚举类型
/**
* 与公有域方法类似但更加简洁,无偿的提供了序列化机制
* 即使是在面对复杂的序列化或者反射攻击的时候,也绝对防止多次实例化
*
* 推荐使用,除非Singleton必须扩展一个超类,而不是扩展Enum(可以声明枚举去实现接口)
*/
public enum Elvis{
INSTANCE;
// otherMethod
public void leaveTheBuilding(){
}
}
编写只包含静态方法和静态域的工具类不希望被实例化(newInstance()),因为实例化对它没有意义。然而缺少显示构造器的时候编译器会自动提供一个公有的、无参的缺省构造器。
public class UtilityClass{
// 显示的构造器是私有的,所以外部无法访问,保证了类在任何情况下都不会被实例化
private UtilityClass(){
throw new AssertionError();
}
}
静态工具类和Singleton类不适合于需要引用底层资源的类。依赖注入形式:当创建一个新的实例时,就将该资源传到构造器中,提升类的灵活性、可重用性和可测试性。
public class SpellChecker{
private final Lexicon dictionary;
/**
* 使用依赖注入来引用资源:
* 创建SpellChecker实例的时候就把Lexicon实例的资源传到构造器
* @param dictionary
*/
public SpellChecker(Lexicon dictionary){
this.dictionary = Objects.requireNonNull(dictionary);
}
}
1)String.matches方法校验一个字符串是否与正则表达式相匹配,但不适合在注重性能的情形中重复使用,因为它在内部为正则表达式创建了一个Pattern实例却只用了一次。优化方法:把Pattern.compile(“正则表达式”)独立出来成为一个属性,定义正则表达式的规则,然后通过方法调用。
2)优先使用基本类型(int)而不是装箱基本类型(如Integer)
①元素被释放了却没有清空对象引用
②对象被缓存
③监听器和其他回调(确保回调被立即当做垃圾回收的最佳方法是只保存它们的弱引用,如只把它们保存成WeakHashMap中的键)
攻击方法:从构造器或者它的序列化对等体(readObject和readResolve方法)抛出异常,恶意子类的终结方法就可以在构造了一部分的应该已经半途夭折的对象上运行。这个终结方法会将对该对象的引用记录在一个静态域中,阻止它被垃圾回收。一旦记录到异常的对象,就可以轻松的在这个对象上调用任何原本不允许在这里出现的方法。
从构造器抛出的异常,应该足以防止对象继续存在;有了终结方法的存在,这一点就做不到了。这种攻击可能造成致命的后果。final类不会受到终结方法攻击,因为没人能够编写出final类的恶意子类。
为了防止非final类受到终结方法攻击,要编写一个空的final的finalize方法。
终结方法和清除方法的好处:
①当资源的所有者忘记调用它的close方法时,终结方法或者清除方法可以充当“安全网”
②本地对等体是一个本地的(非Java的)对象,普通对象通过本地方法委托给一个本地对象。因为本地对象不是一个普通对象,所以垃圾回收器不会知道它,当它的Java对等体被回收的时候,它不会被回收。如果本地对等体没有关键资源,并且性能也可以接受,就使用终结方法或者清除方法执行这项任务。如果本地对等体拥有必须被及时终止的资源,或者性能无法接受,那么该类就应该具有一个close方法。
清除方法的使用:
public class Rom implements AutoCloseable{
private static final Cleaner cleaner = Cleaner.create();
private static class State implements Runnable{
int numJunkPoles;
State(int numJunkPoles){
this.numJunkPoles=numJunkPoles;
}
@Override
public void run() {
System.out.println("Cleaning the room");
numJunkPoles=0;
}
}
private final State state;
private final Cleaner.Cleanable cleanable;
public Rom(int numJunkPiles){
state=new State(numJunkPiles);
cleanable=cleaner.register(this,state);
}
@Override
public void close() throws Exception {
cleanable.clean();
}
}
①结论:处理必须关闭的资源,如InputStream、OutputStream和java.sql.Connection时,优先考虑使用try-with-resources,而不是try-finally
说人话:try-finally的关闭代码块太多了,而且如果第一个try发生了异常,那么第二个try的异常就会被禁止,以保留第一个异常。被禁止的异常会被打印在堆栈轨迹中,并注明它们是被禁止的异常,通过Throwable[] suppressed = e.getSuppressed();遍历该数组可以访问到
②使用方式:类实现implements AutoCloseable接口,包含单个返回void的close方法
public class Test implements AutoCloseable{
public static String main(String[] args) {
try(BufferedReader br = new BufferedReader(new FileReader("path"))){
return br.readLine();
} catch (IOException e) {
// 输出catch块的异常信息
System.out.println(e.getMessage());
// 输出close块的异常信息
Throwable[] suppressed = e.getSuppressed();
for (int i = 0; i < suppressed.length; i++) {
System.out.println(suppressed[i].getMessage());
}
}
@Override
public void close() throws Exception {
// 已自动关闭资源
System.out.println(“已自动关闭资源”);
}
}
}
具体解释:摘自https://blog.csdn.net/weixin_40255793/article/details/80812961
try-finally异常处理有两种情况:
1、try 块没有发生异常时,直接调用finally块,如果 close 发生异常,就处理。
2、try 块发生异常,catch 块捕捉,进行第一处异常处理,然后调用 finally 块,如果 close 发生异常,就进行第二处异常处理。
在 try-with-resources 结构中,异常处理也有两种情况(注意,不论 try 中是否有异常,都会首先自动执行 close 方法,然后才判断是否进入 catch 块):
1、try 块没有发生异常时,自动调用 close 方法,如果发生异常,catch 块捕捉并处理异常。
2、try 块发生异常,然后自动调用 close 方法,如果 close 也发生异常,catch 块只会捕捉 try 块抛出的异常,close 方法的异常会在catch 中被禁止,但是你可以在catch块中,用 Throwable.getSuppressed 方法来获取到被禁止异常的数组。
另外:
1)catch 块中,看不到 try-with-recourse 声明中的变量。
2)try-with-recourse 中,try 块中抛出的异常,在 e.getMessage() 可以获得,而调用 close() 方法抛出的异常在e.getSuppressed() 获得。
3)try-with-recourse 中定义多个变量时,由反编译可知,关闭的顺序是从后往前