目录(?)[+]

  1. 问题1静态变量如何初始化
  2. 问题2JDK如何处理static块
  3. 问题3如何看待静态变量的声明
  4. 对初始问题的解答

在网上看到了下面的一段代码:

  1. public class Test {
  2. static {
  3. _i = 20;
  4. }
  5. public static int _i = 10;
  6. public static void main(String[] args) {
  7. System.out.println(_i);
  8. }
  9. }

public class Test {
static {
_i = 20;
}
public static int _i = 10;

public static void main(String[] args) {
System.out.println(_i);
}
}

上述代码会打印出什么结果来呢?10还是20?本文将以此代码为引子,着重讨论一下静态变量的初始化问题。

问题1:静态变量如何初始化

Java类中可以定义一个static块,用于静态变量的初始化。如:

  1. public class Test {
  2. public static int _i;
  3. static {
  4. _i = 10;
  5. }
  6. }

public class Test {
public static int _i;
static {
_i = 10;
}
}

当然最常用的初始化静态变量的操作是在声明变量时直接进行赋值操作。如:

  1. public class Test {
  2. public static int _i = 10;
  3. }

public class Test {
public static int _i = 10;
}

那么上述两例在本质上有什么区别吗?回答是没有区别。两例代码编译之后的字节码完全一致,通过 “javap -c”查看到的字节码如下:

public class Test extends java.lang.Object{

public static int _i;

public Test();

  Code:

   0: aload_0

   1: invokespecial #1; //Method java/lang/Object."<init>":()V

   4: return

static {};

  Code:

   0: bipush 10

   2: putstatic #2; //Field _i:I

   5: return

}

通过字节码还可以看出,当类的定义中不含有static块时,编译器会为该类提供一个默认的static块。当然这是在含有静态变量初始化操作的前提下。如果静态变量没有初始化操作,则编译器不会为之提供默认的static块。如:

  1. public class Test {
  2. public static int _i;
  3. }

public class Test {
public static int _i;
}

其字节码的表现形式为:

public class Test extends java.lang.Object{

public static int _i;

public Test();

  Code:

   0: aload_0

   1: invokespecial #1; //Method java/lang/Object."<init>":()V

   4: return

}

由于静态变量是通过赋值操作进行初始化的,因此可以通过静态函数返回值的方式为其初始化。如:

  1. public class Test {
  2. public static int _i = init();
  3. private static int init() {
  4. return 10;
  5. }
  6. }

public class Test {
public static int _i = init();

private static int init() {
return 10;
}
}

其本质与下面的代码相同:

  1. public class Test {
  2. public static int _i;
  3. static {
  4. _i = init();
  5. }
  6. private static int init() {
  7. return 10;
  8. }
  9. }

public class Test {
public static int _i;
static {
_i = init();
}

private static int init() {
return 10;
}
}

问题2:JDK如何处理static块

类定义中可以存在多个static块吗?回答是可以。如:

  1. public class Test {
  2. public static int _i;
  3. static {
  4. _i = 10;
  5. }
  6. public static void main(String[] args) {
  7. }
  8. static {
  9. _i = 20;
  10. }
  11. }

public class Test {
public static int _i;
static {
_i = 10;
}

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

static {
_i = 20;
}
}

此类编译之后的字节码为:

public class Test extends java.lang.Object{

public static int _i;

public Test();

  Code:

   0: aload_0

   1: invokespecial #1; //Method java/lang/Object."<init>":()V

   4: return

public static void main(java.lang.String[]);

  Code:

   0: return

static {};

  Code:

   0: bipush 10

   2: putstatic #2; //Field _i:I

   5: bipush 20

   7: putstatic #2; //Field _i:I

   10: return

}

观察static{}部分可以看出,上例的代码与下面的代码效果一致:

  1. public class Test {
  2. public static int _i;
  3. public static void main(String[] args) {
  4. }
  5. static {
  6. _i = 10;
  7. _i = 20;
  8. }
  9. }

public class Test {
public static int _i;

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

static {
_i = 10;
_i = 20;
}
}

此例可以证明,不仅类定义中可以有多个static块,而且在编译时编译器会将多个static块按照代码的前后位置重新组合成一个static块。

问题3:如何看待静态变量的声明

静态变量存放在常量池之中。如何证明呢?如:

  1. public class Test {
  2. public static int _i = 10;
  3. }

public class Test {
public static int _i = 10;
}

使用“javap -c -verbose”查看其字节码的内容如下:

public class Test extends java.lang.Object

  SourceFile: "Test.java"

  minor version: 0

  major version: 49

  Constant pool:

const #1 = Method #4.#14; //  java/lang/Object."<init>":()V

const #2 = Field #3.#15; //  Test._i:I

const #3 = class #16; //  Test

const #4 = class #17; //  java/lang/Object

const #5 = Asciz _i;

const #6 = Asciz I;

const #7 = Asciz <init>;

const #8 = Asciz ()V;

const #9 = Asciz Code;

const #10 = Asciz LineNumberTable;

const #11 = Asciz <clinit>;

const #12 = Asciz SourceFile;

const #13 = Asciz Test.java;

const #14 = NameAndType #7:#8;//  "<init>":()V

const #15 = NameAndType #5:#6;//  _i:I

const #16 = Asciz Test;

const #17 = Asciz java/lang/Object;

{

public static int _i;

public Test();

  Code:

   Stack=1, Locals=1, Args_size=1

   0: aload_0

   1: invokespecial #1; //Method java/lang/Object."<init>":()V

   4: return

  LineNumberTable:

   line 2: 0

static {};

  Code:

   Stack=1, Locals=0, Args_size=0

   0: bipush 10

   2: putstatic #2; //Field _i:I

   5: return

  LineNumberTable:

   line 3: 0

}

我们看到,常量池中const #2指向的就是Test._i,也就是静态变量。静态变量被保存到常量池中的工作原理这里不深入讨论。在此需要注意的是:

  • 静态变量的声明与初始化是两个不同的操作;
  • 静态变量的声明在编译时已经明确了内存的位置。

如:

  1. public class Test {
  2. public static int _i = 10;
  3. }

public class Test {
public static int _i = 10;
}

上述代码的本质可以视为:

  1. public class Test {
  2. // 静态变量的声明
  3. public static int _i;
  4. // 静态变量的初始化
  5. static {
  6. _i = 10;
  7. }
  8. }

public class Test {
// 静态变量的声明
public static int _i;

// 静态变量的初始化
static {
_i = 10;
}
}

由于静态变量的声明在编译时已经明确,所以静态变量的声明与初始化在编码顺序上可以颠倒。也就是说可以先编写初始化的代码,再编写声明代码。如:

  1. public class Test {
  2. // 静态变量的初始化
  3. static {
  4. _i = 10;
  5. }
  6. // 静态变量的声明
  7. public static int _i;
  8. }

public class Test {
// 静态变量的初始化
static {
_i = 10;
}

// 静态变量的声明
public static int _i;
}

对初始问题的解答

解答了上述三个问题,让我们再来看看开篇提到的问题。代码如下:

  1. public class Test {
  2. static {
  3. _i = 20;
  4. }
  5. public static int _i = 10;
  6. public static void main(String[] args) {
  7. System.out.println(_i);
  8. }
  9. }

public class Test {
static {
_i = 20;
}
public static int _i = 10;

public static void main(String[] args) {
System.out.println(_i);
}
}

其本质可以用下面的代码表示:

  1. public class Test {
  2. static {
  3. _i = 20;
  4. }
  5. public static int _i;
  6. static {
  7. _i = 10;
  8. }
  9. public static void main(String[] args) {
  10. System.out.println(_i);
  11. }
  12. }

public class Test {
static {
_i = 20;
}
public static int _i;
static {
_i = 10;
}

public static void main(String[] args) {
System.out.println(_i);
}
}

再简化一下,可以表示为:

  1. public class Test {
  2. public static int _i;
  3. static {
  4. _i = 20;
  5. _i = 10;
  6. }
  7. public static void main(String[] args) {
  8. System.out.println(_i);
  9. }
  10. }

public class Test {
public static int _i;

static {
_i = 20;
_i = 10;
}

public static void main(String[] args) {
System.out.println(_i);
}
}

至此,代码已经明确告诉我们打印结果是什么了!

本文转自:http://blog.csdn.net/darxin/article/details/5293427

java中static变量的声明和初始化的更多相关文章

  1. java中static变量和方法的总结

    转自:http://blog.csdn.net/haobo920/article/details/5921621 java中static变量和方法的总结 java中一切皆是对象 一个类中对象的定义一般 ...

  2. Java中static变量作用和用法详解

    static表示"全局"或者"静态"的意思,用来修饰成员变量和成员方法,也可以形成静态static代码块,但是Java语言中没有全局变量的概念. 被static ...

  3. C#中对于变量的声明和初始化

    C#变量初始化是C#强调安全性的另一个例子.简单地说,C#编译器需要用某个初始值对变量进行初始化,之后才能在操作中引用该变量.大多数现代编译器把没有初始化标记为警告,但C#编译器把它当作错误来看待. ...

  4. Java中静态变量的声明位置

    Java中静态变量只能是成员变量,局部方法中的局部变量除final外不能有任何其他修饰符,例如: public class Test { static String x = "1" ...

  5. C++中类中常规变量、const、static、static const(const static)成员变量的声明和初始化

    C++类有几种类型的数据成员:普通类型.常量(const).静态(static).静态常量(static const).这里分别探讨以下他们在C++11之前和之后的初始化方式. c++11之前版本的初 ...

  6. Java中static的用法,初始化块

    使用 Arrays 类操作 Java 中的数组语法: Arrays.sort(数组名); 可以使用 sort( ) 方法实现对数组的排序,只要将数组名放在 sort( ) 方法的括号中,就可以完成对该 ...

  7. c++ 类与函数中static变量初始化问题(转)

    首先static变量只有一次初始化,不管在类中还是在函数中..有这样一个函数: void Foo() { ; // initialize std::cout << a; a++; } 里的 ...

  8. java中static作用详解

    static表示“全局”或者“静态”的意思,用来修饰成员变量和成员方法,也可以形成静态static代码块,但是Java语言中没有全局变量的概念. 被static修饰的成员变量和成员方法独立于该类的任何 ...

  9. Java中static作用及用法详解(转)

    1.1概述: static是静态修饰符,什么叫静态修饰符呢?大家都知道,在程序中任何变量或者代码都是在编译时由系统自动分配内存来存储的,而所谓静态就是指在编译后所分配的内存会一直存在,直到程序退出内存 ...

随机推荐

  1. 【最短路】【spfa】小vijos P1447 Updown

    小vijos P1447 Updown 背景 开启了升降梯的动力之后,探险队员们进入了升降梯运行的那条竖直的隧道,映入眼帘的是一条直通塔顶的轨道.一辆停在轨道底部的电梯.和电梯内一杆控制电梯升降的巨大 ...

  2. Java高级架构师(一)第03节:多模块多Web应用合并War包

    多模块.多Web应用合并war包 在日常的系统开发中,如果担心各个系统的资源同名覆盖,可以在总的War模块下放置一份最终的资源. 将版本号改成9.1.0.v20131115,ok 在Idea中的Mav ...

  3. 移动端与PHP服务端接口通信流程设计(增强版)

    前面讲过:移动端与PHP服务端接口通信流程设计(基础版) 对于 api_token 的校验,其安全性还可再增强: 增强地方一: 再增加2张表,一个接口表,一个授权表,设计参考如下: 接口表 字段名 字 ...

  4. 如何订阅Linux相关的邮件列表

    转:http://blog.163.com/sunshine_linting/blog/static/44893323201282114012845/ 1.google"linux kern ...

  5. 【java】File的使用:将字符串写出到本地文件,大小0kb的原因

    实现方法: 暂时写一种方法,将字符串写出到本地文件,以后可以补充更多种方法: public static void main(String[] args) { /** * ============== ...

  6. C++之类静态成员变量和静态成员函数

    1.静态成员变量必须在类外初始化 2.静态成员变量在类中不占内存字节: 3.静态成员变量,被所有的类对象共 享 class G {public: static int a; //静态成员变量 int ...

  7. 使用unity3d开发app

    做过一些项目,参入过一些项目的计划安排.总觉得一些工具用起来很麻烦,要么是要收费,要么很大很重.没有针对小团队的简单易用的任务管理工具,也可能是找了些不能适合自己的习惯. 所有准备开始自己开发一款项目 ...

  8. http://www.cnblogs.com/langtianya/archive/2013/02/01/2889682.html

    http://www.cnblogs.com/langtianya/archive/2013/02/01/2889682.html

  9. 【RabbitMQ 参考资料】

    RabbitMQ从入门到精通:http://blog.csdn.net/column/details/rabbitmq.html RabbitMQ消息队列(一): Detailed Introduct ...

  10. python连接mysql并插入数据(自用)

    #coding=utf-8 import MySQLdb db = MySQLdb.connect("IP","用户名","密码",&quo ...