03.面向对象
面向对象
类
1 | public class 类名 { |
-
例如
1
2
3
4
5
6
7
8
9
10
11
12
13public class test1.Phone {
// 属性(成员变量)
String brand;
double price;
// 行为(方法)
public void call() {
...
}
public void playGame() {
...
}
}
类的对象
-
类名 对象名 = new 类名();
-
例如:
Phone p = new Phone();
成员变量
-
修饰符 数据类型 变量名称 = 初始化值
一般无需指定初始化值,存在默认值。
封装
对象代表什么,就得封装对应的数据,并提供数据对应的行为
-
例如:
人画圆: 两个类–人、圆 画行为则属于圆类人关门: 两个类–人、门 关门行为则属于门类 -
我们可以把一些零散的数据封装成一个对象, 以后传递参数的时候, 只要传递一个整体就可以了, 不需要管这些零散的数据
-
例如:
-
可避免以后要新增检查的信息
1
2User userInfo = new User(username, password, null, null);
checkUserInfo(userInfo); -
无法避免
1
checkUserInfo(username, password);
-
构造方法
1 | public class 类名() { |
-
作用: 给成员变量初始化
-
特点
- 方法名与类名相同,大小写也要一致
- 没有返回值类型,连void都没有
- 没有具体的返回值 (不能由return带回结果数据)
-
执行实际
1. 创建对象的时候由虚拟机调用,不能手动调用构造方法
2. 每创建一次对象,就会调用一次构造方法 -
注意事项
-
构造方法的定义:
- 如果没有定义构造方法,系统将给出一个默认的无参数构造方法
- 如果定义了构造方法,系统将不再提供默认的构造方法
-
构造方法的重载:
带参构造方法和无参构造方法,两者方法名相同,但是参数不同,这叫做构造方法的重载
-
推荐的使用方式:
无论是否使用,都手动书写无参和带全部参数的构造方法
-
例如:
1
2
3
4
5
6
7
8
9
10public class Student {
private String name;
private int age;
public Student() {
...(空参构造)
}
public Student(String name, int age) {
...(带参构造)
}
}
-
-
标准的JavaBean类
-
类名需要见名知意
-
成员变量使用private修饰
-
提供至少两个构造方法
-
成员方法
- 提供每一个成员变量对应的
setXXX()/getXXX()
Alt + Insert 或 右键PTG_to_JavaBean 自动补全 - 如果还有其他行为,也需要写上
- 提供每一个成员变量对应的
对象内存图
-
Java内存分配
-
栈: 方法运行时所进的内存,变量也是在这里
-
堆: new出来的东西会在这块内存中开辟空间并产生地址
-
方法区: 字节码文件(包含类中所有的成员变量和成员方法的信息)加载时进入的内存
-
本地方法栈
-
寄存器
-
-
一个对象的内存图:
Student s = new Student()
- 加载class文件 (把Student类的字节码文件加载到内存中)
- 申明局部变量 (对s申明)
- 在堆内存中开辟一个空间 (new开辟空间)
- 默认初始化/显示初始化
- 构造方法初始化
- 将堆内存中的地址值赋值给左边的局部变量 (即s是一个地址)
-
两个对象的内存图:
Student s = new Student()
Student s1 = new Student()
-
此时不需要再加载Student.class文件,其他步骤一样
-
注意: 一个方法执行完毕后要出栈(main也是方法),方法出栈后指向堆的指针消失,堆中的地址没有变量指向,成为垃圾,也消失
-
this
-
this的作用: 区分局部变量和成员变量
-
this的本质: 代表所在方法调用者的地址值
-
在非静态方法的参数中, 隐藏了变量this, 该this是由虚拟机赋值的
成员和局部变量
区别 | 成员变量 | 局部变量 |
---|---|---|
类中位置不同 | 类中, 方法外 | 方法内, 方法申明上 |
初始化值不同 | 有默认初始值 | 没有默认初始值, 使用之前需要完成赋值 |
内存位置不同 | 堆内存 | 栈内存 |
生命周期不同 | 随着对象的创建而存在, 随着对象的消失而消失 | 随着方法的调用而存在, 随着方法的运行结束而消失 |
作用域 | 整个类中有效 | 当前方法中有效 |
-
例如
1
2
3
4
5
6
7
8
9
10
11
12public class Student {
// 成员变量
private String name;
private int age;
}
public class Test {
public static void main(String[] args) {
// 局部变量
int a = 10;
new Student();
}
}
static
-
static表示静态, 是Java中的一个修饰符, 可以修饰成员变量, 成员方法
-
静态变量: 被static修饰的成员变量
- 特点:
- 被该类所有对象共享
- 不属于对象, 属于类
- 随着类的加载而加载, 优先于对象出现
- 调用方式:
- 类名调用 (推荐)
- 对象名调用
- 特点:
-
静态方法: 被static修饰的成员方法, 叫做静态方法
-
特点:
-
多用在测试类和工具类中
- 工具类: 帮助我们做一些事情的, 但是不描述任何事物的类
- 类名见名知意
- 私有化构造方法
- 方法定义为静态
1
2
3
4
5
6
7
8public class ArrUtil {
private ArrUtil() {}
public static int getMax(...) {...}
public static int getMin(...) {...}
public static int getSum(...) {...}
public static int getAvg(...) {...}
} - 工具类: 帮助我们做一些事情的, 但是不描述任何事物的类
-
Javabean类中很少会用
-
-
调用方式:
- 类名调用 (推荐)
- 对象名调用
-
-
注意事项:
- 静态方法只能访问静态变量和静态方法
- 非静态方法可以访问所有
- 静态方法中没有this关键字
继承
-
public class 子类 extends 父类 {}
-
好处
- 可以把多个子类中重复的代码抽取到父类中, 提高代码的复用性
- 子类可以在父类的基础上, 增加其他的功能, 使子类更强大
-
什么时候用继承?
- 当类与类之间存在相同的内容, 并满足子类是父类中的一种, 就可以考虑使用继承, 来优化代码
-
特点
- Java只支持单继承, 不支持多继承, 但支持多层继承
- 每一个类都直接或间接继承与Object类
-
子类能继承父类中的哪些内容?
构造方法 成员变量 成员方法 非私有 不能 能 能加入虚方法表的, 能 private 不能 能, 但不能用 不能 -
只有父类中的虚方法才能被子类继承, 并不是一级一级向上访问
成员变量的访问特点
-
就近原则: 先在局部位置找, 本类成员位置找, 父类成员位置找, 逐级往上
-
-
成员方法的访问特点
-
就近原则
-
方法重写
- 当父类的方法不能满足子类现在的需求时, 需要进行方法重写
- 方法声明与父类一致
@Override
重写注释
-
方法重写的本质
-
注意事项
- 子类重写父类方法时, 访问权限子类必须大于等于父类 (空着不写 < protected < public)
- 子类重写父类方法时, 返回值类型子类必须小于等于父类
- 重写方法尽量与父类保持一致
- 只有被添加到虚方法表中的办法才能被重写
-
-
构造方法的访问特点
- 父类中的构造方法不会被子类继承
- 子类中所有的构造方法默认先访问父类中的无参构造, 再执行自己
- 子类构造方法的第一行语句默认都是: super(), 不写也存在, 且必须在第一行
- 如果想调用父类的有参构造, 必须手写super进行调用
-
this, super使用总结
-
在构造方法中调用本类的其他构造方法, 虚拟机不会再添加super(), 因为调用的其他构造方法中也默认有super(), 且this()和super()一样只能在第一行
-
多态
-
同类型的对象, 表现出的不同形态
-
表现形式:
父类类型 对象名称 = 子类对象;
-
前提
- 有继承关系
- 有父类引用指向子类对象
- 有方法重写
-
好处
- 使用父类型作为参数, 可以接受所有子类对象, 体现多态的扩展性与便利性
-
调用成员的特点
-
变量调用: 编译看左边, 运行也看左边
- javac 编译代码的时候会看左边的父类中有没有这个变量, 如果有, 编译成功, 如果没有则编译失败
- java 运行代码的时候, 实际获取的就是左边父类中成员变量的值
-
方法调用: 编译看左边, 运行看右边
- javac 编译代码的时候会看左边的父类中有没有这个方法, 如果有, 编译成功, 如果没有则编译失败
- java 运行代码的时候, 实际获取的就是子类中的方法
-
内存图解
-
-
优势
- 在多态形式下, 右边对象可以实现解耦合, 便于扩展和维护
- 定义方法的时候, 使用父类型作为参数, 可以接收所有子类对象, 体现多态的便利性和扩展性
-
弊端
-
不能使用子类的特有功能
-
解决方法: 强制类型转换
-
包
-
包就是文件夹, 用来管理各种不同功能的 Java 类, 方便后期代码维护
-
命名规则: 公司域名反写 + 包的作用, 需要全部英文小写, 见名知意 (例如: com.itheima.domain)
-
使用其他类的规则: 使用其他包的类时, 需要使用全类名 (包名.类名) 或导包 (import)
- 使用同一个包中的类时, 不需要导包
- 使用 java.lang 包中的类时, 不需要导包
- 其他情况都需要导包
- 如果同时使用两个包中的同名类, 需要使用全类名
final
-
修饰方法: 表明方法是最终方法, 不能被重写
-
修饰类: 表明该类是最终类, 不能被继承
-
修饰变量: 叫做常量, 只能被赋值一次
-
命名规则
- 单个单词: 全部大写
- 多个单词: 全部大写, 单词之间用下划线隔开
-
修饰基本数据类型: 记录的值不能发生改变
-
修饰引用数据类型: 记录的地址值不能发生改变, 内部的属性值还是可以改变的
权限修饰符
代码块
-
局部代码块 (用于节约内存, 现在已经淘汰了)
-
构造代码块 (渐渐被淘汰了)
- 写在成员位置的代码块
- 可以把多个构造方法中重复的代码抽取出来
- 执行时机: 在创建对象时先执行构造代码块, 再执行构造方法
-
静态代码块
- 格式:
static{ ... }
- 特点: 需要通过static关键字修饰, 随着类的加载而加载, 并且自动触发, 只执行一次
- 使用场景: 在类加载的时候, 做一些数据初始化的时候使用
- 格式:
抽象类
-
抽象方法: 将共性的行为 (方法) 抽取到父类之后, 由于每一个子类执行的内容不一样, 在父类中不能确定具体的方法体, 该方法就可以定义为抽象方法
- 定义格式:
public abstract 返回值类型 方法名(参数列表);
- 定义格式:
-
抽象类: 如果一个类中存在抽象方法, 该类就必须声明为抽象类
- 定义格式:
public abstract class 类名{}
- 强制子类按某种格式重写方法
- 定义格式:
-
注意事项
- 抽象类不能实例化
- 抽象类中不一定有抽象方法, 有抽象方法的类一定是抽象类
- 可以有构造方法
- 抽象类的子类
- 要么重写抽象类中所有的抽象方法
- 要么是抽象类
接口
-
接口是一种规则, 对行为的抽象, 想让哪个类拥有一个行为, 就让这个类实现对用的接口
-
当一个方法的参数是接口时, 可以传递接口所有实现类的对象, 这种方法称为接口多态
-
注意事项
- 接口用关键字 interface 来定义
public interface 接口名{}
- 接口不能实例化, 但是用接口的实现类可以实例化,将实现类的对象在内存中的地址指向接口,这个接口就可以使用了
接口 实例 = new 实现接口的类();
- 接口和类之间是实现关系, 通过 implements 关键字表示
public class 类名 implements 接口名{}
- 可以单实现, 也可以多实现
public class 类名 implements 接口名1, 接口名2{}
- 实现类还可以在继承一个类的同时实现多个接口
public class 类名 extends 父类 implements 接口名1, 接口名2{}
- 可以单实现, 也可以多实现
- 接口的子类 (实现类)
- 要么重写接口中所有的抽象方法
- 要么是抽象类
- 接口用关键字 interface 来定义
-
接口中成员的特点
- 成员变量
- 只能是常量
- 默认修饰符:
public static final
- 构造方法: 没有
- 成员方法
- 只能是抽象方法
- 默认修饰符:
public abstract
- 成员变量
-
接口和接口的关系
- 继承关系, 可以单继承, 也可以多继承
-
JDK8以后接口中新增的方法
-
允许在接口中定义默认方法, 需要使用关键字 default 修饰
- 作用: 解决接口升级问题
- 接口中默认方法的定义格式
- 格式:
public default 返回值类型 方法名(参数列表) {}
- 范例:
public default void show() {}
- 格式:
- 接口中默认方法的注意事项
- 默认方法不是抽象方法, 不强制重写, 但如果被重写, 重写时要去掉 default 关键字
- public可以省略, default不能省略
- 如果实现了多个接口, 多个接口中存在相同名字的默认方法, 子类就必须对该方法进行重写
-
允许在接口中定义静态方法, 需要用关键字 static 修饰
-
接口中静态方法的定义格式
- 格式:
public static 返回值类型 方法名(参数列表) {}
- 范例:
public static void show() {}
- 格式:
-
接口中默认方法的注意事项
- 静态方法只能通过接口名调用, 不能通过实现类名或者对象名调用
- public可以省略, static不能省略
-
-
-
JDK9接口中新增的方法
-
接口中私有方法的定义格式
-
为默认方法服务
- 格式:
private 返回值类型 方法名(参数列表) {}
- 范例:
private void show() {}
- 格式:
-
为静态方法服务
-
格式:
private static 返回值类型 方法名(参数列表) {}
-
范例:
private static void show() {}
-
-
-
-
适配器设计模式
- 当一个接口中抽象方法过多, 但只要使用其中一部分的时候, 可以使用适配器设计模式
- 书写步骤
- 编写中间类
XXXAdapter
, 实现对应的接口 - 对接口中的抽象方法进行空实现
- 让真正的实现类继承中间类, 并重写需要用的方法
- 为了避免其他类创建适配器类的对象, 中间的适配器类用 abstract 进行修饰
- 编写中间类
内部类
-
在一个类的里面再定义一个类
-
内部类表示的事物是外部类的一部分, 单独存在没有意义
-
内部类可以直接访问外部类的成员, 包括私有
-
外部类要访问内部类的成员, 必须创建对象
-
成员内部类
-
写在成员位置, 属于外部类的成员
-
成员内部类可以被一些修饰符所修饰, 比如: private, 默认, protected, public, static等
-
在成员内部类里面, JDK16开始可以定义静态变量
-
获取成员内部类对象
- 方式一: 在外部类编写方法, 对外提供内部类对象 (当内部类被private修饰时使用)
- 方式二:
外部类名.内部类名 对象名 = 外部类对象.内部类对象;
- 范例:
Outer.Inner oi = new Outer().new Inner();
- 范例:
-
内存图
-
-
静态内部类
-
静态内部类只能访问外部类中的静态变量和静态方法, 如果想要访问非静态的需要创建对象
-
静态内部类也是成员内部类中的一种
-
创建静态内部类对象的格式:
外部类名.内部类名 对象名 = new 外部类名.内部类名();
-
调用静态内部类中的方法
- 调用非静态方法的格式: 先创建对象, 用对象调用
- 调用静态方法的格式:
外部类名.内部类名.方法名()
-
-
局部内部类
- 将内部类定义在方法里面, 类似于方法里面的局部变量
- 外界是无法直接使用局部内部类, 需要在方法内创建内部类对象并使用
- 该类可以直接访问外部类的成员, 也可以访问方法内的局部变量
-
匿名内部类
-
隐藏名字的内部类, 可以写在成员位置, 也可以写在局部位置
-
格式:
new 类名或接口名() { 重写方法; };
1
2
3
4
5new Inter() {
public void show() {
// 方法体
}
};-
包含了继承或实现, 方法重写, 创建对象
-
整体就是一个类的子类对象或者接口的实现类对象
-
-
使用场景
- 当方法的参数是接口或类时, 以接口为例, 可以传递这个接口的实现类对象, 如果实现类只要使用一次, 就可以用匿名内部类简化代码
-