从设计模式的角度看Java程序优化
一、前言
Java程序优化有很多种渠道,比如jvm优化、数据库优化等等,但都是亡羊补牢的措施,如果能在设计程序架构时利用设计模式就把程序的短板解决,就能使程序更加健壮切容易维护迭代
二、常用的设计模式
1、单例模式
单例模式可以确保一个类只产生一个实例,对于系统中频繁使用的对象建议使用单例模式设计,可以节省创建对象耗费的时间以及减少GC清理的压力,代码如下
public class A
{
// 实例化一个私有的静态对象
private static A a = new A();
// 构造方法私有,防止其他代码对其实例化
private A()
{
}
// 通过getInstance获取使用该对象
public static A getInstance()
{
return a;
}
}
因为对象实例是静态对象,所以JVM在加载这个类时就会把对象创建出来,这样无法做到延时加载(就是用到的时候再加载),所以还有一些写法会将对象设置为null,当有人调用getInstance方法时,先判断对象是否为空,为空再实例化对象,达到延时加载的目的,代码如下
public class A
{
// 声明对象时赋值为null,加载类时,不会生成对象
private static A a = null;
private A()
{
}
// 加入synchronized关键字,将这个办法锁起来,当多线程调用时竞争锁,达到线程安全
public static synchronized A getInstance()
{
if (a == null)
{
a = new A();
}
return a;
}
}
以上代码虽然解决了线程安全的问题,但是加入同步锁,会使代码效率降低为代价,所以还需要再改进一下,完美的做法如下
public class A
{
private A()
{
}
// 声明一个静态内部类
private static class AHolder
{
private static A a = new A();
}
public static A getInstance()
{
// 直接返回内部类的对象
return AHolder.a;
}
}
以上代码声明了一个静态内部类,当调用A类时,内部类是不会被初始化的,当调用getInstance方法时,内部类才会被调用,内部类中的对象就会随之实例化,这样使用内部类巧妙的达成了延时加载的目的,同时也不用加锁,性能不会降低
2、代理模式
使用代理模式可以使用一个代理类来间接调用其他类的功能,达到屏蔽主类的效果,在程序优化的角度上,他可以做到:当系统启动时,会做许多初始化的操作,导致系统启动时非常缓慢,可以使用代理类来处理初始化的操作,当系统启动时,加载代理类,而不用做真正的初始化操作,当这个组件真正被用到时,代理类才会真正去调用初始化的操作,这样达到了系统压力的分摊,而加快了系统的启动速度。当然代理类除了应对性能问题,还有很多其他的用途。代码如下
public interface IOperation
{
// 业务逻辑接口
void doIt();
}
public class TrueOperation implements IOperation
{
@Override
public void doIt()
{
// 真正的业务逻辑
System.out.println("do it.");
}
}
public class ProxyOperation implements IOperation
{
// 聚合真正的实现类
private TrueOperation trueOperation = null; @Override
public void doIt()
{
if (trueOperation == null)
{
trueOperation = new TrueOperation();
}
// 代理执行真正的方法
trueOperation.doIt();
}
}
以上代码可以看出,其实代理类就是将真正的业务类聚合到自己,然后代执行,只有当业务方法真正被调用时,业务类才会被初始化,也是延时加载的思想,以上是静态代理模式的写法,存在的问题就是,当业务多了以后,同时还要维护代理类,这样就增加了开发维护的难度,所以进一步优化就是使用动态代理模式,如下代码使用JDK自带的动态代理模式
// 业务逻辑接口
public interface IOperation
{
void operationA();
void operationB();
}
// 业务逻辑的实现
public class OperationImpl implements IOperation
{
@Override
public void operationA()
{
System.out.println("do A...");
}
@Override
public void operationB()
{
System.out.println("do B...");
}
}
// 动态代理实现类
public class OperationHandler implements InvocationHandler
{
// 需要代理的业务逻辑
private OperationImpl operation = null; @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
// 延时加载
if (operation == null)
{
operation = new OperationImpl();
}
// 执行一些自定义的逻辑
System.out.println("before work...");
// 代理执行业务逻辑
method.invoke(operation, args);
// 执行一些自定义的逻辑
System.out.println("after work...");
return null;
}
}
public class Main
{
public static void main(String[] args)
{
// 生成代理对象
IOperation operation = (IOperation)Proxy
.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{IOperation.class}, new OperationHandler());
// 代理执行业务
operation.operationA();
operation.operationB();
}
}
如果需要更好性能的动态代理模式,可以使用第三方库,比如CGLIB、Javassist等
3、享元模式
当系统中存在多个相同的对象,可以优化让大家都是用同一个对象,工厂类中拿着一个对象实例,当需要使用到这个对象时,通过工厂类获取对象实例,不用每次使用都创建一遍对象,从而减少创建开销、GC压力和对象的内存占用。这听起来有点像单例模式和对象池的合体,这与对象池的区别就是:对象池中的每个对象都是等价的,而享元模式中的工厂的对象,则各有各自的用途,如下代码
// 业务逻辑接口
public interface IOperation
{
void doIt();
}
// 业务逻辑的实现
public class OperationImplA implements IOperation
{
private String id = null;
public OperationImplA(String id)
{
this.id = id;
} @Override
public void doIt()
{
System.out.println("do it A...");
}
}
// 业务逻辑的实现
public class OperationImplB implements IOperation
{
private String id = null;
public OperationImplB(String id)
{
this.id = id;
} @Override
public void doIt()
{
System.out.println("do it B...");
}
}
// 享元工厂
public class OperationFactory
{
// 相同ID公用同一个对象
private Map<String, IOperation> operationMapA = new HashMap<>();
private Map<String, IOperation> operationMapB = new HashMap<>(); public IOperation getOperationA(String id)
{
IOperation operation = operationMapA.get(id);
// 延时加载
if (operation == null)
{
operation = new OperationImplA(id);
operationMapA.put(id, operation);
}
return operation;
} public IOperation getOperationB(String id)
{
IOperation operation = operationMapB.get(id);
// 延时加载
if (operation == null)
{
operation = new OperationImplB(id);
operationMapB.put(id, operation);
}
return operation;
}
}
public class Main
{
public static void main(String[] args)
{
OperationFactory operationFactory = new OperationFactory();
IOperation operationA = operationFactory.getOperationA("A");
IOperation operationB = operationFactory.getOperationB("B");
operationA.doIt();
operationB.doIt();
}
}
如上代码看出,每个id会共享一个对象,多次使用时可以复用对象
4、装饰者模式
装饰者模式也是比较常见的模式,他可以将系统中的功能组件进行包装,提升功能增加的能力,对于一些功能组件,我们可以用装饰者模式为其提高性能能力,具有低耦合的特性,最典型的例子就是JDK中的文件操作,平常经常使用FileInputStream来读取文件内容,FileInputStream是基于字节流的输入,不具备缓冲区的功能,JDK就使用装饰者模式将FileInputStream包装增加了缓冲区的性能优化,即BufferedInputStream,这个例子就是增加了功能组件的性能,还有例子比如Collections.synchronizedMap(),这个对HashMap进行了包装,增加了线程安全的特性。下面看一个简易实现代码
// 业务逻辑接口
public interface IOperation
{
void doIt();
}
// 业务逻辑的实现
public class OperationImpl implements IOperation
{
@Override
public void doIt()
{
System.out.println("do it...");
}
}
public abstract class AbsOperation implements IOperation
{
IOperation operation = null;
public AbsOperation(IOperation operation)
{
this.operation = operation;
}
}
public class OperationPlusA extends AbsOperation
{
public OperationPlusA(IOperation operation)
{
super(operation);
} @Override
public void doIt()
{
System.out.println("增强功能A。。。");
operation.doIt();
}
}
public class OperationPlusB extends AbsOperation
{
public OperationPlusB(IOperation operation)
{
super(operation);
} @Override
public void doIt()
{
System.out.println("增强功能B。。。");
operation.doIt();
}
}
public class Main
{
public static void main(String[] args)
{
IOperation operation = new OperationPlusA(new OperationPlusB(new OperationImpl()));
operation.doIt();
}
}
以上代码可以看出,我们对功能组件进行了两次增强,每次增强都是互相独立的,不会互相影响
5、观察者模式
观察者模式对于性能优化主要在“通知”,当程序A依赖于程序B的执行,如果程序A开线程去轮询程序B来监视程序B的状态,那么会增加系统的负担,所以使用观察者模式来优化达成“通知”这个目的,当程序B执行完毕,就会回调程序A的方法,来告诉程序A自己已经执行好了,免去了开线程去监视的负担,JDK已经帮我们提供了观察者模式的接口,下面使用简单的代码去尝试一下观察者模式
// 观察者
public class ObserverA implements Observer
{
@Override
public void update(Observable o, Object arg)
{
// 观察者的通知回调
System.out.println("收到。。。");
}
}
// 被观察者
public class ObservableA extends Observable
{
@Override
protected synchronized void setChanged()
{
// 改变状态方法
super.setChanged();
}
}
public class Main
{
public static void main(String[] args)
{
// 实例化观察者和被观察者
ObserverA observerA = new ObserverA();
ObservableA observableA = new ObservableA(); // 将观察者加入到被观察者的观察列表中
observableA.addObserver(observerA); // 改变状态,然后通知观察者自己改变了
observableA.setChanged();
observableA.notifyObservers();
observableA.setChanged();
observableA.notifyObservers();
}
}
观察者需要重写update()方法来定义被通知时要做的事,而被观察者需要重写setChanged方法来改变自己的状态,使用时,先将观察者注册到被观察者中,需要通知时,要调用setChanged()方法改变状态,再调用notifyObservers()方法通知观察者,如果不改变状态是无法发出通知的
从设计模式的角度看Java程序优化的更多相关文章
- Java 程序优化 (读书笔记)
--From : JAVA程序性能优化 (葛一鸣,清华大学出版社,2012/10第一版) 1. java性能调优概述 1.1 性能概述 程序性能: 执行速度,内存分配,启动时间, 负载承受能力. 性能 ...
- 从JVM的角度看JAVA代码--代码优化
从JVM的角度看JAVA代码–代码优化 从JVM的角度看JAVA代码代码优化 片段一反复计算 片段二反复比較 在JVM载入优化为class文件,运行class文件时,会有JIT(Just-In-Tim ...
- 从字节码的角度看Java内部类与外部类的互相访问
Java中non-static内部类为何可以访问外部类的变量?Java中外部类又为何可以访问内部类的private变量?这两个问题困扰过我一段时间,查了一些网上的答案,大多从“闭包”概念入手,理解起来 ...
- 简单总结:以设计模式的角度总结Java基本IO流
在总结 Java Basic IO 时,发现 java.io 包的相关类真心不少--.看到一堆"排山倒海"般的类,我想,唯有英雄联盟中小炮的台词才能表现此刻我的心情: 跌倒了没?崩 ...
- Java程序优化的一些最佳实践(转)
衡量程序的标准 衡量一个程序是否优质,可以从多个角度进行分析.其中,最常见的衡量标准是程序的时间复杂度.空间复杂度,以及代码的可读性.可扩展性.针对程序的时间复杂度和空间复杂度,想要优化程序代码,需要 ...
- java程序优化
程序代码优化要点: 字符串优化:分析String源码,了解String常用方法,使用StringBuffer.StringBuilder. List.Map.Set优化:分析常用ArrayList.L ...
- 超大数据量操作 java程序优化[转载]
一个表中有1000万以上的数据,要对其进行10万次以上的增删查改的操作,请问如何优化java程序对数据库的操作? 通过使用一些辅助性工具来找到程序中的瓶颈,然后就可以对瓶颈部分的代码进行优化. ...
- JAVA程序优化之字符串优化处理
字符串是软件开发中最为重要的对象之一.通常,字符串对象或其等价对象(如char数组),在内存中总是占据了最大的空间块.因此如何高效地处理字符串,必将是提高系统整体性能的关键所在. 1.String对象 ...
- 从JVM角度看Java多态
首先,明确一下,Java多态的三个必要条件: 1. 继承 2. 子类重写父类方法 3. 父类引用指向子类对象 然后看一个例子 package test.xing; class Father{ prot ...
随机推荐
- 8.3-8.4NOIP模拟题总结
一:成绩 Day1 score=100+100+20 Day2 score=100+30+0 这成绩还是不行啊,仍需继续加油(抱怨一句暴力分有点少#滑稽) 二:题目分析 Day1 T1祖孙询问: 已知 ...
- java编程目录
目录结构 java常用数据类型使用 https://www.cnblogs.com/cerofang/p/10250535.html java中的排序 https://www.cnblogs.com ...
- Matlab调用遗传工具箱复现论文模型求解部分
原文转载自:https://blog.csdn.net/robert_chen1988/article/details/52431594 论文来源: https://www.sciencedirect ...
- python多线程和多进程使用
# 多线程 from concurrent.futures import ThreadPoolExecutor # 多进程 from concurrent.futures import Process ...
- 程序执行流程/布尔类型与布尔:运算猜数字游戏;库的使用:turtle
myPrice = 6 while True: guess = int(input()) if guess > myPrice: print('>') elif guess < my ...
- mvc根据绝对路径下载文件
首先页面需要一个a标签直接指向下载文件的Action并传值:图片地址,以及图片名称(记住要带后缀名的). 然后是Action里面的代码. SiteHelper.DownloadFile(fileUrl ...
- 中文转拼音without CJK
Xamarin写Android程序时,通常要使用按中文首字母分组显示(如通讯录) . 于是需要被迫包含CJK,不过包含后包肯定是会变大的,于是....自己写了一个硬枚举的中文转拼音的类. 原理是这样的 ...
- Zookeeper+Dubbo+SpringMVC环境搭建
项目码云GIT地址:https://gitee.com/xshuai/dubbo/ 开发工具 MyEclipse 10.7 JDK 1.7 容器 Tomcat 8(运行dubbo) zookeeper ...
- 阿里面试题,深入理解Java类加载机制
类的生命周期 包括以下 7 个阶段: 加载(Loading) 验证(Verification) 准备(Preparation) 解析(Resolution) 初始化(Initialization) 使 ...
- [Swift]LeetCode847. 访问所有节点的最短路径 | Shortest Path Visiting All Nodes
An undirected, connected graph of N nodes (labeled 0, 1, 2, ..., N-1) is given as graph. graph.lengt ...