JAVA中的Static(静态变量、静态方法、静态代码块)

时间:2023-02-11 19:27:23

       只要是接触编程语言(C、C++、C#、java等等),static这个关键字相信程序猿朋友们都曾经深刻的理解过其含义,以前遇到的静态方法,静态变量还能理解,但是在碰到static代码块时候懵了,完全不知道是啥意思,也不懂其执行的顺序是什么,在什么时候执行,作为初学者,我相信也有人和我一样遇到了同样的问题,今天就对static代码块做个详细的剖析,不足之处还望各位前辈指正。

      我们先来说说static这个关键字在程序中的作用,为什么要用static关键字,用了他有什么好处,然后再看看静态变量和实例变量的异同、静态方法与类方法的区别等,最后用代码来说明下在程序执行的时候被static修饰的代码块分别是如何来执行的?

1、为什么要用static关键字

static修饰的静态变量和静态方法实际上是储存在一个特定位置内存上。一个类中static只分配一个内存空间,一个类虽然可以有多个实例,但是这些实例中的static变量或方法共享一个内存空间;static变量的声明与初始化是两个不同的操作。编写顺序不分先后。另外说一点:java把内存分为栈内存和堆内存,栈内存用来存放一些基本类型的变量和数组及对象的引用变量,而堆内存主要是来放置对象的。用static关键字有以下几点对于程序好处:

(1)用static的方法和变量,不需要和实例捆绑在一起,这可以提高代买的编写效率。

(2)java的主类的main()方法本身就是一个static的,所以main方法的执行没有产生新的实例的情况,对于静态的东东,jvm在加载类的时候,就在内存中开辟了这些惊天的内存空间。

(3)如果需要创建一个脱离实例的变量和方法,使用static是最好的选择,例如统计实例实现的个数,通常的例子就是计数。

(4)使用一种静态的方法编程通常叫做防御编程。它考一个在API供应商突然中断支持的情况下保护代码。

另外static还有一些特点,也可以说是其局限,主要有以下几个方面:

(1)在static方法中只能调用static方法或static变量

(2)在static方法中不能调用this或者super

(3)static变量在定义时必须进行初始化,并且初始化的时间早于非static,而且只能被初始化一次。

2、静态变量

   声明为static的变量实质上就是全局变量。当声明一个对象时,并不产生static变量的拷贝,而是该类所有的实例变量共用同一个static变量。静态变量与静态方法类似。所有此类实例共享此静态变量,也就是说在类装载时,只分配一块存储空间,所有此类的对象都可以操控此块存储空间,当然对于final则另当别论了。

实例变量和类变量的区别:

(1)存放位置不同

类变量随着类的加载而存在于方法区中。

实例变量随着对象的建立而存在于堆内存中。

(2)生命周期不同

静态会随着类的加载而加载,随着类的消失而消失,而实例变量知识类中的一个成员,他的产生和消失是不会随类而变化的。

类变量生命周期最长。随着类的消失而消失。

实例变量生命周期随着对象的消失而消失。

3、静态方法

  在Java里,可以定义一个不需要创建对象的方法,这种方法就是静态方法。要实现这样的效果,只需要在类中定义的方法前加上static关键字。

使用类的静态方法时,需要注意两点:

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

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

4、静态代码块

  现在来到重点,通过static关键字用来形成静态代码块,以优化程序性能。static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次,即静态代码块随着类的加载而执行,只执行一次,并优先于主函数执行。如下列代码:

public class Test extends Base{

static{
System.out.println("test static");
}

public Test(){
System.out.println("test constructor");
}

public static void main(String[] args) {
new Test();
}
}

class Base{

static{
System.out.println("base static");
}

public Base(){
System.out.println("base constructor");
}
}
执行结果为:

base static
test static
base constructor
test constructor

     至于为什么是这个结果,我们先不讨论,先来想一下这段代码具体的执行过程,在执行开始,先要寻找到main方法,因为main方法是程序的入口,但是在执行main方法之前,必须先加载Test类,而在加载Test类的时候发现Test类继承自Base类,因此会转去先加载Base类,在加载Base类的时候,发现有static块,便执行了static块。在Base类加载完成之后,便继续加载Test类,然后发现Test类中也有static块,便执行static块。在加载完所需的类之后,便开始执行main方法。在main方法中执行new Test()的时候会先调用父类的构造器,然后再调用自身的构造器。因此,便出现了上面的输出结果。