Java通用程序设计与集合方法总结

来源:互联网 发布:linux 修改帐号密码 编辑:程序博客网 时间:2024/06/07 02:59

一.  equals() VS “==”
    equals() 通常用来比较两个引用所指向的对象的内容是否相等;而 “==”通常比较的事两个引用所指向的对象是否为同一个对象,即是否引用了同一个内存地址;那么不难推断出,如果两个对象相等,那么它们一定相互
    equals(),而两个对象相互equals()却不一定相等。

 

二. 在类内部的方法可以访问对象的私有实例变量
    乍看似乎不合理引用,但是只要方法所属类与对象的类型是相同的类,那么该方法就能访问对象的私有实例变量。

 


   如果类中有一个实例变量是数组类型,那么clone方法在返回之前需要执行一些额外的工作。额外的工作需要创建一个新的数组,供clone的实例变量引用。这个类可能还有其他一些引用了某些对象的实例变量。在这种情况下,
clone方法还需要执行一些额外工作,为每个实例变量创建其引用的新对象


四. 如果某个方法返回一个object类型的对象,要实际使用该返回值,通常需要对返回值进行强制类型转换。

 

五. 统计对象出现的次数:对于非空目标,使用equals()来确定目标出现的次数,空目标用“==”来确定null的出现次数


六. 陷阱:通用方法的限定
 由编译器推出的与通用类型对应的数据类型只是某个类类型(不能是基本数据类型)。另外,不能调用通用类型的某个构造函数,也不能创建生成由该类型的元素构成的新数组

 


七. 将集合类转换成通用类的8个步骤:
   1.原类名的所有出现均改成XXXX<E>的形式,其中XXXX为原类名,E为通用类型参数;
   2.找到原类中所有表示包中元素类型的位置,将这些位置的数据类型改成通用类型参数E
   3.将静态烦方法改成通用静态方法。类中可以包含一些通用静态方法,但是所有依赖通用类型参数E的静态方法都必须修改成通用方法。
   4.不能创建任何新的E对象或数组。
   5.在原来的类中找到所有使用“==”或“!=”来进行两个元素比较的点,将这些运算符特换成equals方法。
   6.处理空引用,如前所述
   7.将未使用的引用变量设置成null便于垃圾回收,释放内存。
   8.更新所有文档,进一步体现包是由引向对象的引用构成的集合。

 

 

八. 使用某个接口作为形式参数的类型:
   当某接口名称用作方法的形式参数的类型时,实际参数必须是实现了该接口的某种数据类型。可以使用instanceof关键字来判断类是否实现某个接口;也可以判断某个对象是否实现了某个通用接口,可以通过制定通用接口的实例化来进行判断。


九. java.util中的TreeMap类以一种有效的方法实现了map接口,其中要求关键字必须属于某个已经实现了Comparable()接口的类(例如:String)

 

十. 从JAVA5.0开始覆盖方法的返回值类型可能是原始方法返回值类型的任何子孙。用这种方式使用子孙被称为返回值变异。

 

十一. JAVA方法激活机制:当运行程序程序并激活方法时,JAVA运行时系统会检查实际对象的数据类型并使用来自该类型的方法(而不是使用来自引用变量类型的方法)

 

 

十二. 扩展类的构造函数要点:

       1. 扩展类不能继承其他带参超类构造函数。

       2. 扩展类又不属于超类的新的实例变量,那么继承的无参构造函数会将这些新值设置成他们的默认值,然后完成超类的构造函数任务。

       3. 一旦扩展类声明了自己的任何一个构造函数,就不能继承任何超类构造函数,甚至不能继承无参构造函数。

 

 

 

十三. 通用类型参数和继承:

       1. 一般类型参数的向上绑定:形如<T extends B>的通用类型参数必须作为B类的一个子孙被实例化。

       2. 通用类型的向下绑定:形如<T extends X>的通用类型参数必须作为类型X或类型X的祖先进行实例化。

       3. 结合绑定:几个通用类型参数的绑定可以结合在一起。例如,一个通用类型参数<T extends B super X>

必须以一个既是B的子孙又是X的祖先的类型被实例化。

 

十四.选择一份适当的实现品:

                          1.在各种List中做出抉择:最好的做法可能是以ArrayList作为你的缺省选择,当你发现效能问题处于“在List中心处进行过多的安插和移除动作”,才转而使用LinkedList.当然,如果你所处的是固定数量的一组元素,请使用Array. 

                           2.在各种Sets之间抉择:HashSet的效能通常都优于TreeSet(尤其是最重要的两个动作,安插和查找)。TreeSet存在的唯一理由:它能够维护其内元素的排序状态。所以,只有在你需要这个性质时你才应该使用它。

                  3. 在各种Maps之间抉择:当你使用Map,应该优先选择HashMap;只有当你的Map必须保持排序状态,才需要动用TreeMap.

 

 

十五. 复制二维数组实例;

   protected Connect4 clone()

   {

      Connect4 answer;

      int i;

     

      answer = (Connect4)super.clone();

      //创建data和manyUsed数组的新副本,其中data为二维数组,manyUsed为一维数组

 

      answer.manyUsed = (int[])manyUsed.clone();

      //得到一维的引用,data是一个指向大小为6的数组的引用,data[0]到data[5]是指向二维数组一行的一个引用变量

      answer.date = (int[][])data.clone();

      for(int i = 0;i < ROWS; i++)

      {

         //复制二维数组的每一行

         answer.data[i] = (int[])data[i].clone();

      }

      return answer;

 

   }

 

十六.  每一个类可以有一个main方法。这是对类进行单元测试时的一个常用的技巧。


十七.   导入静态方法和导入静态域有两个实际的应用:
1. 算术函数:如果对Math类使用静态导入,就可以采用更加自然的方式使用算数函数。例如:
sqrt(pow(x,2)+pow(y,2));
看起来比
Math.sqrt(Math.pow(x,2)+Math.pow(y,2));
清晰的多

2. 笨重的常量:如果需要使用大量带有冗长名字的常量,就应该使用静态导入。例如:
if(d.get(DAY_OF_WEEK)==MONTH)
看起来比
if(d.get(Calendar.DAY_OF_WEEK)==Calendar.MONTH)
容易的多


十八.  编写一个完美的equals方法步骤:
1.显示参数命名为otherObject,稍后需要将它转换成另一个叫做Other的变量。
2.检测this和otherObject是否引用同一个对象
if(this == otherObject)return true;
这条语句只是一个优化。实际上,这是一种经常采用的方法。因为计算这个等式要比一个个的比较类中的域所付出的代价下的多。
3.检测otherObject是否为null,如果为null,返回false。这项检测是很必要的。
if(otherObject == null)return false;
比较this与otherObject是否属于同一个类。如果equals的语义在每个子类中有所改变,就使用getClass检测:
if(getClass() != otherObject.getClass())return false;
如果所有的子类都拥有统一的定义,就使用instanceof检测:
if(!(otherObject instanceof ClassName)) return false;
4. 将otherObject转换为相应的类类型变量:
ClassName other = (ClassName)otherObject;
5. 现在开始对所有需要比较的域进行比较了。使用==比较基本类型域,使用equals比较对象域。如果所有的域都匹配,就返回true,否则返回false。
return field1 == other.field1
&& field2.equals(other.field2)
&&...;
如果在子类中重新定义equals,就要在其中包含调用super.equals(other);
OK,大功告成!


十九.  在toString()方法中最好通过getClass().getName()得到类名的字符串,而不要将类名硬加到toString方法中。
例如:
public String toString(){
return getClass().getName() + "[name=" + name + ..."]";
}
如果超类中使用了getClass().getName(),那么在子类中调用super.toString()就可以了,并追加子类独有的属性到super.toString()之后。


二十.  强烈建议为自己编写的每一个类增加toString方法,不仅自己受益,所有使用这个类的程序员也都会受益匪浅。


二十一.  AWT)如果需要往组合框中添加大量的选项,addItem方法性能就太低了。取而代之的是构造一个DefaultComBoxModel,并调用addElement方法进行加载,然后在调用JComBox类中的setModel方法。


二十二.  

 

/*** 
1.红黑树算法
2.集合中出现的错误,很有可能是我们自己不恰当的重写了某些方法而造成的
3.注意LinkList这个比较奇怪的类
4.特别注意可变对象作为以哈希算法检索对象的集合的元素时的使用,很有可能由于错误的修改了对象的Key而造成无法准确的检索对象,建议尽量不要在程序当中修改作为Key的可变对象
5.set系列和map系列的某些类的用法和注意事项非常相似,平时要多加注意
6.对象的强引用和对象的弱引用(weakHashmap中涉及的属于)
7.注意IdentityHashMap的使用
****/

 

 

 

set无序不可重复,map代表键值对,list有序可以重复,java集合部分是面向接口编程的良好实现

简单的说,HashSet集合判断两个元素相等的标准是两个对象通过equals方法比较相等,并且两个对象的HashCode方法返回值也相等。


普通情况,将对象丢进集合里面后,集合会把对象当成是object来处理,为了限制集合里面的对象的类型,需要结合泛型来实现,如果不引入泛型,那么程序当中在某些情况下需要注意进行强制类型转换

Iterator仅仅用于遍历集合当中的元素,无装载对象的能力,获得它的前提是必须有一个被迭代的集合才可以生成Iterator.


当使用Iterator遍历集合元素时,Iterator并不是把集合元素本身传递给迭代变量,二是把集合元素的值传递给迭代器变量,所以修改迭代器变量的值对集合元素本身并没有影响。(与foreach遍历集合的机理相似)

注意在使用Iterator遍历集合元素的时候,集合元素不可以被改变,否则会出现并发修改异常,特别是在多线程并发访问集合时容易引发该异常。(与foreach遍历集合的机理相似)


Iterator采用快速失败机制,一旦在迭代过程中检测到该集合已经被修改(通常是程序中的其他线程的修改),就会立即引发异常,而不是显示修改后的结果,这样可以避免共享资源而引发的潜在的问题

当向hashSet集合当中添加可变对象时,应该格外的小心,如果修改hashSet集合当中的对象,有可能导致该对象与集合中的其他对象相等,从而导致hashSet无法准确的访问该对象,严重的情况还有可能导致内存泄露。hashSet集合是根据对象的hashCode值来检索对象的,就像是数组通过索引来检索数组元素一样。

 

LinkedHashSet是HashSet类的子类,LinkedHashSet需要维护插入元素的顺序,因此性能略低于HashSet,但在迭代访问Set里面的全部元素时将会有很好的性能,因为它以链表来维护内部顺序


HashSet采用哈希算法来决定元素的存储位置,TreeSet采用红黑树的数据结构对元素进行排序

HashSet的内存泄露问题:当一个对象被存储进HashSet集合后,就不要修改那个对象中的那些参与计算Hash码值的字段了,否则,对象修改后的哈希值与最初存储进HashSet集合时的Hash值就不同了,在这种情况下,即使在contains方法使用该对象的当前引用作为参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也将导致无法从HashSet集合中单独删除当前的对象,从而造成内存泄露。

 


需要添加到TreeSet中的元素必须实现Comparable接口,因此向TreeSet中添加的应该是同一个类的对象

 

List集合中提供了ListIterator,它继承了Iterator接口,它既可以向后遍历,也可以向前遍历,而且还可以向集合当中添加新的元素


我们可以把Map看成是一个特殊的Set,只是该Set集合里面的包含的元素是Entey对象,而不是普通的对象(Entry是Map当中的一个内部类,它封装了一个键值对)

 

当使用自定义类作为HashMap和Hashtable的Key时,如果重写该类的equals方法和Hashcode方法,应该保证两个方法的判断标准一致,HashMap和Hashtable以及HashSet对Key的要求完全一样。


jdk1.5增加泛型很大程度上是为了让集合能记住其元素的数据类型

 >>>>>>总的来说,如果你希望某个class可被克隆,你应该执行以下几步:

                           1. 实现Cloneable接口

                           2. 复写Clone()方法

                           3. 于你的clone()中调用super.clone()

                           4. 于你的clone()中捕获异常

这样便可以得到最适宜的结果

 

如果某个base class具备克隆能力,那么它所有的derived class都可以克隆,除非你使用某种机制关闭克隆能力。

 

 如果在系统应用中,List对象需要经常在任意位置插入元素,则可以考虑用LinkedList代替ArrayList,因为ArrayList的底层是数组,当进行插入元素时,插入位置以后的数组元素要进行后移,由于要在内存中进行大量的复制和移动操作,因此对系统的性能影响较大。
 容量参数:在能有效的评估ArrayList数组大小初始值的情况下,指定容量大小能对其性能有较大的提升。
 遍历列表:对ArrayList这些基于数组的实现来说,随机访问(for循环遍历)的速度是很快的。在遍历这些List对象时,可以优先考虑随机访问,但对于LinkedList等基于链表的实现,随机访问可能是非常差的,应避免使用。
 LinkedHashMap可以根据元素最后访问时间进行排序。即,每当使用get()方法访问某一元素时,该元素便被移动到链表的尾端。但是,当LinkedHashMap被迭代器迭代遍历且工作在按照元素访问顺序排序的模式下时,get()方法会修改LinkedHashMap的链表结构,这在迭代器模式下将会会抛出异常。

                      

 

 

 

 

 

 

 

原创粉丝点击