java创建一个对象时,内存中发生了什么
Posted 吃鱼尾的咩
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java创建一个对象时,内存中发生了什么相关的知识,希望对你有一定的参考价值。
JAVA是一种面向对象的语言,它本身具有面向对象的三大特性--封装,继承,多态。开发时,我们要记住,属性是用于存储数据的。直接被访问,容易出现安全隐患。所以,类中的属性通常被私有化,并对外提供公共的访问方法。这个方法一般有两个,规范写法:对于属性xx,可以使用setXX(),getXX()对其进行操作。
一、类的成员变量存在于堆内存中,随着对象的产生而存在,消失而消失。局部变量存在于栈内存中,随着所属区域的运行而存在,结束而释放。(着重理解)
二、 创建一个对象都在内存中做了什么事情?Person p = new Person();
1.先将硬盘上指定位置的Person.class文件加载进内存。
2.执行main方法时,在栈内存中开辟了main方法的空间(压栈-进栈),然后在main方法的栈区分配了一个变量p。
3.在堆内存中开辟一个实体空间,分配了一个内存首地址值。new
4.在该实体空间中进行属性的空间分配,并进行了默认初始化。
5.对空间中的属性进行显示初始化。
6.进行实体的构造代码块初始化。
7.调用该实体对应的构造函数,进行构造 函数初始化。
8.将首地址赋值给p,p变量就引用了该实体。(指向了该对象)
三、this和static详解
this:代表对象。就是所在函数所属对象的引用。哪个对象调用了this所在的函数,this就代表哪个对象。就是哪个对象的引用。在定义功能时,如果该功能内部使用到了调用该功能的对象,这时就用this来表示这个对象。this还可以用于构造函数间的调用。调用格式:this(实际参数);
this对象后面跟上.调用的是成员属性和成员方法(一般方法);
this对象后面跟上()调用的是本类中的对应参数的构造函数。
注意:用this调用构造函数,必须定义在构造函数的第一行。因为构造函数是用于初始化的,所以初始化动作一定要执行。否则编译失败。
static:关键字是一个修饰符。用于修饰成员(成员变量 和成员函数)。
特点:
1.想要实现对象中的共性数据的对象共享。可以讲这个数据进行静态修饰。
2.被静态修饰的成员,可以直接被类名所调用。也就是说,静态的成员多了一种调用方式。类名.静态方式。
3.静态随着类的加载而加载。而且优先于对象存在。
弊端:
1.有些数据是对象特有的数据,是不可以被静态修饰的。因为那样的话,特有数据会变成对象的共享数据。这样对事物的描述就出了问题。所以,在定义静态时,必须要明确,这个数据是否是被对象所共享的。
2.静态方法只能访问静态成员,不可以访问非静态成员。
因为静态方法加载时,优先于对象存在,所以没有办法访问对象中的成员。
3.静态方法中不能使用this,super关键字。
因为this代表对象,而静态在时,有可能没有对象,所以this无法使用。
4.主函数是静态的。
什么时候定义静态成员呢?
成员分两种:
1.成员变量。(数据共享时静态化)
该成员变量的数据是否是所有对象都一样;
如果是,那么该变量需要被静态修饰,因为是共享数据。如果不是,那么就说这是对象的特有数据,要存储到对象中。
2.成员函数。(方法中没有调用特有数据时就定义成静态)
如何判断成员函数是否需要被静态修饰呢?
只要参考,该函数内是否访问了对象中的特有数据。如果有访问特有数据,那方法不能被静态修饰。如果没有访问过特有数据,那么这个方法需要被静态修饰。
静态的生命周期很长。静态代码块就是一个有静态关键字标示的一个代码区域。定义在类中。完成类的初始化。静态代码块随着类的加载而执行,而且只执行一次(new多个对象就只执行一次)。如果和主函数在同一个类中,优先于主函数执行。
主函数的解释:保证所在类的独立运行。是程序的入口。被JVM调用。
静态代码块、构造代码块、构造函数同时存在时的执行顺序:
静态代码块--构造代码块--构造函数。
四、再谈继承
继承是一种连结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法。当生成子类对象时,JAVA默认首先调用父类的不带参构造方法,然后执行该构造方法,生成父类的对象。接下来,再去调用子类的构造方法,生成子类的对象。(要想生成子类的对象,首先需要生成父类的对象,没有父类对象就没有子类对象。)
如果子类使用super()显式调用父类的某个构造方法,那么在执行的时候就会寻找与super()所对应的构造方法而不会再去寻找父类的不带参数的构造方法。与this一样,super也必须要作为构造方法的第一条执行语句,前面不能有其他可执行语句。
当两个方法 形成重写关系时,可以在子类方法中通过super.run()形式调用父类的run()方法,其中super.run()不必放在第一行语句,因此此时父类对象已经构造完毕,先调用父类的run()方法还是先调用子类的run()方法是根据程序的逻辑决定的。
方法的覆盖(重写)
重写的要求:子类覆盖方法和父类被覆盖方法的方法返回类型,方法名称,参数列表必须相同;子类覆盖方法的访问权限必须大于等于父类的方法的访问权限;方法覆盖只能存在于子类和父类之间;子类覆盖方法不能比父类被覆盖方法抛出更多异常;
方法重写与方法重载之间的关系:重载发生在同一个类内部的两个或多个方法。重写发生在父类与子类之间。
final关键字在继承中的使用,final可以用于以下四个地方:
1.定义变量,包括静态的和非静态的。
如果final修饰的是一个基本类型,就表示这个变量被赋予的值是不可变的,即它是个常量;如果final修饰的是一个对象,就表示这个变量被赋予的引用是不可变的,不可改变的只是这个变量所保存的引用,并不是这个引用所指向的对象,其实更贴切的表述final的含义的描述,那就是,如果一个变量或方法参数被final修饰,就表示它只能被赋值一次,但是Java虚拟机为变量设定的默认值不记作一次赋值。
被final修饰的变量必须被初始化。初始化的方式 有以下几种:
1.在定义的时候初始化
2.在初始化块中初始化
3.在类的构造器中初始化
4.静态变量也可以再静态初始化块中初始化。
1>定义方法。
当final用来定义一个方法时,它表示这个方法不可以被子类重写,但是它这不影响它被子类继承。
2>定义类
由于final类不允许被继承,编译器在处理时把它所有的方法当作final的,因此final类比普通类拥有更高的效率。
关于继承的几点注意:
a.父类有的,子类也有
b.父类没有的,子类可以增加
c.父类有的,子类可以改变
d.构造方法不能被继承
e.方法和属性可以被继承
f.子类的构造方法隐式的调用父类的不带参数的构造方法
g.当父类没有不带参数的构造方法时,子类需要使用super来显式地调用父类的构造方法,super指的是对父类的引用。
h.super关键字必须是构造方法中的第一行语句。
五、多态
多态的意思就是父类的引用可以指向子类的对象。在一个类中,可以定义多个同名的方法,只要确定它们的参数个数和类型不同,这种现象称为类的多态。类的多态体现在两个方面:一是方法的重载上,包括成员方法和构造方法的重载;二是在继承过程中,方法的重写。
六、抽象
使用abstract关键字所修饰的类叫做抽象类。抽象方法需要定义在抽象类中。
七、接口
JAVA语言不支持一个类有多个直接的父类,但现实例子中,又有很多类似于多继承的例子。JAVA中不可以多继承,但可以实现(implements)多个接口,间接地实现了多继承。
八、比较抽象类与接口
相同点:
1.代表系统的抽象层,当一个系统使用一颗继承树上的类时,应该尽量把引用变量声明为 继承树的上层抽象类型,这样可以提高两个系统之间的送耦合。
2.都不能被实例化
3.都包含抽象方法,这些抽象方法用于描述系统能提供哪些服务,但不提供具体的实现。
不同点:
1. 在抽象类中可以为部分方法提供默认的实现,从而避免在子类中重复实现它们,这是抽象类的优势,但这一优势限制了多继承,而接口中只能包含抽象方法.由于在抽象类中允许加入具体方法,因此扩展抽象类的功能,即向抽象类中添加具体方法,不会对它的子类造成影响,而对于接口,一旦接口被公布,就必须非常稳定,因为随意在接口中添加抽象方法,会影响到所有的实现,这些实现类要么实现新增的抽象方法,要么声明为抽象类。
2.一个类只能继承一个直接的父类,这个父类可能是抽象类,但一个类可以实现多个接口,这是接口的优势,但这一优势是以不允许为任何方法提供实现为代价的。当子类覆盖父类的实例方法或隐藏父类的成员变量及静态方法时,JAVA虚拟机采用不同的绑定规则, 假如还允许一个类有多个直接的父类,那么会使绑定规则更加复杂。因此,为了简化系统结构设计和动态绑定机制,JAVA语言禁止多重继承。一个类即时有多个接口,也不会增加JAVA虚拟机进行动态绑定的复杂度,因为JAVA虚拟机永远不会把方法与接口绑定,而只会把方法与它的实现类绑定,使用接口和抽象类的总体原则。
以上是关于java创建一个对象时,内存中发生了什么的主要内容,如果未能解决你的问题,请参考以下文章