本章内容:

1.字符串与编码
2.定义常量
3.enum枚举类型
4.常用工具类
5.异常处理机制
6.Java集合
7.IO(File类的操作大全)
8.InputStream&OutputStream
9.日期与时间

1.字符串与编码

String【与js,python的字符串操作对比】

在Java中,String是一个引用类型,它本身也是一个class。但是,Java编译器对String有特殊处理,即可以直接用”…”来表示一个字符串。

  1. 字符串比较:比较字符串的内容是否相同。必须使用equals()方法而不能用==
    str1.equals(str2):判断两个字符串的是否相同

  2. 字符索引查找
    字符串.indexOf(“目标字符”)

  3. 判断以什么开头
    字符串.startsWith(“目标字符串”):判断是否以目标字符串开头
    字符串.endWith(“目标字符串”):判断是否以目标目标字符串结尾

  4. 字符串的截取
    字符串.substring(起始索引,结束索引):截取目标字符串,索引从0开始

  5. 去掉首尾空白字符
    字符串.trim()
    字符串.strip():也可以移除字符串首尾空白字符。它和trim()不同的是,类似中文的空格字符\u3000也会被移除

  6. 替换字符串
    字符串.replace(a,b):用字符b替换字符a

  7. 拼接字符串
    拼接字符串使用静态方法String.join,它用指定的字符串连接字符串数组

    1
    2
    String arr = {"A", "B", "C"};
    String s = String.join("***", arr); // "A***B***C"
  8. 基本类型与字符串类型之间的相互转换

要把任意基本类型或引用类型转换为字符串,可以使用静态方法String.valueOf()

1
2
3
4
String.valueOf(123); // "123"
String.valueOf(45.67); // "45.67"
String.valueOf(true); // "true"
String.valueOf(new Object()); // 类似java.lang.Object@636be97c

要把字符串转换为其他类型,就需要根据情况。例如,把字符串转换为int类型

1
2
int n1 = Integer.parseInt("123"); // 123
int n2 = Integer.parseInt("ff", 16); // 按十六进制转换,255

把字符串转换为boolean类型:

1
2
boolean b1 = Boolean.parseBoolean("true"); // true
boolean b2 = Boolean.parseBoolean("FALSE"); // false

把字符串转换为其它基本数据类型
借用包装类的功能

1
2
3
4
int n1 = Integer.parseInt("123"); // 123
boolean b1 = Boolean.parseBoolean("true"); // true
double d1 = Double.parseDouble("3.1415927"); // 3.1415927
...
  1. 是否包含子字符串
    字符串.contains(“目标字符串”):返回结果true 或者 false

  2. 分割字符串
    字符串.split(“指定字符串”)=》数组格式

按指定字符(串)或正则去分割某个字符串,结果以字符串数组形式返回,记住:返回结果是数组,数组无法直接打印

1
2
3
String s = "hello world java!!!";
String[] aa = s.split(" ");
System.out.println(aa[2]);

包装类型:

基本类型===> 对应的引用类型
boolean ===> java.lang.Boolean
byte ===> java.lang.Byte
short ===> java.lang.Short
int ===> java.lang.Integer
long ===> java.lang.Long
float ===> java.lang.Float
double ===> java.lang.Double
char ===> java.lang.Character

拓:

  • public static byte parseByte(String s):将字符串参数转换为对应的byte基本类型。
  • public static short parseShort(String s):将字符串参数转换为对应的short基本类型。
  • public static int parseInt(String s):将字符串参数转换为对应的int基本类型。
  • public static long parseLong(String s):将字符串参数转换为对应的long基本类型。
  • public static float parseFloat(String s):将字符串参数转换为对应的float基本类型。
  • public static double parseDouble(String s):将字符串参数转换为对应的double基本类型。
  • public static boolean parseBoolean(String s):将字符串参数转换为对应的boolean基本类型。

2.定义常量

Java 语言使用 final 关键字来定义一个常量(常量有三种类型:静态常量、成员常量和局部常量。),其语法如下所示:
final dataType variableName = value

1
2
3
4
5
6
7
8
9
10
public class HelloWorld {
// 静态常量
public static final double PI = 3.14;
// 声明成员常量
final int y = 10;
public static void main(String[] args) {
// 声明局部常量
final double x = 3.3;
}
}

3.enum枚举类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Main {
public static void main(String[] args) {
Weekday day = Weekday.SUN;
if (day == Weekday.SAT || day == Weekday.SUN) {
System.out.println("Work at home!");
} else {
System.out.println("Work at office!");
}
}
}

enum Weekday {
SUN, MON, TUE, WED, THU, FRI, SAT;
}

4.常用工具类

Math:进行数学计算

Math.abs()求绝对值
Math.max/min()
Math.pow(a,b)计算a的次方
Math.sqrt()计算开方
Math.exp()计算e的次方
Math.PI 数学常量PI
Math.E 数学常量e
Math.random() 生成随机[0,1)的数

Random:创建伪随机数

1
2
3
4
5
6
7
8
9
10
11
12
// 创建Random实例对象
Random r = new Random(seed); // 种子数只是随机算法的起源数字,和生成的随机数的区间没有任何关系
r.nextInt(); // 2071575453,每次都不一样
r.nextInt(100); // 54,生成一个随机[0,100)的随机整数
r.nextInt(10); // 5,生成一个[0,10)之间的int
r.nextLong(); // 8811649292570369305,每次都不一样
r.nextFloat(); // 0.54335...生成一个[0,1)之间的float
r.nextDouble(); // 0.3716...生成一个[0,1)之间的double

Random rand =new Random(25); // 预设种子数
int i=rand.nextInt(100);
System.out.println(i);

初始化时25并没有起直接作用(注意:不是没有起作用),rand.nextInt(100);中的100是随机数的上限,产生的随机数为0-100的整数,不包括100

对于种子相同的Random对象,生成的随机数序列是一样的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Random ran1 = new Random(10);
System.out.println("使用种子为10的Random对象生成[0,10)内随机整数序列: ");
for (int i = 0; i < 10; i++) {
System.out.print(ran1.nextInt(10) + " ");
}
System.out.println();
Random ran2 = new Random(10);
System.out.println("使用另一个种子为10的Random对象生成[0,10)内随机整数序列: ");
for (int i = 0; i < 10; i++) {
System.out.print(ran2.nextInt(10) + " ");
}

/**
* 输出结果为:
*
* 使用种子为10的Random对象生成[0,10)内随机整数序列:
* 3 0 3 0 6 6 7 8 1 4
* 使用另一个种子为10的Random对象生成[0,10)内随机整数序列:
* 3 0 3 0 6 6 7 8 1 4
*
*/

BigDecimal大数类

float和double只能用来做科学计算或者是工程计算,在商业计算中我们要用 java.math.BigDecimal(大数类的运算效率肯定不如原生类型效率高,代价比较昂贵,需要结合具体的场景来使用)
大数类的传入的值必须为字符串类型

1
2
3
4
5
6
7
8
9
10
11
12
BigDecimal a=new BigDecimal ("4.5");
BigDecimal b=new BigDecimal ("1.5");
a.add(b) // 6.0
add(BigDecimal value):加法,求两个BigDecimal类型数据的和。
subtract(BigDecimal value):减法,求两个BigDecimal类型数据的差。
multiply(BigDecimal  value):乘法,求两个BigDecimal类型数据的积。
divide(BigDecimal divisor):除法,求两个BigDecimal类型数据的商。
remainder(BigDecimal divisor):求余数,求BigDecimal类型数据除以divisor的余数。
max(BigDecimal value):最大数,求两个BigDecimal类型数据的最大值。
min(BigDecimal value):最小数,求两个BigDecimal类型数据的最小值。
abs():绝对值,求BigDecimal类型数据的绝对值。
negate():相反数,求BigDecimal类型数据的相反数。

5.异常处理机制

在Java中,凡是可能抛出异常的语句,都可以用try ... catch捕获。把可能发生异常的语句放在try { … }中,然后使用catch捕获对应的Exception及其子类。

在使用try…catch捕获处理异常时需要注意:
· 不要过度使用异常,不能使用异常处理机制来代替正常的流程控制语句
· 异常捕获时,一定要先捕获小异常,再捕获大异常。否则小异常将无法被捕获
· 避免出现庞大的try块
· 避免使用catch(Exception e){}
· 不要忽略异常

Exception类的常用的几种情况:

  1. IOException有:
    EOFException文件已结束异常;
    FileNotFoundException文件未找到异常。

  2. RuntimeException有:
    IndexOutOfBoundsException索引越界异常;
    ArrayIndexOutOfBoundsException数组索引越界异常;
    ArithmeticException算数条件异常,譬如:整数除零等;
    NullPointerException空指针异常;
    NegativeArraySizeException数组长度为负异常;
    ArrayStoreException数组存储异常,当向数组中存放非数组声明类型对象时抛出;
    SecurityException违背安全原则异常;
    IllegalArgumentException非法参数异常;
    ClassCastException类型强制转换异常。
    ReflectiveOperationException有:
    ClassNotFoundException找不到类异常;
    NoSuchMethodException方法未找到异常;
    NoSuchFieldException找不到属性异常。

  3. 错误类Error:
    一般是指与虚拟机相关的问题,如系统崩溃、虚拟机错误、动态链接失败
    等,这种错误无法恢复或不可能捕获,将导致应用程序中断。所以不应该试图使用catch块来捕捉Error和不能在throws子句中声明该方法可能抛出Error及其任何子类

多catch语句

多catch语句:简单来说,多个catch语句只有一个能被执行 可以使用多个catch语句,每个catch分别捕获对应的Exception及其子类。JVM在捕获到异常后,会从上到下匹配catch语句,匹配到某个catch后,执行catch代码块,然后不再继续匹配。

finally语句

无论是否有异常发生,程序语句最终都要执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class HelloWorld{
public static void main(String[] args) {
try {
process1();
process2();
process3();
} catch (UnsupportedEncodingException e) {
System.out.println("Bad encoding");
} catch (IOException e) {
System.out.println("IO error");
} finally {
System.out.println("END");
}
}
}

6.集合

collection接口

Java的集合与数组都是为了处理一类数据,但Java数组也存在很大的缺陷:

  • 数组初始化后大小不可变;
  • 数组只能按索引顺序存取。

Java标准库自带的java.util包提供了集合类:Collection,它是除Map外所有其他集合类的根接口。Java的java.util包主要提供了以下三种类型的集合:

  • List接口:一种有序列表的集合,允许存储重复的元素,具体实现的类有ArrayList集合,LinkedList集合,Vector集合;
  • Set接口:一种保证没有重复元素的集合,且没有索引(不能使用普通的for循环遍历集合),具体实现的类有TreeSet集合,HashSet集合,LinkedHashSet集合;
  • Map:一种通过键值(key-value)查找的映射表集合。

6.1 List接口

创建List:

1
2
3
4
5
List<String> list = new ArrayList<>(); // 只能放入String类型
List<Integer> list1 = new ArrayList<>(); // 只能放入Integer类型
List<Character> list2 = new ArrayList<>(); // 只能放入Character类型
拓展:根据给定的元素快速创建List
List<Integer> list3 = List.of(1, 2, 5);

collection接口的List接口的共性方法

1
2
3
4
5
6
在末尾添加一个元素:void add(E e)
在指定索引添加一个元素:void add(int index, E e)
删除指定索引的元素:int remove(int index)
删除某个元素:int remove(Object e)
获取指定索引的元素:E get(int index)
获取链表大小(包含元素的个数):int size()

List接口特点:

  1. 它是一个元素存取有序的集合。例如,存元素的顺序是11、22、33。那么集合中,元素的存储就是按照11、22、33的顺序完成的)。
  2. 它是一个带有索引的集合,通过索引就可以精确的操作集合中的元素(与数组的索引是一个道理)。
  3. 集合中可以有重复的元素,通过元素的equals方法,来比较是否为重复的元素。

ArrayList集合

对于ArrayList来说,有一个尖括号代表泛型(泛型:也就是装在集合中的所有元素,全部都是统一的什么类型。注意,泛型只能是引用数据类型,不能是基本类型

java.util.ArrayList集合数据存储的结构是数组结构。元素增删慢,查找快,由于日常开发中使用最多的功能为查询数据、遍历数据,所以ArrayList是最常用的集合。

许多程序员开发时非常随意地使用ArrayList完成任何需求,并不严谨,这种用法是不提倡的。

LinkedList集合

java.util.LinkedList集合数据存储的结构是链表结构。方便元素添加、删除的集合。

LinkedList是一个双向链表,那么双向链表是什么样子的呢,我们用个图了解下

实际开发中对一个集合元素的添加与删除经常涉及到首尾操作,而LinkedList提供了大量首尾操作的方法。这些方法我们作为了解即可:

  • public void addFirst(E e):将指定元素插入此列表的开头。
  • public void addLast(E e):将指定元素添加到此列表的结尾。
  • public E getFirst():返回此列表的第一个元素。
  • public E getLast():返回此列表的最后一个元素。
  • public E removeFirst():移除并返回此列表的第一个元素。
  • public E removeLast():移除并返回此列表的最后一个元素。
  • public E pop():从此列表所表示的堆栈处弹出一个元素。
  • public void push(E e):将元素推入此列表所表示的堆栈。
  • public boolean isEmpty():如果列表不包含元素,则返回true。

LinkedList是List的子类,List中的方法LinkedList都是可以使用,这里就不做详细介绍,我们只需要了解LinkedList的特有方法即可。在开发时,LinkedList集合也可以作为堆栈,队列的结构使用。(了解即可)

6.2 Set接口

java.util.Set接口和java.util.List接口一样,同样继承自Collection接口,它与Collection接口中的方法基本一致,并没有对Collection接口进行功能上的扩充,只是比Collection接口更加严格了。与List接口不同的是,Set接口中元素无序,并且都会以某种规则保证存入的元素不出现重复。

tips: Set集合取出元素的方式可以采用:迭代器,增强for循环

HashSet集合介绍

HashSet是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存取和查找性能。保证元素唯一性的方式依赖于:hashCodeequals方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class HashSetDemo {
public static void main(String[] args) {
//创建 Set集合
HashSet<String> set = new HashSet<String>();

//添加元素
set.add(new String("cba"));
set.add("abc");
set.add("bac");
set.add("cba");
//遍历
for (String name : set) {
System.out.println(name);
}
}
}

输出结果如下,说明集合中不能存储重复元素:

1
2
3
cba
abc
bac

tips:根据结果我们发现字符串”cba”只存储了一个,也就是说重复的元素set集合不存储。

LinkedHashSet集合

我们知道HashSet保证元素唯一,可是元素存放进去是没有顺序的,那么我们要保证有序,怎么办呢?

在HashSet下面有一个子类java.util.LinkedHashSet,它是链表和哈希表组合的一个数据存储结构。

演示代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class LinkedHashSetDemo {
public static void main(String[] args) {
Set<String> set = new LinkedHashSet<String>();
set.add("bbb");
set.add("aaa");
set.add("abc");
set.add("bbc");
Iterator<String> it = set.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
}
结果:
bbb
aaa
abc
bbc

6.3 Map接口

HashMap

前面已经讲解了List接口下的ArrayList类,接下来我们开始了解Map接口的HashMap类(Map<K, V>是一种键-值映射表,Map中不存在重复的key,因为放入相同的key,只会把原有的key-value对应的value给替换掉。
创建HashMap类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Map<键Key数据类型, 值Value的数据类型> map = new HashMap<>();

public class Main {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("apple", 123);
map.put("pear", 456);
map.put("banana", 789);
// for each循环遍历键所组成的集合
for (String key : map.keySet()) {
Integer value = map.get(key);
System.out.println(key + " = " + value);
}
}
}

collection接口的Map接口的共性方法

1
2
3
4
5
map.put(key,value):在末尾添加一组键值对
map.get(key):通过指定的key值获取相应的value值
map.containerKey(key):查询某个Key是否存在
map.containerValue(value):查询某个value是否存在
map.size():获取Map中键值对的元素个数

关于HashMap的小结:

  • 对Map来说,要遍历key可以使用for each循环遍历Map实例的keySet()方法返回的Set集合,它包含不重复的key的集合
  • 同时遍历key和value可以使用for each循环遍历Map对象的entrySet()集合,它包含每一个key-value映射

6.4 Iterator迭代器接口

collection接口下有各种各样的集合,不同的集合存储方式不同,相应的集合取值方式也不同,这时就需要Iterator迭代器接口了。
我们要始终坚持使用迭代器Iterator来访问List。Iterator本身也是一个对象,但它是由List的实例调用iterator()方法的时候创建的。Iterator对象知道如何遍历一个List,并且不同的List类型,返回的Iterator对象实现也是不同的,但总是具有最高的访问效率。

Iterator对象有两个方法:boolean hasNext()判断是否有下一个元素,E next()返回下一个元素。因此,使用Iterator遍历List代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
import java.util.Iterator;
import java.util.List;


public class Main {
public static void main(String[] args) {
List<String> list = List.of("apple", "pear", "banana");
for (Iterator<String> it = list.iterator(); it.hasNext(); ) {
String s = it.next();
System.out.println(s);
}
}
}

童鞋可能觉得使用Iterator访问List的代码比使用索引更复杂。但是,要记住,通过Iterator遍历List永远是最高效的方式。并且,由于Iterator遍历是如此常用,所以,Java的for each循环本身就可以帮我们使用Iterator遍历

1
2
3
4
5
6
7
8
public class Main {
public static void main(String[] args) {
List<String> list = List.of("apple", "pear", "banana");
for (String s : list) {
System.out.println(s);
}
}
}

案例: 找出一组整数缺失的数值案例
给定一组整数,找出缺失的数字:这里推荐使用集合,因为不确定整数的数据多少,而Java中的数组个数无法改变

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import java.util.*;

public class Main {

public static int findMissingNumber(int start, int end, List<Integer> list) {
// 对等差数列进行求和 Sn=na1+n(n-1)d/2或Sn=n(a1+an)/2
int total = ((end+start)*(end-start + 1))/2;

int max = 0;
// 对list集合进行求和
for(Integer p : list)

max+= p;
// 将两个集合之和做差,即可知晓缺失的是那个数
return total - max;
}

public static void main(String[] args) {
// 构造从start到end的序列:
final int start = 10;
final int end = 20;
List<Integer> list = new ArrayList<>();
for (int i = start; i <= end; i++) {
list.add(i);
}
// 随机删除List中的一个元素:
int removed = list.remove((int) (Math.random() * list.size()));
int found = findMissingNumber(start, end, list);
System.out.println(list.toString());
System.out.println("missing number: " + found);
System.out.println(removed == found ? "测试成功" : "测试失败");
}
}

6.5 集合的综合运用:斗地主游戏

游戏原理示意图:

且每次运行后的发牌结果都不相同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package com.bytedance;

import java.util.ArrayList;
import java.util.List;
import java.util.Collections;

public class Pokers {
public static void main(String[] args) {
/*
* 1.准备牌:定义一个集合来存储54张扑克牌
* */
List<String> pokers = new ArrayList<>();
// 定义两个数组或集合来分别存储花色与数字
String[] colors = {"♥","♣","♦","♠"};
String[] numbers = {"2","A","K","Q","J","10","9","8","7","6","5","4","3"};
// 将两组数组两两嵌套,组成一幅扑克牌
for (String color:colors) {
for (String number:numbers) {
pokers.add(color+number);
}
}
// 再把大王,小王存储到pokers集合中,这就组成了一幅完整的牌了
pokers.add("redJoker");
pokers.add("blackJoker");

/*
* 2.将完整的扑克牌就行洗牌,利用集合静态方法Collections.shuffle(英文就有洗牌之意)
* */
Collections.shuffle(pokers);

/*
* 3.发牌:建立四个集合,分别存储四个玩家的牌:
* 遍历pokers集合,获取每一张牌
* 使用pokers集合的索引%3 个玩家轮流发牌
* 剩余三张牌给底牌,注意:先判断底牌索引(是否i>=51),否则牌就发完了
* */

// 定义四个集合来存储玩家的牌和底牌
List<String> play01 = new ArrayList<>();
List<String> play02 = new ArrayList<>();
List<String> play03 = new ArrayList<>();
List<String> diPai = new ArrayList<>();

for(int i = 0;i<pokers.size();i++){
// 获取每一张牌
String p = pokers.get(i);
// 轮流发牌
if(i>=51){
diPai.add(p);
}else if (i%3 == 0){
play01.add(p);
}else if (i%3 == 1){
play02.add(p);
}else {
play03.add(p);
}

}

/*
* 4.看牌:可以遍历也可以直接打印
* */
System.out.println("周星驰:"+play01);
System.out.println("刘德华:"+play02);
System.out.println("郑伊健:"+play03);
System.out.println("底牌:"+diPai);
}
}

集合的综合运用:斗地主游戏(二)
上述在完成斗地主游戏时,实现了基本洗牌,发牌功能,但是对玩家手中的牌却没有进行排序处理,比较散乱,因此需要做出改进,利用Map接口的HashMap类的键值对特性,利用键值来找到相应的value值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package com.bytedance;

import java.util.*;

public class Pokers {

public static void look(String name,ArrayList<Integer> player,HashMap<Integer,String> pokers){
System.out.print(name+":");
// 遍历ArrayList集合,获取元素,作为建,到Map集合中查找值
for (Integer key:player) {
String value = pokers.get(key);
System.out.print(value+" ");
}
// 打印换行
System.out.println();
}

public static void main(String[] args) {
/*
* 1.准备牌:定义一个集合来存储54张扑克牌
* */
// 创建map集合,键值对
HashMap<Integer, String> pokers = new HashMap<>();
// 创建List集合来存储数字编号
List<Integer> pokerNumber = new ArrayList<>();
// 定义两个数组或集合来分别存储花色与数字
String[] colors = {"♥","♣","♦","♠"};
String[] numbers = {"2","A","K","Q","J","10","9","8","7","6","5","4","3"};
// 初始索引为2,因为大小王会占据索引值0,1
int index = 2;
// 将两组数组两两嵌套,组成一幅扑克牌
for (String number:numbers) {
for (String color:colors) {
pokers.put(index,color+number);
pokerNumber.add(index);
index++;
}
}
// 再把大王,小王存储到pokers集合中,这就组成了一幅完整的牌了
pokers.put(0,"redJoker");
pokerNumber.add(0);
pokers.put(1,"blackJoker");
pokerNumber.add(1);

/*
* 2.将完整的扑克牌就行洗牌,利用集合静态方法Collections.shuffle(英文就有洗牌之意)
* */
Collections.shuffle(pokerNumber);

/*
* 3.发牌:建立四个集合,分别存储四个玩家的牌:
* 遍历pokers集合,获取每一张牌
* 使用pokers集合的索引%3 个玩家轮流发牌
* 剩余三张牌给底牌,注意:先判断底牌索引(是否i>=51),否则牌就发完了
* */

// 定义四个集合来存储玩家的牌和底牌
ArrayList<Integer> player1 = new ArrayList<Integer>();
ArrayList<Integer> player2 = new ArrayList<Integer>();
ArrayList<Integer> player3 = new ArrayList<Integer>();
ArrayList<Integer> bottom = new ArrayList<Integer>();


for(int i = 0;i<pokerNumber.size();i++){
// 获取每一张牌
int p = pokerNumber.get(i);
// 轮流发牌
// 前三张作为底牌
if(i<3){
bottom.add(p);
}else if (i%3 == 0){
player1.add(p);
}else if (i%3 == 1){
player2.add(p);
}else {
player3.add(p);
}

}

// 对玩家手中的编号进行排序
Collections.sort(player1);
Collections.sort(player2);
Collections.sort(player3);

/*
* 4.看牌:就是将玩家手中的编号放到map集合中查找,根据键值对
* */
look("周润发",player1, pokers);
look("郭富城",player2, pokers);
look("刘德华",player3, pokers);
look("底牌",bottom, pokers);
}
}

7.IO,【File类的操作大全】

Java的标准库java.io提供了File对象来操作文件和目录

        我们从之前的知识可以推出java把电脑中的文件和文件夹(目录)封装为了一个File类,我们可以使用File类对文件和文件夹进行操作,并且类中存在构造函数,构造函数里传入的值为文件的路径。要构造一个file对象,需要传入文件路径:构造File对象时,可以是绝对路径(以根目录开头的完整路径),也可以是相对路径

1
2
3
4
5
6
public class Main{
public static void main(String[] args){
File f = new File('文件路径');
System.out.println(f);
}
}

注意Windows平台使用\作为路径分隔符,在Java字符串中需要用\\表示一个\。Linux平台使用/作为路径分隔符:

1
2
linux: File f = new File("/usr/bin/javac");
windows: File f = new File("C:\\Windows\\notepad.exe");

7.1 File类的静态方法

由之前的Java静态方法的专题学习中已经了解到静态方法与静态属性的调用方法:类名称.方法名,类名称.属性名。因此下列的静态方法可以直接使用File类调用

  • static String pathSeparator 与系统有关的路径分隔符(即环境变量的路径分割符),为了方便,它被表示为一个字符串。
  • static char pathSeparatorChar 与系统有关的路径分隔符(即环境变量的路径分割符)。
  • static String separator 与系统有关的默认名称分隔符,为了方便,它被表示为一个字符串。
  • static char separatorChar 与系统有关的默认名称分隔符。
1
2
3
4
5
6
7
8
9
10
11
/*
操作路径:路径不能写死了(动态路径)
C:\develop\a\a.txt windows
C:/develop/a/a.txt linux
"C:"+File.separator+"develop"+File.separator+"a"+File.separator+"a.txt"
*/
String pathSeparator = File.pathSeparator;
System.out.println(pathSeparator);//路径分隔符 windows:分号; linux:冒号:

String separator = File.separator;
System.out.println(separator);// 文件名称分隔符 windows:反斜杠\ linux:正斜杠/

7.2 文件和目录

        File对象既可以表示文件,也可以表示目录。特别要注意的是,构造一个File对象,即使传入的文件或目录不存在,代码也不会出错,因为构造一个File对象,并不会导致任何磁盘操作。只有当我们调用File对象的某些方法的时候,才真正进行磁盘操作。

相关的操作方法:用File对象获取到一个文件时,还可以进一步判断文件的权限和大小

  • isFile() : 判断File对象是否存在,即文件是否已经存在
  • isDirectory():判断该File对象是否是一个已存在的目录
  • boolean canRead():是否可读
  • boolean canWrite():是否可写
  • boolean canExecute():是否可执行
  • long length():文件字节大小
  • exists() : 判断文件或目录是否存在
  • createNewFile():当且仅当原文件不存在时才会创建一个新文件(createNewFile声明抛出了IOException,我们调用这个方法,就必须的处理这个异常,要么throws,要么try catch)
  • mkdir():创建一个文件夹或者说目录
  • mkdirs():既可以创建单级目录也可以创建多级目录
  • getAbsolutePath() :返回此File的绝对路径名字符串。
  • getPath() :将此File转换为路径名字符串。
  • getName() :返回由此File表示的文件或目录的名称。
  • delete():删除该文件或目录(直接在硬盘上删除,不走回收站,无法找回,需要谨慎
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import io.*;

public class Main {
public static void main(String[] args) throws IOException {
/*
* 1.调用isFile(),判断该File对象是否是一个已存在的文件,
* 2.调用isDirectory(),判断该File对象是否是一个已存在的目录
*/
File f1 = new File("C:\\Windows");
File f2 = new File("C:\\Windows\\notepad.exe");
File f3 = new File("C:\\Windows\\nothing");
System.out.println(f1.isFile());
System.out.println(f1.isDirectory());
System.out.println(f2.isFile());
System.out.println(f2.isDirectory());
System.out.println(f3.isFile());
System.out.println(f3.isDirectory());
System.out.println("========");
File file = new File("C:\\Users\\CHD\\Desktop\\abc.txt");
if (file.createNewFile()) {
// 文件创建成功:
// TODO:
// file.delete();// 删除文件成功:
System.out.println(file.isFile()); // true
}
}
}

7.3 路径

  • 绝对路径:是一个完整的路径
    以盘符(c:,D:)开始的路径
    c:\a.txt
    C:\Users\itcast\IdeaProjects\shungyuan\123.txt
    D:\demo\b.txt

  • 相对路径:是一个简化的路径,相对路径指的是相对于当前项目的根目录(C:\Users\itcast\IdeaProjects\shungyuan)
    如果使用当前项目的根目录,路径可以简化书写
    C:\Users\itcast\IdeaProjects\shungyuan\123.txt–>简化为: 123.txt(可以省略项目的根目录)

注意:
1.路径是不区分大小写
2.路径中的文件名称分隔符windows使用反斜杠,反斜杠是转义字符,两个反斜杠代表一个普通的反斜杠

PS:java不同于Python,HTML引入文件可以使用. :当前文件.. :当前文件的上一级目录,Java的相对路径是以当前的项目的根目录为准

7.4 遍历文件和目录

File类遍历(文件夹)目录功能

  • public String[] list() :返回一个String数组,表示该File目录中的所有子文件或目录(相对路径)。
  • public File[] listFiles() :返回一个File数组,表示该File目录中的所有的子文件或目录(绝对路径)。

实例一:查找桌面上的所有文件,并过滤掉不想要的文件或目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class Main {
public static void main(String[] args) throws IOException {
File f = new File("C:\\Users\\CHD\\Desktop");
File[] fs1 = f.listFiles(); // 列出所有文件和子目录
printFiles(fs1);
File[] fs2 = f.listFiles(new FilenameFilter() { // 仅列出.exe文件
public boolean accept(File dir, String name) {
return name.endsWith(".txt"); // 返回true表示接受该文件
}
});
printFiles(fs2);
}

static void printFiles(File[] files) {
System.out.println("==========");
if (files != null) {
for (File f : files) {
System.out.println(f);
}
}
System.out.println("==========");
}
private static void show01() {
File file = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan\\08_FileAndRecursion");
String[] arr = file.list();
for (String fileName : arr) {
System.out.println(fileName);
}
}
}

需求:遍历c:\abc文件夹,及abc文件夹的子文件夹,并只要.java结尾的文件

c:\abc
c:\abc\abc.txt
c:\abc\abc.java
c:\abc\a
c:\abc\a\a.jpg
c:\abc\a\a.java
c:\abc\b
c:\abc\b\b.java
c:\abc\b\b.txt

方法一:递归遍历所有的文件或目录,并删除指定的文件
注意:先判断每一个File对象是否是文件夹,再将File对象转换为String数据类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class Demo05Recurison {
public static void main(String[] args) {
File file = new File("c:\\abc");
getAllFile(file);
}

public static void getAllFile(File dir){
//System.out.println(dir);//打印被遍历的目录名称
File[] files = dir.listFiles();
for (File f : files) {
//对遍历得到的File对象f进行判断,判断是否是文件夹
if(f.isDirectory()){
//f是一个文件夹,则继续遍历这个文件夹,我们发现getAllFile方法就是传递文件夹,遍历文件夹的方法,所以直接调用getAllFile方法即可:递归(自己调用自己)
getAllFile(f);
}else{
// 将File对象转换为字符串
//String name = f.getName();//abc.java
//String path = f.getPath();//c:\\abc\\abc.java
//String s = f.toString();//c:\\abc\\abc.java

//把字符串,转换为小写
//s = s.toLowerCase();

//2.调用String类中的方法endsWith判断字符串是否是以.java结尾
//boolean b = s.endsWith(".java");

//3.如果是以.java结尾的文件,则输出
/*if(b){
System.out.println(f);
}*/

if(f.getName().toLowerCase().endsWith(".java")){ //函数式编程,一步到位
System.out.println(f);
}
}
}
}
}

方法二:文件过滤器接口

在File类中有两个和ListFiles重载的方法,方法的参数传递的就是过滤器

  • File[] listFiles(FileFilter filter)
    java.io.FileFilter接口:用于抽象路径名(File对象)的过滤器。
    作用:用来过滤文件(File对象)
    抽象方法:用来过滤文件的方法

    boolean accept(File pathname) 测试指定抽象路径名是否应该包含在某个路径名列表中。
    参数:
        File pathname:使用ListFiles方法遍历目录,得到的每一个文件对象
  • File[] listFiles(FilenameFilter filter)
    java.io.FilenameFilter接口:实现此接口的类实例可用于过滤器文件名。
    作用:用于过滤文件名称
    抽象方法:用来过滤文件的方法

    boolean accept(File dir, String name) 测试指定文件是否应该包含在某一文件列表中。
    参数:
        File dir:构造方法中传递的被遍历的目录
        String name:使用ListFiles方法遍历目录,获取的每一个文件/文件夹的名称

注意:
两个过滤器接口是没有实现类的,需要我们自己写实现类,重写过滤的方法accept,在方法中自己定义过滤的规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class Demo01Filter {
public static void main(String[] args) {
File file = new File("c:\\abc");
getAllFile(file);
}

/*
定义一个方法,参数传递File类型的目录
方法中对目录进行遍历
*/
public static void getAllFile(File dir){
File[] files = dir.listFiles(new FileFilterImpl());//传递过滤器对象
for (File f : files) {
//对遍历得到的File对象f进行判断,判断是否是文件夹
if(f.isDirectory()){
//f是一个文件夹,则继续遍历这个文件夹
//我们发现getAllFile方法就是传递文件夹,遍历文件夹的方法
//所以直接调用getAllFile方法即可:递归(自己调用自己)
getAllFile(f);
}else{
//f是一个文件,直接打印即可
System.out.println(f);
}
}
}
}

/*
创建过滤器FileFilter的实现类,重写过滤方法accept,定义过滤规则
*/
class FileFilterImpl implements FileFilter{
@Override
public boolean accept(File pathname) {
/*
过滤的规则:
在accept方法中,判断File对象是否是以.java结尾
是就返回true
不是就返回false
*/
//如果pathname是一个文件夹,返回true,继续遍历这个文件夹
if(pathname.isDirectory()){
return true;
}

return pathname.getName().toLowerCase().endsWith(".java");
}
}

方法三:匿名内部类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class Demo02Filter {
public static void main(String[] args) {
File file = new File("c:\\abc");
getAllFile(file);
}

/*
定义一个方法,参数传递File类型的目录
方法中对目录进行遍历
*/
public static void getAllFile(File dir){
//传递过滤器对象 使用匿名内部类
/*File[] files = dir.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
//过滤规则,pathname是文件夹或者是.java结尾的文件返回true
return pathname.isDirectory() || pathname.getName().toLowerCase().endsWith(".java");
}
});*/

//使用Lambda表达式优化匿名内部类(接口中只有一个抽象方法)
File[] files = dir.listFiles((File pathname)->{
return pathname.isDirectory() || pathname.getName().toLowerCase().endsWith(".java");
});

for (File f : files) {
//对遍历得到的File对象f进行判断,判断是否是文件夹
if(f.isDirectory()){
//f是一个文件夹,则继续遍历这个文件夹
//我们发现getAllFile方法就是传递文件夹,遍历文件夹的方法
//所以直接调用getAllFile方法即可:递归(自己调用自己)
getAllFile(f);
}else{
//f是一个文件,直接打印即可
System.out.println(f);
}
}
}
}

方法四:lambda表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class Demo02Filter {
public static void main(String[] args) {
File file = new File("c:\\abc");
getAllFile(file);
}

/*
定义一个方法,参数传递File类型的目录
方法中对目录进行遍历
*/
public static void getAllFile(File dir){
//使用Lambda表达式优化匿名内部类(接口中只有一个抽象方法)
File[] files = dir.listFiles((File pathname)->{
return pathname.isDirectory() || pathname.getName().toLowerCase().endsWith(".java");
});

for (File f : files) {
//对遍历得到的File对象f进行判断,判断是否是文件夹
if(f.isDirectory()){
//f是一个文件夹,则继续遍历这个文件夹
//我们发现getAllFile方法就是传递文件夹,遍历文件夹的方法
//所以直接调用getAllFile方法即可:递归(自己调用自己)
getAllFile(f);
}else{
//f是一个文件,直接打印即可
System.out.println(f);
}
}
}
}

7.5 Path

Java标准库还提供了一个Path对象,它位于java.nio.file包。Path对象和File对象类似,但操作更加简单。如果需要对目录进行复杂的拼接、遍历等操作,使用Path对象更方便:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Main {
public static void main(String[] args) throws IOException {
Path p1 = Paths.get(".", "project", "study"); // 构造一个Path对象
System.out.println(p1); // .\project\study
Path p2 = p1.toAbsolutePath(); // 转换为绝对路径
System.out.println(p2); // D:\data\demo2\.\project\study
Path p3 = p2.normalize(); // 转换为规范路径
System.out.println(p3); // D:\data\demo2\project\study
File f = p3.toFile(); // 转换为File对象
System.out.println(f); // D:\data\demo2\project\study
for (Path p : Paths.get("..").toAbsolutePath()) { // 可以直接遍历Path
System.out.println(" " + p);
}
}
}

8.InputStream与OutputStream

InputStream就是Java标准库提供的最基本的输入流。它位于java.io这个包里。java.io包提供了所有同步IO的功能。

要特别注意的一点是,InputStream并不是一个接口,而是一个抽象类,它是所有输入流的超类。这个抽象类定义的一个最重要的方法就是int read()

9.日期与时间类

我们再来看一下Java标准库提供的API。Java标准库有两套处理日期和时间的API:

  • 定义在java.util这个包里面,主要包括Date、Calendar和TimeZone这几个类(这里面有很多方法已经过时,废弃了,会报出warning);
  • 新的API是在Java 8引入的,定义在java.time这个包里面,主要包括LocalDateTime、ZonedDateTime、ZoneId等(推荐使用这个API)

9.1 Date类

Date类目前已经过时废弃了很多方法,仅推荐getTime()方法:把日期对象转换成对应的时间毫秒值

1
2
3
Date date = new Date();
// 1586141801899(十三位数,毫秒级,与js的时间戳相同)
System.out.println(date.getTime());

9.2 Calendar

Calendar可以用于获取并设置年、月、日、时、分、秒,它和Date比,主要多了一个可以做简单的日期和时间运算的功能

概念

日历我们都见过,java.util.Calendar是日历类,在Date后出现,替换掉了许多Date的方法。该类将所有可能用到的时间信息封装为静态成员变量,方便获取。日历类就是方便获取各个时间属性的。

获取方式

Calendar为抽象类,由于语言敏感性,Calendar类在创建对象时并非直接创建,而是通过静态方法创建,返回子类对象,如下:

  1. Calendar静态方法
  • public static Calendar getInstance():使用默认时区和语言环境获得一个日历

例如:

1
2
3
4
5
6
7
import java.util.Calendar;

public class Demo06CalendarInit {
public static void main(String[] args) {
Calendar cal = Calendar.getInstance();
}
}
  1. 常用方法

根据Calendar类的API文档,常用方法有:

  • public int get(int field):返回给定日历字段的值。
  • public void set(int field, int value):将给定的日历字段设置为给定值。
  • public abstract void add(int field, int amount):根据日历的规则,为给定的日历字段添加或减去指定的时间量。
  • public Date getTime():返回一个表示此Calendar时间值(从历元到现在的毫秒偏移量)的Date对象。

Calendar类中提供很多成员常量,代表给定的日历字段:

字段值 含义
YEAR
MONTH 月(从0开始,可以+1使用)
DAY_OF_MONTH 月中的天(几号)
HOUR 时(12小时制)
HOUR_OF_DAY 时(24小时制)
MINUTE
SECOND
DAY_OF_WEEK 周中的天(周几,周日为1,可以-1使用)

  • get/set方法

get方法用来获取指定字段的值,代码使用演示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.util.Calendar;

public class CalendarUtil {
public static void main(String[] args) {
// 创建Calendar对象
Calendar cal = Calendar.getInstance();
// 设置年
int year = cal.get(Calendar.YEAR);
// 设置月
int month = cal.get(Calendar.MONTH) + 1;
// 设置日
int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH);
System.out.print(year + "年" + month + "月" + dayOfMonth + "日");
// 2020年4月6日
}
}

set方法用来设置指定字段的值,代码使用演示:

1
2
3
4
5
6
7
8
9
import java.util.Calendar;

public class Demo07CalendarMethod {
public static void main(String[] args) {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, 2020);
System.out.print(year + "年" + month + "月" + dayOfMonth + "日"); // 2020年1月17日
}
}
  • add方法

add方法可以对指定日历字段的值进行加减操作,如果第二个参数为正数则加上偏移量,如果为负数则减去偏移量。代码如:

1
2
3
4
5
6
7
8
9
10
11
12
import java.util.Calendar;

public class Demo08CalendarMethod {
public static void main(String[] args) {
Calendar cal = Calendar.getInstance();
System.out.print(year + "年" + month + "月" + dayOfMonth + "日"); // 2018年1月17日
// 使用add方法
cal.add(Calendar.DAY_OF_MONTH, 2); // 加2天
cal.add(Calendar.YEAR, -3); // 减3年
System.out.print(year + "年" + month + "月" + dayOfMonth + "日"); // 2015年1月18日;
}
}
  • getTime方法

Calendar中的getTime方法并不是获取毫秒时刻,而是拿到对应的Date对象。

1
2
3
4
5
6
7
8
9
10
import java.util.Calendar;
import java.util.Date;

public class Demo09CalendarMethod {
public static void main(String[] args) {
Calendar cal = Calendar.getInstance();
Date date = cal.getTime();
System.out.println(date); // Tue Jan 16 16:03:09 CST 2018
}
}

小贴士:西方星期的开始为周日,中国为周一。在Calendar类中,月份的表示是以0-11代表1-12月。日期是有大小关系的,时间靠后,时间越大。

从Java 8开始,java.time包提供了新的日期和时间API,主要涉及的类型有:

  1. 本地日期和时间:LocalDateTime,LocalDate,LocalTime;
  2. 带时区的日期和时间:ZonedDateTime;
  3. 时刻:Instant;
  4. 时区:ZoneId,ZoneOffset;
  5. 时间间隔:Duration。
  6. 以及一套新的用于取代SimpleDateFormat的格式化类型DateTimeFormatter。

和旧的API相比,新API严格区分了时刻、本地日期、本地时间和带时区的日期时间,并且,对日期和时间进行运算更加方便。

此外,新API修正了旧API不合理的常量设计:

Month的范围用1~12表示1月到12月;
Week的范围用1~7表示周一到周日。
最后,新API的类型几乎全部是不变类型(和String类似),可以放心使用不必担心被修改。

9.3 DateFormat类

2020-4-6 新增:DateFormat类

java.text.DateFormat 是日期/时间格式化子类的抽象类,我们通过这个类可以帮我们完成日期和文本之间的转换,也就是可以在Date对象与String对象之间进行来回转换。

  • 格式化:按照指定的格式,从Date对象转换为String对象。
  • 解析:按照指定的格式,从String对象转换为Date对象。

①. 构造方法

由于DateFormat为抽象类,不能直接使用,所以需要常用的子类java.text.SimpleDateFormat。这个类需要一个模式(格式)来指定格式化或解析的标准。构造方法为:

public SimpleDateFormat(String pattern):用给定的模式和默认语言环境的日期格式符号构造SimpleDateFormat。

参数pattern是一个字符串,代表日期时间的自定义格式。

②. 格式规则

常用的格式规则为:

标识字母(区分大小写) 含义
y
M
d
H
m
s

③. 常用方法

DateFormat类的常用方法有:

  • public String format(Date date):将Date对象格式化为字符串。
  • public Date parse(String source):将字符串解析为Date对象。
format方法

使用format方法的代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
/*
把Date对象转换成String
*/
public class Demo03DateFormatMethod {
public static void main(String[] args) {
// 获取当前的日期对象
Date date = new Date();
// 创建日期格式化对象,在获取格式化对象时可以指定风格
DateFormat df = new SimpleDateFormat(""yyyy-MM-dd HH:mm:ss"");
String str = df.format(date);
System.out.println(str); // 2020-04-06 10:23:54
}
}
parse方法

使用parse方法的代码为:把目标String转换成相应的Date对象

1
2
3
4
5
6
7
8
9
10
11
12
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Demo04DateFormatMethod {
public static void main(String[] args) throws ParseException {
DateFormat df = new SimpleDateFormat("yyyy年MM月dd日");
Date date = df.parse("2018年12月11日");
System.out.println(date); // Tue Dec 11 00:00:00 CST 2018
}
}

10.System类

java.lang.System类中提供了大量的静态方法,可以获取与系统相关的信息或系统级操作,在System类的API文档中,常用的方法有:

  • public static long currentTimeMillis():返回以毫秒为单位的当前时间。
  • public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length):将数组中指定的数据拷贝到另一个数组中。

案例展示:验证for循环打印数字1-9999所需要使用的时间(毫秒)

1
2
3
4
5
6
7
8
9
10
public class SystemTest1 {
public static void main(String[] args) {
long start = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
System.out.println(i);
}
long end = System.currentTimeMillis();
System.out.println("共耗时毫秒:" + (end - start));
}
}

参考文章


 评论

联系我 | Contact with me

Copyright © 2019-2020 谁知你知我,我知你知深。此恨经年深,比情度日久

博客内容遵循 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议