文章目录
- 1.建模语言(UML)
- 1.1类
- 1.2接口
- 1.3UML类图
- 1.4 类之间的关系
- 2.面向对象语言的设计原则
- 3.单例模式
- 4.工厂模式
- 5.代理模式
- JDK代理
- CgLib代理
1.建模语言(UML)
建模语言(Unified Modeling Language,UML)是用来设计软件蓝图的可视化建模语言,1997 年被国际对象管理组织(OMG)采纳为面向对象的建模语言的国际标准.
1.1类
指具有相同属性、方法和关系的对象的抽象,它封装了数据和行为,是面向对象程序设计(OOP)的基础,具有
封装性、继承性和多态性等三大特性
。
- (1) 类名(Name)是一个字符串,例如,Student。
- (2) 属性(Attribute)是指类的特性,即类的成员变量。UML 按以下格式表示:
[可见性]属性名:类型[=默认值]
“可见性”表示
该属性对类外的元素是否可见
,
包括公有(Public)+
、私有(Private)-
、受保护(Protected)#
,和关联(Friendly)~
4 种.
- (3) 操作(Operations)是类的任意一个实例对象都可以使用的行为,是类的成员方法。表示:
[可见性]名称(参数列表)[:返回类型]
;
例如学生类:
1.2接口
接口具有类的结构但
不可被实例化
,仅可以被子类实现
。它包含抽象操作,但不包含属性。
它描述了类或组件对外可见的动作。
在 UML 中,接口使用一个带有名称的小圆圈来进行表示.
1.3UML类图
用来
显示系统中的类、接口、协作以及它们之间的静态结构和关系的一种静态模型
。
它主要用于描述软件系统的结构化设计
,帮助开发者简化对软件系统的理解,
是系统分析与设计阶段的重要产物,也是系统编码与测试的重要模型依据。
其中的类可以通过某种编程 语言直接实现。类图在软件系统开发的整个生命周期都是有效的,它是面向对象系统的建模中最常见的图。
1.4 类之间的关系
依赖(Dependency)关系 是一种使用关系,它是对象之间耦合度最弱的一种关联方式
,是临时性的关联
。在代码中,某个类的方法通过局部变量、方法的参数或者对静态方法的调用来访问另一个类(被依赖类)中的某些方法来完成一些职责。
参数
例如人与手机之间就是依赖关系
关联(Association)关系 是对象之间的一种引用关系
,用于表示一类对象与另一类对象之间的联系,如老师和学生、师傅和徒弟、丈夫和妻子等。关联关系是类与类之间最常用的一种关系,分为一般关联关系、聚合关系和组合关系。
在一个类中,把另一个类当做属性
关联可以是双向的,也可以是单向的。在 UML 类图中,双向的关联可以用带两个箭头或者没有箭头的实线来表示,单向的关联用带一个箭头的实线来表示,箭头从使用类指向被关联的类。也可以在关联线的两端标注角色名,代表两种不同的角色。
关联(Association)关系 之聚合(Aggregation)关系 是强关联关系
,是整体和部分之间的关系
,是 has-a 的关系。聚合关系也是通过成员对象来实现的,其中成员对象是整体对象的一部分,但是成员对象可以脱离整体对象而独立存在。
例如图书馆和学生之间的关联,图书馆被拆了,但是学生依然存在;
关联(Association)关系 之 组合(Composition)关系 表示类之间的整体与 部分的关系,但它是一种更强烈的聚合关系
,是 cxmtains-a 关系。在组合关系中,整体对象可以控制部分对象的生命周期,一旦整体对象不存在,部分对象也将不存在,部分对象不能脱离整体对象而存在。
例如人体的头部和身体;少了任何一方,普通人类生存是比较困难的.
泛化(Generalization)关系 是对象之间耦合度最大的一种关系
,表示一般与特殊的关系,是父类与子类之间的关系,继承关系,是 is-a 的关系。在 UML 类图中,泛化关系用带空心三角箭头的实线来表示,箭头从子类指向父类。在代码实现时,使用面向对象的继承机制来实现泛化关系。
类继承类, 接口继承接口
实现(Realization)关系 是接口与实现类之间的关系
。
在 UML 类图中,实现关系使用带空心三角箭头的虚线来表示,箭头从实现类
指向接口。
2.面向对象语言的设计原则
重点知识学习(6.1)–[Java的23种设计模式以及设计原则笔记链接整理]
3.单例模式
单例模式
仅提供一个实例对象的模式,
例如,Windows 中只能打开一个任务管理器,这样可以避免因打开多个任务管理器窗口而造成内存资源的浪费,或出现各个窗口显示内容的不一致等错误
- 单例类只有一个实例对象;
- 该单例对象必须由单例类自行创建;
- 单例类对外提供一个访问该单例的全局访问点(提供一个公共的访问方法,内含创建对象);
通常用的有两种方式
懒汉式:
- 在类加载时,不会创建单例对象, 在第一次访问时,才会去创建;
- 懒汉式单例有线程安全问题,必须要加锁处理
案例:
/*
* 懒汉式单例
*/
public class Singleton {
//定义静态的属性;
private static Singleton instance;
//构造方法私有化;
private Singleton (){
}
//向外界提供获取实例的方法 加锁 synchronized 才能保证单例
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
测试
public class Test {
public static void main(String[] args) {
//获取唯一可用的对象
Singleton object1 = Singleton.getInstance();
Singleton object2 = Singleton.getInstance();
Singleton object3 = Singleton.getInstance();
System.out.println(object1);
System.out.println(object2);
System.out.println(object3);
System.out.println(object1 == object2);
}
}
结果
com.xiaozhi.advanced.day06_moshi.singleton.demo1.Singleton@4b67cf4d
com.xiaozhi.advanced.day06_moshi.singleton.demo1.Singleton@4b67cf4d
com.xiaozhi.advanced.day06_moshi.singleton.demo1.Singleton@4b67cf4d
true
饿汉式: (急切式):
在类加载时,就会创建此单例对象,这种写法不会出现线程安全问题;
案例:
/*
* 饿汉式单例
*/
public class Singleton {
//创建 Singleton 的一个对象
private static Singleton instance = new Singleton();
//让构造函数为 private
private Singleton(){}
//获取唯一可用的对象
public static Singleton getInstance(){
return instance;
}
}
测试:
public class Test {
public static void main(String[] args) {
//获取唯一可用的对象
Singleton object1 = Singleton.getInstance();
Singleton object2 = Singleton.getInstance();
Singleton object3 = Singleton.getInstance();
System.out.println(object1);
System.out.println(object2);
System.out.println(object3);
System.out.println(object1 == object2);
}
}
结果:
com.xiaozhi.advanced.day06_moshi.singleton.demo2.Singleton@4b67cf4d
com.xiaozhi.advanced.day06_moshi.singleton.demo2.Singleton@4b67cf4d
com.xiaozhi.advanced.day06_moshi.singleton.demo2.Singleton@4b67cf4d
true
4.工厂模式
工厂模式
定义一个创建产品对象的工厂接口,将产品对象的实际创建工作放置到工厂类当中。“创建与使用相分离”
被创建的对象称为“产品”,``创建产品的对象称为“工厂”。
如果要创建的产品不多,只要一个工厂类就可以完成,这种模式叫“简单工厂模式”。
在简单工厂模式中创建实例的方法通常为静态(static)方法,因此简单工厂模式(Simple Factory Pattern)又叫作静态工厂方法模式
(Static Factory MethodPattern)。
例如:简单工厂
简单工厂(SimpleFactory)
创建所需的产品对象
,工厂类的创建产品类的方法可以被外界直接调用,抽象产品(Product):是简单工厂创建的所有对象的父类,负责描述所有实例共有的公共接口。
具体产品(ConcreteProduct):实现了抽象的接口/抽象类的具体实现类 .
案例:
抽象产品
//抽象产品
public interface Product {
void show();
}
具体产品A
public class ProductA implements Product {
@Override
public void show() {
System.out.println("具体产品A显示...");
}
}
具体产品B
public class ProductB implements Product {
@Override
public void show() {
System.out.println("具体产品B显示...");
}
}
工厂
/*
* 工厂:生产对象
*/
public class SimpleFactory {
public Product createProduct(String className){
if(className == null){
return null;
}else{
try {
//使用了反射机制;
return (Product) Class.forName(className).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
return null;
} catch (IllegalAccessException e) {
e.printStackTrace();
return null;
} catch (ClassNotFoundException e) {
e.printStackTrace();
return null;
}
}
}
}
测试
public class Test {
public static void main(String[] args) {
SimpleFactory simpleFactory = new SimpleFactory();
Product productA = simpleFactory.createProduct("com.xiaozhi.advanced.day06_moshi.simplefactory.ProductA");
Product productB = simpleFactory.createProduct("com.xiaozhi.advanced.day06_moshi.simplefactory.ProductB");
productA.show();
productB.show();
}
}
具体产品A显示...
具体产品B显示...
5.代理模式
代理模式
比如说,你有好多套房子要出租;但是你觉得自己带客户看房子比较麻烦,就找了中介来作为中间件;让中介帮你把房子租出去;这就像是个简单的代理模式原理;
一个客户不能或者不想直接访问另一个对象,这时需要找一个中介帮忙完成某项任务,这个中介就是代理对象。
- 保护目标对象
- 对目标的功能进行扩展
- 将用户和目标进行分类,降低了耦合度
代理模式的结构:
抽象主题(Subject)类
:通过接口或抽象类声明真实主题和代理对象实现的业务方法。真实主题(Real Subject)类
:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。代理(Proxy)类
:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
静态代理
- 一般使用于关系是固定的,代理某类事务必须实现接口,
- 若代理多个目标对象,那么就需要实现更多的接口,后期维护比较麻烦.
静态代理模式的特点,
代理类接受一个 Subject 接口的对象
,任何实现该接口的对象,都可以通过代理类进行代理,增加了通用性。
缺点
:每一个代理类都必须实现一遍委托类(也就是 realsubject)的接口,如果接口增加方法,则代理类中也必须跟着一起修改。(有点违反开闭原则了)
代理类每一个接口对象对应一个委托对象,如果委托对象非常多,则静态代理类就非常臃肿,效率降低.
案例
比如经常写的SSM结构下的调用;
持久层接口定义抽象方法;
/*
Dao接口,定义保存功能的抽象方法;
*/
public interface BaseDao {
void save();
}
具体的持久层实现类;
/*
实际功能实现类
*/
public class UserDaoImpl implements BaseDao {
//子类重写实现;
@Override
public void save() {
System.out.println("UserDaoImpl:save()");
}
}
静态代理类
/*
* 静态代理类
*/
public class StaticDaoProxy implements BaseDao {
//接收所有实现BaseDao接口的实现类对象
private BaseDao baseDao;
// 将被代理者的实例传进动态代理类的构造函数中
public StaticDaoProxy(BaseDao baseDao) {
this.baseDao = baseDao;
}
//代理他们实现功能,可以在调用前,调用后 写上前置/后置的方法;
//有点AOP的感觉了;
@Override
public void save() {
System.out.println("before");
baseDao.save();
System.out.println("after");
}
}
测试:
public class Test {
public static void main(String[] args) {
//把实际执行者交给代理对象管理即可
StaticDaoProxy subject = new StaticDaoProxy(new UserDaoImpl());
subject.save();
}
}
before
UserDaoImpl:save()
after
动态代理
代理类不在 Java 代码中实现,而是在运行时期生成
,
- 相比静态代理,动态代理可方便对委托类的方法进行统一处理,如添加方法,调用次数,添加日志功能.
- 动态代理分为
jdk 动态代理和 cglib 动态代理
JDK代理
通过反射来实现
,借助 Java 自带的java.lang.reflect.Proxy
,通过固定的规则生成。
- 不需要代理类指定的去实现某些抽象接口, 代理的扩展性好.
- 动态生成代理对象, 要求目标类必需有实现接口(以前的spring项目结构持久层都得需要结构,下置实现类).
- 编写一个委托类的接口,即静态代理的
- 实现一个真正的委托类,即静态代理的
- 创建一个动态代理类,实现 InvocationHandler 接口,并重写该 invoke方法
- 在测试类中,生成动态代理的对象。
案例;以刚才的案例为例,接口和实现类不变,代理类改变;
/*
* 动态代理类;实现 InvocationHandler 接口,并重写该 invoke方法
*/
public class DynamicDaoProxy implements InvocationHandler {
// 被代理类的实例
private Object object;
// 将被代理者的实例传进动态代理类的构造函数中
public DynamicDaoProxy(Object object) {
this.object = object;
}
/*
* 覆盖InvocationHandler接口中的invoke()方法
* Object proxy 表示代理对象
* Method method 代理对象中的方法
* Object[] args 表示代理方法中的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
//反射机制获取,目标对象的方法;
Object result = method.invoke(object, args);
System.out.println("after");
return result;
}
}
测试
public class Test {
public static void main(String[] args) {
//代理的真实对象
UserDaoImpl userDaoImpl = new UserDaoImpl();
//代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
InvocationHandler dynamicProxy = new DynamicDaoProxy(userDaoImpl);
/*
* 通过Proxy的newProxyInstance方法来创建代理对象
* 第一个参数 handler.getClass().getClassLoader() ,使用handler这个类的ClassLoader对象来加载代理对象
* 第二个参数realSubject.getClass().getInterfaces(),为代理对象提供的接口是真实对象所实行的接口,表示代理的是该真实对象,即可调用这组接口中的方法
* 第三个参数dynamicProxy,将这个代理对象关联到了上方的 InvocationHandler 这个对象上
*/
BaseDao baseDao = (BaseDao) Proxy.newProxyInstance(dynamicProxy.getClass().getClassLoader(), userDaoImpl.getClass().getInterfaces(), dynamicProxy);
baseDao.save();
}
}
before
UserDaoImpl:save()
after
spring框架项目中,需要用到接口;
**spring早期框架 使用的动态代理只有Jdk代理;而Jdk代理要求目标类必须实现接口, 架构扩展方法时的需求;**
CgLib代理
对于没有接口的类,需要CgLib代理实现动态代理
采用字节码技术,原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。
由于采用的是继承,所以不能对final 修饰的类进行代理
相比于Jdk代理;CgLib代理创建代理对象时所花费的时间却比 JDK 多
- 需要引入 cglib 的 jar 文件,但是 Spring 的核心包中已经包括了 Cglib 功能,所以直接引入
spring-core-xxx.jar
即可. - 引入功能包后,就可以在内存中动态构建子类
- 代理的类不能为 final,否则报错
- 目标对象的方法如果为 final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.
案例
这里就不需要接口了;
//具体主题
public class UserDaoImpl {
public void save() {
System.out.println("UserDaoImpl:save()");
}
}
动态代理类
/*
* 动态代理类
*/
public class CGLibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class<?> clazz) {
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
/*
* 拦截所有目标类方法的调用
* 参数:
* obj 目标实例对象
* method 目标方法的反射对象
* args 方法的参数
* proxy 代理类的实例
*/
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
//代理类调用父类的方法
System.out.println("开始事务");
Object obj1 = proxy.invokeSuper(obj, args);
System.out.println("关闭事务");
return obj1;
}
}
测试
public class Test {
public static void main(String[] args) {
CGLibProxy proxy = new CGLibProxy();
UserDaoImpl userDaoImpl = (UserDaoImpl) proxy.getProxy(UserDaoImpl.class);
userDaoImpl.save();
}
}
开始事务
UserDaoImpl:save()
关闭事务