八、 面向对象
8.1 概述
面向对象是一种编程思想,区别于以前的面向过程
- 面向过程 : 强调是如何去完成每一个步骤,步骤和步骤之间的关系是非常紧密的,耦合度很高
- 面向对象 : 强调的是哪个对象能够完成该功能,我们应该去找到该对象来实现
8.2 面向对象的思想特点
A : 是一种符合我们思考习惯的思想
B : 可以将复杂的事情简单化
C : 让我们从执行者变成了指挥者
8.3 类和对象
类 : 一系列具有相同属性和行为的事物的合集,他是一种抽象的表示形式
类的组成:成员变量/get/set方法/成员方法/构造方法/成员代码块/静态代码块
对象 : 一个类的具体的体现 把抽象的类变成一个具体的可以描述的事物
8.3.1 创建对象
类名 对象名 = new 类名();
8.3.2 成员变量
和我们学习过的变量的定义是一样的,有两个小区别。
位置不同:类中,方法外
初始化值:成员变量不需要给初始化值也可以使用,因为它有默认值
8.3.3 成员方法
和我们学习过的方法的定义是一样的,有一个小区别。
去掉了修饰符中的static关键字
8.3.4 内存图
8.4 关键字和封装
8.4.1 private 关键字
private是一个权限修饰符。
可以修饰成员(成员变量和成员方法)
被 private 修饰的成员只在本类中才能访问。
把成员变量用private修饰
提供对应的getXxx()/setXxx()方法
private String name;
private int age;
public void setName(String n) {
name = n;
}
public String getName() {
return name;
}
public void setAge(int a) {
age = a;
}
public int getAge() {
return age;
}
8.4.2 封装
8.4.2.1 封装概述
是面向对象三大特征之一
是面向对象编程语言对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界无法直接操作和修改。就像刚才说的年龄。
8.4.2.2 封装原则:
将不需要对外提供的内容都隐藏起来。
把属性隐藏,提供公共方法对其访问。
成员变量private,提供对应的getXxx()/setXxx()方法
8.4.2.3 好处
通过方法来控制成员变量的操作,提高了代码的安全性
把代码用方法进行封装,提高了代码的复用性
8.4.3 this 关键字
this是一个引用类型,它保存了当前对象的内存地址 指向该对象自身,每一个new出来的对象都有一个属于自己的this
对于方法来说,谁调用该方法,this就代表谁/this还可以用来区分同名的成员变量和局部变量
this(参数...) 调用本类中其他的有参构造
8.5 构造方法
8.5.1 概述
给对象的数据进行初始化
8.5.2 格式
没有返回值 也不能写void
方法名必须和类名一致
如何定义一个类没有提供任何的构造 jvm会默认生成一个无参构造
Student s = new Student();
8.6 继承
8.6.1 概述
1. 多个类中存在相同的属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需在定义这些属性和行为,只要继承那个类即可
2. 单独的这个类称为父类,基类或者叫超类,多个类可以称为子类或者派生类
3. 有了继承以后,我们定义一个类的时候,可以在一个已经存在的类的基础上,还可以定义自己的新成员
8.6.2 格式
public class 子类名 extends 父类名{}
8.6.3 好处和弊端
8.6.3.1 继承的好处
1. 提高了代码的复用性
多个类相同的成员可以放到同一个类中
2. 提高了代码的维护性
如果功能的代码需要修改,修改一处即可
3. 让类与类之间产生了关系,是多态的前提
8.6.3.2 继承的弊端
好处的第三点同时也是继承的弊端:类与类之间产生了关系,让类的耦合性增强了
设计原则:高内聚低耦合
8.6.4 继承的特点
1. 只能单继承,不能多继承 但是可以多层继承
2. 如果一个类继承了某个父类,那么它可以继承该类中非私有的成员
3. java中如果一个类, 没有显性的定义它的父类,那么默认继承Object类 Object是java中所有类的根类
8.6.4.1 继承中成员变量的特点
1. 成员变量名称不一样,使用的时候非常简单
2. 成员变量名称一样的情况:
在子类中访问变量:(就近原则)
在方法的局部范围找,如果有就使用
在子类的成员范围找,如果有就使用
在父类的成员范围找,如果有就使用
如果还找不到就报错
8.6.4.2 继承中成员方法的特点
1. 如果成员范围 父类范围都有同名的方法,程序运行时遵循就近原则,先从成员->父类
2. 如果指明要使用成员范围的方法:this.方法名(); 注意:this可以省略
3. 如果指明要使用父类范围的方法:super.方法名();
8.6.5 super 关键字
super的用法和this很像
this代表本类对象的引用
super代表父类存储空间的标识(可以理解为父类对象引用)
用法(this和super均可如下使用)
访问成员变量:this.成员变量 super.成员变量
访问构造方法:this(…) super(…)
访问成员方法:this.成员方法() super.成员方法(
注意:this() 和 super() 不能同时出现,super() 必须放在子类构造方法的第一行,如果是调用父类无参构造,那么可以省略
8.6.6 方法的重写
8.6.6.1 概述
子类中出现了和父类中一摸一样的方法声明
8.6.6.2 应用
当子类需要父类的功能,而功能主体子类有自己特有的内容时,可以重写中的方法,这样重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容。
8.6.6.3 注意事项
1. 必须有继承关系
2. 子类重写方法 只能修改方法体,方法的声明必须和父类中的方法的声明一致
3. 私有的方法不能被重写
4. @Override 注解 表名该方法是对父类方法的重写
8.7 多态
8.7.1 概述
某一个事物,在不同时刻表现出来的不同状态。
8.7.2 前提和体现
1. 有继承关系
2. 有方法重写
3. 有父类引用指向子类对象
父类型 对象名 = new 子类型();
8.7.3 成员的访问特点
8.7.3.1 成员变量
编译看左边,运行看左边
8.7.3.2 成员方法
编译看左边,运行看右边,不能直接使用子类中特有的成员方法
8.7.4 类型转换
向上转型 upcasting 父类型 对象名 = new 子类型 ( ) ;
向下转型 downcasting 子类型 对象名 = (子类型)原始对象名 ;
注意:强转时需要注意:如果两者之间没有继承关系 那么强转时会引发类型转换异常 为了避免异常发生 在做强转之前最好先判断一下两者之间是否存在关系
boolean flag = a instanceof b
如果 flag 为 true,那么就可以强转,否则不允许强转
8.7.5 内存图
8.8 final 关键字和 static 关键字
8.8.1 final 关键字
final关键字是最终的意思,可以修饰类,成员变量,成员方法。
修饰类,类不能被继承
修饰变量,变量就变成了常量,只能被赋值一次
修饰方法,方法不能被重写
8.8.2 static 关键字
8.8.2.1 静态的特点
1. 被类的所有对象共享:这也是我们判断是否使用静态关键字的条件
2. 可以通过类名调用
3. 优先于对象存在
4. 随着类的加载而加载
8.8.2.2 静态方法的访问特点
1. 静态方法只能访问静态的成员变量和静态的成员方法
2. 在静态方法中是没有this,super关键字的,静态的内容是随着类的加载而加载,this和super是随着对象的创建而存在。
3. 先进内存的,不能访问后进内存的
8.9 抽象类
8.9.1 抽象类的格式
public abstract class 类名{}
8.9.2 抽象类的特点
1. 抽象类不能被实例化 存在意义就是为了被别的类继承
2. 如果一个类继承了抽象类 必须重写抽象类中所有的抽象方法
3. 创建子类对象时 可以使用抽象类多态的方式
父抽象类名 对象名 = new 子类();
8.9.3 成员组成
成员变量
可以是变量,也可以是常量
构造方法
有构造方法,但是不能实例化(用于子类访问父类数据的初始化)
成员方法
可以有抽象方法(限定子类必须完成某些动作),也可以有非抽象方法 提高代码复用性
8.10 接口
8.10.1 接口的格式
public interface 接口名{}
8.10.2 接口的实现格式(implements)
public class 类名 implements 接口名 {}
8.10.3 接口的特点
1. 接口也是不能被实例化 存在的意义就是为了让别的类实现
2. 如果一个类实现了一个接口 必须重写该接口中所有的抽象方法
3. 创建子实现类对象时 可以使用接口多态的方式定义
父接口名 对象名 = new 子实现类();
8.10.4 成员组成
成员变量
只能是常量,默认修饰符 public static final
构造方法
没有,因为接口主要是扩展功能的,而没有具体存在
成员方法
只能是抽象方法,默认修饰符 public abstract
8.11 类和接口的关系
8.11.1 类与类
继承关系,只能单继承,但是可以多层继承
8.11.2 类与接口
实现关系,可以单实现,也可以多实现。还可以在继承一个类的同时实现多个接口
8.11.3 接口与接口
继承关系,可以单继承,也可以多继承
8.12 包
8.12.1 概述
对定义的类进行分门别类的管理
8.12.2 格式
package 包名; 多级包用 . 分开即可
注意事项:
package语句必须是程序的第一条可执行的代码
package语句在一个java文件中只能有一个
8.12.3 导包
要引入其他包的类,但是 java.lang 下的类可以直接使用,无需导包
import java.包名.类名;
8.13 权限修饰符
本类 | 同包下的子类和其他类 | 不同包下的子类 | 不同包下的其他类 | |
---|---|---|---|---|
public | √ | √ | √ | √ |
protected | √ | √ | √ | |
默认修饰符 | √ | √ | ||
private | √ |
8.14 代码块
//BlockTest静态代码块执行 --- BlockTest的主函数执行了 --- Coder静态代码块执行 ---
//Coder构造代码块执行 --- Coder无参空构造执行 --- Coder构造代码块执行 --- Coder无参空构造执行
public class BlockTest {
static {
System.out.println("BlockTest静态代码块执行");
}
{
System.out.println("BlockTest构造代码块执行");
}
public BlockTest(){
System.out.println("BlockTest无参构造执行了");
}
public static void main(String[] args) {
System.out.println("BlockTest的主函数执行了");
Coder c = new Coder();
Coder c2 = new Coder();
}
}
class Coder {
static {
System.out.println("Coder静态代码块执行");
}
{
System.out.println("Coder构造代码块执行");
}
public Coder() {
System.out.println("Coder无参空构造执行");
}
}
8.15 内部类
8.15.1 概述
将类写在其他类的内部,可以写在其他类的成员位置和局部位置,这时写在其他类内部的类就称为内部类。
8.15.2 内部类的使用场景
在描述事物时,若一个事物内部还包含其他可能包含的事物,比如在描述汽车时,汽车中还包含这发动机,这时发动机就可以使用内部类来描述。
class 汽车 { //外部类
class 发动机 { //内部类
}
}
8.15.3 成员内部类
8.15.3.1 概述
在类的成员位置,和成员变量以及成员方法所在的位置是一样的
在内部类当中,可以直接访问外部类的成员,包括私有成员
8.15.3.2 定义格式
class 外部类 {
修饰符 class 内部类 {
//其他代码
}
}
8.15.3.3 访问方式
外部类名.内部类名 变量名 = new 外部类名().new 内部类名();
8.15.3.4 成员内部类的修饰符
我们可以使用权限修饰符修饰成员内部类,但是如果使用私有来修饰,则无法在其他类中访问
我们可以使用 static 修饰成员内部类,不用再创建外部类的对象了
另外还可用 abstract ,final 修饰成员内部类(不常用)
8.15.4 局部内部类
8.15.4.1 概述
局部内部类,定义在外部类方法中的局部位置。与访问方法中的局部变量相似,可通过调用方法进行访问
在方法内,出了方法之后就不能使用
8.15.4.2 定义格式
class 外部类 {
修饰符 返回值类型 方法名(参数) {
class 内部类 {
//其他代码
}
}
}
8.15.4.3 访问方式
在外部类方法中,创建内部类对象,进行访问
8.15.5 匿名内部类
8.15.5.1 概述和特点
可以把匿名内部类看成是一个没有名字的局部内部类
定义在方法当中
必须在定义匿名内部类的时候创建他的对象
8.15.5.2 格式
new 类/接口(){
//如果是创建了继承这个类的子类对象,我们可以重写父类的方法
//如果是创建了实现这个接口的子类对象,我们必须要实现该接口的所有方法
};
原理:创建了继承这个类的子类对象或者是创建了实现这个接口的子类对象
8.15.5.3 应用场景
作为参数进行传递,只用一次