目录
第一节 异常的捕获
1.1 引入异常
1.2 异常体系
1.3 异常处理:try-catch-finally
第二节:异常的抛出
2.1 异常处理:throws、throw
2.2 自定义异常
第三节 面向对象设计原则
3.1 单一职责原则
3.2 开闭原则 OCP
3.3 里氏替代原则
3.4 依赖倒置原则DIP
3.5 接口分离原则ISP
3.6 迪米特法则LOD
3.7 合成/聚合复用原则CARP
第四节 类与类之间的6种关系
4.1 认识UML
4.2 认识PowerDesigner
4.3类和类的六种关系
生活中的异常:
正常情况下,小王每日开车去上班,耗时大约30分钟
但是,异常情况迟早要发生!
面对异常该怎么办呢?生活中,我们会根据不同的异常进行相应的处理,而不会就此中断我们的生活
程序中的异常
示例1:给出除数和被除数,求商
示例2:将d:/a.txt复制到e:/a.txt
面对异常该怎么办呢?
方式1:由开发者通过if-else来解决异常问题
方式2:开发者不需要通过if-else来解决异常问题,而是Java提供异常处理机制。它将异常处理代码和和业务代码分离,使程序更优雅,更好的容错性,高键壮性
异常( Exception 也称例外)就是在程序的运行过程中所发生的不正常的事件,它会中断正在运行的程序
当Java程序出现以上的异常时,就会在所处的方法中产生一个异常对象。这个异常对象包括异常的类型,异常出现时程序的运行状态以及对该异常的详细描述。
Java的异常处理是通过5个关键字来实现的:try、catch、 finally、throw、throws
【示例1】引入异常
package exceptionDemo;
public class Test1 {
public static void main(String[] args) throws InterruptedException {
int a=0;
if(a!=0){
System.out.println(1/a);
}
// int[] arr={1};
//System.out.println(arr[10]);
/*Person p=null ;
System.out.println(p.equals(p));*/
Thread.sleep(1000);
System.out.println("后续代码");
}
}
class Person{}
张三谈了一个女友 ,要和女友出去吃饭,没有好看的衣服, 室友李四有一件非常好看的衬衫,张三想借过来穿,但是衣服有一些脏;
异常:脏
捕获处理
李四把衬衫洗干净,交给张三,李四把异常处理完毕,李四捕获异常
抛出处理
李四不洗,让张三自己去洗,李四把问题留个张三,李四抛出异常
优点:Java已经提供了异常处理机制,发生异常后,会给出异常类型、异常提示信息、异常的位置
缺点:出现异常后,后续语句不执行了;提示信息太专业,可读性差
解决:try-catch-finally 处理异常;throws 抛出异常不处理,调用者处理
Error
Error类层次描述了Java运行时系统内部错误和资源耗尽错误,一般指与JVM或动态加载等相关的问题,如虚拟机错误,动态链接失败,系统崩溃等。
这类错误是我们无法控制的,同时也是非常罕见的错误。所以在编程中,不去处理这类错误。注:我们不需要管理Error!
Exception
所有异常类的父类,其子类对应了各种各样可能出现的异常事件。
Exception分类
可不必对其处理,系统自动检测处理
一类特殊的异常,如被 0 除、数组下标超范围等,其产生比较频繁,处理麻烦,如果显式的声明或捕获将会对程序可读性和运行效率影响很大
必须捕获进行处理,否则会出现编译错误。
注意:只有Java提供了Checked异常,体现了Java的严谨性,提高了Java的健壮性。同时也是一个备受争议的问题。
【示例2】区分运行时异常和检查异常
public class TestException5 {
public static void main(String[] args) {
//运行时异常,异常可以不进行捕获,运行时才会发生异常
String str= "bjsxt";
System.out.println(str.toUpperCase());
NullPointerException ne;
ArithmeticException ae;
InputMismatchException ime;
ClassCastException cce;
int result = 10 / 0 ;
//检查异常 checked Exception
try {
Class.forName("com.bjsxt.adfadfa.afasdfad").newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
try {
InputStream is = new FileInputStream("d:/bjsxt.txt");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
【示例3】使用try-catch处理异常
// try catch 简单使用
public static void main(String[] args) {
testA();
}
public static void testA(){
System.out.println("程序启动");
try{
System.out.println(1/0);
}catch(ArithmeticException e){
System.out.println(e.getMessage());
e.printStackTrace();
}
System.out.println("程序结束");
}
单个catch捕获多个不同异常
public class Test1 {
public static void main(String[] args) {
System.out.println("第一行");
System.out.println("第二行");
// 使用try语句对可能出现异常的代码进行监控
// 通过catch语句 准备异常一旦发生,对应的处理机制
// java中万物皆对象
try {
int[] arr={1};
System.out.println(arr[10]);
String s=null;
System.out.println(s.length());
System.out.println(1 / 0);
}catch(NullPointerException|ArithmeticException|ArrayIndexOutOfBoundsException e){
// 异常发生之后的处理办法
// 获得异常信息
String message = e.getMessage();
System.out.println(message);
e.printStackTrace();
}
System.out.println("后续代码");
}
}
多重catch捕获异常
public class Test3 {
static String s;
public static void main(String[] args) {
try {
//System.out.println(s.length());
//System.out.println(1 / 0);
//String s0 = (String) new Object();
int[] arr={1};
System.out.println(arr[10]);
}catch (ClassCastException e){
/*e.printStackTrace();*/
System.out.println("类型转换异常的处理代码");
}catch(ArithmeticException e){
System.out.println("数学运算异常的处理代码");
}catch(NullPointerException e){
System.out.println("空指针异常处理代码");
}catch (RuntimeException e){
System.out.println("其他运行时异常处理代码");
}catch (Exception e){
System.out.println("其他检查型异常处理代码");
}
System.out.println("程序执行结束");
}
}
try catch嵌套
public class Test4 {
static String s;
public static void main(String[] args) {
try {
System.out.println("异常产生之前的代码");
try {
System.out.println(s.length());
}catch (NullPointerException e){
System.out.println("空指针异常了");
}
System.out.println("异常产生之后的代码");
}catch (Exception e){
e.printStackTrace();
}
System.out.println("程序执行结束");
}
}
finally语句块的使用
package com.bjsxt.exceptionDemo2;
import java.util.InputMismatchException;
import java.util.Scanner;
/**
* finally语句块 无论异常是否出现,都会执行的 语句块
* 一般用于释放资源,关闭资源
*
*/
public class Test5 {
public static void main(String[] args) {
Scanner sc =new Scanner(System.in);
System.out.println("请输入整数");
try{
int i=sc.nextInt();
}catch (InputMismatchException e){
System.out.println("输入格式有误");
}finally{
System.out.println("finally语句块执行");
sc.close();
}
}
}
package com.bjsxt.exceptionDemo2;
/**
* 在方法体中,finally语句块一定会在return之前执行
*/
public class Test6 {
public static void main(String[] args) {
int chu = getChu(10, 2);
System.out.println(chu);
}
public static int getChu(int a,int b){
int result=0;
try{
System.out.println("try");
result=a/b;
}catch (ArithmeticException e){
e.printStackTrace();
result= 0;
}finally {
System.out.println("finally执行完毕了");
//return 100;
}
return result;
}
}
try-catch执行情况
不执行catch块代码,执行catch块后边的代码
执行catch块代码,执行catch块后边的代码
不执行catch块代码,不执行catch块后边的代码,程序会中断运行
注意:
catch块中如何处理异常
System.err.println("除数不能为零。");
System.err.println("被除数和除数必须是整数。");
toString ( )方法,显示异常的类名和产生异常的原因
void printStackTrace() 输出异常的堆栈信息
String getMessage()返回异常信息描述字符串,printStackTrace()信息一部分
throw e
异常类型 |
说 明 |
Exception |
异常层次结构的根类 |
ArithmeticException |
算术错误情形,如以零作除数 |
ArrayIndexOutOfBoundsException |
数组下标越界 |
NullPointerException |
尝试访问 null 对象成员 |
ClassNotFoundException |
不能加载所需的类 |
InputMismatchException |
欲得到数据类型与实际输入类型不匹配 |
IllegalArgumentException |
方法接收到非法参数 |
ClassCastException |
对象强制类型转换出错 |
NumberFormatException | 数字格式转换异常,如把"ab"转换成数字 |
问题1:在什么情况下,catch后面的语句是不执行的
情况1:throw e;
情况2:发生的异常和catch中异常类型不匹配
情况3:return
问题2:不管什么情况下,希望某些语句都执行,怎么办
finally语句
【示例4】使用try-catch-finally处理异常
package com.bjsxt.tryDemo;
import java.util.InputMismatchException;
import java.util.Scanner;
public class Test5 {
public static void main(String[] args) {
Scanner sc =new Scanner(System.in);
try {
System.out.println("请录入第一个数字");
double num1 = sc.nextDouble();
System.out.println("请录入符号");
String f = sc.next();
System.out.println("请录入第二个数字");
double num2 = sc.nextDouble();
switch (f) {
case "+":
System.out.println(num1 + num2);
break;
case "/":
System.out.println(num1 / num2);
break;
case "*":
System.out.println(num1 * num2);
break;
case "-":
System.out.println(num1 - num2);
break;
default:
System.out.println("操作符只能是+-*/");
}
}catch (NullPointerException e){
System.out.println("出异常了");
} finally {
System.out.println("finally block");
// 无论程序是否出现异常都会执行的代码 一般用来做资源释放工作
sc.close();
}
System.out.println("程序执行结束了");
}
}
问题3:return和finally语句的执行顺序
执行return之前的语句----执行finally语句-----执行return
问题4:finally在实际开发中的使用场合
IO流的管理,数据库连接的关闭 socket的关闭
问题5:唯一的例外
System.exit(0); 终止当前正在运行的 Java 虚拟机。
package com.bjsxt.tryDemo;
public class Test6 {
//定义我一个方法 返回a/b的结果
public static void main(String[] args) {
int c=chu(10,0);
System.out.println(c);
}
public static int chu(int a,int b){
//当finally遇见return的时候,finally一定会在return之前先执行
// 要么就别在finally中写return 要么就只在finally里面写return
int res=0;
try{
res=a/b;
}catch(ArithmeticException e){
e.printStackTrace();
}finally {
return res;
}
}
}
问题:一段代码可能会引发多种类型的异常,是否可以分开处理
人为抛出异常throw
声明方法的异常列表throws
【示例5】throws的使用:声明方法的异常列表
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class Test2 {
public static void main(String[] args) throws InterruptedException, FileNotFoundException {
showHahaha();
}
/*
* 方法头
* 访问修饰符
* 返回值类型
* 方法名
* 参数列表
* 异常列表:代表当前方法在使用时可能会出现的异常,如果要使用该方法,则就要对异常作出处理
* 方法体{}
*
* */
// 定义一个方法,每隔一秒向控制台输出一个"哈" 5个
public static void showHahaha()throws InterruptedException, FileNotFoundException,ArithmeticException {
for (int i = 0; i < 5; i++) {
System.out.println("哈");
Thread.sleep(1000);
}
System.out.println(1/0);
FileInputStream fis = new FileInputStream("e:/aaa.txt");
}
}
【示例6】throw的使用 :人为抛出异常
public class Test {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
System.out.println("请录入一个学生的年龄");
int age = sc.nextInt();
if(age >0 && age <100){
System.out.println("OK ");
}else{
/*人为抛出异常的关键字 throw
* 在throw后面必须放一个异常对象
* */
throw new ArithmeticException("/ 测试人为抛出异常");
}
sc.close();
}
}
在程序中,可能会遇到任何标准异常类都没有充分的描述清楚的问题,这种情况下可以创建自己的异常类。
从Exception类或者它的子类派生一个子类即可。习惯上,定义的类应该包含2个构造器:一个是默认构造器,另一个是带有详细信息的构造器
【示例7】自定义异常
package com.bjsxt.throwDemo;
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
System.out.println("请录入一个学生的年龄");
int age = sc.nextInt();
if(age >0 && age <100){
System.out.println("OK ");
}else{
/*人为抛出异常的关键字 throw
* 在throw后面必须放一个异常对象
* */
throw new AgeOutofRangeException("年龄范围有误:"+age);
}
sc.close();
}
}
/*
* 检查型异常 Exception
* 运行时异常 RuntimeException
*
* */
class AgeOutofRangeException extends RuntimeException {
public AgeOutofRangeException(){
}
public AgeOutofRangeException(String message){
super(message);
}
}
面向对象设计原则是面向对象设计的基石,面向对象设计质量的依据和保障。一共有7个设计原则。设计模式就是面向对象设计原则的经典应用。
1.概念
单一职责原则 SRP --- Single Responsibility Principle
There should never be more than one reason for a class to change。
应该有且仅有一个原因引起类的变更
系统中的每个类都应只有一个职责,而所有类所关注的就是自身职责的完成
2.好处
3.举例
饭店不同员工职责不同,我们不需要老板一肩扛起所有工作,可以安排不同岗位的员工。即使某个员工离职了,也比较好招聘到新员工。
JavaEE中的分层框架模式实际上体现了单一职责原则
对于用户操作,可以分解存储数据的实体类User,完成数据库操作的DAO类UserDao、完成业务操作的业务类UserService、显示用户信息的页面userList.jsp,甚至增加负责调度的UserServlet。
4.注意
1.概念
开闭原则OCP--Open Closed Principle
Software entities should be open for extension,but closed for modification。
软件实体应当对扩展开放,对修改关闭。更通俗翻译:软件系统中的各种组件,如模块(Modules)、类(Classes)以及功能(Functions)等,应该在不修改现有代码的基础上,引入新功能。
2.好处
3.举例
定义飞行接口Flyable,可以有多个实现类。showFly()使用Flyable作为参数,可以传递所有实现类的对象。有了新选手,增加实现类即可。不需要修改showFly()所在的类。
【示例8】开闭原则:
简单工厂模式 >>> 抽象工厂模式
public class Test {
public static void main(String[] args) {
// 获得工厂对象
Factory factory=new ToyotaFactory();
Car car = factory.getCar();
car.run();
}
}
interface Factory{
Car getCar();
}
class AudiFactory implements Factory{
@Override
public Car getCar() {
return new Audi();
}
}
class BMWFactory implements Factory{
@Override
public Car getCar() {
return new BMW();
}
}
class BYDFactory implements Factory{
@Override
public Car getCar() {
return new BYD();
}
}
class ToyotaFactory implements Factory{
@Override
public Car getCar() {
return new BYD();
}
}
interface Car{
void run();
}
class BMW implements Car{
@Override
public void run() {
System.out.println("宝马跑");
}
}
class Audi implements Car{
@Override
public void run() {
System.out.println("奥迪跑");
}
}
class BYD implements Car{
@Override
public void run() {
System.out.println("乱跑");
}
}
class Toyota implements Car{
@Override
public void run() {
System.out.println("丰田跑");
}
}
JavaEE多层模式下,可以定义业务、DAO接口,是和其他层对接的规范。可以增加不同的实现类,而不是去修改原来的实现类。
开发一个计算器类。可以定义一个运算接口,提供基本的加减乘除运算的实现类,如果需要增加取模、开平方、幂等运算,增加新的实现类即可。
4.总结
1.概念
里氏替代原则LSP--Liskov Substitution Principle
functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.
所有引用基类的地方必须能透明地使用其子类的对象。
通俗点讲只要父类能出现的地方子类就可以出现,而且调用子类还不产生任何的错误或异常,调用者可能根本就不需要知道是父类还是子类。但是反过来就不成了,有子类出现的地方,父类未必就能适应。
里氏替换法则包含了四层意思:
2.好处
3.举例
1、企鹅/鸵鸟是鸟吗?
2、玩具手枪是手枪吗?
建议:如果子类不能完整实现父类的方法,或者是父类的某些方法在子类中已经发生“畸变”,那么建议断开父子继承关系,采用依赖、聚集、组合等关系代替继承。
4.总结
1.概念
依赖倒置原则DIP --Dependence Inversion Principle
High level modules should not depend upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions。翻译过来,包含三层含义:高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。
抽象:即抽象类或接口,两者是不能够实例化的。
细节:即具体的实现类,实现接口或者继承抽象类的类,可通过关键字new直接被实例化。
依赖正置就是类间的依赖是实实在在的实现类间的依赖,也就是面向实现编程
依赖倒置原则的本质其实就是通过抽象(抽象类或接口)使各个类或模块的实现彼此独立,不相互影响,实现模块间的松耦合 面向要求进行依赖
2.好处
3.举例
4.总结
1.概念
接口分离原则ISP-- Interface Segregation Principle
有两种定义
第一种:Clients should not be forced to depend upon interfaces that they don't use.客户端不应该强行依赖它不需要的接口
第二种:The dependency of one class to another one should depend on the smallest possible interface.类间的依赖关系应该建立在最小的接口上
接口隔离原则的含义:
建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。也就是说,要为各个类建立专用接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。
2.好处
3.举例
4.总结
1.概念
迪米特法则LOD-- Law of Demeter
talk only to your immediate friends
只与你直接的朋友通信(不跟陌生人说话,朋友越少越好)
如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用.如果其中一个类需要调用另一个类的方法的话,可以通过第三者转发这个调用.
迪米特法则的初衷在于降低类之间的耦合。由于每个类尽量减少对其他类的依赖,因此,很容易使得系统的功能模块功能独立,相互之间不存在(或很少有)依赖关系。
不符合LOD 符合LOD
2.好处
3.举例
买房找一个中介即可,不用是认识很多卖房人。去医院看病不用自己知道所有科室的位置,找一个导医即可。
4.总结
1.概念
合成/聚合复用原则CARP-- Composite Aggregate Reuse Principle
Favor object composition over class inheritance.
优先使用对象组合,而不是类继承.(要尽量使用合成和聚合,尽量不要使用继承)
合成聚合复用原则是指在一个新对象中通过关联关系(组合和聚合关系)使用原来已经存在的一些对象,是之成为新对象的一部分,新的对象通过向这些原来已经具有的对象委派相应的动作或者命令达到复用已有功能的目的。
为何“要尽量使用合成和聚合,尽量不要使用继承”呢?这是因为:
而是用合成和聚合的时候新对象和已有对象的交互往往是通过接口或者抽象类进行的,就可以很好的避免上面的不足,而且这也可以让每一个新的类专注于实现自己的任务,符合单一职责原则。
2.好处
3.举例
【示例9】组合聚合复用原则
public class FatherClass {
public void method1(){
System.out.println("FatherClass method1");
}
public void method2(){
System.out.println("FatherClass method2");
}
}
public class SubClass1 extends FatherClass{
public void method2(){
System.out.println("SubClass1 method2");
}
}
public class SubClass2 extends FatherClass{
public void method2(){
System.out.println("SubClass2 method2");
}
}
使用继承:不灵活;编译的时候就知道运行的结果;可以重用父类的代码,但是不能重用父类的子类的代码
public class MyClass extends FatherClass{
@Override
public void method1() {
System.out.println("MyClass method1");
}
public static void main(String[] args) {
MyClass myClass = new MyClass();
myClass.method1();
myClass.method2();
}
使用组合:灵活。根据注入的具体的子类类型,而出现不同的运行结果。
public class MyClass2 {
private FatherClass clazz;//使用组合,关联
public MyClass2() {
super();
}
public MyClass2(FatherClass clazz) {
super();
this.clazz = clazz;
}
public FatherClass getClazz() {
return clazz;
}
public void setClazz(FatherClass clazz) {
this.clazz = clazz;
}
public void method1() {
clazz.method1();
}
public void method3() {
clazz.method2();
}
public static void main(String[] args) {
MyClass2 clazz = new MyClass2();
//FatherClass fc = new FatherClass();
FatherClass fc = new SubClass2();//多态
clazz.setClazz(fc);
clazz.method1();
clazz.method3();
}
}
4.总结
单一职责原则 |
★★★★ |
开闭原则 |
★★★★★ |
里氏代换原则 |
★★★ |
依赖倒置原则 |
★★★★★ |
接口分离原则 |
★★★ |
迪米特原则 |
★★ |
组合/聚合复用原则 |
★★★★ |
用例图
静态图:类图,包图,对象图。
行为图:状态图和活动图
交互图:顺序图和协作图
实现图:组件图、部署图
1.概要
2.主要功能
1.继承关系(泛化关系 Generalization)
2.实现关系(Realization)
3.依赖关系(Dependency)
a)语义:一个类A使用到了另一个类B,但是这种使用关系是具有偶然性的、临时性的、非常弱的,但是类B的变化会影响到类A
b)语法:类B作为类A的方法的参数(或者局部变量)存在
c)符号:
4.关联关系 (Association)
a)语义:
Class Customer {
Order[] orders;
}
Class Order{
Product[] products;
}
Class Product{
}
b).语法:类B作为成员变量形成存在于类A中
c) 符号:
3. 在关联的两端可以标注关联双方的角色和多重性标记
5. 聚合关系(Aggregation)
a) 语义:
b) 语法:同关联关系
c) 符号:空心菱形加实线箭头
6.组合关系(Composition)
a) 语义:
b) 语法:同关联关系
c) 符号:实心菱形加实线箭头
7.总结
a) 继承和实现一般没有争议
b) 后四种关系的强弱:组合>聚合>关联>依赖。
c) 关联和依赖的区别:
i 关联关系强、长期
ii 关联关系是通过属性来实现;依赖关联是通过方法形参或者局部变量实现
d) 关联、组合/聚合的异同
i 相同:都是关联,都是做类的属性
ii 不同点:组合 /聚合表示的是整体和部分的关系,关联可以表示所有关系
e) 组合和聚合的异同
i 相同:都是关联的特例,都是表示的整体和部分的关系
不同点:整体部分的生命周期是否相同?组合更强