先要澄清和区别一些概念,“静态类”和“所有方法皆为静态方法的类”。

严格说来,Java中的静态类,指的是“static class”这样修饰的类定义,语法上的要求,使得这样的类一定是内部类,换言之,“静态内部类”是对它的完整定义。静态内部类最大的好处在于可以隐藏自己(只让自己被所在外层的类用到),同时又可以访问到所在外层类的属性。和“非静态”的内部类相比,它可以放置一些静态的成员变量和方法定义,而非静态类不可以;而且,静态内部类不能访问外层类非静态的属性。

但是,通常我们所说的“静态类”,也是下文所述的“静态类”,是指所有的方法、属性都是静态的类,同时,我们在使用它们的时候,直接调用它们的静态方法、访问其中的静态属性,而不需要对其实例化。这类所谓的“静态类”往往具备这样两个特点,一个是使用final修饰,它们往往没有子类;其二是构造器都被私有化了,不允许被构造实例。

1、单例模式便于mock,可测性好。虽说静态方法也可以mock (比如需要使用一些特殊的注解),但是毕竟相对还是麻烦一些,也没有那么灵活。

2、有人说单例模式可以做到lazy load,但是静态类不行。这肯定是扯淡,静态类也完全可以做到第一次使用的时候再加载。不过,其中值得一提的是单例中的Double Check Lock,这里做一个简单介绍。看下面的代码:

  1. public Singleton {
  2. private static Singleton instance;
  3. private Number n = new Number();
  4. public Number get() {
  5. return this.n; //(1)
  6. }
  7. public static Singleton getInstance() {
  8. if(instance == null) {
  9. synchronized(Singleton.class) {
  10. if(instance == null)
  11. instance = new Singleton();
  12. } //(2)
  13. }
  14. return instance;
  15. }
  16. }

是很常见的一种写法,不过,由于编译器的优化,允许出现主存和线程工作内存数据不一致问题,这就是“DCL失效”的问题。上面的代码是其典型表现:

根据JMM规范,主存数据和工作内存的数据是允许存在不一致的。JDK1.2之后,分配空间、对象初始化等等操作才都放到工作内存中进行了。由于synchronized关键字的关系,执行到语句(2)的时候,走出同步块时,JVM会将主存和工作内存的instance引用的对象刷新到一致,即instance是“可见”的。但问题出在上面的(1),没有synchronized,也没有volatile、final,没有人来保证调用get方法时获得的n是正确的值,即这个n未必是“可见”的。如果n比instance晚同步到主存,就存在一个时间间隙,这个间隙内获取到的instance是一个不健康的instance,其中的this.n是取不到正确的Number对象的。

在JSR133中,对JMM做了一个修正,后引入或增强了synchronized、volatile和final关键字,通过它们的运用,上述问题能够得到解决。另外,还有一种解决的方法可以是使用静态的内部类:

private static class InnerInstance { 
   public static Instance instance = new Instance(); 
public static Instance getInstance() { 
   return InnerInstance.instance; 
}

3、单例可以被继承,这是一个很大的好处,这便于用户overwrite其中的某方法,当然,继承单例的场景较少见;而静态类一般不被继承。关于单例的继承细节,这里暂不讨论,有几种办法,有兴趣的同学可以自行阅读JDK的Calendar类。

4、单例可以实现自某接口,可以继承自某类。静态类也可以继承自某类,但是就没法使用父类里面的protect成员了。推广来说,这一点和上一点都可以看做是面向对象带来的好处:封装、继承和多态,静态类不能很好地具备其中的后两点。

5、单例可以比较方便地扩展为有限实例。根据需要,我可以通过工厂,生产出两个内部状态不同的单例对象——这在静态类中是难以做到的。Spring可以看做一系列大工厂,但其中的bean也只有singleton和prototype两种,生产不出static的新类型;当你的工具成为了对象,就能够保持良好的扩展性。

还有一个有趣的例子是JDK的Calendar.getInstance()方法,从方法看很像是获得一个单例,其实不是,每次都去创建了新的Calendar对象;同时,使用abstract修饰它自己,保证了无法使用new实例化,又开放了getInstance这样一个接口来获取默认实现,而获取的默认实现,又恰恰是Calendar的子类。这种形式可以看做是单例的一个变体。

6、有人说,单例在使用过程中申请的资源可以被及时释放并回收内存,但是静态类不行。这也是没有道理的,别忘了静态类也是可以存放状态的,在确定不再使用资源后,及时将资源的引用置为null就可以了。

7、如果希望在类加载的时候做复杂的操作,那么在静态类中,需要引入static块来初始化数据,如果期间抛出了异常,就可能发生一个“ClassDefNotFoundError”的诡异错误,这对问题定位是不利的。

转自:http://www.raychase.net/257

Java静态类的更多相关文章

  1. java静态类与非静态类区别

    java静态与非静态区别   这里的静态,指以static关键字修饰的,包括类,方法,块,字段. 非静态,指没有用static 修饰的. 静态有一些特点: 1.全局唯一,任何一次的修改都是全局性的影响 ...

  2. Java 静态类 static

    静态的方法是非虚方法(Java中的非虚方法有private,final,static,构造器,非虚方法无需根据具体的对象遍历方法区的方法表,决定调用关系) 也就是说,对于静态类型方法的调用,是其声明类 ...

  3. java 静态类与静态方法应用场景

    静态类:工具类 例如 Array.sort(arry) 静态方法:设置文件名

  4. java静态类、静态方法、静态代码块,静态变量及实例方法,实例变量初始化顺序及内存管理,机制

    1.当一个类被第一次使用时,它需要被类加载器加载,而加载过程涉及以下两点: (1)在加载一个类时,如果它的父类还未被加载,那么其父类必须先被加载: (2)当类加载到内存之后,按照在代码中的出现顺序执行 ...

  5. Java学习笔记(七):内部类、静态类和泛型

    内部类 在Java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类.广泛意义上的内部类一般来说包括这四种:成员内部类.局部内部类.匿名内部类和静态内部类.下面就先来了解一下这四种 ...

  6. Java关键字--static

    在Java中,将关键字static分为三部分进行讨论,分别为Java静态变量.Java静态方法.Java静态类 Java Static Variables Java instance variable ...

  7. java 小记

    1.获取web项目根目录的绝对路径 request.getContextPath()  获取项目名称,如    /BiYeSheJi getServletContext().getRealPath(& ...

  8. 实战系列之 Node.js 玩转 Java

    这些年以来,Node.js的兴起,JavaScript已经从当年的“世界最被误解的语言”变成了“世界最流行的语言”.且其发展之势,从语言本身的进化,库和包的增长,工具支持的完善,star项目和领域解决 ...

  9. Java 基础 内部类

    Java 基础 内部类 内部类(嵌套类) nested class 目的为外围类enclosing class提供服务. 四种: 静态成员类 static member class 非静态成员类 no ...

随机推荐

  1. GUI编程笔记(java)07:GUI把文本框的值移到文本域案例

    1.首先我们了解一下我们的需求,如下: 输入“风清扬”,点击“数据转移”,这样的文本会出现到下面的文本域中,这就是我们的需求. 2.代码如下: package cn.itcast_05; import ...

  2. CSS画三角形引发的一些思考

      今天刷知乎时看到了一个问题,有谁能详细讲一下css如何画出一个三角形?怎么想都想不懂? - 知乎.很巧,刚入前端坑的我前不久也遇到过这个问题,今天再来谈一谈这个问题则是因为知乎的一些答案引发了我的 ...

  3. jquery 提示信息显示后自动消失的具体实现

    方法一: 复制代码 代码如下: $("#errormsg").html("您的信息输入错误,请重试!").show(300).delay(3000).hide( ...

  4. Java SE (1)之 JFrame 组件 FlowLayout 布局

    package com.sunzhiyan; import java.awt.*; import java.awt.event.*; import javax.swing.*; public clas ...

  5. 使用linq获得当前文件夹下的下一级满足条件的文件夹

    使用linq获得当前文件夹下的下一级满足条件的文件夹.             SPFolderCollection subAlbums = Folder.SubFolders;            ...

  6. xml处理相关文章收藏

    XPath语法 在C#中使用XPath示例:http://blog.csdn.net/yukaizhao/article/details/6630613 .Net那点事儿系列:C#操作Xml:通过Xm ...

  7. PhpStorm 注册码

    JetBrains PhpStorm key PhpStorm注册码 User Name :  EMBRACE  License Key : License Key : ===== LICENSE B ...

  8. requirejs 笔记

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http ...

  9. tupian

     http://www.iconfont.cn/https://icons8.com/http://ico.58pic.com/http://www.easyicon.net/ 

  10. css sprites精灵技术:Html将所有图片放在一张图片上

    使用最近做的某项目常见页面作为联系素材: 分析:1.切图:步骤条可以切成四种图,即黄灰.红黄.红.灰. 2.html:需要五个li标签,每个包含一个图片及文字. 将要取得图片放到同一张图片上,从左到右 ...