静态方法 与 实例化方法

本文探讨了实例化类的主要理由及其在实际应用中的常见误区,包括线程安全性、复用性和扩展性等方面,并提供了正确的设计模式建议。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

 

@用实例化类的最主要理由就是:
1.实例化一个类是为了获得类对象的数据成员或者状态
2.为了满足系统复用性和扩展性需要

 

____________________________________________________________________

 

 

@先看一个类
public   class       Test{
    public   static     String   hello(String   str){
        String   tmp= " ";
        tmp=     tmp+str;
        return   tmp;
    }
}

hello方法会不会有多线程安全问题呢?没有!!
静态方法如果没有使用静态变量,则没有线程安全问题。
为什么呢?因为静态方法内声明的变量,每个线程调用时,都会新创建一份,而不会共用一个存储单元。比如这里的tmp,每个线程都会创建自己的一份,因此不会有线程安全问题
注意,静态变量,由于是在类加载时占用一个存储区,每个线程都是共用这个存储区的,所以如果在静态方法里使用了静态变量,这就会有线程安全问题!


所以一个方法是否是线程安全的,判断的标准只有一个,  
这个方法是否访问了   可以写的“成员变量”。(所谓的state)  
注意,只有两个条件:  
(1)可以改变的   (2)成员变量  
如果访问了可以写的“成员变量”,那么这个方法不是   线程安全的。  
如果没有访问   可以写的“成员变量”,那么这个方法是   线程安全的。  

 

____________________________________________________________________

 

 

1、不要迷信你看到的所谓经典,当你认为它错误时,首先应该去怀疑它,而不是推翻你自己

错误的OO用法象天上的星星一样多。不要以为Java/DotNet这样的业界标准就一定是100%的榜样,Java/DotNet虽然号称以OO为己任,但是其中的API和OO用法问题也照样是连篇累牍。所以不要看到别人的程序这样用,或者书籍上这样用,就认为OO是这样用的。
举一个简单例子,我见过有人这样做:
 代码:


//DotNet程序,本类是DB数据操作层类的实现
public class DB
{
//获取所有用户的列表
public DataSet GetAllUsers();
//获取指定ID的用户
public User GetUserByID(string a_id);
...
}



这个类中没有一个数据成员,也就是楼主所谓的State。既然没有State,也搞这种实例化类来进行数据操作,明显是错误的OO用法。
用实例化类的最主要理由就是:实例化一个类是为了获得类对象的数据成员或者状态。
这个问题一直在某个CRM系统中使用了数年,后来被我重新培训之后,这伙人才认识到数年的惯例用法原来是错误的。正确的实现应该如下:
 代码:


public class DB
{
//获取所有用户的列表
public static DataSet GetAllUsers() { ... }
//获取指定ID的用户
public static User GetUserByID(string a_id) { ... }
...
}



上面的类改成静态方法才是正确的OO用法。

楼主看到的绝大多数情况应该都是类似这种错误的用法,用者自己往往还没有意识到,这不是OO的正确理解和用法。


2、不得不做的妥协。

还有一种用法,原本确实可以用静态成员或静态类来实现,但是出于复用性或者扩展性的需要,从而放弃静态类,转用实例化类。
在一些大型的、设计规范严谨的系统中,Java/DotNet中也能看到,往往可以看到这种类设计妥协的出现。

这种用法不是错误的用法,恰恰相反,是真正透彻理解了OO以后才能做到的用法。

举个例子。
假设要做一个数据库操作类,但是,根据系统扩展性和复用性的需要,要求这个数据库操作类必须能够适应SQLServer和DB2两种数据库。
如果用静态方法来实现,就只能这样:
 代码:


public class DB_AllInOne
{
//执行一段SQL
public static int ExecuteSQLs_MSSQL(string[] a_sqls) { ... }
public static int ExecuteSQLs_DB2(string[] a_sqls) { ... }
//执行一个存储过程
public static int ExecuteProcedure_MSSQL(string a_procedure, object[] a_args) { ... }
public static int ExecuteProcedure_DB2(string a_procedure, object[] a_args) { ... }
}



或者这样实现:

 代码:


public class DB_MSSQL
{
//执行一段SQL
public static int ExecuteSQLs(string[] a_sqls) { ... }
//执行一个存储过程
public static int ExecuteProcedure(string a_procedure, object[] a_args) { ... }
}

public class DB_DB2
{
//执行一段SQL
public static int ExecuteSQLs(string[] a_sqls) { ... }
//执行一个存储过程
public static int ExecuteProcedure(string a_procedure, object[] a_args) { ... }
}



上面的代码有问题,如果要使用这种静态方法的类,就必须将具体的实现类与代码死死绑定在一起。
例如下面的代码:

 代码:


...
switch(DBSYSTEM)
{
case MSSQL:
DB_MSSQL.ExecuteSQLs(new string[] { "select * from Table1 where Field1='%test%' " });
break;
case DB2:
DB_DB2.ExecuteSQLs(new string[] { "select * from Table1 where Field1='%test%' " });
break;
}
...



一旦象上面代码这样绑定了具体实现,那么系统就失去了复用性和扩展性。想想看,如果现在要求再兼容Oracle数据库,那么就不得不在所有执行数据库操作的地方都加入对Oracle的判断和执行,一个典型的中小型系统,至少有数以近百处的地方会调用数据库操作,那么就要修改这些地方,一旦遗漏或者疏忽了某些地方,就会产生严重的BUG问题。

所以,业界也认识到提高复用性和扩展性是多么重要的一个目标。

于是,在这种情况下,优秀的OO设计师和程序员会将原本的静态用法转变为实例用法,从而大大提高了系统的复用性和扩展性。
下面是修改以后的代码:
 代码:


//数据库操作的通用接口
public interface IDB
{
//执行一段SQL
int ExecuteSQLs(string[] a_sqls);
//执行一个存储过程
int ExecuteProcedure(string a_procedure, object[] a_args);
}

//MSSQL的接口实现
public class DB_MSSQL : IDB
{
//执行一段SQL
public int ExecuteSQLs(string[] a_sqls) { ... }
//执行一个存储过程
public int ExecuteProcedure(string a_procedure, object[] a_args) { ... }
}

//DB2的接口实现
public class DB_DB2 : IDB
{
//执行一段SQL
public int ExecuteSQLs(string[] a_sqls) { ... }
//执行一个存储过程
public int ExecuteProcedure(string a_procedure, object[] a_args) { ... }
}



除了去掉static和增加了一个接口以外,似乎没有太大的改变,但是从OO角度来看,可是翻天覆地的变化。
我们如何使用这种实例类?看如下例子:
 代码:


...
//只需在此处创建一次,即可永久使用
IDB db = null;
switch(DBSYSTEM)
{
case MSSQL:
db = new DB_MSSQL();
break;
case DB2:
db = new DB_DB2();
break;
//如果将来需要增加对其他数据库的支持,只需修改此处一段代码即可
}
...
//使用DB,看,只需要用接口实例即可,无需知道实现类是MSSQL还是DB2,这样就大大提高了系统复用性和扩展性
db.ExecuteSQLs(new string[] { "select * from Table1 where Field1='%test%' " });
...



而且,对于Java/DotNet现代语言来说,通过Reflection特性,甚至连一行代码都不用改就可以达到上面代码的目的,限于篇幅,具体实现方法就不写出了。

只修改一处代码与四处修改成百上千处代码相比,谁好谁坏?

不用静态类的原因只能有一条:为了满足系统复用性和扩展性需要。

### 使用静态工厂方法实例化对象 在 Java 中,静态工厂方法是一种常用的设计模式,用于创建对象而不暴露创建逻辑。这种方式使得客户端可以更方便地获取对象实例,而无需关心具体实现细节。 #### 创建静态工厂方法的类 定义一个包含多个静态工厂方法的类 `CarFactory` 来展示这一概念: ```java public class Car { private String model; // 私有构造函数防止外部直接实例化 private Car(String model) { this.model = model; } // 静态工厂方法之一:创建轿车模型 public static Car createSedan() { return new Car("Sedan"); } // 另一个静态工厂方法:创建跑车模型 public static Car createSportsCar() { return new Car("Sports Car"); } } ``` 此代码片段展示了两个静态工厂方法 `createSedan()` 和 `createSportsCar()`,这两个方法都返回了不同类型汽车的新实例[^1]。 #### 调用静态工厂方法 为了使用这些静态工厂方法来获得新的 `Car` 对象,可以在其他地方这样写: ```java // 不需要显式导入Car类因为已经在同一文件夹内 public class MainApp { public static void main(String[] args) { // 通过静态工厂方法创建对象而不是new关键字 Car myDailyDriver = Car.createSedan(); System.out.println("My daily driver is a " + myDailyDriver.getModel()); Car weekendFun = Car.createSportsCar(); System.out.println("For weekends, I drive a " + weekendFun.getModel()); // 输出结果应为: // My daily driver is a Sedan // For weekends, I drive a Sports Car } // 增加getter以便打印model名称 public String getModel(){ return this.model; } } ``` 这段程序说明了如何利用静态工厂方法简化对象创建过程,并且保持了封装性,即内部构造逻辑对外部不可见[^3]。 #### 关联到接口中的静态方法 自 Java 8 开始支持在接口中声明默认和静态方法。虽然这看起来似乎静态工厂方法有关联,但实际上两者服务于不同的目的。前者主要用于提供可选行为或工具性质的功能;后者则是面向对象编程的一种实践形式,旨在优化对象构建流程。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值