在谈到类实例化的内存过程时先说说几个概念:
对象:内存上来说是分配在堆上面的一块内存区域
类:把一类具体事物相同特征,功能/行为抽象为属性与方法过程。
类是对象的模板,对象是类的具体表现。
构造函数:与类名同名的函数,用来实例化对象并初始化成员变量。
注意:构造器、构造函数不能称为构造方法。
类的结构:
静态块:当对应的class文件被首次加载到虚拟机时执行。
代码块
构造函数
静态属性与变量首先加载。其次时静态块,之后是代码块,最后是构造函数。
具体过程:
当一个类被创建(A a=new A();),并且这个类是首次被加载时,方法区会开辟出一块内存存放类的class文件并且将全部成员放入。之后会在堆中开辟一块内存,存储这个类并且将这个类的非静态的成员变量拷贝过来(静态成员不拷贝,所有实例共享),并持有对应的方法区的方法的句柄,这块内存有一个唯一内存地址,栈中的a对象指向的就是这个内存地址。
之后你为类的成员变量赋值时,堆中的变量的值会从默认值更改为设定值(方法区中变量无值)。
如果此时在实例化一个新的类(A a2=new A()😉,此时方法区中已经有一个A类的class,所以不会在创建一个A.class,但是此时会在堆中开辟一块新的空间并且将这个类的非静态成员拷贝并持有对应的方法区类的方法的句柄,这块内存空间标注一个新的内存地址。
此时,栈中a指向的是堆中第一个类的内存地址,a2指向的是堆中的第二个类的内存地址,而堆中这两块内存地址指向的是同一个方法区的class文件。
所以栈中对象要么存的是一个内存地址(引用)要么就是一个具体的值,存放的是一个具体值的话他就是一个基本变量。
注意:堆中的非静态成员变量存放的具体值,并不会指向方法区中非静态成员的地址,所以非静态成员的值与方法区无关,但是静态成员变量的话是存在于方法区的,堆中不能持有。 具体过程见下图:
面向对象的特征:
封装,集成,多态,抽象。
封装:隐藏对象功能实现的细节。
1:私有化类中的属性,外部的类不能访问本类的成员变量(需要自己设定访问修饰符)。
2:提供给外部类造作的getter与setter方法
3:提供给外部成员调用方法使用Public修饰。
继承:子类继承父类的成员变量。
1:继承后子类操作的是属于子类的变量,不影响父类的变量值。
2:supper指向的是父类的引用,this是本身的引用。
3:如果子类创建一个成员变量与父类成员变量同名时变量不会被覆盖。
4:子类实例化时会先实例化父类,因为实例化子类时构造器会有一个隐式的super();这是调用父类的构造器。
5:子类继承父类的所有属性与方法,同名属性不覆盖。同名方法会覆盖(重写),不过只能覆盖实例方法,静态方法与finall除外。
6:final不能被重写,类不能被继承。
7:静态方法又叫做类方法,该方法需要类的名称访问。该方法只属于这个类,不属于:某个类对象。
8:static修饰的方法中必须调用静态的方法和使用静态变量。
多态:就是一个事物的多种表现形态。
表现形式:重写(子类重写父类的方法)、重载(同一个类方法名相同参数不同,与返回值无关)。
重写与重载没什么关系,只是名字有点相似而已,有些面试会问到。
抽象:理论,思想,生活中不能具体描述出来。
具体表现形式:抽象类,接口。
抽象类:使用abstract关键字修饰的类,就是抽象类。
1:语法:public abstract class 抽象类名称。
2:抽象类不可以被实例化,但是有构造函数,因为还需要初始化。
3: 抽象类中可以定义抽象方法与普通方法。
4: 含有抽象方法得类一定是抽象类或者是接口。
5: 抽象类天生就是需要被继承的,否则无意义,抽象类也可以继承抽象类,它提供方法模板(抽象方法),抽象方法不可以使用private修饰,也没有意义。
6: 抽象类就是模板设计模式,提供模板,子类具体去实现
接口:接口是一个特殊的抽象类,不能有构造函数,不能有成员变量
1:接口中所有成员,常量,方法都是public
2:因为不能初始化,所以接口中定义方法都是抽象方法
3:接口是必须让实现类实现接口中所有定义的抽象类的
4:jdk1.8版本及以上可以在接口中使用static与default关键字定义实现方法
作用:限定实现类必须含有的方法。