static

前言:

以下大部分内容我从谷歌上搜索,结合官方的文档总结的。我认为会使用谷歌、会阅读官方的文档非常作用,这里都是最权威的参考。

Java中的Static关键字用的很多,但是总结起来,大概有:Static member、Static Block、Static import三部分内容。下面分别介绍

1. Static Entry

Static试题包括静态变量、静态方法、静态内部类。

首先理解一下Static这个词,在Java中,这个词的意思是这些实体归类所有,与类绑定在一起,所有该类的实例共享这些实体。例如同一个厂商生产的自行车,把自行车抽象为一个类,对象就是每一辆自行车。每一辆自行车有价格、颜色等属性,这些属性每一辆自行车都不一样,但是,他们的声场厂商都是一样的,这个时候就可以把生产厂商定义成一个静态类型。Static实体在内存中单独存放,不跟对象的变量存放在一起。

1.1 静态变量:

定义如下:

[java]   view plain copy
  1. private static int VENDOR = 'ABC' ;  

一般情况下,我们将静态变量用大写字母表示,有多个单词的时候,用下划线分开。静态变量也成为类变量,顾名思义,这些变量属于类的,而不是实例对象的。对于特定的类加载器来说,只存在一个静态变量,无论你创建多少个对象,或者你根本就没有创建对象。注意我的前提,对于特定的类加载器,也就是说,不同的类加载器可能会有多个讲台变量。访问静态变量时,可以直接通过类访问:ClassName.VENDOR. 当然也可以通过实例访问,不过不建议那么做,因为体现不出“静态”二字。

那么,在哪些地方可以声明静态变量呢?对于一般的类中,可以在类中直接声明(与方法并列),注意不能在方法中声明静态变量,不管该方法是否为静态。在内部类当中,情况有些不一样,非静态内部类中不能声明静态变量(事实上,非静态内部类不能声明任何静态试实体)。静态内部内则可以声明静态变量。一句话,静态变量只能在外层类中或者静态内类中定义。


1.2 静态方法:

定义格式如下:

[java]   view plain copy
  1. public static void doSomething() {...  

一样的,静态方法也属于类所有,但是对象一样可以调用。静态方法一般用于对静态变量进行操作,在静态方法中,不能直接引用非静态变量。因为非静态变量属于某个实例所有,如果还没有创建对象,那就没有实例变量存在,请问如何引用??如果要引用非静态变量,只能通过对象引用。静态方法中不能引用this、super这些关键字。

最常见的静态方法当然是main()方法。因为在其他所有对象创建之前,main方法就必须被调用,此时只能通过类来调用这个main方法。在main方法中,是不能直接引用非静态变量的。

[java]   view plain copy
  1. public class StaticMethod {  
  2.   
  3.     private int age = 0 ;  
  4.     private static String VENDOR = "JAC";  
  5.       
  6.     /** 
  7.      * @param args 
  8.      */  
  9.     public static void main(String[] args) {  
  10.         /* 
  11.          * doesn't work 
  12.          */  
  13.         System.out.println(age);  
  14.         /** 
  15.          * OK 
  16.          */  
  17.         System.out.println(VENDOR);  
  18.   
  19.     }  
  20.   
  21. }  

静态方法中同样不能直接调用非静态方法,只能通过对象引用。总结一下:实例方法可以直接访问实例变量、静态变量,可以直接调用实例方法、静态方法。但是,静态方法只能直接访问静态变量、调用静态方法,如果要访问非静态变量或方法,只能通过对象引用。举例如下:
[java]   view plain copy
  1. public class StaticMethod {  
  2.   
  3.     private int age = 0 ;  
  4.     private static String VENDOR = "JAC";  
  5.       
  6.     public void show() {  
  7.         System.out.println("Non-Static");  
  8.     }  
  9.       
  10.     public static void staticShow() {  
  11.         System.out.println("Static");  
  12.     }  
  13.       
  14.     public static void main(String[] args) {  
  15.         //直接访问  
  16.         System.out.println(VENDOR);   
  17.         staticShow();  
  18.          //通过对象引用  
  19.         StaticMethod staticMethod = new StaticMethod();  
  20.         System.out.println(staticMethod.age);  
  21.         staticMethod.show();  
  22.   
  23.     }  
  24.   
  25. }  
那么,在哪里可以定义静态方法呢?官方说法是这样的,Static methods can only be declared in a static or top-level type.也就是说,在外层的类中,可以定义静态方法。但是在内层的类当中,只有静态的内部类才能定义静态方法。跟静态变量的情况其实是一样的。


1.3 静态类

只能在内部类中定义静态类。也就是说,内部类有静态和非静态之分。静态内部类与外层类绑定,即使没有创建外层类的对象,它一样存在。但是非静态内部类不一样,它是与特定的外层类对象绑定的,只有外部类对象存在,内部类才存在。

内部静态内中的方法一样有静态与非静态之分,如果是静态方法,在外层中可以通过内部静态内直接调用,如果是非静态方法,则必须先创建内部静态类的对象之后再调用。

[java]   view plain copy
  1. public class Test{  
  2.   
  3.     public static void main(String[] args) {  
  4.         StaticInnerClass.diaplay();  
  5.         StaticInnerClass i = new StaticInnerClass();  
  6.         i.test();  
  7.           
  8.     }  
  9.       
  10.     static class StaticInnerClass {  
  11.         private static String VENDOR = "JAC";  
  12.         private static void diaplay() {  
  13.             System.out.println("Inner static class, static method");  
  14.         }  
  15.           
  16.         private void test() {  
  17.             System.out.println("Inner static class,non-static methon");  
  18.         }  
  19.           
  20.     }  
  21.   
  22. }  

至于非静态内部类,不是本文的探讨内容,参考http://docs.oracle.com/javase/tutorial/java/javaOO/nested.html。静态实体的探讨就到这里。


2. Static Block

静态块的格式如下:(下面是一个完整例子,接下来说明用)

[java]   view plain copy
  1. import java.util.HashMap;  
  2. import static java.lang.Math.PI;  
  3. /** 
  4.  *  
  5.  * @author bingduanLin 
  6.  * 
  7.  */  
  8.   
  9.   
  10. public class StaticBlock {  
  11.   
  12.     {  
  13.         System.out.println("Instance Initializer, run when an object is created! ");  
  14.     } // can bu reuser in constructors!!  
  15.       
  16.       
  17.       
  18.     private static final HashMap<String, String> MAP = new HashMap<String, String>();  
  19.     static {  
  20.         MAP.put("banana""honey");  
  21.         MAP.put("peanut butter""jelly");  
  22.         MAP.put("rice""beans");  
  23.       }  
  24.     static {  
  25.         System.out.println("Static Initializer, run when the class is loaded!");  
  26.         MAP.put("cons""cons");  
  27.     }  
  28.       
  29.       
  30.     /** 
  31.      * @param args 
  32.      */  
  33.     public static void main(String[] args) {  
  34.         new StaticBlock();  
  35.         new StaticBlock();  
  36.         new StaticBlock();  
  37.         System.out.println(MAP);  
  38.         /** 
  39.          * The static import declaration is analogous to the normal import declaration.  
  40.          * Where the normal import declaration imports classes from packages, allowing  
  41.          * them to be used without package qualification, the static import declaration  
  42.          * imports static members from classes, allowing them to be used without 
  43.          * class qualification. 
  44.          */  
  45.         System.out.println(PI);  
  46.   
  47.     }  
  48.   
  49. }  

static { }这一部分就是静态块,当类加载器载入类的时候,这一部分代码被执行,常用于对静态变量进行初始化工作。当然,可以声明一个静态方法来完成这些初始化工作,这样做的好处就是这些初始化工作可以再次被调用,而在初始化块中,代码只能执行一次,不能再调用。在静态块中,可以访问静态变量,调用静态方法。

如果去掉static,{ }中的代码则会在创建类对象的时候才执行,(相当于把这部分代码复制到各个构造函数中)这样可以实现块中的内容在多个构造函数中的复用。上面的代码输出结果如下:

[java]   view plain copy
  1. Static Initializer, run when the class is loaded!  
  2. Instance Initializer, run when an object is created!   
  3. Instance Initializer, run when an object is created!   
  4. Instance Initializer, run when an object is created!   
  5. {banana=honey, rice=beans, peanut butter=jelly, cons=cons}  
  6. 3.141592653589793  

可以看到, static{ }中的代码只执行一次,而{ }中的代码每当创建一个新对象的时候就会被执行。{ } 这样的代码块也被称为构造函数块(Constructor Block)。再举个例子:

[java]   view plain copy
  1. package learn.study;  
  2.   
  3. public class StaticExample{  
  4.     static {  
  5.         System.out.println("This is first static block");  
  6.     }  
  7.   
  8.     public StaticExample(){  
  9.         System.out.println("This is constructor");  
  10.     }  
  11.   
  12.     public static String staticString = "Static Variable";  
  13.   
  14.     static {  
  15.         System.out.println("This is second static block and refer to STATIC VARIABLES:"  
  16.                                                 + staticString);  
  17.     }  
  18.   
  19.     public static void main(String[] args){  
  20.         /*StaticExample statEx = new StaticExample(); 
  21.         StaticExample.staticMethod2();*/  
  22.         staticMethod2();  
  23.     }  
  24.   
  25.     static {  
  26.         System.out.println("This is third static block and refer to STATIC METHOD");  
  27.         staticMethod();  
  28.     }  
  29.   
  30.     public static void staticMethod() {  
  31.         System.out.println("This is static method");  
  32.     }  
  33.   
  34.     public static void staticMethod2() {  
  35.         System.out.println("This is static method2");  
  36.     }  
  37. }  

输出:
[java]   view plain copy
  1. This is first static block  
  2. This is second static block and refer to STATIC VARIABLES:Static Variable  
  3. This is third static block and refer to STATIC METHOD  
  4. This is static method  
  5. This is static method2  

当然,静态块也有它的局限性,比如静态块代码大小不能超过JVM规定的某个值,不能用this,super关键字,不能从静态块中返回值,增加调试难度等等。因此,必须小心处理静态块中可能出现的异常。


3. 静态导入:

最后提一下静态导入,如上面的代码: static import java.lang.Math.PI

看上面的注释,import之后,我们可以省略Class前面的包名直接使用类,如System.out.println(Math.PI).

静态导入之后,可以把类名也省略掉,直接使用类当中的变量或者方法,即System.out.println(Math.PI).静态导入虽然有它的一些方便,但使用的时候要非常小心。参考官方文档:http://docs.oracle.com/javase/1.5.0/docs/guide/language/static-import.html

 

 

java中的static【从内存角度理解】

java当中,一共有三种类型的变量:
类变量:类变量是类中独立于方法之外的变量,用static 修饰。
实例变量:实例变量也是类中独立于方法之外的变量,不过没有static修饰。
局部变量:局部变量是类的方法中的变量。

1、针对类变量:

类变量仅在生成第一个对象时分配内存,所有实例对象共享同一个类变量(也就是所有实例对象共享一个内存空间),每个实例对象对类变量的改变都会影响 到其它的实例对象 (就像c语言中指向同一个地址(既同一个内存空间)的不同指针变量,改变每一个指针的值都会影响到其他指针的值) 。类变量可通过类名直接访问,无需先生成一个实例对象,也可以通过实例对象访问类变量。

类变量的数值可以通过实例来改变,各个实例共享类变量中的值。(假设有3个book实例,有类变量BookNum表示图书总数,在每一个book实 例中修改BookNum均会影响图书的总数)而实例变量是不需要用static来声明的,每个实例各占用一块内存空间,各实例变量互不影响。

static关键字在java里面用于类方法和类变量,如果你想不论产生了多少个对象,或不存在任何对象的情形下,那些特定数据的存储空间都只有一 份;或者希望某个函数不要和class object绑在一起。即使没有产生任何object,外界还是可以调用其static函数,或是取用其static data。

实例方法可以对当前对象的实例变量进行操作,也可以对类变量进行操作,实例方法由实例对象调用。
但类方法不能访问实例变量,只能访问类变量。类方法可以由类名直接调用,也可由实例对象进行调用。类方法中不能使用this或super关键字。

2、实例变量:

实例变量是可以补赋初始值的变量,比如一个整型的实例变量如果没有初始化,则默认值为0;而局部变量如果不赋初值语法上是通过的,但是在使用这个变量是程序就报错了,应该报的是空指针异常。
实例变量在堆和栈中都分配内存空间,在堆当中分配的是对象本身,而栈中则是对这个对象的引用(个人认为Object类的hoshcode方法就是对栈中的实例变量的引用地址的处理生成)

3、局部变量:

局部变量是在方法内部声明的变量,生命期仅在方法内,方法结束后变量就消失了;
局部变量必须初始化再使用,否则会报错,也就是说,如果你在方法内定义了一个局部变量,并且没有赋值,那么你在使用这个变量的时候一定得赋值,不然就报错了。

局部变量本身肯定是线程安全的,或者说它根本没有线程安全的问题。
局部变量分配在栈上,而栈是专属于线程的概念 - 每个线程都有它自己的栈。

如果变量是在静态方法里面申明的,那么属于局部变量,每次调用都会有自己的值,和你调用次数或者是不是多线程无关。如果静态方法里面有类变量,也就是静态变量,那么你多次调用的时候肯定会受影响。因为内存中只有一个值。

 

java之static分析

对于学习static我们先来看两个例子:
//Example 1  Tst.java,no main() method
   package com.blogchina.qb2049;
   public class Tst
   {
      static { System.out.println("111111"); } 
   }
运行结果为: 111111
           Exception in thread "main" java.lang.NoSuchMethodError: main
同样的道理看第二个例子:
//Example 2  TFrame.java,no main() method
  package com.blogchina.qb2049;
  import javax.swing.JFrame;
  public class TFrame
  {
    static {
               JFrame jfr_fr= new JFrame("无main()窗体 ");
               jfr_fr.setLocation(400,300);
               jfr_fr.setSize(200,300);
               jfr_fr.setVisible(true);
               jfr_fr.setDefaultCloseOperation(jfr_fr.EXIT_ON_CLOSE);
            } 
 }运行结果如下图:


     这到底怎么回事 ,这不和我们大多数教程 介绍的无main()方法程序似乎不能运行相矛盾吗。其实,这其中的缘由 就在于那个static的修饰。我在学习java时,static的学习让我很受启发 ,特此把它的总结如下,希望对想学习java的朋友有所帮助。同时,本文中的不足还希望大家多多的提出来,我们共同学习。

学习本文你到底要学到什么:
1、 static在java中到底代表什么,为何要用它?
2、 static在java中怎么用?
3、 static 有那些特点和使用的“局限”?


1、 static在java中到底代表什么,为何要用它?
     static――静态――“指定位置“
     首先,我们来看看java的内存:java把内存分为栈内存和堆内存,栈内存用来存放一些基本类型的变量和数组及对象的引用变量,而堆内存主要是来放置对象的。
      用 static的修饰的变量和方法,实际上是指定了这些变量和方法在内存中的“固定位置”-static storage。既然要有“固定位置”那么他们的 “大小”似乎就是固定的了,有了固定位置和固定大小的特征了,在栈中或堆中开辟空间那就是非常的方便了。如果静态的变量或方法在不出其作用域的情况下,其引用句柄是不会发生改变的。
      我们常看到:static变量有点类似于C中的全局变量的概念;静态表示的是内存的共享,就是它的每一个实例都指向同一个内存地址。把static拿来,就是告诉JVM它是静态的,它的引用(含间接引用)都是指向同一个位置,在那个地方,你把它改了,它就不会变成原样,你把它清理了,它就不会回来了。我们常可看到类似以下的例子来说明这个问题:
    class Student{
                static int numberOfStudents=0;
                Student()
                 {
                   numberOfStudents++;
                 }
               } 每一次创建一个新的Student实例时,成员numberOfStudents都会不断的递增,并且所有的Student实例都访问同一个 numberOfStudents变量,实际上int numberOfStudents变量在内存中只存储在一个位置上。为了来更好的说明这个问题,我特意学习张老师(张孝祥)使用一个形象的图解:
            创建两个Student实例,stu1=new Student(); stu2=new Student();    

              

             多个实例共享一个变量似乎不足以让我们对static那么的热情,实际上java引入static却有另外的含义:

      (1)、引用static的方法和变量,不需要和实例捆绑在一起,这可以提高代码的编写的效率,这样的例子我们随处可见;
      (2)、 java的主类中main()方法本身就是一个static的,所以main方法的执行就是在没有产生新的实例的情况;对于静态的东西,JVM在加载类时,就在內存中开辟了这些静态的空间,所以虽没有静态的main()方法但是程序还是执行了,不过抛出来了无main()方法的异常。这也不知算不算是 java的一个漏洞;
      (3)、如果需要创建一个脱离于实例的变量或方法(只与整个类有关),那么用static作修饰是再好不过了,如我们经常看到要统计实例实现的个数(通常的例子就是计数)。
      (4)、使用一种静态的方法的编程通常叫做防御(defensive)编程,它可以在API供应商突然中断支持的情况下保护代码  


2、 static在java中怎么用?
     使用static时,要记着我阐述的static代表什么。
      static 使用非常的简单,如果要修饰一个静态块只需:staic {……..}即可(常用静态块来初始化一些变量); 静态方法就参照main()的形式:访问标识 static returnType method(…) {};静态变量就是:static type fields;
     在使用静态的方法时,可以直接用类名来引用,不需要创建实例(当然创建实例也是可以的),例如,System.out,String.valueOf()等等。

3、 static 有那些特点和使用的“局限”?
     从上面的分析可知,static的东西在类加载时,就分配了内存空间,即编译时就为这些成员变量的实例分配了空间。
      那么在static块内和方法体内,我们能给它放一个在内存中还没有着落的变量?显然与我们先前所说的相左。static的东西,人家是在 static storage中有“指定位置“的,如果我们茫然的在static的作用域中放置一个普通的变量,那么编译时JVM就毫不客气的给你个异常:
non-static variable a cannot be referenced from a static context或 non- static method Test() cannot be referenced from a static context(注:Test() 是我试验时的一个例子 ),除非我在static中现场开辟空间,用new来要内存。
对于static的初始化问题,我们还是值得讨论的。现看下面的例子:
//StaticInit show the static decorated initialization 
package com.blogchina.qb2049;
public class StaticInit
{
   static int i;
   int a;
   public StaticInit()
   {
      a=6;
      System.out.println("a 的初始化"+a);
   } 
   public static void main(String[] args)
  {
    new StaticInit();
  }
  static 
  {
     i=5;
     System.out.println("i 的初始化"+i);
  }
}运行结果如下:i 的初始化5
                        a 的初始化6
 静态块的初始化要早于非静态的,原因就是在于这些东西是在类装载时就开始初始化了。
      说起static的“局限“,总结起来就是:在static的方法中仅能够调用其他的static方法和static变量;在static方法中不能以任何方式引用this或super;static变量在定义时必须进行初始化,并且初始化的时间早于非静态。还有一个局限我需要具体的说明一下,static 的变量的初始化仅能一次,如下例:
    //Static.java, initialize only one
    class T1
    {   
     static int t=1;
     T1(int b)
     {
       t=b;
     }
   }
   public class Static
    {
       T1 t1=new T1(2);
       T1 t2=new T1(3);
       T1 t3=new T1(4);
       public Static()
       {
         System.out.println("t1: "+t1.t);
         System.out.println("t2: "+t2.t);
         System.out.println("t3: "+t3.t);
      }
      public static void main(String args[])
      {
        new Static();
      }
   }
运行结果为: t1: 4
                        t2: 4
                        t3: 4

该static变量只是接受了最后一次的初始化.实际这还是我们先前提到的多个实例共享一个静态的变量的问题。
总之,static就是给我们一个这样的忠告:static的东西在编译时,就已向内存要到了存取空间了。

你可能感兴趣的:(static)