加油站:从实践中总结出战略性的设计思想,再把这种思想应用到各个领域实践中,使到实践产生质的飞跃,起到领军带头作用.
前言:
在看享元设计模式时,发现是一种降低程序运行代价,提高系统系能的一种设计模式思想理论,首先先介绍下理论,然后再举例java语言中针对于享元模式的设计思想详解-String常量池.
官方术语解说:
运用共享技术有效的支持大量细粒度的对象。
中文名: 享元模式(蝇量模式)
性 质: 软件设计模式-结构型模式
享元模式: 共享技术的支持大量细粒度的对象
外文名: Flyweight Pattern技术支持: 共享技术
是一种软件设计模式,使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件;
它适合用于只是因重复而导致使用无法令人接受的大量内存的大量物件。
通常物件中的部分状态是可以分享。
常见做法是把它们放在外部数据结构,当需要使用时再将它们传递给享元。
UML结构图:
抽象享元角色:为具体享元角色规定了必须实现的方法,而外蕴状态就是以参数的形式通过此方法传入。在Java中可以由抽象类、接口来担当。
具体享元角色:实现抽象角色规定的方法。如果存在内蕴状态,就负责为内蕴状态提供存储空间。
享元工厂角色:负责创建和管理享元角色。要想达到共享的目的,这个角色的实现是关键!
客户端角色:维护对所有享元对象的引用,而且还需要存储对应的外蕴状态。
经典场景:
享元模式经典的应用场景就是池的技术了,
如:String常量池、数据库连接池、线程池、缓冲池...都是享元模式的应用,享元模式是池技术的重要实现方式。
个人应用理解:
一个工厂类(享元工厂:构建一个池容器).
调用时,要使用共享对象时,从工厂获取.
复用机制,工厂有个复用对象的池子,池子中有富裕的对象时直接返回该对象,没有则创建。(享元模式在结构模式上类似于单例模式和简单工厂模式的结合)
事例分析问题:
首先为什么说享元模式很重要?
享元模式可以替代哪些地方?
为什么面试都会问到String源码?
面试人员问String他们到底想听什么?
String常量池中享元模式的体现在哪?
带着几个问题,我们来详解String 常量池,从享元模式角度来在看String:
实例分析一:
String对象是final类型,对象一旦创建就不可改变。在JAVA中字符串常量都是存在常量池中的,JAVA会确保一个字符串常量在常量池中只有一个拷贝。String a="abc",其中"abc"就是一个字符串常量。
看个大家最熟悉的例子:
/**
* @author 方健* @desc 微信公众号:十点攀程*/public class Main {public static void main(String[] args) {
String a = "hello";String b = "hello";System.out.println(a == b); //true}
}
分析原理:
输出结果是:true
首先可以看出if条件比较的是两a和b的地址。
可以说是内存空间中可以共享的对象,返回的同一类型的对象其实是同一实例,当客户端要求生成一个对象时,工厂会检测是否存在此对象的实例,如果存在那么直接返回此对象实例,如果不存在就创建一个并保存起来,这点有些单例模式的意思。
通常工厂类会有一个集合类型的成员变量来用以保存对象.
如hashtable,hashmap,vector等。在java中,数据库连接池,线程池等即是用享元模式的应用。
实例分析二:
1. 首先String不属于8种基本数据类型,String是一个对象。
因为对象的默认值是null,所以String的默认值也是null;但它又是一种特殊的对象,有其它对象没有的一些特性。
2. new String()和new String(“”)都是声明一个新的空字符串,是空串""不是null;
3. String str="kvill";
String str=new String (“kvill”);的区别:
在这里,我们不谈堆,也不谈栈,只先简单引入常量池这个简单的概念。常量池(constant pool)指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。它包括了关于类、方法、接口等中的常量,也包括字符串常量。
再看个例子:
/**
* @author 方健* @desc 微信公众号:十点攀程*/public class Main {public static void Main(String[] args) {
String s0 = "come on";String s1 = "come on";String s2 = "come " + "on";System.out.println(s0 == s1);//trueSystem.out.println(s0 == s2);//true}
}
分析原理:
首先,我们要知结果为道Java会确保一个字符串常量只有一个拷贝。
因为例子中的s0和s1中的"come on"都是字符串常量,它们在编译期就被确定了,所以s0==s1为true;而"come "和"on"也都是字符串常量,当一个字符串由多个字符串常量连接而成时,它自己肯定也是字符串常量,所以s2也同样在编译期就被解析为一个字符串常量,所以s2也是常量池中"come on"的一个引用。
所以我们得出s0==s1==s2;
实例分析三:
用new String()创建的字符串不是常量,不能在编译期就确定,所以new String() 创建的字符串不放入常量池中,它们有自己的地址空间。看个例子:
/**
* @author 方健* @desc 微信公众号:十点攀程*/public class main {public static void ain(String[] args) {
String s0 = "come on";String s1 = new String("come on");String s2 = "come " + new String("on");System.out.println(s0 == s1);//falseSystem.out.println(s0 == s2);//falseSystem.out.println(s1 == s2);//false}
}
分析原理:
首先以上代码运行结果其实就是对实例分析三的一个结果验证,即:用new String()创建的字符串不是常量,不能在编译期就确定,所以new String() 创建的字符串不放入常量池中,它们有自己的地址空间。
s1因为无法在编译期确定,所以是运行时创建的新对象"come on"的引用,s2因为有后半部分new String("on")所以也无法在编译期确定,所以也是一个新创建对象"come on"的应用,明白了这些也就知道为何得出此结果了。
本章关于从享元模式再看String分析完毕,感谢和小编一起学习.
最后附带两个小小的面试题送给初级入门小伙伴:
一: 关于equals()和==区别:
答案:这个从上述应该可以明显看出来结论:对于String简单来说就是比较两字符串的Unicode序列是否相当,如果相等返回true;而==是比较两字符串的地址是否相同,也就是是否是同一个字符串的引用。
二: 关于String为什么是不可变的:
这一说又要说很多,大家只要知道String的实例一旦生成就不会再改变了,看下定义String源码就知道了:
看String源码得出: 不可变原因是因为String修饰类有个final关键字.
那么问题又来了:那可能大家只知道加个关键字final就会让变量不可变,但是不知道具体原因,看个例子,一起分析下:
代码一(不带有final修饰常量):
/**
* @author 方健* @desc 微信公众号:十点攀程* @date 2020/7/11
*/public class FinalDemo {public static void main(String[] args) {
System.out.println(Demo.test);}static class Demo {public static String test = "我是demo类的一个测试字符串";
static {
System.out.println("我是demo的静态代码块!!!");}
}
}//输出结果:
//我是demo的静态代码块!!!
//我是demo类的一个测试字符串
代码二(带有final修饰常量):
/**
* @author 方健* @desc 微信公众号:十点攀程* @date 2020/7/11
*/public class FinalDemo {public static void main(String[] args) {
System.out.println(Demo.test);}static class Demo {public static final String test = "我是demo类的一个测试字符串";
static {
System.out.println("我是demo的静态代码块!!!");}
}
}//输出结果:
//我是demo类的一个测试字符串
分析原理:
首先对比结果会发现final的作用(亲测有效).
如果加入了常量关键字,也就是final关键字,JVM会把这个常量放到FinalDemo这个类里面的常量池当中,因此并不会主动加载demo这个类.
然后继续这个final话题再往下分析得出一个新的结论:
工作中许多场景都需要用到字符串拼接.
因为String的"不可变"会产生了很多临时变量,这也就是为什么建议用StringBuffer的原因了,是因为StringBuffer是可变的.
这也就是为什么StringBuffer代替String拼接的原因.
本篇文章分享结束,以学习优秀设计思想为目的,到java语言String事例场景分析,通过看源码的方式以及亲测运行实例,以及最后探寻的几个面试题答案,辩论充分有效且真实,感谢您的观看,精彩持续进行中......
您所喜欢,点个在看