面向对象基础
面向对象思想
面向对象是相对于面向过程而言的,指把相关数据和方法组织为一个整体
来看待。
与面向过程相比,面向对象更关注的是具备功能的对象(不那么关注繁琐的细节,更关注统筹架构的问题),而不是执行过程。
三大思想
-
OOA:面向对象分析(Object Oriented Analysis)
-
OOD:面向对象设计(Object Oriented Design)
-
OOP:面向对象程序(Object Oriented Programming)
三大特征
-
封装:所有内容对外不可见
-
继承:将其他功能继承下来继续发展
-
多态:方法的重载本身就是一个多态性的体现
类与对象
-
类表示共同的属性,是一个综合特征
-
对象是个性化的,是一个个体的特征
类必须通过对象才能使用,对象的所有操作都定义在类中。
类
定义格式:
class 类名称{
成员属性
成员方法
}
类由属性和方法组成:
-
属性:相当于对象的特征,如人的性别、身高、体重
-
方法:相当于对象的行为,如说话、吃饭、睡觉
属性定义格式:
数据类型 属性名;
属性定义并赋值:
数据类型 属性名 = 初始值;
方法定义格式:
权限修饰符 返回值类型 方法名(形式参数列表) {
// 方法体
return 返回值;
}
对象
对象的创建与使用:
对象定义格式:
类名称 对象名称 = new 类名称();
访问类中的属性和方法:
-
属性: 对象.属性名;
-
方法: 对象.方法名(实际参数列表);
对象创建时的内存分析
- 栈
-
Java栈只占2m左右的区域,特点是存取速度特别快(仅次于PC寄存器)。
-
栈存储的特点——先进先出
存储速度快的原因——栈内存通过“栈指针”
来创建和释放空间:
- 指针向下移动->创建新内存,向上移动->释放内存,如下
public static void main(String[] args) {
int a = 10;
Test t = new Test();
// 上述代码中 10(基本数据类型的数据) 和 t(引用变量) 将存在栈内存中。
}
这种方式必须要明确移动的大小和范围——为了方便指针移动,这是一个对数据存储的限制,存储的数据大小是固定的,影响了程序的灵活性。
因此栈中只存放方法体
中的 局部变量(若为基本数据类型,则该变量的值也存在栈内存中)
及 引用数据类型的引用变量
而把更大部分的数据存在堆内存中。
- 堆
-
存放的是类的对象(普通成员变量——基础数据类型)。
-
对象都是通过new关键字创建的,new关键字是告诉JVM,需要去创建一个新的对象,去开辟一块新的堆内存空间。
-
优点在于:创建对象时,不必关注堆内存中需要开辟多少存储空间,也不需要关注内存占用时长。
-
内存释放由GC(垃圾回收器)完成:当
栈内存
中不存在此对象的引用时,则视其为垃圾,等待GC回收。如:
Test t0 = new Test(); // 假设创建的Test对象地址为0x123
Test t1 = new Test(); // 假设创建的Test对象地址为0x124
t1 = t0; // 此时t1和t0都指向内存地址0x123,则地址为0x124的对象在堆内存中被视为垃圾,等待GC回收。
-
方法区
存放:
- 类信息
- 静态成员变量
- 常量
- 成员方法
方法区中存在一个常量池,将由final 和 static共同修饰的静态常量存入其中。
-
PC寄存器
PC寄存器保存:当前正在执行的JVM指令的地址。
Java程序中,每个线程启动时,都会创建一个PC寄存器。
-
本地方法栈
保存本地(native)方法的地址。
举例理解Java对象创建时的内存细节
public Test{
public static void main(String[] args) {
Person p = new Person();
p.Test();
}
}
public Person{
private int a = 1;
private String b = "strb";
public static int c = 2;
public static String d = "strd";
public void Test() {
int x = 10;
String str = "str";
String str1 = new String("str");
String str2 = "str";
Person p1 = new Person();
System.out.println(str == str1); // false
System.out.println(str == str2); // true
}
}
分析:
当Person对象被创建时,会在堆内存开辟一块新的存储空间,其中
-
成员变量:
-
非静态:存储在
堆中的对象
中(即存储在堆中的对象实际上就是各种非静态成员变量)。 -
静态:存储在
方法区
中。
-
-
成员方法:
-
调用时都会在栈内存中开辟空间,静态与非静态的区别在于:
非静态:在栈内存中存储的是
对象的引用this
和形参对应的临时变量(若有)
。静态:在栈内存中存储的是
类名的引用
(指向方法区中该类的静态方法区地址)。
-
-
方法体中的变量:
-
基础数据类型的变量:
变量
和值
都存在栈内存。 -
引用数据类型的变量:
引用变量
存在栈内存,new出来的对象存在堆内存。
-
-
特殊的String类型:
- String类型的对象有两种创建方式:
String str = "str";
String str1 = new String("str");
-
第一种创建方式,jvm首先会到方法区中的字符串常量池中查找是否存在常量"str",若存在则直接返回该常量的引用,若不存在则创建该常量,并将该常量的引用返回给String str。
-
第二种创建方式,jvm会直接在堆内存中创建一个字符串对象,并返回该对象的引用给String str1。(不管该字符串在字符串常量池中是否已存在,都会在堆内存中开辟新空间。且如果不存在,也不会将该字符串加入到常量池中。)
构造方法(构造器)
在对象创建的格式中:
Test t = new Test();
在Test后面出现的(),实际上就是在调用该对象的构造方法。
-
作用
用于对象初始化。
-
执行时机
在创建对象时自动调用
-
特点
- 所有的Java类都会至少存在一个构造方法
- 如果一个类中没有明确编写构造方法,则编译器会自动生成一个无参构造方法,该构造方法中没有任何代码。
- 如果自行编写了任一构造器,则编译器不会再自动生成无参构造方法。因此自定义构造方法后,最好再重写一个无参构造方法。
-
定义格式
方法名
必须与类名
相同,且没有返回值声明!public class Demo{ public static void main(String[] args) { Test t = new Test(); t = new Test(); t = new Test(); t = new Test(); } } class Test{ public Test() { System.out.println("调用构造方法!"); } }
-
构造方法设计
- 建议自定义无参构造方法,不要对编译器形成依赖,避免错误发生。
- 当类中有非常量成员变量时:建议提供两个构造方法——无参构造方法、全参构造方法。
- 当类中所有成员变量都是常量或没有成员变量时,建议不提供任何版本的构造方法。
方法重载
-
当方法名称相同,参数的类型、顺序、个数不同时,可以完成方法的重载。!!注意,方法的重载与返回值无关!!
-
作用:在不同需求下,通过传递不同的参数来完成具体的功能。
构造方法的重载
-
一个类可以存在多个构造方法,参数列表的长度、类型、顺序不同就可以完成构造方法的重载。
-
作用:在不同需求下,调用不同的构造方法来完成对象的初始化。
匿名对象
-
即没有名称的对象。
-
匿名对象只能使用一次,由于没有任何的对象引用,被视为垃圾,只能等待GC回收。
-
只使用一次的对象可通过匿名对象的方式完成。