zoukankan      html  css  js  c++  java
  • C# static的用法详解

    static静态的,不变的,在某个类中只有一个,不会因实例化对象的不同而不同。static可以修饰类、字段、属性、方法等。如一个方法前加上static修饰后要对其进行调用可以直接通过类名点出来,不用再对类进行实例化。

    一、静态类

      静态类与非静态类的重要区别在于静态类不能实例化,也就是说,不能使用 new 关键字创建静态类类型的变量。在声明一个类时使用static关键字,具有两个方面的意义:首先,它防止程序员写代码来实例化该静态类;其次,它防止在类的内部声明任何实例字段或方法。

      1、静态类的主要特性:

      [1] 仅包含静态成员。

      [2] 无法实例化。

      [3] 静态类的本质,是一个抽象的密封类,所以不能被继承,也不能被实例化。

      [4] 不能包含实例构造函数。

      [5] 如果一个类下面的所有成员,都需要被共享,那么可以把这个类定义为静态类。

      2、静态类与私有构造函数区别:

      [1] 私有构造器方式仍然可以从类的内部对类进行实例化,而静态类禁止从任何地方实例化类,其中包括从类自身内部。

      [2] 使用私有构造器的类中,是允许有实例成员的,编译器不允许静态类有任何实例成员。

      [3] 使用静态类的优点在于,编译器能够执行检查以确保不致偶然地添加实例成员,编译器将保证不会创建此 类的实例。

      [4] C#编译器会自动把它标记为sealed。这个关键字将类指定为不可扩展;换言之,不能从它派生出其他类。

    二、静态成员

      1、通过static关键字修饰,是属于类,实例成员属于对象,在这个类第一次加载的时候,这个类下面的所有静态成员会被加载。

      2、静态成员只被创建一次,所以静态成员只有一份,实例成员有多少个对象,就有多少份。

      3、类加载的时候,所有的静态成员就会被创建在“静态存储区”里面,一旦创建直到程序退出,才会被回收。

      4、成员需要被共享的时候,方法需要被反复调用的时候,就可以把这些成员定义为静态成员。

      5、在静态方法中,不能直接调用实例成员,因为静态方法被调用的时候,对象还有可能不存在。

      6、this/base 关键字在静态方法中不能使用,因为有可能对象还不存在。

      7、可以创建这个类的对象,制定对象的成员在静态方法中操作。

      8、在实例方法中,可以调用静态成员,因为这个时候静态成员肯定存在。

      9、非静态类可以包含静态的方法、字段、属性或事件;

      10、无论对一个类创建多少个实例,它的静态成员都只有一个副本;

      11、静态方法和属性不能访问其包含类型中的非静态字段和事件,并且不能访问任何对象的实例成员;

      12、静态方法只能被重载,而不能被重写,因为静态方法不属于类的实例成员;

      13、虽然字段不能声明为 static const,但 const 字段的行为在本质上是静态的。这样的字段属于类,不属于类的实例。

    三、静态方法

      1、静态方法是不属于特定对象的方法;

      2、静态方法可以访问静态成员;

      3、静态方法不可以直接访问实例成员,可以在实例函数调用的情况下,实例成员做为参数传给静态方法;

      4、静态方法也不能直接调用实例方法,可以间接调用,首先要创建一个类的实例,然后通过这一特定对象来调用静态方法。

    四、静态构造函数

      1、静态类可以有静态构造函数,静态构造函数不可继承;
      2、静态构造函数可以用于静态类,也可用于非静态类;
      3、静态构造函数无访问修饰符、无参数,只有一个 static 标志;
      4、静态构造函数不可被直接调用,当创建类实例或引用任何静态成员之前,静态构造函数被自动执行,并且只执行一次。

      例如:

    复制代码
    复制代码
    复制代码
    class Program
     {
             public static int i =0;
             public Program()
             {
                 i = 1;
                 Console.Write("实例构造方法被调用");
             }
             static Program()
             {
                 i = 2;
                 Console.Write("静态构造函数被执行");
             }
             static void Main(string[] args)
             {
                 Console.Write(Program.i);//结果为2,首先,类被加载,所有的静态成员被创建在静态存储区,i=0,接着调用了类的成员,这时候静态构造函数就会被调用,i=2
                 Program p = new Program();
                 Console.Write(Program.i);//结果为1,实力化后,调用了实例构造函数,i=1,因为静态构造函数只执行一次,所以不会再执行。
             }
     }
    复制代码
    复制代码
    复制代码

    五、静态成员的存储

      使用 static 修饰符声明属于类型本身而不是属于特定对象的静态成员static修饰符可用于类、字段、方法、属性、运算符、事件和构造函数,但不能用于索引器、析构函数或类以外的类型。

      静态全局变量

      定义:在全局变量前,加上关键字 static 该变量就被定义成为了一个静态全局变量。

      特点:A、该变量在全局数据区分配内存。   B、初始化:如果不显式初始化,那么将被隐式初始化为0。

     

      静态局部变量

      定义:在局部变量前加上static关键字时,就定义了静态局部变量。

      特点:A、该变量在全局数据区分配内存。   B、初始化:如果不显式初始化,那么将被隐式初始化为0。   C、它始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,当定义它的函数或 语句块结束时,其作用域随之结束。

     

      静态数据成员
      特点

      A、内存分配:在程序的全局数据区分配。   

      B、初始化和定义:     a、静态数据成员定义时要分配空间,所以不能在类声明中定义。     b、为了避免在多个使用该类的源文件中,对其重复定义,所在,不能在类的头文件中定义。     c、静态数据成员因为程序一开始运行就必需存在,所以其初始化的最佳位置在类的内部实现。   

      C、特点     a、对相于 public,protected,private 关键字的影响它和普通数据成员一样,     b、因为其空间在全局数据区分配,属于所有本类的对象共享,所以,它不属于特定的类对象,在没产生类对象时其作用域就可见,即在没有产生类的实例时,我们就可以操作它。
      D、访问形式     a、 类对象名.静态数据成员名

            E、静态数据成员,主要用在类的所有实例都拥有的属性上。比如,对于一个存款类,帐号相对于每个实例都是不同的,但每个实例的利息是相同的。所以,应该把利息设为存款类的静态数据成员。这有两个好处,第一,不管定义多少个存款类对象,利息数据成员都共享分配在全局区的内存,所以节省存贮空间。第二,一旦利息需要改变时,只要改变一次,则所有存款类对象的利息全改变过来了,因为它们实际上是共用一个东西。  

      静态成员函数
      特点:   A、静态成员函数与类相联系,不与类的对象相联系。   B、静态成员函数不能访问非静态数据成员。原因很简单,非静态数据成员属于特定的类实例。
      作用:   主要用于对静态数据成员的操作。

      调用形式:   A、类对象名.静态成员函数名()

     

      static静态变量的实例与分析,代码如下

    复制代码
    复制代码
    复制代码
    class Program
        {
            static int i = getNum();
            int j = getNum();
    
            static int num = 1;
    
            static int getNum()
            {
                return num;
            }
    
            static void Main(string[] args)
            {
                Console.WriteLine("i={0}", i);
                Console.WriteLine("j={0}", new Program().j);
                Console.Read();
            }
    
        }
    复制代码
    复制代码
    复制代码

      分析上面的代码

      Console.WriteLine("i={0}", i);

      这里 i 是 static 变量,在类 Program  第一次被加载时,要先为 Program  里面所有的 static 变量分配内存。尽管现在有超线程技术,但是指令在逻辑上还是逐条的按顺序自上而下执行,所以 先为 static int i 分配内存,并且在该内存中保持int的缺省值0,接着再为 static int num 变量分配内存,值当然也为0。

      然后第二步,为变量赋值:先为 static int i 变量赋值,i=getNum(),看 getNum() 里面的代码,就是return num,这个时候 num 的值是 0 ,于是 i=0 。然后对变量num赋值,num=1;这行代码执行后,num就为1了。所以,j=1。

      所以最后的结果为:

       i=0 j=1

      注意:

      当类第一次被加载时,会对类中的静态变量先按顺序进行分配内存空间,当全部分配完内存空间之后,在对静态变量按顺序赋值。

       

      首先分为两部分 寄存器和内存(包括缓存)

      内存分为两部分 代码和数据

      数据分为两部分 静态存储区和运行时存储

      运行时存储分为 堆栈 和 堆

      静态存储分为 全局静态存储 和 常量

  • 相关阅读:
    垂死挣扎还是涅槃重生 -- Delphi XE5 公布会归来感想
    自考感悟,话谈备忘录模式
    [每日一题] OCP1z0-047 :2013-07-26 alter table set unused之后各种情况处理
    Java实现 蓝桥杯 算法提高 p1001
    Java实现 蓝桥杯 算法提高 拿糖果
    Java实现 蓝桥杯 算法提高 拿糖果
    Java实现 蓝桥杯 算法提高 求arccos值
    Java实现 蓝桥杯 算法提高 求arccos值
    Java实现 蓝桥杯 算法提高 因式分解
    Java实现 蓝桥杯 算法提高 因式分解
  • 原文地址:https://www.cnblogs.com/bruce1992/p/14084917.html
Copyright © 2011-2022 走看看