《Effective Java》读书笔记-1

第一章 引言

书里写道:“本书不是针对初学者的,而是假设读者已经熟悉Java编程语言”

《Effective Java》读书笔记-1_第1张图片

本书大部分内容不讨论性能,而是关心如何编写出清晰、正确、可用、健壮、灵活 和 可维护的程序。

第二章创建和销毁对象第 1 条【用静态工厂方法代替构造器】

举例:

有一个类 People,当我们需要一个People的对象时,一般会用

People people = new People();

本书建议 在People类中添加一个静态方法:

public static People getPeople(){ return new People(); }

在需要时可以通过 getPeople 这个静态方法来获取一个实例: 

People people = People.getPeople();

方法优势:

1. 可读性

比起 构造器名称只能是类的名字,静态工厂方法可以通过自定义名字携带更多信息,增加代码可读性

2. 可以避免创建不必要的重复对象。

************原因有以下几点:
  1. 对象的缓存:静态工厂方法可以在内部维护一个对象池或缓存,通过缓存机制来管理已经创建的对象实例。当需要创建一个新对象时,首先检查缓存中是否已经存在相同的对象实例,如果存在则直接返回缓存中的对象,避免了重复创建相同的对象。

  2. 单例模式:静态工厂方法可以实现单例模式,确保在同一个类加载器范围内只创建一个对象实例。通过静态工厂方法返回同一个对象实例,可以避免重复创建多个对象,保持对象的唯一性。

  3. 对象池:静态工厂方法可以实现对象池的机制,将一些常用的对象实例保存在对象池中,当需要使用对象时,可以从对象池中获取对象实例,而不是每次都创建新的对象。这样可以减少对象的创建和销毁次数,提高性能和资源利用率

************举例:

假设我们有一个简单的用户管理系统,其中有一个 User 类表示用户信息,我们希望通过静态工厂方法来创建 User 对象,并且避免创建重复的用户对象。我们可以使用静态工厂方法来实现对象的缓存机制,确保同一个用户名只创建一个对应的 User 对象。

import java.util.HashMap;
import java.util.Map;

class User {
    private String username;

    private User(String username) {
        this.username = username;
    }

    public static User createUser(String username) {
        return UserFactory.getUser(username);
    }

    public String getUsername() {
        return username;
    }
}

class UserFactory {
    private static Map userCache = new HashMap<>();

    public static User getUser(String username) {
        if (userCache.containsKey(username)) {
            System.out.println("Returning cached user: " + username);
            return userCache.get(username);
        } else {
            User user = new User(username);
            userCache.put(username, user);
            System.out.println("Creating new user: " + username);
            return user;
        }
    }
}

public class Main {
    public static void main(String[] args) {
        User user1 = User.createUser("Alice");
        User user2 = User.createUser("Bob");
        User user3 = User.createUser("Alice");

        System.out.println(user1.getUsername()); // Output: Alice
        System.out.println(user2.getUsername()); // Output: Bob
        System.out.println(user3.getUsername()); // Output: Alice
    }
}
************例子分析:

在上面的示例中,UserFactory 类维护了一个 userCache 缓存,用于存储已经创建的 User 对象。静态工厂方法 getUser 会首先检查缓存中是否存在相同用户名的用户对象,如果存在则直接返回缓存中的对象,否则创建新的 User 对象并放入缓存中。

在 Main 类中,我们通过静态工厂方法 User.createUser 来创建用户对象,当创建相同用户名的用户时,会直接返回缓存中的对象,避免了重复创建相同用户名的用户对象。这样就实现了静态工厂方法可以避免创建不必要的重复对象的效果。

此外,当我们的代码在调用某个类的时候只需要一个实例,但是并不关心这个实例是否是一个新的对象时(只是为了使用类中的方法),也通过静态工厂方法便可以很方便的实现,同时提升程序性能。

3. 静态工厂方法可以返回原返回类型的任何子类型对象
************举例:

假设我们有一个接口 Shape 和它的两个实现类 Circle 和 Rectangle,并且有一个静态工厂方法 createShape,它返回 Shape 类型的对象。在该静态工厂方法中,我们可以根据不同的条件来返回 Circle 或 Rectangle 类型的对象。

interface Shape {
    void draw();
}

class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a circle");
    }
}

class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a rectangle");
    }
}

public class ShapeFactory {
    public static Shape createShape(String type) {
        if ("circle".equalsIgnoreCase(type)) {
            return new Circle();
        } else if ("rectangle".equalsIgnoreCase(type)) {
            return new Rectangle();
        } else {
            throw new IllegalArgumentException("Invalid shape type");
        }
    }

    public static void main(String[] args) {
        Shape circle = ShapeFactory.createShape("circle");
        circle.draw(); // Output: Drawing a circle

        Shape rectangle = ShapeFactory.createShape("rectangle");
        rectangle.draw(); // Output: Drawing a rectangle
    }
}
************例子分析:

在上面的示例中,静态工厂方法 createShape 返回了 Shape 类型的对象,但根据传入的类型参数不同,它可以返回 Circle 或 Rectangle 类型的对象。这样的设计允许我们在调用工厂方法时动态地选择返回哪种子类型对象。

4. 静态工厂方法所返回的对象可以随着每次调用而发生变化,这取决于参数值

结合上例即可看出。另外,静态工厂方法可以根据参数值设置对象的属性或状态,从而影响返回的对象实例。

5. 静态工厂方法返回的对象所属的类,在编写包含该静态工厂方法的类时可以不存在
************原因解释:
  1. 接口隔离原则: 静态工厂方法可以返回接口类型或抽象类类型的对象实例,而不一定要返回具体的实现类对象。这样,在编写包含静态工厂方法的类时,可以针对接口或抽象类进行编程,而不需要关心具体的实现类是否存在。

  2. 工厂方法模式: 静态工厂方法通常用于实现工厂方法模式,即通过工厂方法来创建对象,而不是直接在类中使用构造方法创建对象。在这种模式下,静态工厂方法可能返回的是某种产品的抽象类型,而具体的产品类可以在不同的地方实现,因此在编写包含静态工厂方法的类时,不需要关心具体的产品类是否存在。

  3. 延迟加载: 静态工厂方法可以用于延迟加载对象,只有在需要时才真正创建对象。这种延迟加载的方式可以避免在编写类时提前创建对象,从而减少类之间的耦合性。

************服务提供者框架:

服务提供者框架(Service Provider Framework)是一种设计模式,用于在应用程序中提供可插拔的服务实现。这种框架的核心思想是将服务的接口与具体的实现分离开来,使应用程序能够在运行时动态选择和加载服务的实现。

服务提供者框架通常包含以下几个关键组件:

  1. 服务接口(Service Interface): 定义了服务的功能和操作,是服务提供者和服务消费者之间的契约。

  2. 服务提供者(Service Provider): 实现了服务接口的具体服务类。服务提供者可以有多个不同的实现类,可以在运行时动态切换。

  3. 服务注册(Service Registry): 用于注册和管理服务提供者的信息,包括服务接口和对应的实现类。服务注册通常以配置文件、注解或其他方式进行。

  4. 服务消费者(Service Consumer): 使用服务接口来获取服务的功能。服务消费者从服务注册中获取服务提供者的信息,并动态选择合适的实现类进行调用。

通过服务提供者框架,应用程序可以实现松耦合的架构,使得服务的实现和调用可以独立演化和扩展。同时,服务提供者框架还能够支持运行时动态加载和替换服务的实现,提高了系统的灵活性和可维护性。

在Java中,ServiceLoader 类是用于实现服务提供者框架的标准类库,它可以用于加载和实例化服务提供者。通过定义服务接口、实现服务提供者和使用ServiceLoader类,可以很方便地实现服务提供者框架。

静态工厂方法返回的对象所属的类在编写包含该静态工厂方法的类时可以不存在,

************这种特性为构建服务提供者框架提供了基础,

因为这种特性提供了一种灵活的方式来实现服务提供者框架中的服务注册和动态加载的功能。

该特性的优势包括以下几点:

  1. 解耦合: 静态工厂方法将服务的接口和实现类分离开来,使得服务提供者和服务消费者之间的耦合度降低。服务提供者的实现类可以在编写包含静态工厂方法的类时不存在,从而实现了解耦合。

  2. 动态加载: 静态工厂方法可以根据需要动态选择并加载服务的实现类。这种灵活性使得应用程序可以在运行时动态决定使用哪种具体的服务实现,从而实现了动态加载的功能。

  3. 可扩展性: 由于静态工厂方法返回的对象所属的类可以在编写包含该静态工厂方法的类时不存在,因此可以很容易地添加新的服务实现类,而无需修改现有的代码。这样就实现了服务提供者框架中的可扩展性。

  4. 灵活性: 静态工厂方法的特性提供了一种灵活的机制来管理和使用服务的实现类。通过合适的配置和管理,可以实现不同环境下的不同服务实现的选择和切换。

方法劣势:

1、类如果不含公有的或者受保护的构造器,就不能被子类化

如果类不含上述的这两种构造器,子类无法调用父类的构造器来创建对象实例,就没办法被继承,也无法覆盖父类的静态工厂方法。

2、程序员很难发现静态工厂方法

你可能感兴趣的:(《Effective,Java》读书笔记,java,开发语言)