目录(?)[+]

  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. MSGPACK序列和还原TFDParams

    MSGPACK序列和还原TFDParams unit Unit1; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, S ...

  2. JavaScript数组api简单说明

    1.一个数组加上另一个(一些)数组,不会修改原数组只会返回新数组 arrayObject.concat(arrayX,arrayX,......,arrayX) 2.把数组按照指定字符串分离,不会修改 ...

  3. C#属性和字段区别、get与set用法

    属性和字段的区别 在C#中,我们可以非常自由的.毫无限制的访问公有字段,但在一些场合中,我们可能希望限制只能给字段赋于某个范围的值.或是要求字段只能读或只能写,或是在改变字段时能改变对象的其他一些状态 ...

  4. 轻松学习JavaScript十二:JavaScript基于面向对象之创建对象(二)

    四原型方式 我们创建的每一个函数都有一个通过prototype(原型)属性.这个属性是一个对象,它的用途是包括能够由特定类型 的全部实例共享的属性和方法. 逻辑上能够这么理解:prototypt通过条 ...

  5. Java 多线程之 synchronized 和 volatile 的比較

    概述 在做多线程并发处理时,常常须要对资源进行可见性訪问和相互排斥同步操作.有时候,我们可能从前辈那里得知我们须要对资源进行 volatile 或是 synchronized 关键字修饰处理.但是,我 ...

  6. 【原】Ubuntu下使用teamviewer

    想尝试直接使用Xmanager打开Ubuntu桌面版,结果一直存在问题,迂回一下,尝试使用teamviewer解决,结果OK.办法如下: 在Ubuntu中,下载teamviewer,通过Windows ...

  7. Python学习笔记(五)多进程实现并发服务器

      每创建一个TCP连接,就创建一个进程. 代码如下: # coding: utf-8 import socket import os import sys import signal import ...

  8. linux 修改时间

    实例:设置时间伟2008年8月8号12:00# date -s "2008-08-08 12:00:00"修改完后,记得执行clock -w,把系统时间写入CMOS date -s ...

  9. HDU 4925 Apple Tree(推理)

    HDU 4925 Apple Tree 题目链接 题意:给一个m*n矩阵种树,每一个位置能够选择种树或者施肥,假设种上去的位置就不能施肥,假设施肥则能让周围果树产量乘2.问最大收益 思路:推理得到肯定 ...

  10. Refactoring之——代码的坏味道(四)过长参数列

    1.1.4 Long Parameter List(过长参数列) 特征:一个方法有超过三四个的参数. 问题原因: 过长参数列可能是将多个算法并到一个函数中时发生的.函数中的入参可以用来控制最终选用哪个 ...