[Java教程]15.實現可變數組與時間類的使用

Java教程專欄:https://blog.csdn.net/qq_41806966/category_9929686.html

hello,I'm shendi

本節將使用數組實現一個類便於使用,以及學習使用時間類.


目錄

 

封裝數組的類,實現可變長度數組

實現

接下來先從最簡單的開始,獲取(get)

其次簡單的是設置(set)

然後完成添加操作

最後完成刪除操作

時間類

Date類

SimpleDateFormat類

Calendar 類

總結


封裝數組的類,實現可變長度數組

從之前學過的數組,我們知道數組的長度是不可變的,爲了方便(也爲了鍛鍊),自己將對數組的操作封裝成一個工具類,使得長度不能改變的數組變得可以改變.

思路:

我們可以通過創建一個新的數組來改變長度,所以要實現一個對數組封裝的工具類很簡單.

 

分析數組的功能:

數組長度不能變,但是可以改變裏面每一個項的值,每一個項默認都爲默認值.

我們不能獲取/改變超過數組長度的項,會出現錯誤.

用數組的大多數場景都是項很多,而且一般都是未知的.需要循環遍歷等...

 

通過分析,我們的類裏面需要有修改指定內容的方法.

爲了簡單,簡便,我們不希望再有長度固定的概念,而是你的項有多少,就有多長...

所以我們類裏面需要有 添加/刪除 的方法(不需要默認值,刪除掉就沒有這一項了).

我們也需要提供一個獲取項的功能(與數組一樣,超出項現有的數量就報錯(自定義異常沒學,先用輸出代替))


實現

既然是對數組功能的封裝類,類名就叫 MyArray 吧(我的數組)

因爲是工具類,所以我們不用寫 main 函數(供其他類使用)

首先,根據上面的分析,可以知道這個類最少有四個方法 [增加/刪除/設置/獲取]

並且,除了增加,其餘方法都是對指定項進行操作(並不是所有).

所以我們除了增加的方法,其餘的方法都要加上一個參數,用於指定是操作哪一個項.(int index)

我們的增加方法,需要有一個參數,參數內容爲我們需要添加進數組的值.

增加(add),刪除(delete,簡寫爲del),修改也等於設置(set),獲取(get)

我們現在分析一下方法的返回值.

  • 我們的增加方法,因爲是增加,我們不需要方法給我們傳遞什麼信息,所以是無返回值
  • 刪除方法,也不需要返回什麼信息,所以也無返回值
  • 設置方法....無返回值
  • 獲取,我們需要獲取到指定項的值,所以需要一個返回值
  • Object類是所有類的父類(萬物都是Object),所以我們使用Object作爲參數類型,使用的時候需要強轉
    • 後面學到泛型可以更好地處理此操作

所以我們類裏面代碼如下

現在我們就要去實現以下花括號裏的空白了.

在實現之前我們繼續確定需求,我們是要封裝數組,簡便操作...

所以我們肯定需要數組.

數組長度是固定的,所以我們需要知道當前項的數量(而不是數組的長度).

在當前項的數量等於數組的長度的時候,我們就要換新數組了,因爲原數組長度無法滿足我們需求了.

新數組長度肯定比原數組長度大,至於大多少,我們自己設置.


現在有了 數組,數組內有效(項)的數量,數組默認長度和每次換新數組加的長度.

接下來就是分析四個方法具體實現了.

  • 添加
    • 我們添加項到數組內,添加到當前有效項的最後面.
    • 數組長度固定的,有限的,所以我們添加前需要判斷當前有效項和數組長度是否相等,相等我們就換新數組,將原來數組的值拷貝到新數組內
  • 刪除
    • 刪除指定項,如果沒有指定項,那就不進行操作,也不提醒(因爲沒必要,刪除本來就是不需要那個項)
    • 我們刪除指定項,刪除的不是最後一個項就需要將後面的數據往前移動,並且將當前項的數量-1
    • 例如有以下幾個項
      • 1,2,3,4,5,6,7,8
    • 我們把5刪除,那麼現在的項就爲
      • 1,2,3,4,6,7,8
    • 而不是
      • 1,2,3,4,0,6,7,8(0爲默認值)
  • 設置
    • 設置指定項的值,如果沒有此項那麼就提醒,並且不執行後面操作.
    • 也就是賦值操作.
    • 因爲我們類型是Object的,所以什麼類型的都可以往裏面設置(雖然感覺挺方便的,但是你會弄不清楚是什麼類型),所以建議統一類型...
  • 獲取
    • 這個簡單,直接先判斷是否有沒有這個項,有則返回,無則提示

接下來就一個一個進行實現.

首先定義好數組和變量,數組爲Object類型,變量爲int類型(實際數量什麼的)


接下來先從最簡單的開始,獲取(get)


其次簡單的是設置(set)

判斷項是否存在與獲取是一樣的,所以我們可以複製過來.


然後完成添加操作

我們直接將要添加的項放到數組現有值的最後,但是在添加前需要先判斷數組是否超過大小了


最後完成刪除操作

刪除操作代碼比較多,因爲我們在刪除後,需要將後面的項一個個往前移.

我們還需要提供一個獲取當前有效長度大小的方法

最後試一下,看看使用起來感覺如何.

寫一個類,裏面讓用戶輸入字符串,提供增刪改查功能


編譯 MyArray 和 TestArray,運行TestArray看一下結果

現在我們不需要擔心長度問題,因爲這個長度是可以一直增加的,重新測試一下

因爲我們的程序沒有寫退出功能,所以這個時候需要用到快捷鍵 Ctrl + C,強制退出.

下面是兩個類的代碼

MyArray


public class MyArray {

	/**
	 * 數組剛開始的大小爲 10.
	 */
	Object[] objs = new Object[10];
	
	/**
	 * 實際項的數量,剛開始的時候沒有一個項,所以爲0.
	 */
	int num = 0;
	
	/**
	 * 數組超過大小時,增加的大小.
	 */
	int newLength = 5;
	
	/**
	 * 添加進數組的方法
	 */
	public void add(Object obj) {
		// 超過了數組的長度則新建一個數組,並複製
		if (num == objs.length) {
			// 原數組放到另一個變量裏保存起來,然後原數組改成新數組,在原長度上增加.
			Object[] temp = objs;
			objs = new Object[temp.length + newLength];
			// 將 temp 拷貝到原數組(已經被替換成新數組)
			// 使用循環 一個個賦值...(也有更高效的方法,但是現在重點不是這個)
			for (int i = 0;i < temp.length;i++) {
				objs[i] = temp[i];
			}
		}
		// 添加新項,位置爲現有的最後一個
		objs[num] = obj;
		// 現有項 + 1
		num++;
	}

	/**
	 * 刪除指定項的方法, index是這個項在數組內的位置.
	 * 這個地方也可以加個返回值,返回刪除的內容.
	 */
	public void del(int index) {
		// 先判斷是否有指定項,有則刪 無則不操作
		if (index < 0 || index >= num) {
			return;
		}
		// 刪除,直接將此項後面的往前移...
		// 這裏 - 1 是爲了避免在執行下面操作數組越界
		int length = objs.length - 1;
		for (int i = index;i < length;i++) {
			objs[i] = objs[i + 1];	
		}
		// 總數量 - 1
		num--;
	}
	
	/**
	 * 設置指定項的方法, index是把 obj 設置到哪個位置(位置必須存在)
	 */
	public void set(int index,Object obj) {
		// 判斷項是否存在與 get 方法是一樣的,所以複製過來
		// 不同的是沒有返回值,我們直接return,代表後面代碼不執行.
		if (index < 0 || index >= num) {
			System.err.println("你設置的東西沒有了");
			return;
		}
		// 直接將指定位置值設爲傳遞的值
		objs[index] = obj;
	}

	/**
	 * 獲取指定位置的項,返回的是Object類型,因爲我們需要讓這個類可以存所有類型...
	 * 不好的就是,你不知道這個類的具體類型是什麼(使用的時候要強轉,類型不對會出錯).
	 */
	public Object get(int index) {
		// 獲取 index 位置的數,如果不存在,則提醒並返回空.
		// index 小於 0 或者 大於 當前實際的項總數,就代表沒有這個項,提醒並返回空
		if (index < 0 || index >= num) {
			// 之前我們使用的都是 System.out... err代表錯誤輸出(顏色是紅色的)
			System.err.println("你獲取的東西沒有了,給你返回空");
			// 返回後就不會執行下面的代碼了,並且獲取到的值爲 null
			return null;
		}
		// 直接返回.
		return objs[index];
	}
	
	/**
	 * 獲取當前有效大小.
	 */
	public int getSize() {
		return num;
	}
}

TestArray

import java.util.Scanner;

public class TestArray {
	public static void main(String[] args) {
		Scanner	sc = new Scanner(System.in);
	
		// 創建我們自己寫的數組類的對象
		MyArray my = new MyArray();
		while (true) {
			System.out.println("1增,2刪,3改,4查");
			int input = sc.nextInt();
			switch (input) {
			case 1:
				System.out.println("請輸入要保存的數據");
				my.add(sc.next());
				break;
			case 2:
				System.out.println("請輸入要刪除的數據位置(數字)");
				my.del(sc.nextInt());
				break;
			case 3:
				System.out.println("請輸入要設置的數據位置(數字)");
				int index = sc.nextInt();
				System.out.println("請輸入要設置的數據");			
				my.set(index,sc.next());
				break;
			case 4:
				// 這裏直接循環獲取進行輸出
				for (int i = 0;i < my.getSize();i++) {
					System.out.println(i + "\t" + my.get(i));
				}
				break;
			}
		}
	}
}

至此,對數租的掌握程度已經上了一個臺階.


時間類

我們製作軟件的時候難免會使用到時間,像定時鬧鐘,時間顯示,簽到等等,都需要時間.

在 Java 中,提供了 Date 類用於表示時間, Date 位於 Java.util 包中

我們獲取當前時間有以下幾種方法.

  1. 使用 System.currentTimeMillis() 獲取當前時間戳(時間戳就是表示時間的一串數字)
  2. 使用 new Date() 獲取當前時間.
  3. 使用Calendar

我們可以使用一下上面兩種,看看輸出是什麼結果

新建一個類,TestDate 內容如下

輸出如下

上面那個是時間戳,下面那個是Date的輸出形式,可以看出 年月日 時間,只不過是英文的表示形式

現在我們認識一個新的類, SimpleDateFormat,在java.text 包中.

此類可以將 時間戳,Date 轉爲我們熟悉的形式

例如我們要輸出如下

  • 年-月-日 時:分:秒

代碼如下

運行結果如下

Date類

我們每一次執行 new Date() 獲取到的都是當前的時間.

Date類提供了很多方法,但是有些已經棄用了(不推薦使用),經過我自己的測試,發現使用Date的效率比Calendar快很多...

官方文檔介紹(中文翻譯版)

擁有獲取 年,月,日的方法等

需要了解更多可以自行查看官方提供的 API

我這裏也提供鏈接方便下載,1.8中文版的 API

https://download.csdn.net/download/qq_41806966/12518739

SimpleDateFormat類

此類用於將時間轉換爲我們認識的,熟悉的形式.

從上面例子來看,我們創建此類對象,然後才能進行轉換.

在創建的時候,我們有個參數爲字符串的

new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

上面的 yyyy 代表年,MM代表月,dd代表天,HH代表小時,mm代表分鐘,ss代表秒

格式可以自己更改,例如去掉空格等

Calendar 類

因爲 Date 類的大部分方法都被棄用了,所以需要學習使用Calendar類(雖然我感覺Date能滿足我的所有對日期處理的需求)

與 Date 一樣, Calendar也在java.util包下

創建Calendar類的對象需要使用這種方法

通過Calendar.getInstance(); 來獲取此類對象.

我們看一下官方的描述

所述Calendar類是一個抽象類,可以爲在某一特定時刻和一組之間的轉換的方法calendar fields如YEAR , MONTH , DAY_OF_MONTH , HOUR ,等等,以及用於操縱該日曆字段,如獲取的日期下個星期。 時間上的瞬間可以用毫秒值表示,該值是從1970年1月1日00:00 00:00.000 GMT(Gregorian)的Epoch的偏移量。 
該類還提供了用於在包外部實現具體日曆系統的其他字段和方法。 這些字段和方法定義爲protected 。 

與其他區域設置敏感的類一樣, Calendar提供了一種類方法getInstance ,用於獲取此類型的一般有用的對象。 Calendar的getInstance方法返回一個Calendar對象,其日曆字段已使用當前日期和時間進行初始化: 

     Calendar rightNow = Calendar.getInstance();
 Calendar對象可以產生實現特定語言和日曆風格的日期時間格式化所需的所有日曆字段值(例如日語 - 公曆,日語 - 繁體)。 Calendar定義某些日曆字段返回的值的範圍及其含義。 例如,日曆系統第一個月的值爲MONTH == JANUARY爲所有日曆。 其他值由具體的子類定義,如ERA 。 有關詳細信息,請參閱各個實體文檔和子類文檔。 

獲取和設置日曆字段值 
可以通過調用set方法設置日曆字段值。 在Calendar設置的任何字段值將不被解釋,直到它需要計算其時間值(從Epoch的毫秒)或日曆字段的值。 調用get , getTimeInMillis , getTime , add和roll涉及這樣的計算。 

寬大 
Calendar有兩種方式來解釋日曆字段, 寬鬆和不寬泛。 當Calendar處於寬鬆模式時,它接受的日曆字段值範圍比產生的範圍更廣。 當Calendar計算日曆字段值返回get()時,所有日曆字段都被歸一化。 例如,寬鬆的GregorianCalendar解釋MONTH == JANUARY DAY_OF_MONTH == 32爲2月1日。 

當Calendar處於非寬鬆模式時,如果其日曆字段中存在任何不一致,則會拋出異常。 例如, GregorianCalendar總是在1和月份之間生成DAY_OF_MONTH值。 如果設置了超出範圍的字段值,則不寬大的GregorianCalendar會在計算其時間或日曆字段值時拋出異常。 

First Week 
Calendar使用兩個Calendar定義了一個特定地區的七天周:第一週的第一天和第一週的最小天數(從1到7)。 當Calendar時,這些數字取自區域設置資源數據。 也可以通過設置其值的方法來明確指定它們。 
當設置或獲取WEEK_OF_MONTH或WEEK_OF_YEAR字段時, Calendar必須確定月或年的第一週作爲參考點。 一個月或一年的第一週被定義爲最早的七天內開始對getFirstDayOfWeek() ,並含有至少getMinimalDaysInFirstWeek()那一個月或一年的天。 星期編號...,-1,0在第一週之前; 週數爲2,3,...跟隨。 請注意,由get()返回的get()可能不同。 例如,特定的Calendar子類可以指定一年的第1周之前的一週的週一周的n 。 

日曆字段分辨率 
在計算日曆字段的日期和時間時,可能沒有足夠的計算信息(例如只有年月日沒有月份),或可能存在不一致的信息(例如1996年7月15日星期二(格里高利) - 1996年7月15日實際上是星期一)。 Calendar將以Calendar方式解析日曆字段值以確定日期和時間。 
If there is any conflict in calendar field values, Calendar gives priorities to calendar fields that have been set more recently.以下是日曆字段的默認組合。 將使用由最近設置的單個字段確定的最新組合。 

For the date fields : 

 YEAR + MONTH + DAY_OF_MONTH
 YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK
 YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK
 YEAR + DAY_OF_YEAR
 YEAR + DAY_OF_WEEK + WEEK_OF_YEAR
 For the time of day fields : 
 HOUR_OF_DAY
 AM_PM + HOUR
 如果在所選字段組合中沒有設置其值的任何日曆字段, Calendar使用其默認值。 每個字段的默認值可能會因具體的日曆系統而異。 例如, GregorianCalendar ,字段的默認值是相同的時代的開始:即YEAR = 1970 , MONTH = JANUARY , DAY_OF_MONTH = 1 ,等等。 

注意:在某些奇異時期的解釋中存在某些可能的含糊之處,其解決方式如下: 

23:59是一天的最後一分鐘,00:00是第二天的第一分鐘。 因此,一九九九年十二月三十一日(星期三)在二○○○年一月一日(星期一) 
雖然歷史上不太精確,但午夜也屬於“am”,中午屬於“pm”,所以在同一天12:00(午夜)上午12時01分,中午12:00(中午)下午01點 
日期或時間格式字符串不是日曆定義的一部分,因爲這些字符串必須在運行時由用戶修改或覆蓋。 使用DateFormat格式化日期。 

現場操縱 
:日曆字段可以用三種方法來改變set() , add()和roll() 。 
set(f, value)將日曆字段f改爲value 。 此外,它設置內部成員變量以指示日曆字段f已被改變。 儘管日曆字段f立即更改,以毫秒爲單位日曆的時間值不重新計算,直到下一次調用get() , getTime() , getTimeInMillis() , add() ,或roll()而成。 因此,對set()多次調用不會觸發多個不必要的計算。 由於使用set()更改日曆字段,其他日曆字段也可能會根據日曆字段,日曆字段值和日曆系統而更改。 此外,在set計算日曆字段之後, get(f)會返回value設置的調用set方法。 具體細節由具體的日曆類確定。 

示例 :考慮一個GregorianCalendar設置爲1999年8月31日的GregorianCalendar.呼叫set(Calendar.MONTH, Calendar.SEPTEMBER)將日期設置爲1999年9月31日。這是一個臨時內部表示,解析爲1999年10月1日,如果getTime()被調用。 但是,在撥打set(Calendar.DAY_OF_MONTH, 30)之前致電getTime()將日期設置爲1999年9月30日,因爲set()本身後沒有重新set() 。 

add(f, delta)增加delta到現場f 。 這相當於調用set(f, get(f) + delta)進行了兩次調整: 

Add rule 1. The value of field f after the call minus the value of field f before the call is delta, modulo any overflow that has occurred in field f. Overflow occurs when a field value exceeds its range and, as a result, the next larger field is incremented or decremented and the field value is adjusted back into its range.

Add rule 2. If a smaller field is expected to be invariant, but it is impossible for it to be equal to its prior value because of changes in its minimum or maximum after field f is changed or other constraints, such as time zone offset changes, then its value is adjusted to be as close as possible to its expected value. A smaller field represents a smaller unit of time. HOUR is a smaller field than DAY_OF_MONTH. No adjustment is made to smaller fields that are not expected to be invariant. The calendar system determines what fields are expected to be invariant.

此外,與set()不同, add()強制立即重新計算日曆的毫秒數和所有字段。 

示例 :考慮一個GregorianCalendar設置爲1999年8月31日的add(Calendar.MONTH, 13)呼叫add(Calendar.MONTH, 13)將日曆設置爲2000年9月30日。 添加規則1將MONTH字段設置爲9月,因爲在8月份添加13個月將給明年9月。 由於DAY_OF_MONTH不能31月在GregorianCalendar , 增加規則2套DAY_OF_MONTH 30,最接近的可能值。 雖然它是一個較小的領域, DAY_OF_WEEK沒有被規則2調整,因爲預計在GregorianCalendar的月份變化時會改變。 

roll(f, delta)增加delta到現場f不更改更大的字段。 這相當於調用add(f, delta)進行以下調整: 

Roll rule. Larger fields are unchanged after the call. A larger field represents a larger unit of time. DAY_OF_MONTH is a larger field than HOUR.

示例 :參見GregorianCalendar.roll(int, int) 。 

使用模式 。 爲了激勵add()和roll()的行爲,請考慮一個用戶界面組件,其中包含月,日和年的增量和減量按鈕,以及底層的GregorianCalendar 。 如果接口讀取1999年1月31日,用戶按月增量按鈕,應該讀什麼? 如果基礎實施使用set() ,則可能會在1999年3月3日發佈。更好的結果是1999年2月28日。此外,如果用戶再次按下月增加按鈕,應該在1999年3月31日,而不是1999年3月28日通過保存原始日期並使用add()或roll() ,根據是否影響較大的字段,用戶界面可以像大多數用戶一樣直觀地期待。 

從以下版本開始: 
JDK1.1 

我們想要獲取年/月/日的值,需要使用對象的 get方法

參數爲此類的某個字段

例如獲取 天

年月同理, 年 YEAR,月 MONTH

同樣,我們可以設置時間,通過set方法...

更多的參考API. 都是使用的方法,沒有什麼思想.這裏不過多累述

總結

  1. 數組要想改變長度需要創建新數組,然後將原數組複製到新數組
  2. 創建 Date 可以表示當前的時間
  3. 使用System.currentTimeMillis() 獲取當前時間的時間戳表示形式
  4. 使用 SimpleDateFormat 可以將格式變爲我們熟悉的格式
  5. Calendar 類提供了更方便的方法...

下一節我們實現排序.

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