一、前言

  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程序优化的更多相关文章

  1. Java 程序优化 (读书笔记)

    --From : JAVA程序性能优化 (葛一鸣,清华大学出版社,2012/10第一版) 1. java性能调优概述 1.1 性能概述 程序性能: 执行速度,内存分配,启动时间, 负载承受能力. 性能 ...

  2. 从JVM的角度看JAVA代码--代码优化

    从JVM的角度看JAVA代码–代码优化 从JVM的角度看JAVA代码代码优化 片段一反复计算 片段二反复比較 在JVM载入优化为class文件,运行class文件时,会有JIT(Just-In-Tim ...

  3. 从字节码的角度看Java内部类与外部类的互相访问

    Java中non-static内部类为何可以访问外部类的变量?Java中外部类又为何可以访问内部类的private变量?这两个问题困扰过我一段时间,查了一些网上的答案,大多从“闭包”概念入手,理解起来 ...

  4. 简单总结:以设计模式的角度总结Java基本IO流

    在总结 Java Basic IO 时,发现 java.io 包的相关类真心不少--.看到一堆"排山倒海"般的类,我想,唯有英雄联盟中小炮的台词才能表现此刻我的心情: 跌倒了没?崩 ...

  5. Java程序优化的一些最佳实践(转)

    衡量程序的标准 衡量一个程序是否优质,可以从多个角度进行分析.其中,最常见的衡量标准是程序的时间复杂度.空间复杂度,以及代码的可读性.可扩展性.针对程序的时间复杂度和空间复杂度,想要优化程序代码,需要 ...

  6. java程序优化

    程序代码优化要点: 字符串优化:分析String源码,了解String常用方法,使用StringBuffer.StringBuilder. List.Map.Set优化:分析常用ArrayList.L ...

  7. 超大数据量操作 java程序优化[转载]

        一个表中有1000万以上的数据,要对其进行10万次以上的增删查改的操作,请问如何优化java程序对数据库的操作? 通过使用一些辅助性工具来找到程序中的瓶颈,然后就可以对瓶颈部分的代码进行优化. ...

  8. JAVA程序优化之字符串优化处理

    字符串是软件开发中最为重要的对象之一.通常,字符串对象或其等价对象(如char数组),在内存中总是占据了最大的空间块.因此如何高效地处理字符串,必将是提高系统整体性能的关键所在. 1.String对象 ...

  9. 从JVM角度看Java多态

    首先,明确一下,Java多态的三个必要条件: 1. 继承 2. 子类重写父类方法 3. 父类引用指向子类对象 然后看一个例子 package test.xing; class Father{ prot ...

随机推荐

  1. Unity-NGUI UILabel换行

    表里填写的是"\n",代码读出来会变成"\\n", 那就需要代码中将 "\\n" 重新变成 "\n" 才能够换行. st ...

  2. 分布式、集群、微服务、SOA 之间的区别

    分布式:不同模块部署在不同服务器上 作用:分布式解决网站高并发带来问题 集群:多台服务器部署相同应用构成一个集群 作用:通过负载均衡设备共同对外提供服务 SOA:Service Oriented Ar ...

  3. Tensor索引操作

    #Tensor索引操作 ''''' Tensor支持与numpy.ndarray类似的索引操作,语法上也类似 如无特殊说明,索引出来的结果与原tensor共享内存,即修改一个,另一个会跟着修改 ''' ...

  4. 理解pytorch中的softmax中的dim参数

    import torch import torch.nn.functional as F x1= torch.Tensor( [ [1,2,3,4],[1,3,4,5],[3,4,5,6]]) y11 ...

  5. JS中[object object]怎么取值

    错误信息:本来是要显示JSON对象的  结果控制台打印了[object object] 需要做一个简单的转换,如下: var jsonData = JSON.stringify(data);// 转成 ...

  6. wait event & wake up

    在linux驱动中一个常用的场景, 驱动需要等待中断的响应, 才得以执行后续的代码,达到一个原子操作的目的 /* 静态申请队列 */ static DECLARE_WAIT_QUEUE_HEAD(s_ ...

  7. gridlayout代码注释

    <div class="wrapper"> //定义一节或者一部分区域,它的css样式对应的css中class选择器的wrapper <div class=&qu ...

  8. AtiveMQ初次连接的 http error:503 连接错误 Prolem accessing /.Reason : Service Unavailable

    503错误 说明是服务器内部的错误了 这是 为什么嫩  这是因为你的Linux虚拟机(我用的是centos版本的)的机器名 和 你的 ip地址的映射关系 不一致  导致访问失败 查看机器名的地址:ca ...

  9. 下载 mysql 数据库 的步骤 完整版

    1. 官网(点这里)上下载 2. 3. 4. 5. 6. 7.

  10. python从入门到实践-9章类

    #!/user/bin/env python# -*- coding:utf-8 -*- # 类名采用的是驼峰命名法,即将类名中每个单词的首字母大写,而不使用下划线.# 对于每个类,都应紧跟在类定义后 ...