• 对象及其内存管理 笔记


    这里的内存分配特指创建java对象时jvm为该对象在堆内存中所分配的内存空间

    内存回收是指当该java对象失去引用,变成垃圾时jvm的垃圾回收机制自动清理该对象,并回收该对象所占用的内存。

    1:java程序变量分为局部变量和成员变量

    局部变量:形参

    方法内的局部变量

    代码块内的局部变量

    局部变量的作用时间很短暂,它们都被存储在栈内存中

    另外,jvm对类中的类变量(有static修饰的变量)在创建类的时候只分配一块内存空间。并且类变量随着类的初始化而初始化

    而实例变量是随着对象的初始化而初始化。

    而在初始化一个对象之前,先初始化该对象所属的类,因此,类变量的初始化时机总是在实例变量初始化之前。

    个类的类变量只需一块内存,但对于实例变量,该类每创建一个实例,就需要为实例变量分配一块内存空间。即有几个实例,实例变量就需要几块内存空间。

    2:实例变量的初始化时机

    每次创建java对象时都需要为实例变量分配内存空间,并对实例变量执行初始化

    程序一般可在三个地方对实例变量执行初始化

    A:定义实例变量时

    B:非静态初始化快中

    C:通过构造器

    AB比C更早执行,AB执行顺序与他们在源代码中的排序顺序相同

    实际上,实例变量的初始化分为两个阶段:1:创建java对象时,系统为其分配内存,2:这条语句将被提取到java构造器中执行。

    经过编译器处理之后,三种对应的赋值语句都被合并到构造器中,在合并的过程中,定义变量语句转换得到赋值语句,初始化块里的语句转换得到赋值语句,总是位于构造器的所有语句之前,合并后,两种赋值语句的顺序保持为他们在源代码中的顺序

    3:类变量的初始化时机

    jvm对java的一个类只初始化一次,初始化类的时候为类变量分配一次内存空间。

    一般在两个地方对类变量执行初始化

    A:定义类变量时

    B:静态初始化块中对类变量指定初始值

    这两种方式的执行顺序与他们在源代码中的排列顺序相同。

    对类变量初始化时,初始化的第一阶段,为类变量分配内存空间,将值设为默认值,接着初始化进入第二阶段依次为变量赋值

    因此在这个例子中输出结果为-2.8和17.2

    4:父类构造器

     当创建java对象时,程序会依次调用每个父类的非静态初始化块,构造器执行初始化,然后才调用本类的非静态初始化块 构造器执行初始化

    super和this 调用都只能在构造器中使用,而且必须作为构造器中的第一行代码,super用于显示调用父类构造器,this用于显式调用本类中重载的构造器。

    根据传入的实参列表来确定调用父类的哪个构造器

    当变量的编译时类型和运行时类型不同时,通过该变量访问它引用的对象的实例变量时,该实例变量的值由声明该变量的类型决定,但是通过该变量调用引用的方法时,该变量的值由声明了该变量的类型决定

    如果父类的构造器调用了被子类重写的方法,且通过子类构造器来创建子类对象,调用(显式或隐式)了这个父类构造器,就会导致子类的重写方法在子类构造器的所有代码之前被执行,从而导致出现子类重写方法访问不到子类的实例变量值的情形。

    java通过变量调用方法时,方法的行为总是表现出他们实际类型的行为,但通过变量访问所指对象的实例变量,这些实例变量的值总是表现出声明这些变量所用类型的行为,java继承在处理成员变量和方法时是有区别的,编译时不可能将父类的成员变量转移到子类中,因此父类和子类可以同时拥有同名的成员变量,如果子类重写了父类的方法就意味着子类方法完全覆盖了父类同名方法,系统不可能将父类里的方法转移到子类中

    定义一个子类实例对象时,内存中只有一个子类对象,并没有父类对象,只不过这个子类对象保存了子类中定义的所有实例变量,还保存了它所有父类所定义的全部实例变量。

    当程序创建一个子类对象时,系统不仅会为该类中定义的实例变量分类内存也会为父类中定义的所有实例变量分配内存,即使子类定义了父类同名的实例变量,其实是子类中定义的变量会隐藏父类中定义的变量,注意不是完全覆盖,因此系统在创建子类对象时,依然会为父类中定义的,被隐藏的变量分配内存空间

    类变量也是属于同样的情况

    5:final修饰符

    final修饰的变量必须进行显式初始化,而且只能在下面三种情况下进行:

    经过编译器的处理,这三种方式都会被抽取到构造器中赋初始值,本质上final实例变量只能在构造器中被赋初始值,

    本质上,final类变量只能在静态初始化块中被赋初始值

    当使用final修饰类变量时,如果定义该final类变量时制定了初始值,而且该初始值可以在编译时就被确定下来,系统将不会再静态初始化快中对该类变量赋初始值,而是在类定义中直接使用该类变量代替该final变量。此时这个final变量将不再是一个变量,系统会将其当成宏变量处理,也就是说所有出现该变量的地方,系统将直接把他当成对应的值处理

    由于book是个宏变量,所以可被直接替换成“疯狂java讲义:”+99.0

    但是book2却不相等

    对于实例变量而言,除了可以在定义改变量时赋初始值之外,还可以在非静态初始化快,构造器中对他赋初始值,而且这三个地方指定初始值的效果基本一样,但是对于final实例变量而言,只有在定义该变量时指定初始值才会有“宏变量”的效果,在非静态初始化块,构造器中为final实例变量指定初始值则不会有这种效果。

    天哪 之前override都不知道,oh, my god

    如果程序需要在匿名内部类中使用局部变量,那么这个局部变量必须使用final修饰符修饰

    final修饰的父类方法子类可以访问,但是不能重写

  • 相关阅读:
    清除浮动的几种方法
    call() 、 apply() 、bind()方法的作用和区别!
    关于如何通过json更改背景图片
    js验证码实现
    解决python3 UnicodeDecodeError: 'gbk' codec can't decode byte
    Rest接口测试,巧用firebug插件
    PHP中字符串的连接和换行
    PHP内置函数file_put_content(),将数据写入文件,使用FILE_APPEND 参数进行内容追加
    PHP的三种输出方式
    PHP中的include、include_once、require、require_once
  • 原文地址:https://www.cnblogs.com/gracyandjohn/p/4589382.html
Copyright © 2020-2023  润新知