- 浏览: 5629 次
- 性别:
- 来自: 南京
最近访客 更多访客>>
最新评论
-
老马睡不醒:
直接设置为0的方式不可取。可能造成套接字缓冲区里的数据还来不及 ...
解决Mina的传输中造成的TIME_WAIT过多的问题(不能立即断开连接)
总叙述:什么时候、如何创建对象;什么时候、如何避免创建对象;如何保证对象能够适时地销毁;对象被销毁之前如何管理各种清理工作。
item1:考虑用静态工厂方法代替构造函数
类可以提供一个公有的静态工厂方法,实际上只是一个简单的静态方法,它返回的是类的一个实例。
例如:Boolean类的简单例子<他把一个boolean原语值转换为一个Boolean对象引用。
public static Boolean valueOf(boolean b){
return (b ? Boolean.TRUE : Boolean.FALSE);
}
类可以为他的客户提供一些静态工厂的方法,来替代构造函数,或者同时也提供一些构造函数。用静态工厂方法来代替公有的构造函数,就有好处也有不足之处。
静态工厂方法的一个好处是,与构造函数不同,静态工厂方法具有名字。如果一个构造函数的参数没有确切地描述被返回的对象,那么选用适当名字的静态工厂方法可以使一个类更易于使用,并且相应的客户代码更易于阅读。一个类只能有一个原型相同的构造函数。那么我们怎样绕开这个限制呢,他可以提供两个共造函数,它们的参数列表只是在参数类型的顺序上有所不同。这并不是一个好主意,面对这样的API,用户永远也记不住该用哪个构造函数,如果常常会调用到错误的构造函数上。并且,读到使用这样的构造函数的代码时往往会不知所云,除非去查看该类的文档。
因为静态工厂方法有自己的名字,所以它们没有构造函数那样的限制,对于给定的原型特征可以有不止一个静态工厂方法。如果一个类看起来需要多个构造函数,并且它们的原型特征相同,那么你应该考虑用静态工厂方法来代替其中一个或多个构造函数,并且慎重选择他们的名字以便明显地标出他们的不同。
静态工厂方法的第二个好处是,与构造函数不同,它们每次被调用的时候,不要求非得创建一个新的对象。这使得一些非可变类可以使用一个预先构造好的实例,或者把已经构造好的实例缓存起来,以后再把这些实例分发给用户,从而避免创建不必要的重复对象。Boolean.valueOf(boolean)方法说明了这项技术:它从来不创建对象。如果一个程序要频繁地创建相同的对象,并且创建对象的代价很昂贵,则这项技术可以极大地提高性能。
静态工厂方法可以为重复的调用返回同一个对象,这也可以被用来控制“在某一时刻哪些实例应该存在”。这样做有两个理由:第一,他使得一个类可以保证是一个 singleton。第二,他使非可变类可以保证“不会有两个相等的实例存在”,即当且仅当a==b的时候才有a.equals(b)为true。如果 一个类保证了这一点,那么它的客户就可以用==操作符来代替equals(Object)方法,其结果是实质性的性能提高。
静态工厂方法的第三个好处是,与构造函数不同,它们可以返回一个原返回类型的子类型的对象。使用这样的静态工厂方法,可以强迫客户通过接口来引用被返回的对象,而不是通过实现类来引用被返回的对象,这是一个很好的习惯。
公有的静态工厂方法所返回的对象的类不仅可以是非公有的,而且该类可以随着每次调用而发生变化,这取决于静态工厂方法的参数值,只要是已声明的返回类型的子类型,都是允许的,而且,为了增强软件的可维护性,返回对象的类型也可以随着不同的发行版本而不同。
静态工厂方法返回 的对象所属的类,在编写包含该静态工厂方法的类时可以并不存在。
静态工厂方法的主要缺点是,类如果不含公有的或者受保护的构造函数,就不能被子类化。
静态工厂方法的第二个缺点是,它们与其他的静态方法没有任何区别。
总的来说,静态工厂方法和公有的构造函数方法都有他们各自的用途,我们需要理解他们各自的长处。如果你正在权衡这两种选择,又没有其他因素强烈地影响你的选择,那么你最好还是简单地使用构造函数,毕竟他是语言提供的规范。
item2.使用私有构造函数强化singleton属性
singleton是这样的类,它只能实例化一次。singleton通常被用来代表那些本质上具有惟一性的系统组件,比如视频显示或者文件系统。
实现singleton有两种方法,这两种方法都要把构造函数保持为私有的,并且提供一个静态成员,以便允许客户能够访问该类惟一的实例:在第一种方法中,公有静态成员是一个final域:
public class Elvis{
public static final Elvis INSTANCE =new Elvis();
private Elvis(){
}
}
私有构造函数仅被调用一次,用来实例化公有静态final域Elvis.INSTANCE。由于缺少公有的或者受保护的构造函数,所以保证了 Elvis的全局惟一性。一旦Elvis类被实例化之后,只有一个Elvis实例存在--不多也不少。客户的任何行为都不会改变这一点。
第二种方法提供了一个私有的静态工厂方法,而不是公有的静态final域:
public class Elvis{
private static final Elvis INSTANCE =new Elvis();
private Elvis(){}
public static Elvis getInstance(){
return INSTANCE;
}
}
所有对于静态方法Elvis.getInstance的调用,都会返回同一个对象的引用,所以,不会有别的Elvis实例被创建。
第一种方法主要好处是:组成类的成员的声明很清楚地表明了这个类是一个singleton。公有的静态域是final的,所以该域将总是包含相同的对象引用。第一种方法可能在性能上稍微领先,但是在第二种方法中,一个优秀的JVM实现应该能够通过将静态工厂方法的调用内联化,来消除这种差别。
第二种方法的主要好处在于,他提供了灵活性:在不改变API的前提下,允许我们改变想法,把该类作成singleton,或者不做singleton.sigleton的静态工厂方法返回该类的惟一实例,但是,他也很容易被修改,比如说,为每个调用该方法的线程返回一个惟一的实例。
总而言之,如果你确信该类将永远是一个singleton,那么使用第一种方法是由意义的。如果你想保留一点余地,那么请使用第二种方法。
item3: 通过私有构造函数强化不可实例化的能力
由于只有当一个类不包含显式的构造函数的时候,编译器才会生成默认构造函数,所以,我们只要让这个类包含单个显式的私有构造函数,则它就不可被实例化了。
public class UtilityClass{
private UtilityClass(){}
}
因为显式构造函数是私有的,所以在该类的外部它是不可被访问的。假设该构造函数不会被类自身从内部调用,就能保证该类永远不会被实例化。
这种习惯用法也有副作用,它使得一个类不能被子类化。所有的构造函数都必须要调用一个可访问的超类构造函数,无论显式或隐式地调用,在这种情况下,子类就没有可访问的构造函数来调用了。
item4:避免创建重复的对象
重复使用同一个对象,而不是每次需要的时候就创建一个功能上等价的新对象,通常前者更为合适。
例如:
String s=new String("silly");
该语句每次被执行的时候都创建一个新的String实例,但是这些创建对象的动作没有一个是真正必需的.传递给String构造函数的实参 ("silly")本身就是一个String实例,功能上等同于所有的构造函数创建的对象。如果这种用法是在一个循环中,或者是在一个被频繁调用的方法中,那么成千上万不必要的String实例会被创建出来。
改进版本:String s="no longer silly";
这个版本只使用一个String实例,而不是每次执行的时候创建一个新的实例。而且,它可以保证,对于所有在同一个虚拟机中运行的代码,只要它们包含相同的字符串常量,则该对象就会被重用。
对于提供了静态工厂方法和构造函数的非可变类,你通常可以利用静态工厂方法而不是构造函数,以避免创建重复的对象。例如,静态工厂方法 Boolean.valueOf(String)几乎总是优先于构造函数Boolean(String)。构造函数在每次调用的时候都会创建一个新的对象,而静态工厂方法从来不要求这样做。
item5:消除过期的对象引用
对于一种具有垃圾回收功能的语言,我们工作可以很容易,因为当我们用完了对象之后,它们会被自动回收,但就认为自己不再需要考虑内存管理的事情了,实际上这是不正确的。
例如:
public class Stack{
private Object[] elements;
private int size=0;
public Stack(int initialCapacity){
this.elements=new Object[initialCapacity];
}
public void push(Object e){
ensureCapacity();
elements[size++]=e;
}
public Object pop(){
if(size==0) throw new EmptyStackException();
return elements[--size];
}
private void ensureCapacity(){
if(elements.length==size){
Object[] oldElements=elements;
elements=new Object[2*elements.length + 1];
System.arraycopy(oldElements,0,elements,0,size);
}
}
}
分析:这个程序没有很显然的错误。无论你如何测试,它都会成功地通过你的每一项测试,但是,这个程序中潜伏着一个问题。不严格地讲,这个程序有一个 “内存泄露”,随着垃圾回收器活动的增加,或者由于不断增加的内存占用,程序性能的降低会逐渐表现出来。那么,程序中哪里会发生内存泄露呢?如果一个栈先是增长,然后再收缩,那么,从栈中弹出来的对象将不会被当做垃圾回收,即使使用栈的客户程序不再应用这些对象,它们也不会被回收。这是因为,栈内部维护着这些对象的过期引用。所谓过期引用,是指永远也不会被解除的引用。在本例中,凡是在elements数组的“活动区域”之外的引用都是过期的,elements的活动区域是指下标小于size的那一部分。
在支持垃圾回收的语言中,内存泄露是很隐蔽的,如果一个对象引用被无意识地保留起来了,那么,垃圾回收机制不仅不会处理这个对象,而且也不会处理被这个对象引用的所有其他对象。即使只有少量的几个对象引用被无意识地保留下来,也会有很多的对象被排除在垃圾回收机制之外,从而对性能造成潜在的重大影响。
要想修复这一类问题也很简单:一旦对象引用已经过期,只需清空这些引用即可。在上述例子的Stack类中,只要一个单元被弹出栈,指向它的引用就过期了。pop方法的修订版本如下所示:
public Object pop(){
if(size==0) throw new EmptyStackException();
Object result=elements[--size];
elements[size]=null;//Eliminate obsolete reference
return result;
}
清空过期引用的另一个好处是,如果它们在以后又被错误地解除引用、则程序会立即抛出NullPointerException异常,而不是悄悄地错误运行下去。尽可能早地检测出程序中的错误总是有益的。
对于一个对象的引用,一旦程序不再使用到它,就把他清空。这样做既没必要,也不是我们所期望的,因为这样做会把程序代码弄得很乱,并且可以想象还会降低程序的性能。“清空对象引用”这样的操作应该是一种例外,而不是一种规范行为。消除过期引用最好的方法是重用一个本来已经包含对象引用的变量,或者让这个变量结束其生命周期。如果你是在最紧凑的作用域范围内定义每一个变量,则这种情形就会自然而然地发生。应该注意到,在目前的JVM实现平台上,紧紧退出定义变量的代码是不够的,要想使引用消失,必须退出包含该变量的方法。
那么,何时应该清空一个引用呢?Stack类的哪个方面特性使得它遭受了内存泄露的影响?简而言之,问题在于,Stack类自己管理内存。存储池包含了elements数组的元素。数组的活动区域中的元素是已分配的,而数组其余部分的元素是自由的。但是垃圾回收器并不知道这一点;对于垃圾回收器而言,elements数组中的所有对象引用都同等有效,只有程序员知道数组的非活动区域是不重要的。程序员可以把这个情况告知垃圾回收器,做法很简单:一旦数组元素变成了非活动区域的一部分,程序员就手工清空这些元素。
一般而言,只要一个类自己管理它的内存,程序员就应该警惕内存泄露问题,一旦一个元素被释放掉,则该元素中包含的任何对象引用应该被清空。
内存泄露的另一个常见来源是缓存。一旦你把一个对象引用放到一个缓存中,它就很容易被遗忘掉,从而使得它不再有用之后很长一段时间内仍然留在缓存中。对于这个问题,有两种可能的解决方案。如果你正巧要实现这样的缓存:只要在缓存之外存在对某个条目的键的引用,该条目就有意义,那么你可以使用 WeakHashMap来代表缓存;当缓存中的条目过期之后,它们会自动被删除。而更为常见的情形是,“被缓存的条目是否有意义”的周期并不很容易确定,其中的条目会在运行的过程中变得越来越没有价值。在这样的情况下,缓存应该时不时地清除掉无用的条目。这项清除工作可以由一个后台线程来完成,或者也可以在加入新条目的时候做清理工作。
item6:避免使用终结函数
终结函数通常是不可预测的,常常也是很危险的,一般情况下是不必要的。使用终结函数会导致不稳定的行为、更差的性能,以及带来移植性问题。当然终结函数也有可用之处,经验告诉我们:应该避免使用终结函数。
时间关键的任务不应该由终结函数来完成。因为从一个对象变得不可达到开始,到它的终结函数被执行,这段时间的长度是任意的、不确定的。
我们不应该依赖一个终结函数来更新关键性的永久状态。
//考虑用静态工厂方法代替构造函数
import java.util.*;
// Provider framework sketch
public abstract class Foo {
// Maps String key to corresponding Class object
private static Map implementations = null;
// Initializes implementations map the first time it's called
private static synchronized void initMapIfNecessary() {
if (implementations == null) {
implementations = new HashMap();
// Load implementation class names and keys from
// Properties file, translate names into Class
// objects using Class.forName and store mappings.
// ...
}
}
public static Foo getInstance(String key) {
initMapIfNecessary();
Class c = (Class) implementations.get(key);
if (c == null)
return new DefaultFoo();
try {
return (Foo) c.newInstance();
} catch (Exception e) {
return new DefaultFoo();
}
}
public static void main(String[] args) {
System.out.println(getInstance("NonexistentFoo"));
}
}
class DefaultFoo extends Foo {
}
//使用私有构造函数来强化singleton属性的两种方法
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private Elvis() {
}
public static void main(String[] args) {
System.out.println(Elvis.INSTANCE);
}
}
public class Elvis {
private static final Elvis INSTANCE = new Elvis();
private Elvis() {
}
public static Elvis getInstance() {
return INSTANCE;
}
public static void main(String[] args) {
System.out.println(Elvis.INSTANCE);
}
}
//为了让一个singleton类变成可序列化的,仅在声明中加上"implements Serializsble"是不够的,为了维护singleton性,必须也要
提供一个readResolve方法。否则的话,一个序列化的实例在每次反序列化的时候,都会导致创建一个新的实例。
import java.io.*;
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private Elvis() {
// ...
}
// ... // Remainder omitted
// readResolve method to preserve singleton property
private Object readResolve() throws ObjectStreamException {
/*
* Return the one true Elvis and let the garbage collector
* take care of the Elvis impersonator.
*/
return INSTANCE;
}
public static void main(String[] args) {
System.out.println(Elvis.INSTANCE);
}
}
//通过私有强化不可实例化的能力
public class UtilityClass {
// Suppress default constructor for noninstantiability
private UtilityClass() {
// This constructor will never be invoked
}
// ... // Remainder omitted
}
//消除过期的对象引用
import java.util.*;
// Can you spot the "memory leak"?
public class Stack {
private Object[] elements;
private int size = 0;
public Stack(int initialCapacity) {
this.elements = new Object[initialCapacity];
}
public void push(Object e) {
ensureCapacity();
elements[size++] = e;
}
public Object pop() {
if (size==0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; // Eliminate obsolete reference
return result;
}
/**
* Ensure space for at least one more element, roughly
* doubling the capacity each time the array needs to grow.
*/
private void ensureCapacity() {
if (elements.length == size) {
Object[] oldElements = elements;
elements = new Object[2 * elements.length + 1];
System.arraycopy(oldElements, 0, elements, 0, size);
}
}
public static void main(String[] args) {
Stack s = new Stack(10);
for (int i=0; i<args.length; i++)
s.push(args[i]);
for (int i=0; i<args.length; i++)
System.out.println(s.pop());
}
}
相关推荐
白色简洁风格的软件UI界面后台管理系统模板.zip
自动软包电芯极耳短路测试精切一体机sw17可编辑全套技术资料100%好用.zip
RuntimeException如何解决.md
定期分析系统的投资回报率(ROI)是确保企业在实施云链客服系统后获得实际效益的关键步骤。以下是一个系统的框架和方法,帮助您有效地进行投资回报率分析。 投资回报率(ROI)分析框架 一、定义投资回报率 投资回报率(ROI)是衡量投资效率的指标,通常通过以下公式计算: ROI= 成本 收益−成本 ×100% 收益:通过实施系统所带来的直接经济利益,例如收入增加、成本节省等。 成本:系统的实施和运营成本,包括初始投资和持续运营费用。 二、确定收益来源 直接收益 销售增长:由于客服系统提升了客户满意度和响应速度,导致客户购买量增加。 客户保留率提高:系统帮助降低客户流失率,保持长期客户关系。 跨卖和追加销售:通过更好的客户互动和数据分析,提升交叉销售和追加销售的机会。 间接收益 运营效率提升:客服人员的工作效率提高,能够处理更多客户请求,减少人力成本。 品牌形象增强:客户体验的改善有助于提升品牌形象,吸引新客户。 客户忠诚度提升:满意的客户更可能成为回头客,提升长期收益。
白色简洁风格的室内设计案例源码下载.rar
html+css+js学习代码html+css+js学习代码html+css+js学习代码 html+css+js学习代码html+css+js学习代码html+css+js学习代码 html+css+js学习代码html+css+js学习代码html+css+js学习代码 html+css+js学习代码html+css+js学习代码html+css+js学习代码 html+css+js学习代码html+css+js学习代码html+css+js学习代码 html+css+js学习代码html+css+js学习代码html+css+js学习代码 html+css+js学习代码html+css+js学习代码html+css+js学习代码 html+css+js学习代码html+css+js学习代码html+css+js学习代码 html+css+js学习代码html+css+js学习代码html+css+js学习代码 html+css+js学习代码html+css+js学习代码html+css+js学习代码 html+css+js学习代码html+css+js学习代码html+css+j
三相逆变 单相 三相逆变器 SPWM ---stm32主控(输入、输出具体可根据需要设定),本逆变器可以二次开发。 本内容只包括 逆变程序,实现变频(0~100Hz)、变压调节,均有外接按键控制(使用C语言实现)。
内容概要:本文详细介绍了基于STM32单片机的激光雕刻机控制系统的设计。系统包括硬件设计、软件设计和机械结构设计,主要功能有可调节激光功率大小、改变雕刻速率、手动定位、精确雕刻及切割。硬件部分包括STM32最小系统、步进电机驱动模块、激光发生器控制电路、人机交互电路和串口通信电路。软件部分涉及STM32CubeMX配置、G代码解析、步进电机控制、激光功率调节和手动定位功能的实现。 适合人群:对嵌入式系统和激光雕刻机感兴趣的工程师和技术人员。 使用场景及目标:① 适用于需要高精度激光雕刻的应用场合;② 为开发类似的激光雕刻控制系统提供设计参考。 阅读建议:本文提供了详细的硬件和软件设计方案,读者应结合实际应用场景进行理解,重点关注电路设计和代码实现。
北航软件体系架构.7z
白色简洁风格的高端汽车预订企业网站源码下载.zip
白色宽屏风格的时尚摄影图片网站模板下载.zip
### 大数据技术之Hadoop(入门)知识点详解 #### 第1章 大数据概论 ##### 1.1 大数据概念 大数据是指无法在一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合。这些数据具有体量巨大、来源多样化、格式复杂等特点。 ##### 1.2 大数据特点(4V) **Volume(体量大)**:指的是数据量非常庞大。 **Velocity(速度快)**:指数据产生的速度极快。 **Variety(多样性)**:指数据类型多样,不仅限于结构化数据,还包括大量半结构化和非结构化数据。 **Value(价值密度低)**:尽管数据总量很大,但真正有价值的信息可能只占一小部分。 ##### 1.3 大数据应用场景 - **金融行业**:风险控制、精准营销、反欺诈等。 - **零售行业**:客户行为分析、库存管理优化等。 - **医疗健康**:疾病预测、个性化治疗方案制定等。 - **交通物流**:智能交通系统、物流路径优化等。 ##### 1.4 大数据发展前景 随着物联网、云计算等技术的发展,大数据的应用场景将会更加广泛。预计未来几年内,大数据技术将更加成熟,处理能力更强,为
UnknownHostException(解决方案).md
LP3_PLC程序培训_01.zip
白色简洁风格的重型汽车销售企业网站源码下载.zip
白色简洁风格的摄影图片模板下载.zip
白色宽屏风格的农家乐有机蔬菜企业网站模板.rar
北航智能自主系统.7z
白色简洁风格的网络实验室CSS模板.zip
门铃是日常生活中常见的一种设备,它通过发出声音来通知人们有访客或者有其他重要事件发生。在信息技术领域,特别是在嵌入式系统中,利用单片机设计定时器门铃是一项基础且实用的技术实践。单片机,即单片微型计算机,因其集成度高、成本低、应用广泛,常被用于各种控制系统的开发。本文将详细探讨如何使用单片机实现定时器门铃的设计。 我们需要了解单片机的基本结构。单片机通常包括CPU、存储器(ROM和RAM)、定时/计数器、输入/输出接口等组成部分。其中,定时/计数器是实现定时器功能的关键。它可以通过对内部时钟脉冲的计数来达到定时的效果,或者对外部事件的计数来实现计数功能。 在设计定时器门铃时,我们会用到单片机的定时器功能。定时器工作模式通常有多种,如自由运行模式、捕获模式、比较模式等。对于门铃应用,我们可能选择自由运行模式,设置一个预设的时间间隔,当定时器溢出时,触发中断,从而启动门铃音效。 实现门铃的代码主要包括以下几个部分: 1. 初始化定时器:这一步通常包括设置定时器的工作模式、初值、分频系数等。例如,我们可以选择定时器工作在自动重装载模式,并设定合适的初值,使得定时器在一定时间后溢出。 2. 中断