• (二)Java对象与内存控制


    一、java的类变量和实例变量:

    java中的变量可分成两种:成员变量和局部变量。

    1、局部变量包括以下几类:

    • 方法内的局部变量:作用域为方法体。
    • 代码块内的局部变量:作用域为代码块
    • 形参:方法内的形式参数,作用域为方法体。
    注意:局部变量的生存期很短,被存储在方法的栈内存之中。

    2、成员变量:类体内定义的变量,使用static修饰的成员变量称为 静态变量或者类变量。

    没有被static修饰的成员变量称为 实例变量

    注意1:static关键字的用法

    static可以修饰的部分包括:成员变量、方法、内部类、初始化代码块、内部枚举类。如果使用static修饰,这些成

    员就属于类本身,否则属于类实例。

    注意2:Java中的前向引用。

    一般看来,java没有规定定义成员变量时要有先后顺序。但Java要求定义成员变量时必须采用合法的前向引用。

    package com.beijng.object;
    
    
    public class Object01 {
            
    
            //a变量在b变量没定义之前就引用它,属于非法前向引用
    
    int  a = b + 1;
    int b = 2;
    }
    


    同样,两个类变量也不允许前向引用。如下

    public class Object02 {
    
    
            //属于非法前向引用
    static int  a = b + 1;
    static int b = 2;
    }
    


    但是,一个实例变量总是可以引用类变量。如下
    public class Object01 {
    
    
            //完全正常
    
     int  a = b + 1;
    static int b = 2;
    }
    


    总结: (1)b是类变量,a是实例变量,在java中,类变量的初始化总是在实例变量的初始化之前。

    (2)在同一个JVM中,一个类对应一个Class对象,因此同一个JVM的一个类的类变量只需要一块内存空间。

    但每个类可以创建多个Java对象。每创建一个Java对象,对象的实例变量就会被分配一块内存空间。

    二、实例变量的初始化

    1、实例变量的初始化有三种情况

    • 定义实例变量时指定初始值
    • 非静态初始化块中为实例变量指定初始值
    • 构造方法中对实例变量指定初始值
    需要注意的是:第1、2中情况比第3中情况更早执行,1、2两种情况的执行顺序与它们在源程序中的排列顺序相同。

    示例如下:
     
    package com.beijng.object;
    
    
    public class User {
     
    String username;
    String password;
    public User(String username,String password){
    System.out.println("执行构造方法");
    this.username = username;
    this.password = password;
    }
    {
    System.out.println("执行非静态初始化块");
    age = 18;
    }
    //定义时指定初始值
    int age  =20;
    public String toString(){
     return "username="+username+",password="+password+",age="+age;
    }
    public static void main(String[] args){
    User user1 = new User("xiaoming","123");
    System.out.println(user1);
    }
    }

     
    ———————————————————————————————————————————————————执行非静态初始化块
    执行构造方法
    username=xiaoming,password=123,age=20

    其实, 定义实例变量指定的初始值、初始化块中为实例变量指定初始值的语句地位是平等的,当经过编译器处理后,它

    们都被提取到构造方法中

    示例如下:

    package com.beijng.object;
    
    
    public class JavapToolTest {
    
    
    int a = 30;
    {
    a = 31;
    }
    public JavapToolTest(){
    System.out.println(a);
    }
    public JavapToolTest(String b){
    System.out.println(b);
    }
    
    
    }

    使用命令:javap -c JavapToolTest 可以查看Java编译器对JavapToolTest做的处理,可以显示每个方法具体

    的字节码。

    如下图中,构造方法JavapToolTest()和JavapToolTest(String name)中都包含了初始化实例变量

    语句。

     



    三、类变量的初始化

    1、类变量属于Java类本身,程序初始化Java类时会为类变量分配内存空间,执行初始化。

    可以在定义类变量时指定初始值,或者在静态初始化块中为类变量指定初始值。

    2、程序初始化过程:分成2个阶段

    (1)系统为程序的类变量分配内存空间

    (2)按初始化代码(定义时指定初始值和初始化块中执定初始值)的排列顺序对类变量执行初始化

    3、示例省略

    四、父类构造器

    1、当创建Java对象时,程序总是会依次调用每个父类非静态初始化块、父类构造方法(总是从Object类开始)执行

    初始化,最后才调用本类的非静态初始化块、构造方法执行初始化。
    2、 Java对象是在构造方法中创建的吗?

    示例如下:

    package com.beijng.object;
    class Base {
    private int i = 2;
    public Base(){
                    System.out.println(this.i);//输出2
    this.display();
    }
    public void display(){
    System.out.println(i);//输出0
    }
    }
    
    
    class Derived extends Base {
    private int i = 22;
    public Derived(){
    i = 222;
    }
    public void display(){
    System.out.println(i);
    }
    }
    
    
    public class ConstructorTest {
    
    
    public static void main(String[] args) {
    new Derived();
    }
    
    
    }



    注解 :(1)构造方法只是负责对Java对象实例变量进行初始化(即赋予初始值),在执行构造方法之前,该对象所占

    用的内 存已经被分配了,这些内存里都默认是控制,基本类型变量的默认值是0或false,引用类型变量的默认值都是

    null.  

    (2)所以在程序执行new Derived()时,系统先为Derived对象分配内存空间,此时系统内存需要为Derived

    对象分配两块内存,一个存放属于Base类定义的i实例变量,一个存放属于Derived类定义的i实例变量,此时这两个

    实例变量的值都为0. 然后,在执行Derived类的构造方法之前,首先会执行Base类的构造方法,把Base的i变量赋

    值为2,接着执行this.display(), 问题出现了?this代表哪个类的对象呢?

    注意:当this在构造方法中时,this代表正在创建的Java对象,此时情况是:this位于Base构造方法内,但是这些

    代码实际放在Derived的构造方法内执行,即 Derived的构造方法隐式调用了Base类的构造方法,所以此时this代表

    的应该是 Derived对象,而不是Base对象。


    (3)当变量的编译时类型和运行时类型不同时,通过该变量访问它引用的对象的实例变量时,该实例变量的值由声明

    该变量的类型决定。但是通过该变量调用它引用对象的实例方法时,该方法行为将由它实际所引用的对象决定。

    所以,当程序访问this.i时, this虽然代表 Derived对象,但是它位于Base构造方法中,它的编译类型是Base,

    所以就会访问Base类中定义个i实例变量,即输出2.  但是执行this.display()时,则实际表现出 Derived对象

    的行为,即输出 Derived对象的i实例变量,也就是0.
















  • 相关阅读:
    MiniUI表单验证实践
    MiniUI官方表单验证示例
    MiniUI表单验证总结
    Js-事件分发与DOM事件流
    Windows远程桌面连接的利器-mRemote
    Git 以分支的方式同时管理多个项目
    GIT 如何合并另一个远程Git仓库的文件到本地仓库里某个指定子文件夹并不丢失远程提交记录?
    如何导入另一个 Git库到现有的Git库并保留提交记录
    Total Commander如何设置自定义快捷键在当前目录打开ConEmu
    PHP ECSHOP中 诡异的问题:expects parameter 1 to be double
  • 原文地址:https://www.cnblogs.com/suncoolcat/p/3366168.html
Copyright © 2020-2023  润新知