`

《Effetive Java》读书笔记一

阅读更多
《Effetive Java》和 《Effective C++》、《More Effective C++》保持了一致的
写作风格,使用条款的方式对使用该语言的应该注意和极易出现问题的地方,提出了错误的做法,并讲述了应该怎么做,java之父James Gosling都需要的书,我们更应该看看,看了一遍收获颇多,简单记录下来一些要点。
第二章 创建和销毁对象
条款一: 考虑用静态工厂方法来替代构造函数
优点:1、静态工厂方法有更具有描述力的名字,对于有多个构造方法的类来说,使得程序更清晰。
      2、对于创建不变对象,约束实例创建如単例,不必要每次都创建一个对象,提高性能。
      3、不像构造子,它还可以创建任何子类的对象。
         java.util.Collections.通过私有的Collection的静态实现类不暴露Collection的实现类而
创建子类的对象,返回的类型是Collection接口类型。不仅可以减少API,而且可以减少
         Conceptual weight
     java.util.EnumSet根据enum type的大小来创建两种实例:RegularEnumSet,JumboEnumSet。
     Service provider frameworks
       4、当创建具有类型参数的实例时,可以减少类型参数的使用(泛型)
    
          Map<String, List<String>> m = new HashMap<String, List<String>>();
         

需要写两次参数,加上工厂方法后:
	
			public static <K, V> HashMap<K, V> newInstance() {
				return new HashMap<K, V>();
			}
		

         只需要一次:
Map<String, List<String>> m = HashMap.newInstance();

缺点: 1、如果没有public或者protected构造函数就不能子类化。
       2、工厂方法不是标准的创建实例的方法,有时很难和其他方法区分:
  命名约定:valueOf of getInstance newInstance getType newType
条款二:当构造函数有很多参数时,考虑使用builder.
       Telescoping constructor pattern:
       Hard to write client code, and harder to read.
       JavaBean pattern:
       可能导致不一致的状态,例如没有必选的参数要set的约束。
       Builder pattern:
      
     	   // Builder Pattern
			public class NutritionFacts {
				private final int servingSize;
				private final int servings;
				private final int calories;
				private final int fat;
				private final int sodium;
				private final int carbohydrate;
				public static class Builder {
				// Required parameters
					private final int servingSize;
					private final int servings;
					// Optional parameters - initialized to default values
					private int calories = 0;
					private int fat = 0;
					private int carbohydrate = 0;
					private int sodium = 0;
					public Builder(int servingSize, int servings) {
						this.servingSize = servingSize;
						this.servings = servings;
					}
					public Builder calories(int val){
						calories = val; 
						return this;
					}					
					public Builder fat(int val){
						fat = val; return this;
					}
					public Builder carbohydrate(int val){
						carbohydrate = val;
						return this;
					}
					public Builder sodium(int val){
						sodium = val;
						return this;
					}
					public NutritionFacts build() {
						return new NutritionFacts(this);
					}
				}
				private NutritionFacts(Builder builder) {
					servingSize = builder.servingSize;
					servings = builder.servings;
					calories = builder.calories;
					fat = builder.fat;
					sodium = builder.sodium;
					carbohydrate = builder.carbohydrate;
				}
			}
       

       Class的newInstance破坏了编译的异常检查,错误越早发现越好,特别是在编译的时候发现最         好。
条款三、通过私有化构造子和使用枚举类型来实现単例
    方法一、直接公开一个静态属性:public static final Elvis INSTANCE = new Elvis();
方法二、使用工厂方法
方法三、使用枚举方法
			public enum Elvis {
				INSTANCE;
				public void leaveTheBuilding() { ... }
			}
		

        前两种方法,用户可以通过反射来调用私有构造子创建新的实例:可以在私有构造子中抛出异常来解决。
如果实现了Serializable接口,需要把所有的成员声明为transit,并提供
			// readResolve method to preserve singleton property
			private Object readResolve() {
				// Return the one true Elvis and let the garbage collector
				// take care of the Elvis impersonator.
				return INSTANCE;
			}
		

使用枚举方法可以避免上述两种问题,所以是最佳的方式。
単例的缺点是难于测试,很难去mock,不可子类化。
条款四、通过私有化构造子来实现不可实例化
        Utility类(工具类)不应该去实例化的,因为实例化毫无意义,里面只是一堆静态的方法。但是编译器会
提供一个默认的构造子。我们想让它不可实例化,有几种方法:
方法一、把类写成抽象的。这种方法的缺点是容易混淆设计的意图,认为是留给别人实现用的。
方法二、私有化构造子,并在构造子中抛出访问异常。
		// Noninstantiable utility class
		public class UtilityClass {
			// Suppress default constructor for noninstantiability
			private UtilityClass() {
			throw new AssertionError();
			}
			... // Remainder omitted
		}
		

条款五、避免不必要的对象创建
1、避免使用不变类的构造函数,而使用提供的工厂方法:
String s = new String("stringette");
使用String s = "stringette";
使用工厂方法Boolean.valueOf(String)而不是Boolean的构造函数Boolean(String)
2、把可变的对象,但在你的应用中不变的对象的创建作为常量放在静态块中。
3、使用懒初始化的方法,在必要的时候做初始化。例如単例中的lazy方法。
4、autoboxing(自动装箱)容易导致不必要的实例创建:
  
			Long sum = 0L;
			for (long i = 0; i < Integer.MAX_VALUE; i++) {
				sum += i;
			}
			System.out.println(sum);
		   

   代码中由于使用了Long,导致了2^31个Long对象的不必要的创建。所以优先使用primitives而不是
   boxed primitives。
5、只对重量级别的对象做Object pool,不要对轻量级的对象做Object pool,因为jvm创建和回收一个轻
   量级的对象性能非常的好。
条款六、清除已经没用的对象引用
Stack的pop方法的实现:
			public Object pop() {
				if (size == 0)
				throw new EmptyStackException();
				return elements[--size];
			}
		

这个方法的实现导致了当pop出来的对象,没有被垃圾回收站回收,从而导致内存泄露。具有垃圾回收功能的
语言,无意的对象驻留会导致内存泄露,即使是很少的这种无意的对象驻留也可能影响大量对象的回收,因为
这些对象组成了一个图,他们之间相互引用。
上面代码的正确实现:
			public Object pop() {
				if (size == 0)
				throw new EmptyStackException();
				Object result = elements[--size];
				elements[size] = null; // Eliminate obsolete reference
				return result;
			}
		

最好的清除没用对象引用的方法是让对象定义在它真正的作用范围内,也就是把变量定义在最窄的可能范围内。

另一种内存泄露来源与Cache。使用WeakHashMap可以保证当外部的key的引用超过生命周期的时候,可

以自动把WeakHashMap对应的Entry清除。一个好的生命周期控制的cache,应该有一个后台的线程定期

清除不活跃的元素。LinkedHashMap具有removeEldestEntry可以给予帮助。
第三种内存泄露来源于监听器和其他的回调。
注册监听器或回调如果没有deregister,他们就会不断的积聚。最好的方式是只保存他们的Weak

references.例如在WeakHashMap只保存他们的Keys

条款7: 避免使用finalizers
finalizers是不可预测的,往往会很危险,一般没有必要使用。
finalizers的
第一个缺点是不能保证立即回收。
第二个缺点finalizer不同的jvm实现的算法不同,依赖与finlizer的程序具有不可以移植性。
第三个缺点是使用finalizer会有严重的性能影响
不要使用System.gc and System.runFinalization
一般提供一个显示的中止函数,例如InputStream, OutputStream, and java.sql.Connection都有
close方法。java.util.Timer的cancel
显示的中止函数往往和try finally一起使用来回收资源。
使用finalizers的两种情况:
一、可以在用户忘记调用显式的中止函数时,作为安全网发挥点作用,因为晚点释放资源要比不释放的

好。这时候要log一下警告发现资源没有被中止,以便发现bug的存在。
二、在native peers中使用来终止不关键的资源,因为native的东西JVM并不知道它,也不会负责回收

它。如果native方法持有关键的资源,java对应端应该有显式的中止函数。

使用finalizers要显式的调用父类的finalizers
// Manual finalizer chaining
@Override protected void finalize() throws Throwable {
	try {
		... // Finalize subclass state
	} finally {
		super.finalize();
	}
}

如果子类覆写的实现忘了被调用,那么finalizer将永远不会被调用。一个解决方法是用内部类创建一

个成员对象finalizerGuardian
// Finalizer Guardian idiom
public class Foo {
	// Sole purpose of this object is to finalize outer Foo object
	private final Object finalizerGuardian = new Object() {
		@Override protected void finalize() throws Throwable {
			... // Finalize outer Foo object
		}
	};
	... // Remainder omitted
}
2
0
分享到:
评论

相关推荐

    Effetive c++ second Edition

    這本書是多年來我對專業程式員所做的C++ 教學課程下的一個自然產物。我發現,大部份學生在一個星期的密集訓練之後,即可適應這個語言的基本架構,但要他們「將這些基礎架構以有效的方式組合運用」,我實在不感樂觀。...

    More Effetive c++

    #### 一、C++ 学习难度剖析 C++ 被誉为一门难学易用的语言,其复杂性主要体现在以下几个方面: 1. **语法广泛**:C++ 拥有一套庞大的语法体系,包括基本的数据类型、控制结构、函数定义等。 2. **语义复杂**:语法...

    Effetive STL.doc

    书中通过一系列的条款,详细讲解了STL容器、迭代器、算法、仿函数等方面的关键知识点,旨在帮助开发者写出更高效、更可靠的代码。 1. 容器的选择与管理: - 条款1强调选择适合的容器,如vector、list、set等,考虑...

    EFFETIVE STL

    《Effective STL》是由著名C++专家Scott Meyers撰写的一本经典图书,主要针对已经对标准模板库(STL)有一定了解的程序员,旨在帮助他们更深入、更有效地使用STL。这本书通过一系列实践性强的编程指导,揭示了STL...

    学生成绩管理系统java程序设计.doc

    学生成绩管理系统_java程序设计 本文档主要介绍了学生成绩管理系统的设计和...学生成绩管理系统是一个基于 Java 语言的学生信息管理系统,旨在提供一个 effetive 和efficient 的方式来管理学生信息,提高教学效率。

    effective-go中文版

    Go语言,也称为Golang,是一种由Google开发的开源编程语言,旨在实现简洁、快速和高效的编程。自从2007年公开宣布后,Go语言受到了广泛的关注和应用。《Effective Go》是Go语言官方提供的一个指南,它由一系列的建议...

    A Markov Process Based Approach to Effective Attacking JPEG Steganography

    JPEG隐写分析是信息安全领域中的一个重要分支,它关注于在JPEG图像格式中隐藏信息的检测和分析。JPEG(联合图像专家小组)格式自1990年代以来一直是图像压缩的标准之一,广泛用于网络上的图像数据传输。在JPEG图像中...

    不错的STL文档学习资料

    1. **Effetive+STL.doc**:这可能是一份关于《Effective STL》的文档,作者是Scott Meyers。这本书提供了50条实用的编程准则和技巧,帮助程序员更好地理解和使用STL,包括如何避免常见错误、提高效率以及利用STL的...

    基于WEB的 教育资源管理系统设计与开发

    基于WEB的教育资源管理系统设计与开发 本文将讨论基于WEB的教育资源管理系统的设计与开发,涵盖需求分析、功能模块流程图、教育资源的...该系统旨在为教学提供一个 effetive的资源管理平台,提高教学的效率和质量。

    c# 2nd note

    ### 1. 使用锁机制确保线程安全 在示例代码中,`Customer` 类的 `Name` 属性使用了一个私有的 `syncHandle` 对象来进行加锁操作,以确保在多线程环境下属性的读写操作是线程安全的。这种方式可以有效地防止多个线程...

    医疗卫生面试真题:卫生类典型面试题汇总及答案.pdf

    一、面试基本概念 * 面试是医疗卫生行业招聘的重要环节,旨在评估候选人的专业知识、技能和职业素质。 * 面试的目的是为了了解候选人的职业背景、工作经验、技能和职业目标,以确定其是否适合该岗位。 二、面试...

Global site tag (gtag.js) - Google Analytics