java设计模式及实践豆瓣,java开发中用到了哪些设计模式
本篇文章给大家带来了关于爪哇岛的相关知识,其中主要介绍了关于与性能相关的设计模式,大多数设计模式只是代码的一种组织方式,只有部分设计模式与性能相关,包括代理模式、单例模式、享元模式、原型模式等,下面一起来看一下,希望对大家有帮助。
写爬虫互联网协议(互联网协议)被封了怎么解决?立即使用
代码的结构对应用的整体性能,有着重要的影响。结构优秀的代码,可以避免很多潜在的性能问题,在代码的扩展性上也有巨大的作用;结构清晰、层次分明的代码,也有助于帮你找到系统的瓶颈点,进行专项优化。
设计模式就是对常用开发技巧进行的总结,它使得程序员之间交流问题,有了更专业、便捷的方式。
事实上,大多数设计模式并不能增加程序的性能,它只是代码的一种组织方式。本文,我们将一一举例讲解和性能相关的几个设计模式,包括代理模式、单例模式、享元模式、原型模式等。
代理模式
代理模式(代理)可以通过一个代理类,来控制对一个对象的访问。
爪哇中实现动态代理主要有两种模式:一种是使用JDK,另外一种是使用CGLib。其中,JDK方式是面向接口的,主要的相关类是动态代理和代理人;字节码生成可以代理普通类,主要的相关类是环绕通知和增强剂。
这个知识点面试频率非常高。
CGLib
包cn。wja。代理。cglibproxy导入org。spring框架。cglib。代理。方法拦截器;导入org。spring框架。cglib。代理。方法代理;导入Java。郎。反思。方法;公共类CglibInterceptor实现MethodInterceptor {
@覆盖
公共对象拦截(对象哦,方法方法,对象[]对象,方法代理方法代理)抛出可投掷的
返回methodProxy.invokeSuper(o,objects);
} }包cn。wja。代理。cglibproxy导入cn。wja。代理。JDK代理。目标;导入cn。wja。代理。JDK代理。目标impl导入org。spring框架。cglib。代理。增强剂;公共类CglibFactory {
公共静态目标newInstance() {
增强剂增强剂=新增强子();
增强剂。设置超类(目标实现。类);
增强剂。设置回调(new CglibInterceptor());
返回(目标)增强剂。create();
}
公共静态void main(String[] args) {
target=new instance();
系统。出去。println(目标。目标metod(4));
}}
JDK
包cn。wja。代理。JDK代理;公共接口目标{
int target方法(int I);}包cn。wja。代理。JDK代理;公共类TargetImpl实现目标{
@覆盖
public int targetMethod(int i) {
返回我*我
} }包cn。wja。代理。JDK代理;导入Java。郎。反思。调用处理程序;导入Java。郎。反思。方法;公共类JdkInvocationHandler实现InvocationHandler {
私有目标目标;
公共JdkInvocationHandler(目标目标){
目标=目标
}
@覆盖
公共对象调用(对象代理、方法方法、对象[]参数)抛出可投掷的
//之前
object对象=方法。invoke(target,args);
//之后
返回对象;
} }包cn。wja。代理。JDK代理;导入Java。郎。反思。代理人;公共类JDK工厂{
公共静态目标newInstance(目标target) {
对象对象=代理。newproxyinstance(jdkinvocationhandler。班级。获取类加载器(),
新班级?[]{Target.class},
new JdkInvocationHandler(target));
返回目标。班级。cast(对象);
}
公共静态void main(String[] args) {
target t=新目标impl();
target=新实例(t);
系统。出去。println(目标。目标方法(4));
}}下面是爪哇岛开发工具包方式和字节码生成方式代理速度的怡和控股测试结果:
我在
Benchmark | Mode | Cnt | Score | Error | Units |
---|---|---|---|---|---|
ProxyBenchmark.cglib | thrpt | 10 | 78499.580 | 1771.148 | ops/ms |
ProxyBenchmark.jdk | thrpt | 10 | 88948.858 | 814.360 | ops/ms |
我们来看看代理的创建速度,结果如下。可以看到,在代理类初始化方面,JDK的吞吐量是CGLib的两倍。
Ken1@166.com:
Spring动态代理
Spring广泛使用代理模式,用CGLIB增强Java字节码。在复杂的项目中,会有大量的AOP代码,比如权限、日志等方面。AOP在方便编码的同时,也给不熟悉项目代码的学生带来了很多麻烦。
下面,我就来分析一个用阿尔萨斯找动态代理人逻辑慢的具体原因。这种方法在复杂的项目中非常有效,不需要熟悉项目的代码就可以定位性能瓶颈。
首先,我们创建最简单的Bean。
包cn . wja . spring;导入org . spring framework . stereotype . component;@ Componentpublic class ABean {
公共void方法(){
System.out.println(****ABean方法* * * * * * * * * * * * * * * * *);
}}然后,我们用方面标注来完成小节的编写。在前面的方法中,我们让线程休眠1秒钟。
包cn . wja . spring;导入org . AspectJ . lang . annotation . aspect;导入org . AspectJ . lang . annotation . before;导入org . AspectJ . lang . annotation . pointcut;导入org . spring framework . stereotype . component;导入Java . util . concurrent . time unit;@ Aspect @ component public class my Aspect {
@ Pointcut( execution(* cn . wja . spring . a bean . *(.))))
公共void切入点(){
}
@Before(pointcut())
之前的公共void
system . out . println( before );
尝试{
Thread.sleep(时间单位。seconds . tomillis(1));
} catch (InterruptedException e) {
抛出新的IllegalStateException();
}
}}创建一个启动类,当你访问/aop链接时,会输出Bean的类名及其耗时。
包cn . wja . spring;导入org . spring framework . beans . factory . annotation . auto wired;导入org . spring framework . boot . spring application;导入org . spring framework . boot . auto configure . spring boot application;导入org . spring framework . scheduling . annotation . enable async;导入org . spring framework . stereotype . controller;导入org . spring framework . web . bind . annotation . get mapping;导入org . spring framework . web . bind . annotation . response body;@ spring boot application @ enable async @ controller public class App {
公共静态void main(String[] args) {
spring application . run(app . class,args);
}
@自动连线
私有的。
@ResponseBody
@GetMapping(/aop )
公共字符串aop() {
long begin=system . current time millis();
a bean . method();
long cost=system . current time millis()-begin;
String cls=aBean.getClass()。toString();
返回cls“”开销;
}}}访问结果如下。可以看到AOP代理已经生效,内存中的Bean对象变成了EnhancerBySpringCGLIB类型。调用方法method需要1005ms。
下面用arthas来分析这个执行过程,找出耗时最多的AOP方法。启动arthas后,您可以从列表中看到我们的应用程序。这里输入1进入分析界面。
在终端输入trace命令,然后访问/aop接口,终端会打印出一些调试信息,可以发现耗时的操作是Spring的代理类。
trace cn.wja.spring.ABean方法
单例模式
当Spring创建一个组件时,可以通过scope注释来指定它的作用域,scope注释用来指明它是原型(多实例)还是单例(单实例)。
当指定为singleton(默认行为)时,Spring容器中只有一个组件,当注入相关组件时,获得的组件实例也是同一个。
如果是普通的singleton类,我们通常会将singleton的构造方法设置为private,singleton有懒加载模式和饿加载模式。
饿汉模式
了解JVM类加载机制的同学都知道,一个类从加载到初始化要经历五个步骤:加载、验证、准备、解析、初始化。
其中,属于类的静态字段和静态代码块已经在类加载的初始化阶段执行。它对应于字节码中的方法,属于类(构造方法)。因为该类只初始化一次,所以它可以保证这个加载操作是线程安全的。
根据上述原理,只要在方法中放入单个实例的初始化动作,就可以实现饥饿中文模式。
private static Singleton instance=new Singleton();理论上来说,饿汉模式会浪费资源,可能会生成一些永远用不到的对象,所以很多教程不推荐。但其实这个的本质就是脱裤子放屁。如果你真的从来不需要这个对象,为什么要创建这个类,写一个singleton模式?我觉得饿了么-韩的模式对于普通项目完全够用。
饱汉模式
和对象初始化不同。通常,当我们创建一个新对象时,我们调用它的构造函数,用来初始化对象的属性。因为多线程可以同时调用函数,所以我们需要使用synchronized关键字来同步生成过程。
包cn . wja . singleton;公共类DoubleCheckSingleton {
private volatile static DoubleCheckSingleton实例=null
private DoubleCheckSingleton() {
}
公共静态DoubleCheckSingleton getInstance(){
if (null==instance) {
synchronized(doublechecksingleton . class){
if (null==instance) {
instance=new DoubleCheckSingleton();
}
}
}
返回实例;
}}如果以上是双检的关键代码,那我们来介绍四个关键点:
第一次,当实例为空时,进入对象实例化逻辑,否则,直接返回。同步锁,这里是一个类锁。第二次检查是关键。如果不添加这个空判断操作,多个线程可能会进入同步代码块,从而生成多个实例。最后一个关键点是volatile关键字。在一些较低版本的Java中,由于指令重排的原因,singleton在被new释放后,在构造函数可以执行之前,可能会被其他线程使用。这个关键字可以防止字节码指令的重新排序。写双重校验码的时候,习惯上加volatile。如你所见,双支票的书写比较复杂,需要注意的点也很多。现在其实已经是反模式了,不再推荐。我也不建议你在自己的代码中使用。但能考察面试官对并发的理解,所以经常问这个问题。
推荐一个使用enum的懒加载的例子,书《Effective Java》也推荐这个方法。代码片段如下:
包cn . wja . singleton;公共类EnumSingleton {
private EnumSingleton() {
}
公共静态EnumSingleton getInstance() {
返回支架。HOLDER.instance
}
私有枚举持有者{
持有人;
私有final EnumSingleton实例;
持有者(){
instance=new EnumSingleton();
}
}
公共静态void main(String[] args) {
system . out . println(getInstance());
}}如果想用spring框架,那就更简单了:
包cn . wja . singleton;导入org . spring framework . context . annotation . scope;导入org . spring framework . stereotype . component;@Component@Scope(singleton )公共类SpringBean {
//具体内容}
享元模式
Flyweight是一种专门针对性能优化的设计模式,通过共享技术最大限度的重用对象。元模式一般通过唯一的识别码来判断,然后返回对应的对象。将它们存储在HashMap这样的集合中是非常合适的。
上面的描述我们非常熟悉,因为在本专栏之前的博文中,我们可以看到很多元模式,比如说在Java pool技术中谈论池化对象的博文,以及在Java大型对象中博文如何处理对象重用的博文。
案例:Integer
在Java中,我们常见的整数,为了提高效率,在[1,127]范围内创建对象时也使用元共享模式。这可以通过下面的测试代码来验证。
@Testpublic void myTest()抛出异常{
整数a=1;
整数b=1;
System.out.println(a==b?A b同一个对象“)‘a b不是同一个对象’);
整数c=128
整数d=128
System.out.println(c==d?“C d相同对象”:“c d不是相同对象”);}
多视角看问题
设计模式抽象了我们通常的编码。如果你从不同的角度来解释设计模式,你会发现一些设计思想的共同点。例如,singleton模式是元共享模式的特例,它通过共享单个实例来实现对象的重用。
值得一提的是,同样的代码,不同的解读,会产生不同的效果。例如,下面的代码:
MapString,Strategy s=new HashMap();strategys.put(a ,new a strategy());strategys.put(b ,new BStrategy());从对象复用的角度来看,是元共享模式;如果我们看一个对象的功能,它是一个策略模式。因此,在讨论设计模式时,我们必须注意上下文中的这些差异。
原型模式
原型类似于复制粘贴的思路。它可以先创建一个实例,然后通过这个实例创建一个新对象。在Java中,最典型的例子就是Object类的clone方法。
但是,这种方法在编码中很少使用。上面提到的代理模式的原型并不是通过克隆实现的,而是使用了更复杂的反射技术。
更重要的一个原因是,如果clone只复制当前级别的对象,那么只会实现浅层复制。现实中,对象往往非常复杂。如果要实现深度复制,需要在clone方法中做大量的编码,远不如调用new方法方便。
要实现深度复制,还有序列化等手段,比如实现可序列化接口或者将对象转换成JSON。
因此,在现实中,原型模式成为一种思想,而不是一种加快创建对象的工具。
推荐:《java视频教程》以上是讲Java中与性能相关的设计模式细节。更多请关注我们的其他相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。