静态工厂方法就是让类提供一个公用的静态工厂方法,根据该方法来返回一个实例对象
例如Boolean中的valueof方法:
public final class Boolean implements java.io.Serializable,
Comparable<Boolean>
{
/**
* The {@code Boolean} object corresponding to the primitive
* value {@code true}.
*/
public static final Boolean TRUE = new Boolean(true);
/**
* The {@code Boolean} object corresponding to the primitive
* value {@code false}.
*/
public static final Boolean FALSE = new Boolean(false);
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
}
这跟传统的构造方法来创建对象有什么区别和优点呢?
1.与构造器不同,静态工厂方法有名称,能做到见名知意,使用起来更见方便,代码阅读性更好,而传统构造方法是以类名作为名称
public class materia {
private String country;
private String quality;
private long price;
private materia(String country, String quality) {
this.country = country;
this.quality = quality;
}
private materia(long price) {
this.price = price;
}
//只关心他的来源和材质
public static materia getCountryAndQuality(String country,String quality){
return new materia("中国","银");
}
//只关心他的价格
public static materia getPrice(long price ){
return new materia(600);
}
public static void main(String[] args) {
//传统创建方式
materia steel = new materia("中国", "钢");
materia silver = new materia(300);
//静态工厂方法创建对象 代码可读性好
materia steel1 = materia.getCountryAndQuality("中国", "钢"))
materia silver2 = materia.getPrice(300);
}
}
2.不用再每次调用的时候都创建一个新的意义相同的实例而产生资源浪费。静态工厂方法每次调用都返回的是同一个对象,这就使得不管什么时候,类都能严格控制存在哪些实例,可以确保一个类是Singleton或是不可实例化的,其次,可以实现一个不可变的值的类,保证不存在两个相同的实例
public class materia {
private String country;
private String quality;
private long price;
private static materia gold=new materia(700);
private materia(long price) {
this.price = price;
}
public static materia getGold(){
System.out.println("创建金材料");
return gold;
}
public static void main(String[] args) {
materia gold1 = materia.getGold();
materia gold2 = materia.getGold();
System.out.println(gold1==gold2);//打印true
}
}
其实这就是用静态工厂方法实现的单例模式
3.静态工厂方法可以返回所声明的返回类型的任何子类型的对象:
public class materia {
private String country;
private long price;
private String name;
public materia(String name) {
this.name = name;
}
public String getName() {
return name;
}
public static materia getInstance(String name) {
if (name.equalsIgnoreCase("gold")) {
return new gold();
} else if (name.equalsIgnoreCase("sliver")) {
return new sliver();
} else {
return new materia(name);
}
}
public static void main(String[] args) {
materia materia1 = materia.getInstance("gold");
System.out.println(materia1.getName());//打印gold
}
}
4.静态工厂方法所返回对象的类可以输入的参数不同而改变:
EnumSet类没有公有的构造器,只有静态工厂方法,再OpenJD的实现中,根据底层枚举类型的元素数量,他们会返回两个子类之一的实例:如果元素数量小于或等于64,就像大多数枚举类型那样,静态工厂方法会返回一个RegularEnumSet实例,其底层是一个long类型的值,如果数量大于64,该工厂返回一个Ju'm'boEnumSet实例,其底层是一个数组。
这两个实现类的存在对客户端来说是不可见的。如何对于小型的枚举类型,RegularEnu'm'Set的性能优势不复存在,这个类就有可能再未来发行版中被删除,而这不会造成任何不良的影响,同样的,如果能提升性能,未来的发行版本中可能会添加Enum的第三个甚至第四个实现,客户端即不知道也不关心他们从这个静态工厂方法得到的对象的具体类,他们只关心一点,这类是EnumSet的某个子类。
5.静态工厂方法在编写包含该方法的类时,所返回对象的类并不一定存在:
(这里小编也没能理解,这是从网上摘抄的)
在JDBC(Java数据库连接)中的服务提供者框架是一个典型的例子,其中静态工厂方法可以返回对象的类在编写包含该方法的类时并不一定存在。
在JDBC中,服务提供者框架用于加载和管理数据库驱动程序。驱动程序供应商可以通过实现特定接口并提供驱动程序的实现来注册其驱动程序。服务提供者框架允许应用程序通过静态工厂方法获取适当的驱动程序实例,而无需显式引用特定的驱动程序类。
下面是一个简化的示例,展示了JDBC服务提供者框架的代码:
public class JDBCProviderFramework {
// 私有构造函数,防止实例化
private JDBCProviderFramework() {
}
// 静态工厂方法,返回数据库连接对象
public static Connection getConnection(String url, String username, String password) throws SQLException {
// 加载并注册合适的数据库驱动程序
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
throw new SQLException("Failed to load database driver");
}
// 获取数据库连接
return DriverManager.getConnection(url, username, password);
}
}
public class Main {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydb";
String username = "root";
String password = "password";
try {
Connection connection = JDBCProviderFramework.getConnection(url, username, password);
// 使用数据库连接执行操作
// ...
} catch (SQLException e) {
e.printStackTrace();
}
}
}
在上述示例中,JDBCProviderFramework 类是服务提供者框架的一部分。它包含一个私有构造函数和一个静态工厂方法 getConnection(),用于获取数据库连接对象。
在 getConnection() 方法中,首先使用 Class.forName() 方法加载并注册合适的数据库驱动程序,这里以MySQL驱动程序为例。然后,通过 DriverManager.getConnection() 方法获取数据库连接对象,并将其返回。
在 Main 类的 main() 方法中,我们使用 JDBCProviderFramework.getConnection() 方法获取数据库连接对象,并在实际应用程序中使用该连接对象执行数据库操作。
这里的关键是,在编写 JDBCProviderFramework 类时,并没有显式引用或依赖于特定的数据库驱动程序类。相反,通过使用服务提供者框架和静态工厂方法,可以在未来动态加载和使用不同的数据库驱动程序,而无需修改 JDBCProviderFramework 类的代码。
这种设计允许在未来定义或扩展返回的对象类(即不同的数据库驱动程序),以适应新的数据库技术或供应商。通过配置和动态加载,应用程序可以灵活地选择和切换不同的数据库驱动程序,而不需要修改主要的代码逻辑。
这个例子展示了静态工厂方法在JDBC服务提供者框架中的应用,其中返回的对象的类在编写包含该方法的类时并不一定存在,而是根据具体的实现在运行时动态加载和使用。
同样,提供静态工厂方法主要缺点就是如果没有公有的或受保护的构造器,就无法为这样的类创建子类。例如,集合框架的工具类都无法创建子类,不过也因祸得福,这样就鼓励程序员们使用组合而非继承,这真是不可变类型所必须的。
静态工厂方法程序员很难找到他们。
总之,静态工厂方法和构造器各有所长,一般来讲首选静态工厂,切记在没有考虑静态工厂方法是,要提供公有的构造方法。