在做东西的时候发现在做到一个资源的方法处理的时候,想到了用单例来实现.但是实现的过程中我发现其实有些东西是可以用静态方法来做的.然后就带着这个疑问在网上搜集资料,下面就是几点心得.
首先的一个问题就是我把Java 的 静态类 和 C++ 的静态类当成了一个东西.就是说 任何的一个类都可以是静态的.而实际上在 JAVA中的静态类只能是内部类.而顶层类不能是static的,下面是关于java 类的一些说明:
- 顶级类
- 可以定义任何静态和非静态的成员;
- 顶级类不能是静态(static)的,若要加上其它关键字只能是public,final,abstract
- 抽象类(abstract)和接口(interface)不能是final的.
- 顶级类的静态方法中:可以直接访问该类的静态成员;访问非静态成员通过“new 顶级类().成员“的方法访问。访问静态内部类的成员通过“new 静态内部类().成员”的方法访问; 访问非静态内部类的成员通过“new 顶级类().new 非静态内部类().成员”方法访问.
- 顶级类的非静态方法中:可以直接访问顶级类中的所有静态和非静态成员;访问静态内部类的成员和非静态内部类的成员方法一样,通过“new 静态内部类().成员”、“new 非静态内部类().成员”访问
- 非静态内部类
- 非静态内部类中不允许定义静态的成员;
- 非静态内部类非静态方法,访问静态内部类成员的方法:
如下代码:
Public class C1 {
class A {
public void call() {
System.out.println(B.i); // 访问静态B类中的静态成员变量i
// new B()可以访问静态类中的所有静态和非静态成员
System.out.println(new B().k); // 访问静态B类中非静态成员变量k
System.out.println(new B().i); // 访问静态B类中的静态成员变量i
}
}
static class B {
static int i, j;
int k;
}
}
- 非静态内部类非静态方法中:可以直接访问该类的非静态成员、外部类内中的静态和非静态的成员;访问静态内部类和非静态内部类的成员的方法一样
- 访问静态内部类 new 静态内部类().成员
- 访问非静态内部类 new 非静态内部类().成员
- 静态类内部类
- 静态内部类中可以定义任何静态和非静态的成员;
- 静态内部类里的静态方法中:可以直接访问该类和外部类中的静态成员,访问该类和外部类中成员通过创建对象访问,访问方法有:
- 访问外部类的成员方法 new 外部类.成员;
- 访问外部类的非静态内部类成员的方法 new 外部类().new 非静态内部类().成员;
- 访问外部类的静态内部类成员的方法 new 静态内部类().成员;
- 静态内部类里的非静态方法中:可以直接访问该类中的所有的非静态、静态成员和直接访问外部类中的静态成员;访问外部类中成员通过创建类对象访问,访问方法有:
- 访问外部类的成员方法 new 外部类.成员;
- 访问外部类的非静态内部类成员的方法 new 外部类().new 非静态内部类().成员;
- 访问外部类的静态内部类成员的方法 new 静态内部类().成员;
注:以上所说的“成员”包括“成员方法”“成员变量”“成员对象”,所说的“成员”可能是静态或是非静态的,如果这个类里的成员是静态的可以直接用“静态类.静态成员“,“非静态类.静态成员“方法引用。
关于内部类的静态和非静态的理解我是这样认为的:
- 首先java 的一个顶层类是不能静态化的,就是说顶层类一定可能有多个实例存在.但是顶层类可以有静态方法.
- 内部类作为静态类和非静态类的访问权限和规则实际上和对象方法是一致的.总体的原则就是,一个静态方法只能访问静态的属性和方法,如果需要访问非静态的东西,那么一定是等于访问变量一样,在这个方法内首先创建一个对象然后访问. 这时候这个对象的生命期在这个函数内部.
- 关于2的原因很简单,一个非静态的函数或者属性的内容在,没有实例话的时候是不确定或者说不知的.所以不能直接像静态方法一样访问,必须要依托在一个类实例才可以访问.
- 因为静态的内部类,可以看做一个静态的方法来看,所以静态的内部类实际上是可以有多个实例的,而非理解上的只能有一个内存实例. 通过如下代码可以验证:
package example;
public class Demo {
private static class Deom1 {
static int i = 10;
int j = 0;
public Deom1(int j) {
this.j = j;
}
void print() {
System.out.println(i);
System.out.println(j);
}
void add() {
i++;
}
}
public static void main(String[] args) {
Deom1 a = new Deom1(1);
Deom1 b = new Deom1(2);
a.print();
System.out.println(a.hashCode());
System.out.println(b.hashCode());
a.add();
b.print();
}
}
- 非静态内部类可以看做一个非静态的方法,所以只能在非静态的方法中使用.而要在静态方法中使用,必须要先创建一个对象实例.
下面的主题就是关于使用静态类 和 Singleton 的问题了.首先看下他们各自的好处,这里因为java 中的顶层类不能静态话.所以说我们要使用的话,只能使用静态方法而不是静态类.所以这里对静态的内部类来说是肯定不可行的方法,不做多于的讨论.
- Singleton 的好处:
- 单例可以继承类,实现接口,而静态类不能(可以集成类,但不能集成实例成员)
- 单例可以被延迟初始化,静态类一般在第一次加载是初始化
- 单例类可以被集成,他的方法可以被重写
- 或许最重要的是,单例类可以被用于多态而无需强迫用户只假定唯一的实例。举个例子,你可能在开始时只写一个配置,但是以后你可能需要支持超过一个配置集,或者可能需要允许用户从外部从外部文件中加载一个配置对象,或者编写自己的。你的代码不需要关注全局的状态,因此你的代码会更加灵活。
- 静态发方法的好处
- 静态方法中产生的对象,会随着静态方法执行完毕而释放掉,而且执行类中的静态方法时,不会实例化静态方法所在的类。如果是用singleton, 产生的那一个唯一的实例,会一直在内存中,不会被GC清除的(原因是静态的属性变量不会被GC清除),除非整个JVM退出了。
在静态方法和singleton 中有几个要解决的不同的地方:
- 对象创建的是否迟话,比如在处理一些初始化信息的时候.比如配置文件可能我们我们需要在启动系统的时候就被装载了.而静态属性(配合静态方法使用的)确实是在开始就装载了的.但是singleton也是可以做到的.
单例分为的 Eager型和 Lazy型
Eager 型是这样的
public class Demo {
private Demo demo = new Demo();
private Demo getInstance(){
return demo;
}
}
而lazy型是这样的
public class Demo {
private Demo demo ;
private Demo getInstance(){
if(demo == null)
demo = new Demo();
return demo;
}
}
Eager 是加载内存的时候就首先把类实例化了,这样的单例模式在多线程情况下和一些不变类的处理方面比较常用.他们就是在一开始就装载了信息的,但是可能这个实例永远也用不到就浪费了.而lazy是迟钝的实例化, 也就是只有在需要的时候才会实例出来的.但是这个地方就存在多线程的问题.如果两个线程几乎同时的用到了这个东西,那么就会出现问题.
- 状态属性. Singleton的重要意义,在于全局中对Singleton实例的所有引用指向同一个对象实例。这样作的最大的好处,在于可以利用SingleTon保持一个全局一致的对象实例。和直接使用static方法相比,Singleton的对象实例是可以有状态的,例如该对象被多少次引用到等等,利用static方法+static变量同样可以做到这一点。唯一的好处,是看起来代码更加整齐一些,相关的static方法和static变量被包装在一个Singleton对象中。
- 资源开销,如果全部用静态方法的话,很多需要处理的对象在方法结束以后会被释放.而用singleton处理的对象,并不会施放了.这里有个问题就是静态类的处理,因为没有绝对的静态类,所以我们只能用 AA.xx()方法来调用.而如果AA 本身不是C++ 的那种静态的,那么我们就必须要初始化AA本身带来一些开销.而singleton这个对象则会一直存在在内存中间.
所以在综合考虑一些因素以后我的观点是.
- 当你并不需要一个唯一的实例,或者说你需要或者不需要一个实例的并不是问题的时候.就是如果一个类没有状态或者属性.只是一个简单的功能,比如一些工具类或者一些工厂方法.那么我们不用制造成singleton这么复杂的描述.只用一个简单的静态方法就可以了.比如:
isLager(int a ,int b){
return a>b;
}
这样的类实际不存在状态和属性,所有的东西都是用参数传入的.方法本身只是做一些简单的操作,就是一个无状态的类,用singleton就显得有点过头了.
- 当一个类是有状态的.需要对里面的一些东西做处理的时候,并且在全局需要保存一个唯一的实例.而不是像1一样并不需要一个实例的时候.比如对一些资源做了缓存,并且也会对资源做一些改变的时候,用静态方法和静态属性确实可以实现,但是这时候用singleton似乎更合适.
- 在很多的情况下,singleton和静态方法都可以完成一个相同的功能,他们之间存在一些重叠.用那个都能完成功能上的需求,但是我还是觉得singleton更合适,毕竟他更符合对象的概念,而静态方法似乎更合适于C的时代.