Java Static关键字详解
先思考两个问题:
1.为什么要使用Static关键字?
2.加了Static关键字修饰后会有什么不同?
Static
一.为什么要使用Static关键字?
这里摘录一下《Java编程思想(第四版)》里关于static关键字的一段原话:(P29)通常来说,当创建类时,就是在描述那个类的对象的外观与行为。除非用new创建那个对象,否则,实际上并未获得任何对象。执行new来创建对象的时候,数据存储空间才被分配,其方法才供外界调用。有两种情形用上述方法是无法解决的。一种情形是,只想为某特定域分配单一存储空间,而不去考虑究竟要创建多少个对象,甚至根本不需要创建任何对象。另一种情形是,希望某个方法不与包含他的类的任何对象关联在一起。也就是说,即使没有创建对象,也能够调用方法。简单来说,static的主要目的就是创建独立于具体对象的域变量与方法。
二.加了Static关键字修饰会有什么不同?
1.this不能调用被static修饰的属性字段。
因为被static修饰的会在类加载的时候初始化,被称为类级变量(属于类);而类的实例是在运行的时候初始化,属于对象级变量(属于对象)。
this是指当前对象的实例,super是指父类对象的实例。this(super)不能调用被static修饰的属性字段的原因:jvm有类加载器,第一次加载时执行static域,jvm会专门划分一个内存区域给static程序块,所以什么时候都可以调用这个被static修饰的属性字段或者方法,属于类。this指针是指向类的对象,在实例化对象时候jvm会在堆区分配给一个具体对象,this指向该对象。所以在静态方法中不能调用非静态属性或者方法,如果在静态方法中使用this关键字,则this无法指向合适的对象。(通俗一点的话:即jvm的类加载机制决定了加载静态会先与非静态,即加载静态的时候非静态属性和方法都不存在)
2.static修饰属性和方法
1.static修饰属性时候,即所有该类对象共享一份存储空间。
public class StaticTest { public static int staticA; public int A; public static void main(String[] args){ StaticTest test; StaticTest A1=new StaticTest(); StaticTest A2=new StaticTest(); A1.staticA++; A2.staticA++; A1.A++; A2.A++; System.out.println("StaticAInfo:"+A1.staticA+" "+A2.staticA); System.out.println("AInfo"+A1.A+" "+A2.A); } } /** 运行结果 * StaticAInfo:2 2 * AInfo1 1 **/
即static修饰的不是某个具体对象所有,而是该类所有对象共有的,StaticTest.staticA只有一份存储空间。
2.static修饰方法时候,类和对象都能调用该方法,没有被static修饰的方法只能被实例化对象调用。
public class StaticTest { public static void staticTest(){ } public void test(){ } public static void main(String[] args){ StaticTest.staticTest(); //StaticTest.test(); 该调用是错误的 StaticTest test=new StaticTest(); test.staticTest(); test.test(); } }
static修饰成员方法最大的作用,就是可以使用"类名.方法名"的方式操作方法,避免了先要new出对象的繁琐和资源消耗,一个static修饰的类中,不能使用非static修饰的成员变量和方法,这很好理解,因为static修饰的方法是属于类的,如果去直接使用对象的成员变量,它会不知所措(不知该使用哪一个对象的属性)。
static修饰成员方法的时候最大的好处是可以节省new一个对象的开销,即可以直接通过类来调用方法。
3.static修饰内部类
一般用的比较少只能用来修饰内部类,一个static修饰的内部类中,不能使用非static修饰的成员变量和方法,这很好理解,因为static修饰的方法是属于类的,如果去直接使用对象的成员变量,它会不知所措(不知该使用哪一个对象的属性)。
4.静态块
class Book{ public Book(String msg) { System.out.println(msg); } } public class Person { Book book1 = new Book("book1成员变量初始化"); static Book book2 = new Book("static成员book2成员变量初始化"); public Person(String msg) { System.out.println(msg); } Book book3 = new Book("book3成员变量初始化"); static Book book4 = new Book("static成员book4成员变量初始化"); public static void funStatic() { System.out.println("static修饰的funStatic方法"); } public static void main(String[] args) { Person.funStatic(); System.out.println("****************"); Person p1 = new Person("p1初始化"); } /**Output * static成员book2成员变量初始化 * static成员book4成员变量初始化 * static修饰的funStatic方法 * *************** * book1成员变量初始化 * book3成员变量初始化 * p1初始化 *///~ }
在上面的例子中我们可以发现两个有意思的地方,第一个是当我们没有创建对象,而是通过类去调用类方法时,尽管该方法没有使用到任何的类成员,类成员还是在方法调用之前就初始化了,这说明,当我们第一次去使用一个类时,就会触发该类的成员初始化。第二个是当我们使用了类方法,完成类的成员的初始化后,再new该类的对象时,static修饰的类成员没有再次初始化,这说明,static修饰的类成员,在程序运行过程中,只需要初始化一次即可,不会进行多次的初始化。
class Book{ public Book(String msg) { System.out.println(msg); } } public class Person { Book book1 = new Book("book1成员变量初始化"); static Book book2; static { book2 = new Book("static成员book2成员变量初始化"); book4 = new Book("static成员book4成员变量初始化"); } public Person(String msg) { System.out.println(msg); } Book book3 = new Book("book3成员变量初始化"); static Book book4; public static void funStatic() { System.out.println("static修饰的funStatic方法"); } public static void main(String[] args) { Person.funStatic(); System.out.println("****************"); Person p1 = new Person("p1初始化"); } /**Output * static成员book2成员变量初始化 * static成员book4成员变量初始化 * static修饰的funStatic方法 * *************** * book1成员变量初始化 * book3成员变量初始化 * p1初始化 *///~ }