jvm理论:(三/7)关于类变量成员变量局部变量的案例总结(代码片段)

zjxiang zjxiang     2022-12-18     699

关键词:

一、类变量、成员变量、局部变量的内存分布

  结合前文,对类变量、成员变量、局部变量三种变量的内存分布进行总结

1)类变量:方法区。静态变量随类加载到方法区中。方法区中存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。线程共享。

2)成员变量:堆。从父类继承下来或在子类中定义的各种类型的字段内容都存在对象的实例数据里,成员变量会在对象实例化时随着对象一起分配在Java堆中。线程共享。

3)局部变量:在栈帧里。一个方法对应一个栈帧,局部变量表占多大空间,在编译期存到Code属性里,局部变量需要赋初始值。结合操作数栈,操作数栈相当于是局部变量进行运算的场所。线程私有。

4)数组:堆。数组也是一个对象。https://www.cnblogs.com/duanxz/p/6102583.html

5)字符串常量 :运行时常量池。运行时常量池是方法区的一部分。常量池是Class文件里的结构,会在类加载后进入方法区的运行时常量池中存放。

 

二、类初始化顺序

回顾一下前面的知识:

对于初始化阶段,虚拟机规范则是严格规定了有且只有5种情况必须立即对类进行“初始化”(而加载、验证、准备自然需要在此之前开始):

1)使用new关键字实例化对象的时候、读取或设置一个类的静态字段的时候(被final修饰、已在编译期把结果放入常量池的静态字段除外)、调用一个类的静态方法的时候。(new、getstatic、putstatic、invokestatic)

2)使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。

3)当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。

4)当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类。

5)当使用JDK 1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。

 

类初始化的顺序如下:

  父类——静态块、初始化静态变量(静态块和静态变量根据代码先后顺序,只执行一次
    子类——静态块、初始化静态变量(静态块和静态变量根据代码先后顺序,只执行一次
  父类——非静态块、初始化非静态变量(也根据代码先后顺序)
  父类——构造方法
    子类——非静态块、初始化非静态变量(也根据代码先后顺序)
    子类——构造方法

 

//案例1 —— 针对初始化的时机
public class StaticTest   
    public static int k = 0;  
    public static StaticTest t1 = new StaticTest("t1");  
    public static StaticTest t2 = new StaticTest("t2");  
    public static int i = print("i");  
    public static int n = 99;  
    public int j = print("j");        
      
        print("构造块");  
        
    static  
        print("静态块");  
      
    public StaticTest(String str)   
        System.out.println((++k) + ":" + str + " i=" + i + " n=" + n);  
        ++n;  
        ++i;  
            
    public static int print(String str)   
        System.out.println((++k) + ":" + str + " i=" + i + " n=" + n);  
        ++i;  
        return ++n;  
         
    public static void main(String[] args)   
        StaticTest t = new StaticTest("init");  
      


输出结果:
//要执行main方法前,会先初始化main方法所在的类StaticTest,对于Java来说,一旦开始初始化静态部分,无论是否完成,后续都不会再重新触发静态初始化流程了,类的静态初始化只会进行一次。下面一条一条对输出结果进行分析。

1:j i=0 n=0     
2:构造块 i=1 n=1
3:t1 i=2 n=2
//前3句一起分析,要执行main方法前,会先初始化main方法所在的类StaticTest,按顺序从上到下开始初始化静态变量,但是当执行到 public static StaticTest t1 = new StaticTest("t1"); 时发现需要先创建一个对象,
//这里虽然StaticTest类的初始化还未完成,但也不会再重新触发一次,不会再次初始化静态变量,所以直接开始初始化成员变量,直接执行 public int j = print("j"); ,所以第一句打印的是与 j 相关的,继续初始化对象,
//接着执行构造块和构造方法。值得注意的是,这时StaticTest类的静态变量i,n都还没执行到赋值语句,所以这时都还是系统的零值,为0。

4:j i=3 n=3
5:构造块 i=4 n=4
6:t2 i=5 n=5
//4-6句与第一部分类似,创建另一个对象t2,这时也还没有执行到静态变量i,n的赋值语句。

7:i i=6 n=6
//开始执行静态变量i的赋值语句 public static int i = print("i"); ,会把n的值7返回给i,值得注意的是这里给i赋值后,前面不管i的值是什么都会变成新值。

8:静态块 i=7 n=99
//public static int n = 99; ,不管n之前的值为多少,这里初始化后的n为99,此时已经完成对类StaticTest静态变量的初始化,这句话是执行静态代码块时打印的。

9:j i=8 n=100
10:构造块 i=9 n=101
11:init i=10 n=102
//以上已经完成在执行main前对main方法所在类StaticTest的静态初始化,此时开始执行main方法里的语句StaticTest t = new StaticTest("init");,同样以成员变量,构造块,构造函数的顺序执行。

 

//案例2 —— 针对构造方法的选择
public class SonClass extends ParentClass
  ParentClass parentClass;
  public SonClass()
    System.out.println("1");
  
  public SonClass(String name)
    System.out.println("2");
    this.name = name;
    parentClass = new ParentClass("FEI");
  
  public static void main(String[] args) 
    System.out.println("------ main start ------ ");
    new SonClass("fei");
    System.out.println("------ main end ------ ");
  

 
public class ParentClass
  String name ;
  public ParentClass()
    System.out.println("3");
  
  public ParentClass(String name)
    System.out.println("4");
    this.name = name ;
  


//如果子类没有显示地调用父类构造函数,不管子类是否存在带参数的构造方法都默认调用父类无参的构造函数,若父类没有无参的构造方法则编译出错。
输出结果:
------ main start ------ 
3        //调用子类构造方法SonClass(String name)前先调父类的构造,且没有显示调用父类构造方法,所以调的是ParentClass()
2        //继续执行子类构造方法SonClass(String name)
4        //执行到parentClass = new ParentClass("FEI");,会调用父类中带参的构造方法ParentClass(String name)
------ main end ------ 

 

 三、静态方法不能直接调用实例方法及成员变量

  实例方法内部可以直接使用静态方法,但是静态方法中则无法直接调用实例方法,也无法直接访问成员变量,编译会无法通过。

  通过Class文件中方法表里标识access_flags能知道这个方法是静态方法还是实例方法。在类加载的时候,就为静态方法分配了入口地址,而实例方法,只有在对象创建后才被分配入口地址。

  当我们创建第一个对象时,实例方法就分配了入口地址,当再创建对象时,不再分配入口地址,也就是说,方法的入口地址被所有的对象共享,当所有的对象都不存在时,实例方法的入口地址才被取消。

public class TestClass 
   private static void testMethod()
        System.out.println("testMethod");
   
   public static void main(String[] args) 
        ((TestClass)null).testMethod();
   


输出结果:    
testMethod

//1)此处是类对方法的调用,不是对象对方法的调用。
//2)null可以被强制类型转换成任意类型(不是任意类型对象),于是可以通过它来执行静态方法。
//3)若将testMethod()方法前的static去掉,变成实例方法,必须依赖对象被创建后才能使用,会报空指针异常 。

补充一下关于null的知识:
  1)null可以被强制转换为任何引用类型。
  2)任何含有null值的包装类在自动拆箱成基本数据类型时都会抛出一个空指针异常。
  3)不能用一个值为null的引用类型变量来调用非静态方法,这样会抛出空指针异常,但是静态方法可以被一个值为null的引用类型变量调用而不会抛出空指针异常。

 

*注意static关键字所导致的内存泄漏问题

 

 

 https://blog.csdn.net/itm_hadf/article/details/7519598

 https://www.cnblogs.com/jichen/p/8522205.html

https://blog.csdn.net/weixin_35756522/article/details/77684031

 https://www.jb51.net/article/115613.htm

 https://blog.csdn.net/u013182381/article/details/74574278

https://blog.csdn.net/u013256816/article/details/50837863

http://www.importnew.com/18566.html

https://blog.csdn.net/lvsongsng91/article/details/52511724

 https://www.cnblogs.com/duanxz/p/6102583.html









虚拟机内存结构以及虚拟机中销毁和新建对象(代码片段)

...一、前言二、JVM的内存结构2.1对象、成员变量和局部变量关于对象和变量在JVM中的存储,我们需要搞懂三个问题,分别是:(1)对象:一个对象在哪里?一个对象的引用放在哪里?(2)成员变量:一个成员... 查看详情

java局部变量与成员变量(代码片段)

成员变量:a局部变量:ia可以直接声明变量不用初始化值,因为在初始化类的时候jvm会初始化a;i不可以不初始化而进行运算,javac强制规定如此,局部变量在调用的时候是可以确定该变量的值的,所以... 查看详情

成员变量类变量局部变量的区别

一:成员变量和局部变量的区别1、范围:员变量定义在类中,在整个类中都可以被访问。局部变量定义在局部范围内,如:函数内,语句内等。2、存储:成员变量随着对象的建立而建立,随着对象的消失而消失,存在于对象所... 查看详情

成员变量局部变量类变量

成员变量和局部变量的区别    成员变量:     1、成员变量定义在类中,在整个类中都可以被访问。     2、成员变量随着对象的建立而建立,随着对象的消失而消失,存在于对... 查看详情

java当中成员变量和局部变量的区别

1:成员变量定义在类中,整个类中都可以访问.2:局部变量定义在函数,语句,局部代码块中,只在所属的区域有效.3:成员变量存在于堆内存的对象中.4:局部变量存在于栈内存的方法中.5:成员变量随着对象的创建而存在,随着对象的小事... 查看详情

jvm学习--局部变量表

 变量分类:1)基本数据类型 、引用数据类型2)成员变量(在使用前,都经历过默认初始化值):类变量(liking的prepare阶段给类变量默认赋值,在initial阶段,给类变量显示赋值及静态代码块赋值)、实例变量(随着对... 查看详情

成员变量类变量局部变量的区别

变量名首写字母使用小写,如果由多个单词组成,从第2个单词开始的其他单词的首写字母使用大写。如果局部变量的名字和成员变量的名字相同,要想在该方法中使用成员变量,必须使用关键字this[java]viewplaincopyclassPeople{ &nb... 查看详情

成员变量跟局部变量

publicclassVariables{ /* *成员变量跟局部变量的特点 *成员变量:定义类中有什么,在类中定义,在整个类中都是可见的 *JAVa会给成员变量赋一个初始值 *局部变量:类的方法中定义,用来临时保存数据,仅限于定义它的方法 *JAVA不会... 查看详情

成员变量(实例变量)&局部变量&静态变量(类变量)的区别

成员变量(实例变量)&局部变量区别:(1)作用域 成员变量:针对整个类有效。 局部变量:只在某个范围内有效。(一般指的就是方法,语句体内) (2)存储位置 成员变量:随着对象的创建而存在,随着对象的消失而消失,存储... 查看详情

成员变量局部变量

成员变量:作为类的成员而存在,直接存在于类中。所有类的成员变量可以通过this来引用。局部变量:作为方法或语句块的成员而存在,存在于方法的参数列表和方法定义中。 1.成员变量可以被public,protect,private,static等... 查看详情

记录java学习的历程之关于局部变量与成员变量

 最近一段时间开始学习JAVA,遇到了不少问题。写下这些东西,纯粹为了记录自己的学习历程,借此激励自己,希望能在程序员的路上越走越远。 在JAVA中,我们会遇到局部变量与成员变量这两个变量的分类,这是按声明... 查看详情

成员变量和局部变量

成员变量是在类范围内定义的变量;局部变量是在一个方法内定义的变量; 成员变量可以分为:实例属性(不用static修饰)随着实例属性的存在而存在;类属性(static修饰)随着类的存在而存在;成员变量无需显式初始化,... 查看详情

成员变量类变量局部变量的区别

变量名首写字母使用小写,如果由多个单词组成,从第2个单词开始的其他单词的首写字母使用大写。如果局部变量的名字和成员变量的名字相同,要想在该方法中使用成员变量,必须使用关键字this [java] viewplain copy&nb... 查看详情

面向对象

...性取值,只有具体的对象才能决定属性取值  成员变量和局部变量:局部变量成员变量定义在方法体中的变量定义在方法体之外储存在栈帧中     成员方法:只要把我们之前的写的方法中的static修饰符去... 查看详情

成员变量与局部变量

成员变量:在类中定义,用来描述对象将要有什么;可以由本类中的方法调用;其他类的方法也可以调用;局部变量:在类的方法中定义,在方法中临时保存数据;只有它本身的方法可以调用;成员变量与局部变量的区别:作用... 查看详情

成员变量和局部变量

含义:  成员变量:定义在类(直接)中的变量  局部变量:定义在方法中的变量区别  a.作用域不同    成员变量的作用域在整个类的内部都是可见的    局部变量的作用域仅限于定义它的方法中  b.初始值... 查看详情

成员变量和局部变量

成员变量和局部变量: 变量声明的位置决定变量作用域 变量作用域确定可在程序中按变量名访问该变量的区域 成员变量: 定义在类中的变量(类或其他类中可以调用)局部变量: 定义在方法中的变量(方法... 查看详情

局部变量和成员变量的区别

局部变量和成员变量的区别:1.定义的位置不一样【重点】局部变量:在方法的内部成员变量:在方法的外部,直接写在类当中2.作用范围不一样【重点】局部变量:只有方法当中才可以使用,出了方法就不能再用了成员变量:... 查看详情