Java学习笔记(二)

时间:2022-05-17 11:56:57
1. Java抽象类:
  • 抽象类和抽象方法都必须用abstract修饰,而且抽象方法不能有方法体
  • 抽象类不能被实例化,即便这个抽象类没有包含抽象方法
  • 抽象类的构造器不能用于创建实例,只能用于被其子类调用
  • 含有抽象方法的类(包括直接定义一个抽象方法;继承了一个抽象父类但没有完全实现父类包含的抽象方法;以及实现了一个接口,但没有完全实现接口包含的抽象方法三种情况)只能被定义为抽象类
  • 注意:
    • abstract修饰的方法只能被子类复写并实现,而final修饰的方法不允许子类复写,故abstract和final永远不要一起使用
    • abstract不能用于修饰属性和局部变量,也不能用于修饰构造器
  • abstract也不能与private同时使用(private修饰的变量不会被子类重写)
2. 局部内部类、变量只在方法内有效,它的上一级程序单元是方法而不是类,故不能用static和访问控制权限符修饰

3. HashSet:
  •  1 import java.util.*;
     2 
     3 //A类的equals方法总是返回ture,但没有重写切hashCode方法
     4 class A{
     5     public boolean equals(Object obj){
     6         return true;
     7     }
     8 }
     9 
    10 //B类的hashCode方法总是返回1,但没有重写其equals方法
    11 class B{
    12     public int hashCode(){
    13         return 1;
    14     }
    15 }
    16 
    17 //C类的hashCode总是返回2,有重写其equals方法
    18 class C{
    19     public int hashCode(){
    20         return 2;
    21     }
    22 
    23     public boolean equals(Object obj){
    24         return true;
    25     }
    26 }
    27 
    28 public class TestSet 
    29 {
    30     public static void main(String[] args) 
    31     {
    32         HashSet books = new HashSet();
    33         books.add(new A());
    34         books.add(new A());
    35         books.add(new B());
    36         books.add(new B());
    37         books.add(new C());
    38         books.add(new C());
    39         System.out.println(books);
    40     }
    41 }
    42 
    43 
    44 /*
    45 [B@1, B@1, C@2, A@2a139a55, A@15db9742]
    46 请按任意键继续. . . 
    47 */

     

  • HashSet通过同时判断equals和hashCode来区别重复元素(如果equals和hashCode对比返回都是true, 则为重复元素)
  • HashSet储存是无序的,是根据hashCode的值来对应一个储存位置来存储元素(其实hashCode就相当于HashSet的索引,它通过这个索引来找到对应元素,速度很快)
  • 重写hashCode的规则:
    • 当两个对象通过equals方法比较返回true时,这两个对象的hashCode的值应该相等
    • 对象中用作equals比较标准的属性,都应该用来计算hashCode的值
  • 向HashSet中添加一个个可变对象,并改变其值可能会产生的问题:
    •  1 import java.util.*;
       2 class R
       3 {
       4     int count;
       5     public R(int count){
       6         this.count = count;
       7     }
       8 
       9     public String toString(){
      10         return "R(count 属性 :" + count + " )";
      11     }
      12 
      13     public boolean equals(Object obj){
      14         if(obj instanceof R){
      15             R r = (R)obj;
      16             if(r.count == this.count){
      17                 return true;
      18             }
      19         }
      20         return false;
      21     }
      22 
      23     public int hashCode(){
      24         return this.count;
      25     }
      26 }
      27 
      28 
      29 public class TestHashSet 
      30 {
      31     public static void main(String[] args) 
      32     {
      33         HashSet hs = new HashSet();
      34         hs.add(new R(5));
      35         hs.add(new R(-3));
      36         hs.add(new R(9));
      37         hs.add(new R(253));
      38 
      39         //打印HashSet集合
      40         System.out.println(hs);
      41 
      42         //取出第一个元素
      43         Iterator it = hs.iterator();
      44         it.next();
      45         R first = (R)it.next();
      46         //为第一个元素的count赋值
      47         first.count = -3;
      48 
      49         //再次输出HashSet集合,集合有重复元素
      50         System.out.println(hs);
      51 
      52         //删除count为-3的R对象
      53         hs.remove(new R(-3));
      54 
      55         //可以看到被删除了一个R元素
      56         System.out.println(hs);
      57 
      58         //输出false
      59         System.out.println("hs是否包含count为-3的R对象?" + hs.contains(new R(-3)));
      60         //输出false
      61         System.out.println("hs是否包含count为5的R对象?" + hs.contains(new R(5)));
      62     }
      63 }
      64 
      65 /*
      66 [R(count 属性 :-3 ), R(count 属性 :5 ), R(count 属性 :9 ), R(count 属性 :253 )]
      67 [R(count 属性 :-3 ), R(count 属性 :-3 ), R(count 属性 :9 ), R(count 属性 :253 )]
      68 [R(count 属性 :-3 ), R(count 属性 :9 ), R(count 属性 :253 )]
      69 hs是否包含count为-3的R对象?false
      70 hs是否包含count为5的R对象?false
      71 请按任意键继续. . .    
      72 */
    • 说明:
      • 第二个-3虽然值为-3,但它却是放在原来值为-5的位置,删除时不会被删除,而且这个时候equals和hashCode永远不能保持一致,之后这个HashSet的访问将会存在问题
    • 注意:当向HashSet中添加可变对象时,必须十分小心,如果修改HashSet集合中的对象,有可能导致该对象与集合中的其他对象相等,从而导致HashSet无法准确访问该对象

 


4. TreeSet(是SortedSet接口的唯一实现)
  • 自然排序:Java提供了一个Comparable接口,该接口里定义了一个compareTo(Object obj)方法,实现了该接口就可以比较大小,常见类已经实现了该方法,TreeSet根据该方法来进行排序
  • 如果试图将一个对象添加进TreeSet,则该对象的类必须实现Comparable接口,否则程序将会抛出异常(向TreeSet里添加对象时,只有第一个元素可以不实现Comparable接口,但这不是一个好做法,当取出该元素是会抛出ClassCastException)
  • 向TreeSet里添加的也应该是同一类型的对象,否则也会抛出ClassCastException(实现compareTo(Object obj)方法时也应该将obj强制转换为相同类型来进行比较)
  • 判断重复元素的标准:
    • equals方法返回true
    • compareTo方法返回0
  • 如果两个对象的equals方法返回true,则应该保证compareTo方法返回0;

5. LinkedList
  •  1 import java.util.*;
     2 
     3 public class TestLinkedList  
     4 {
     5     public static void main(String[] args) 
     6     {
     7         LinkedList books = new LinkedList();
     8         //将字符串元素加入队列的尾部
     9         books.offer("Robbin1");
    10         //将一个字符串元素入栈
    11         books.push("Robbin2");
    12         //将字符串元素添加到队列的头部
    13         books.offerFirst("Robbin3");
    14         for(int i = 0; i < books.size(); i++){
    15             System.out.println(books.get(i));
    16         }
    17 
    18         //访问并不删除队列的第一个元素
    19         System.out.println(books.peekFirst());
    20         //访问并不删除队列的最后一个元素
    21         System.out.println(books.peekLast());
    22         //采用出栈的方式将第一个元素pop出队列
    23         System.out.println(books.pop());
    24         //下面输出将看到第一个元素被删除
    25         System.out.println(books);
    26         //访问并删除队列的最后一个元素
    27         System.out.println(books.pollLast());
    28         System.out.println(books);
    29     }
    30 }
    31 
    32 /*
    33 Robbin3
    34 Robbin2
    35 Robbin1
    36 Robbin3
    37 Robbin1
    38 Robbin3
    39 [Robbin2, Robbin1]
    40 Robbin1
    41 [Robbin2]
    42 请按任意键继续. . .
    43 */

     

  • LinkedList兼备List,stack和双向队列的功能,一般与ArrayList没有太大性能上的差异,但在一些性能敏感的地方要注意选择
  • 关于List集合的建议:
    • 如果需要遍历List集合元素,对于ArrayList和vector集合,则应该使用随机访问方法(get)来遍历集合元素,这样性能更好。对于LinkedList集合,则应该采用迭代器(Iterator)来遍历集合元素;
    • 如果需要经常执行插入、删除操作来改变List集合的大小,则应该使用LinkedList集合而不是ArrayList。使用ArrayList、vector集合将需要重新分配内部数组大小,其时间开销常常是使用LinkedList的时间开销的几十倍,效果更差;
    • 如果有多条线程同时访问List集合中的元素,可以考录vector这个同步实现;
 
 

6. 操作集合的工具类

排序操作:

    •  1 import java.util.*;
       2 public class TestSort 
       3 {
       4     public static void main(String[] args) 
       5     {
       6         ArrayList nums = new ArrayList();
       7         nums.add(2);
       8         nums.add(-5);
       9         nums.add(3);
      10         nums.add(0);
      11         nums.add(1);
      12 
      13         System.out.println(nums);
      14         //将List集合元素的次序反转
      15         Collections.reverse(nums);
      16         System.out.println(nums);
      17         //将List集合元素按自然排序排序
      18         Collections.sort(nums);
      19         System.out.println(nums);
      20         //将List集合元素按随机顺序排序
      21         Collections.shuffle(nums);
      22         //每次输出的次序不固定
      23         System.out.println(nums);
      24 
      25     }
      26 }
      27 
      28 /*
      29 输出结果:[2, -5, 3, 0, 1]
      30 [1, 0, 3, -5, 2]
      31 [-5, 0, 1, 2, 3]
      32 [2, 1, 0, 3, -5]
      33 请按任意键继续. . .
      34 */
    •   1 import java.util.*;
        2 import java.lang.reflect.Array;
        3 public class ShowHand 
        4 {
        5     //定义该游戏最多支持几个玩家
        6     private final int PLAY_NUM = 5;
        7     //定义扑克牌的所有花色
        8     //下面是四个特殊字符,会在控制台打印出方块,草花, 红心,黑桃
        9     private String[] types = {"\4 ", "\5 ", "\3 ", "\6 "};
       10     private String[] values = {"2 ", "3 ", "4 ", "5 ", "6 ", "7 ", "8 ", "9 ", "10 ", "J ", "Q ", "K ", "A "};
       11 
       12     //cards是一局游戏中剩下的扑克牌
       13     private List<String> cards = new LinkedList<String>();
       14     //定义所有玩家
       15     private String[] players = new String[PLAY_NUM];
       16     //所有玩家手上的扑克牌
       17     private List<String>[] playersCards = new List[PLAY_NUM];
       18 
       19     /**
       20     * 初始化扑克牌,放入52张扑克牌,并且使用shuffle方法将他们按随机顺序排序
       21     */
       22     public void initCards(){
       23         for(int i = 0; i < types.length; i++){
       24             for(int j = 0; j < values.length; j++){
       25                 cards.add(types[i] + values[j]);
       26             }
       27         }
       28         //随机排序
       29         Collections.shuffle(cards);
       30 
       31     }
       32 
       33 
       34     /**
       35     * 初始化玩家,为每个玩家分配用户名
       36     */
       37     public void initPlayer(String... names){
       38         if(names.length > PLAY_NUM || names.length < 2){
       39             //校检玩家数量,此时处理异常更合理
       40             System.out.println("玩家数量不对");
       41             return ;
       42         }else{
       43             //初始化玩家用户名
       44             for(int i = 0; i < names.length; i++){
       45                 players[i] = names[i];
       46             }
       47         }
       48     }
       49 
       50     /**
       51     * 初始化玩家手上的扑克牌,开始游戏时每个玩家手上的扑克牌为空,
       52     * 程序使用一个长度为0的LinkedList表示。
       53     */
       54     public void initPlayerCards(){
       55         for(int i = 0; i < players.length; i++){
       56             if(players[i] != null && !players[i].equals("")){
       57                 playersCards[i] = new LinkedList<String>();
       58             }
       59         }
       60     }
       61 
       62     /**
       63     * 输出全部扑克牌,该方法没有实际作用,仅用作测试
       64     */
       65     public void showAllCards(){
       66         for(String card : cards){
       67             System.out.println(card);
       68         }
       69     }
       70 
       71     /**
       72     * 派扑克牌
       73     * @param first 最先派给谁
       74     */
       75     public void deliverCard(String first){
       76         //调用ArrayUtils工具类的search方法。查询出制定元素在数组中的索引
       77         //int firstPos = ArrayUtils.search(players, first);
       78         int firstPos = 0;
       79         //依次给位于该指定玩家之后的每个玩家派扑克牌
       80         for(int i = firstPos; i < PLAY_NUM; i++){
       81             if(players[i] != null){
       82                 playersCards[i].add(cards.get(0));
       83                 cards.remove(0);
       84             }
       85         }
       86         //依次给位于该指定玩家之前的每个玩家派扑克牌
       87         for(int i = 0; i < firstPos; i++){
       88             if(players[i] != null){
       89                 playersCards[i].add(cards.get(0));
       90                 cards.remove(0);
       91             }
       92         }
       93     }
       94 
       95     /**
       96     * 输出玩家手上的扑克牌
       97     * 实现该方法时,应该控制每个玩家看不到别人的第一张牌, 但此处没有增加该功能
       98     */
       99     public void showPlayerCards(){
      100         for(int i = 0; i < PLAY_NUM; i++){
      101             //当玩家不为空时
      102             if(players[i] != null){
      103                 System.out.print(players[i] + " : ");
      104                 //便利玩家手上的牌
      105                 for(String card : playersCards[i]){
      106                     System.out.print(card + "\t");
      107                 }
      108                 System.out.print("\n");
      109             }
      110         }
      111     }
      112     public static void main(String[] args) 
      113     {
      114         ShowHand sh = new ShowHand();
      115         sh.initPlayer("电脑玩家", "ME");
      116         sh.initCards();
      117         sh.initPlayerCards();
      118         //下面测试所有扑克牌,没有实际作用
      119         sh.showAllCards();
      120         System.out.println("---------------------------------------------------------------------");
      121         //下面从ME开始派牌
      122         sh.deliverCard("ME");
      123         sh.showPlayerCards();
      124 
      125         sh.deliverCard("电脑玩家");
      126         sh.showPlayerCards();
      127     }
      128 }
      129 
      130 
      131 /*
      132  5
      133  2
      134  9
      135  6
      136  K
      137  K
      138  8
      139  7
      140  6
      141  8
      142  4
      143  6
      144  8
      145  A
      146  2
      147  2
      148  4
      149  3
      150  3
      151  J
      152  9
      153  A
      154  7
      155  10
      156  3
      157  Q
      158  6
      159  Q
      160  J
      161  9
      162  4
      163  A
      164  5
      165  4
      166  10
      167  K
      168  10
      169  8
      170  9
      171  10
      172  7
      173  Q
      174  A
      175  K
      176  J
      177  2
      178  5
      179  7
      180  Q
      181  5
      182  3
      183  J 
      184 ---------------------------------------------------------------------
      185 电脑玩家 :  5
      186 ME :  2
      187 电脑玩家 :  5   9
      188 ME:  2         6
      189 请按任意键继续. . .    
      190 */
    • 查找替换操作:
      • static int binarySearch (List list, Object key):使用二分法搜索指定List集合,以获得指定对象在List集合中的索引(如果该方法要能正常工作,必须保证List集合元素已经处于有序状态)
      • static Object max(Collection coll):更具元素的自然顺序,返回给定集合中的最大元素
      • static Object max(Collection coll, Comparator comp):根据指定Comparator产生的顺序,返回指定集合的最大元素
      • static Object min(Collection coll)
      • static Object min(Collection coll, Comparator comp)
      • static void fill(List list, Object obj):使用指定元素obj替换指定List集合中的所有元素
      • static int frequency(Collection c, Object o):返回指定List集合中的指定对象的元素数量
      • static int indexOfSubList(List source, List target):返回List对象在母List对象中第一次出现的位置索引;如果母List中没有出现这样的子List,则返回-1
      • static int lastIndexOfSubList(List source, List target)
      • static boolean replaceAll(List list, Object oldVal, Object newVal):使用一个新值newVal替换List对象所有的旧值oldVal
      •  1 import java.util.*;
         2 public class TestSearch 
         3 {
         4     public static void main(String[] args) 
         5     {
         6         ArrayList nums = new ArrayList();
         7         nums.add(2);
         8         nums.add(-5);
         9         nums.add(3);
        10         nums.add(0);
        11         
        12         System.out.println(nums);
        13         //输出最大元素,将输出3
        14         System.out.println(Collections.max(nums));
        15         //输出最小元素,将输出-5
        16         System.out.println(Collections.min(nums));
        17         //将nums中的0用1来替代
        18         Collections.replaceAll(nums, 0, 1);
        19         System.out.println(nums);
        20         //判断-5在集合nums中出现的次数,将输出1
        21         System.out.println(Collections.frequency(nums, -5));
        22         //对nums集合排序
        23         Collections.sort(nums);
        24         System.out.println(nums);
        25         //只有排序后的List集合才可用二分法查询,输出3
        26         System.out.println(Collections.binarySearch(nums, 3));
        27     }
        28 }
        29 
        30 /*
        31 [2, -5, 3, 0]
        32 3
        33 -5
        34 [2, -5, 3, 1]
        35 1
        36 [-5, 1, 2, 3]
        37 3
        38 请按任意键继续. . .
        39 
        40 */
    • 同步控制:(Java常用集合框架中推荐使用的三么实现类:HashSet、ArrayList和HashMap都是线程不安全的。如果有多条线程访问它们,而且有超过一条的线程试图改变它们,则可能出现错误。Collections提供了多个静态方法用于创建同步集合)

      •  1 import java.util.*;
         2 public class TestSynchronized 
         3 {
         4     public static void main(String[] args) 
         5     {
         6         //下面程序创建了四个同步集合对象
         7         Collection c = Collections.synchronizedCollection(new ArrayList());
         8         List list = Collections.synchronizedList(new ArrayList());
         9         Set s = Collections.synchronizedSet(new HashSet());
        10         Map m = Collections.synchronizedMap(new HashMap());
        11     }
        12 }

         

  • 设置不可变集合:

    • emptyXxx()
    • singletonXxx()
    • unmodifiableXxx()