`

JAVA 静态变量

    博客分类:
  • java
阅读更多
个人的总结
1 静态变量只有一份被类的所有实例共享
2 静态变量的声明在编译时已经明确了内存的位置
3 延迟初始化是改变静态变量的值

引用

Java静态变量的初始化(static块的本质)

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

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

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.       
   4.     private static int init() {  
   5.         return 10;  
   6.      }  
   7. } 

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.       
   7.     private static int init() {  
   8.         return 10;  
   9.      }  
  10. } 

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.       
   7.     public static void main(String[] args) {  
   8.      }  
   9.       
  10.     static {  
  11.          _i = 20;  
  12.      }  
  13. } 

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.       
   4.     public static void main(String[] args) {  
   5.      }  
   6.       
   7.     static {  
   8.          _i = 10;  
   9.          _i = 20;  
  10.      }  
  11. } 

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.     // 静态变量的初始化  
   6.     static {  
   7.          _i = 10;  
   8.      }  
   9. } 

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

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

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

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.       
   7.     public static void main(String[] args) {  
   8.          System.out.println(_i);  
   9.      }  
  10. } 

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.       
  10.     public static void main(String[] args) {  
  11.          System.out.println(_i);  
  12.      }  
  13. } 

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.       
   4.     static {  
   5.          _i = 20;  
   6.          _i = 10;  
   7.      }  
   8.       
   9.     public static void main(String[] args) {  
  10.          System.out.println(_i);  
  11.      }  
  12. } 

public class Test { public static int _i; static { _i = 20; _i = 10; } public static void main(String[] args) { System.out.println(_i); } }

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

分享到:
评论

相关推荐

    static 静态变量加载

    statilocal java 静态变量和静态块加载顺序,静态变量和同名静态变量块加载的顺序。

    springboot 使用yml配置文件给静态变量赋值教程

    由于我们有一个静态工具类`ClickHouseUtil`,其中的方法依赖于静态变量,所以我们不能直接使用`@Value`注解为静态变量赋值。Spring框架不允许在静态字段上直接注入值,因为它不支持实例化静态对象。 为了解决这个...

    java静态变量与方法.doc

    Java中的静态变量和静态方法是面向对象编程中的关键概念,它们与实例变量和实例方法有着显著的区别。在Java中,`static`关键字用于声明类级别的成员,而不是对象级别的成员。 **一、静态成员变量与实例成员变量的...

    基于Java class对象说明、Java 静态变量声明和赋值说明(详解)

    "Java Class 对象、静态变量声明和赋值详解" Java 中的 Class 对象是 Java 语言中非常重要的一部分,它代表了 Java 应用程序中的类和接口。每个类和接口都可以用 Class 对象来表示,包括枚举和注解。甚至数组也可以...

    Java中的静态变量静态方法静态块与静态类.docx

    Java 中的静态变量、静态方法、静态块和静态类 Java 中的静态变量、静态方法、静态块和静态类是 Java 编程语言的四个重要概念,它们之间存在着紧密的关系。下面将对这四个概念进行详细的介绍。 一、静态变量...

    static静态变量使用@Value注入方式.md

    在Java中,可以通过类名直接访问静态变量而无需创建类的实例。例如: ```java public class MyClass { public static String myVar = "Hello World"; } // 访问静态变量 System.out.println(MyClass.myVar); ``` ...

    java中静态与非静态的变量定义与使用

    总结起来,Java 中的静态变量与非静态变量之间的主要区别在于它们的生命周期、存储位置以及访问方式。静态变量是类级别的,与对象无关,而实例变量是对象级别的,每个对象都有自己的一份副本。理解这两种变量类型...

    Java中static静态变量的初始化完全解析

    Java中的静态变量(static变量)是在类加载时初始化的,而不是在对象创建时。静态变量属于类,而不属于任何特定的对象,因此它们是共享的,所有类实例都可以访问。了解静态变量的初始化顺序对于理解和避免潜在的编程...

    静态变量的导入以及集合的运用

    在Java编程语言中,静态变量和集合是两个非常重要的概念。本文将详细介绍如何导入静态变量,并深入探讨集合类的使用方法及其实例。 #### 一、静态变量的导入 静态变量(也称为类变量)是在类的生命周期内共享的...

    Java成员变量局部变量静态变量成员方法全局方法等概念的区

    以下是关于Java成员变量、局部变量、静态变量以及成员方法和全局方法的基本概念和区别的详细阐述。 1. 成员变量(Member Variables): 成员变量是类的一部分,它们定义了对象的状态。它们可以是实例变量(非静态...

    JAVA语言静态变量和静态方法的分析及其应用研究.pdf

    JAVA语言静态变量和静态方法的分析及其应用研究.pdf

    静态变量和非静态变量的区别.xmind

    静态变量和非静态变量的区别

    JAVAStatic静态变量.doc

    Java中的`static`关键字是用来声明静态变量或静态方法的,它是面向对象编程中非常关键的一个概念。静态变量属于类,而不是类的实例,这意味着它们在内存中只有一份拷贝,所有的类实例共享这个相同的变量。当我们声明...

    java-静态变量方法块

    在Java编程语言中,静态变量、静态方法、静态代码块以及非静态变量和非静态方法是核心概念,它们对于理解和编写Java程序至关重要。下面将详细解释这些概念,并通过实例程序来阐述它们的使用和内存管理。 首先,静态...

    关于Java静态成员变量和静态初始化块等的初始化顺序的详细介绍

    详细介绍了Java的静态成员变量、静态数据块、非静态成员变量和非静态成员变量等初始化顺序

    静态变量与非静态变量获取配置文件application中变量值的区别.docx

    静态变量与非静态变量获取配置文件application中变量值的区别 在 SpringBoot 应用程序中,经常会遇到获取配置文件中的变量值,并将其赋值给变量的情况。在这种情况下,静态变量和非静态变量的使用会出现一些问题。...

    java 利用反射获取内部类静态成员变量的值操作.docx

    ### Java 利用反射获取内部类静态成员变量的值操作 #### 一、引言 在Java编程中,反射是一个非常强大的工具,它允许程序在运行时检查类、接口、字段和方法的信息,并且能够创建和操作对象。本文将详细介绍如何使用...

    19.静态变量.zip

    19.静态变量.zip19.静态变量.zip19.静态变量.zip19.静态变量.zip19.静态变量.zip19.静态变量.zip19.静态变量.zip19.静态变量.zip19.静态变量.zip19.静态变量.zip19.静态变量.zip19.静态变量.zip19.静态变量.zip19....

    android jni使用static变量

    这样,你就成功地在Android JNI中实现了对Java静态变量的访问。 总结来说,通过JNI,我们可以轻松地在Android应用的Java层和原生代码之间共享静态变量,实现跨层通信。这种方法在处理性能敏感的场景,如图像处理、...

Global site tag (gtag.js) - Google Analytics