在java的关键字中,static是常常用到的关键字。static修饰的成员变量和成员方法独立于该类的任何对象。也就是说,它不依赖类特定的实例,被类的全部实例共享。只要这个类被加载,Java虚拟机就能根据类名在运行时数据区的方法区内定找到他们。所以,static对象能够在它的任何对象建立以前访问,无需引用任何对象。html
static关键字有以下几种用法:java
一、用来修饰成员变量,将其变为类的成员,从而实现全部对象对于该成员的共享;数据库
二、用来修饰成员方法,将其变为类方法,能够直接使用“类名.方法名”的方式调用,经常使用于工具类;jvm
三、静态块用法,将多个类成员放在一块儿初始化,使得程序更加规整,其中理解对象的初始化过程很是关键;函数
四、静态导包用法,将类的方法直接导入到当前类中,从而直接使用“方法名”便可调用类方法,更加方便。工具
下面将详细分析这四种用法: 性能
按照是否静态的对类成员变量进行分类可分两种:一种是被static修饰的变量,叫静态变量或类变量;另外一种是没有被static修饰的变量,叫实例变量。二者的区别是:优化
对于静态变量在内存中只有一个拷贝(节省内存),JVM只为静态分配一次内存(在虚拟机的方法区),在加载类的过程当中完成静态变量的内存分配,可用类名直接访问(方便),固然也能够经过对象来访问(可是这是不推荐的)。 this
对于实例变量,每建立一个实例,就会为实例变量分配一次内存,实例变量能够在内存中有多个拷贝,互不影响(灵活)。spa
static成员变量的初始化顺序按照定义的顺序进行初始化。static不能够修饰局部变量。
因此通常在须要实现如下两个功能时使用静态变量:(1)在对象之间共享值时 (2)方便访问变量时
接下来看下面的例子:
public class Person { String name; int age; public String toString() { return "Name:" + name + ", Age:" + age; } public static void main(String[] args) { Person p1 = new Person(); p1.name = "zhangsan"; p1.age = 10; Person p2 = new Person(); p2.name = "lisi"; p2.age = 12; System.out.println(p1); System.out.println(p2); } /**Output * Name:zhangsan, Age:10 * Name:lisi, Age:12 *///~ }
上面的代码咱们很熟悉,根据Person构造出的每个对象都是独立存在的,保存有本身独立的成员变量,相互不会影响,他们在内存中的示意以下:
从上图中能够看出,p1和p2两个变量引用的对象分别存储在内存中堆区域的不一样地址中,因此他们之间相互不会干扰。但其实,在这当中,咱们省略了一些重要信息,相信你们也都会想到,对象的成员属性都在这了,由每一个对象本身保存,那么他们的方法呢?实际上,不论一个类建立了几个对象,他们的方法都是同样的:
从上面的图中咱们能够看到,两个Person对象的方法实际上只是指向了同一个方法定义。这个方法定义是位于内存中的一块不变区域(由jvm划分),咱们暂称它为静态存储区。这一块存储区不只存放了方法的定义,实际上从更大的角度而言,它存放的是各类类的定义,当咱们经过new来生成对象时,会根据这里定义的类的定义去建立对象。多个对象仅会对应同一个方法,这里有一个让咱们充分信服的理由,那就是无论多少的对象,他们的方法老是相同的,尽管最后的输出会有所不一样,可是方法老是会按照咱们预想的结果去操做,即不一样的对象去调用同一个方法,结果会不尽相同。
咱们知道,static关键字能够修饰成员变量和方法,来让它们变成类的所属,而不是对象的所属,好比咱们将Person的age属性用static进行修饰,结果会是什么样呢?请看下面的例子:
public class Person { String name; static int age; /* 其他代码不变... */ /**Output * Name:zhangsan, Age:12 * Name:lisi, Age:12 *///~ }
咱们发现,结果发生了一点变化,在给p2的age属性赋值时,干扰了p1的age属性,这是为何呢?咱们仍是来看他们在内存中的示意:
咱们发现,给age属性加了static关键字以后,Person对象就再也不拥有age属性了,age属性会统一交给Person类去管理,即多个Person对象只会对应一个age属性,一个对象若是对age属性作了改变,其余的对象都会受到影响。咱们看到此时的age和toString()方法同样,都是交由类去管理。
虽然咱们看到static可让对象共享属性,可是实际中咱们不多这么用,也不推荐这么使用。由于这样会让该属性变得难以控制,由于它在任何地方都有可能被改变。若是咱们想共享属性,通常咱们会采用其余的办法:
public class Person { private static int count = 0; int id; String name; int age; public Person() { id = ++count; } public String toString() { return "Id:" + id + ", Name:" + name + ", Age:" + age; } public static void main(String[] args) { Person p1 = new Person(); p1.name = "zhangsan"; p1.age = 10; Person p2 = new Person(); p2.name = "lisi"; p2.age = 12; System.out.println(p1); System.out.println(p2); } /**Output * Id:1, Name:zhangsan, Age:10 * Id:2, Name:lisi, Age:12 *///~ }
上面的代码起到了给Person的对象建立一个惟一id以及记录总数的做用,其中count由static修饰,是Person类的成员属性,每次建立一个Person对象,就会使该属性自加1而后赋给对象的id属性,这样,count属性记录了建立Person对象的总数,因为count使用了private修饰,因此从类外面没法随意改变。
静态方法的好处就是不用生成类的实例就能直接调用,能够这样理解使用static修饰的成员再也不归对象因此,而是属于类能够理解为是共有的,也就说只要经过类名就能够访问,就是可使用"类名.方法名"的方式操做方法,不须要耗费资源反复new出对象,由于在程序第一次加载的时候就已经在内存中了,直到程序结束该内存才会释放。若是不是static修饰的成员在使用完以后该内存就会被回收,因此说static要慎用,根据实际状况而定
若是这个方法是做为一个工具来使用,就声明为static,不用new一个对象出来就可使用了,好比链接到数据库,我声明一个 getConnection()的方法,就定义为静态的,由于链接到数据库不是某一个对象所特有的。它只做为一个链接到数据库的工具。至于提升效率的也未 必,要看具体的方法的用处,去定义这个方法是否是静态的。
咱们可能会常常在帮助类中看到它的使用:
public class PrintHelper { public static void print(Object o){ System.out.println(o); } public static void main(String[] args) { PrintHelper.print("Hello world"); } }
上面即是一个例子(如今还不太实用),可是咱们能够看到它的做用,使得static修饰的方法成为类的方法,使用时经过“类名.方法名”的方式就能够方便的使用了,至关于定义了一个全局的函数(只要导入该类所在的包便可)。不过它也有使用的局限,一个static修饰的类中,不能使用非static修饰的成员变量和方法,这很好理解,由于static修饰的方法是属于类的,若是去直接使用对象的成员变量,它会不知所措(不知该使用哪个对象的属性)。
static代码块也叫静态代码块,是在类中独立于类成员的static语句块,能够有多个,位置能够随便放,它不在任何的方法体内,JVM加载类时会执行这些静态的代码块,若是static代码块有多个,JVM将按照它们在类中出现的前后顺序依次执行它们,每一个代码块只会被执行一次,因此说static块能够用来优化程序性能。
static方法块和static方法的区别:
静态代码块是自动执行的;
静态方法是被调用的时候才执行的.
静态方法:若是咱们在程序编写的时候须要一个不实例化对象就能够调用的方法,咱们就可使用静态方法,具体实现是在方法前面加上static,以下:
public static void method(){}
在使用静态方法的时候须要注意一下几个方面:
在静态方法里只能直接调用同类中其余的静态成员(包括变量和方法),而不能直接访问类中的非静态成员。这是由于,对于非静态的方法和变量,须要先建立类的实例对象后才可以使用,而静态方法在使用前不用建立任何对象。(备注:静态变量是属于整个类的变量而不是属于某个对象的)
静态方法不能以任何方式引用this和super关键字,由于静态方法在使用前不用建立任何实例对象,当静态方法调用时,this所引用的对象根本没有产生。
静态程序块:当一个类须要在被载入时就执行一段程序,这样可使用静态程序块。
public class DemoClass { private DemoClass(){} public static DemoClass _instance; static{ if(null == _instance ){ _instance = new DemoClass(); } } public static DemoClass getInstance(){ return _instance; } }
这样的程序在类被加载的时候就执行了static中的代码。
相比于上面的三种用途,第四种用途可能了解的人就比较少了,可是实际上它很简单,并且在调用类方法时会更方便。以上面的“PrintHelper”的例子为例,作一下稍微的变化,便可使用静态导包带给咱们的方便:
/* PrintHelper.java文件 */ package com.dotgua.study; public class PrintHelper { public static void print(Object o){ System.out.println(o); } }
/* App.java文件 */ import static com.dotgua.study.PrintHelper.*; public class App { public static void main( String[] args ) { print("Hello World!"); } /**Output * Hello World! *///~ }
上面的代码来自于两个java文件,其中的PrintHelper很简单,包含了一个用于打印的static方法。而在App.java文件中,咱们首先将PrintHelper类导入,这里在导入时,咱们使用了static关键字,并且在引入类的最后还加上了“.*”,它的做用就是将PrintHelper类中的全部类方法直接导入。不一样于非static导入,采用static导入包后,在不与当前类的方法名冲突的状况下,无需使用“类名.方法名”的方法去调用类方法了,直接能够采用"方法名"去调用类方法,就好像是该类本身的方法同样使用便可。
参考文献:一、https://www.cnblogs.com/dotgua/p/6354151.html?utm_source=itdadao&utm_medium=referral
二、https://www.cnblogs.com/wonderlands/p/5630573.html
三、https://www.cnblogs.com/itcqx/p/5519464.html