静态类和单例模式之间的区别?

时间:2009-02-06 08:13:23

标签: design-patterns static singleton

静态类和单例模式之间存在什么真实(即实际)差异?

两者都可以在没有实例化的情况下调用,两者都只提供一个“实例”,它们都不是线程安全的。还有其他区别吗?

40 个答案:

答案 0 :(得分:1149)

是什么让你说单例或静态方法不是线程安全的?通常两个应该实现为线程安全的。

单例和一堆静态方法之间的最大区别在于单例可以实现接口(或者从有用的基类派生,尽管根据我的经验这不太常见),所以你可以传递单例,就好像它是“只是另一个”实施。

答案 1 :(得分:439)

真正的答案是Jon Skeet,on another forum here

  

单身人士可以访问单人   创建实例 - 该实例(或   相反,对该实例的引用)   可以作为参数传递给其他人   方法,并视为正常   对象

     

静态类只允许静态   方法

答案 2 :(得分:342)

  1. Singleton对象存储在中,但静态对象存储在堆栈中。
  2. 我们可以克隆(如果设计者没有禁止它)单例对象,但我们无法克隆静态类对象
  3. Singleton类遵循 OOP (面向对象的原则),静态类不遵循。
  4. 我们可以使用Singleton类实现interface,但是类的静态方法(或者例如C#static class)不能。

答案 3 :(得分:136)

Singleton模式比静态类有几个优点。首先,单例可以扩展类和实现接口,而静态类不能(它可以扩展类,但不会继承它们的实例成员)。单例可以懒惰或异步初始化,而静态类通常在首次加载时初始化,从而导致潜在的类加载器问题。然而,最重要的优点是单例可以多态处理,而不会强迫用户假设只有一个实例。

答案 4 :(得分:62)

static类不适用于任何需要状态的类。将一堆函数放在一起很有用,例如Math(或Utils在项目中。因此,类名只是为我们提供了一个线索,我们可以在其中找到函数而已。

Singleton是我最喜欢的模式,我用它来管理单点内容。它比static类更灵活,可以维持它的状态。它可以实现接口,从其他类继承并允许继承。

我在staticsingleton之间选择的规则:

如果有一堆函数应该保存在一起,那么static就是选择。 其他需要单独访问某些资源的内容可以实现为singleton

答案 5 :(得分:54)

静态类: -

  1. 您无法创建静态类的实例。

  2. 当加载包含该类的程序或命名空间时,由.NET Framework公共语言运行库(CLR)自动加载。

  3. 静态类不能有构造函数。

  4. 我们无法将静态类传递给方法。

  5. 我们不能将静态类继承到C#中的另一个静态类。

  6. 具有所有静态方法的类。

  7. 更好的性能(静态方法在编译时绑定)

  8. <强>的Singleton: -

    1. 您可以创建一个对象实例并重复使用它。

    2. 在用户请求时首次创建Singleton实例。

    3. Singleton类可以有构造函数。

    4. 您可以创建singleton类的对象并将其传递给方法。

    5. Singleton类没有说任何继承限制。

    6. 我们可以处理单例类的对象,但不能处理静态类的对象。

    7. 可以覆盖方法。

    8. 可以在需要时加载延迟(始终加载静态类)。

    9. 我们可以实现接口(静态类不能实现接口)。

答案 6 :(得分:49)

静态类是仅具有静态方法的类,对于该类,更好的单词将是“函数”。静态类中体现的设计风格纯粹是程序性的。

另一方面,单身人士是一种特定于OO设计的模式。它是一个对象的实例(具有其中的所有可能性,例如多态性),具有创建过程,该过程确保在其整个生命周期中只有该特定角色的一个实例。

答案 7 :(得分:33)

在单例模式中,您可以将单例创建为派生类型的实例,但不能使用静态类。

快速示例:

if( useD3D )
    IRenderer::instance = new D3DRenderer
else
    IRenderer::instance = new OpenGLRenderer

答案 8 :(得分:24)

展开Jon Skeet's Answer

  

单例和一堆静态方法之间的最大区别在于单例可以实现接口(或者从有用的基类派生,虽然这不太常见的IME),所以你可以传递单例,好像它只是“另一个” “实施。

单人测试课程时,单身人士更容易使用。无论您将单例作为参数(构造函数,设置器或方法)传递,您都可以替换单例的模拟或存根版本。

答案 9 :(得分:21)

单例的另一个优点是它可以很容易地序列化,如果你需要将其状态保存到光盘或者远程发送到某个地方,这可能是必要的。

答案 10 :(得分:19)

这是一篇好文章: http://javarevisited.blogspot.com.au/2013/03/difference-between-singleton-pattern-vs-static-class-java.html

静态类

  • 包含所有静态方法的课程。
  • 更好的性能(静态方法在编译时绑定)
  • 不能覆盖方法,但可以使用方法隐藏。 (What is method hiding in Java? Even the JavaDoc explanation is confusing

    public class Animal {
        public static void foo() {
            System.out.println("Animal");
        }
    }
    
    public class Cat extends Animal {
        public static void foo() {  // hides Animal.foo()
            System.out.println("Cat");
        }
    }
    

的Singleton

总之,我只使用静态类来保存util方法,并将Singleton用于其他所有方法。


<强>编辑

答案 11 :(得分:17)

我不是一个伟大的OO理论家,但据我所知,我认为静态类与Singletons相比缺乏的唯一OO特性是多态性。 但是如果你不需要它,使用静态类,你当然可以继承(不确定接口实现)和数据和函数封装。

Morendil的评论,“静态类中体现的设计风格纯粹是程序性的”我可能错了,但我不同意。 在静态方法中,您可以访问静态成员,这与访问其单个实例成员的单例方法完全相同。

编辑:
我现在实际上在想另一个区别是静态类在程序启动时实例化并且在程序的整个生命周期中存在,而单例在某个时刻被显式实例化并且可以是也被毁了。

*或者它可能会在第一次使用时被实例化,具体取决于语言,我认为。

答案 12 :(得分:15)

为了说明Jon的观点,如果Logger是静态类,则无法完成下面显示的内容。类SomeClass期望将ILogger实现的实例传递到其构造函数中。

Singleton类对依赖注入很重要。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {

            var someClass = new SomeClass(Logger.GetLogger());
        }


    }

    public class SomeClass 
    {
        public SomeClass(ILogger MyLogger)
        {

        }
    }

    public class Logger : ILogger
    {
        private static Logger _logger;
        private Logger() { }

        public static Logger GetLogger()
        {
            if (_logger==null)
            {
                _logger = new Logger();
            }

            return _logger;
        }

        public void Log()
        {

        }

    }


    public interface ILogger
    {
         void Log();
    }
}

答案 13 :(得分:11)

单个单例只是一个正常的类,它被实例化,但只是一次,间接来自客户端代码。静态类未实例化。 据我所知,静态方法(静态类必须有静态方法)比非静态方法快。

编辑:
FxCop性能规则说明: “不访问实例数据或调用实例方法的方法可以标记为静态(在VB中共享)。执行此操作后,编译器将向这些成员发出非虚拟调用站点,这将阻止在运行时检查每个调用确保当前对象指针不为空。这可以为性能敏感的代码带来可测量的性能提升。在某些情况下,访问当前对象实例的失败代表了正确性问题。“
我实际上并不知道这是否也适用于静态类中的静态方法。

答案 14 :(得分:9)

Singleton是实例化的,它只有一个实例被实例化,因此Singleton中的单个

静态类不能由其他任何东西实例化。

答案 15 :(得分:6)

与静态类的区别

JDK具有单例和静态的示例,一方面java.lang.Math是具有静态方法的最终类,另一方面java.lang.Runtime是单例类。

单例的优势

  • 如果需要维护状态而不是单例模式比静态类更好,这是因为在静态类中维护状态会导致错误,尤其是在并发环境中,这可能导致竞态条件,而没有足够的同步并行修改多个线程。

  • Singleton类如果是沉重的对象,则可以延迟加载,但静态类没有这种优势,并且总是急于加载。

  • 使用单例,您可以使用继承和多态性来扩展基类,实现接口并提供不同的实现。

  • 由于Java中的静态方法不能被覆盖,因此会导致不灵活。另一方面,可以通过扩展它来覆盖单例类中定义的方法。

静态类的缺点

  • 为单例编写单元测试比静态类要容易,因为您可以在需要单例时传递模拟对象。

静态类的优点

  • 静态类比单例提供更好的性能,因为静态方法是在编译时绑定的。

单例模式的几种实现各有优缺点。

  • 急于加载单例
  • 双重检查锁定单例
  • 按需初始化持有人习惯用法
  • 基于枚举的单例

每个人的详细说明都太冗长,因此我只链接了一篇好文章-All you want to know about Singleton

答案 16 :(得分:6)

主要区别是:

  • Singleton有一个实例/对象,而静态类是一堆 静态方法
  • Singleton可以扩展,例如通过静态接口 上课不能。
  • 可以继承Singleton,它支持开放/关闭原则 另一方面,静态类的SOLID原则不能被继承 我们需要自己做出改变。
  • Singleton对象可以传递给方法,而静态类也可以传递给方法 没有实例不能作为参数传递

答案 17 :(得分:6)

我同意这个定义:

  

单词“”表示整个应用程序生命周期中的单个对象   循环,因此范围在应用程序级别。

     

静态没有   任何Object指针,因此范围是App Domain级别。

     

此外,两者都应该实现为线程安全的。

您可以找到有关其他有趣的差异:Singleton Pattern Versus Static Class

答案 18 :(得分:6)

从测试的角度来看,Singleton是更好的方法。 与静态类不同,单例可以实现接口,您可以使用模拟实例并注入它们。

在下面的例子中,我将说明这一点。 假设您有一个方法isGoodPrice(),它使用方法getPrice()并将getPrice()实现为单例中的方法。

提供getPrice功能的单例:

public class SupportedVersionSingelton {

    private static ICalculator instance = null;

    private SupportedVersionSingelton(){

    }

    public static ICalculator getInstance(){
        if(instance == null){
            instance = new SupportedVersionSingelton();
        }

        return instance;
    }

    @Override
    public int getPrice() {
        // calculate price logic here
        return 0;
    }
}

使用getPrice:

public class Advisor {

    public boolean isGoodDeal(){

        boolean isGoodDeal = false;
        ICalculator supportedVersion = SupportedVersionSingelton.getInstance();
        int price = supportedVersion.getPrice();

        // logic to determine if price is a good deal.
        if(price < 5){
            isGoodDeal = true;
        }

        return isGoodDeal;
    }
}


In case you would like to test the method isGoodPrice , with mocking the getPrice() method you could do it by:
Make your singleton implement an interface and inject it. 



  public interface ICalculator {
        int getPrice();
    }

最终单身人士实施:

public class SupportedVersionSingelton implements ICalculator {

    private static ICalculator instance = null;

    private SupportedVersionSingelton(){

    }

    public static ICalculator getInstance(){
        if(instance == null){
            instance = new SupportedVersionSingelton();
        }

        return instance;
    }

    @Override
    public int getPrice() {
        return 0;
    }

    // for testing purpose
    public static void setInstance(ICalculator mockObject){
        if(instance != null ){
instance = mockObject;
    }

测试类:

public class TestCalculation {

    class SupportedVersionDouble implements ICalculator{
        @Override
        public int getPrice() { 
            return 1;
        }   
    }
    @Before
    public void setUp() throws Exception {
        ICalculator supportedVersionDouble = new SupportedVersionDouble();
        SupportedVersionSingelton.setInstance(supportedVersionDouble);

    }

    @Test
    public void test() {
          Advisor advidor = new Advisor();
          boolean isGoodDeal = advidor.isGoodDeal();
          Assert.assertEquals(isGoodDeal, true);

    }

}

如果我们采用静态方法来实现getPrice()的替代方法,那么模拟getPrice()很难。 您可以使用power mock来模拟静态,但并非所有产品都可以使用它。

答案 19 :(得分:5)

一个值得注意的差异是Singletons带来了不同的实例化。

对于静态类,它由CLR创建,我们无法控制它。 对于单例,对象在尝试访问的第一个实例上实例化。

答案 20 :(得分:4)

在许多情况下,这两者没有实际区别,特别是如果单例实例从未改变或变化非常缓慢,例如持有配置。

我认为最大的区别是单例仍然是普通的Java Bean,而不是专门的静态Java类。因此,在更多情况下接受单身人士;它实际上是Spring Framework的默认实例化策略。消费者可能知道也可能不知道它是一个传递的单例,它只是像普通的Java bean一样对待它。如果需求发生变化并且单例需要成为原型,正如我们在Spring中经常看到的那样,它可以完全无缝地完成,而不需要对消费者进行一行代码更改。

其他人之前已经提到静态类应该纯粹是程序性的,例如: java.lang.Math中。在我看来,这样的类永远不应该被传递,除了静态final之外,它们永远不应该作为属性。对于其他一切,使用单例,因为它更灵活,更易于维护。

答案 21 :(得分:4)

我们的数据库框架与后端建立连接。为避免对多个用户进行脏读,我们使用单例模式确保在任何时间点都有单个实例。

在c#中,静态类无法实现接口。当单个实例类需要为业务合同或IoC目的实现接口时,这就是我使用没有静态类的Singleton模式的地方

Singleton提供了一种在无状态场景中维护状态的方法

希望能帮到你..

答案 22 :(得分:3)

  1. 我们可以创建单例类的对象并将其传递给方法。

  2. Singleton类没有任何继承限制。

  3. 我们不能处理静态类的对象,但可以单独使用类。

答案 23 :(得分:3)

我阅读以下内容并认为它也有意义:

  

照顾生意

     

请记住,最重要的OO规则之一是对象对自己负责。这意味着关于类的生命周期的问题应该在类中处理,而不是委托给像static这样的语言结构。等等。

来自“面向对象的思想过程”第4版。

答案 24 :(得分:3)

一个。序列化 - 静态成员属于该类,因此无法序列化。

湾虽然我们已经将构造函数设为私有,但静态成员变量仍然会被带到子类。

℃。我们不能进行延迟初始化,因为只有在加载类时才会加载所有内容。

答案 25 :(得分:3)

从客户端的角度来看,客户端已知静态行为,但可以从客户端隐藏Singleton行为。客户可能永远不会知道他一次又一次地玩弄一个单一的实例。

答案 26 :(得分:3)

在我写的一篇文章中,我描述了我对单身人士为什么比静态类好得多的观点:

  1. 静态类实际上不是规范类 - 它是具有函数和变量的命名空间
  2. 由于违反面向对象的编程原则,使用静态类不是一个好习惯
  3. 静态类不能作为其他
  4. 的参数传递
  5. 静态类不适合“延迟”初始化
  6. 始终难以跟踪静态类的初始化和使用
  7. 实施线程管理很难

答案 27 :(得分:3)

  1. 延迟加载
  2. 支持接口,以便可以提供单独的实现
  3. 能够返回派生类型(作为延迟加载和接口实现的组合)

答案 28 :(得分:2)

单个静态类实例(即,类的单个实例,恰好是静态或全局变量)与单个静态指针之间存在巨大差异到堆上类的实例:

当您的应用程序退出时,将调用静态类实例的析构函数。这意味着如果您将该静态实例用作单例,则您的单例停止正常工作。如果仍然运行使用该单例的代码,例如在不同的线程中,该代码可能会崩溃。

答案 29 :(得分:2)

  • Singleton类在应用程序生命周期中提供一个对象(仅一个实例),例如java.lang.Runtime

    虽然Static类仅提供java.lang.Math之类的静态方法

  • Java中的静态方法不能被覆盖,但是可以通过扩展它来覆盖Singleton类中定义的方法。

  • Singleton类具有继承和多态性的功能,可以扩展基类,实现接口并提供不同的实现。而静态不是。

例如:java.lang.Runtime是Java中的Singleton类,对getRuntime()方法的调用返回与当前Java应用程序关联的运行时对象,但确保每个JVM仅一个实例。

答案 30 :(得分:1)

正如我理解静态类和非静态Singleton类之间的区别,静态只是C#中的非实例化“类型”,其中Singleton是一个真正的“对象”。换句话说,静态类中的所有静态成员都被分配给该类型,但是在Singleton中被置于该对象下。但请记住,静态类仍然表现得像引用类型,因为它不是像Struct这样的值类型。

这意味着当你创建一个Singleton时,因为类本身不是静态的但是它的成员是,优点是Singleton中的静态成员引用它本身连接到一个实际的“对象”而不是一个空洞的“类型”本身。现在澄清了静态和非静态单例之间的区别,超出了它的其他特性和内存使用,这对我来说很困惑。

两者都使用静态成员,它们是成员的单个副本,但是Singleton将引用的成员包装在一个真实例化的“对象”周围,除了静态成员之外还存在地址。该对象本身具有属性,其中in可以传递并引用,增加值。 Static类只是一个类型,因此除了指向其静态成员之外它不存在。这种概念巩固了Singleton vs Static Class的目的,超越了继承和其他问题。

答案 31 :(得分:1)

我的不同之处在于实现面向对象编程(Singleton / Prototype)或函数编程(Static)。

当我们应该关注的是最终我们持有一个对象时,我们过于关注单例模式创建的对象的数量。像其他人已经说过的那样,它可以扩展,作为参数传递,但最重要的是它是状态满的。

另一方面,static用于实现函数式编程。静态成员属于一个类。他们是无国籍的。

顺便提一下,你知道你可以创建单例静态类:)

答案 32 :(得分:1)

当我想要具有完整功能的课程时,例如有很多方法和变量,我使用单例;

如果我希望课程中只有一种或两种方法,例如MailService类,它只有一个方法SendMail()我使用静态类和方法。

答案 33 :(得分:1)

Java中的静态类只有静态方法。它是一个容器 功能。它是基于程序编程设计创建的。

Singleton类是面向对象设计中的模式。单身人士 class在JVM中只有一个对象实例。这种模式是 以这样的方式实现,即始终只有一个实例 该类出现在JVM中。

答案 34 :(得分:1)

单例仅是类上的一次写入静态变量,该类一旦初始化便始终引用自身的相同实例。

因此,您不能“使用单例而不是静态变量”,也不能避免通过使用单例将状态保持在静态变量中。

单例的优点仅在于:即使其他代码试图将其初始化一千次,它也不会被重新初始化。这对于诸如网络处理程序之类的东西非常有用,您知道,如果在等待响应的过程中实例被另一个实例替换,它将很烂。

除非您想要整个应用程序都没有任何实例-都是静态的!-那么单例对于这些情况我们就可以说,因为我们不能依靠缺乏人为错误,因为这是唯一保证某些事情不会被覆盖的情况。

但是请注意,单身人士并不能保证国家无处不在。您的网络处理程序本身可能还依赖于其他单例,依此类推。现在,我们的状态生活在许多单例中……很棒。

并且没有编译器可以确保在编译时单例是您所有状态或其他任何此类思想所处的位置。在具有单例的类上可以有一百个静态变量。并且单例可以访问静态变量。而且编译器不会在意。

因此,我会提醒任何人不要以为使用单身人士可以保证任何有关国家居住地的信息。 它唯一的保证就是,它永远是该类的唯一实例。这也是它唯一的优势。

其他答案认为单例的其他优势是编译器无法保证的,并且可能因语言而异。依赖注入是一种可能依赖单例的补充模式,尽管它可能是也可能不是给定语言中的最佳解决方案或唯一解决方案。在缺少泛型或对调用静态访问器和函数施加任意限制的语言中,采用单例模式可能确实是解决给定问题的最佳方法。

使用诸如Swift这样的语言来获取依赖注入,可测试的代码,状态良好的管理,线程安全的访问器,多态等,根本不需要使用单身。但是,它对于确保单个实例仍然很有用。

回顾一下:单例只不过是一个静态变量,它可以防止存在给定类的多个实例,并且可以防止单个实例被新实例覆盖。就是这样,句号,句号。

答案 35 :(得分:0)

带有静态类的示例

public class Any {

    private static Any instance = new Any();

    private Singleton() {
        System.out.println("creating");
    }
}
具有单例模式的

仅存在一个实例:

public class Singleton {

    private static Singleton instance = new Singleton();

    private Singleton() {
        System.out.println("creating");
        if (instance != null) {
            throw new RuntimeException("Imposible create a new instance ");
        }
    }
}

答案 36 :(得分:0)

静态类通常用于库,如果我只需要特定类的一个实例,则使用单例。 从内存的角度来看,有一些区别:通常在堆中只分配对象,分配的唯一方法是当前正在运行的方法。静态类的所有方法也都是静态的,并且从一开始就在堆中,因此通常静态类会消耗更多内存。

答案 37 :(得分:-1)

Singleton的一个主要优点:多态性 例如:使用类工厂创建实例(基于某些配置说),我们希望这个对象真的是单例。

答案 38 :(得分:-2)

  

两者都可以在没有实例化的情况下调用,两者都只提供一个&#34;实例&#34;它们都不是线程安全的。还有其他区别吗?

这个问题都是错误的。 请注意:这里的静态类意味着嵌套的静态类而不是只有静态方法的类。

我假设(即静态类意味着嵌套的静态类,而不是只有静态成员的类)所以因为如果我看到最流行的Singleton实现,即DCL方式,它只是静态声明实例和静态方法获取Singleton实例。它的一个实现。所以在这种情况下,Singleton和只有静态成员的类之间的区别是什么。虽然其他实现可能像使用Enum。

让我纠正这些陈述:

  1. Singleton类可以在应用程序范围内使用单实例。嵌套的静态类可以有多个实例(请参阅下面的代码作为证明)。阅读嵌套类here的基础知识。

  2. 没有类本质上是线程安全的,必须以编程方式使其成为线程安全的。它既可以用于嵌套的静态类,也可以用于Singleton。

  3. 下面还有一些神话破坏者(这个问题的大多数答案已经给出了这些陈述,因此认为以编程方式证明它是好的):

    1. 嵌套静态类可以像任何其他类一样实现接口。
    2. 嵌套的静态类可以扩展其他非final类。
    3. 嵌套静态类可以包含实例变量。
    4. 嵌套的静态类可以有参数化的构造函数。
    5. 在下面的代码中,您可以看到嵌套的静态类 NestedStaticClass 实现接口,扩展另一个类,具有实例变量和参数化构造函数。

       package com.demo.core;
      
          public class NestedStaticClassTest
          {
              public static void main(String[] args)
              {
                  OuterClass.NestedStaticClass obj1 = new OuterClass.NestedStaticClass();
                  OuterClass.NestedStaticClass obj2 = new OuterClass.NestedStaticClass();
      
                  if(obj1 == obj2)
                  {
                      System.out.println("Both nested static objects are equal....");
                  }
                  else
                  {
                      System.out.println("NOT EQUAL......");
                  }
      
                  System.out.println(OuterClass.NestedStaticClass.d);
      
                  obj1.setD(5);
      
                  System.out.println(OuterClass.NestedStaticClass.d);
      
                  System.out.println(obj1.sum());
              }
          }
      
          class OuterClass
          {
              int a =1;
              static int b = 2;
      
              static class NestedStaticClass extends OneClass implements Sample
              {
                  int c = 3;
                  static int d = 4;
      
                  public NestedStaticClass()
                  {
                  }
      
                  //Parameterized constructor
                  public NestedStaticClass(int z)
                  {
                      c = z;
                  }
      
                  public int sum()
                  {
                      int sum = 0;
                      sum = b + c + d + getE();
                      return sum;
                  }
      
                  public static int staticSum()
                  {
                      int sum = 0;
                      sum = b + d;
                      return sum;
                  }
      
                  public int getC()
                  {
                      return c;
                  }
                  public void setC(int c)
                  {
                      this.c = c;
                  }
                  public static int getD()
                  {
                      return d;
                  }
                  public static void setD(int d)
                  {
                      NestedStaticClass.d = d;
                  }
              }
          }
      
          interface Sample
          {
      
          }
      
          class OneClass
          {
              int e = 10;
              static int f = 11;
      
              public int getE()
              {
                  return e;
              }
              public void setE(int e)
              {
                  this.e = e;
              }
              public static int getF()
              {
                  return f;
              }
              public static void setF(int f)
              {
                  OneClass.f = f;
              }
      
          }
      

答案 39 :(得分:-2)

我试图超越WTMI和WTL; DR响应。

单身人士是一个对象的实例......完全停止

你的问题从根本上询问了一个类和一个类的实例之间的区别。我认为这很清楚,不需要详细说明。

单身人士的班级通常采取措施确保建立一个实例;这很聪明,但并不是必需的。

示例:var connection = Connection.Instance;

说这是Connection类:

public sealed class Connection 
{
    static readonly Connection _instance = new Connection();

    private Connection() 
    {
    }

    public static Connection Instance
    {
        get
        {
           return _instance;
        }
    } 
}

请注意,您可以在该类上抛出一个接口并将其模拟以用于测试目的,这是静态类无法轻松完成的。