【持续更新】后端根基

【持续更新】后端根基
M-YoungBWeN【持续更新】后端根基
-面向对象
三大特征:封装、继承、多态。
万物皆可归类,一个类与外界的封装关系,一个父类和子类的继承关系,一个类和多个类的多态关系。
- 封装:对象代表什么,就得封装对应的数据,并提供数据对应的行为。
封装对外隐藏了类的内部实现机制,对外提供访问它的方法,可以在不影响外部使用的情况下改变类内部的结构。说白了就是低耦合,高内聚。使用者通过实现制定好的方法来访问数据,无需关心方法的内部实现。
创建对象时问自己三个问题:要干什么、要用什么东西才能干、干完要不要反馈
- 继承:复用。子类继承父类的属性和行为,使得子类可以直接具有与父类相同的属性和行为。
子类可以直接访问父类中的非私有的属性和行为。Java通过extends关键字实现继承。Java是单继承的,一个类只能继承一个直接父类,但是可以多级继承。
子类不能继承父类的构造方法。
子类可以继承父类的私有成员(成员变量,方法),只是子类无法直接访问而已。可以通过getter/setter方法访问父类的私有成员变量。子类父类中出现了同名的成员变量时,子类会优先访问自己对象中的成员变量。如果想访问父类成员变量,可以使用super关键字,super代表的是父类对象的引用,this代表的是当前对象的引用。
- 多态:是指同一行为,具有多个不同表现形式。
多态的前提:1)有继承或者实现关系;2)方法的重写;3)父类引用指向子类对象。
子类对象是可以赋值给父类类型的变量。例如Animal是一个动物类型,而Cat是一个猫类型。Cat继承了Animal,Cat对象也是Animal类型,自然可以赋值给父类类型的变量。
如果没有多态,在下图中register方法只能传递学生对象,其他的Teacher和administrator对象是无法传递给register方法方法的,在这种情况下,只能定义三个不同的register方法分别接收学生,老师和管理员。
有了多态之后,方法的形参就可以定义为共同的父类Person。
多态的特点:
调用成员变量时:编译看左边,运行看左边
调用成员方法时:编译看左边,运行看右边
1 | Fu f = new Zi(); |
多态的弊端:当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥有而父类没有的方法。编译都错误,更别说运行了。所以,想要调用子类特有的方法,必须做向下转型,即父类类型向子类类型向下转换的过程,这个过程是强制的。
为了避免转型报错,Java提供了instanceof关键字。
if(a instanceof Dog d)
先判断a是否为Dog类型,如果是,则强转成Dog类型,转换之后变量名为d,如果不是Dog类型,则不强转,结果直接是false
-方法重写
子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写。
@Override:注解,重写注解校验。只有被添到虚方法表中的方法才能被重写,私有和静态不能添加到虚方法表!
-成员变量、局部变量
- 成员变量是定义在类的内部,任何方法之外的变量。它们代表了类的状态或属性,每个类的实例(对象)都拥有自己的一套成员变量副本。会有默认初始化,在堆对象内存里(这个说法很片面)。
“成员变量在堆内存里,局部变量在栈内存里”这句话是一中比较片面的说法。不管成员变量还是局部变量,如果是基础数据类型,那么直接存在于栈中,如果是引用数据类型,其引用放在栈里,对象实体放在堆里。
- 局部变量在方法里声明,存储在栈里,没有默认初始化,需要手动赋值。
1 | public void calculate() { |
-简要理解JVM内存区
堆区:
-
存储的全部是对象,每个对象都包含一个与之对应的类的信息。(类信息的目的是得到操作指令)
所有实例变量都存储在堆(Heap)内存中,作为对象实例数据的一部分。当你是基本类型时,你的值本身直接存储在对象所属的那块堆内存空间中。当 你是引用类型时,在对象所属的堆内存中,存储的是你指向的那个对象的内存地址(引用),你所指向的那个实际对象也存储在堆内存中。
1 | public class Student { |
- jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身。
栈区:
- 每个线程包含一个栈区,只保存局部变量和自定义对象的引用(不是对象)。
当基本数据类型作为局部变量(在方法内部声明),存储在栈中。
当基本数据类型作为对象的成员变量(实例字段)时,它们存储在堆(Heap)中,因为它们是对象的一部分。
当基本数据类型被
static
关键字修饰时,它们属于类,而不是任何对象实例,存储在方法区。
-
每个栈中的数据都是私有的,其他栈不能访问。
-
栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。
方法区:
1.方法区又叫静态区,被所有的线程共享。方法区包含所有的类信息和static变量。
方法区中包含的都是在整个程序中永远唯一的元素。
1 | public class Student { |
2.因为不属于任何对象,所以不会在创建对象时在堆中为它们分配空间。它们在类加载阶段就被初始化并放入方法区。。
总结:
-
看声明位置:问自己“这个变量是在哪里声明的?”
-
应用规则:
- 方法内部? → 栈
- 类内部(无static)? → 作为对象一部分,在堆
- 类内部(有static)? → 方法区
-
为什么这么设计?(背后的原理)
这种设计是基于性能和内存管理的考虑:
- 栈:分配和销毁效率极高(只是移动栈指针),适合生命周期短、仅限于方法内部的变量。
- 堆:用于存储生命周期不确定的对象及其所有数据,方便进行统一的垃圾回收(GC)。
- 方法区:用于存储“唯一”的、与类相关的数据,所有对象实例共享这一份数据。
下图中new
出来的 Student
对象,其所有成员变量都存储在堆内存中:
下图为两个对象的内存图:
-虚方法表
-构造方法
构造方法是一种特殊的方法,用来完成对象数据的初始化。(可以使用带参构造,为成员变量进行初始化)
- 如果没有定义构造方法,系统将给出一个默认的无参数构造方法。
- 如果定义了构造方法,系统将不再提供默认的构造方法。
子类的构造方法中默认有一个super() ,表示调用父类的构造方法。this()默认是去找本类中的其他构造方法,根据参数来确定具体调用哪一个构造方法,为了借用其他构造方法的功能。super() 和 this() 都必须是在构造方法的第一行,所以不能同时出现。
-标准JavaBean
-
类名需要见名知意;
-
成员变量(类内方法外)使用private修饰;
-
提供至少两个构造方法;
- 无参构造方法
- 带全部参数的构造方法
-
get和set方法;
提供每一个成员变量对应的setXxx()/getXxx()
-
如果还有其他行为(hashCode、equals、toString等),也需要写上。