最近项目经理要我整理一下java基础,找个重点讲一下,刚好最近在研究关于静态static的东西,所以拿出来先分享一下。
首先讲一下java中有关静态代码块、静态变量、构造代码块、构造方法的定义。
静态代码块:直接在类中定义的并且加了static关键字的代码块{},静态代码块是在类加载时自动执行的一段语句。形如:
public class Test
{
static {
//.......
}
}
静态变量:被static修饰的变量。形如:
static int num = 1;构造代码块:直接在类中定义的没有加static关键字的代码块{}。
public class Test
{
{
//.......
}
}
构造方法:
public class Test然后开始我们的实验。
{
Test()
{
//无参构造方法
}
Test(int i)
{
//有参构造方法
}
}
1、对于一个类里的静态变量和静态代码块,执行的顺序是谁在前先执行谁。
测试代码:
Test4.java
public class Test4
{
public static int no = 1;
public static int num1 = 6;
static {
if(num1 == 6)
System.out.println("第" + (no++) + "次加载静态变量");
System.out.println("第" + (no++) + "次加载静态代码块");
}
public static int num2 = 10;
static {
if(num2 == 10)
System.out.println("第" + (no++) + "次加载静态变量");
System.out.println("第" + (no++) + "次加载静态代码块");
}
public static void main(String args[])
{
new Test4();
}
}
测试结果:
第1次加载静态变量
第2次加载静态代码块
第3次加载静态变量
第4次加载静态代码块
2、对于一个类里的静态代码块(静态变量)、构造代码块、构造方法的执行顺序为:
静态代码块(静态变量)>实例化[构造代码块]>实例化[构造方法] 测试代码: Test5.java
public class Test5测试结果:
{
public static int no = 1;
{
System.out.println("第" + (no++) + "次加载构造代码块");
}
static {
System.out.println("第" + (no++) + "次加载静态代码块");
}
Test5()
{
System.out.println("第" + (no++) + "次加载构造方法");
}
public static void main(String args[])
{
new Test5();
}
}
第1次加载静态代码块
第2次加载构造代码块
第3次加载构造方法
3、对于有继承关系的父类子类中的静态代码块(静态变量)、构造代码块、构造方法的执行顺序为:
父类静态代码块(静态变量)>子类的静态代码块(静态变量)>父类构造代码块>父类构造方法>子类构造代码块>子类构造方法 测试代码: TestParent.java
public class TestParentTestChild.java
{
{
System.out.println("这是父类的构造代码块");
}
static {
System.out.println("这是父类的静态代码块");
}
TestParent()
{
System.out.println("这是父类的构造方法");
}
}
public class TestChild extends TestParentTest6.java
{
{
System.out.println("这是子类的构造代码块");
}
static {
System.out.println("这是子类的静态代码块");
}
TestChild()
{
System.out.println("这是子类的构造方法");
}
}
public class Test6测试结果:
{
public static void main(String args[])
{
new TestChild();
}
}
这是父类的静态代码块其中静态代码块(静态变量)和静态方法的区别在于静态代码块是自动执行,而静态方法则是被调用的时候才执行。
这是子类的静态代码块
这是父类的构造代码块
这是父类的构造方法
这是子类的构造代码块
这是子类的构造方法
4、对于静态变量和静态代码块,自动执行且只执行一次。
构造代码块和构造方法是实例化几次执行几次。注意是实例化,不是声明,声明不会执行构造代码块和构造方法 测试代码: Test7.java
public class Test7测试结果:
{
public static int no = 1;
static {
if(no == 1)
System.out.println("第" + (no++) + "次加载静态变量");
System.out.println("第" + (no++) + "次加载静态代码块");
}
{
System.out.println("第" + (no++) + "次加载构造代码块");
}
Test7()
{
System.out.println("第" + (no++) + "次加载构造方法");
}
public static void main(String args[])
{
new Test7();
new Test7();
}
}
第1次加载静态变量
第2次加载静态代码块
第3次加载构造代码块
第4次加载构造方法
第5次加载构造代码块
第6次加载构造方法
特殊情况1、当类里面声明并实例化一个自己的静态类,对于当前的静态变量而言,如果静态变量在静态类之前,则先执行静态变量;如果静态变量在静态类之后,则绕开静态变量,执行静态类里面的构造代码块和构造方法:
静态类[构造代码块]>静态类[构造方法]>当前类[静态代码块(静态变量)]>实例化[构造代码块]>实例化[构造方法] 测试代码: Test8.java
public class Test8测试结果:
{
public static int no = 1;
public static Test8 t8 = new Test8();
static {
if(no == 1)
System.out.println("第" + (no++) + "次加载静态变量");
System.out.println("第" + (no++) + "次加载静态代码块");
}
{
System.out.println("第" + (no++) + "次加载构造代码块");
}
Test8()
{
System.out.println("第" + (no++) + "次加载构造方法");
}
public static void main(String args[])
{
new Test8();
}
}
第1次加载构造代码块
第2次加载构造方法
第3次加载静态代码块
第4次加载构造代码块
第5次加载构造方法
Test8.java(1)
public class Test8测试结果:
{
public static Test8 t8 = new Test8();
public static int no = 1;
static {
if(no == 1)
System.out.println("第" + (no++) + "次加载静态变量");
System.out.println("第" + (no++) + "次加载静态代码块");
}
{
System.out.println("第" + (no++) + "次加载构造代码块" );
}
Test8()
{
System.out.println("第" + (no++) + "次加载构造方法");
}
public static void main(String args[])
{
new Test8();
}
}
第0次加载构造代码块
第1次加载构造方法
第1次加载静态变量
第2次加载静态代码块
第3次加载构造代码块
第4次加载构造方法
注意:这里之所以加载构造代码块的序号为0,是因为静态变量在静态类之后,而在静态类执行的时候,是不执行静态变量、静态代码块的,但是,会对静态变量进行声明,虽然能访问到no这个变量,但是没有初始化,所以为0。当加载构造方法时因为 no自增加了1,所以值变为1。但是当执行完静态类以后,no变量又被初始化为1。这也进一步验证了静态变量、静态代码块只执行一次。
特殊情况2、当类里面声明并实例化一个具有父类的自己的静态类,则该类的执行顺序为:
父类的静态代码块(静态变量)>父类的构造代码块>父类的构造方法>子类的构造代码块>子类的构造方法>子类的静态代码块(静态变量) 测试代码: TestParent.java
public class TestParentTestChild1.java
{
{
System.out.println("这是父类的构造代码块");
}
static {
System.out.println("这是父类的静态代码块");
}
TestParent()
{
System.out.println("这是父类的构造方法");
}
}
public class TestChild1 extends TestParent测试结果:
{
public static TestChild1 tc1 = new TestChild1();
{
System.out.println("这是子类的构造代码块");
}
static {
System.out.println("这是子类的静态代码块");
}
TestChild1()
{
System.out.println("这是子类的构造方法");
}
public static void main(String args[])
{
}
}
这是父类的静态代码块
这是父类的构造代码块
这是父类的构造方法
这是子类的构造代码块
这是子类的构造方法
这是子类的静态代码块
特殊情况3、当类里面声明并实例化一个不是当前类的静态类,则该类的执行顺序为:
静态类[静态代码块(静态变量)]>静态类[构造代码块]>静态类[构造方法]>当前类[静态代码块(静态变量)] 测试代码: Test9.java
public class Test9 {Test10.java
public static Test10 t10;
public static Test10 t11 = new Test10();
public static int no = 1;
static {
if(no == 1)
System.out.println("第" + (no++) + "次加载静态变量");
System.out.println("第" + (no++) + "次加载静态代码块");
}
{
System.out.println("第" + (no++) + "次加载构造代码块");
}
Test9()
{
System.out.println("第" + (no++) + "次加载构造方法");
}
public static void main(String args[])
{
}
}
public class Test10测试结果:
{
public static int no = 1;
static {
if(no == 1)
System.out.println("第" + (no++) + "次加载静态变量");
System.out.println("第" + (no++) + "次加载静态代码块");
}
{
System.out.println("第" + (no++) + "次加载构造代码块");
}
Test10()
{
System.out.println("第" + (no++) + "次加载构造方法");
}
}
第1次加载静态变量这里可以看出,如果实例化的静态类不是当前类,那么该静态类的执行顺序还是正常的,也就是说不会绕开静态代码块和静态变量。注意,这里如果没有实例化而只是声明了静态类,那么该静态类不会执行任何语句。
第2次加载静态代码块
第3次加载构造代码块
第4次加载构造方法
第1次加载静态变量
第2次加载静态代码块
总结: 1、不考虑特殊情况,类的执行顺序为静态代码块(静态变量)>构造代码块>构造方法 2、特殊情况中,在类里面实例化自己的静态类这种情况在实际编码中极少出现,所以也不太需要考虑这种情况。