1. static
1.1 面向对象高级部分内容介绍
1.2 static:修饰成员变量
static:静态,可以修饰成员变量和成员方法。
类变量在计算机中的执行原理:类变量在计算机中的执行原理.mp4 链接: https://pan.baidu.com/s/1GbxQNu8xGV2iZtBjmU35dw?pwd=hxtn 提取码: hxtn
类变量在计算机中的执行原理
1.3 static:类变量的应用场景
在开发中,如果某个数据只需要一份,且希望能够被共享(访问、修改),则该数据可以定义成类变量来记住。
需求:要求用户类记住创建了多少个用户对象。
用户类:
package com.itheima.staticDemo;
/**
* @ClassName User
* @Description 用户类
* @Author 孙克旭
* @Date 2025/3/15 13:58
*/
public class User {
//类变量一般用public修饰
public static int number;
//每创建一个用户对象,都会执行一次构造器
public User() {
number++;
}
}
测试类:
package com.itheima.staticDemo;
/**
* @ClassName UserTest
* @Description 用户测试类
* @Author 孙克旭
* @Date 2025/3/15 14:01
*/
public class UserTest {
public static void main(String[] args) {
User u1 = new User();
User u2 = new User();
User u3 = new User();
User u4 = new User();
User u5 = new User();
System.out.println(User.number); //5
}
}
1.4 static:修饰成员方法
类方法在计算机中的执行原理:类方法在计算机中的执行原理.mp4 链接: https://pan.baidu.com/s/15rq4Qg041Cgh4e6HdikcNw?pwd=r3re 提取码: r3re
类方法在计算机中的执行原理
1.4.1 搞懂main方法
1.5 static:类方法的应用场景-工具类
类方法最常见的应用场景是做工具类。
- 工具类中的方法都是一些类方法,每个方法都是用来完成一个功能的,工具类是给开发人员共同使用的特殊类。
- 使用工具类的好处:提高了代码复用;调用方便,提高了开发效率。
- 工具类没有创建对象的需求,建议将工具类的构造器进行私有。
1.5.1 为啥工具类中使用类方法?而不是实例方法?
- 实例方法需要创建对象来调用,此时对象只是为了调用方法,对象占内存,这样会浪费内存。
- 类方法,直接用类名调用即可,调用方便,也能节省内存。
1.6 static:注意事项
package com.itheima.staticDemo;
/**
* @ClassName Student
* @Description 学生类
* @Author 孙克旭
* @Date 2025/3/15 13:14
*/
public class Student {
//类变量
static String name;
//实例变量(对象的变量)
int age;
//1.类方法中可以访问类成员(类变量、类方法),不能访问实例成员
//3.this关键字只能在实例方法中,不能被类方法调用
public static void print() {
name = "孙克旭";
// System.out.println(age); 访问失败
print1();
// print2(); 访问失败
// System.out.println(this); 报错
}
//类方法
public static void print1() {
System.out.println("Hello World1");
}
//2.实例方法中既可以访问类成员,可以访问实例成员
public void print2() {
System.out.println("Hello World2");
name = "孙克旭";
age = 10;
print1();
print3();
System.out.println(this);
}
//实例方法
public void print3() {
System.out.println("Hello World3");
}
}
1.7 单元测试
1.8 单元练习1
1.8.1 什么是static关键字?
static是java中的关系字,可以修饰成员变量,被称为静态变量,也叫类变量,也可以修饰成员方法,被称为静态方法,又叫类方法
1.8.2 static关键字有什么特点?
1.可以通过类名调用,也可以通过对象名调用,推荐使用类名调用
2.被类的所有对象共享
3.随着类的存在而存在,优先于对象存在
1.8.3 请分析如下程序编译是否会报错并说明原因?
public class Student {
public static void method() {
System.out.println("static的method方法");
show();
}
private void show() {
System.out.println("非静态的show方法");
}
}
会报错,静态方法method直接调用非静态方法show
1.8.4 编码题1
学生具有姓名、年龄、生日等属性(这些学生均为15岁),有学习的能力
要求:
①定义一个学生类,描述班级学生的班级、姓名、年龄、生日这些属性(私有)
②定义一个可以通过类名来调用学生学习的方法
③编写测试类通过类名调用学习方法(输出格式如下)
好好学习,天天向上
④创建两个学生对象张三和李四,在控制台输出学员的信息(输出格式如下)
张三,15岁,生日:9月12日
李四,15岁,生日:9月15日
学生类:
package com.itheima.staticDemo.work;
/**
* @ClassName Student
* @Description 学生类
* @Author 孙克旭
* @Date 2025/3/15 15:24
*/
public class Student {
private String name;
private final int age = 15;
private String birthday;
private String Class;
/**
* 类方法
*/
public static void study() {
System.out.println("好好学习,天天向上!");
}
@Override
public String toString() {
return name + "," + age + ", birthday:" + birthday;
}
public Student() {
}
public Student(String name, String birthday) {
this.name = name;
this.birthday = birthday;
}
public Student(String name, String birthday, String aClass) {
this.name = name;
this.birthday = birthday;
Class = aClass;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public String getBirthday() {
return birthday;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}
public void setClass(String aClass) {
Class = aClass;
}
}
测试类:
package com.itheima.staticDemo.work;
/**
* @ClassName Demo1
* @Description 编码题
* @Author 孙克旭
* @Date 2025/3/15 15:23
*/
public class Demo1 {
public static void main(String[] args) {
Student.study();
Student student = new Student("张三", "9月12日");
Student student2 = new Student("李四", "9月15日");
System.out.println(student);
System.out.println(student2);
}
}
1.8.5 编码题2
定义一个尊享无忧的学生类(Student),
①学生类的成员属性分别是姓名(name),年龄(age),语文成绩(yuwen),数学成绩(shuxue),班级(banji)(所有的学生都是尊享无忧班级)
②定义学习的方法(study)
③创一个测试类,并完成如下内容:
创建两个学生对象张三和李四,通过成员方法(study)将学生信息打印到控制台上。(格式如下)
张三,18岁,语文成绩:98,数学成绩:90,班级:尊享无忧
好好学习,天天向上
张三,19岁,语文成绩:98,数学成绩:90,班级:尊享无忧
好好学习,天天向上
学生类:
package com.itheima.staticDemo.work;
/**
* @ClassName Student
* @Description 学生类
* @Author 孙克旭
* @Date 2025/3/15 15:24
*/
public class Student {
private String name;
private int age;
private double yuwen;
private double shuxue;
public void study() {
System.out.println(name + "," + age + "岁,语文成绩:" + yuwen + ",数学成绩:" + shuxue + ",班级:尊享无忧");
System.out.println("好好学习,天天向上!");
}
public Student(String name, int age, double yuwen, double shuxue) {
this.name = name;
this.age = age;
this.yuwen = yuwen;
this.shuxue = shuxue;
}
}
测试类:
package com.itheima.staticDemo.work;
/**
* @ClassName Demo2
* @Description 编码题
* @Author 孙克旭
* @Date 2025/3/15 15:23
*/
public class Demo1 {
public static void main(String[] args) {
Student student = new Student("张三", 18, 98, 90);
Student student2 = new Student("李四", 19, 90, 89);
student.study();
student2.study();
}
}
1.8.6 编码题3
超市从新西兰引进一批牛肉,请定义一个类,有成员属性分别是部位,价格,产地(都是新西兰),创建一个测试类并完成如下内容:
① 创建对象通过成员方法将肉类信息打印到控制台上。(格式如下)
产自新西兰的上脑115元/斤
产自新西兰的吊龙105元/斤
② 在main方法中生成一个1-1000钱随机数(整数、单位:元),问最多能购买多少斤上脑并输出在控制台上,最多能购买多少斤吊龙并输出在控制台上。(不足1斤,则舍掉)
最终结果例如:
150元能买1斤上脑
150元能买1斤吊龙
牛肉类:
package com.itheima.staticDemo.work;
/**
* @ClassName Beef
* @Description 牛肉类
* @Author 孙克旭
* @Date 2025/3/15 16:18
*/
public class Beef {
private String part; //部位
private double price; //价格
public static String origin = "新西兰"; //产地
public void buy(int money) {
System.out.println(money + "元能买" + (int) (money / price) + "斤" + part);
}
public Beef(String part, double price) {
this.part = part;
this.price = price;
}
public String getPart() {
return part;
}
public void setPart(String part) {
this.part = part;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public static String getOrigin() {
return origin;
}
public static void setOrigin(String origin) {
Beef.origin = origin;
}
@Override
public String toString() {
return "产自" + origin + "的" + part + "==" + price + "元/斤";
}
}
测试类:
package com.itheima.staticDemo.work;
import java.util.Random;
/**
* @ClassName BeefTest
* @Description 牛肉测试类
* @Author 孙克旭
* @Date 2025/3/15 16:21
*/
public class BeefTest {
public static void main(String[] args) {
Random random = new Random();
Beef b1 = new Beef("上脑", 115);
Beef b2 = new Beef("吊龙", 105);
System.out.println(b1);
System.out.println(b2);
int money = random.nextInt(1000) + 1;
b1.buy(money);
b2.buy(money);
}
}
2. 设计模式&继承
2.1 类的成分:代码块
2.1.1 静态代码块
- 格式:static { }
- 特点:类加载时自动执行,由于类只会加载一次,所以静态代码快也只会执行一次。
- 作用:完成类的初始化,例如:对类变量的初始化赋值。
2.1.2 实例代码块
- 格式:{ }
- 特点:每次创建对象时,执行实例代码块,并在构造器前执行
- 作用:和构造器一样,都是用来完成对象的初始化的,例如:对实例变量进行初始化赋值
2.2 设计模式:概述、饿汉式单例
2.2.1 单例设计模式
确保一个类只有一个对象(对类做结扎手术)
饿汉式单例:
- 把类的构造器私有
- 定义一个类变量记住类的一个对象
- 定义一个类方法,返回对象
2.2.2 单例有啥应用场景?有啥好处?
- 任务管理器、获取运行时对象(Runtime)
- 在这些业务场景下,使用单例模式,可以避免浪费内存
2.3 设计模式:懒汉式单例
2.4 继承:概述
Java中提供了一个关键字extends,用这个关键字,可以让一个类和另一个类建立起父子关系。
- 继承的特点:子类能继承父类的非私有成员(成员变量、成员方法)
- 继承后对象的创建:子类的对象是由子类、父类共同完成的
继承的执行原理:继承的执行原理.mp4 链接: https://pan.baidu.com/s/1nX0noKghwJUdawW_1IAX7w?pwd=4g6k 提取码: 4g6k
继承的执行原理
2.5 继承:使用继承的好处
- 减少重复代码的编写
父类:
package com.itheima.extendsDemo;
/**
* @ClassName People
* @Description 父类
* @Author 孙克旭
* @Date 2025/3/15 21:16
*/
public class People {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
子类:
package com.itheima.extendsDemo;
/**
* @ClassName Teacher
* @Description 子类
* @Author 孙克旭
* @Date 2025/3/15 21:18
*/
public class Teacher extends People {
private String skill;
public String getSkill() {
return skill;
}
public void setSkill(String skill) {
this.skill = skill;
}
public void print() {
System.out.println(getName() + "具备的技能:" + skill);
}
}
测试类:
package com.itheima.extendsDemo;
/**
* @ClassName Test
* @Description 测试类
* @Author 孙克旭
* @Date 2025/3/15 21:24
*/
public class Test {
public static void main(String[] args) {
Teacher teacher = new Teacher();
teacher.setName("孙克旭");
teacher.setSkill("Java、Linux");
teacher.print();
}
}
2.6 继承:权限修饰符
- “任意包下的子类”:表示子类而不是子类对象
2.7 单元练习2
2.7.1 使用单例模式的好处有哪些?
实例控制,单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。
灵活性,因为类控制了实例化过程,所以类可以灵活更改实例化过程
2.7.2 下面程序编译是否会报错并说明原因?
public class fu {
private int a=45;
}
class zi extends fu {
public void show() {
System.out.println(a);
}
}
会报错,子类不能直接调用父类私有变量a
2.7.3 使用继承的好处有哪些?
提高了代码的复用性(多个类相同的成员可以放到同一个类里)
提高了代码的维护性(如果放大的代码需要修改,修改一处就行)
2.7.4 编码题1
模拟教学管理系统师生信息。
①定义Person类包含属性:姓名、年龄;和成员方法:getXxx方法,setXxx方法,显示基本信息showMsg方法
②定义Teacher类,继承Person包含属性:学科;和成员方法:getXxx方法,setXxx方法,讲课方法
③定义Student类,继承Person包含属性:分数和成员方法:getXxx方法,setXxx方法,考试方法
④最后定义测试类,输出案例如图:
People类:
package com.itheima.extendsDemo.work;
/**
* @ClassName Person
* @Description Person类
* @Author 孙克旭
* @Date 2025/3/16 8:00
*/
public class Person {
private String name; //姓名
private int age; // 年龄
public void showMsg() {
System.out.println("姓名:" + name + ",年龄:" + 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;
}
}
学生类:
package com.itheima.extendsDemo.work;
import com.itheima.extendsDemo.People;
/**
* @ClassName Student
* @Description 学生类
* @Author 孙克旭
* @Date 2025/3/16 8:04
*/
public class Student extends People {
private double score;
public void examination() {
System.out.println(getName() + "考试得了"+score);
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
}
教师类:
package com.itheima.extendsDemo.work;
import com.itheima.extendsDemo.People;
/**
* @ClassName Teacher
* @Description Teacher类
* @Author 孙克旭
* @Date 2025/3/16 8:03
*/
public class Teacher extends People {
private String subject; //subject
public void speak() {
System.out.println(getName() + "讲课" + subject);
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
}
测试类:
package com.itheima.extendsDemo.work;
/**
* @ClassName Test1
* @Description 测试类
* @Author 孙克旭
* @Date 2025/3/16 8:06
*/
public class Test1 {
public static void main(String[] args) {
Teacher teacher = new Teacher();
teacher.setName("王老师");
teacher.setSubject("Java");
teacher.speak();
Student student = new Student();
student.setName("孙克旭");
student.setScore(100);
student.examination();
}
}
2.7.5 编码题2
按照提示模拟汽车网站信息
①定义汽车Auto类
属性:品牌,车长,价格
②定义SUV继承Auto类,包含
(1)属性:小型车车长标准值:4295,中型车车长标准值:5070。
(2)定义判断车型方法
判断小型车:小于小型车车长标准值
判断大型车:大于中型车车长标准值
判断中型车:大于小型车车长标准值并且小于等于中型车车长标准值
③测试类中,创建若干SUV对象,保存到集合,遍历集合,输出中型SUV
输出示例如图:
Auto类:
package com.itheima.extendsDemo.work;
/**
* @ClassName Auto
* @Description Auto类
* @Author 孙克旭
* @Date 2025/3/16 8:15
*/
public class Auto {
private String brand; //品牌
private double length;//车长
private double price;// 价格
public Auto() {
}
public Auto(String brand, double length, double price) {
this.brand = brand;
this.length = length;
this.price = price;
}
@Override
public String toString() {
return "Auto{" +
"brand='" + brand + '\'' +
", length=" + length +
", price=" + price +
'}';
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public double getLength() {
return length;
}
public void setLength(double length) {
this.length = length;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
SUV类:
package com.itheima.extendsDemo.work;
/**
* @ClassName SUV
* @Description SUV
* @Author 孙克旭
* @Date 2025/3/16 8:28
*/
public class SUV extends Auto {
private int smallCarLength = 4295;
private int mediumCarLength = 5070;
public SUV(String brand, double length, double price) {
super(brand, length, price);
}
public String judge() {
double length = getLength();
if (length < smallCarLength) {
return "小型车";
} else if (length > mediumCarLength) {
return "大型车";
} else if (length > smallCarLength && length <= mediumCarLength) {
return "中型车";
} else {
return null;
}
}
}
测试类:
package com.itheima.extendsDemo.work;
import java.util.ArrayList;
/**
* @ClassName Test2
* @Description 测试类
* @Author 孙克旭
* @Date 2025/3/16 8:34
*/
public class Test2 {
public static void main(String[] args) {
ArrayList<SUV> suvs = new ArrayList<>();
suvs.add(new SUV("A", 5000, 52453));
suvs.add(new SUV("B", 4500, 5453));
suvs.add(new SUV("C", 4500, 452453));
suvs.add(new SUV("D", 6000, 4553));
suvs.add(new SUV("E", 4550, 452453));
for (int i = 0; i < suvs.size(); i++) {
SUV suv = suvs.get(i);
if ("中型车".equals(suv.judge())) {
System.out.println(suv);
}
}
}
}
2.7.6 编码题3
请使用面向对象的思想,设计自定义类,描述出租车和家用轿车的信息,要求如下。
①分析出租车和家用轿车的公共成员,提取出父类—汽车类(这里父类不写方法,可在学完方法重写后再优化)
分析:
出租车类属性包括:车型,车牌,所属出租公司;方法包括:启动,停止(输出相应对顾客的提醒问候语)
家用轿车类属性包括:车型,车牌,车主姓名;方法包括:启动,停止
②利用继承机制,实现出租车类和家用轿车类
③编写测试类,分别测试出租车类和家用轿车类对象的相关方法
④定义名为car的包存放汽车类,出租车类,家用轿车类和测试类
Car类:
package com.itheima.extendsDemo.work;
/**
* @ClassName Car
* @Description Car类
* @Author 孙克旭
* @Date 2025/3/16 8:45
*/
public class Car {
private String type; //车型
private String brand; //车牌
public void start() {
System.out.println("汽车启动!");
}
public void stop() {
System.out.println("汽车停止!");
}
public Car() {
}
public Car(String type, String brand) {
this.type = type;
this.brand = brand;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
}
Taxi类:
package com.itheima.extendsDemo.work;
/**
* @ClassName Taxi
* @Description 出租车类
* @Author 孙克旭
* @Date 2025/3/16 8:52
*/
public class Taxi extends Car {
private String belongingCompany; //所属公司
@Override
public void start() {
System.out.println("欢迎乘坐" + belongingCompany + "的车牌号为" + getBrand() + "的" + getType() + "型出租车,请系好安全带");
}
@Override
public void stop() {
System.out.println("感谢乘坐" + belongingCompany + "的车牌号为" + getBrand() + "的" + getType() +
"型出租车,下车时请带好随身物品");
}
public Taxi() {
}
public Taxi(String type, String brand, String belongingCompany) {
super(type, brand);
this.belongingCompany = belongingCompany;
}
public String getBelongingCompany() {
return belongingCompany;
}
public void setBelongingCompany(String belongingCompany) {
this.belongingCompany = belongingCompany;
}
}
家用汽车类:
package com.itheima.extendsDemo.work;
/**
* @ClassName FamilyCar
* @Description 家用轿车
* @Author 孙克旭
* @Date 2025/3/16 8:57
*/
public class FamilyCar extends Car {
private String CarOwnerName; //车主姓名
@Override
public void start() {
System.out.println("您现在驾驶的是" + CarOwnerName + "的车牌号为" + getBrand() + "的" + getType() + ",请系好安全带");
}
@Override
public void stop() {
System.out.println(CarOwnerName + "的车牌号为" + getBrand() + "的" + getType() + "行驶完毕");
}
public FamilyCar() {
}
public FamilyCar(String type, String brand, String carOwnerName) {
super(type, brand);
CarOwnerName = carOwnerName;
}
public String getCarOwnerName() {
return CarOwnerName;
}
public void setCarOwnerName(String carOwnerName) {
CarOwnerName = carOwnerName;
}
}
测试类:
package com.itheima.extendsDemo.work;
/**
* @ClassName Test3
* @Description 测试类
* @Author 孙克旭
* @Date 2025/3/16 9:00
*/
public class Test3 {
public static void main(String[] args) {
Taxi taxi = new Taxi("SUV", "京A.J888888", "传智播客");
taxi.start(); //欢迎乘坐传智播客的车牌号为京A.J888888的SUV型出租车,请系好安全带
taxi.stop(); //感谢乘坐传智播客的车牌号为京A.J888888的SUV型出租车,下车时请带好随身物品
FamilyCar familyCar = new FamilyCar("Tesla", "京A.J888888", "孙克旭");
familyCar.start(); //您现在驾驶的是孙克旭的车牌号为京A.J888888的Tesla,请系好安全带
familyCar.stop(); //孙克旭的车牌号为京A.J888888的Tesla行驶完毕
}
}
3. 继承
3.1 继承:特点详解
- Object类是所有类的祖宗类,所有类都是Object类的子类
3.2 继承:方法重写
子类重写一个方法名称、参数列表一样的方法,去覆盖父类的这个方法,这就是方法重写。
重写后,方法的访问,Java会遵循就近原则。
方法重写的其它注意事项
- 重写小技巧:使用Override注解,他可以指定java编译器,检查我们方法重写的格式是否正确,代码可读性也会更好。
- 子类重写父类方法时,访问权限必须大于或者等于父类该方法的权限(public>protected>缺省)。
- 重写的方法返回值类型,必须与被重写方法的返回值类型一样,或者范围更小。
- 私有方法、静态方法不能被重写,如果重写会报错的
方法重载的应用:
- 子类重写toString方法,输出对象的属性
3.3 继承:子类访问的特点
- 在子类方法中访问其他成员(成员变量、成员方法),是依照就近原则的。
- 如果子父类中,出现了重名的成员,会优先使用子类的,如果此时一定要在子类中使用父类怎么办?
- 可以通过super关键字,指定父类的成员:super.父类成员变量/父类成员方法
3.4 继承:子类构造器的特点
子类的全部构造器,都回先调用父类的构造器,再调用自己的。
- 默认情况下,子类全部构造器的第一行代码都是super()(写不写都有),它会调用父类的无参数构造器。
- 如果父类没有无参数构造器,则我们必须在子类构造器的第一行手写super(…),指定去调用父类的有参数构造器。
3.5 继承:子类构造器的常见应用
- 将多个属性通过父类和子类构造器进行赋值。
package com.itheima.extendsDemo;
/**
* @ClassName Demo1
* @Description 子类构造器
* @Author 孙克旭
* @Date 2025/3/16 11:15
*/
public class Demo1 {
public static void main(String[] args) {
Student student = new Student("孙克旭", 20, "学习");
/* 父类构造器
子类构造器*/
}
}
class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
System.out.println("父类构造器");
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;
}
}
class Student extends Person {
private String skill;
public Student() {
}
public Student(String name, int age, String skill) {
super(name, age);
System.out.println("子类构造器");
this.skill = skill;
}
public String getSkill() {
return skill;
}
public void setSkill(String skill) {
this.skill = skill;
}
}
子类构造器的执行原理:子类构造器的执行原理.mp4 链接: https://pan.baidu.com/s/1wSdxbqbVW1Xl4hRwX6ac3w?pwd=t1wq 提取码: t1wq
子类构造器的执行原理
3.6 其他:this()调用兄弟构造器
在类中,可以通过this()调用本类的其他构造器
3.7 单元测试
3.8 课后练习
3.8.1 题目一
分析事物的共性,并抽取出正确的继承体系
现有基础班老师(BaseTeacher)和就业班老师(WorkTeacher)两个类,两者都含有姓名和年龄两个属性,还有一个讲课的行为teach,但不同的是,基础班老师的teach方法输出【基础班老师讲JavaSE】,就业班老师的teach方法输出【就业班老师讲JavaEE】,请用代码实现。
运行结果:
张三老师…23
基础班老师讲JavaSE
李四老师…24
就业班老师讲JavaEE
##【训练目标】:
能够独立分析事物抽取出正确的继承体系
##【思路分析】:
- 两个类都有属性的姓名年龄,行为的teach方法均为共性内容,是否可以抽取?
- BaseTeacher和WorkTeacher作为子类,对于teach方法都有自己的实现方式,这时候需要做什么?
- 测试类创建两个类的对象,获取属性打印,并调用teach方法
Teacher类:
package com.itheima.extendsDemo.work1;
/**
* @ClassName Teacher
* @Description 教师类
* @Author 孙克旭
* @Date 2025/3/16 12:57
*/
public class Teacher {
private String name; //姓名
private int age; //年龄
/**
* 讲课
*/
public void teach() {
System.out.println("讲课");
}
public Teacher() {
}
public Teacher(String name, int age) {
this.name = name;
this.age = age;
System.out.println(getName() + "……" + getAge());
}
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;
}
}
BaseTeacher类:
package com.itheima.extendsDemo.work1;
/**
* @ClassName BaseTeacher
* @Description 基础班老师
* @Author 孙克旭
* @Date 2025/3/16 12:55
*/
public class BaseTeacher extends Teacher {
public BaseTeacher(String name, int age) {
super(name, age);
}
@Override
public void teach() {
System.out.println("【基础班老师讲JavaSE】");
}
}
WorkTeacher类:
package com.itheima.extendsDemo.work1;
/**
* @ClassName WorkTeacher
* @Description 就业班老师
* @Author 孙克旭
* @Date 2025/3/16 13:03
*/
public class WorkTeacher extends Teacher {
public WorkTeacher(String name, int age) {
super(name, age);
}
@Override
public void teach() {
System.out.println("【就业班老师讲JavaEE】");
}
}
测试类:
package com.itheima.extendsDemo.work1;
/**
* @ClassName Test
* @Description 测试类
* @Author 孙克旭
* @Date 2025/3/16 13:08
*/
public class Test {
public static void main(String[] args) {
BaseTeacher t1 = new BaseTeacher("孙克旭", 25);
t1.teach();
WorkTeacher t2 = new WorkTeacher("王老师", 40);
t2.teach();
}
}
3.8.2 题目二
结合继承的思想,分析下列需求并实现
- 定义项目经理类Manager 【属性:姓名 工号 工资 奖金】【行为:工作work】
- 定义程序员类Coder【属性:姓名 工号 工资】【行为:工作work】
- 要求:通过有参构造创建两个类的对象,并调用各自的work方法
运行结果:
姓名为:张三,工号为:9527,工资为:10000.0,的程序员正在编写代码
姓名为:李四,工号为:9528,工资为:15000.0,奖金为:2000.0,的项目经理正在管理程序员写代码
##【训练目标】:
能够抽取出正确的继承体系,并解决子类特有属性的初始化问题
##【思路分析】:
- 姓名 工号 工资属性,work行为均为共性内容,是否可以向上抽取?
- 项目经理类中的奖金属于特有的内容,不应该抽取到父类中,那该定义在哪里?
- 集合题目和运行结果,重写work方法的时候,里面应该打印属性,子类该通过什么方式获取属性打印?
##【参考步骤】:
- 将同性行为向上抽取出一个父类,Employee(员工类)
- 编写子类构造方法的时候,通过super访问父类构造方法初始化,单独在项目经理Manager类中定义奖金属性,其中姓名 工号 工资属性通过父类初始化,奖金属性自己初始化。
- 重写父类work方法,通过super.getXxx获取属性并拼接打印
- 创建子类对象,并调用work方法
员工类:
package com.itheima.extendsDemo.work1;
/**
* @ClassName Employee
* @Description 员工类
* @Author 孙克旭
* @Date 2025/3/16 13:29
*/
public class Employee {
private String name; //姓名
private int id; //工号
private double money; //工资
/**
* 工作
*/
public void work() {
System.out.print("姓名为" + name + ",工号为" + id + ",工资为" + money);
}
public Employee() {
}
public Employee(String name, int id, double money) {
this.name = name;
this.id = id;
this.money = money;
}
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 double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
}
经理类:
package com.itheima.extendsDemo.work1;
/**
* @ClassName Manager
* @Description 经理类
* @Author 孙克旭
* @Date 2025/3/16 13:31
*/
public class Manager extends Employee {
private double bonus; //奖金
@Override
public void work() {
super.work();
System.out.println("的项目经理正在管理程序员写代码");
}
public Manager() {
}
public Manager(String name, int id, double money, double bonus) {
super(name, id, money);
this.bonus = bonus;
}
public double getBonus() {
return bonus;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
}
程序员类:
package com.itheima.extendsDemo.work1;
/**
* @ClassName Coder
* @Description 程序员类
* @Author 孙克旭
* @Date 2025/3/16 13:34
*/
public class Coder extends Employee {
public Coder(String name, int id, double money) {
super(name, id, money);
}
@Override
public void work() {
super.work();
System.out.println("的程序员正在编写代码");
}
}
测试类:
package com.itheima.extendsDemo.work1;
/**
* @ClassName Test2
* @Description 测试类
* @Author 孙克旭
* @Date 2025/3/16 13:37
*/
public class Test2 {
public static void main(String[] args) {
Manager manager = new Manager("张三", 9527, 50000, 2000);
manager.work(); // 姓名为张三,工号为9527,工资为50000.0的项目经理正在管理程序员写代码
Coder coder = new Coder("孙克旭", 8628, 20000);
coder.work(); // 姓名为孙克旭,工号为8628,工资为20000.0的程序员正在编写代码
}
}
3.9 单元练习3
3.9.1 如何调用父类中的成员?
父类成员域由private修饰,那么在子类中不能直接访问父类成员域,但是可以通过父类中的公共方法访问以及修改父类成员域
3.9.2 继承中构造方法的访问有什么特点?
首先是构造方法的方法名要与类名一致。
构造方法无返回类型,void类型也不可以。
如果没有定义构造器,则会默认一个无参构造器。
与普通方法一样,构造器也支持重载。一个对象中是可以支持同时定义多个构造器,通过不同的参数列表来实现重载。
构造方法是不能被继承的,但是可以使用super来调用,且super必须声明在在子类构造方法中的首行。子类构造方法会默认调用父类无参构造器,如果父类没有无参构造器,则必须在子类构造器的第一行通过 super关键字指定调用父类的哪个构造器
3.9.3 编码题1
模拟简历信息
①定义一个Person类,有姓名(name)、年龄(age)、住址(address)属性,定义一个方法info描述个人信息
②编写学生类继承Person,重写info方法,描述自己的学生身份和个人信息
③编写老师类继承Person, 重写info方法,描述自己的老师身份和个人信息
④定义一个测试类,创建学生和老师对象,并分别调用info方法,把信息输出到控制台
Person类:
package com.itheima.extendsDemo.work2;
/**
* @ClassName Person
* @Description Person类
* @Author 孙克旭
* @Date 2025/3/16 13:56
*/
public class Person {
private String name;
private int age;
private String address;
public void info() {
System.out.println(name + ",年龄:" + age + ",地址:" + address);
}
public Person() {
}
public Person(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
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 getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
学生类:
package com.itheima.extendsDemo.work2;
/**
* @ClassName Student
* @Description 学生类
* @Author 孙克旭
* @Date 2025/3/16 14:00
*/
public class Student extends Person {
public Student(String name, int age, String address) {
super(name, age, address);
}
@Override
public void info() {
System.out.print("我是学生:");
super.info();
}
}
教师类:
package com.itheima.extendsDemo.work2;
/**
* @ClassName Teacher
* @Description 教师类
* @Author 孙克旭
* @Date 2025/3/16 14:02
*/
public class Teacher extends Person {
public Teacher(String name, int age, String address) {
super(name, age, address);
}
@Override
public void info() {
System.out.print("我是老师:");
super.info();
}
}
测试类:
package com.itheima.extendsDemo.work2;
/**
* @ClassName Test
* @Description 测试类
* @Author 孙克旭
* @Date 2025/3/16 14:02
*/
public class Test {
public static void main(String[] args) {
Student student = new Student("孙克旭", 24, "济南");
student.info(); //我是学生:孙克旭,年龄:24,地址:济南
Teacher teacher = new Teacher("王老师", 35, "北京");
teacher.info(); //我是老师:王老师,年龄:35,地址:北京
}
}
3.9.4 编码题2
定义一个动物类,有品种、年龄(age)等属性,用吃食物等行为
①在动物类的子类,鲸鱼和狗(需重写吃食物的方法)
②定义一个测试类,创建鲸鱼和狗对象,并分别调用eat方法,把信息输出到控制台
动物类:
package com.itheima.extendsDemo.work2;
/**
* @ClassName Animal
* @Description 动物类
* @Author 孙克旭
* @Date 2025/3/16 14:09
*/
public class Animal {
private String brand; //品种
private int age; //年龄
public void eat() {
System.out.println(age + "岁大的" + brand + "正在吃东西");
}
public Animal() {
}
public Animal(String brand, int age) {
this.brand = brand;
this.age = age;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
鲸鱼类:
package com.itheima.extendsDemo.work2;
/**
* @ClassName Whale
* @Description 鲸鱼类
* @Author 孙克旭
* @Date 2025/3/16 14:11
*/
public class Whale extends Animal{
public Whale(String brand, int age) {
super(brand, age);
}
}
狗类:
package com.itheima.extendsDemo.work2;
/**
* @ClassName Dog
* @Description 狗类
* @Author 孙克旭
* @Date 2025/3/16 14:13
*/
public class Dog extends Animal {
public Dog(String brand, int age) {
super(brand, age);
}
}
测试类:
package com.itheima.extendsDemo.work2;
/**
* @ClassName Test2
* @Description 测试类
* @Author 孙克旭
* @Date 2025/3/16 14:14
*/
public class Test2 {
public static void main(String[] args) {
Whale whale = new Whale("虎鲸", 3);
whale.eat(); //3岁大的虎鲸正在吃东西
Dog dog = new Dog("柴犬", 5);
dog.eat(); //5岁大的柴犬正在吃东西
}
}
4. 多态、关键字、抽象类
4.1 多态:概述
多肽是在继承/实现情况下的一种现象,表现:对象多态、行为多态
多态前提:
- 有继承/实现关系
- 存在父类引用子类对象
- 存在方法重写
多态的一个注意事项
- 多态是对象/行为的多态,Java中的属性(成员变量)不谈多态
4.2 多态:使用多态的好处
- 在多态形式下,右边对象是解耦合的,更便于扩展和维护。
- 定义方法时,使用父类类型的形参,可以接收一切子类对象,扩展性更强、更便利。
4.2.1 类型转换
4.3 前置知识:final关键字
- final关键字是最终的意思,可以修饰(类、方法、变量)
- 修饰类:该类被称为最终类,特点是不能被继承了。
- 修饰方法:该方法被称为最终方法,特点是不能被重写了。
- 修饰变量:该变量只能被赋值一次。
final修饰变量的注意:
- final修饰基本类型的变量,变量存储的数据不能被改变。
- final修饰引用类型的变量,变量存储的地址不能被改变,但地址所指向对象的内容是可以被改变的。
4.4 前置知识:常量
使用了static final修饰的成员变量就被称为常量;
作用:通常用于记录系统的配置信息。
使用常量的优势:
- 代码可读性更好,可维护性也更好。
- 程序编译后,常量会被“宏替换”:出现常量的地方全部会被替换成其记住的字面量
这样可以保证使用常量和直接用字面量的性能是一样的。
4.5 抽象类:认识抽象类
在Java中有一个关键字叫:abstract,它就是抽象的意思,可以用它修饰类、成员方法。
abstract修饰类,这个类就是抽象类;修饰方法,这个方法就是抽象方法。
抽象类的注意事项、特点:
- 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类。
- 类该有的成员(成员变量、方法、构造器)抽象类都可以有。
- 抽象类最主要的特点:抽象类不能创建对象,仅作为一种特殊的父类,让子类继承并实现。
- 一个类继承抽象类,必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
4.6 抽象类:使用抽象类的好处
- 两种主要的应用场景,一种是:用抽象类,我们可以把子类中相同的代码,包括方法签名都抽
上来,这样能更好的支持多态,以提高代码的灵活性。 - 一种是:反过来用,我们不知道系统未来具体的业务实现时,我们可以先定义抽象类,将来让
子类去继承实现,以方便系统的扩展。
4.7 抽象类:模板方法设计模式
1、定义一个抽象类
2、在里面定义2个方法
一个是模板方法:把相同代码放里面去。
一个是抽象方法:具体实现交给子类完成。
建议使用final关键字修饰模板方法,为什么?
- 模板方法是给对象直接使用的,不能被子类重写。一旦子类重写了模板方法,模板方法就失效了。
4.8 单元测试
4.9 单元练习4
4.9.1 下面代码编译时报错吗?如果报错怎么修改?
public abstract class Bike {
int colornum;
int brand;
int speed;
public abstract void speedup();
}
public class SpeedBike extends Bike { //位置一
public void speedup(); //位置二
}
会报错,在位置一处将SpeedBike类设为抽象类,同时将位置2处的speedup也设为抽象方法
或将位置二中speedup()方法中加入方法的实现
4.9.2 多态的前提条件是什么?
(1)有继承关系。
(2)有方法的重写。
(3)有父类引用指向子类对象。
4.9.3 多态的好处有哪些?
提高了代码的维护性(继承保证);提高了代码的扩展性
4.9.4 编码题1
①定义一个汽水类Drink(抽象类),汽水类中有一个味道的方法taste
②定义一个可口可乐类,Coco继承自汽水类,重写父类的taste方法,输出“我是可口可乐,我是甜汽水”;
③定义一个盐汽水类SaltDrink继承自汽水类,重写父类的taste方法,输出“我是盐汽水,我是咸的”;
④定义一个测试类,提供一个售卖汽水的方法sell,接收用户输入的值,如果是“甜的”,就卖给客户甜的汽水,否则卖给客户咸的汽水
汽水类:
package com.itheima.polymorphic;
/**
* @ClassName Drink
* @Description 汽水类
* @Author 孙克旭
* @Date 2025/3/16 17:53
*/
public abstract class Drink {
/**
* 味道
*/
public abstract void taste();
}
可口可乐类:
package com.itheima.polymorphic;
/**
* @ClassName Coco
* @Description 可口可乐类
* @Author 孙克旭
* @Date 2025/3/16 17:54
*/
public class Coco extends Drink {
@Override
public void taste() {
System.out.println("我是可口可乐,我是甜汽水");
}
}
咸汽水类:
package com.itheima.polymorphic;
/**
* @ClassName SaltDrink
* @Description 盐汽水类
* @Author 孙克旭
* @Date 2025/3/16 17:55
*/
public class SaltDrink extends Drink {
@Override
public void taste() {
System.out.println("我是盐汽水,我是咸的");
}
}
测试类:
package com.itheima.polymorphic;
import java.util.Scanner;
/**
* @ClassName Test1
* @Description 测试类
* @Author 孙克旭
* @Date 2025/3/16 17:56
*/
public class Test1 {
public static void main(String[] args) {
sell();
}
public static void sell() {
Scanner scanner = new Scanner(System.in);
System.out.println("您喜欢什么味道的饮料?");
String taste = scanner.next();
switch (taste) {
case "甜的":
Coco coco = new Coco();
coco.taste();
break;
case "咸的":
SaltDrink saltDrink = new SaltDrink();
saltDrink.taste();
break;
default:
break;
}
}
}
4.9.5 编码题2
① 定义一个图形类Picture(抽象类),需包含属性:边长(sideLength)、边数(sideCount), 需包含抽象方法:求周长getPerimeter方法、求面积getArea方法、显示图形信息show方法
② 定义图形类Picture的两个子类:圆形类Circle 和 矩形类Rect
圆形类Cirle需包含属性:圆心x(centerX)、圆心y (centerY)、半径(radius),重写求周长、求面积方法、显示信息方法,矩形类Rect需包含属性:长(length)、宽(width),重写求周长、求面积方法、显示信息方法
③ 定义测试类:测试方法中用多态的形式创建圆形类Cirle对象 和 矩形类Rect对象,分别调用show方法显示信息
图形类:
package com.itheima.polymorphic;
/**
* @ClassName Picture
* @Description 图形类
* @Author 孙克旭
* @Date 2025/3/16 19:04
*/
public abstract class Picture {
private int sideLength; //边长
private int sideCount; //边数
/**
* 求周长
*
* @return
*/
public abstract double getPerimeter();
/**
* 求面积
*
* @return
*/
public abstract double getArea();
/**
* 显示图形信息
*/
public abstract void show();
public Picture() {
}
public Picture(int sideLength, int sideCount) {
this.sideLength = sideLength;
this.sideCount = sideCount;
}
public int getSideLength() {
return sideLength;
}
public void setSideLength(int sideLength) {
this.sideLength = sideLength;
}
public int getSideCount() {
return sideCount;
}
public void setSideCount(int sideCount) {
this.sideCount = sideCount;
}
}
圆形类:
package com.itheima.polymorphic;
/**
* @ClassName Circle
* @Description 圆形类
* @Author 孙克旭
* @Date 2025/3/16 19:10
*/
public class Circle extends Picture {
private final double PI = 3.14;
private int centerX; //圆心x
private int centerY; //圆心y
private int radius; //半径
public Circle() {
}
public Circle(int radius) {
this.radius = radius;
}
public Circle(int centerX, int centerY, int radius) {
this.centerX = centerX;
this.centerY = centerY;
this.radius = radius;
}
public int getCenterX() {
return centerX;
}
public void setCenterX(int centerX) {
this.centerX = centerX;
}
public int getCenterY() {
return centerY;
}
public void setCenterY(int centerY) {
this.centerY = centerY;
}
public int getRadius() {
return radius;
}
public void setRadius(int radius) {
this.radius = radius;
}
@Override
public double getPerimeter() {
return 2 * PI * radius;
}
@Override
public double getArea() {
return PI * radius * radius;
}
@Override
public void show() {
System.out.println("圆的半径:" + radius + ",周长:" + getPerimeter() + ",面积:" + getArea());
}
}
矩形类:
package com.itheima.polymorphic;
/**
* @ClassName Rect
* @Description 矩形类
* @Author 孙克旭
* @Date 2025/3/16 19:19
*/
public class Rect extends Picture {
private int length; //长
private int width; //宽
public Rect() {
}
public Rect(int length, int width) {
this.length = length;
this.width = width;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
@Override
public double getPerimeter() {
return 2 * (length + width);
}
@Override
public double getArea() {
return length * width;
}
@Override
public void show() {
System.out.println("矩形的长:" + length + ",宽:" + width + ",周长:"
+ getPerimeter() + ",面积:" + getArea());
}
}
测试类:
package com.itheima.polymorphic;
/**
* @ClassName Test2
* @Description 测试类
* @Author 孙克旭
* @Date 2025/3/16 19:23
*/
public class Test2 {
public static void main(String[] args) {
Picture circle = new Circle(2);
circle.show(); //圆的半径:2,周长:12.56,面积:12.56
Picture rect = new Rect(5, 8);
rect.show(); //矩形的长:5,宽:8,周长:26.0,面积:40.0
}
}
4.9.6 编码题3
有一个游戏设计有以下需求:
① 父类:角色Role 是所有职业的父类(抽象类),需包含属性:角色的名字(name),
需包含抽象方法:伤害统计方法:public int attack() :返回值为角色的攻击对敌人的伤害值
② 定义Role的两个子类:法师Magician 和 战士Soldier
法师Magician需包含属性:魔法等级(magicLevel),
方法:重写父类attack(); 返回法师的攻击对敌人造成的伤害值。法师攻击伤害值为:魔法等级*魔法基本伤害值(固定为5)
战士Soldier需包含属性:攻击伤害值(attackPower),
方法:重写父类attack(); 返回战士的攻击对敌人造成的伤害值。战士的攻击伤害值为:其攻击伤害属性值
③ 定义Team类(表示组队):方法:addMember(); 增加一个队员(注意:组队成员最多为6人) 提示:利用一个数组属性,保存所有成员
方法:attackSum (); 表示组队所有成员进行攻击时,对敌人造成的总伤害值
④ 定义测试类:创建一个Magician对象和一个Soldier对象,以及一个小队Team ,将Magician和Soldier对象加入小队,然后调用小队的伤害计算方法
角色类:
package com.itheima.polymorphic;
/**
* @ClassName Role
* @Description 角色类
* @Author 孙克旭
* @Date 2025/3/16 19:33
*/
public abstract class Role {
private String name;
/**
* 攻击的伤害值
*
* @return
*/
public abstract int attack();
public Role() {
}
public Role(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
法师类:
package com.itheima.polymorphic;
/**
* @ClassName Magician
* @Description 法师
* @Author 孙克旭
* @Date 2025/3/16 19:37
*/
public class Magician extends Role {
private int magicLevel; //魔法等级
@Override
public int attack() {
// 魔法等级*魔法基本伤害值(固定为5)
return magicLevel * 5;
}
public Magician() {
}
public Magician(String name, int magicLevel) {
super(name);
this.magicLevel = magicLevel;
}
public int getMagicLevel() {
return magicLevel;
}
public void setMagicLevel(int magicLevel) {
this.magicLevel = magicLevel;
}
}
战士类:
package com.itheima.polymorphic;
/**
* @ClassName Soldier
* @Description 战士类
* @Author 孙克旭
* @Date 2025/3/16 19:39
*/
public class Soldier extends Role {
private int attackPower; //攻击伤害值
@Override
public int attack() {
return attackPower;
}
public Soldier() {
}
public Soldier(String name, int attackPower) {
super(name);
this.attackPower = attackPower;
}
public int getAttackPower() {
return attackPower;
}
public void setAttackPower(int attackPower) {
this.attackPower = attackPower;
}
}
队伍类:
package com.itheima.polymorphic;
import java.util.ArrayList;
/**
* @ClassName Team
* @Description 组多类
* @Author 孙克旭
* @Date 2025/3/16 19:42
*/
public class Team {
private ArrayList<Role> roles = new ArrayList<>();
/**
* 增加一个队员
*
* @param role
*/
public void addMember(Role role) {
if (roles.size() < 6) { //队伍人数未满
roles.add(role);
System.out.println("组队成功!");
} else {
System.out.println("队伍满员,加入失败!");
}
}
/**
* 计算队伍伤害总值
*
* @return
*/
public int attackSum() {
int sum = 0;
for (int i = 0; i < roles.size(); i++) {
Role role = roles.get(i);
sum += role.attack();
}
return sum;
}
}
测试类:
package com.itheima.polymorphic;
/**
* @ClassName Test3
* @Description 测试类
* @Author 孙克旭
* @Date 2025/3/16 19:50
*/
public class Test3 {
public static void main(String[] args) {
Team team = new Team();
team.addMember(new Magician("上官婉儿", 25));
team.addMember(new Soldier("关羽", 1500));
System.out.println(team.attackSum());
}
}
5. 接口
5.1 接口:认识接口
Java提供了一个关键字interface,用这个关键字可以定义出一个特殊的结构:接口。
注意:接口不能创建对象;接口是用来被类实现(implements)的,实现接口的类称为实现类。
一个类可以实现多个接口(接口可以理解成干爹),实现类实现多个接口,必须重写完全部接口的全部抽象
方法,否则实现类需要定义成抽象类。
5.2 接口:使用接口的好处
- 弥补了类单继承的不足,一个类同时可以实现多个接口。
- 让程序可以面向接口编程,这样程序员就可以灵活方便的切换各种业务实现。
5.3 接口:综合案例
学生类:
package com.itheima.interfaceDemo;
/**
* @ClassName Student
* @Description 学生类
* @Author 孙克旭
* @Date 2025/3/17 7:48
*/
public class Student {
private String name;
private char sex;
private double score;
public Student() {
}
public Student(String name, char sex, double score) {
this.name = name;
this.sex = sex;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
}
班级管理类:
package com.itheima.interfaceDemo;
import java.util.ArrayList;
/**
* @ClassName ClassManager
* @Description 班级管理类
* @Author 孙克旭
* @Date 2025/3/17 7:52
*/
public class ClassManager {
private ArrayList<Student> students = new ArrayList<>();
private StudentOperator studentOperator;
public ClassManager(StudentOperator studentOperator) {
this.studentOperator = studentOperator;
students.add(new Student("孙克旭", '男', 100));
students.add(new Student("老男孩", '男', 1000));
students.add(new Student("马林思维", '男', 999));
students.add(new Student("刘风凌", '男', 800));
students.add(new Student("图灵机", '男', 950));
}
/**
* 打印学生的全部信息
*/
public void printInfo() {
studentOperator.printInfo(students);
}
/**
* 打印学生的平均成绩
*/
public void printAvgScore() {
studentOperator.printAvgScore(students);
}
}
学生操作接口:
package com.itheima.interfaceDemo;
import java.util.ArrayList;
/**
* @ClassName StudentOperator
* @Description 学生操作接口
* @Author 孙克旭
* @Date 2025/3/17 8:01
*/
public interface StudentOperator {
void printInfo(ArrayList<Student> students);
void printAvgScore(ArrayList<Student> students);
}
学生操作实现类1:
package com.itheima.interfaceDemo;
import java.util.ArrayList;
/**
* @ClassName StudentOperatorImpl1
* @Description 学生操作接口实现类1
* @Author 孙克旭
* @Date 2025/3/17 8:04
*/
public class StudentOperatorImpl1 implements StudentOperator {
@Override
public void printInfo(ArrayList<Student> students) {
for (int i = 0; i < students.size(); i++) {
Student student = students.get(i);
System.out.println("姓名:" + student.getName() + ",性别:" + student.getSex() + ",成绩:" + student.getScore());
}
}
@Override
public void printAvgScore(ArrayList<Student> students) {
double allScore = 0;
for (int i = 0; i < students.size(); i++) {
Student student = students.get(i);
allScore += student.getScore();
}
System.out.println("平均分:" + (allScore / students.size()));
}
}
学生操作实现类2:
package com.itheima.interfaceDemo;
import java.util.ArrayList;
/**
* @ClassName StudentOperatorImpl2
* @Description 学生操作实现类2
* @Author 孙克旭
* @Date 2025/3/17 8:15
*/
public class StudentOperatorImpl2 implements StudentOperator {
@Override
public void printInfo(ArrayList<Student> students) {
int manCount = 0, womanCount = 0;
for (int i = 0; i < students.size(); i++) {
Student student = students.get(i);
char sex = student.getSex();
System.out.println("姓名:" + student.getName() + ",性别:" + sex + ",成绩:" + student.getScore());
if (sex == '男') {
manCount++;
} else {
womanCount++;
}
}
System.out.println("男生人数:" + manCount + ",女生人数:" + womanCount);
System.out.println("班级总人数:" + students.size());
}
@Override
public void printAvgScore(ArrayList<Student> students) {
double allScore = 0, max = students.get(0).getScore(), min = students.get(0).getScore();
for (int i = 0; i < students.size(); i++) {
Student student = students.get(i);
double score = student.getScore();
allScore += score;
if (score > max) {
max = score;
} else if (score < min) {
min = score;
}
}
System.out.println("最高分:" + max + ",最低分:" + min + ",平均分:" + (allScore - max - min) / (students.size() - 2));
}
}
测试类:
package com.itheima.interfaceDemo;
/**
* @ClassName Test
* @Description 测试类
* @Author 孙克旭
* @Date 2025/3/17 8:30
*/
public class Test {
public static void main(String[] args) {
ClassManager classManager = new ClassManager(new StudentOperatorImpl1());
classManager.printInfo();
classManager.printAvgScore();
System.out.println("--------------------------------");
ClassManager classManager1 = new ClassManager(new StudentOperatorImpl2());
classManager1.printInfo();
classManager1.printAvgScore();
}
}
5.4 接口:JDK8开始、新增的方法
5.5 接口:接口的多继承、使用接口的注意事项
5.6 单元测试
5.7 课后练习
5.7.1 题目1
请对下列代码进行补充,打印出接口中的变量num, 随后调用method方法,要求程序与最终运行结果吻合,(注意:打印num变量,不允许创建对象调用)
public class Demo1 {
/*
* 请编写程序, 打印出接口中的变量num, 随后调用method方法
* 要求程序与最终运行结果吻合
*/
public static void main(String[] args) {
}
}
interface Inter {
int num = 10;
void method();
}
【训练目标】:
能够熟知接口中的成员特点,并创建接口的实现类使用
【思路分析】:
- 接口中的num变量不允许创建对象,思考接口中的变量都默认被什么修饰?
- method方法是非静态的,非静态的方法只能创建对象调用,接口本身能创建对象吗?不能怎么办?
【参考步骤】: - 通过类名.调用接口中的num变量并打印
- 编写Inter接口的实现类,随后创建实现类对象,调用method方法
package com.itheima.interfaceDemo;
public class Demo1 {
public static void main(String[] args) {
System.out.println(Inter.num);
InterImpl inter = new InterImpl();
inter.method();
}
}
interface Inter {
int num = 10;
void method();
}
class InterImpl implements Inter {
@Override
public void method() {
System.out.println("method方法");
}
}
5.7.2 题目2
1.定义USB接口:(开启功能)(关闭功能)
2.定义笔记本类:(开机功能)(关机功能)
3.定义鼠标类:要符合USB接口
4.定义测试类:创建电脑对象,依次调用开机方法,使用USB设备, 关机方法
运行结果:
开机
连接鼠标的USB
断开鼠标的USB
关机
【训练目标】:
能够独立定义接口
【思路分析】:
笔记本类中使用USB设备的功能
【参考方案】:
【参考步骤】:
1.按照题目要求,定义USB接口、鼠标类、笔记本类,笔记本类中定义的使用usb设备方法,形参接受接口类型
2.定义测试类,创建笔记本类对象,根据案例运行结果调用内部方法。
public class Demo5 {
public static void main(String[] args) {
// 创建笔记本对象
Computer c = new Computer();
// 笔记本开机
c.start();
// 使用鼠标
c.useUSB(new Mouse());
// 笔记本关机
c.end();
}
}
// 定义USB接口
interface USB {
void open();
void close();
}
// 定义笔记本类
class Computer {
public void start(){
System.out.println("开机");
}
public void end(){
System.out.println("关机");
}
public void useUSB(USB u){
u.open();
u.close();
}
}
// 定义鼠标类
class Mouse implements USB{
@Override
public void open() {
System.out.println("连接鼠标的USB");
}
@Override
public void close() {
System.out.println("断开鼠标的USB");
}
}
5.7.3 题目3
请在main方法中通过多态创建对象,随后使用对象,使得程序符合最终运行结果
public class Demo1 {
/*
请在main方法中通过多态创建对象,随后使用对象,使得程序符合最终运行结果
*/
public static void main(String[] args) {
}
}
class Fu {
int num = 10;
public void method(){
System.out.println("父类的method方法");
}
}
class Zi extends Fu{
int num = 20;
public void method(){
System.out.println("子类的method方法");
}
public void show(){
System.out.println("子类的show方法");
}
}
运行结果:
10
子类的method方法
子类的show方法
【训练目标】:
能够使用多态的方式创建对象,并清楚多态的成员访问特点。
【思路分析】:
多态形式创建的对象,有什么弊端?遇到了这个弊端该如何解决?
【参考步骤】:
- 多态创建对象,然后直接使用父类引用,得到【10】【子类的method方法】
- 向下转型后,使用子类的引用去调用show方法
package com.itheima.interfaceDemo;
public class Demo2 {
public static void main(String[] args) {
Fu zi = new Zi();
System.out.println(zi.num); //10
zi.method(); //子类的method方法
Zi z = (Zi) zi;
z.show(); //子类的show方法
}
}
class Fu {
int num = 10;
public void method() {
System.out.println("父类的method方法");
}
}
class Zi extends Fu {
int num = 20;
@Override
public void method() {
System.out.println("子类的method方法");
}
public void show() {
System.out.println("子类的show方法");
}
}
5.7.4 题目4
定义人(Person)类,人类中有吃饭方法(eat)和工作方法(work)。
定义超人(SuperMan)类,超人类中有救人方法(save)并且超人也属于人类。
要求:使用多态的方式创建超人对象,调用吃饭和工作的方法。再将此对象转为超人对象,调用救人的方法。
##【训练目标】:
能够使用多态的方式创建对象,能完成向下转型。
##【思路分析】:
人类和超人类有什么样的关系?
##【参考步骤】:
- 创建人类,人类中定义吃饭方法和工作方法。
- 创建超人类,继承人类,并定义自己特有的救人方法。
- 在测试类中,使用父类引用指向子类对象的方式创建对象。
- 分别调用父类的吃饭方法和工作方法。
- 将对象向下转型成超人对象。
- 使用子类对象调用救人方法。
Person类:
package com.itheima.interfaceDemo.work;
/**
* @ClassName Person
* @Description Person类
* @Author 孙克旭
* @Date 2025/3/17 9:51
*/
public class Person {
/**
* 吃饭
*/
public void eat() {
System.out.println("人是铁饭是钢,一顿不吃饿的慌!");
}
/**
* 工作
*/
public void work() {
System.out.println("劳动最光荣!");
}
}
超人类:
package com.itheima.interfaceDemo.work;
/**
* @ClassName SuperMan
* @Description 超人类
* @Author 孙克旭
* @Date 2025/3/17 9:53
*/
public class SuperMan extends Person {
/**
* 救人
*/
public void save() {
System.out.println("惩恶扬善,造福一方!");
}
}
测试类:
package com.itheima.interfaceDemo.work;
/**
* @ClassName Test2
* @Description 测试类
* @Author 孙克旭
* @Date 2025/3/17 9:54
*/
public class Test2 {
public static void main(String[] args) {
Person man = new SuperMan();
man.eat();
man.work();
SuperMan superMan = (SuperMan) man;
superMan.save();
}
}
5.8 单元练习5
5.8.1 抽象类是否可实现(implements)接口?需要重写抽象方法吗?
可以,并且因为抽象类允许有抽象方法,所以不是一定要重写抽象方法
5.8.2 接口和抽象类的联系和区别?
联系主要体现在:
抽象类:
(1)一个类中有抽象方法,这个类就变成了抽象类。
(2)抽象类中class的前面必须有abstract修饰符。
(3)抽象类中可以有普通方法,也可以有抽象方法,而抽象方法的个数可以是0个,也可以是多个。
(4)子类继承父类,必须重写全部的抽象方法,除非这个类也变成了抽象类。
接口:
(1)表面上看,接口是一种特殊的抽象类,但是类是类,接口是接口,是并列的关系。
(2)接口中所有方法都必须是抽象的。
(3)接口中方法定义默认为public abstract类型,成员变量默认为public static final 类型。(如果省略,系统会默认补全)。
区别主要体现在:
(1)抽象类可以有构造方法,接口中不能有构造方法。
(2)抽象类中可以有成员变量,接口中没有成员变量。(被final修饰变成了常量)
(3)抽象类中可以有普通方法,接口中所有方法都必须是抽象的。
(4)抽象类中抽象方法的访问类型可以是public,protected,但接口中抽象方法的访问类型只能是public,并且默认为public abstract(省略则自动默认补全)。
(5)抽象类中可以有静态方法,接口中不能有静态方法。
5.8.3 接口的优点有哪些?
减少代码的书写(上边分析的代码重载)
提高了代码的可维护性和扩展性。
在团队合作中,代码的规范性
5.8.4 编码题1
按如下要求编写Java程序::
① 定义接口A,里面包含值为3.14的常量PI和 抽象方法double area();计算面积
② 定义接口B,里面包含 抽象方法void setColor(String c);设置颜色
③ 定义接口C,该接口继承了接口A和B,里面包含 抽象方法 double volume();计算体积
④ 定义圆柱体类Cylinder实现接口C,该类中包含三个成员变量:底圆半径radius、圆柱体的高height、颜色color,并进行方法重写(area方法计算圆柱体的侧面积,setColor方法设置颜色,volume方法计算圆柱体的体积)
⑤定义测试类及测试方法,创建Cylinder对象,打印该圆柱体对象的侧面积、体积及颜色
提示:圆柱体的侧面积计算公式为:S侧=2πrh 圆柱体的体积计算公式为:V=πr²h
接口A:
package com.itheima.interfaceDemo.work2;
/**
* @ClassName A
* @Description 接口A
* @Author 孙克旭
* @Date 2025/3/17 10:09
*/
public interface A {
double PI = 3.14;
/**
* 计算面积
*
* @return
*/
double area();
}
接口B:
package com.itheima.interfaceDemo.work2;
/**
* @ClassName B
* @Description 接口B
* @Author 孙克旭
* @Date 2025/3/17 10:10
*/
public interface B {
/**
* 设置颜色
*
* @param c
*/
void setColor(String c);
}
接口C:
package com.itheima.interfaceDemo.work2;
/**
* @ClassName C
* @Description 接口C
* @Author 孙克旭
* @Date 2025/3/17 10:11
*/
public interface C extends A, B {
/**
* 计算体积
*
* @return
*/
double volume();
}
圆柱体类:
package com.itheima.interfaceDemo.work2;
/**
* @ClassName Cylinder
* @Description 圆柱体类
* @Author 孙克旭
* @Date 2025/3/17 10:12
*/
public class Cylinder implements C {
private int radius; //底圆半径
private int height; //高
private String color; //颜色
/**
* 圆柱体的侧面积
*
* @return
*/
@Override
public double area() {
return 2 * PI * radius * height;
}
@Override
public void setColor(String c) {
this.color = c;
}
/**
* 圆柱体的体积
*
* @return
*/
@Override
public double volume() {
return PI * radius * radius * height;
}
public Cylinder() {
}
public Cylinder(int radius, int height, String color) {
this.radius = radius;
this.height = height;
this.color = color;
}
public int getRadius() {
return radius;
}
public void setRadius(int radius) {
this.radius = radius;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public String getColor() {
return color;
}
}
测试类:
package com.itheima.interfaceDemo.work2;
/**
* @ClassName Test
* @Description 测试类
* @Author 孙克旭
* @Date 2025/3/17 10:19
*/
public class Test {
public static void main(String[] args) {
Cylinder cylinder = new Cylinder(3, 6, "红色");
System.out.println(cylinder.area());
System.out.println(cylinder.volume());
}
}
5.8.5 编码题2
利用接口做参数,写个计算器,能完成加减乘除运算。
①定义一个接口Compute含有一个方法int computer(int n, int m)。
②设计四个类分别实现此接口,完成加减乘除运算。
③设计一个类UseCompute,类中含有方法:public void useCom(Compute com, int one, int two),此方法能够用传递过来的对象调用computer方法完成运算,并输出运算的结果。
④设计一个主类Test,调用UseCompute中的方法useCom来完成加减乘除运算
计算器接口:
package com.itheima.interfaceDemo.work2;
/**
* @ClassName Compute
* @Description 计算器接口
* @Author 孙克旭
* @Date 2025/3/17 10:48
*/
public interface Compute {
/**
* 计算
*
* @param n
* @param m
* @return
*/
double computer(int n, int m);
}
加法类:
package com.itheima.interfaceDemo.work2;
/**
* @ClassName Addition
* @Description 加法类
* @Author 孙克旭
* @Date 2025/3/17 11:01
*/
public class Addition implements Compute {
@Override
public double computer(int n, int m) {
return n + m;
}
}
减法类:
package com.itheima.interfaceDemo.work2;
/**
* @ClassName Subtraction
* @Description 减法类
* @Author 孙克旭
* @Date 2025/3/17 11:02
*/
public class Subtraction implements Compute {
@Override
public double computer(int n, int m) {
return n - m;
}
}
乘法类:
package com.itheima.interfaceDemo.work2;
/**
* @ClassName Multiplication
* @Description 乘法类
* @Author 孙克旭
* @Date 2025/3/17 11:03
*/
public class Multiplication implements Compute {
@Override
public double computer(int n, int m) {
return n * m;
}
}
除法类:
package com.itheima.interfaceDemo.work2;
/**
* @ClassName Division
* @Description 除法类
* @Author 孙克旭
* @Date 2025/3/17 11:04
*/
public class Division implements Compute {
@Override
public double computer(int n, int m) {
return 1.0 * n / m;
}
}
计算器使用类:
package com.itheima.interfaceDemo.work2;
/**
* @ClassName UseCompute
* @Description 计算器使用类
* @Author 孙克旭
* @Date 2025/3/17 11:06
*/
public class UseCompute {
public void useCom(Compute com, int one, int two) {
double result = com.computer(one, two);
String flag;
if (com instanceof Addition) {
flag = "+";
} else if (com instanceof Subtraction) {
flag = "-";
} else if (com instanceof Multiplication) {
flag = "*";
} else {
flag = "/";
}
System.out.println(one + " " + flag + " " + two + " is " + result);
}
}
测试类:
package com.itheima.interfaceDemo.work2;
/**
* @ClassName Test2
* @Description 计算器测试类
* @Author 孙克旭
* @Date 2025/3/17 11:12
*/
public class Test2 {
public static void main(String[] args) {
UseCompute useCompute = new UseCompute();
useCompute.useCom(new Addition(), 10, 5); // 10 + 5 is 15.0
useCompute.useCom(new Subtraction(), 10, 5); //10 - 5 is 5.0
useCompute.useCom(new Multiplication(), 10, 5); // 10 * 5 is 50.0
useCompute.useCom(new Division(), 10, 5); // 10 / 5 is 2.0
}
}
5.8.6 编码题3
现有一个5星级酒店,按照要求完成代码:
①定义抽象类员工(Employee),具有姓名name,工号id等属性
②接口Service,实现这一接口表示具有vip服务
③再定义厨师Cooker类,服务员Waiter类,经理Manager类,三个员工都有姓名name,工号id的属性,只有厨师和服务员有vip服务,厨师vip服务加菜,服务员嘘寒问暖,
④定义程序实现功能并测试
员工类:
package com.itheima.interfaceDemo.work3;
/**
* @ClassName Employee
* @Description 员工类
* @Author 孙克旭
* @Date 2025/3/17 11:52
*/
public abstract class Employee {
private String name;
private int id;
public Employee() {
}
public Employee(String name, int id) {
this.name = name;
this.id = id;
}
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;
}
}
服务接口:
package com.itheima.interfaceDemo.work3;
/**
* @ClassName Service
* @Description 服务接口
* @Author 孙克旭
* @Date 2025/3/17 11:53
*/
public interface Service {
void vip();
}
厨师类:
package com.itheima.interfaceDemo.work3;
/**
* @ClassName Cooker
* @Description 厨师类
* @Author 孙克旭
* @Date 2025/3/17 11:54
*/
public class Cooker extends Employee implements Service {
public Cooker(String name, int id) {
super(name, id);
}
@Override
public void vip() {
System.out.println("加菜");
}
}
服务员类:
package com.itheima.interfaceDemo.work3;
/**
* @ClassName Waiter
* @Description 服务员类
* @Author 孙克旭
* @Date 2025/3/17 11:55
*/
public class Waiter extends Employee implements Service {
public Waiter(String name, int id) {
super(name, id);
}
@Override
public void vip() {
System.out.println("嘘寒问暖");
}
}
经理类:
package com.itheima.interfaceDemo.work3;
/**
* @ClassName Manager
* @Description 经理类
* @Author 孙克旭
* @Date 2025/3/17 11:56
*/
public class Manager extends Employee {
public Manager(String name, int id) {
super(name, id);
}
}
测试类:
package com.itheima.interfaceDemo.work3;
/**
* @ClassName Test
* @Description 测试类
* @Author 孙克旭
* @Date 2025/3/17 11:58
*/
public class Test {
public static void main(String[] args) {
Employee e1 = new Cooker("张三", 100);
Cooker e = (Cooker) e1;
e.vip();
}
}
6. 内部类、枚举
6.1 内部类:概述、成员内部类
内部累是类中的五大成分之一(成员变量、方法、构造器、内部类、代码块),如果一个类定义在另一个类的内部,这个类就是内部类。
场景:当一个类的内部,包含了一个完整的事物,且这个事物没有必要单独设计时,就可以把这个事物设计成内部类。
6.1.1 成员内部类
6.1.2 静态内部类
6.1.3 局部内部类
6.1.4 匿名内部类
匿名内部类就是一种特殊的局部内部类;所谓匿名:指的是程序员不需要为这个类声明名字。
- 特点:匿名内部类本质就是一个子类,并会立即创建出一个子类对象。
- 作用:用于更方便的创建一个子类对象。
6.2 内部类:匿名内部类的使用场景
6.3 枚举:认识枚举
枚举是一种特殊的类。
6.4 枚举:枚举的作用
6.5 单元测试
6.6 单元练习6
6.6.1 请问有几种形式的内部类?
4种;分别是成员内部类、静态成员内部类、局部内部类、匿名内部类
6.6.2 请问内部类会被编译成class文件吗?
在编译一个类的时候,假如该类中有内部类,则会直接生成 类$内部类.class 文件
6.6.3 枚举的优点有哪些?
1 增强代码可读性
2 传递参数错误
3 去除equals两者判断 由于常量值地址唯一,使用枚举可以直接通过“==”进行两个值之间的对比,性能会有所提高。
4 编译优势(与常量类相比)
常量类编译时,常量被直接编译进二进制代码中,常量值在升级中变化后,需要重新编译引用常量的类,因为二进制代码中存放的是旧值。枚举类编译时,没有把常量值编译到代码中,即使常量值发生改变,也不会影响引用常量的类。
5 修改优势(与常量类相比)
枚举类编译后默认final class,不允许继承可防止被子类修改。常量类可被继承修改、增加字段等,易导致父类不兼容。
6 枚举型可直接与数据库交互。
7 Switch语句优势
使用int、String类型switch时,当出现参数不确定的情况,偶尔会出现越界的现象,这样我们就需要做容错操作(if条件筛选等),使用枚举,编译期间限定类型,不允许发生越界
6.6.4 编码题1
利用接口做参数,写个计算器,能完成加减乘除运算:
① 定义一个接口Compute含有一个 方法int computer(int n, int m);
②设计一个类UseCompute,类中含有 方法:public void useCom(Compute com, int one, int two),
此方法能够用传递过来的对象调用computer方法完成运算,并输出运算的结果
③ 设计一个主类Test,调用UseCompute中的方法useCom来完成加减乘除运算(使用匿名内部类)
计算器接口:
package com.itheima.innerClass.work;
/**
* @ClassName Compute
* @Description 计算器接口
* @Author 孙克旭
* @Date 2025/3/17 15:54
*/
public interface Compute {
int computer(int n, int m);
}
计算器使用类:
package com.itheima.innerClass.work;
/**
* @ClassName UseCompute
* @Description 计算器使用类
* @Author 孙克旭
* @Date 2025/3/17 15:56
*/
public class UseCompute {
public void useCom(Compute com, int one, int two) {
int computer = com.computer(one, two);
System.out.println(computer);
}
}
测试类:
package com.itheima.innerClass.work;
/**
* @ClassName Test
* @Description 测试类
* @Author 孙克旭
* @Date 2025/3/17 16:00
*/
public class Test {
public static void main(String[] args) {
UseCompute useCompute = new UseCompute();
useCompute.useCom(new Compute() {
@Override
public int computer(int n, int m) {
return n+m;
}
},10,20);
useCompute.useCom(new Compute() {
@Override
public int computer(int n, int m) {
return n-m;
}
},10,20);
useCompute.useCom(new Compute() {
@Override
public int computer(int n, int m) {
return n*m;
}
},10,20);
useCompute.useCom(new Compute() {
@Override
public int computer(int n, int m) {
return n/m;
}
},40,20);
}
}
6.6.5 编码题2
某手机需完成闹钟功能(通过匿名内部类方式),需要如下:
①定义一个铃声接口Bell,里面有个ring方法。
②定义一个手机类Cellphone,具有闹钟功能alarmClock,参数是Bell类型
③定义一个测试类,来测试手机类的闹钟功能,通过匿名内部类 ( 对象 ) 作为参数,打印:懒猪起床了
④再在测试类中通过匿名内部类 ( 对象 ) 作为参数,打印:小伙伴上课了
Bell接口:
package com.itheima.innerClass.work;
/**
* @ClassName Bell
* @Description 手机铃声
* @Author 孙克旭
* @Date 2025/3/17 16:08
*/
public interface Bell {
void ring();
}
手机类:
package com.itheima.innerClass.work;
/**
* @ClassName Cellphone
* @Description 手机类
* @Author 孙克旭
* @Date 2025/3/17 16:09
*/
public class Cellphone {
/**
* 闹钟
*
* @param bell
*/
public void alarmClock(Bell bell) {
bell.ring();
}
}
测试类:
package com.itheima.innerClass.work;
/**
* @ClassName Test2
* @Description 测试类
* @Author 孙克旭
* @Date 2025/3/17 16:12
*/
public class Test2 {
public static void main(String[] args) {
Cellphone cellphone = new Cellphone();
cellphone.alarmClock(new Bell() {
@Override
public void ring() {
System.out.println("懒猪起床了");
}
});
cellphone.alarmClock(new Bell() {
@Override
public void ring() {
System.out.println("小伙伴上课了");
}
});
}
}
6.6.6 编码题3
小明家是果农,因为针对不同品相的苹果收购价格不同,现需要实现苹果按照品相分级,需要工人挑苹果,按照不同品相完成定价;为完成品相分级这一功能,请按照需求完成代码:
①定义苹果类需包含属性:大小,颜色;提供基本的构造方法和get方法,set方法
②定义接口CompareAble包含定义默认方法compare,挑选较大苹果。
③定义接口实现类Compare。
④定义工人类要有成员方法:挑选苹果Apple pickApple(CompareAble,Apple a1,Apple a2)。
⑤测试类:创建Worker对象、创建两个Apple对象,一个Apple(5,“青色”),一个Apple(3,“红色”);需实现默认挑选大的苹果,打印苹果信息;指定颜色挑选,通过匿名内部类实现。
苹果类:
package com.itheima.innerClass.work;
/**
* @ClassName Apple
* @Description 苹果类
* @Author 孙克旭
* @Date 2025/3/17 16:23
*/
public class Apple {
private int size; //大小
private String color; //颜色
@Override
public String toString() {
return "Apple{" +
"size=" + size +
", color='" + color + '\'' +
'}';
}
public Apple() {
}
public Apple(int size, String color) {
this.size = size;
this.color = color;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
比较接口:
package com.itheima.innerClass.work;
/**
* @ClassName Compareable
* @Description 比较苹果接口
* @Author 孙克旭
* @Date 2025/3/17 16:27
*/
public interface Compareable {
/**
* 比较两个苹果大小
*
* @param a1
* @param a2
*/
Apple compare(Apple a1, Apple a2);
}
比较接口实现类:
package com.itheima.innerClass.work;
/**
* @ClassName Compare
* @Description 比较苹果实现类
* @Author 孙克旭
* @Date 2025/3/17 16:30
*/
public class Compare implements Compareable {
@Override
public Apple compare(Apple a1, Apple a2) {
return (a1.getSize() > a2.getSize()) ? a1 : a2;
}
}
测试类:
package com.itheima.innerClass.work;
/**
* @ClassName Test3
* @Description 测试类
* @Author 孙克旭
* @Date 2025/3/17 16:37
*/
public class Test3 {
public static void main(String[] args) {
Worker worker = new Worker();
Apple a1 = new Apple(5, "青色");
Apple a2 = new Apple(3, "红色");
Apple apple = worker.pickApple(new Compare(), a1, a2);
System.out.println(apple);
Apple apple1 = worker.pickApple(new Compareable() {
@Override
public Apple compare(Apple a1, Apple a2) {
if (a1.getColor().equals("红色")) {
return a1;
} else {
return a2;
}
}
}, a1, a2);
System.out.println(apple1);
}
}
7. 泛型
7.1 泛型:认识泛型
定义类、接口、方法时,同时声明了一个或者多个类型变量(如:
<E>
),称为泛型类、泛型接口,泛型方法、它们统称为泛型。
- 作用:泛型提供了在编译阶段约束所能操作的数据类型,并自动进行检查的能力!这样可以避免强制类型转换,及其可能出现的异常。
- 泛型的本质:把具体的数据类型作为参数传给类型变量。
7.2 泛型:泛型类
7.3 泛型:泛型接口
7.4 泛型:泛型方法
7.5 泛型:泛型的注意事项
7.6 单元练习7
7.6.1 什么是Java泛型,它有什么作用?
泛型的主要作用是为了在编译时限制类或方法可以操作的数据类型,从而避免在运行时出现类型转换错误。
通过使用泛型,我们可以编写更加通用、可重用的代码,同时减少运行时错误。
7.6.2 请简述Java中的泛型通配符及其作用。
1.无限定通配符:<?>,表示任何类型。
2.上界通配符:<? extends T>,表示类型T或其子类型。
3.下界通配符:<? super T>,表示类型T或其父类型。
通过使用通配符,我们可以编写更加灵活的泛型代码,处理各种不同类型的情况。
7.6.3 使用泛型的注意事项都有哪些
泛型是工作在编译阶段的,一旦程序编译成class文件,class文件中就不存在泛型了,这就是泛型擦除。
泛型不支持基本数据类型,只能支持对象类型(引用数据类型)。
7.6.4 编码题1
请定义一个泛型类 MyMap<K,V>,包含两个成员变量 first 和 second,first的类型是K,second的类型是V,
① 定义有参构造方法和first、second的get/set方法。
② 新建一个测试类,在main方法中创建MyMap对象传入值,打印first和second
③ 在MyMap类中定义一个无返回值的泛型方法print,接收一个泛型参数,并将接收到的泛型参数打印到控制台上
MyMap类:
package com.itheima.genericDemo;
public class MyMap<K, V> {
private K first;
private V second;
public void print(K k) {
System.out.println(k);
}
public MyMap(K first, V second) {
this.first = first;
this.second = second;
}
public K getFirst() {
return first;
}
public void setFirst(K first) {
this.first = first;
}
public V getSecond() {
return second;
}
public void setSecond(V second) {
this.second = second;
}
}
测试类:
package com.itheima.genericDemo;
public class Test {
public static void main(String[] args) {
MyMap<String, String> map = new MyMap<>("A", "B");
System.out.println(map.getFirst());
System.out.println(map.getSecond());
map.print("孙克旭");
}
}
7.6.5 编码题2
编写一个泛型接口 DataStructure
① 定义一个抽象方法 void push(E element),表示向数据结构中添加元素。
② 再定义一个抽象方法 E pop(),表示从数据结构中取出一个元素并返回。
③ 编写一个泛型类 Stack,实现 DataStructure 接口,并完成 push 和 pop 方法的实现。
④ 新建一个测试类,在main方法中测试调用xxx类的 push (存一个数据)和 pop(取出一个数据)
DataStructure类:
package com.itheima.genericDemo;
public interface DataStructure<E> {
void push(E element);
E pop();
}
Stack类:
package com.itheima.genericDemo;
public class Stack<E> implements DataStructure<E> {
private E element;
@Override
public void push(E element) {
this.element = element;
}
@Override
public E pop() {
return element;
}
}
测试类:
package com.itheima.genericDemo;
public class Test2 {
public static void main(String[] args) {
Stack<String> stack = new Stack<>();
stack.push("孙克旭");
System.out.println(stack.pop());
}
}
7.6.6 编码题3
设计一个简单的音乐播放器程序。
①使用泛型类 Playlist 来管理播放列表中的音乐。
②每首音乐都有一个名称和一个时长,可以用 Music 类来表示。
③定义两个方法 addMusic 和 play,并分别在 Playlist 类中实现。
④在 Playlist 类中,我们定义了一个泛型类型参数 T extends Music,表示 T 必须是 Music 的子类或本身。我们使用 List 来存储播放列表中的音乐,并实现了两个方法 addMusic 和 play,分别用于向播放列表中添加音乐和按添加顺序播放音乐。
⑤在测试类中,新建一个 Playlist 对象,并向播放列表中添加三首音乐。然后播放音乐
音乐类:
package com.itheima.genericDemo;
/**
* @ClassName Music
* @Description 音乐类
* @Author 孙克旭
* @Date 2025/3/17 20:53
*/
public class Music {
private String name;
private int time;
public Music() {
}
public Music(String name, int time) {
this.name = name;
this.time = time;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getTime() {
return time;
}
public void setTime(int time) {
this.time = time;
}
}
PlayList类:
package com.itheima.genericDemo;
import java.util.ArrayList;
public class Playlist<E extends Music> {
private ArrayList<E> arrayList = new ArrayList<>();
public void addMusic(E e) {
arrayList.add(e);
}
public void play() {
for (int i = 0; i < arrayList.size(); i++) {
E e = arrayList.get(i);
System.out.println("播放音乐:" + e.getName() + ",播放时间:" + e.getTime());
}
}
}
测试类:
package com.itheima.genericDemo;
public class Test3 {
public static void main(String[] args) {
Playlist<Music> musicPlaylist = new Playlist<>();
musicPlaylist.addMusic(new Music("只因你太美", 2));
musicPlaylist.addMusic(new Music("挪威的坤坤", 3));
musicPlaylist.addMusic(new Music("坤的救赎", 4));
musicPlaylist.play();
}
}
7.7 章节测评
7.7.1 接口和抽象类的联系和区别?
联系主要体现在:
抽象类:
(1)一个类中有抽象方法,这个类就变成了抽象类。
(2)抽象类中class的前面必须有abstract修饰符。
(3)抽象类中可以有普通方法,也可以有抽象方法,而抽象方法的个数可以是0个,也可以是多个。
(4)子类继承父类,必须重写全部的抽象方法,除非这个类也变成了抽象类。
接口:
(1)表面上看,接口是一种特殊的抽象类,但是类是类,接口是接口,是并列的关系。
(2)接口中所有方法都必须是抽象的。
(3)接口中方法定义默认为public abstract类型,成员变量默认为public static final 类型。(如果省略,系统会默认补全)。
区别主要体现在:
(1)抽象类可以有构造方法,接口中不能有构造方法。
(2)抽象类中可以有成员变量,接口中没有成员变量。(被final修饰变成了常量)
(3)抽象类中可以有普通方法,接口中所有方法都必须是抽象的。
(4)抽象类中抽象方法的访问类型可以是public,protected,但接口中抽象方法的访问类型只能是public,并且默认为public abstract(省略则自动默认补全)。
(5)抽象类中可以有静态方法,接口中不能有静态方法。
7.7.2 多态的前提条件是什么?
(1)有继承关系。
(2)有方法的重写。
(3)有父类引用指向之类对象。
7.7.3 什么是方法重写?
子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写。声明不变,重新实现。
7.7.4 编码题1
① 定义一个图形类Picture(抽象类),需包含属性:边长(sideLength)、边数(sideCount), 需包含抽象方法:求周长getPerimeter方法、求面积getArea方法、显示图形信息show方法
② 定义图形类Picture的两个子类:圆形类Circle 和 矩形类Rect
圆形类Cirle需包含属性:圆心x(centerX)、圆心y (centerY)、半径(radius),重写求周长、求面积方法、显示信息方法,矩形类Rect需包含属性:长(length)、宽(width),重写求周长、求面积方法、显示信息方法
③ 定义测试类:测试方法中用多态的形式创建圆形类Cirle对象 和 矩形类Rect对象,分别调用show方法显示信息
package com.itheima;
/**
* 图形类
*/
public abstract class Picture {
private int sideLength; //边长
private int sideCount; //边数
public Picture() {
}
public Picture(int sideLength, int sideCount) {
this.sideLength = sideLength;
this.sideCount = sideCount;
}
public int getSideLength() {
return sideLength;
}
public void setSideLength(int sideLength) {
this.sideLength = sideLength;
}
public int getSideCount() {
return sideCount;
}
public void setSideCount(int sideCount) {
this.sideCount = sideCount;
}
/**
* 求周长
* @return
*/
public abstract double getPerimeter();
/**
* 求面积
* @return
*/
public abstract double getArea();
/**
* 显示图形信息
*/
public abstract void show();
}
package com.itheima;
/**
* 圆形类
*/
public class Circle extends Picture{
private double centerX; //圆心x
private double centerY;//圆心y
private double radius; //半径
public Circle(double centerX, double centerY, double radius) {
this.centerX = centerX;
this.centerY = centerY;
this.radius = radius;
}
public Circle(int sideLength, int sideCount, double centerX, double centerY, double radius) {
super(sideLength, sideCount);
this.centerX = centerX;
this.centerY = centerY;
this.radius = radius;
}
public double getCenterX() {
return centerX;
}
public void setCenterX(double centerX) {
this.centerX = centerX;
}
public double getCenterY() {
return centerY;
}
public void setCenterY(double centerY) {
this.centerY = centerY;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
/**
* 重写求周长方法,圆形周长为2πr
*/
@Override
public double getPerimeter() {
return 2 * Math.PI * radius;
}
/**
*重写求面积方法,圆形面积为πr²
*/
@Override
public double getArea() {
return Math.PI * radius * radius;
}
/**
* 显示圆形图形信息
*/
@Override
public void show() {
System.out.println("圆形的周长:" + getPerimeter());
System.out.println("圆形的面积:" + getArea());
}
}
package com.itheima;
/**
* 矩形类
*/
public class Rect extends Picture{
private double length;
private double width;
public Rect(double length, double width) {
this.length = length;
this.width = width;
}
public Rect(int sideLength, int sideCount, double length, double width) {
super(sideLength, sideCount);
this.length = length;
this.width = width;
}
public double getLength() {
return length;
}
public void setLength(double length) {
this.length = length;
}
public double getWidth() {
return width;
}
public void setWidth(double width) {
this.width = width;
}
/**
* 重写求周长方法,矩形周长: 为2倍的长加2倍的宽
* @return
*/
@Override
public double getPerimeter() {
return 2 * length + 2 * width;
}
/**
* 重写求面积方法,矩形面积为长乘以宽
* @return
*/
@Override
public double getArea() {
return length * width;
}
/**
* 显示矩形图形信息
*/
@Override
public void show() {
System.out.println("矩形的周长:" + getPerimeter());
System.out.println("矩形的面积:" + getArea());
}
}
package com.itheima;
/**
* 测试类
*/
public class AppTest {
public static void main(String[] args) {
//1.多态的形式创建Circle对象
Picture circle = new Circle(5, 6, 7);
//2.多态的形式创建Rect对象
Picture rect = new Rect(8, 9);
//3.分别调用show方法()
circle.show();
rect.show();
}
}
7.7.5 编码题2
现有一个5星级酒店,按照要求完成代码:
①定义抽象类员工(Employee),具有姓名name,工号id等属性
②接口Service,实现这一接口表示具有vip服务
③再定义厨师Cooker类,服务员Waiter类,经理Manager类,三个员工都有姓名name,工号id的属性,只有厨师和服务员有vip服务,厨师vip服务加菜,服务员嘘寒问暖,
④定义程序实现功能并测试
package com.itheima;
/**
* 测试类
*/
public class AppTest {
public static void main(String[] args) {
// 1.分别创建厨师对象、服务员对象、经理对象
Chef chef = new Chef("厨师", 1);
Waiter waiter = new Waiter("服务员", 2);
Manager manager = new Manager("经理", 3);
// 2.输出厨师对象的普通服务内容、VIP服务内容
chef.providerService();
chef.vipService();
// 3.输出服务员对象的普通服务内容、VIP服务内容
waiter.providerService();
waiter.vipService();
// 4.输出经理的服务内容
manager.providerService();
}
}
package com.itheima;
public class Chef extends Employee implements Service{
public Chef(String name, int id) {
super(name, id);
}
/**
* 普通服务
*/
@Override
public void providerService() {
System.out.println( getName() + "提供的普通服务:做菜");
}
/**
* VIP
*/
@Override
public void vipService() {
System.out.println( getName() + "提供的vip服务:加菜");
}
}
package com.itheima;
public abstract class Employee {
protected String name;
protected int id;
public Employee(String name, int id) {
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
public int getId() {
return id;
}
public abstract void providerService();
}
package com.itheima;
public class Manager extends Employee{
public Manager(String name, int id) {
super(name, id);
}
@Override
public void providerService() {
System.out.println( getName() + "提供的服务:管理");
}
}
package com.itheima;
public interface Service {
void vipService();
}
package com.itheima;
public class Waiter extends Employee implements Service{
public Waiter(String name, int id) {
super(name, id);
}
@Override
public void providerService() {
System.out.println( getName() + "提供的普通服务:端菜");
}
@Override
public void vipService() {
System.out.println( getName() + "提供的vip服务:嘘寒问暖");
}
}
7.7.6 编码题3
定义一个动物类,有品种、年龄(age)等属性,用吃食物等行为
①在动物类的子类,鲸鱼和狗(需重写吃食物的方法)
②定义一个测试类,创建鲸鱼和狗对象,并分别调用eat方法,把信息输出到控制台
public class Exe3 {
public static void main(String[] args) {
Jingyu jingyu = new Jingyu("虎鲸", 3);
jingyu.eat();
Dog dog = new Dog("柴犬", 2);
dog.eat();
}
}
public class Dog extends DongWu {
public Dog() {
}
public Dog(String pinZhong, int age) {
super(pinZhong, age);
}
public void eat(){
System.out.println(this.getAge()+"岁大的"+this.getPinZhong()+"正在吃东西");
}
}
public class Jingyu extends DongWu {
public Jingyu() {
}
public Jingyu(String pinZhong, int age) {
super(pinZhong, age);
}
public void eat(){
System.out.println(this.getAge()+"岁大的"+this.getPinZhong()+"正在吃东西");
}
}
public class DongWu {
private String pinZhong;
private int age;
public DongWu() {
}
public DongWu(String pinZhong, int age) {
this.pinZhong = pinZhong;
this.age = age;
}
public String getPinZhong() {
return pinZhong;
}
public void setPinZhong(String pinZhong) {
this.pinZhong = pinZhong;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void eat(){
System.out.println(this.age+"岁大的"+this.pinZhong+"正在吃东西");
}
}