Static关键字深入理解

1、static变量

按照是否静态的对类成员变量进行分类可分两种:一种是被static修饰的变量,叫静态变量或类变量;另一种是没有被static修饰的变量,叫实例变量。

两者的区别是:

对于静态变量在内存中只有一个拷贝(节省内存),JVM只为静态分配一次内存,在加载类的过程中完成静态变量的内存分配,可用类名直接访问(方便),当然也可以通过对象来访问(但是这是不推荐的)。

对于实例变量,没创建一个实例,就会为实例变量分配一次内存,实例变量可以在内存中有多个拷贝,互不影响(灵活)。

static成员变量的初始化顺序按照定义的顺序进行初始化。static不可以修饰局部变量

所以一般在需要实现以下两个功能时使用静态变量:

  • 在对象之间共享值时
  • 方便访问变量时


2、静态方法

静态方法的好处就是不用生成类的实例就能直接调用,可以这样理解使用static修饰的成员不再归对象所以,而是属于类,可以理解为是共有的,也就说只要通过类名就可以访问,不需要耗费资源反复创建对象,因为在程序第一次加载的时候就已经在内存中了,直到程序结束该内存才会释放。如果不是static修饰的成员在使用完之后该内存就会被回收,所以说static要慎用,根据实际情况而定

如果这个方法是作为一个工具来使用,就声明为static,不用new一个对象出来就可以使用了,比如连接到数据库,我声明一个 getConnection()的方法,就定义为静态的,因为连接到数据库不是某一个对象所特有的。它只作为一个连接到数据库的工具。至于提高效率的也未 必,要看具体的方法的用处,去定义这个方法是不是静态的。

因为static方法独立于任何实例,因此static方法必须被实现,而不能是抽象的abstract。

 

3.static修饰类

一般静态内部类可以用static修饰(java内部类分为四种:常规内部类、静态内部类、局部内部类、匿名内部类)。只能访问外部类的static成员,不能直接访问外部类的实例变量与实例方法。

4、static代码块

static代码块也叫静态代码块,是在类中独立于类成员的static语句块,可以有多个,位置可以随便放,它不在任何的方法体内,JVM加载类时会执行这些静态的代码块,如果static代码块有多个,JVM将按照它们在类中出现的先后顺序依次执行它们,每个代码块只会被执行一次,所以说static块可以用来优化程序性能。

 

static方法块和static方法的区别:

静态代码块是自动执行的;

静态方法是被调用的时候才执行的.

静态方法:如果我们在程序编写的时候需要一个不实例化对象就可以调用的方法,我们就可以使用静态方法,具体实现是在方法前面加上static,如下:

public static void method(){}

在使用静态方法的时候需要注意一下几个方面:

在静态方法里只能直接调用同类中其他的静态成员(包括变量和方法),而不能直接访问类中的非静态成员。这是因为,对于非静态的方法和变量,需要先创建类的实例对象后才可使用,而静态方法在使用前不用创建任何对象。(备注:静态变量是属于整个类的变量而不是属于某个对象的)

静态方法不能以任何方式引用this和super关键字,因为静态方法在使用前不用创建任何实例对象,当静态方法调用时,this所引用的对象根本没有产生。

静态程序块:

(1)当一个类需要在被载入时就执行一段程序,这样可以使用静态程序块。

public class DemoClass {

private DemoClass(){}

public static DemoClass _instance;

static{

if(null == _instance ){

_instance = new DemoClass();

}

}

public static DemoClass getInstance(){

return _instance;

}

}

这样的程序在类被加载的时候就执行了static中的代码。

(2)如果静态变量在定义的时候就赋给了初值(如 static int X=100),那么赋值操作也是在类加载的时候完成的,并且当一个类中既有static{}又有static变量的时候,同样遵循“先定义先执行”的原则。

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class A {  
  2.         public static int x = 10;  
  3.         static{  
  4.             System.out.println(x);  
  5.             x = 20;  
  6.             System.out.println(x);  
  7.         }  
  8.           
  9.         public static void main(String args[]){  
  10.            int y = A.x;  
  11.         }  
  12.     }//结果为10  20  

(3)通过static来理解类的加载顺序

类A中执行main()的时候,首先加载A,B初始化之前,首先加载B

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class A   {  
  2.     static{  
  3.         System.out.println("Static A");  
  4.     }  
  5.     public A(){  
  6.         System.out.println("constructor A");  
  7.     }  
  8.     public static void main(String args[]){  
  9.          new B();   
  10.     }  
  11. }  
  12. class B {  
  13.     static{  
  14.         System.out.println("Static B");  
  15.     }  
  16.     public B(){  
  17.         System.out.println("constructor B");  
  18.     }  
  19. }  

运行结果:

Static A
Static B
constructor B

类A加载之前首先加载其父类

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class A extends B  {  
  2.     static{  
  3.         System.out.println("Static A");  
  4.     }  
  5.     public A(){  
  6.         System.out.println("constructor A");  
  7.     }  
  8.     public static void main(String args[]){  
  9.          new A();   
  10.     }  
  11. }  
  12. class B {  
  13.     static{  
  14.         System.out.println("Static B");  
  15.     }  
  16.     public B(){  
  17.         System.out.println("constructor B");  
  18.     }  
  19. }  
结果:

Static B
Static A
constructor B
constructor A

只有直接定义静态字段X的类才会被初始化
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class A  extends B{  
  2.     static{  
  3.         System.out.println("Static A");  
  4.     }  
  5.     public A(){  
  6.         System.out.println("constructor A");  
  7.     }  
  8.       
  9. }  
  10. class B {  
  11.     public static int x = 10;  
  12.     static{  
  13.         System.out.println("Static B");  
  14.     }  
  15.     public B(){  
  16.         System.out.println("constructor B");  
  17.     }  
  18. }  
  19. public class C {  
  20.     public static void main(String args[]){  
  21.          System.out.println(A.x);  
  22.     }  
  23. }  
结果:

Static B
10

总结:

1.static修饰的成员变量和成员方法独立于该类的任何对象。也就是说,它不依赖类特定的实例,被类的所有实例共享。

只要这个类被加载,Java虚拟机就能根据类名在运行时数据区的方法区内定找到他们。因此,static对象可以在它的任何对象创建之前访问,无需引用任何对象。

需要了解jvm classloader

2.类属性中被static所引用的变量,会被作为GC的root根节点。作为根节点就意味着,这一类变量是基本上不会被回收的。因此,static很容易引入内存泄漏的风险。

(内存泄漏的问题,就是因为static修饰的一个Map类型的变量导致的,最后排查了堆栈信息找到了问题的所在,并且解决了这个问题)

3.static会将所引用的属性、方法以及内部类,与类直接产生引用关系,而非与类的实例。这就是为什么,你可以使用类名.属性、类名.方法以及类名.内部类名,来直接引用一个被static所修饰的属性、方法或者内部类

4.

一个没有被static修饰的内部类,必须要这么声明。

OutterClass.InnerClass innerClass = new OutterClass().new InnerClass();

如果你使用了static修饰,那么你就可以这样使用内部类。

OutterClass.StaticInnerClass staticInnerClass = new OutterClass.StaticInnerClass();

这两种方式最大的区别就是,第一种方式,如果你想要获得InnerClass的实例,你必须有一个OutterClass的实例,所有其实这种方式你创建了两个实例,所以有两个new关键字。而第二种方式就好理解一些,静态内部类不依赖于外部类的实例存在,因此只需要直接创建内部类的实例就可以了,所以只有一个new关键字。


posted @ 2016-11-30 10:05  伴我前行  阅读(290)  评论(0编辑  收藏  举报