Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >《Effective Java》读书笔记(一)之创建和销毁对象

《Effective Java》读书笔记(一)之创建和销毁对象

作者头像
老马的编程之旅
发布于 2022-06-22 03:32:44
发布于 2022-06-22 03:32:44
37300
代码可运行
举报
文章被收录于专栏:深入理解Android深入理解Android
运行总次数:0
代码可运行

最近在研读《Effective Java》一书,读书不做点笔记,感觉很容易就忘掉,于是用本篇博客来记录阅读此书的笔记。 郑重声明: 由于是《Effective Java》一书的笔记,所以大部分内容基本来自此书,还有一小部分是自己的理解。

第1条 考虑用静态工厂方法代替构造器

一般来说,获取一个类的实例,大家想到的都是通过该类的公有构造函数来new 一个实例,而第一条推荐我们可以考虑使用静态工厂方法来创建实例,这里的工厂方法并不直接对应于设计模式中的工厂方法模式。

静态工厂方法与构造器不同的第一大优势在于:它们有名称 使用静态工厂方法可以通过方法的名称较好的描述返回的对象是什么类型,例如BigInteger的构造方法,BigInteger(int, int, Random)可能返回素数,用BigInteger .probablePrime(int bitLength, Random rnd) 就让人很容易通过方法名,知道返回值是什么类型。

静态工厂方法与构造器不同的第二大优势在于:不必再每次调用它们的时候都创建一个新对象 如果一个类是不可变类,那在获取它的实例时,就不必每次都通过构造方法去new,而是通过静态工厂方法将之前已经创建好,并缓存的实例返回即可。这样可以在单进程中保持一直都只有一个实例,最典型的例子莫过于单例模式了。

静态工厂方法与构造器不同的第三大优势在于,它们可以返回原返回类型的任何子类型的对象 它的灵活性在于,API可以返回对象,同时又不会使对象变为公有的。典型就是Collections几乎都是通过静态工厂方法在一个不可实例化的类中导出。

静态工厂方法返回对象所属的类,在编写包含该静态工厂方法的类时可以不必存在,这种灵活的方式构成了服务提供者框架

服务提供者框架包含三个组件:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
服务接口 提供者实现
提供者注册API 系统用来注册实现,让客户端访问
服务访问API 客户端用来获取服务的实例
服务提供者接口(可选) 负责创建其服务实现的实例

示例代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface Service
{
    public void service();
}
public interface Provider
{
    Service newService();
}
public class Services
{
    private Services(){}
    private static final Map< String, Provider > = new ConcurrentHashMap< String, Provider >();
    public static final String DEFAULT_PROVIFER_NAME = "<def>";

    public static void registerDefaultProvider( Provider p )
    {
        registerProvider( DEFAULT_PROVIDER_NAME, p );
    }
    public static void registerProvider( String name, Provider p )
    {
        providers.put( name, p );
    }

    public static Service newInstance()
    {
        return newInstance( DEFAULT_PROVIDER_NAME );
    }
    public static Service newInstance( String name )
    {
        Provider p = providers.get( name );
        if ( p == null )
        {
            throw new IllegalArgumentException( "No provider registered with name : " + name );
        }
        return p.newServices();
    }
}

静态工厂方法的第四大优势在于,在创建参数化类型实例的时候,它们使代码变得更加简洁

优化前:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Map<String,List<String>> m = new HashMap<String,List<String>>();

静态工厂方法优化

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static< K, V > HashMap< K, V > newInstance()
{
    return new HashMap< K, V >();
}

Map< String, List< String > > map =    HashMap.newInstance();

静态工厂方法的主要缺点在于,类如果不含公有的或者受保护的构造器,就不能被子类化

静态工厂方法的第二个缺点在于,它们与其他的静态方法实际上没有任何区别

静态工厂方法惯用名称:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
valueOf 返回的实例和其参数具有相同的值,实际上是类型转换方法
of valueOf的替代
getInstance 根据方法的参数返回相应的实例,对于Singleton,无参数,返回唯一的实例
newInstance 和getInstance功能类似,但确保返回的每个实例是新创建的
getType 和getInstance功能类似,在工厂方法处于不同的类中的时候使用,Type表示工厂方法所返回的对象类型
newType 和newInstance功能类似,在工厂方法处于不同的类中的时候使用

第2条 遇到多个构造器参数时要考虑用构建器

静态工厂和构造器的局限:不能很好地扩展到大量的可选参数

对于这种情况,一般有以下解决方案: 1.重载构造器模式

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class NutritionFacts {
    private final int servingSize; // (mL) required
    private final int servings; // (per container) required
    private final int calories; // optional
    private final int fat; // (g) optional
    private final int sodium; // (mg) optional
    private final int carbohydrate; // (g) optional

    public NutritionFacts(int servingSize, int servings) {
        this(servingSize, servings, 0);
    }

    public NutritionFacts(int servingSize, int servings, int calories) {
        this(servingSize, servings, calories, 0);
    }

    public NutritionFacts(int servingSize, int servings, int calories, int fat) {
        this(servingSize, servings, calories, fat, 0);
    }

    public NutritionFacts(int servingSize, int servings, int calories, int fat,
            int sodium) {
        this(servingSize, servings, calories, fat, sodium, 0);
    }

    public NutritionFacts(int servingSize, int servings, int calories, int fat,
            int sodium, int carbohydrate) {
        this.servingSize = servingSize;
        this.servings = servings;
        this.calories = calories;
        this.fat = fat;
        this.sodium = sodium;
        this.carbohydrate = carbohydrate;
    }

    public static void main(String[] args) {
        NutritionFacts cocaCola = new NutritionFacts(240, 8, 100, 0, 35, 27);
    }
}

缺点:当有许多参数的时候,客户端代码会很难编写,且难以阅读

2.javaBean模式 调用一个无参构造器来创建对象,然后调用setter方法来设置每个必要的参数,以及每个相关的可选参数

缺点:构造过程中javaBean可能出于不一致的状态,类无法仅仅通过检验构造器参数的有效性来保证一致性

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class NutritionFacts {
    // Parameters initialized to default values (if any)
    private int servingSize = -1; // Required; no default value
    private int servings = -1; // "     " "      "
    private int calories = 0;
    private int fat = 0;
    private int sodium = 0;
    private int carbohydrate = 0;

    public NutritionFacts() {
    }

    // Setters
    public void setServingSize(int val) {
        servingSize = val;
    }

    public void setServings(int val) {
        servings = val;
    }

    public void setCalories(int val) {
        calories = val;
    }

    public void setFat(int val) {
        fat = val;
    }

    public void setSodium(int val) {
        sodium = val;
    }

    public void setCarbohydrate(int val) {
        carbohydrate = val;
    }

    public static void main(String[] args) {
        NutritionFacts cocaCola = new NutritionFacts();
        cocaCola.setServingSize(240);
        cocaCola.setServings(8);
        cocaCola.setCalories(100);
        cocaCola.setSodium(35);
        cocaCola.setCarbohydrate(27);
    }
}

3.Builder模式 既能保证像重叠构造器模式那样的安全性,也能保证像javaBean模式那么好的可读性。它不直接生成想要的对象,而让客户端利用所有必要的参数调用构造器(或者静态工厂),得到一个builder对象。然后客户端在builder对象上调用类似于setter的方法,来设置每个相关的可选参数。最后,客户端调用无参的build方法来生成不可变的对象。这个builder是它构建的类的静态成员类。另外,一个明显的优势是,builder利用单独的方法来设置每个参数,你想要多少个可变参数,它们就可以有多少个,直到每个setter方法都有一个可变参数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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;

    public static class Builder {
        // Required parameters
        private final int servingSize;
        private final int servings;

        // Optional parameters - initialized to default values
        private int calories = 0;
        private int fat = 0;
        private int carbohydrate = 0;
        private int sodium = 0;

        public Builder(int servingSize, int servings) {
            this.servingSize = servingSize;
            this.servings = servings;
        }

        public Builder calories(int val) {
            calories = val;
            return this;
        }

        public Builder fat(int val) {
            fat = val;
            return this;
        }

        public Builder carbohydrate(int val) {
            carbohydrate = val;
            return this;
        }

        public Builder sodium(int val) {
            sodium = val;
            return this;
        }

        public NutritionFacts build() {
            return new NutritionFacts(this);
        }
    }

    private NutritionFacts(Builder builder) {
        servingSize = builder.servingSize;
        servings = builder.servings;
        calories = builder.calories;
        fat = builder.fat;
        sodium = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }

    public static void main(String[] args) {
        NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8)
                .calories(100).sodium(35).carbohydrate(27).build();
    }
}

builder模式十分灵活,可以利用单个builder构建多个对象。builder的参数可以再创建对象期间进行调整,也可以随着不同的对象而改变,builder可以自动填充某些域,例如每次创建对象时自动增加序列号。

简而言之,如果类的构造器或者静态工厂中具有多个参数,设计这种类时,Builder模式就是中不错的选择,特别是当大多数参数都是可选参数的时候。与使用传统的重叠构造器模式相比,使用builder模式的客户端代码将更易于阅读和编写,构建器也比javaBean更加安全。

第3条 用私有构造器或者枚举类型强化Singleton属性

使类成为Singleton会使它的客户端测试变的十分困难,因为无法给Singleton替换模拟实现,除非它实现一个充当其类型的接口。

Singleton方法一般有两种

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Elvis {
    public static final Elvis INSTANCE = new Elvis();

    private Elvis() {
    }

    public void leaveTheBuilding() {
        System.out.println("Whoa baby, I'm outta here!");
    }

    // This code would normally appear outside the class!
    public static void main(String[] args) {
        Elvis elvis = Elvis.INSTANCE;
        elvis.leaveTheBuilding();
    }
}

私有构造器仅被调用一次,客户端的任何行为都不会改变这一点,但要提醒一点:享有特权的客户端可以借助AccessObject.setAccessible方法,通过反射机制调用私有构造器。如果需要抵御这种攻击,可以修改该构造器,让它在被要求创建第二个实例的时候抛出异常。

第二种方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Elvis {
    private static final Elvis INSTANCE = new Elvis();

    private Elvis() {
    }

    public static Elvis getInstance() {
        return INSTANCE;
    }

    public void leaveTheBuilding() {
        System.out.println("Whoa baby, I'm outta here!");
    }

    // This code would normally appear outside the class!
    public static void main(String[] args) {
        Elvis elvis = Elvis.getInstance();
        elvis.leaveTheBuilding();
    }
}

需要注意的是,为了实现Singleton类可序列化(Serializable),仅仅在声明中加上“implements Serializable”是不够的,为了维护并保证Singleton,必须声明所有实例域都是瞬时(transient)的,并提供一个readResolve方法。否则,每次反序列化一个序列化实例时,都会创建一个新的实例。实现如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Elvis {
    public static final Elvis INSTANCE = new Elvis();

    private Elvis() {
    }

    public void leaveTheBuilding() {
        System.out.println("Whoa baby, I'm outta here!");
    }

    private Object readResolve() {
        // Return the one true Elvis and let the garbage collector
        // take care of the Elvis impersonator.
        return INSTANCE;
    }

    // This code would normally appear outside the class!
    public static void main(String[] args) {
        Elvis elvis = Elvis.INSTANCE;
        elvis.leaveTheBuilding();
    }
}

从Java1.5发行版本起,实现Singleton还有第三种方法。只需编写一个包含单个元素的枚举类型。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public enum Elvis {
    INSTANCE;

    public void leaveTheBuilding() {
        System.out.println("Whoa baby, I'm outta here!");
    }

    // This code would normally appear outside the class!
    public static void main(String[] args) {
        Elvis elvis = Elvis.INSTANCE;
        elvis.leaveTheBuilding();
    }
}

使用单个元素的枚举类型来保证Singleton的方法,更加简洁,且无偿地提供了序列化机制,绝对防止多次实例化,即使是在面对复杂的序列化或者反射攻击的时候。虽然这种方法还没有广泛采用,但是它已经成为实现Singleton的最佳方法。

第4条 通过私有构造器强化不可实例化的能力

有时候,可能需要编写只包含静态方法和静态域的类,如:Java.lang.Math或者java.util.Arrays,这样的工具类,不希望被实例化,实例对它没有任何意义。然而,在缺少显式构造器的情况下,编译器会自动提供一个公有的、无参的缺省构造器(default constructor)。对于用户而言,这个构造器与其他的构造器没有任何区别。

企图通过将类做成抽象类来强化该类不可被实例化是行不通的,由于只有当类不包含显式的构造器时,编译器才会生成缺省的构造器,因此我们只要让这个类包含私有构造器,它就不被实例化了:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class UtilityClass {
    // Suppress default constructor for noninstantiability
    private UtilityClass() {
        throw new AssertionError();
    }
}

其中,AssertionError不是必须的,但是它可以避免不小心在类的内部调用构造器。它保证该类在任何情况下都不会被实例化。这种习惯用法有点违背直觉,好像构造器就是专门设计成不能被调用一样,因此,明智的做法就是在代码中增加一条注释,如上代码所示。

第5条 避免创建不必要的对象

一般来说,最好能重用对象而不是在每次需要的时候就创建一个相同功能的新对象 最极端的例子如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
String s = new String( "aaa" );//Donot do this

上面的代码,每次被执行都会创建一个新的String 实例,但这些创建对象的动作都是不必要的。

改进如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
String s = "aaa";

这个版本创建了一个String 实例

对于同时提供了静态工厂方法和构造器的不可变类,通常优先使用静态工厂方法,避免创建不必要的对象

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Person
{
    private final Date birthDate;

    //Donot do this
    public boolean isBabyBoomer()
    {
        Calendar gmtCal = Calendar.getInstance( TimeZone.getTimeZone("GMT") );
        gmtCal.set( 1946, Calendar.JANUARY, 1, 0, 0, 0 );
        Date boomStart = gmtCal.getTime();
        gmtCal.set( 1965, Calendar.JANUARY, 1, 0, 0, 0 );
        Date boomEnd = gmtCal.getTime();
        return birthDate.compareTo( boomStart )>=0
                && birthData.compareTo( boomEnd )<0;
    }
}

isBabyBoomer方法每次调用的时候,都会去创建boomStart 和boomEnd 两个值,然而这两个值都是固定的,没必要每次都创建,修改如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//better implements
public class Person
{
    private final Date birthDate;

    private static final Date BOOM_START;
    private static final Date BOOM_END;
    static
    {
        Calendar gmtCal = Calendar.getInstance( TimeZone.getTimeZone( "GMT" ) );
        gmtCal.set( 1946, Calendar.JANUARY, 1, 0, 0, 0 );
        BOOM_START = gmtCal.getTime();
        gmtCal.set( 1965, Calendar.JANUARY, 1, 0, 0, 0 );
        BOOM_END = gmtCal.getTime();
    }

    //Do this
    public boolean isBabyBoomer()
    {
        return birthDate.compareTo( BOOM_START)>=0
                && birthData.compareTo( BOOM_END)<0;
    }
}

在Java1.5发行版本中,有一种创建“多余”对象的新方法,乘坐自动装箱(autoboxing),它允许程序员将基本类型和装箱基本类型(Boxed Primitive Type)混用,按需要自动装箱和拆箱。自动装箱使得基本类型和装箱基本类型之间的差别变的模糊起来,但是并没有完全消除。非常值得关注的一点,使用自动装箱的性能远低于使用基本类型的性能。要当无意识的自动装箱。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Sum {
    // Hideously slow program! Can you spot the object creation?
    public static void main(String[] args) {
        Long sum = 0L;
        for (long i = 0; i < Integer.MAX_VALUE; i++) {
            sum += i;
        }
        System.out.println(sum);
    }
}

上述sum是Long类型而不是long,就是这个Long,就会导致计算的耗时增加

通过维护自己的对象池来避免创建对象并不是一种好的做法,除非池中的对象是非常重量级的,真正正确使用对象池的典型对象示例就是数据库连接池 一般而言,维护自己的对象池会增加代码的复杂性,增加内存占用,还会损害性能

当应该重用现有对象的时候,不要创建新的对象 当该创建新对象的时候,不要重用现有的对象 在提倡使用保护性拷贝的时候,因重用对象而付出的代价要远远大于因创建重复对象而付出的代价 必要时如果没能实施保护性拷贝,将会导致潜在的错误和安全漏洞;不必要地创建对象则只会影响程序的风格和性能

第6条 消除过期的对象引用

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Stack {
    private Object[] elements;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;

    public Stack() {
        elements = new Object[DEFAULT_INITIAL_CAPACITY];
    }

    public void push(Object e) {
        ensureCapacity();
        elements[size++] = e;
    }

    public Object pop() {
        if (size == 0)
            throw new EmptyStackException();
        return elements[--size];
    }

    /**
     * Ensure space for at least one more element, roughly doubling the capacity
     * each time the array needs to grow.
     */
    private void ensureCapacity() {
        if (elements.length == size)
            elements = Arrays.copyOf(elements, 2 * size + 1);
    }
}

上面的代码存在内存泄漏的可能,如果一个栈先是增长,然后再收缩,那么,从栈弹出的对象将不会当作垃圾进行回收,即使是使用栈的程序不在引用这些对象,因为栈维护着过期引用 过期引用,指永远也不会再被解除的引用。

如果一个对象引用被无意识地保留起来了,那么垃圾回收机制不仅不会处理这个对象,而且也不会处理被这个对象所引用的所有其他对象

这类问题解决方式是:一旦对象的引用已经过期,只需要清空这些引用即可。如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public Object pop() {
        if (size == 0)
            throw new EmptyStackException();
        Object result = elements[--size];
        elements[size] = null;
        return result ;
    }

清空对象引用是一种例外,而不是一种规范行为,只要类是自己管理内存,应该警惕内存泄漏问题,消除过期引用最好的方法让包含该引用的变量结束其生命周期。

内存泄漏的另外一个常见来源是缓存:只要在缓存之后存在对某个项的键的引用,该项就有意义,可以用WeakHashMap代表缓存;当缓存中的项过期之后,会自动被删除

只有当所要的缓存项的生命周期是由该键的外部引用而不是由值决定时,WeakHashMap才有用处

内存泄漏的第三个常见来源是监听器和其他回调:确保回调立即被当做垃圾回收的最佳方法是只保存它们的弱引用

第7条 避免使用终结方法

终结方法(finalize)通常是不可预测的,也是很危险的,一般情况下是不必要的

C++的程序员别告知”不要把终结方法当作是C++中析构器(destructors)的对应物“。在C++中,析构器是回收一个对象所占用资源的常规方法,是析构器所必需的对应物。 在java中,当一个对象变得不可到达的时候,垃圾回收器会回收与该对象相关联的存储空间,并不需要程序员做专门的工作。

C++的析构器也可以被用来回收其他的非内存资源,而在java中,一般用try-finally块来完成类似的工作。

终结方法的缺点是不能保证会被及时地执行,从一个对象变得不可到达开始,到它的终结方法被执行,所花费的时间是任意长的。

Java语言规范不仅不保证终结方法会被及时地执行,而且根本就不保证其被执行,不应该依赖终结方法来更新重要的持久状态

不要被System.gc和System.runFinalization这两个方法所诱惑,它们确实增加了终结方法被执行的机会,但是它们并不保证终结方法一定会被执行。唯一声称保证终结方法被执行的方法是System.runFinalizersOnExit,以及它臭名昭著的孪生兄弟Runtime.runFinalizersOnExit。这两个方法都有致命的缺陷,已经被废弃了。

使用终结方法有一个非常严重的性能损失,用终结方法创建和销毁对象的速度更加缓慢。

对于确实需要终止的方法,应提供一个显示的终止方法,并要求改类的客户端在每个实例不再有用的时候调用这个方法 显式的终止方法通常与try-finally结构结合起来使用,以确保及时终止

终结方法有两种合法用途

1.当对象所有者忘记调用显式终止方法时,终结方法充当“安全网” 2.与对象的本地对等体有关。本地对等体是一个本地对象,普通对象通过本地方法委托给一个本地对象,垃圾回收器无法感知本地对象的存在,当Java对等体被回收时,它不会被回收 “终结方法链”不会被自动执行,需要进行显式调用 另外一种可选方法是终结方法守卫者

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017-07-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【Effective Java】Ch2_创建销毁对象:Item2_当构造函数参数过多时考虑使用builder
静态工厂和构造函数都有一个限制:可选参数数量很大时,他们都不能很好地扩展。考虑一下这个例子:用一个类来表示袋装食品上的营养成分标签,这些标签有几个必选字段:每份的含量、每罐的份数、每份的卡路里;还有超过20个可选字段:总脂肪含量、饱和脂肪含量、转化脂肪含量、胆固醇含量、钠含量等等。大多数产品只有少数几个可选字段是非零值。
用户7886150
2020/12/15
5010
Effective Java(一)
对于类而言,为了让客户端获取它自身的一个实例,最传统的方法就是提供一个公有的构造器。还有一种方法,也应该在每个程序员的工具箱中占有一席之地。类可以提供一个公有的静态工厂方法( static factory method ),它只是一个返回类的实例的静态方法。下面是一个来自 Boolean(基本类型 boolean 装箱类)的简单示例。这个方法将 boolean 基本类型值转换成了 Boolean 对象引用:
Remember_Ray
2020/08/03
6780
Effective.Java 读书笔记(3)关于单例模式
使一个类成为单例这件事会使得难以测试它的用户,正如对于使用一个单例代替一个模拟实现除非它实现了一个接口,而这个接口作为它的类型的事是不可能的
Mezereon
2019/02/25
3790
效率编程 之「创建和销毁对象」
但是很遗憾,在标准的 JDK 中,并没有提供类似的静态工厂方法。不过,我们可以通过在项目中引入谷歌发布的Guava,使用类似上述的静态工厂方法。当然,静态工厂方法也不是尽善尽美的,也有其缺点:
CG国斌
2019/05/26
5360
java与es8实战之一:以builder pattern开篇
欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 关于《java与es8实战》系列 《java与es8实战》系列是欣宸与2022年夏季推出的原创系列,如标题所述,该系列从一个java程序员视角去学习和实践elasticsearch的8.2版本,目标是与大家一起掌握与elasticsearch开发相关的技能,以应对实际应用中的需求和挑战 本篇概览 纵观欣宸过往各种系列文章,开篇无外乎两种套路 第一种是对该系列的主
程序员欣宸
2022/06/19
6740
java与es8实战之一:以builder pattern开篇
Effective Java 2.0_Item 2_中文版
静态工厂和构造函数有一个共同的限制:对于大量可选参数它们都不能很好的扩展。考虑这样一种情况:用一个类来表示包装食品上的营养成分标签。这些标签有几个字段是必须的——每份含量、每罐含量(份数)、每份的卡路里,二十个以上的可选字段——总脂肪量、饱和脂肪量、转化脂肪、胆固醇、钠等等。大多数产品中这些可选字段中的仅有几个是非零值。
Tyan
2022/05/09
2790
Effective.Java 读书笔记(2)使用Builder
静态工厂和构造器都有一个限制,它们不能够很好地缩减大量地选项参数,想象一下一种情况,你的类有着很多的成员变量,有些必须填写有些可以选填,那么如果使用传统的构造方法的话,排列组合一下可以想象会有多少个构造方法出现,这样的情况不是我们所需要的,程序员们通常会使用一种名为”伸缩构造函数模式(Telescoping constructor pattern )“的办法,就是先提供必须要的选项参数作为最简单的构造方法,然后把非必须的选项参数逐渐加上去构成新的构造方法,不考虑组合的问题
Mezereon
2018/09/13
3780
effective Java 创建和销毁对象篇
小伙伴们好呀,我是 小羊 ,今天来和大家分享下 《Effective Java》这本书的 第2章 —— 创建和销毁对象 。
Java4ye
2024/02/14
3050
effective Java 创建和销毁对象篇
Effective Java 2.0_中英文对照_Item 2
Static factories and constructors share a limitation: they do not scale well to large numbers of optional parameters. Consider the case of a class representing the Nutrition Facts label that appears on packaged foods. These labels have a few required fields—serving size, servings per container, and calories per serving and over twenty optional fields—total fat, saturated fat, trans fat, cholesterol, sodium, and so on. Most products have nonzero values for only a few of these optional fields.
Tyan
2022/05/09
3430
《Effective Java 》系列一
编写实例受控类有几个原因。实例受控使得类可以确保他是一个Singleton或者是不可实例化的。他还使得不可变类可以确保不会存在两个相等的实例。
高广超
2018/12/12
8630
java(4)-深入理解java嵌套类、内部类以及内部类builder构建构造函数六、使用 builder 模式解决构造方法参数过多的情况
  可以在一个类的内部定义另一个类,这种类称为嵌套类(nested classes),它有两种类型:静态嵌套类和非静态嵌套类。静态嵌套类使用很少,最重要的是非静态嵌套类,也即是被称作为内部类(inner)。嵌套类从JDK1.1开始引入。其中inner类又可分为三种:  其一、在一个类(外部类)中直接定义的内部类;   其二、在一个方法(外部类的方法)中定义的内部类;   其三、匿名内部类。
黄规速
2022/04/14
1.7K0
重叠构造函数模式_Effective Java 2.0_Item 2知识点
Telescoping Constructor Pattern,中文名称为重叠构造函数(方法)模式,在实际项目中经常会用到的一种模式,主要是在构造函数参数有多个,且部分参数具有默认值的情况下使用,通常由一个默认构造函数和多个参数个数不同的构造函数组成,多个参数不同的构造函数最后都会委托给默认构造函数来构造类的实例。但这种模式有个缺点就是不能很好的进行扩展(Effective Java上讲的)。
Tyan
2022/05/09
2790
当构造方法参数过多时使用builder模式
静态工厂和构造方法都有一个限制:它们不能很好地扩展到很多可选参数的情景。请考虑一个代表包装食品上的营养成分标签的例子。这些标签有几个必需的属性——每次建议的摄入量,每罐的份量和每份卡路里 ,以及超过 20 个可选的属性——总脂肪、饱和脂肪、反式脂肪、胆固醇、钠等等。大多数产品都有非零值,只有少数几个可选属性。
海仔
2019/08/06
9930
笔记《Effective Java》01: 创建和销毁对象
《Effective Java》这本书可以说是程序员必读的书籍之一。这本书讲述了一些优雅的,高效的编程技巧。对一些方法或API的调用有独到的见解,还是值得一看的。刚好最近重拾这本书,看的是第三版,顺手整理了一下笔记,用于自己归纳总结使用。建议多读一下原文。今天整理第一章节:创建和销毁对象。
有一只柴犬
2025/01/24
860
笔记《Effective Java》01: 创建和销毁对象
Java架构师教你写代码(二) - 使用建造者替代多参数的构造器
比如一个类,表示包装食品上的营养标签。 有些字段是必需的:净含量、毛重和每单位份量的卡路里, 还有 20 个可选字段,如:总脂肪、饱和脂肪、反式脂肪、胆固醇、钠… 大多食品只使用可选字段中的少数,且非零值。
JavaEdge
2021/02/22
6800
Java架构师教你写代码(二) - 使用建造者替代多参数的构造器
Effective Java tips
所有对 Elvis.getInstance 的调用都返回相同的对象引用,并且不会创建其他的 Elvis 实例(与前面提到的警告相同)
只喝牛奶的杀手
2023/04/07
3020
Effective Java tips
Effective-java-读书笔记之创建和销毁对象
对于类而言, 最常用的获取实例的方法就是提供一个公有的构造器, 还有一种方法, 就是提供一个公有的静态工厂方法(static factory method), 返回类的实例.
特特
2022/10/08
4200
Java中的静态内部类_Effective Java 2.0_Item 2知识点
在Java中有静态代码块、静态变量、静态方法,当然也有静态类,但Java中的静态类只能是Java的内部类,也称为静态嵌套类。静态内部类的定义如下:
Tyan
2022/05/09
3310
建造者模式
将一个复杂的对象的构建和它的表示分离,使得同样的构建过程可以创建不同的表示。 ——《设计模式:可复用面向对象软件的基础》
mingmingcome
2021/11/29
3860
建造者模式
Effective Java要点笔记
工作中如果构造函数有多个 且 特定 的话,我一般倾向写两三个函数签名不一样构造器。但是如果在构造参数很多且多变,要写一个内部构建器,用builder模式,而不是大量重叠构造器。
程序员小强
2020/04/14
4610
Effective Java要点笔记
推荐阅读
相关推荐
【Effective Java】Ch2_创建销毁对象:Item2_当构造函数参数过多时考虑使用builder
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验