设计模式虚拟机
@@@@@@设计模式 http://www.runoob.com/design-pattern/abstract-factory-pattern.html
创建型模式(5种):工厂方法模式,抽象工厂模式,单例模式,建造者模式,原型模式。
结构型模式(7种):代理模式,外观模式,适配器模式,装饰器模式,桥接模式,享元模式。
行为型模式(11种):中介者模式、责任链模式、观察者模式、策略模式、模板方法模式、迭代子模式、命令模式、备忘录模式、状态模式、访问者模式、解释器模式。
工厂相关模式
下面例子中鼠标,键盘,耳麦为产品,惠普,戴尔为工厂。
****简单工厂模式不是 23 种里的一种,简而言之,就是有一个专门生产某个产品的类。
比如下图中的鼠标工厂,专业生产鼠标,给参数 0,生产戴尔鼠标,给参数 1,生产惠普鼠标。
****工厂模式也就是鼠标工厂是个父类,有生产鼠标这个接口。
戴尔鼠标工厂,惠普鼠标工厂继承它,可以分别生产戴尔鼠标,惠普鼠标。
生产哪种鼠标不再由参数决定,而是创建鼠标工厂时,由戴尔鼠标工厂创建。
后续直接调用鼠标工厂.生产鼠标()即可
简单工厂:各产品继承一个父类,不同入参获取不同产品,父类变量接收
工厂方法:各制造类(具体工厂)继承父制造类(抽象工厂),制造不同的产品(各产品继承一个父类,父类变量接收)
当有新产品时,只要创建并继承抽象产品;新建具体工厂继承抽象工厂;而不用修改任何一个类,工厂方法模式是完全符合开闭原则的
抽象工厂:
制造类制造的产品(宝马、奔驰、奥迪等不同产品树)有些共同特性的比如(宝马跑车,奔驰跑车)
抽象工厂抽象类包含三种产品制造的方法,抽象工厂实现类是某一产品族的实现,三个方法都实现(跑车实现类,商务车实现类)
问题:产品族的实现类会比较多,用一个反射类来代替所有实现类,不管是奔驰的商务车、跑车还是宝马的商务车、跑车,通过传入类路径用反射实例化
https://www.cnblogs.com/gclokok/p/10029088.html https://blog.andyqiao.top/article/12/ 简单工厂优点: 1.很明显,简单工厂的特点就是“简单粗暴”,通过一个含参的工厂方法,我们可以实例化任何产品类,上至飞机火箭,下至土豆面条,无所不能。所以简单工厂有一个别名:上帝类。 简单工厂缺点: 1.任何“东西”的子类都可以被生产,负担太重。当所要生产产品种类非常多时,工厂方法的代码量可能会很庞大。 由于工厂类集中了所有实例的创建逻辑,违反了高内聚责任分配原则,将全部创建逻辑集中到了一个工厂类中 2.在遵循开闭原则(对拓展开放,对修改关闭)的条件下,简单工厂对于增加新的产品,无能为力。因为增加新产品只能通过修改工厂方法来实现扩展复杂,当简单工厂需要生产出另外一种产品的时候,
需要扩展工厂的内部创建逻辑,比较有可能引起较大的故障 工厂方法:(对简单工厂拆分,工厂方法针对每一种产品提供一个工厂类。通过不同的工厂实例来创建不同的产品实例。) 工厂方法的定义是:定义一个创建对象的接口,让实现s这个接口的的类去决定实例化具体的类。工厂方法让类的实例化推迟到实现接口的子类中进行。 工厂方法优点: 工厂方法模式就很好的减轻了工厂类的负担,把某一类/某一种东西交由一个工厂生产;(对应简单工厂的缺点1) 同时增加某一类”东西“并不需要修改工厂类,只需要添加生产这类”东西“的工厂即可,使得工厂类符合开放-封闭原则。通过增加具体的工厂来扩展,而不是像简单工厂那样修改工厂类 工厂方法缺点: 相比简单工厂,实现略复杂。 对于某些可以形成产品族的情况处理比较复杂。 他们不仅仅生产手机,还生产电脑,耳机等一系类产品,那么我们把苹果,小米,华为这样的厂商可以认为他们生产的是一个产品族 不管是小米华为还是苹果,他们生产的产品是按照一定的规则来生产,显示屏,电池,处理器等等,所以对于纵向的产品来说,他们又是属于同一个产品等级,每个厂商都生产电脑,电脑是一个等级 汽车可以分为轿车、SUV、MPV等,也分为奔驰、宝马等。我们可以将奔驰的所有车看作是一个产品族,而将宝马的所有车看作是另一个产品族 抽象工厂优点:针对产品族场景,实现比较工整; 抽象工厂缺点:比较复杂,只适用于产品族场景
产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中
,海尔电冰箱位于电冰箱产品等级结构中,海尔电视机、海尔电冰箱构成了一个产品族
工厂方法也就是抽象工厂产品族为一个产品的情况
策略模式:
工厂模式实例化一个产品的操作是在服务端来做的,换句话说客户端传达给服务端的只是某种标识,服务端根据该标识实例化一个对象。而策略模式的客户端传达给服务端的是一个实例,服务端只是将该实例拿过去在服务端的环境里执行该实例的方法
例如:各制造类(具体工厂)继承父制造类(抽象工厂)通过传入不同的制造类实例(父类接收)
制造不同的产品(各产品继承一个父类,父类变量接收)
当有新产品时,只要创建并继承抽象产品;新建具体工厂继承抽象工厂;而不用修改任何一个类,工厂方法模式是完全符合开闭原则的
设计模式有很多种,其中功能相似的很多,但是为什么还要分这么多种名字,查阅资料,我觉得下面的解释最为合理:用途不一样,名字就有区别,一把斧头用来砍人就叫凶器,用来砍柴就叫伐木斧,用来劈门就叫消防斧,这些模式的名字都是根据具体使用时的场景,联系了现实里某样东西或某种习惯而取得,所以很相似的模式行为有不同叫法。 今天我们就来研究一些工厂模式与策略模式的一些区别: 工厂模式是创建型模式,适应对象的变化。 策略模式是行为性模式,适应行为的变化
建造者模式:建造模式利用一个导演者对象和具体建造者对象一个个地建造出所有的零件,从而建造出完整的产品对象
Builder接口,其实现类具体实现产品的各个部件如何制作,Director类传入Builder接口,调用各个部件的制作方法
单例模式 volatile 饱汗模式
Facade外观模式:
客户端只需要跟Facade类交互,不需要亲自调用子系统中的A、B、C模块了,也不需要知道系统内部的实现细节,甚至都不需要知道A、B、C模块的存在,
从而更好地实现了客户端和子系统中A、B、C模块的解耦,让客户端更容易地使用系统
● 松散耦合
门面模式松散了客户端与子系统的耦合关系,让子系统内部的模块能更容易扩展和维护。
● 简单易用
门面模式让子系统更加易用,客户端不再需要了解子系统内部的实现,也不需要跟众多子系统内部的模块进行交互,只需要跟门面类交互就可以了。
● 更好的划分访问层次
通过合理使用Facade,可以帮助我们更好地划分访问的层次。有些方法是对系统外的,有些方法是系统内部使用的。把需要暴露给外部的功能集中到门面中,这样既方便客户端使用,也很好地隐藏了内部的细节。
代理模式:
jdk动态代理,实现aop切面编程 InvocationHandler
适配器模式:
类的适配器模式把适配的类的API转换成为目标类的API。客户端需要方法1,2 源类只有1 适配器类继承源类并提供方法2
对象的适配器,为使客户端能够使用Adaptee类,需要提供一个包装(Wrapper)类Adapter。这个包装类包装了一个Adaptee的实例,从而此包装类能够把Adaptee的API与Target类的API衔接起来。Adapter与Adaptee是委派关系,这决定了适配器模式是对象的。
适配器把源类的对象(或子类)内嵌到适配器中,进而提供源类的已有方法
组合模式:将对象组合成树形结构以表示‘部分-整体’的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性
当发现需求中是体现部分与整体层次结构时,以及你希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时,就应该考虑组合模式了
总店、分店、加盟店(叶子节点)总店消费的积分累加到其下各店,分别继承主类,总店、分店一个类,加盟店一个类,
因为前者计算积分时要遍历子节点,后者没有子节点不需遍历
中介者模式
减小了系统的耦合。一个好的设计,必定不会把所有的对象关系处理逻辑封装在本类中,而是使用一个专门的类来管理那些不属于自己的行为
类A类B互相影响,就称为同事类,A中的操作用到B、B中的操作用到A
引入中介者后,只与中介者交互 中介者包含A、B实例
观察者模式
被观察者 有一个List集合,用以保存注册的观察者,等需要通知观察者时,遍历该集合即可
观察者,微信公众号的具体观察者为用户User
首先注册了三个用户,ZhangSan、LiSi、WangWu。公众号发布了一条消息"PHP是世界上最好用的语言!",三个用户都收到了消息。触发了三个用户的某个方法,
用户ZhangSan看到消息后颇为震惊,果断取消订阅,这时公众号又推送了一条消息,此时用户ZhangSan已经收不到消息,其他用户
还是正常能收到推送消息
[1]创建被观察者类,它继承自java.util.Observable类;
[2]创建观察者类,它实现java.util.Observer接口;
责任链:
为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦,在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
不同的数据由不同的接受者处理,或者满足某个接收者的条件就由其处理
责任链模式
客户端创建了两个处理者对象,并指定第一个处理者对象的下家是第二个处理者对象,而第二个处理者对象没有下家。然后客户端将请求传递给第一个处理者对象。
由于本示例的传递逻辑非常简单:只要有下家,就传给下家处理;如果没有下家,就自行处理
封装:
1.定义:隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别。
2.封装的目的是:增强安全性和简化编程,使用者不必了解具体的实现细节,而只是要通过外部接口,一特定的访问权限来使用类的成员。
继承:
1.目的:实现代码的复用
包外子类可访问父类protected修饰的类成员
多态:
继承、接口实现 重载:同一个类中,相同的方法名对应着不同的方法实现,但是方法的参数不同
抽象类不能被实例化,但是可以定义一个抽象类的对象变量,这个变量可以引用非抽象子类的对
抽象类和子类具有父子关系,子类能拥有父类中一些属性。接口 虽然某个类实现一个接口,但是由于接口中的变量都为静态常量,不存在继承关系
@@@@@@虚拟机
java类的加载过程
加载:根据全量名获取二进制流,在内存中生成class对象
验证:是否继承了不被继承的类,方法体中类型转换是否异常
准备:静态变量分配内存并将其初始化为默认值,准备阶段不分配类中的实例变量的内存,实例变量将会在对象实例化时随着对象一起分配在Java堆中
解析、初始化
内存模型
线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化
java垃圾回收
在java中,程序员是不需要显示的去释放一个对象的内存的,而是由虚拟机自行执行。在JVM中,有一个垃圾回收线程,它是低优先级的,在正常情况下是不会执行的,只有在虚拟机空闲或者当前堆内存不足时,才会触发执行,扫面那些没有被任何引用的对象,并将它们添加到要回收的集合中,进行回收。
类加载器
启动类加载器(Bootstrap ClassLoader)用来加载java核心类库,无法被java程序直接引用。
扩展类加载器
系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。
****内存屏障
volatile关键字通过提供“内存屏障”的方式来防止指令被重排序,为了实现volatile的内存语义,编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。大多数的处理器都支持内存屏障的指令, 写操作前后、读操作后
java内存分区
****方法区
主要用来存储已被虚拟机加载的类的信息、常量、静态变量和即时编译器编译后的代码等数据。
****堆
java堆是所有线程所共享的一块内存,在虚拟机启动时创建,几乎所有的对象实例都在这里创建,因此该区域经常发生垃圾回收操作。
****虚拟机栈
1. 虚拟机栈也就是我们平常所称的栈内存,它为java方法服务,每个方法在执行的时候都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接和方法出口等信息。
2. 虚拟机栈是线程私有的,它的生命周期与线程相同。
-Xms java 虚拟机初始化时的堆最小内存;
-Xmx java 虚拟机可使用堆的最大内存;
-XX: PermSize 非堆内存永久保留区域
-XX:MaxPermSize 非堆内存最大永久保留区域
现公司服务器内存一般都可以加到最大2G ,所以可以采取以下配置:
JAVA_OPTS=’-Xms1024m -Xmx2048m -XX: PermSize=256M -XX:MaxNewSize=256m -XX:MaxPermSize=256m
堆的分区
堆被划分为新生代和旧生代,新生代又被进一步划分为Eden和Survivor区,最后Survivor由FromSpace和ToSpace组成。
新生代。新建的对象都是用新生代分配内存,Eden空间不足的时候,会把存活的对象转移到Survivor中,新生代
大小可以由-Xmn来控制,也可以用-XX:SurvivorRatio来控制Eden和Survivor的比例
旧生代。用于存放新生代中经过多次垃圾回收仍然存活的对象
垃圾回收算法 https://blog.csdn.net/yrwan95/article/details/82829186
1)复制算法(新生代)
将内存分为(大小相等)两部分,每次只使用其中一块进行内存分配,当内存使用完后,就出发GC,将存活的对象直接复制到另一块空闲的内存中,然后对当前使用的内存块一次性清除所有,然后转到另一块内存进行使用。GC复制算法只搜索并复制活动对象,所以跟一般的GC标记-清除算法相比,它能在短时间内完成GC,也就是说其吞吐量优秀
优点:简单,高效。解决内存碎片
缺点:浪费内存,因为每次都有另一块内存空闲着。如果存活对象很多,那么Copying算法的效率将会大大降低
2)标记-清除算法
分两步进行,第一步标记出可以回收的对象,第二步统一清理可以回收的对象内存。
缺点:首先标记和清除步骤效率都不高,其次会产生内存碎片。大对象分配空间时无法找到足够的空间而提前触发新的一次垃圾收集动作
3)标记-整理算法(老年代)
类似于标记-清除算法,但是第二步进行内存回收时,将存活的对象向内存一端移动,达到消除内存碎片问题。
4)分代收集算法
java sun hotspot虚拟机将内存分为新生代(堆)、老年代(堆)、永久代(方法区、常量池、即时编译代码)几个区域,新生代主要使用基于复制算法的垃圾回收,老年代和永久代主要使用标记-整理算法进行垃圾回收。具体每个区域使用哪种垃圾回收算法还要视收集器的实现制约
对象如何进入老年代
一般而言对象首次创建会被放置在新生代的eden区,如果没有GC介入,则对象不会离开eden区,那么eden区的对象如何进入老年代那呢?一般来说只要对象的年龄达到一定的大小,就会自动离开年轻代进入老年代,对象年龄是由对象经历数次GC决定的,在新生代每次GC后,如果没有被回收则年龄加1.虚拟机提供一个参数来控制新生代对象的最大年龄,当超过这个年龄范围就会晋升老年代
****强引用 Object o=new Object();//强引用 o=null;// 帮助垃圾收集器回收此对象
在一个方法的内部有一个强引用,这个引用保存在栈中,而真正的引用内容(Object)保存在堆中。当这个方法运行完成后就会退出方法栈,则引用内容的引用不存在,这个Object会被回收。
但是如果这个o是全局的变量时,就需要在不用这个对象时赋值为null,因为强引用不会被垃圾回收
****软引用
Browser prev = new Browser(); // 获取页面进行浏览
SoftReference sr = new SoftReference(prev); // 浏览完毕后置为软引用
if(sr.get()!=null){
rev = (Browser) sr.get(); // 还没有被回收器回收,直接获取
}else{
prev = new Browser(); // 由于内存吃紧,所以对软引用的对象回收了
sr = new SoftReference(prev); // 重新构建
}
****弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象
String abc = abcWeakRef.get(); //再次变为一个强引用 WeakReference<String> sr = new WeakReference<String>(new String("hello"));
****虚引用顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中
反射
能够使我们很方便的创建灵活的代码,这些代码可以再运行时装配,无需在组件之间进行源代码链接,灵活加载类并执行其方法。
灵活的传入方法名执行
潜复制 implements Cloneable
我们发现Student的字段如果不是一个引用时,修改clone()得到对象的该字段(name, age)时并不会影响原来的对象,但是当字段为一个引用时,修改clone()得到对象的该字段(professor)时并会影响原来的对象。上面实现的clone()方法为浅复制(shadow copy)。
深复制
在实现clone方法时 把professor也复制一份给新的主对象

浙公网安备 33010602011771号