目录(?)[+]

  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. 【MySQL笔记】: unable to connect to remote host. catalog download has failed.

    安装完MySQL之后,它每天凌晨启动一个Intaller任务,甚是烦人:   这是一个Windows的计划服务,在这里删除即可,开始/附件/系统工具/任务计划程序,把mysql的定时任务计划取消/删除 ...

  2. Bean实例化(三种方法)

    (一)构造器实例化Bean 1. Bean1.java package com.inspur.ioc; public class Bean1 { } 2.Beans1.xml <?xml ver ...

  3. 使用virtualenv为应用提供了隔离的Python运行环境

    在开发Python应用程序的时候,系统安装的Python3只有一个版本:3.4.所有第三方的包都会被pip安装到Python3的site-packages目录下. 如果我们要同时开发多个应用程序,那这 ...

  4. 如何提高码农产量,基于ASP.NET MVC的敏捷开发框架之工作流开发随笔三

    前言 “厂长,APP的那几个功能都差不多了,接下来要做工作流,工作流这东西我完全没概念啊.” “查尔斯,一般来说工作流就是指将指定的数据.文件.任务按照预定的规则进行传递流转.比如说你要请假,拿个请假 ...

  5. 【k8s】了解一下k8s

    了解一下k8s 地址: https://www.kubernetes.org.cn/doc-11

  6. Oracle查询库中记录数大于2千万的所有表

    Oracle查询库中记录数大于2千万的所有表 假如当前用户拥有select any table权限,则可以使用下列sql语句: select table_name, num_rows from dba ...

  7. telnet命令

    详细资料 telnet命令使用方法详解-telnet命令怎么用-win7没有telent怎么办 2017年07月26日 15:37:36 阅读数:1010 什么是Telnet? 对于Telnet的认识 ...

  8. vue - check-version

    描述:check-versions.js,vue-cli中检查版本的js文件 使用:

  9. javascript - 闭包以及函数

    /** * 匿名函数 */ (function () { /** * 是否启用跟踪用户隐私 * * 启用:isPrivacys(true) * 不启用:isPrivacys(false) * */ f ...

  10. Unity多媒体展示项目经验分享-ImageEffect+动态绑定

    Unity多媒体展示项目经验分享-ImageEffect+动态绑定+网络通信 <ignore_js_op> “海尔科技展墙”是去年年初我们为上海家电博览会制作的一个多媒体展项,有限的工期以 ...