Java基礎——IO編程

I/O編程

I/O

  • 簡單介紹一下Java中的輸入 / 輸出系統
  • 輸入輸出是對於磁盤和內存來說的,程序的運行是在內存中,所以要將數據輸入到內存。處理完畢的數據需要保存,便輸出到磁盤。
  • Java 中將數據的輸入輸出抽象爲流,流是一組有順序的,單向的,有起點和終點的數據集合
  • 按照數據單元分爲字節流和字符流
  1. 字節流:以 8 位(即 1 byte,8 bit)作爲一個數據單元,數據流中最小的數據單元是字節;
    使用InputStream/OutputStream類實現
  2. 字符流:以 16 位(即 1 char,2 byte,16 bit)作爲一個數據單元,數據流中最小的數據單元是字符, Java 中的字符是 Unicode 編碼,一個字符佔用兩個字節
    使用Reader/Writer類實現
    在這裏插入圖片描述
  • Reader/Writer本質上是一個能自動編解碼的InputStream/OutputStream
  • JDK提供的java.io是同步IO,java.nio是異步IO
    在這裏插入圖片描述
  • 我們較常使用的IO類
    在這裏插入圖片描述
  • 如果有興趣可以讀一下源碼
  • 先從目錄/文件說起

File

  • java.io.File表示文件系統的一個文件或者目錄
	// 構造方法
	File f = new File("C:\\Windows\\notepad.exe")	// windows下使用 \\

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

import java.io.File;
import java.io.IOException;

public static void main(String[] args) throws IOException {
	File win = new File("C:\\Windows");
	System.out.println(win.isDirectory()); // true
	File notepad = new File("C:\\Windows\\notepad.exe");
	System.out.println(notepad.isFile()); // true
	File dir = new File("C:\\abc\\xyz");
	System.out.println(dir.mkdir()); // -> mkdirs
	File readme = new File("./src/readme.txt");
	System.out.println(readme.isFile()); // false
	System.out.println(readme.getAbsolutePath());
	System.out.println(readme.getCanonicalPath());
}

InputStream

  • java.io.InputStream是所有輸入流的超類
public static void main(String[] args) throws IOException {
	// 推薦寫法,try(resource)保證流被正確關閉
	try (InputStream input = new FileInputStream("readme.txt")) {// FileInputStream文件流
		int n;
		byte[] buffer = new byte[1000];	// 建立buffer緩衝區
		// 一次儘可能多的將字節讀入緩衝區,但不會超過;需要使用while保證所有字節讀出
		while ((n = input.read(buffer)) != -1) {	
			System.out.println(n);	// 返回字節數
			// System.out.println(buffer);
		}
	}
}

OutputStream

  • java.io.OutputStream是所有輸出流的超類
public static void main(String[] args) throws IOException {
	try (OutputStream output = new FileOutputStream("output.txt")) {// FileOutputStream文件流
		byte[] b1 = "Hello".getBytes("UTF-8");
		output.write(b1);
		byte[] b2 = "你好".getBytes("UTF-8");
		output.write(b2);
	}
}
  • ByteArrayOutputStream/ByteArrayInputStream 在內存中模擬字節流的輸出輸入

Filter模式

  • InputStream可以被繼承從而擴展出很多子類。出現子類爆炸的現象
  • 爲了避免這種情況我們使用組合功能而非繼承的設計模式,稱爲Filter模式(或Decorator)
  • 通過少量的類組合出複雜的功能
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述
  • FileStream與FilterStream相互配合,包裝所需的類
  • 可以參考如下包裝形式理解:
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.GZIPInputStream;

public static void main(String[] args) throws IOException {
	try (InputStream input = new GZIPInputStream(new BufferedInputStream(new FileInputStream("test.txt.gz")))) {// 最後向上轉型爲InputStream
		ByteArrayOutputStream output = new ByteArrayOutputStream();
		byte[] buffer = new byte[1024];
		int n;
		while ((n = input.read(buffer)) != -1) {
			output.write(buffer, 0, n);
		}
		byte[] data = output.toByteArray();
		String text = new String(data, "UTF-8");
		System.out.println(text);
	}
}
  • ZipInputStream是一種FilterInputStream,配合FileInputStream可以直接讀取zip內容
  • ZipOutputStream同理
    在這裏插入圖片描述
  • 下面是ZipStream的基本用法,也是FileStream與FilterStream組合的例子
public static void main(String[] args) throws IOException {
	try (ZipInputStream zip = new ZipInputStream(new BufferedInputStream(new FileInputStream("test.jar")))) {	// 包裝
		ZipEntry entry = null;
		while ((entry = zip.getNextEntry()) != null) {// zip不能向上轉型爲InputStream
			if (entry.isDirectory()) {	// 如果是目錄
				System.out.println("D " + entry.getName());
			} else {
				System.out.println("F " + entry.getName() + " " + entry.getSize());
				printFileContent(zip);
			}
		}
	}
}

static void printFileContent(ZipInputStream zip) throws IOException {	// 打印文件內容
	ByteArrayOutputStream output = new ByteArrayOutputStream();// 內存中模擬字節流
	byte[] buffer = new byte[1024];
	int n;
	while ((n = zip.read(buffer)) != -1) {
		output.write(buffer, 0, n);
	}
	byte[] data = output.toByteArray();
	System.out.println("  size: " + data.length);
}

classpath資源文件

  • Java存放.class的目錄或者jar包可以包含任意類型的其他文件(jar包主要是壓縮的class文件和輔助資源)
  • 從classpath讀取文件可以避免不同環境下(Windows/Linux)文件路徑不一致的問題
  • Class對象的getResourceAsStream()可以從classpath讀取資源
public static void main(String[] args) throws IOException {
	// 從classpath讀取配置文件:
	try (InputStream input = Main.class.getResourceAsStream("/conf.properties")) {// src根目錄下
		if (input != null) {	// 需要檢查返回的InputStream是否爲null
			System.out.println("Read /conf.properties...");
			Properties props = new Properties();// 配置讀取器
			props.load(input);
			System.out.println("name=" + props.getProperty("name"));
		}
	}
	// 從classpath讀取txt文件:
	String data = "/com/hello/sample/data.txt";
	try (InputStream input = Main.class.getResourceAsStream(data)) {
		if (input != null) {	// 要檢查是否爲null
			System.out.println("Read " + data + "...");
			// Java流讀取
			BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8"));
			System.out.println(reader.readLine());
		} else {
			System.out.println("Resource not found: " + data);
		}
	}
}

序列化

  • 序列化是將一個對象變成二進制文件byte[]
  • 可序列化的java對象必須實現java.io.Serializable接口
  • 類似Serializable這樣的空接口被稱爲“標記接口”(marker interface)
  • 反序列化時JVM不調用構造方法
// Main
public static void main(String[] args) throws IOException, ClassNotFoundException {
	String dataFile = "saved.data";
	try (ObjectOutputStream output = new ObjectOutputStream(
			new BufferedOutputStream(new FileOutputStream(dataFile)))) {
		// 依次寫入 int, String, Person:
		output.writeInt(999);
		output.writeUTF("Hello, world!");
		output.writeObject(new Person("Xiao Ming"));
	}
	System.out.println("Read...");
	try (ObjectInputStream input = new ObjectInputStream(new BufferedInputStream(new FileInputStream(dataFile)))) {
		// 依次讀入 int, String, Person:
		System.out.println(input.readInt());
		System.out.println(input.readUTF());
		Person p = (Person) input.readObject();
		System.out.println(p);
	}
}
public class Person implements Serializable {// 必須實現此接口
// 可以藉助IDEA設置serialVersionUID作爲版本號(可選),這樣我們即使刪除了寫入數據的步驟,可以記住UID讀取序列化文件!
	private final String name;

	public Person(String name) {
		System.out.println(name);// 反序列化時不調用構造方法
		this.name = name;
	}
	public String getName() {
		return name;
	}
	@Override
	public String toString() {
		return "(Person: " + name + ")";
	}
}
  • Java的序列化機制適用於Java,如果要與其他語言交互,必須 使用通用的序列化方法例如 json

Reader

  • Reader是所有字符輸入流的超類,它與InputStream的區別:
    在這裏插入圖片描述
    在這裏插入圖片描述
  • Reader實際上是基於InputStream構造的,FileReader內部持有一個FileInputStream
  • 可以通過InputStreamReader()將任意的InputStream轉化爲Reader
public static void main(String[] args) throws IOException {
	// try (Reader reader = new InputStreamReader(new FileInputStream("readme.txt"), "UTF-8"));// 指定編碼
	try (Reader reader = new FileReader("readme.txt")) {// 無法指定編碼
		int n;
		while ((n = reader.read()) != -1) {
			System.out.println((char) n);
		}
	}
}
  • CharArrayReader/CharArrayWriter在內存中模擬字符流輸入輸出
  • 和InputStream一樣也是阻塞(blocking)的

Writer

  • Writer是所有字符輸入流的超類,它與OutputStream的區別:
    在這裏插入圖片描述
  • 好了不想多說類比一下吧!
  • 都要使用try(resource)格式保證正常關閉哦!

時間和日期

  • 需要了解時間戳,時區,計時規則和表示形式。比較簡單百度一下
  • Java中有新舊兩套操作時間日期的類,因爲目前有的程序還在混合使用
// 舊接口
import java.util.Date;
// Main
public class Main {
	public static void main(String[] args) throws Exception {
		// 獲取系統當前時間戳:
		System.out.println(System.currentTimeMillis());// 獲取當前時間戳
		// 獲取當前時間:
		Date now = new Date();
		System.out.println(now);
		// 把Date轉化爲long:
		long t = now.getTime();
		System.out.println(t);
		// 把long轉化爲Date:
		System.out.println(new Date(t));
	}
}

// Date2String
import java.util.Date;

public class Date2String {
	public static void main(String[] args) throws Exception {
		// 獲取當前時間:
		Date now = new Date();
		// 以當前時區打印日期時間:
		System.out.println(now.toString());
		// 以GMT+00:00時區打印日期時間:
		System.out.println(now.toGMTString());
		// 以當前時區+當前Locale打印日期時間:
		System.out.println(now.toLocaleString());	// 國際化
	}
}

// Format
import java.text.SimpleDateFormat;
import java.util.Date;

public class Format {	// 格式化輸出
	public static void main(String[] args) throws Exception {
		// 獲取當前時間:
		Date now = new Date();
		// 指定格式打印:
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// "MM-dd-yyyy HH:mm:ss"
		System.out.println(sdf.format(now));
	}
}

// Parse
import java.text.SimpleDateFormat;
import java.util.Date;

public class Parse {	// 字符串解析爲時間
	public static void main(String[] args) throws Exception {
		// 按系統Locale解析日期時間:
		String s1 = "2016-11-20 12:15:59";
		Date date1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(s1);
		System.out.println(date1);
		// 解析MMM時默認按照系統Locale:
		String s2 = "Nov/20/2016 12:15:59";
		Date date2 = new SimpleDateFormat("MMM/dd/yyyy HH:mm:ss").parse(s2);
		System.out.println(date2);
		// 按ISO 8601標準格式解析:
		String iso = "2016-11-20T12:15:59";
		Date date3 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").parse(iso);
		System.out.println(date3);
	}
}
// Calendar接口 
import java.util.Calendar;

// Main
public class Main {
	public static void main(String[] args) throws Exception {
		// 獲取當前時間表示的Calendar:
		Calendar c = Calendar.getInstance();
		// 轉換爲Date打印:
		System.out.println(c.getTime());
		// 轉換爲long打印:
		System.out.println(c.getTimeInMillis());
		// 獲取年月日時分秒:
		System.out.println("    Year = " + c.get(Calendar.YEAR));
		// 注意月份從0開始:1月=0,2月=1,...,12月=11:
		System.out.println("   Month = " + c.get(Calendar.MONTH));// 月份從0開始,反人類吧!
		System.out.println("     Day = " + c.get(Calendar.DAY_OF_MONTH));
		// 注意星期從1開始:星期日=1,星期一=2,...,星期六=7:
		System.out.println(" Weekday = " + c.get(Calendar.DAY_OF_WEEK));
		System.out.println("    Hour = " + c.get(Calendar.HOUR_OF_DAY));
		System.out.println("  Minute = " + c.get(Calendar.MINUTE));
		System.out.println("  Second = " + c.get(Calendar.SECOND));
		System.out.println("  Millis = " + c.get(Calendar.MILLISECOND));
		// 默認時區:
		System.out.println("TimeZone = " + c.getTimeZone());// Asia/shanghai
	}
}

// setTime
public class SetTime { // 可以設置時間
	public static void main(String[] args) throws Exception {
		// 獲取當前時間表示的Calendar:
		Calendar c = Calendar.getInstance();
		// 轉換爲Date打印:
		System.out.println(c.getTime());
		// 設置爲指定時間:
		c.clear();
		c.set(Calendar.YEAR, 1999);
		c.set(Calendar.MONTH, 10); // 11月
		c.set(Calendar.DAY_OF_MONTH, 30);
		c.set(Calendar.HOUR_OF_DAY, 21);
		System.out.println(c.getTime());
	}
}

// Calculate
public class Calculate {	// 可以運算日期時間
	public static void main(String[] args) throws Exception {
		// 獲取當前時間表示的Calendar:
		Calendar c = Calendar.getInstance();
		// 轉換爲Date打印:
		System.out.println(c.getTime());
		// + 5 days:
		c.add(Calendar.DAY_OF_MONTH, 5);
		// - 2 hours:
		c.add(Calendar.HOUR_OF_DAY, -2);
		// 轉換爲Date打印:
		System.out.println(c.getTime());
	}
}

// 
import java.util.TimeZone;

public class Zone {	// 獲取指定時區的時間
	public static void main(String[] args) throws Exception {
		// 獲取當前時間:
		Calendar c = Calendar.getInstance();
		System.out.println(c.getTime());
		// 獲取紐約時間:
		c.setTimeZone(TimeZone.getTimeZone("America/New_York"));
		int y = c.get(Calendar.YEAR);
		int m = c.get(Calendar.MONTH) + 1;
		int d = c.get(Calendar.DAY_OF_MONTH);
		int hh = c.get(Calendar.HOUR_OF_DAY);
		int mm = c.get(Calendar.MINUTE);
		int ss = c.get(Calendar.SECOND);
		System.out.println(y + "-" + m + "-" + d + " " + hh + ":" + mm + ":" + ss);
	}
}

java.time

在這裏插入圖片描述

// 新接口
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;

public class Main {
	public static void main(String[] args) throws Exception {
		// 獲取當前本地日期:
		LocalDate d1 = LocalDate.now();
		System.out.println(d1);
		System.out.println("Week = " + d1.getDayOfWeek().getValue());
		// 注意11月=11:
		LocalDate d2 = LocalDate.of(2016, 11, 30);
		System.out.println(d2);

		// 獲取當前本地時間:
		LocalTime t1 = LocalTime.now();
		System.out.println(t1);
		LocalTime t2 = LocalTime.of(15, 16, 17);
		System.out.println(t2);

		// 獲取當前本地日期和時間:
		LocalDateTime dt1 = LocalDateTime.now();
		System.out.println(dt1);
		// 用LocalDate和LocalTime組合:
		LocalDateTime dt2 = LocalDateTime.of(d2, t2);
		System.out.println(dt2);
	}
}

// Format
import java.time.format.DateTimeFormatter;

public class Format {
	public static void main(String[] args) {
		// 格式化:
		DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
		System.out.println(dtf.format(LocalDateTime.now()));
		// 按ISO格式解析:
		LocalDateTime dt1 = LocalDateTime.parse("2016-11-30T15:16:17");
		System.out.println(dt1);
		// 按指定格式解析:
		LocalDateTime dt2 = LocalDateTime.parse("2016-11-30 15:16:17", dtf);
		System.out.println(dt2);
	}
}

// Calculate
mport java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Period;
import java.time.temporal.TemporalAdjusters;

public class Calculate {
	public static void main(String[] args) throws Exception {
		// 獲取當前日期和時間:
		LocalDateTime ldt = LocalDateTime.now();
		System.out.println(ldt);
		// + 5 days:
		LocalDateTime ldt2 = ldt.plusDays(5);
		System.out.println(ldt2);
		// - 2 hours:
		LocalDateTime ldt3 = ldt2.minusHours(2);
		System.out.println(ldt3);
		// 獲得當月第一天:
		LocalDate firstDay = LocalDate.now().withDayOfMonth(1);
		LocalDate firstDay2 = LocalDate.now().with(TemporalAdjusters.firstDayOfMonth());
		System.out.println(firstDay.equals(firstDay2));
		System.out.println(firstDay);
		// 獲得當月最後一天:
		LocalDate lastDay = LocalDate.now().with(TemporalAdjusters.lastDayOfMonth());
		System.out.println(lastDay);
		// 獲得當月第一個星期日:
		LocalDate firstSunday = LocalDate.now().with(TemporalAdjusters.firstInMonth(DayOfWeek.SUNDAY));
		System.out.println(firstSunday);
		// 判斷兩個日期哪個在前:
		System.out.println(firstSunday.isBefore(LocalDate.now()));
		// 兩個日期相差?年?月?天:
		Period p = LocalDate.now().until(LocalDate.of(2050, 1, 1));
		System.out.println(p);
		// 兩個日期一共相差多少天:
		System.out.println(LocalDate.of(2050, 1, 1).toEpochDay() - LocalDate.now().toEpochDay());
	}
}
  • 這裏的LocalDateTime不能裝換爲long

ZonedDateTime

在這裏插入圖片描述
在這裏插入圖片描述

  • Instant表示時刻
  • long表示時間戳(秒值)
    在這裏插入圖片描述
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public class Main {
	public static void main(String[] args) throws Exception {
		// 獲取當前默認時區的日期和時間:
		ZonedDateTime now = ZonedDateTime.now();
		System.out.println(now);
		// 打印時區:
		System.out.println(now.getZone());
		// 獲取Instant:
		Instant ins = now.toInstant();
		System.out.println(ins.getEpochSecond());
		// 按指定時區獲取當前日期和時間:
		ZonedDateTime london = ZonedDateTime.now(ZoneId.of("Europe/London")); // 倫敦時間
		System.out.println(london);
		// 把倫敦時間轉換到紐約時間:
		ZonedDateTime newyork = london.withZoneSameInstant(ZoneId.of("America/New_York")); // 紐約時間
		System.out.println(newyork);
	}
}

// Local2Zoned
public class Local2Zoned {	// 轉換時區
	public static void main(String[] args) throws Exception {
		// 把LocalDateTime轉換爲ZonedDateTime:
		LocalDateTime ldt = LocalDateTime.of(2016, 11, 30, 8, 15, 59);
		// 關聯到當前默認時區:
		ZonedDateTime bj = ldt.atZone(ZoneId.systemDefault());
		System.out.println(bj);
		// 關聯到紐約時區:
		ZonedDateTime ny = ldt.atZone(ZoneId.of("America/New_York"));
		System.out.println(ny);
	}
}

public class ChangeZone { // 更改時區
	public static void main(String[] args) {
		// 把LocalDateTime轉換爲ZonedDateTime:
		LocalDateTime ldt = LocalDateTime.of(2016, 11, 30, 8, 15, 59);
		// 關聯到當前默認時區:
		ZonedDateTime bj = ldt.atZone(ZoneId.systemDefault());
		System.out.println(bj);
		// 轉換到紐約時區:
		ZonedDateTime ny = bj.withZoneSameInstant(ZoneId.of("America/New_York"));
		System.out.println(ny);
	}
}
  • 新舊接口的轉換
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述
  • 設置符合不同用戶習慣的時間格式
    在這裏插入圖片描述
  • Locale對象會根據國家常量構建不同的語言環境(主要是格式上)
  • 時間的確定主要是DateTime/long/Instant

單元測試

  • 什麼是單元測試?
    在這裏插入圖片描述
    一個項目往往包含了大量的方法,可能有成千上萬個。如何去保證這些方法產生的結果是我們想要的呢?最容易想到的一個方式,就是我們通過System.out來輸出我們的結果,看看是不是滿足我們的需求,但是項目中總不能在每一個方法中都去輸出一遍非常繁瑣,所以需要單元測試框架
  • 測試驅動開發(TDD)
    在這裏插入圖片描述
  • 測試框架JUnit
    在這裏插入圖片描述

JUnit模塊

  • 特點:
    在這裏插入圖片描述
  • 使用斷言Assert
    在這裏插入圖片描述
  • 使用JUnit需要注意:
    在這裏插入圖片描述

  • 測試之前我們一般要初始化一些資源,稱爲Fixture
  • 需要使用如下方法
    在這裏插入圖片描述
    使用步驟:
  1. 在需要測試的類中使用ctrl+shift+T創建測試類,可能會自動下載一些jar包添加到lib庫
  2. 在IDEA爲我們創建好的測試方法中填寫邏輯
  3. 測試方法一般都爲void類型,必須使用@Test註解
package Hello;

import org.junit.Test;
import static org.junit.Assert.*;
public class RecursionTest {
    @Test
    public void isNumberic() {
        Recursion re = new Recursion();
        boolean x = re.isNumberic("666");
        assertEquals(x,true);
    }
}
  • 我們要了解測試的生命週期
  • 一般情況下,只需將公有的實例化步驟放在@Before註解的方法中即可,一般命名爲setUp()
  • 但如果需要創建數據庫等耗時操作,需要在@BeforeClass註解的方法中執行
  • 順序是:@BeforeClass–>@Before–>@After–>…[@Before–>@After]…–>@AfterClass

測試異常

  • 使用expected測試異常
// 在測試類中
@Test(expected=NumberFormatException.class)
public void testCalcWithNull() {
	calc.calculate(null);	// 如果測試未通過,說明我們傳入null時沒有報此異常,需要修改代碼
}

參數化測試

  • 當我們使用一組參數對相同的方法測試時,可以將參數用方法返回,進行一次性測試,而不用不斷重複
  • 參數必須由靜態方法data()返回,被標記(註解)爲@Parameters,返回類型爲Collection<>
  • 此測試類必須被標記爲@RunWith(parameterized.class)
  • 構造方法參數必須和測試參數(data中定義的參數)一一對應
    在這裏插入圖片描述
  • 相當於測試類會使用構造方法進行實例化,將數據賦值給成員變量
  • 可以通過@Parameter(index)標記public字段,就不用寫構造方法,JUnit會自動創建實例,並把數據注入!
    在這裏插入圖片描述
  • 還可以爲測試設置超時
@Test(timeout=1000)	// 單位是毫秒

超時測試不能取代性能測試和壓力測試

正則表達式

  • 一個正則表達式就是一個描述規則的字符串,編寫正確的規則,就可以通過正則引擎判斷目標字符串是否符合規則
  • 可以應用於任何語言,JDK中使用java.util.regex
  • 規則可以參考博文

注:Java 的正則表達式字符串轉義有兩層次的意義,那就是 Java 字符串轉義出符合正則表達式語法的字符串,所以匹配“ . ”要使用“ \\. ”,Java轉義後交給正則表達式的就是 “\.”。如果要匹配到“\”,就需要使用“\\\\”,因爲要保證Java 程序裏輸出的是 “\\”纔行,交給正則表達時候,規則我們就比較熟悉了!

  • 我們常用分組匹配的方式
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Time {
    /**
     * TODO: 提取合法時間字符串的時,分,秒
     *
     * @param str
     *            Time字符串
     * @return Time對象,包含時,分,秒,或者當字符串不合法時返回null
     */
    // 只編譯一次規則,提高效率
    static Pattern p = Pattern.compile("^([0-9]|[0-1][0-9]|2[0-3])\\:([0-9]|[0-5][0-9])\\:([0-9]|[0-5][0-9])$");
    public static Time parseTime(String str) {
        int h = 0;
        int m = 0;
        int s = 0;
        // FIXME:
        Matcher mt = p.matcher(str);// 正則匹配
        // 可以使用mt.find()實現搜索
        if (mt.matches()) {// 匹配成功
        	// group(0)表示匹配到的所有值
            h = Integer.parseInt(mt.group(1));// 第一個分組匹配到的值
            m = Integer.parseInt(mt.group(2));
            s = Integer.parseInt(mt.group(3));
            return new Time(h, m, s);
        }
        return null;
    }

    private final int hour;
    private final int minute;
    private final int second;

    public Time(int hour, int minute, int second) {// 構造方法
        this.hour = hour;
        this.minute = minute;
        this.second = second;
    }
    public int getHour() {
        return hour;
    }
    public int getMinute() {
        return minute;
    }
    public int getSecond() {
        return second;
    }
    @Override
    public boolean equals(Object o) {
        if (o == this) {
            return true;
        }
        if (o instanceof Time) {
            Time t = (Time) o;
            return t.hour == this.hour && t.minute == this.minute && t.second == this.second;
        }
        return false;
    }
    @Override
    public int hashCode() {
        return Objects.hash(hour, minute, second);
    }
    @Override
    public String toString() {
        return String.format("%02d:%02d:%02d", hour, minute, second);
    }
}

  • 用測試康康
import org.junit.Test;

import static org.junit.Assert.*;

public class TimeTest {

    @Test
    public void parseTime() {
        assertEquals(new Time(0, 0, 0), Time.parseTime("0:0:0"));
        assertEquals(new Time(0, 0, 59), Time.parseTime("0:0:59"));
        assertEquals(new Time(0, 59, 0), Time.parseTime("0:59:0"));
        assertEquals(new Time(5, 0, 9), Time.parseTime("05:00:9"));
        assertEquals(new Time(10, 2, 9), Time.parseTime("10:02:9"));
        assertEquals(new Time(10, 2, 9), Time.parseTime("10:2:09"));
        assertEquals(new Time(23, 0, 1), Time.parseTime("23:0:01"));
        assertEquals(new Time(23, 59, 59), Time.parseTime("23:59:59"));

        assertNull(Time.parseTime("000:00:00"));
        assertNull(Time.parseTime("23:59:60"));
        assertNull(Time.parseTime("23:60:59"));
        assertNull(Time.parseTime("24:00:00"));
        assertNull(Time.parseTime("0:0:A"));
        assertNull(Time.parseTime("11-12-12"));
    }
}
  • 我們可以使用正則表達式實現搜索、替換等等
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public static void main(String[] s) {
	String s = "Roy is   a handsome  boy";
	String r = s.replaceAll("\\s+", " ");
	String w = r.replaceAll("\\w+", "<h1>$1<h1>");	// $1表示匹配到的字符
}

小結

這裏總結了Java的IO編程,主要包括字符字節流,Filter模式,通過classpath獲取資源,序列化對象等。然後介紹了Java中操作時間日期的方法,如何進行代碼測試以及正則表達式的基本使用方法。

部分圖片來源於網絡
部分代碼來自廖雪峯老師的教程
如有不當,煩請指出

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章