Java基础教程-05-面向对象
1. 面向过程和面向对象解释
1.1 面向过程
1.1.1 简述
我们回想一下, 前面我们完成一个需求的步骤是怎样的?
- 首先是搞清楚我们要做什么.
- 然后在分析怎么做.
- 最后我们在通过代码一步一步去实现.
上述的每一个步骤, 我们都是参与者, 并且需要面对具体的每一个步骤和过程, 这就是面向过程最直接的体现.
1.1.2 举例
• 生活中的例子: 洗衣服. 想完成洗衣服这个需求, 我们得完成下述的每一个步骤:
- 找个盆.
- 接点水.
- 到洗衣液.
- 把衣服放进去浸泡10分钟.
- 把衣服揉搓干净.
- 漂洗.
- 拧干水分.
- 晾晒.
注意:
- 上述的这些步骤, 但凡有一个我们不会做, 就没有办法完成洗衣服这个需求.
- 即: 每一步都需要我们亲自做, 这就是: 面向过程思想.
• 代码举例: 把数组元素按照[11, 22, 33, 44, 55]的格式进行输出.
- 定义方法printArray(), 参数列表为: int[] arr, 返回值的类型为String.
- 定义字符串变量s, 用来记录数组元素拼接后的结果.
- 字符串变量s的初始化值设置为[.
- 对数组对象进行判断, 看其是否是合法对象.
即: 数组对象不为: null - 通过for循环遍历数组, 获取到每一个元素.
- 判断当前获取到的元素是否是最后一个元素, 并和字符串变量s进行拼接.
• 如果是最后一个元素, 则在后边在拼接上]
• 如果不是最后一个元素, 则在后边在拼接上, - 当for循环执行结束后, 字符串变量s记录的就是拼接后的结果, 将其直接返回即可.
注意: 上述的每一步代码都需要我们编写, 这样做比较繁琐.
1.1.3 总结
面向过程思想其实就是一种编程思想. 所谓的面向过程开发, 指的就是面向着具体的每一个步骤和过程, 把每一个步骤和过程完成, 然后由这些功能方法相互调用, 完成需求.
记忆: 面向过程的代表语言是: C语言.
1.2 面向对象
1.2.1 简述
当需求单一, 或者简单时, 我们一步一步去操作是没问题的, 并且效率也挺高. 可随着需求的更改, 功能的增多, 发现需要面对每一个步骤就很麻烦了, 于是我们就想着, 能不能把这些步骤和功能在进行封装, 封装时根据不同的功能, 进行不同的封装, 功能类似的用一个类封装在一起, 这样结构就清晰了很多. 用的时候, 找到对应的类就可以了. 这就是面向对象的思想.
1.2.2 举例
• 生活中的例子: 洗衣服.
- 想完成洗衣服这个需求, 我们可以通过洗衣机 来实现.
- 对于我们来讲, 洗衣机就是我们的对象.
- 总结: 万物皆对象.
• 代码举例: 把数组元素按照[11, 22, 33, 44, 55]的格式进行输出.
上述的需求, 我们可以通过Arrays类的toString()方法来实现.
Int[] arr = {11, 22, 33, 44, 55};
sop(Arrays.toString(arr)); -> “[11, 22, 33, 44, 55]”
1.2.3 思想特点
- 是一种符合我们思考习惯的思想.
- 可以将复杂的事情简单化.
- 让我们从执行者变成了指挥者.
1.2.4 总结
面向对象思想是一种编程思想, 它是基于面向过程的, 强调的是以对象为基础完成各种操作.
总结来讲, 万物皆对象.
2. 类和对象
2.1 概述
问题一: 你为什么学习编程语言?
我们学习编程语言, 其实就是为了把现实世界的事物通过代码模拟出来, 实现信息化.
例如:
• 超市的计费系统.
• 银行的核心系统.
• 千亿级数据仓库.
分析PB级数据,为企业提供高效、稳健的实时数据洞察。
采用类似阿里巴巴大数据数仓设计的分层架构思想,使用主流的实时仓库技术Flink、Druid、Kafka。
• 企业级360°全方位用户画像.
360°全方位还原用户画像,实现对个体和群体信息的标签化,实现精准推荐和营销.
• 电商推荐系统.
项目利用Neo4j构建用户和商品的关系图示,基于词向量相似度推荐商品、CTR/CVR点击率预估模型、逻辑斯特回归算法进行CTR点击率预估。
问题二: 我们是如何表示现实世界的事物呢?
- 属性.
– 属性指的就是事物的描述信息(名词).
– 属性在Java中被称之为成员变量. - 行为.
– 行为指的就是事物能够做什么.
– 行为在Java中被称之为成员方法.
例如: 学生
• 属性: 姓名, 年龄, 性别…
• 行为: 学习, 吃饭, 睡觉…
问题三: Java语言是如何表示现实世界的事物呢?
- 在Java语言中, 是通过类来体现事物的. Java语言最基本的单位是类, 它是一个抽象的概念, 看不见, 摸不着.
- 对象: 对象就是该类事物的具体体现, 实现.
举例:
类 学生 大象
对象 张三, 23 北京动物园叫图图的大象
2.2 类的定义格式
2.2.1 简述
定义类其实就是定义类的成员(成员变量和成员方法)
• 成员变量:
- 和以前定义变量是一样的, 只不过位置发生了改变, 写到类中, 方法外
- 而且成员变量还可以不用赋值, 因为它有默认值.
• 成员方法:
- 和以前定义方法是一样的, 只不过把static关键字去掉.
- 这点先记忆即可, 后面我们再讲解static关键字的用法.
2.2.2 格式
public class 类名 {
//成员变量
//成员方法
}
2.2.3 示例
需求
定义一个学生类.
参考代码
public class Student{
//成员变量, 就是属性.
String name; //姓名
int age; //年龄
//成员方法, 就是行为.
//学习的方法
public void study() {
System.out.println("键盘敲烂, 月薪过万!...");
}
//吃饭的方法
public void eat() {
System.out.println("学习饿了要吃饭!...");
}
}
2.3 类的使用
2.3.1 简述
所谓类的使用, 就是使用类中定义的成员(成员变量和成员方法).
2.3.2 格式
- 创建该类的对象.
类名 对象名 = new 类名();
- 通过对象名.的形式, 调用类中的指定成员即可.
//成员变量
对象名.成员变量//成员方法
对象名.成员方法(参数列表中各数据类型对应的值…)
2.3.3 示例
需求
使用学生类中的成员.
参考代码
public class StudentTest{
public static void main(String[] args) {
//1. 创建学生类的对象.
Student s = new Student();
//2. 访问成员变量.
System.out.println(s.name);
System.out.println(s.age);
//3. 给成员变量赋值.
s.name = "张三";
s.age = 23;
//4. 访问成员变量.
System.out.println(s.name);
System.out.println(s.age);
//5. 访问成员方法.
s.study();
s.eat();
}
}
运行结果为:
null
0
张三
23
好好学习, 天天向上!
为了保持精力, 要按时吃饭.
3. 手机类的定义和使用
3.1 需求
- 定义手机类Phone.
属性: 品牌(brand), 价格(price), 颜色(color)
行为: 打电话(call), 发短信(sendMessage) - 创建测试类PhoneTest, 在类中定义main方法, 并访问手机类(Phone类)中的成员.
3.2 参考代码
手机类
public class Phone {
// 属性: 也叫成员变量, 就是用来描述事物的外在特征的(即: 名词)
String brand; //品牌
int price; //价格
String color; //颜色
// 行为: 打电话(call), 发短信(sendMessage)
//打电话
public void call(String name) { //夯哥
System.out.println("给" + name + "打电话!...");
}
//发短信
public void sendMessage(String name) {
System.out.println("给" + name + "发短信!...");
}
}
手机类的测试
public class PhoneTest {
//main方法是程序的主入口, 所有的代码都是从这里开始执行的.
public static void main(String[] args) {
//1. 创建手机类的对象.
Phone p = new Phone();
//2. 设置成员变量值.
p.brand = "华为";
p.price = 5999;
p.color = "黑色";
//3. 打印成员变量值.
System.out.println(p.brand);
System.out.println(p.price);
System.out.println(p.color);
//4. 调用成员方法
p.call("夯哥");
p.sendMessage("夯哥");
}
}
运行结果为:
华为
5999
黑色
给夯哥打电话!...
给夯哥发短信!...
4. 对象的内存图
4.1 一个对象的内存图
代码
//手机类
public class Phone {
//属性, 成员变量
String brand; //品牌
int price; //价格
String color; //颜色
//行为, 成员方法
//打电话
public void call(String name) {
System.out.println("给" + name + "打电话!...");
}
//发短信
public void sendMessage(String name) {
System.out.println("给" + name + "发短信!...");
}
}
//手机类的测试类
public class PhoneTest {
public static void main(String[] args) {
//1. 创建手机类的对象.
Phone p = new Phone();
//2. 设置成员变量值.
p.brand = "华为";
p.price = 6666;
p.color = "黑色";
//3. 打印成员变量.
System.out.println(p.brand + "--" + p.price + "--" + p.color);
//4. 调用成员方法.
p.call("夯哥");
p.sendMessage("夯哥");
}
}
内存图解
4.2 两个对象的内存图
代码
//手机类
public class Phone {
//属性, 成员变量
String brand; //品牌
int price; //价格
String color; //颜色
//行为, 成员方法
//打电话
public void call(String name) {
System.out.println("给" + name + "打电话!...");
}
//发短信
public void sendMessage(String name) {
System.out.println("给" + name + "发短信!...");
}
}
//手机类的测试类
public class PhoneTest {
public static void main(String[] args) {
//1. 创建手机类的对象.
Phone p = new Phone();
//2. 设置成员变量值.
p.brand = "华为";
p.price = 6666;
p.color = "黑色";
//3. 打印成员变量.
System.out.println(p.brand + "--" + p.price + "--" + p.color);
//4. 调用成员方法.
p.call("传智播客");
p.sendMessage("传智播客");
Phone p2 = new Phone();
p2.brand = "小米";
p2.price = 3333;
p2.color = "白色";
System.out.println(p2.brand + "--" + p2.price + "--" + p2.color);
p2.call("黑马程序员");
p2.sendMessage("黑马程序员");
}
}
内存图解
5. 成员变量和局部变量的区别
5.1 简述
• 成员变量: 指的是定义在类中, 方法外的变量.
• 局部变量: 指的是定义在方法中, 或者方法声明上的变量.
它们的区别如下:
- 定义位置不同.
– 成员变量: 定义在类中, 方法外.
– 局部变量: 定义在方法中, 或者方法声明上. - 在内存中的存储位置不同.
– 成员变量: 存储在堆内存.
– 局部变量: 存储在栈内存. - 生命周期不同.
– 成员变量: 随着对象的创建而存在, 随着对象的消失而消失.
– 局部变量: 随着方法的调用而存在, 随着方法的调用完毕而消失. - 初始化值不同.
– 成员变量: 有默认值.
– 局部变量: 没有默认值, 必须先定义, 再赋值, 然后才能使用.
5.2 代码演示
public class VariableDemo{
int x;
public void show() {
int y = 10;
System.out.println(x);
System.out.println(y);
}
}
6. 封装
上述的代码中, 我们可以任意的设置属性的值, 包括我们可以设置一些非法值, 例如: 把年龄设置成负数, 这样做程序就容易出问题, 针对于这种情况, 我们可以通过private关键字来优化它.
6.1 private关键字
6.1.1 简述
private是一个关键字, 也是访问权限修饰符的一种, 它可以用来修饰类的成员(成员变量和成员方法).
6.1.2 特点
被private修饰的内容只能在本类中直接使用.
6.1.3 应用场景
- 在实际开发中, 成员变量基本上都是用private关键字来修饰的.
- 如果明确知道类中的某些内容不想被外界直接访问, 都可以通过private来修饰.
6.1.4 示例
需求
- 定义学生类Student, 包含姓名, 年龄属性.
- 在StudentTest测试类中, 创建Student类的对象, 并调用Student类中的成员.
- 对年龄或者姓名属性加private修饰, 然后观察结果.
参考代码
//学生类
public class Student{
//属性
String name; //姓名
private int age; //年龄
//getXxx()和setXxx()方法
public void setAge(int a) {
if(a >= 0 && age <= 150) {
//年龄合法, 就赋值.
age = a;
}
}
public int getAge() {
return age;
}
//行为
public void show() {
//被private修饰的内容, 可以在本类中直接访问.
System.out.println(name + "..." + age);
}
}
//学生类的测试类
public class StudentTest{
public static void main(String[] args) {
//1. 创建学生类的对象
Student s = new Student();
//2. 给成员变量赋值.
s.name = "张三";
//s.age = -23; //被private修饰的内容, 外界无法直接访问.
s.setAge(-23);
//3. 调用成员方法.
s.show();
}
}
6.2 标准代码
刚才的代码, 为了讲解private关键字, 只给年龄属性加了private, 而在实际开发中, 除非必要, 否则成员变量都要用private来修饰, 然后提供对应的getXxx()和setXxx()方法, 方便用户访问对应的成员变量, 接下来, 我们来写一个实际开发中的标准代码.
需求
- 定义一个标准的学生类Student, 属性: 姓名和年龄, 行为: 学习, 吃饭.
- 在测试类中创建学生类的对象, 然后访问类中的成员.
参考代码
public class Student {
//属性, 全部用private修饰.
//姓名
private String name;
//年龄
private int age;
public String getName() {
return name;
}
public void setName(String n) {
name = n;
}
public int getAge() {
return age;
}
public void setAge(int a) {
age = a;
}
//行为, 也就是成员方法.
public void study() {
System.out.println("键盘敲烂, 月薪过万!");
}
public void eat() {
System.out.println("学习饿了就要吃饭!.");
}
}
//测试类
public class StudentTest {
public static void main(String[] args) {
//1. 创建学生对象.
Student s = new Student();
//2. 设置成员变量值.
s.setName("张三");
s.setAge(23);
//3. 打印成员变量值.
System.out.println(s.getName() + "..." + s.getAge());
//4. 调用成员方法.
s.study();
s.eat();
}
}
6.3 封装的概述和好处
6.3.1 概述
封装是面向对象编程思想的三大特征之一, 所谓的封装指的就是隐藏对象的属性和实现细节, 仅对外提供一个公共的访问方式.
记忆:
== 面向对象的三大特征: 封装, 继承, 多态==.
问题一: 怎么隐藏?
通过private关键字实现.
问题二: 公共的访问方式是什么?
getXxx() 和 setXxx()方法.
6.3.2 原则
- 把不需要对外提供的内容都隐藏起来.
- 把属性隐藏, 并提供公共方法对其访问.
解释: 就是成员变量都用private修饰, 并提供对应的getXxx()和setXxx()方法, 除此以外, 都用public修饰.
6.3.3 好处
- 提高代码的安全性.
这点是由private关键字来保证的. - 提高代码的复用性.
这点是由方法来保证的, 方法也是封装的一种体现形式.
6.4 this关键字
6.4.1 概述
this代表本类当前对象的引用, 大白话翻译: 谁调用, this就代表谁.
6.4.2 作用
用来解决局部变量和成员变量重名问题的.
6.4.3 示例
需求
- 定义一个标准的学生类Student, 属性: 姓名和年龄, 行为: 学习, 吃饭.
- 在测试类中创建学生类的对象, 然后访问类中的成员.
参考代码
public class Student {
//属性, 全部用private修饰.
//姓名
private String name;
//年龄
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
//s.age = 23;
}
//行为, 也就是成员方法.
public void study() {
System.out.println("键盘敲烂, 月薪过万!");
}
public void eat() {
System.out.println("学习饿了就要吃饭!.");
}
}
//测试类
public class StudentTest {
public static void main(String[] args) {
//1. 创建学生对象.
Student s = new Student();
//2. 设置成员变量值.
s.setName("张三");
s.setAge(23);
//3. 打印成员变量值.
System.out.println(s.getName() + "..." + s.getAge());
//4. 调用成员方法.
s.study();
s.eat();
}
}
6.4.4 总结
Java中, 使用变量遵循就近原则, 局部位置有就使用, 没有就去本类的成员位置找, 有就使用, 没有就报错.
解释: 先这么记忆, 不严谨, 因为本类没有, 还会去父类中查找, 这点在继承部分再解释.
6.5 构造方法
6.5.1 概述
构造方法是用来创建对象的, 捎带着可以给对象的各个成员变量赋值.
大白话:
构造方法就是用来快速对对象的各个属性赋值的.
6.5.2 格式
- 构造方法名必须和类名完全一致(包括大小写).
- 构造方法没有返回值类型, 连void都不能写.
- 构造方法没有具体的返回值, 但是可以写return(实际开发, 一般不写).
public 类名(参数类型 参数名1, 参数类型 参数名2) { //这里可以写多个参数.
//给对象的各个属性赋值即可.
}
6.5.3 示例: 构造方法入门
需求
- 定义学生类Student, 在类的空参构造中打印一句话"这是构造方法".
- 在StudentTest测试类中, 创建学生类的对象, 并观察程序的运行结果.
小技巧: 创建对象的格式如下:
类名 对象名 = new 构造方法(参数列表);
参考代码
//学生类.
public class Student{
//类的空参构造.
public Student() {
System.out.println("这是构造方法");
}
}
//学生类的测试类
public class StudentTest{
public static void main(String[] args) {
Student s = new Student();
}
}
6.5.4 构造方法的注意事项
- 如果我们没有给出构造方法, 系统将给出一个默认的无参构造供我们使用.
- 如果我们给出了构造方法, 系统将不再提供默认的构造方法给我们使用.
- 这个时候, 如果我们还想使用无参构造, 就必须自己提供.
- 建议定义类时, 我们给出无参构造, 方便用户调用(实际开发都这么做的).
思考题
问: 给成员变量赋值有几种方式?
答: 1. 通过setXxx()方法实现(该方式不会创建新对象).
2. 通过构造方法实现(该方式会创建新对象).
6.6 标准的类的定义和使用
6.6.1 格式
以后在实际开发中, 也都是这样写的, 即: 标准的类的定义格式如下:
public class 类名{
//属性(成员变量), 全部用private修饰.
//构造方法, 一般提供两个(无参, 全参)
//getXxx()和setXxx()方法
//行为(成员方法), 根据需求来定义.
}
6.6.2 示例
需求
- 定义一个标准的学生类Student, 属性: 姓名和年龄, 行为: 学习, 吃饭.
- 在测试类中创建学生类的对象, 然后访问类中的成员.
参考代码
//学生类
public class Student {
//属性(成员变量), 全部用private修饰.
private String name; //姓名
private int age; //年龄
//构造方法, 一般提供两个(无参, 全参)
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
//getXxx()和setXxx()方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//行为(成员方法), 根据需求来定义.
public void study() {
System.out.println("键盘敲烂, 月薪过万!");
}
public void eat() {
System.out.println("学习饿了要吃饭");
}
}
//学生类的测试类
public class StudentTest {
public static void main(String[] args) {
//1. 创建学生对象.
Student s = new Student();
//2. 设置成员变量值.
s.setName("张三");
s.setAge(23);
//3. 打印成员变量值.
System.out.println(s.getName() + "..." + s.getAge());
//4. 调用成员方法.
s.study();
s.eat();
}
}
7. 继承
7.1 概述
多个类中存在相同属性和行为时, 将这些内容抽取到单独的一个类中, 那么这多个类就无需再定义这些属性和行为了, 只要继承那个类即可. 这个关系, 就叫继承.
注意:
有了继承以后, 我们在定义一个类的时候, 可以在一个已经存在的类的基础上, 还可以定义自己的新成员.
7.2 格式
在Java中, 可以通过extends关键字来实现类与类的继承, 具体格式如下:
public class 类A extends 类B { //子承父业
}
解释:
• 类A: 叫子类, 或者派生类.
• 类B: 叫父类, 基类, 或者超类.
我们一般会念做: 子类和父类.
7.3 继承的好处和弊端
7.3.1 示例
需求
- 按照标准格式定义一个人类(Person类), 属性为姓名和年龄.
- 定义老师类(Teacher), 继承自人类, 并在老师类中定义teach()方法.
- 定义学生类(Student), 继承自人类, 并在学生类中定义study()方法.
- 在PersonTest测试类的main方法中, 分别创建老师类和学生类的对象, 并调用各自类中的成员.
参考代码
人类代码:
public class Person {
//属性
private String name;
private int age;
//构造方法, 如果我们不写, 系统会默认加一个: 空参构造.
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//行为
public void eat() {
System.out.println("人要吃饭");
}
public void sleep() {
System.out.println("人要睡觉");
}
}
老师类代码:
public class Teacher extends Person {
//老师类独有的功能: 讲课
public void teach() {
System.out.println("老师要讲课!");
}
}
学生类代码:
public class Student extends Person {
//学生类独有的功能: 学习
public void study() {
System.out.println("好好学习, 天天向上!");
}
}
测试类代码
public class PersonTest {
public static void main(String[] args) {
//1. 测试老师类
Teacher t = new Teacher();
//属性
//从Person类继承.
t.setName("刘亦菲");
t.setAge(33);
System.out.println(t.getName() + "..." + t.getAge());
//方法
t.eat(); //从Person类继承.
t.sleep(); //从Person类继承.
t.teach(); //本类的独有功能.
//2. 测试学生类, 留给你自己写.
}
}
运行结果为:
刘亦菲...33
人要吃饭
人要睡觉
老师要讲课!
7.3.2 好处
- 提高了代码的复用性.
- 提高了代码的可维护性.
- 让类与类之间产生关系, 是多态的前提.
7.3.3 弊端
让类与类之间产生了关系, 也就让类的耦合性增强了.
解释:
开发原则: 高内聚, 低耦合.
• 内聚: 指的是类自己独立完成某些事情的能力.
• 耦合: 指的是类与类之间的关系.
7.4 Java中继承的特点
7.4.1 示例
- 定义GrandFather类, 该类有一个grandFatherSay()方法, 该方法打印一句话爷爷都是从孙子过来的.
- 定义Father类, 该类有一个fatherSay()方法, 该方法打印一句话爸爸都是从儿子过来的.
- 定义Son类, 分别先继承自GrandFather类和Father类, 此时发现, 上述的两个方法只能同时调用一个.
- 如果想让Son类的对象, 同时能调用上述的两个方法, 则可以这样做:
Son类继承自Father类, Father类继承自GrandFather类.
7.4.2 参考代码
//爷爷类,
//一个类如果没有显式的写父类, 默认就继承自Object类.
//Object类, 是所有类的父类.
public class GrandFather {
public void grandFather() {
System.out.println("grandFather....");
}
}
//父亲类
public class Father extends GrandFather{
public void father() {
System.out.println("father....");
}
}
//儿子类
public class Son extends Father {
}
//测试类
public class SonTest {
public static void main(String[] args) {
//创建子类对象.
Son s = new Son();
//问: Son的对象, 可以调用哪些方法?
s.father(); //从Father类继承过来的.
s.grandFather(); //从Father类继承过来的, 而Father类又从GrandFather类继承过来了.
}
}
7.4.3 总结
- Java中类与类之间只能单继承, 不能多继承.
public class 类A extends 类B,类C { //这种写法会报错.
}
- Java中类与类之间, 可以多层继承.
public class A { }
public class B extends A{ }
public class C extends B{ } //这种写法可以.
7.5 继承中成员变量的特点
7.5.1 示例
- 定义Father类, 在该类的成员位置定义变量: int age = 30;
- 定义Son类, 让它继承Father类, 并在该类的成员位置定义变量: int age = 20;
- 在测试类FatherTest的main方法中, 定义变量: int age = 10;
- 通过输出语句, 直接打印age变量的值, 并查看程序的运行结果.
7.5.2 参考代码
public class Father {
int age = 30;
}
public class Son extends Father {
int age = 20;
}
public class FatherTest {
public static void main(String[] args) {
int age = 10;
System.out.println(age); //这里打印结果会是多少呢?
}
}
7.5.3 总结
Java中使用变量遵循就近原则, 局部位置有就使用, 没有就去本类的成员位置找. 有就使用, 没有就去父类的成员位置找, 有就使用, 没有就报错.
注意: 不考虑父类的父类这种情况, 因为会一直往上找, 直到把所有的父类都找完, 还找不到, 就报错了.
7.6 super关键字
7.6.1 概述
super的用法和this很像:
• this: 代表本类对象的引用.
• super: 代表当前对象的父类的内存空间标识.
解释: 可以理解为父类对象引用.
7.6.2 用法
功能 | 本类 | 父类 |
---|---|---|
访问成员变量 | this.成员变量名 | super.成员变量名 |
访问构造方法 | this(…) | super(…) |
访问成员方法 | this.成员方法名(参数值…) | super.成员方法名(参数值…) |
7.7 方法重写
7.7.1 概述
子类中出现和父类一模一样的方法时, 称为方法重写. 方法重写要求返回值的数据类型也必须一样.
7.7.2 应用场景
当子类需要使用父类的功能, 而功能主体又有自己独有需求的时候, 就可以考虑重写父类中的方法了, 这样, 即沿袭了父类的功能, 又定义了子类特有的内容.
7.7.3 示例
需求
- 定义Phone类, 并在类中定义call(String name)方法.
- 定义NewPhone类, 继承Phone类, 然后重写call(String name)方法.
- 在PhoneTest测试类中, 分别创建两个类的对象, 然后调用call()方法, 观察程序执行结果.
参考代码
手机类
public class Phone {
//打电话的功能.
public void call(String name) {
System.out.println("给" + name + "打电话!...");
}
}
新手机类
//定义一个手机类, 表示: 新式手机.
public class NewPhone extends Phone{
//重写父类Phone#call()方法, 在打电话的时候, 播放彩铃.
@Override
public void call(String name) {
//因为基本的打电话的功能, 父类已经实现了, 我们可以直接拿来用.
//System.out.println("给" + name + "打电话!...");
//调用父类的call()方法, 实现打电话功能.
super.call(name);
//在父类call()方法的功能主体上, 加入自己独有的需求: 播放彩铃
System.out.println("播放彩铃...");
}
}
测试类
public class PhoneTest {
public static void main(String[] args) {
//1. 测试 老式手机.
Phone p = new Phone();
p.call("刘亦菲");
System.out.println("----------------");
//2. 测试 新式手机
NewPhone np = new NewPhone();
np.call("赵丽颖");
}
}
运行结果为:
给刘亦菲打电话!...
----------------
给赵丽颖打电话!...
播放彩铃...
7.7.4 注意事项
- 子类重写父类方法时, 方法声明上要用@Override注解来修饰.
- 父类中私有的方法不能被重写.
- 子类重写父类方法时, 访问权限不能更低.
8. 多态
8.1 概述
多态指的是同一个事物(或者对象)在不同时刻表现出来的不同状态.
例如: 一杯水.
• 常温下是液体.
• 高温下是气体.
• 低温下是固体.
但是水还是那杯水, 只不过在不同的环境下, 表现出来的状态不同.
8.2 前提条件
- 要有继承关系.
- 要有方法重写.
- 要有父类引用指向子类对象.
8.3 示例: 多态入门
需求
- 定义动物类Animal, 并在类中定义一个成员方法: eat()
- 定义猫类Cat, 继承Animal类, 并重写eat()方法.
- 在AnimalTest测试类的main方法中, 通过多态的方式创建猫类对象.
- 通过猫类对象, 调用eat()方法.
参考代码
动物类:
//父类, 动物类
public class Animal {
public void eat() {
System.out.println("动物要吃");
}
}
猫咪类:
//子类, 猫类
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼!");
}
public void catchMouse() {
System.out.println("猫会抓老鼠!....");
}
}
测试类:
public class AnimalTest {
public static void main(String[] args) {
//1 多态形式的创建猫类对象
Animal an = new Cat();
//2. 演示多态的弊端.
an.eat(); //编译看左 运行看右.
//3. 升级功能: 尝试通过多态版的 猫类对象, 调用猫类中独有的 catchMouse()功能.
//an.catchMouse(); //这样做不行, 因为an是Animal动物类的引用, 我们不能说 动物会抓老鼠
//4. 通过向下转型, 把 多态版的 猫类对象 -> Cat类型的猫类对象.
Cat c = (Cat)an; //引用类型的: 向下转型, 即: 大 -> 小.
c.catchMouse();
//int a = (int)10.3; 基本类型的: 强制类型转换, 即: 大 -> 小.
}
}
运行结果为:
猫吃鱼!
猫会抓老鼠!....
8.4 多态中的成员访问特点
• 成员变量: 编译看左边, 运行看左边.
• 成员方法: 编译看左边, 运行看右边.
需求
- 定义一个人类Person. 属性为姓名和年龄, 行为是: eat()方法.
- 定义Student类, 继承自Person类, 定义age属性及重写eat()方法.
- 在PersonTest测试类的main方法中, 创建Student类的对象, 并打印其成员.
参考代码
人类:
public class Person {
//属性
String name = "刘亦菲";
int age = 50;
//行为
public void eat() {
System.out.println("人要吃饭!");
}
}
学生类:
//子类, 学生类
public class Student extends Person{
int age = 30;
@Override
public void eat() {
System.out.println("学生吃牛肉!");
}
}
测试类:
public class PersonTest {
public static void main(String[] args) {
//1. 通过多态, 创建学生类对象.
Person p = new Student();
//2. 调用成员变量.
System.out.println(p.age); //50
//3. 调用成员方法.
p.eat(); //1学生吃牛肉
}
}
运行结果为:
50
学生吃牛肉!
8.5 好处和弊端.
8.5.1 好处
提高了程序的扩展性.
8.5.2 弊端
父类引用不能访问子类的特有功能.
问: 那如何解决这个问题呢?
答: 通过向下转型来解决这个问题.
8.5.3 示例
需求
- 定义动物类Animal, 该类有一个eat()方法.
- 定义猫类Cat, 继承自Animal类, 该类有一个自己独有的方法: catchMouse().
- 在AnimalTest测试类中, 通过多态的方式创建Cat类的对象, 并尝试调用catchMouse()方法.
参考代码
动物类:
//父类, 动物类
public class Animal {
public void eat() {
System.out.println("动物要吃");
}
}
猫咪类:
//子类, 猫类
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼!");
}
public void catchMouse() {
System.out.println("猫会抓老鼠!....");
}
}
测试类:
public class AnimalTest {
public static void main(String[] args) {
//1 多态形式的创建猫类对象
Animal an = new Cat();
//2. 演示多态的弊端.
an.eat(); //编译看左 运行看右.
//3. 升级功能: 尝试通过多态版的 猫类对象, 调用猫类中独有的 catchMouse()功能.
//an.catchMouse(); //这样做不行, 因为an是Animal动物类的引用, 我们不能说 动物会抓老鼠
//4. 通过向下转型, 把 多态版的 猫类对象 -> Cat类型的猫类对象.
Cat c = (Cat)an; //引用类型的: 向下转型, 即: 大 -> 小.
c.catchMouse();
//int a = (int)10.3; 基本类型的: 强制类型转换, 即: 大 -> 小.
}
}
8.5.4 向上转型和向下转型
向上转型
//格式
父类型 对象名 = new 子类型();
//例如
Animal an = new Cat();
向下转型
//格式
子类型 对象名 = (子类型)父类引用;
//例如
Cat c = (Cat)an;
小Bug
//下述代码会报: ClassCastException(类型转换异常)
Animal an = new Cat();
Cat c = (Cat)an; //这样写不报错.
Dog d = (Dog)an; //这样写会报错.
8.6 示例: 猫狗案例
需求
- 已知猫狗都有姓名和年龄, 都要吃饭.
- 猫类独有自己的catchMouse()方法, 狗类独有自己的lookHome()方法.
- 在AnimalTest测试类的main方法中, 通过多态分别创建猫类, 狗类的对象.
- 分别通过猫类对象和狗类对象, 访问对象的成员.
参考代码
动物类:
//父类, 动物类.
public class Animal {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
猫咪类:
//子类, 猫.
public class Cat extends Animal {
}
狗狗类:
//子类, 狗.
public class Dog extends Animal {
}
测试类:
public class AnimalTest {
public static void main(String[] args) {
//1. 多态的形式创建, 猫类对象.
Animal an = new Cat(); //向上转型.
//Animal an2 = new Dog(); //向上转型.
//2. 向下转型.
Cat c = (Cat)an; //这样写不报错.
//细节: 编译期间只检查语法问题, 所以下边这样代码在编译期间不会报错.
// Dog d = (Dog) an; //这样写报错, ClassCastException, 类型转换异常.
}
}
9. 两个关键字
9.1 final关键字
9.1.1 概述
final是一个关键字, 表示最终的意思, 可以修饰类, 成员变量, 成员方法.
• 修饰的类: 不能被继承, 但是可以继承其他的类.
• 修饰的变量: 是一个常量, 只能被赋值一次.
• 修饰的方法: 不能被子类重写.
9.1.2 示例
需求
- 定义Father类, 并定义它的子类Son.
- 先用final修饰Father类, 看Son类是否还能继承Father类.
- 在Son类中定义成员变量age, 并用final修饰, 然后尝试给其重新赋值, 并观察结果.
- 在Father类中定义show()方法, 然后用final修饰, 看Son类是否能重写该方法.
参考代码
父亲类:
public class Father {
//int age = 20; //变量
final int AGE = 20; //自定义常量
String name; //变量
public void show() {
System.out.println("这个是绝密文件, 不能动!");
}
}
儿子类:
//子类
public class Son extends Father {
@Override
public void show() {
System.out.println("这个是垃圾文件, 赶紧删除!");
}
}
测试类:
public class FatherTest {
public static void main(String[] args) {
Father f = new Father();
//f.age = 25; 常量只能赋值一次, 不能重复赋值.
System.out.println(f.AGE);
System.out.println("------------------");
Son s = new Son();
s.show();
}
}
运行结果为:
20
------------------
这个是垃圾文件, 赶紧删除!
9.2 static关键字
9.2.1 概述
static是一个关键字, 表示静态的意思, 可以修饰成员变量, 成员方法.
9.2.2 特点
- 随着类的加载而加载.
- 优先于对象存在.
- 被static修饰的内容, 能被该类下所有的对象共享.
解释: 这也是我们判断是否使用静态关键字的条件. - 可以通过类名.的形式调用.
9.2.3 示例
需求
- 定义学生类, 属性为姓名, 年龄, 毕业院校(graduateFrom).
- 在学生类中定义show()方法, 用来打印上述的各个属性信息.
- 在测试类的main方法中, 创建学生对象, 并调用学生类的各个成员.
参考代码
//学生类
public class Student {
//属性
String name;
int age;
static String graduateFrom;
//行为
public void show() {
System.out.println(name + ".." + age + ".." + graduateFrom);
}
}
//测试类
public class StudentTest {
public static void main(String[] args) {
//1. 设置学生的毕业院校.
Student.graduateFrom = "传智学院";
//2. 创建学生对象s1.
Student s1 = new Student();
s1.name = "刘亦菲";
s1.age = 33;
//3. 创建学生对象s2.
Student s2 = new Student();
s2.name = "赵丽颖";
s2.age = 31;
//4. 打印属性值.
s1.show();
s2.show();
}
}
9.2.4 静态方法的访问特点及注意事项
• 访问特点
静态方法只能访问静态的成员变量和静态的成员方法.
简单记忆: 静态只能访问静态.
• 注意事项
- 在静态方法中, 是没有this, super关键字的.
- 因为静态的内容是随着类的加载而加载, 而this和super是随着对象的创建而存在.
即: 先进内存的, 不能访问后进内存的.
需求
- 定义学生类, 属性为姓名和年龄(静态修饰), 非静态方法show1(),show2(), 静态方法show3(), show4().
- 尝试在show1()方法中, 调用: 姓名, 年龄, show2(), show4().
结论: 非静态方法可以访问所有成员(非静态变量和方法, 静态变量和方法) - 尝试在show3()方法中, 调用: 姓名, 年龄, show2(), show4().
结论: 静态方法只能访问静态成员.
参考代码
public class Student {
String name = "刘亦菲"; //非静态成员变量
static int age = 33; //静态的成员变量
public void show1() {
System.out.println(name);
System.out.println(age);
//this.show2(); 标准写法
show2();
//Student.show3(); 标准写法
show3(); //因为是访问本类的静态成员方法, 所以可以省略类名不写.
show4();
System.out.println("show1方法 非静态方法");
}
public void show2() {
System.out.println("show2方法 非静态方法");
}
public static void show3() {
//System.out.println(name); //非静态成员变量
System.out.println(age); //静态成员变量
//this.show2(); //非静态方法
Student.show4(); //静态方法
System.out.println("show3方法 静态方法");
}
public static void show4() {
System.out.println("show4方法 静态方法");
}
}
测试类:
public class StudentTest {
public static void main(String[] args) {
Student s = new Student();
//s.show1(); //结论: 非静态方法可以访问所有成员(非静态变量和方法, 静态变量和方法)
//s.show3(); 这样写不报错, 只是不推荐.
Student.show3(); //静态方法只能访问静态成员
}
}
运行结果为:
33
show4方法 静态方法
show3方法 静态方法
10. 抽象类
10.1 概述
回想前面我们的猫狗案例, 提取出了一个动物类, 这个时候我们可以通过Animal an = new Animal();
来创建动物对象, 其实这是不对的, 因为, 我说动物, 你知道我说的是什么动物吗? 只有看到了具体的动物, 你才知道, 这是什么动物. 所以说, 动物本身并不是一个具体的事物, 而是一个抽象的事物. 只有真正的猫, 狗才是具体的动物. 同理, 我们也可以推想, 不同的动物吃的东西应该是不一样的, 所以, 我们不应该在动物类中给出具体的体现, 而是应该给出一个声明即可. 在Java中, 一个没有方法体的方法应该定义为抽象方法, 而类中如果有抽象方法, 该类必须定义为抽象类
10.2 入门案例
需求
- 创建抽象类Animal.
- 在该类中定义抽象方法eat()
参考代码
//抽象的动物类.
public abstract class Animal {
//抽象方法, 吃.
public abstract void eat();
}
10.3 抽象类的特点
- 抽象类和抽象方法必须用abstract关键字修饰.
//抽象类
public abstract class 类名{ }
//抽象方法
public abstract void eat();
- 抽象类中不一定有抽象方法, 有抽象方法的类一定是抽象类.
- 抽象类不能实例化.
– 那抽象类如何实例化呢?
– 可以通过多态的方式, 创建其子类对象, 来完成抽象类的实例化. 这也叫: 抽象类多态. - 抽象类的子类:
- 如果是普通类, 则必须重写父抽象类中所有的抽象方法.
- 如果是抽象类, 则可以不用重写父抽象类中的抽象方法.
需求
- 定义抽象类Animal , 类中有一个抽象方法eat(), 还有一个非抽象方法sleep().
- 尝试在测试类中, 创建Animal类的对象, 并观察结果.
- 创建普通类Cat, 继承Animal类, 观察是否需要重写Animal#eat()方法.
参考代码
动物类:
//父类, 动物类
public abstract class Animal {
//1.类中有一个抽象方法eat(), 还有一个非抽象方法sleep().
public abstract void eat();
public void sleep() {
System.out.println("动物要睡!");
}
}
猫咪类:
public class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫吃鱼!");
}
}
测试类:
public class AnimalTest {
public static void main(String[] args) {
//1. 演示: 抽象类不能实例化.
//Animal an = new Animal(); 这样写会报错.
//2. 抽象类可以通过创建其子类对象的形式, 来完成抽象类的初始化.
//抽象类多态
Animal an = new Cat();
}
}
10.4 抽象类的成员特点
抽象类中可以有变量, 常量, 构造方法, 抽象方法和非抽象方法.
思考: 既然抽象类不能实例化, 那要构造方法有什么用?
答: 用于子类对象访问父类数据前, 对父类数据进行初始化.
需求
- 定义抽象类Person, 在类中定义变量age, 常量country, 空参, 全参构造.
- 在Person类中定义非抽象方法show(), 抽象方法eat().
- 在测试类的main方法中, 创建Person类的对象, 并调用类中的成员.
参考代码
人类:
public abstract class Person {
//变量
int age; //年龄
//常量
static final String COUNTRY = "中国"; //国籍
//构造方法
public Person() {
}
public Person(int age) {
this.age = age;
}
//getXxx(), setXxx()
//成员方法,
public void show() {
System.out.println("show方法 非抽象方法");
}
//抽象方法, 强制要求子类必须完成某些事情
public abstract void eat();
}
学生类:
//学生类
public class Student extends Person {
public Student() {
super();
}
public Student(int age) {
super(age);
}
@Override
public void eat() {
System.out.println("学生吃牛肉!...");
}
}
测试类:
public class PersonTest {
public static void main(String[] args) {
//1. 创建Person类型的对象.
//只能通过 抽象类多态的形式 实现.
Person p = new Student();
//2. 调用其成员.
System.out.println(p.age);
System.out.println(p.COUNTRY);
System.out.println(Person.COUNTRY);
p.show();
p.eat();
}
}
运行结果为:
0
中国
中国
show方法 非抽象方法
学生吃牛肉!...
10.5 案例: 老师类
10.5.1 需求
- 传智播客公司有基础班老师(BasicTeacher)和就业班老师(WorkTeacher), 他们都有姓名和年龄, 都要讲课.
- 不同的是, 基础班老师讲JavaSE, 就业班老师讲解JavaEE.
- 请用所学, 模拟该知识点.
10.5.2 分析
- 定义父类Teacher, 属性: 姓名和年龄, 行为: 讲课(因为不同老师讲课内容不同, 所以该方法是抽象的).
- 定义BasicTeacher(基础班老师), 继承Teacher, 重写所有的抽象方法.
- 定义WorkTeacher(就业班老师), 继承Teacher, 重写所有的抽象方法.
- 定义TeacherTest测试类, 分别测试基础班老师和就业班老师的成员.
10.5.3 参考代码
老师类:
//父类, 表示老师类.
public abstract class Teacher {
//属性.
private String name;
private int age;
//构造方法
public Teacher() {
}
public Teacher(String name, int age) {
this.name = name;
this.age = age;
}
//getXxx(), setXxx()
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//行为
//抽象方法, 讲课.
public abstract void teach();
}
基础班类:
//子类, 基础班老师
public class BasicTeacher extends Teacher{
//构造方法
public BasicTeacher() {
}
public BasicTeacher(String name, int age) {
super(name, age);
}
//重写Teacher#teach()
@Override
public void teach() {
System.out.println("基础班老师讲: JavaSE");
}
}
就业班类:
//子类, 就业班老师
public class WorkTeacher extends Teacher{
//构造方法
public WorkTeacher() {
}
public WorkTeacher(String name, int age) {
super(name, age);
}
//重写Teacher#teach()
@Override
public void teach() {
System.out.println("就业班老师讲: JavaEE, Linux, Hadoop...");
}
}
测试类:
//案例: 演示抽象类版的 老师类案例.
public class TeacherTest {
public static void main(String[] args) {
//1. 测试基础班老师, 空参.
//抽象类多态版
Teacher t1 = new BasicTeacher();
//BasicTeacher t1 = new BasicTeacher();
t1.setName("刘亦菲");
t1.setAge(33);
System.out.println(t1.getName() + "..." + t1.getAge());
t1.teach();
System.out.println("---------------------");
//2. 测试基础班老师, 全参.
Teacher t2 = new BasicTeacher();
t2.setName("赵丽颖");
t2.setAge(31);
System.out.println(t2.getName() + "..." + t2.getAge());
t2.teach();
//3. 测试就业班老师, 空参, 自己做.
//4. 测试就业班老师, 全参.
}
}
运行结果为:
刘亦菲...33
基础班老师讲: JavaSE
---------------------
赵丽颖...31
基础班老师讲: JavaSE
11. 接口
11.1 概述
继续回到我们的猫狗案例,我们想想狗一般就是看门,猫一般就是作为宠物了。但是,现在有很多的驯养员或者是驯兽师,可以训练出:猫钻火圈,狗跳高,狗做计算等。而这些额外的动作,并不是所有猫或者狗一开始就具备的,这应该属于经过特殊的培训训练出来的。所以,这些额外的动作定义到动物类中就不合适,也不适合直接定义到猫或者狗中,因为只有部分猫狗具备这些功能。
所以,为了体现事物功能的扩展性,Java中就提供了接口来定义这些额外功能,并不给出具体实现,将来哪些猫狗需要被训练,只需要这部分猫狗把这些额外功能实现即可。
11.2 特点
- 接口用interface关键字修饰.
- 类和接口之间是实现关系, 用implements关键字表示.
- 接口不能实例化.
– 那接口如何实例化呢?
– 可以通过多态的方式, 创建其子类对象, 来完成接口的实例化. 这也叫: 接口多态. - 接口的子类:
- 如果是普通类, 则必须重写父接口中所有的抽象方法.
- 如果是抽象类, 则可以不用重写父接口中的抽象方法.
需求
- 定义Jumpping接口, 接口中有一个抽象方法jump().
- 定义Cat类, 实现Jumpping接口, 重写jump()方法.
- 在测试类的main方法中, 创建Jumpping接口对象, 并调用其jump()方法
接口:
//定义Jumpping接口, 表示: 跳高的功能
public interface Jumpping {
//定义jump方法, 表示跳高.
public abstract void jump();
}
猫咪类:
public class Cat implements Jumpping {
public void catchMouse() {
System.out.println("猫会抓老鼠!");
}
@Override
public void jump() {
System.out.println("猫会跳高");
}
}
测试类:
public class CatTest {
public static void main(String[] args) {
//1. 创建猫类对象.
Cat c = new Cat();
//2. 调用猫类中的功能.
c.catchMouse();
c.jump(); //这个方法, 是Cat从Jumpping接口中继承过来的.
System.out.println("------------------");
//3. 测试接口多态
Jumpping jp = new Cat();
jp.jump();
//向下转型
Cat c2 = (Cat)jp;
c2.catchMouse();
}
}
运行结果为:
猫会抓老鼠!
猫会跳高
------------------
猫会跳高
猫会抓老鼠!
11.3 成员特点
接口中有且只能有常量或者抽象方法, 原因是因为:
• 成员变量有默认修饰符: public static final
• 成员方法有默认修饰符: public abstract
注意: 因为接口主要是扩展功能的, 而没有具体存在, 所有接口中是没有构造方法的.
记忆: JDK1.8的时候, 接口中加入了两个新的成员: 静态方法, 默认方法(必须用default修饰).
需求
定义接口Inter, 测试接口中的成员特点.
参考代码
接口类:
public interface Inter {
//变量的默认修饰符: public static final
public static final int age = 20;
//方法的默认修饰符: public abstract
public abstract void show();
//尝试编写接口中的构造方法(这样写会报错, 因为接口中没有构造方法)
//public Inter(){}
//以下是JDK1.8的新特性: 接口中加入了静态方法和默认方法.
public static void method() {
System.out.println("我是JDK1.8的新特性: 接口中可以写静态方法");
}
public default void method2() {
System.out.println("我是JDK1.8的新特性: 接口中可以写默认方法");
}
}
测试类:
public class InterTest {
public static void main(String[] args) {
//1. 测试接口中的成员变量 -> 其实是一个常量
//Inter.age = 50;
System.out.println(Inter.age);
}
}
运行结果为:
20
11.4 类与接口之间的关系
• 类与类之间: 继承关系, 只能单继承, 不能多继承, 但是可以多层继承.
• 类与接口之间: 实现关系, 可以单实现, 也可以多实现. 还可以在继承一个类的同时实现多个接口.
• 接口与接口之间: 继承关系, 可以单继承, 也可以多继承.
12. 运动员和教练案例
12.1 需求
- 已知有乒乓球运动员(PingPangPlayer)和篮球运动员(BasketballPlayer), 乒乓球教练(PingPangCoach)和篮球教练(BasketballCoach).
- 他们都有姓名和年龄, 都要吃饭, 但是吃的东西不同.
- 乒乓球教练教如何发球, 篮球教练教如何运球和投篮.
- 乒乓球运动员学习如何发球, 篮球运动员学习如何运球和投篮.
- 为了出国交流, 跟乒乓球相关的人员都需要学习英语.
- 请用所学, 模拟该知识.
12.2 代码:
人类:
//父类, 人类
public abstract class Person {
//属性, 姓名, 年龄.
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//行为: 抽象方法eat()
public abstract void eat();
}
教练类:
//Person类的子类, 表示教练类
public abstract class Coach extends Person{
//构造方法
public Coach() {
}
public Coach(String name, int age) {
super(name, age);
}
//自身的方法: teach()
public abstract void teach();
}
球员类:
//Person类的子类, 表示运动员类
public abstract class Player extends Person{
//构造方法
public Player() {
}
public Player(String name, int age) {
super(name, age);
}
//自身的方法: study()
public abstract void study();
}
说英语接口:
//定义接口, 表示: 说英语的功能
public interface SpeakEnglish {
void speak();
}
篮球教练类:
//定义类, 表示篮球教练
public class BasketballCoach extends Coach{
//构造方法
public BasketballCoach() {
}
public BasketballCoach(String name, int age) {
super(name, age);
}
//重写父类的抽象方法.
@Override
public void teach() {
System.out.println("篮球教练教如何运球和投篮");
}
@Override
public void eat() {
System.out.println("篮球教练吃牛肉, 喝牛奶");
}
}
篮球球员类:
//篮球运动员
public class BasketballPlayer extends Player{
//构造方法
public BasketballPlayer() {
}
public BasketballPlayer(String name, int age) {
super(name, age);
}
//重写父类的抽象方法
@Override
public void study() {
System.out.println("篮球运动员学习如何运球和投篮");
}
@Override
public void eat() {
System.out.println("篮球运动员吃羊肉, 喝羊奶");
}
}
乒乓球教练类:
//定义乒乓球教练类
public class PingPangCoach extends Coach implements SpeakEnglish {
//构造方法
public PingPangCoach() {
}
public PingPangCoach(String name, int age) {
super(name, age);
}
//重写父类(接口)的所有抽象方法
@Override
public void teach() {
System.out.println("乒乓球教练教如何发球");
}
@Override
public void eat() {
System.out.println("乒乓球教练吃大白菜, 喝小米粥");
}
@Override
public void speak() {
System.out.println("乒乓球教练需要学习英语");
}
}
乒乓球球员类:
//定义类, 表示乒乓球运动员
public class PingPangPlayer extends Player implements SpeakEnglish {
//构造方法
public PingPangPlayer() {
}
public PingPangPlayer(String name, int age) {
super(name, age);
}
//重写父类(接口)的所有的抽象方法
@Override
public void study() {
System.out.println("乒乓球运动员学习如何发球");
}
@Override
public void eat() {
System.out.println("乒乓球运动员吃小白菜, 喝大米粥");
}
@Override
public void speak() {
System.out.println("乒乓球运动员需要学习英语!");
}
}
测试类:
//案例: 运动员和教练类案例, 该类是一个测试类
public class PersonTest {
public static void main(String[] args) {
//1. 测试乒乓球运动员.
//接口多态
//SpeakEnglish se = new PingPangPlayer("马龙", 31);
//抽象类多态
//Person p = new PingPangPlayer("马龙", 31);
//Player p2 = new PingPangPlayer("马龙", 31);
//标准写法
PingPangPlayer p1 = new PingPangPlayer("马龙", 31);
//打印属性值.
System.out.println(p1.getName() + "..." + p1.getAge());
//调用方法
p1.eat();
p1.study();
p1.speak();
System.out.println("----------------------------");
//2. 测试乒乓球教练.
PingPangCoach p2 = new PingPangCoach("刘国梁", 45);
//打印属性值.
System.out.println(p2.getName() + "..." + p2.getAge());
//调用方法
p2.eat();
p2.teach();
p2.speak();
//3. 测试篮球运动员.
//4. 测试篮球教练.
}
}
运行结果为:
马龙...31
乒乓球运动员吃小白菜, 喝大米粥
乒乓球运动员学习如何发球
乒乓球运动员需要学习英语!
----------------------------
刘国梁...45
乒乓球教练吃大白菜, 喝小米粥
乒乓球教练教如何发球
乒乓球教练需要学习英语
13. 包
13.1 简述层
包(package)就是文件夹, 用来对类进行分类管理的. 例如:
• 学生的增加, 删除, 修改, 查询.
• 老师的增加, 删除, 修改, 查询.
• 其他类的增删改查…
• 基本的划分: 按照模块和功能划分.
• 高级的划分: 就业班做项目的时候你就能看到了.
13.2 格式
package 包名1.包名2.包名3; //多级包之间用.隔开
注意:
- package语句必须是程序的第一条可执行的代码.
- package语句在一个.java文件中只能有一个.
13.3 常见分类
• 按照功能分
– com.itheima.add
• AddStudent.java
• AddTeacher.java
– com.itheima.delete
• DeleteStudent.java
• DeleteTeacher.java
• 按照模块分
– com.itheima.student
• AddStudent
• DeleteStudent
– com.itheima.teacher
• AddTeacher
• DeleteTeacher
13.4 导包
13.4.1 概述
不同包下的类之间的访问,我们发现,每次使用不同包下的类的时候,都需要加包的全路径。比较麻烦。这个时候,java就提供了导包的功能。
13.4.2 格式
import 包名;
- import java.util.*; 意思是导入java.util包下所有类, 这样做效率较低, 不推荐使用.
- import java.util.Scanner; 意思是导入java.util.Scanner类, 推荐使用. 用谁就导入谁.
14. 权限修饰符
14.1 概述
权限修饰符是用来修饰类, 成员变量, 构造方法, 成员方法的, 不同的权限修饰符对应的功能不同. 具体如下:
public | protected | 默认 | private | |
---|---|---|---|---|
同一个类中 | √ | √ | √ | √ |
同一个包中的子类或者其他类 | √ | √ | √ | |
不同包中的子类 | √ | √ | ||
不同包中的其他类(无关类) | √ |
14.2 示例
需求
- 在com.itheima包下定义Father类, 该类中有四个方法, 分别是show1(), show2(), show3(), show4().
- 上述的四个方法分别用权限修饰符private, 默认, protected, public修饰.
- 在Father类中, 创建main方法, 然后创建Father类的对象, 并调用上述的4个方法, 并观察结果.
- 在com.itheima包下创建Test类及main方法, 然后创建Father类的对象, 并调用上述的4个方法, 并观察结果.
- 在com.itheima包下创建Father类的子类Son, 添加main方法.
- 在main方法中, 创建Son类的对象, 并调用上述的4个方法, 并观察结果.
- 在cn.itcast包下创建Father类的子类Son, 然后在该类中添加main方法.
- 在main方法中, 创建Son类的对象, 并调用上述的4个方法, 并观察结果.
- 在cn.itcast包下创建Test类及main方法, 然后创建Father类的对象, 并调用上述的4个方法, 并观察结果.
参考代码
father类:
public class Father {
private void show1() {
System.out.println("Father show1方法, private 修饰");
}
void show2() {
System.out.println("Father show2方法, 默认权限修饰符 修饰");
}
protected void show3() {
System.out.println("Father show3方法, protected 修饰");
}
public void show4() {
System.out.println("Father show4方法, public 修饰");
}
public static void main(String[] args) {
//1. 创建Father对象.
Father f = new Father();
//2. 尝试调用Father类中的方法.
f.show1();
f.show2();
f.show3();
f.show4();
}
}
son类:
//和Father的关系: 同包下的子类
public class Son extends Father {
public static void main(String[] args) {
//1. 创建Father对象.
Father f = new Father();
//2. 尝试调用Father类中的方法.
//f.show1();
f.show2();
f.show3();
f.show4();
System.out.println("------------------------------");
Son s = new Son();
//s.show1();
s.show2();
s.show3();
s.show4();
}
}
测试类:
public class Test {
public static void main(String[] args) {
//1. 创建Father对象.
Father f = new Father();
//2. 尝试调用Father类中的方法.
//f.show1();
f.show2();
f.show3();
f.show4();
}
}
运行结果为:
Father show2方法, 默认权限修饰符 修饰
Father show3方法, protected 修饰
Father show4方法, public 修饰
14.3 总结
- 访问权限修饰符的权限从大到小分别是: public > protected > 默认 > private
- 在实际开发中, 如果没有特殊需求, 则成员变量都用private修饰, 其它都用public修饰.
- 大白话总结四个访问权限修饰符的作用:
1. private: 强调的是给自己使用.
2. 默认: 强调的是给同包下的类使用.
3. protected: 强调的是给子类使用.
4. public: 强调的是给大家使用.
15. 作业
- 定义手机类Phone, 属性和行为如下, 并在测试类PhoneTest中, 创建手机类的对象, 然后访问类中的成员.
属性:
品牌: brand
价格: price
颜色: color
行为:
打电话: call(String name) //该方法接收一个"姓名"参数, 输出格式为: 给张三打电话…
发短信: sendMessage(String name) //该方法接收一个"姓名"参数, 输出格式为: 给张三发短信…
代码:
手机类:
public class Phone {
// 属性.成员变量
// 手机品牌
String brand;
// 价格
int price;
// 颜色
String color;
public Phone() {
}
public Phone(String brand, int price, String color) {
this.brand = brand;
this.price = price;
this.color = color;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
//行为,成员方法
//打电话
public void call(String name) {
System.out.println("给" + name + "打电话!");
}
// 发短信
public void sendMessage(String name) {
System.out.println("给" + name + "发短信!!");
}
}
测试类:
public class PhoneTest {
public static void main(String[] args) {
// 创建手机类的对象
Phone p = new Phone();
// 设置变量值
p.brand = "华为";
p.price = 5999;
p.color = "曜石黑";
// 打印成员变量
System.out.println(p.brand);
System.out.println(p.price);
System.out.println(p.color);
// 调用方法
p.call("落空空");
p.sendMessage("落空空");
}
}
运行结果为:
华为
5999
曜石黑
给落空空打电话!
给落空空发短信!!
- 定义老师类Teacher, 属性和行为如下, 并在测试类TeacherTest中, 创建老师类的对象, 然后访问类中的成员.
属性:
姓名: name
年龄: age
讲课内容: content
行为:
吃饭: eat()
讲课: teach()
老师类:
public class Teacher {
private String name;
private int age;
private String content;
public Teacher() {
}
public Teacher(String name, int age, String content) {
this.name = name;
this.age = age;
this.content = content;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
// 行为方法
// 吃饭
public void eat(){
System.out.println("年龄为" + age + "的" + name + "正在吃饭");
}
// 讲课
public void teach(){
System.out.println("年龄为" + age + "的" + name + "正在讲" + content);
}
}
测试类:
public class TeacherTest {
public static void main(String[] args) {
Teacher teacher = new Teacher("刘老师",32,"java");
// 打印行为
teacher.eat();
teacher.teach();
}
}
运行结果为:
年龄为32的刘老师正在吃饭
年龄为32的刘老师正在讲java
- 定义项目经理类Manager, 属性和行为如下, 并在测试类ManagerTest中, 创建项目经理类的对象, 然后访问类中的成员.
属性:
姓名: name
工号: id
工资: salary
奖金: bonus
行为:
工作: work()
经理类:
public class Manager {
private String name;
private int id;
private int salary;
private int bonus;
public Manager() {
}
public Manager(String name, int id, int salary, int bonus) {
this.name = name;
this.id = id;
this.salary = salary;
this.bonus = bonus;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
public int getBonus() {
return bonus;
}
public void setBonus(int bonus) {
this.bonus = bonus;
}
// 行为
public void work(){
System.out.println("工号为" + id + ",基本工资为" + salary + ",奖金为" + bonus + "的项目经理" + name + "正在努力工作!!!");
}
}
测试类:
public class ManagerTest {
public static void main(String[] args) {
Manager m1 = new Manager();
m1.setId(123);
m1.setSalary(16666);
m1.setBonus(6666);
m1.setName("张三");
m1.work();
Manager m2 = new Manager("李四",124,12345,1234);
m2.work();
}
}
运行结果为:
工号为123,基本工资为16666,奖金为6666的项目经理张三正在努力工作!!!
工号为124,基本工资为12345,奖金为1234的项目经理李四正在努力工作!!!