Java中的static关键字,初学者使用感受方便的同时相信也犯过各种的错误。下面将从以下几个方面对static关键字进行一个解析。
一:静态属性
二:静态方法
三:静态代码块
四:关于static的一个经典例子。
静态属性
首先应该记住:
静态属性归类所有,而不是归单个对象所有,而一般的属性归单个对象所有。
即静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当类初次被加载的时候初始化。而非静态变量是单个对象所拥有的的,在创建对象的时候被初始化,在内存中存在多个副本。
public class TestStatic {
private static int x;
private static int y;
private int z;
public TestStatic(int x,int y,int z){
this.x=x;
this.y=y;
this.z=z;
}
public static void main(String[]args){
TestStatic t1=new TestStatic(1,1,1);
TestStatic t2=new TestStatic(2,2,2);
TestStatic t3=new TestStatic(3,3,3);
TestStatic t4=new TestStatic(4,4,4);
System.out.println(t1.x+"--"+t1.y+"--"+t1.z);
System.out.println(t2.x+"--"+t2.y+"--"+t2.z);
System.out.println(t3.x+"--"+t3.y+"--"+t3.z);
System.out.println(t4.x+"--"+t4.y+"--"+t4.z);
}
}
x,y为静态的,z为一般的属性。
在创建对象t1的时候,x为1,y为1,z为1
在创建对象t2的时候,因为x,y为静态的,所以即使是一个新的对象,这时候操作的x,y还是以前的那个x,y,此时x,y分别为2,2,而此时z是归t2对象所有,此时t2.z==2;
同理在以下创建对象的时候,则x,y在不停的覆盖,最后
x为4,y为4.
t1.z为1
t2.z为2
t3.z为3
t4.z为4
静态方法
这里应该先指明的是:静态方法中只能调用静态的方法以及属性,而非静态的方法既可以调用普通属性和方法,又可以调用静态的属性和方法。
看下面这段代码:
public class TestStatic02 {
public static int Id;
public static String Name;
public int ClassNumber;
public void print01(){
System.out.println(TestStatic02.Id);
System.out.println(TestStatic02.Name);
System.out.println(this.ClassNumber);
print03();
}
public static void print02(){
System.out.println(TestStatic02.Id);
System.out.println(TestStatic02.Name);
//下面两行代码编译器会报错,而报错的提示是将属性ClassNumber,以及方法print01改为静态static
//这即使上面所说的静态方法中只能调用静态的方法以及属性
System.out.println(TestStatic02.ClassNumber);
print01();
print03();
}
public static void print03(){
}
public static void main(String[]args){}}
静态代码块
使用静态的代码块减少程序的开销以及运行的速度
某一个属性只需要创建依次而被重用多次可以使用静态代码块
看下面代码,观察代码执行的顺序
public class TestStatic03 {
static{
System.out.println("TestStatic03_static_01");
}
public static void main(String[]args){
System.out.println("main");
}
static{
System.out.println("TestStatic03_static_02");
}
static{
System.out.println("TestStatic03_static_03");
}
}
执行的结果为:
TestStatic03_static_01
TestStatic03_static_02
TestStatic03_static_03
main
关于中间是怎么执行的,在最后一个经典例子中会说到。
经典例子
先看一下代码,分析代码执行后的结果:
public class TestStatic06 extends A{
public TestStatic06(){
System.out.println("TestStatic06_constructor");
}
static {
System.out.println("TestStatic06_static01");
}
public static void main(String[] args){
new TestStatic06();
}
static {
System.out.println("TestStatic06_static02");
}
}
class A{
public A(){
System.out.println("A_constructor");
}
static {
System.out.println("A_static");
}
}
一眼看出似乎有点混乱,又是代码块,又是构造函数,还有继承关系。
关于执行的顺序,自己也稍微总结了一下:
1:加载static代码块-(父类static代码块-->子类static代码块)
|
2: 加载主函数(main方法)
|
3: 按顺序执行相应的代码顺序
即程序会找到有主函数的那个类,如果主函数有static代码块,则会先加载静态代码块,在加载静态代码块的时候,如果有继承关系,在应该加载父类中的静态代码块(当然这里父类中静态代码块,如果没有当然就不加载),然后在加载自身的静态代码块,加载完之后程序会找到main方法执行相应的操作。
这里应该注意的就是关于构造函数的执行,简单的说来就是在哪里创建新的对象时候,构造函数调用的时候,先执行父类的,然后在执行子类的。关于这里,之前的一篇有关super的关键字的博客中说到过。
而对于这题,可以看到,在有main方法中的TestStatic06的类中,因为其继承类A,则先加载类A中的静态代码块,打印
A_static
然后加载自身的静态代码块,打印
TestStatic06_static01
TestStatic06_static02
然后加载main方法,创建对象,调用父类构造函数以及自身的构造函数,打印
A_constructor
TestStatic06_constructor