Java学习笔记(二)
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()