@用实例化类的最主要理由就是:
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特性,甚至连一行代码都不用改就可以达到上面代码的目的,限于篇幅,具体实现方法就不写出了。
只修改一处代码与四处修改成百上千处代码相比,谁好谁坏?
不用静态类的原因只能有一条:为了满足系统复用性和扩展性需要。