Java进阶
“生态农场”游戏介绍
需求
每个玩家拥有一块土地,可选择种植作物
假设目前两种作物:苹果树,玉米
名称
品种
生长周期
采摘时长
果实数量
苹果树
富士、金帅
10天
2天
100个
名称
生长周期
采摘时长
果实数量
收割机选择
玉米
8天
3天
200个
家用收割机:50元
联合收割机:100元
每个作物有不同生长周期
苹果树生长特征
玉米生长特征
作物生长过程经历:生长期、采摘期和已死亡
生长期后进入采摘期,可采摘
如果采摘期完成采摘,收获所有果实;否则无法收获到果实
采摘期结束后,作物死亡
程序窗口提示功能
种植作物:选择种植的作物,并输出作物特征信息
查看生长状态:根据作物终止时长,更新作物生长状态,输出相关信息
收获果实:记录是否允许采摘,收获果实数量
退出
阶段目标:根据控制台提示,选择作物
苹果树、玉米
如果选择苹果树,提示苹果类型
富士、金帅
选择玉米提示收割机类型
家用收割机、联合收割机
收割机对应不同费用
控制台输出作物特征信息
名称、生长周期、采摘时长、果实数量
苹果树品种 或 收割机费用
封装
抽象类的步骤
从需求中提取名词——确定类
从需求中提取名词——确定类的属性
从需求中查找动词——确定类的方法
类图分析
设计类时遵循原则
属性和方法的设置是为了解决业务问题
关注主要属性和方法
如果没有必要,不要增加额外的类、属性和方法
构造方法
用来初始化对象的特殊方法
分类:
[访问修饰符]构造方法名([参数列表]){
//对象初始化代码
}无参构造:
我们不创建构造时,系统自动分配
我们可自建无参构造完成预定初始化
有参构造:
我们自行创建
系统不再分配默认无参构造方法
可以创建多个有参构造,但参数列表不能相同
this关键字
是对一个对象的默认引用
注意:他是对象内部指代自身的引用,所以只能调用实例变量、实例方法和构造方法,不能调用类变量
和类方法,也不能调用局部变量
可以调用信息:
调用成员变量
调用成员方法
调用已定义构造方法
方法重载
同一个类
方法名相同
参数列表不同(个数或类型)
public AppleTree(d){
this.name=name;
}
public void changName(String name){
this.name=name;
this.print();
}
public AppleTree(String name,String brand){
this.name=name;
this.brand=brand;
}
public AppleTree(String name,String brand,int growTime,int harvestTime){
this(name, brand);//调用已有构造方法,参数列表要对应
this.growTime=growTime;
this.harvestTime=harvestTime;
this.isHarvested=false;//默认初始化值
}封装(面向对象三大特征之一)
将类的某些信息隐藏在类的内部,不允许外部程序直接访问
通过该类提供 方法来实现对隐藏信息的操作和访问
封装的好处:
隐藏类的实现细节
方便加入控制语句
方便修改实现
只能通过规定方法访问数据
封装的步骤:
修改属性的可见性 --------> 设为private
为每一个属性创建可见的getter/setter方法 -------->getter方法:属性可读;setter方法:属性可
写
在getter/setter方法中加入属性存取控制语句 -------->判断属性值的合法性
IDEA封装方法
菜单选取
或鼠标右键选择菜单弹出选项
选择需要封装的字段(属性)同一类中
同一包中
子类中
外包中
private
√
×
×
×
(default)
√
√
×
×
protected
√
√
√
×
public
√
√
√
√
完成
访问级别:
static关键字
可以t通过类名直接访问方法
可以修饰属性、方法及代码块
static 修饰的属性称为——静态变量或类变量
没有static修饰的属性——实例变量
static修饰的元素被类创建的所有元素共享static代码块在JVM初始化阶段,只执行一次;一般情况下使用static代码块对static变量进行初始化
注意:方法中不能定义static变量
fifinal关键字
通常和static共同定义常量
常量名一般由大写字母组成
声明常量是一般要赋初值
静态方法:直接使用,不需要实例化对象
方法分类:
静态方法(又称类方法):使用static关键字修饰的方法
实例方法:未使用static修饰的方法
小结:*总点
//语法:
public static void main(String[] args){...}
//调用
类名.成员属性名;
类名.成员方法名();
public static final 数据类型 常量名 = 初始值 ;
//错误示例
public class JBS {
public static int i;
public static int j=10;
public int k;
public static void staticMethod(){
System.out.println("执行静态方法");
k=25;
System.out.println("打印 k :"+k);
}
}
//正确写法:静态方法使用成员变量,需要先创建对象
public class JBS {
public static int i;
public static int j=10;
public int k;
public static void staticMethod(){
System.out.println("执行静态方法");
JBS jbs=new JBS();
jbs.k=25;
System.out.println("打印 k :"+jbs.k);
}
}static、非private修饰
非static、private修饰
属性
类属性、类变量
实例属性、实例变量
方法
类方法
实例方法
调用方法
类名.属性
类名.方法()
对象.属性
对象.方法()
对象.属性
对象.方法()
归属
类
单个对象
注意:
静态方法中,不能直接访问实例变量和实例方法
实例方法中,可调用类中定义的静态变量和静态方法
实践示例:
需求:
编写printGrowReport()方法,实现查看苹果树生长状态功能
根据生长时间更新生长状态数据,并输出生长状态信息
生长状态包括生长期、采摘期和已死亡
编写harvest()方法,实现收获果实功能
如果当前状态为采摘期,可以采摘果实,否则,不能采摘
根据采摘结果输出不同信息
实现输出玉米生长报告和收获果实的需求
编写测试类Test,分别测试以上两个方法
提示:
使用静态常量存储生长期、采摘期、已死亡三种状态数据
不同的状态下,有不同的输出结果
继承
准备:
1. 为什么需要继承?
2. 如何实现类的继承?
3. 方法重写必须遵循的规则是什么?
目标:
掌握继承的优点和实现方法
掌握子类重写父类的方法
掌握继承条件下构造方法的执行过程
设计缺陷:
代码重复,如果需要修改,大量的类都会需要重新书写如果扩建类,很多的类都要修改,带来很多不必要的工作量
继承优化:
方便修改;减少代码量;子类仅写各自特有属性方法
继承:面向对象三大特性之一
定义:
一个类获取现有类的所有属性和行为的机制
创建基于现有类的新类,可以重用现有类的属性和方法
可以在新的子类中添加新的属性方法
名词理解:
父类、子类
作用:
代码重用,拓展灵活
完整体现一个应用系统,逻辑清晰
增加软件可扩展性,以适应不同的业务需求
何时使用继承
继承与真实世界类似
符合 is-a关系 的设计使用继承
继承是代码重用的一种方式:将子类共有属性和行为方到父类
语法:方法
说明
toString()
返回当前对象本身的有关信息,返回字符串对象
equals()
比较两个对象是否是同一个对象。若是,返回true
clone()
生成当前对象的一个副本,并返回
hashCode()
返回该对象的哈希代码值
getClass()
获取当前对象所属的类的信息,返回Class对象
示例:
1. 提取共有属性方法写成父类
2. 定义AppleTree继承Crop,并写专有属性
3. 定义Corn继承Crop,并写专有属性
4. 写测试类
理解继承:
1. 不能被继承的父类成员:
private成员
子类与父类不在同一个包中,使用默认访问权限的成员
构造方法
2. 访问修饰符:protected(保护)
可以修饰属性、方法
本类、同包、子类可以访问
访问级别查阅之前封装处同知识点表格
Object类
是所有Java类的祖先
所有java类都直接或间接继承Object
位于java.lang包中
定义类时没有使用extends关键字时,此类直接继承于Object
常用方法
注意:java.lang.String类重写了Object类中的equals() 方法,用于比较两个字符串的值是否相等
//编写父类
[访问修饰符] class <SuperClass>{
//属性
//方法
}
//编写子类
[访问修饰符] class <SubClass> extends <SuperClass>{
//子类特有属性
//子类特有方法
}Super关键字
子类访问父类成员:访问父类构造方法
语法:
如果想在子类中调用父类中被重写的方法
super关键字代表当前对象的直接父类对象的默认引用
super可用于子类访问父类属性、父类方法
注意:
super关键字必须出现在子类(子类的方法或构造方法)中,不允许出现在其他位置
使用super可以访问父类的成员(属性、方法、构造)
注意访问权限限制,如:无法访问private成员
使用this/super关键字的注意事项
在构造方法中,如果出现this或super关键字,则只能是该方法的第一句
在一个构造方法中,不允许同时使用this和super关键字调用构造方法
在类方法中,不允许出现this和super关键字
在实例方法中,this和super语句不要求是第一条语句,可以共存
方法重写
使用继承后效果
子类直接使用父类方法,但父类方法只能使用他所能调用的属性及方法(自身或继承的父类成员)
方法的重写或方法的覆盖(Overriding)
在子类中,根据需求对从父类继承的方法体进行重新编写,以实现子类的需求
遵守的规则
必须有相同的方法名
必须有相同的参数列表
重写的方法返回类型必须和被重写方法的返回类型相同或为其子类
访问权限不能被缩小
IDEA 对父类方法的重写操作
super(参数);//在子类构造方法中调用必须是第一句
super.<父类属性/父类方法>;
//示例
super.print();位置
方法名
参数表
返回值
访问修饰符
方法重载
同类
相同
不同
无关
无关
方法重写
子类
相同
相同
相同或使其子类
不能比父类更严格
或使用 Ctrl+O(字母O)
方法重载与方法重写方法重写
子类
相同
相同
相同或使其子类
不能比父类更严格
提问:
构造方法也能被重写吗?
构造方法不能被继承,所以不能被重写
简述方法重写的规则
1. 方法名相同
2. 参数列表相同
3. 返回值类型相同或其子类
4. 访问权限不能严于父类
实践示例:实现部门介绍功能
训练要点:
掌握继承的语法
能够运用继承设计类的结构
掌握子类重写父类的方法
掌握super关键字的用法
需求描述
某公司现有10个部门,要求
使用OO思想输出人力资源部(HR)和研发部(R&D)的部门介绍,包括部门名称、经理名
称、部门员工人数、部门职责介绍
HR部门具有招聘目标属性,R&D部门具有研发项目数属性,需要在部门职责介绍中进行特别
输出
设计思路:
定义部门类(Department)
属性:部门名称(name)、经理名称(managerName)、部门员工人数(employeeNum)、部门职责
介绍(responsibility)
编写部门介绍方法——printInfo()
定义HR类
继承自Department
添加招聘目标属性(recruitGoal)并重写父类输出方法
定义RD类
继承自Department
添加研发项目数属性(resProjects)并重写父类输出方法
编写测试类
分别创建HR对象和RD对象
调用printInfo()方法
public class Department {
private String name;//部门名称
private String managerName;//经理名称
private int ployeeNum;//部门员工人数private String responsibility;//部门职责介绍
public Department(String name,String managerName,int ployeeNum,String
responsibility){
this.name=name;
this.managerName=managerName;
this.ployeeNum=ployeeNum;
this.responsibility=responsibility;
}
public void printInfo(){
System.out.println("部门名称:" +name);
System.out.println("经理名称:" +managerName);
System.out.println("部门员工人数:" +ployeeNum);
System.out.println("部门职责:"+responsibility);
}
}
//=================
public class HR extends Department{
private int recruitGoal;//招聘目标
@Override
public void printInfo() {
super.printInfo();
System.out.println("本部门招聘目标:"+recruitGoal+"人");
}
public HR(String name, String managerName, int ployeeNum, String
responsibility,int recruitGoal) {
super(name, managerName, ployeeNum, responsibility);
this.recruitGoal=recruitGoal;
}
public int getRecruitGoal() {
return recruitGoal;
}
public void setRecruitGoal(int recruitGoal) {
this.recruitGoal = recruitGoal;
}
}
//--------------
public class RD extends Department{
private int resProjects;//研发项目数
@Override
public void printInfo() {
super.printInfo();
System.out.println("研发项目数:"+resProjects+"项");
}
public RD(String name, String managerName, int ployeeNum, String
responsibility,int resProjects) {
super(name, managerName, ployeeNum, responsibility);
this.resProjects=resProjects;
}项目实践
使用继承重构“农场”类
需求描述
抽取AppleTree类和Corn类中共有属性和方法,创建父类Crop类
根据各种作物的特征,在其子类中重写print()、printGrowReport()及harvest()方法
进行测试
提示:
使用super关键字访问父类资源
父类中不能访问子类元素,子类通过重写方法实现对子类特有成员访问
多级继承
一个类可以继承自某一个类,成为这个类的子类
同时,也可以在自身的基础上创建新的类,即成为它类的父类
如:Object<-----轿车类<-----新能源轿车类<-----无人驾驶轿车类
注意:Java中只支持单继承,即每个类只能有一个直接父类
继承中的构造方法:
Java虚拟机按照先父类后子类的顺序执行一系列的构造方法
public int getResProjects() {
return resProjects;
}
public void setResProjects(int resProjects) {
this.resProjects = resProjects;
}
}
//=====================================
@Test
public void testDepartment(){
HR hr=new HR("人力资源部","小丽",6,"招聘及员工绩效考核",15);
hr.printInfo();
System.out.println("-------------------------------");
RD rd=new RD("项目研发部","小李",15,"项目研发",15);
rd.printInfo();
}
//轿车类
public class Car {
String brand;//品牌
public Car(){
// super();
System.out.println("execute Car()");
}
public Car(String brand){
this.brand=brand;
System.out.println("execute Car(brand)");子类继承父类时构造方法的调用规则
没有通过super、this关键字调用父类或自身其他构造时,系统自动调用父类无参构造;此情况下
super()可省
如果子类构造通过super显示调用父类构造,执行调用的构造放弃无参构造
如果通过this关键字调用自身的其它构造,遵循以上两条
多级继承遵循以上规则逐层向父级传递,直到顶级父类Object类无参构造为止
综合实践:实现高速公路车辆收费系统
需求描述
}
}
//新能源类
public class NewEnergyCar extends Car {
String battery;//电池
public NewEnergyCar(){
// super();
System.out.println("execute NewEnergyCar()");
}
public NewEnergyCar(String brand,String battery){
super(brand);//显示调用,不执行无参构造
this.battery=battery;
System.out.println("execute NewEnergyCar(battery)");
}
}
//无人驾驶轿车类
public class DriverLessCar extends NewEnergyCar{
String system;//无人驾驶系统
public DriverLessCar(){
// super();
System.out.println("execute DriverLessCar()");
}
public DriverLessCar(String brand,String battery,String system){
super(brand,battery);
this.system=system;
System.out.println("execute DriverLessCar(system)");
}
}
//================
//测试类
@Test
public void testCar(){
DriverLessCar driverLessCar=new DriverLessCar();
System.out.println("-------------------");
DriverLessCar driverLessCar1=new DriverLessCar("吉利","比亚迪","鸿蒙Sys");
}序号
车辆类型
车型标准
收费标准(元/公里)
1
客车
车长<6000mm且核载人数<=9人
0.6
2
客车
车长<6000mm且核载人数(10-19)人
0.6
3
客车
车长>=6000mm且核载人数<=39人
0.9
4
客车
车长>=6000mm且核载人数>40人
0.9
5
货车
2轴;车长<6000mm且最大重量<4500kg
0.6
6
货车
2轴;车长>=6000mm或最大重量>=4500kg
0.9
7
货车
3轴
1.02
8
货车
4轴
1.315
9
货车
5轴
1.428
10
货车
6轴
1.428
采用OO思想设计,计算缴费情况
package cn.framsys.vehicledemo;
public class Vehicle {
private int length;//车长(单位:mm)
private String plateNo;//车牌号public Vehicle(){}
public Vehicle(int length,String plateNo){
this.length=length;
this.plateNo=plateNo;
}
public void print(){
System.out.println("车牌号:"+plateNo);
System.out.println("车长:"+length);
}
public double getRate(){
return 0;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public String getPlateNo() {
return plateNo;
}
public void setPlateNo(String plateNo) {
this.plateNo = plateNo;
}
}
package cn.framsys.vehicledemo;
public class Truck extends Vehicle{
private double weight;//最大允许载货量
private int numbersOfAxies;//车轴数量
public Truck(){}
public Truck(int length,String plateNo,double weight,int numbersOfAxies){
super(length, plateNo);
this.weight=weight;
this.numbersOfAxies=numbersOfAxies;
}
@Override
public void print() {
super.print();
}
@Override
public double getRate() {
double rate=0;
switch (numbersOfAxies) {
case 2:
if (super.getLength() < 6000 && weight < 4500) {
rate = 0.6;
} else if (super.getLength() >= 6000 || weight >= 4500) {rate = 0.9;
}
break;
case 3:
rate = 1.02;
break;
case 4:
rate = 1.315;
break;
case 5:
case 6:
rate = 1.428;
break;
}
return rate;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
public int getNumbersOfAxies() {
return numbersOfAxies;
}
public void setNumbersOfAxies(int numbersOfAxies) {
this.numbersOfAxies = numbersOfAxies;
}
}
package cn.framsys.vehicledemo;
public class Bus extends Vehicle{
private int passengers;//核定载客数
public Bus(){}
public Bus(int length,String plateNo,int passengers){
super(length, plateNo);
this.passengers=passengers;
}
@Override
public void print() {
super.print();
System.out.println("核定载客数:"+passengers);
}
@Override
public double getRate() {
double rate=0;
if (super.getLength()<6000&&passengers<=19){
rate=0.6;
}else if (super.getLength()>=6000){
rate=0.9;多态
准备:
什么是多态?
使用多态有什么优点
实现多态有哪几种方式
Instanceof运算符的作用是什么?
目标:
掌握多态的优势和应用场合
会进行子类和父类之间的类型转换
}
return rate;
}
public int getPassengers() {
return passengers;
}
public void setPassengers(int passengers) {
this.passengers = passengers;
}
}
//---------
package cn.framsys.vehicledemo;
import org.junit.Test;
import java.util.Scanner;
public class TestVehicle {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
System.out.println("****欢迎使用高速公路车辆收费系统****");
String plateNo="陕A00000";
System.out.println("请确认车牌号:"+plateNo+"(1:是 2:否)");
int num=input.nextInt();
if (num==1){
// Bus bus=new Bus(4000,plateNo,5);
// bus.print();
Truck truck=new Truck(6000,plateNo,6000,5);
truck.print();
double distance=170;//高速行驶距离
// double fees=distance* bus.getRate();
double fees=distance*truck.getRate();
System.out.println("您的车辆需交费:"+fees+" 元。");
}else{
System.out.println("已退出收费系统!");
}
}
}掌握instanceof运算符的使用
会使用父类作为方法形参实现多态
会使用父类作为返回值实现多态
为什么使用多态?
代码简洁
什么是多态?
程序中的多态:父类引用指向子类对象
同一个引用类型,使用不同的实例而执行不同的操作
同一种操作,由于条件不同,产生结果不同
如何实现多态
实现多态三要素
继承关系的父子类
子类重写父类方法
父类的引用指向子类的对象
多态的类型
向上转型——子类到父类的转换:自动类型转换
向下转型——父类到子类的转换:强制类型转换
向上转型:子类到父类的转换
就是将一个父类的引用指向一个子类对象
语法
示例:
说明:
系统会自动进行类型转换
通过父类引用变量调用的方法是子类覆盖或继承的子类方法,不是父类的方法
通过父类应用变量无法调用子类特有方法
向下转型:父类到子类的转换
将一个指向子类对象的父类引用赋给一个子类的引用,即将父类类型转换为子类类型
向下转型必须进行强制转型
向下转型后,父类才可调用子类特有属性
<父类型> <引用变量名> = new <子类型>();
Crop crop = new AppleTree("富士");
crop.print();
<子类型><引用变量名>=(<子类型>)<父类型的应用变量>;示例:
instanceof运算符:运算结果true|false
进行类型判断,判断一个对象是否属于一个类或者实现了一个接口
作用:避免不必要的类型转换错误,提高代码健壮性
应用场合:向下转型之前,先使用instanceof进行判断
注意:instanceof后的“类|接口”必须是在其前方的对象的继承树上,具有上下级关系
多态具有哪些优势:
1. 可替换性
2. 可扩充性
3. 灵活性
4. 简化性
实践练习:
1. 土地上种植作物、查看生长状态和收获果实
2. 实现控制动物叫声的功能
1. 需求描述
1. 一个主人领养了三只动物:狗、猫、鸭子,主人可以控制各个动物的叫的行为
2. 实现一个主人类,通过多态实现主人控制动物叫的功能
2. 实现思路
1. 定义Host类的donate()方法
1. 主人可以根据其他人的要求进行赠送
1. 主人使用type变量作为参数,返回值为Animal类型
3. 实现主人赠送动物的功能
1. 需求描述
1. 假设小狗、小猫和小鸭子被一位主人领养,主人可以根据其他人的要求任意送出一只宠
物
2. 当送出动物时,动物可以叫
2. 实现思路
1. 编写动物类Animal及cry()方法
2. 编写小狗类Dog、小猫类Cat和小鸭子类Duck,均继承自动物类
1. 分别在类中重写匪类cry()方法,实现动物叫声
3. 编写主人类Host及控制动物叫的方法letCry()
1. 将Animal父类对象作为方法的形参
4. 编写测试类
面向对象编程小结
面向对象三大特性:封装、继承、多态
AppleTree appleTree = (AppleTree) crop;
appleTree.grafting("粉红佳人");
对象 instanceof 类|接口封装隐藏对象的属性和实现细节
将类的成员属性声明为私有的,同时提供公有的方法实现对该成员属性的存取操作
继承是软件可重用性的一种表现
新类可以再不增加自身代码的情况下,通过从现有的类中继承其属性和方法充实自身内容
多态是具有表现多种形态的能力的特征
在程序设计术语中,意味着一个特定类型的变量可以引用不同类型的对象,自动地调用引用
的对象的方法
根据作用到的不同对象类型,响应不同的操作
综合练习:实现图书馆计算罚金功能
需求描述
图书馆为读者提供节约书籍和文献资料
每位读者可以一次借阅多本书籍和文献资料
超时未还书籍的罚款构造
成人书籍
允许借阅的时间是21天,每超时1天,需要缴纳罚金2元
如果超过3天以上,每天需要交纳罚金5元
儿童书籍
允许借阅的时间是21天,每超时1天,需要缴纳罚金2元
文献资料
允许借阅的时间是14天,每超时1天,需要缴纳罚金5元
如果超过3天以上,每天需要交纳罚金10元
使用OOP的多态特征实现计算罚金的功能代码示例:
抽象类和接口
准备:
抽象类和普通类的区别是什么?
Java中如何定义接口
接口中可以包含哪些类型的方法
目标:
掌握抽象类和抽象方法
掌握fifinal修饰符的用法
掌握接口的用法
理解面向对象设计原则
抽象类(abstract)
不具有实例的意义的类;不需要具有实例化能力
语法:抽象方法:
抽象类中使用抽象方法优化代码
语法:
abstract关键字修饰抽象方法说明:
抽象方法没有方法体
抽象方法必须在抽象类中
抽象方法必须在子类中实现,除非子类是抽象类
抽象方法所在的类必然是抽象类;抽象类中方法不一定是抽象方法
抽象类的优势:
提高可重用性
抽象类可以看作是类的模板,定义子类的行为,可以为子类提供默认实现,无需子类中重复
实现这些方法
降低代码耦合,更易于维护
子类可以分别实现抽象父类中定义的抽象方法,将方法定义和方法实现相分离
方便实现多态
抽象类作为继承关系下的抽象层,不能被实例化,通常定义抽象类类型变量,其具体应用是
实现抽象类的子类对象
抽象类的应用场合
抽象类用来列举一个类所需的共性行为
抽象类不明确提供具体实现方法
抽象类必须由其子类实现它的抽象方法(除非子类也具有抽象性)
抽象类和抽象方法
抽象方法只有方法声明,没有方法实现
有抽象方法的类必须声明抽象类
子类必须实现所有的抽象方法才能实现;否则,子类也必须声明成抽象类
抽象类中可没有、有一个或多个,甚至全部方法都是抽象方法
抽象类可以有构造方法,其构造方法可被本类的其他构造方法调用
不是有private修饰构造方法,可以被其子类的构造方法调用
注意:abstract可以用来修饰类和方法,不能用修饰属性和构造方法
fifinal用法
修饰fifinal类:不能被继承
[访问修饰符] abstract class <类名>{
}
[访问修饰符] abstract class 类名{
[访问修饰符] abstract <返回类型><方法名>([参数列表]);
}修饰fifinal方法:不能被重写
修饰fifinal属性:常量
易错示例:
实践操作:抽象类模拟“愤怒的小鸟”游戏
需求:
玩家将弹弓拉到极限后发射,小鸟就飞出去攻击
不同类型的小鸟具有不同的攻击方法
模拟分裂鸟和火箭鸟飞行、叫和攻击的行为
要求:
分裂鸟会分裂攻击
火箭鸟会加速攻击
分裂鸟和火箭鸟飞行过程中都伴随“嗷嗷叫”的声音
实现思路:
按OO分析方法,通过提取名词和动词的方法分别找出类的属性和方法
因为不同的鸟有不同的攻击行为,父类将攻击行为定义为抽象方法,在子类中实现
提示:
抽象类(Bird),实现方法飞flfly()、叫twitter()、攻击attack()
定义分裂鸟(SplitBird),实现方法
定义火箭鸟(RocketBird),实现方法
接口(Interface):不能实例化
生活中:接口是一种规范
Java中:
是一种规范和标准:可以约束类的行为,是的实现接口的类(或结构)在形式上保持一致
//易错一:final对象不能将对象再重新指向新对象
final AppleTree appleTree=new AppleTree("富士");
appleTree.print();
appleTree=new AppleTree("黄元帅");
//易错二:final方法形参不能在方法内重新赋值
public void add(final int num1,final int num2){
num1=9;
num2=7;
}
//易错三:抽象方法没有方法体,不能使用static修饰
public static abstract void printNew();
//易错四:私有方法不能被继承重写,抽象方法不能使用private修饰
private abstract void printNew();
//易错五:final方法不能被继承重写,抽象方法不能使用final修饰
public final abstract void printeNew();是一些方法特征的集合
看做是一种特殊“抽象类”
采用与抽象类不同的语法
抽象类利于代码复用,接口利于代码扩展和维护
语法:
说明:
如果是public,则整个项目中可见;如果省略,包中可见
接口中的变量都是全局静态常量
自动使用 public static fifinal 修饰
必须定义时指定初值
类的接口实现
语法:
说明:
实现类必须实现接口的所有方法
实现类可以实现多个接口
注意:
JDK1.8之前,接口中只能定义抽象方法
JDK1.8开始,接口还允许定义静态方法和默认方法
向后兼容
允许开发者在已有接口里添加新的方法时不需改动已经实施该接口的所有实现类
示例:
[访问修饰符] interface 接口名{
//接口成员
}
class 类名 implement 接口名{
//类成员
}
public interface MyInterface {
int P=5;
//抽象方法
void function1();
/*
接口中抽象方法
系统自动添加 public abstract 修饰
*/
//默认方法
default void function2(){
System.out.println("这是一个默认方法");
}
/*接口中默认方法
如果不能满足某个实现类的需求,可在实现类中重写
*/
//静态方法
static void function3(){
System.out.println("这是一个静态方法");
}
/*
接口中静态方法
·不允许在接口的实现类中重写
·只能通过接口名称调用
*/
}
//接口实现:
//关键字:implements
public class MyInterfaceImpl implements MyInterface{
@Override
public void function1() {
System.out.println("function 1");
}
//default 方法可以改写,也可以不改写
@Override
public void function2() {
System.out.println("new function 2");
}
public static void main(String[] args) {
MyInterfaceImpl myInterface=new MyInterfaceImpl();
myInterface.function1();
myInterface.function2();
MyInterface.function3();//static型仅能通过接口名调用
}
}
示例:
模拟声卡、显卡、网卡装配大计算机的PCI插槽进行工作
分析:
PCI本身没有实现任何功能
PCI插槽规定了启动和停止各种卡的要求
PCI可实现多种设备
//创建接口
public interface PCI {
//开始
public void start();
//结束
public void stop();
//输出信息
public default void print(){
System.out.println("符合PCI插槽标准");
}}
//创建实现类
public class SoundCard implements PCI{
@Override
public void start() {
System.out.println("声卡已启动");
}
@Override
public void stop() {
System.out.println("声卡已关闭");
}
}
public class GraphicCard implements PCI {
@Override
public void start() {
System.out.println("显卡已呈现图像");
}
@Override
public void stop() {
System.out.println("显卡停止呈现图像");
}
}
public class NetworkCard implements PCI{
@Override
public void start() {
System.out.println("网卡开始传输数据");
}
@Override
public void stop() {
System.out.println("网卡断开网络连接");
}
}
//创建测试计算机
public class TestPCI {
public static void main(String[] args) {
TestPCI pc=new TestPCI();//声明计算机
System.out.println("***装配网卡***");
PCI networkCard=new NetworkCard();
pc.assmble(networkCard);//装配网卡
System.out.println("***装配声卡***");
PCI soundCard=new SoundCard();
pc.assmble(soundCard);//装配声卡
System.out.println("***装配显卡***");
PCI graphicCard=new GraphicCard();
pc.assmble(graphicCard);//装配显卡
}
//装配测试功能
private void assmble(PCI pci) {
pci.print();
pci.start();
pci.stop();接口表示一种能力
体现:在其方法上
!!!----------面向接口编程-------------!!!
程序设计时:
关心实现类有何能力,不关心实现细节
面向接口的约定而不关心接口的具体实现
定义复杂接口
+++接口多继承+++
注意:一个接口可以继承多个接口,但接口不能继承“类”
+++类实现多个接口+++
注意:
一个普通类只能继承一个父类,但能同时实现多个接口
extends 关键字必须位于 implements 关键字之前
类必须实现所有接口(接口1,接口2,……)的全部抽象方法,否则必须定义为抽象类
}
}
/*
----------- 易 错 点 : ----------------------
1.接口中声明构造方法
2.接口中非default方法声明后给方法体
3.接口中方法声明为private访问级别
-------------------------------------------------
*/
//语法:
[访问修饰符] interface 接口名 extends 父接口1,父接口2,……{
//常量定义
//方法定义
}
[访问修饰符] class 类名 extends 父类名 implements 接口1,接口2,……{
//类的成员
}
public interface A {
default void print(){示例:面向接口编程,重写“愤怒小鸟”
分析:
各类鸟:炸弹鸟、喷火鸟、旋转鸟、超级鸟都是鸟
每类鸟具备相应的装备:返回类、展示装备能力
每个鸟类继承父类Bird的同时,根据需求实现不同的能力接口
一个人可以具有多个能力——一个类可以实现多个接口**
面向对象设计原则
为了让代码更具灵活性,更能适应变化,须遵守原则
摘取代码中变化的部分,形成接口
多用组合,少用继承
面向接口编程,不依赖具体实现
针对扩展开放,针对改变关闭——开闭原则
经验:面向接口编程,可以实现接口与实现相分离,可在客户端位置的情况下修改实现
System.out.println("print A");
}
}
public interface B {
default void print(){
System.out.println("print B");
}
}
//实现接口中有同名默认方法时,必须重写
public class C implements A,B {
@Override
public void print(){
System.out.println("print C");
}
}如何理解接口是一种能力?
接口有比抽象类更好的特性:
可以实现多继承
设计和实现完全分离
更自然体现多态
更易搭建程序框架
更容易实现
……
实践操作:模拟生活中的USB接口
需求:
生活中USB接口是企业和组织所制定的一种规范和标准,它规定了大小、排线等。因此不管设备类
型以及内部结构,只要符合规范,就可以插到USB接口上正常工作
用接口模拟USB接口以及插入U盘和手机的工作过程
思路:
定义USB接口,包括start()和stop()方法
定义UDisk类实现USB接口
定义Phone类实现USB接口
定义计算机Computer,模拟插入不同设备进行识别
实践操作:实现打印机打印功能
需求:
打印机的墨盒可能是彩色的,也可能是黑白的
所有的纸张可以有多种类型,如A4、B5等,并且墨盒和纸张都不是打印机厂商提供的
打印机厂商要保证自己的打印机和市场上的墨盒、纸张匹配
模拟实现打印机功能
思路:
定义墨盒接口InkBox,约定墨盒有颜色
定义纸张接口Paper,约定纸张大小
定义打印机类,引用墨盒接口、纸张接口实现打印功能
墨盒厂商按照InkBox接口实现ColorInkBox和GrayInkBox类
纸张厂商按照Paper接口实现A4Paper类和B5Paper类
“组装”打印机,让打印机通过不同墨盒和纸张实现打印功能
实践案例:特种部队
需求:
1. 设计类结构,并完成编码
1. 需求
1. 玩家初始化
2. 装配枪弹
3. 双方对战
4. 根据需求,设计“特种部队”游戏的类结构,并完成编码
2. 提示
1. 分析需求归纳类及属性2. 分析类的方法
2. 实现游戏初始化
1. 需求
1. 实现玩家初始化和装备初始化
1. 包含录入玩家昵称,选择对手,装备弹夹,玩家持枪等操作
2. 显示玩家信息
2. 提示
1. 玩家初始化:创建玩家,初始化昵称、血量
2. 装备初始化:
1. 分别创建机枪、弹夹对象,将弹夹装满子弹,玩家手持机枪,并向机枪装配已装满
弹的弹夹
2. 需要调用已实现玩家类、弹夹类中相关方法
3. 实现双方对战
1. 需求
1. 在每回合中交战玩家分别向对方射击,最先打败对手的一方获胜
1. 因子弹伤害值为1~15间随机数,每次交战玩家损失血量为等值随机数
2. 每个回合中,均需计算弹夹剩余子弹数和玩家剩余血量,并输出
3. 循环交战,每次询问“是否继续对战”
4. 当玩家主动结束对战或有一方被击败时,将退出游戏
代码示例:
/**
* 子弹类
*/
public class Bullet {
//子弹击中敌人
public void hitEnemy(Player player){
int hurt=(int)(Math.random()*15+1);//随机伤害值
player.damage(hurt);
}
}
/**
* 弹夹类
*/
public class Clip {
private Bullet[] magazine;//弹仓
private int capacity=30;//弹夹容量
private int surplus=0;//子弹盈余
public Clip(){
this(30);
}
public Clip(int capacity) {
this.magazine=new Bullet[capacity];
this.surplus=0;
}
//装子弹
public void pushBullet(Bullet bullet){
if (surplus==capacity){
System.out.println(">>>弹夹已装满,请勿重复装弹!");return;
}
magazine[surplus]=bullet;
surplus++;
}
//卸子弹
public Bullet popBullet(){
if (surplus==0){
System.out.println(">>>弹夹已空,无法弹出子弹!");
return null;
}
Bullet bullet=magazine[surplus-1];
surplus--;
showClip();
return bullet;
}
//显示弹夹信息
private void showClip() {
System.out.printf(">>>单价状态:%d/%d\n",surplus,capacity);
}
public Bullet[] getMagazine() {
return magazine;
}
public void setMagazine(Bullet[] magazine) {
this.magazine = magazine;
}
public int getCapacity() {
return capacity;
}
public void setCapacity(int capacity) {
this.capacity = capacity;
}
public int getSurplus() {
return surplus;
}
public void setSurplus(int surplus) {
this.surplus = surplus;
}
}
/**
* 枪类
*/
public class Gun {
private Clip clip;//弹夹
public Gun(){}
public Gun(Clip clip){
this.clip=clip;
}
//装弹夹
public void loadClip(Clip clip){
this.clip=clip;}
//开枪
public void shootEnemy(Player player){
if (clip==null){
System.out.println(">>>枪械没有子弹,放一个空枪!");
return;
}
Bullet bullet=clip.popBullet();
if (bullet==null){
System.out.println(">>>枪械弹夹已空,放一个空枪!");
return;
}
bullet.hitEnemy(player);
}
//显示枪械信息
public void showGun(){
if (clip!=null){
System.out.println(">>>枪械信息:有弹夹");
}else {
System.out.println(">>>枪械信息:无弹夹");
}
}
public Clip getClip() {
return clip;
}
public void setClip(Clip clip) {
this.clip = clip;
}
}
/**
* 玩家类
*/
public class Player {
private String name;//昵称
private int blood=100;//血量
private Gun gun;//枪械
public Player(){this(null);}
public Player(String name){
this(name,100);
}
public Player(String name,int blood){
this.name=name;
this.blood=blood;
}
/**
* 持枪
* @param gun
*/
public void holdGun(Gun gun){
this.gun=gun;
}
/**
* 射击
* @param player
*/public void shootEnemy(Player player){
System.out.printf("%s向%s开了一枪\n",this.name,player.name);
if (gun==null){
System.out.println(">>>"+name+"没有枪,无法进行射击!");
return;
}
gun.shootEnemy(player);
}
/**
* 装弹夹
* @param clip
*/
public void loadClip(Clip clip){
if (gun==null){
System.out.println(">>>"+name+"没有枪,无法装弹夹!");
return;
}
gun.loadClip(clip);
}
//计算损失
public void damage(int hurt) {
if (blood==0){
System.out.println(">>>"+name+"已死亡,请勿鞭尸!");
return;
}
blood-=hurt;
if (blood<=0){
blood=0;
System.out.println(">>>"+name+"已死亡!");
}
}
//显示玩家信息
public void showPlayer(){
boolean isHoldGun=(gun!=null);
System.out.printf(">>>玩家信息:姓名:%s,血量:%d,是否持枪
=%b\n",name,blood,isHoldGun);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getBlood() {
return blood;
}
public void setBlood(int blood) {
this.blood = blood;
}
public Gun getGun() {
return gun;
}public void setGun(Gun gun) {
this.gun = gun;
}
}
/**
* 游戏类
*/
public class TestGunGame {
public static void main(String[] args) {
Scanner input =new Scanner(System.in);
System.out.println("欢迎来到和平之战");
System.out.println("*****战前准备*****");
//创建玩家
Player p1=null;
Player p2=null;
System.out.println("请录入玩家昵称:");
String uname=input.next();
System.out.println("请选择对战玩家:");
System.out.println("1.你 2.秦始皇 3.老子 请选择:");
int num= input.nextInt();
String pname="匿名";
switch (num){
case 1:
pname="你";
break;
case 2:
pname="秦始皇";
break;
case 3:
pname="老子";
break;
default:
System.out.println("你的输入有误!");
break;
}
p1=new Player(uname,100);
p2=new Player(pname,100);
//准备枪支道具
Gun gun1=new Gun();
Gun gun2=new Gun();
//玩家持枪
p1.holdGun(gun1);
p2.holdGun(gun2);
//给抢添弹夹
Clip clip1=new Clip();
Clip clip2=new Clip();
for (int i = 0; i <30 ; i++) {
System.out.println("添加"+(i+1)+"发子弹");
clip1.pushBullet(new Bullet());
clip2.pushBullet(new Bullet());
}
//玩家装弹夹
p1.loadClip(clip1);实践案例:实现冰箱功能显示
需求:
使用OO技术模拟普通冰箱、智能冰箱相应功能,并显示信息
所有冰箱都具有品牌、型号的特征,能够冷藏和冷冻食品
普通冰箱具有温度调节功能
智能冰箱具有温度调节、食品管理、物联云服务功能
提示:
运用继承的只是,分别创建冰箱类及其子类,普通冰箱和智能冰箱,定义属性和方法
不同冰箱的功能创建为接口
普通冰箱和智能冰箱通过实现不同接口,完成各自功能
p2.loadClip(clip2);
//展示玩家信息
p1.showPlayer();
p2.showPlayer();
//模拟游戏开始
int i=0;
System.out.println("*****对战开式******");
boolean isExit=false;
while (true){
System.out.println("---------第 "+(i+1)+" 个回合----------");
i++;
p1.shootEnemy(p2);
if (p2.getBlood()==0){
System.out.println(p1.getName()+" 获胜");
System.out.println("大吉大利~回家吃鸡~~");
return;
}
p2.shootEnemy(p1);
if (p1.getBlood()==0){
System.out.println(p2.getName()+" 获胜");
System.out.println("大吉大利~回家吃鸡~~");
return;
}
System.out.println();
p1.showPlayer();
p2.showPlayer();
System.out.println("是否继续?(y/n)");
if (!(input.next().equals("y"))){
System.out.println("退隐江湖");
return;
}
}
}
}
//factory包
public class Fridge {//属性
private String brand;//品牌
private String type;//型号
//方法
//冷藏
public void coldStorage(){
System.out.println("冰箱能够对食品进行冷藏保鲜。");
}
//冷冻
public void freezing(){
System.out.println("冰箱能够对食品冷冻,保鲜时间更长。");
}
//显示信息
public void info(){
System.out.println("这是一款型号为"+type+"的"+brand+"冰箱。");
}
//构造
public Fridge(){}
public Fridge(String brand,String type){
this.brand=brand;
this.type=type;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
//温度调控
public interface TemperControl {
//调节冷藏
void adjustColdTemper(int temperature);
//调节冷冻
void adjustFreezTemper(int temperature);
}
//食品管理
public interface FoodManage {
//显示食品信息
void showFoodInfo();
}
//物联云服务
public interface CloudService {
//服务
void service();
}
//-------------------
//impl包
public class CommonFridge extends Fridge implements TemperControl {
public CommonFridge(){}public CommonFridge(String brand,String type){
super(brand, type);
}
@Override
public void coldStorage() {
System.out.println("普通冰箱冷藏温度设定为-2~8度。");
}
@Override
public void freezing() {
System.out.println("普通冰箱冷冻温度设定为-15~-25度");
}
@Override
public void adjustColdTemper(int temperature) {
System.out.println("手动调整将"+this.getBrand()+this.getType()+"冰箱冷藏温度
设定为"+temperature+"度。");
}
@Override
public void adjustFreezTemper(int temperature) {
System.out.println("手动调整将"+this.getBrand()+this.getType()+"冰箱冷冻温度
设定为"+temperature+"度。");
}
}
public class IntelligentFridge extends Fridge implements CloudService,
FoodManage, TemperControl {
public IntelligentFridge(){}
public IntelligentFridge(String brand,String type){
super(brand, type);
}
@Override
public void coldStorage() {
System.out.println("智能冰箱冷藏室正在通过气味自动将不新鲜食材调动到距离冰箱门近的
地方。");
}
@Override
public void freezing() {
System.out.println("智能冰箱冷冻室正在自动智能除霜!");
}
@Override
public void service() {
System.out.println(this.getBrand()+this.getType()+"冰箱正在统计食材存量,并
通知附近超市上门补货。");
}
@Override
public void showFoodInfo() {
System.out.println(this.getBrand()+this.getType()+"正在统计食材种类、保质
期,根据用户信息偏好推荐食谱显示到大屏幕");
}
@Override
public void adjustColdTemper(int temperature) {
System.out.println(this.getBrand()+this.getType()+"冰箱通过电脑温控系统将冷
藏温度设定为"+temperature);
}
@Override异常
准备
什么是异常
什么是异常处理
Java中如何进行异常处理
如何使用log4j2记录日志
目标:
熟练使用try-catch-fifinally进行异常处理
理解Java异常处理机制的执行过程
会使用throws声明异常
会使用throw抛出异常
了解异常分类
会使用log4j2记录日志
异常处理顺序:
public void adjustFreezTemper(int temperature) {
System.out.println(this.getBrand()+this.getType()+"冰箱通过电脑温控系统将冷
冻温度设定为"+temperature);
}
}
//test包
public class TestFridge {
public static void main(String[] args) {
System.out.println("****普通冰箱****");
CommonFridge commonFridge=new CommonFridge("西门子","BCD-123W");
commonFridge.info();
commonFridge.coldStorage();
commonFridge.freezing();
commonFridge.adjustColdTemper(0);
commonFridge.adjustFreezTemper(-30);
System.out.println("****智能冰箱****");
IntelligentFridge intelligentFridge=new IntelligentFridge("海尔","BCD-
998W");
intelligentFridge.info();
intelligentFridge.coldStorage();
intelligentFridge.freezing();
intelligentFridge.adjustColdTemper(1);
intelligentFridge.adjustFreezTemper(-25);
intelligentFridge.service();
intelligentFridge.showFoodInfo();
}
}
try{
①
}catch(Exception e){
②
}finally{认识异常:
Java中,所有的异常都被定义成类
除了内置的异常类之外,也可以自定义异常类
Java中异常处理机制也允许自行抛出异常
Java中的异常处理:
关键字:try、catch、fifinally、throws、throw
catch()块中
多重catch()
③
}
/*
说明:
①为可能出现异常的代码块
②为try{}中出现异常后执行的代码,如try{}无异常此处不执行
③为①或②任意执行完后此处===必执行===
--------------------------------------------
try结构后,catch与finally结构必有一处存在
--------------------------------------------
在try或catch结构中
——如存在return语句,执行的时机在finally之后
——如存在System.exit(1)语句,程序立即结束并退出,不在执行其他代码
--------------------------------------------
catch()中的异常如果不匹配仍会终止程序
--------------------------------------------
*/
e.printStackTrace();//内存堆栈信息
e.getMessage();//String类型异常描述,从printStackTrace()中输出信息的一部分
try {
//有可能出现异常的代码
}catch (异常1 e){
//异常1 处理方案
}catch (异常2 e){
//异常2 处理方案运行时异常:
是可以在程序中避免的异常
当程序进行时发生异常,会输出异常堆栈信息并终止程序运行
可以使用try……catch捕获
常见类型如上图
Check异常
}
……
finally {
//一定会执行的语句,释放资源
}
/*
说明:
排列异常顺序:异常1,2,……先子类后父类
发生异常时按顺序逐个匹配
***只执行第一个与异常类型匹配的catch语句
*/是指运行时异常以外的异常
是用户错误或问题引起的异常
程序员无法预见
编译器会提示
如果不捕获会出现编译器错误
常见的异常类型:
FileNotFoundException异常
SQLException异常
……
经验:
对于异常来说,不同项目会有不同标准,要求严格的项目每个异常单独处理,会增加一定代码量
声明异常:
经验:
开发中,main()方法不建议声明异常,会导致程序中断
抛出异常:
除了系统自动抛出异常外,某些问题要程序员主动抛出异常
根据程序逻辑自定义异常类,在Java异常体系中并未提供,不能抛出
根据业务需求自行选择异常抛出时机,或自定义异常处理逻辑
语法:
说明:
throw只能抛出Throwable类或其子类的对象
示例:
某影院3~6岁儿童及60岁以上老人可以买半票25元/张,其他年龄观众全价50元/张
在购票系统中,输入正常范围年龄,可提示正确票价信息;否则,输出错误提示
public void 方法名(String[] args) throws 异常类型1[,异常类型2[,……]]{//声明多个异常
用","(逗号)隔开
//方法体
}
throw new 异常名([参数列表]);
//示例:
throw new Exception();
//定义异常方法
public String showTicketPrice(int age) throws Exception{
if (age<3){
throw new Exception("您录入的年龄有误!");
}else if (age>=60||age<=6){
return "您可以购买半价票 25元/张";
}else {
return "您可以购买半价票 25元/张";问题:
说出5个常见的运行时异常?
Error和Exception区别
throw与throws的区别是什么
自定义异常
当Java异常体系中提供的异常类型不能满足程序的需求时
步骤:
1. 定义异常类,继承Exception类或者RuntimeException类
2. 编写异常类的构造方法,并继承父类的实现
1. 常见构造方法
3. 实例化自定义异常对象,并使用throw关键字抛出
代码示例:
}
}
//调用异常
Scanner input=new Scanner(System.in);
System.out.println("请输入年龄:");
int age=input.nextInt();
try {
ExceptionDemo3 demo3=new ExceptionDemo3();
System.out.println(demo3.showTicketPrice(age));
} catch (Exception e) {
e.printStackTrace();
}
ArithmeticException //算数异常
ArrayIndexOutOfBoundsException //数组下标越界
NullPointerException //访问Null对象成员、
ClassNotFoundException //不能加载所需类
InputMismatchException //输入数据类型不匹配
IllegalArgumentException //参数非法异常
ClassCastException //强类型转换异常
NumberFormatException //数字格式转换异常
……
//如下代码是否正确?为什么?
throw new String("exception"); //错,throw只能抛出Throwable类或其子类的对象
public Exception(){super();}
public Exception(String message){super(message);}
public Exception(String message,Throwable cause){super(message,cause);}
public Exception(Throwable cause){super(cause);}//自定义异常
public class AgeException extends Exception {
public AgeException(){
super();
}
public AgeException(String message){
super(message);
}
//……其他方法
}
//使用异常
public class ExceptionDemo3 {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
System.out.println("请输入年龄:");
int age=input.nextInt();
try {
ExceptionDemo3 demo3=new ExceptionDemo3();
System.out.println(demo3.showTicketPrice(age));
} catch (AgeException e) {
e.printStackTrace();
}
}
public String showTicketPrice(int age) throws AgeException {
if (age<3){
throw new AgeException("您录入的年龄有误!");
}else if (age>=60||age<=6){
return "您可以购买半价票 25元/张";
}else {
return "您可以购买半价票 25元/张";
}
}
}
使用场景:
抛出Java中原生不存在,而业务逻辑中需要的异常
团队开发中同一对外表现的异常信息
代码阅读:
//父类
public class SuperClass {
public void start() throws IOException{
throw new IOException("Not able to open file.");
}
}
//子类
public class SubClass extends SuperClass{
public void start() throws Exception{//检查有误:继承中,子类重写方法抛出不能高于
父类抛出异常
throw new Exception("Not able to open file.");
}
}实践示例:
根据票款输出支付信息
需求:
自动售票:输入购买票数量及支付金额
假设每张票50元/张,支付足额时:“支付成功”;
否则,抛出异常信息:
1. 票数<1,抛出“您输入的票数至少一张”;
2. 支付金额不足时,抛出“您支付的金额不足!应支付XX元!”
提示:
自定义异常类PayException ,编写所需构造方法
根据业务逻辑,使用throw关键字抛出异常
使用关键字throws关键字声明可能发生的异常
//自定义异常
public class PayException extends Exception{
public PayException(){
super();
}
public PayException(String message){
super(message);
}
}
//异常调用
public class PayExecptionTest {
public static void main(String[] args) {
Scanner input =new Scanner(System.in);
System.out.println("您需要购买几张票?");
int num= input.nextInt();
System.out.println("您输入的金额?");
float money= input.nextFloat();
try {
PayExecptionTest test=new PayExecptionTest();
test.showPayMessage(num,money);
} catch (PayException e) {
e.printStackTrace();
}
}
private void showPayMessage(int num, float money) throws PayException {
if (num<=0){
throw new PayException("您输入的购票数量至少一张");
}
int payMoney=num*50;
if (payMoney>money){
throw new PayException("你支付的金额不足!应支付"+payMoney+"元。");
}else {
System.out.println("支付成功!");
}
}
}日志记录框架
日志(log)
主要用来记录系统运行中一些重要操作信息
便于监视系统运行情况,帮助用户提前发现和避开可能出现的问题,或者出现问题后根据日志找到
答案
日志分类
SQL日志、异常日志、业务日志
日志主要用途
问题追踪
状态监控
安全审计
日志框架log4j
一款非常优秀的日志框架
控制日志的输出级别
控制日志输出的目的地(控制台、文件等)
控制日志每条的格式
log4j2使用:
1. 导入项目操作步骤:
1. 编写配置文件
文件后缀名可为 .xml、.json或 .jsn
需手工创建
通常log4j2.xml命名
2. 定义日志记录器Logger
获取日志记录器的方式方法
描述
public void debug(Object msg)
public void debug(Object msg,Throwable t)
记录debug级别日志
public void info(Object msg)
public void info(Object msg,Throwable t)
记录info级别日志
public void warn(Object msg)
public void warn(Object msg,Throwable t)
记录warn级别日志
public void error(Object msg)
public void error(Object msg,Throwable t)
记录error级别日志
public void fatal(Object msg)
public void fatal(Object msg,Throwable t)
记录fatal级别日志
3. 记录日志
Logger类可以替代System.out或System.err,供开发者记录日志信息
Logger类常用方法
日志记录级别(由 低----------> 高)
all:最低等级,用于打开所有日志记录
trace:用于程序追踪输出
debug:指出细粒度信息事件,对高度应用程序是非常有帮助的
info:在粗颗粒度级别上指明消息,强调应用程序的运行过程
warn:表示警告信息,即可能会出现的潜在错误
error:指出虽然发生错误事件,但仍然不影响系统的继续运行
fatal:指出严重错误事件将会导致应用程序退出
OFF:最高级别,用于关闭所用日志记录
--程序会输出高于等级所设置的日志
--设置日志等级越高,输出日志越少
2. 配置过程编写log4j2.xml文件<?xml version="1.0" encoding="UTF-8" ?>
<configuration staus="OFF">
<appenders>
<!--输出日志到控制台的配置-->
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%l] %-5level - %msg%n"
/>
</Console>
<!--输出日志到文件的配置-->
<File name="log" fileName="log/test.log">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%l] %-5level -
%msg%n" />
</File>
</appenders>
<loggers>
<root level="all">
<appender-ref ref="Console"/>
<appender-ref ref="log"/>
</root>
</loggers>
</configuration>