iT邦幫忙

0

【左京淳的JAVA學習筆記】第七章 API

API(application program interface)是指程式之間具有特定規範的接口。透過這些接口可以引用其他程式來協助完成整體機能。
其實JAVA當中引用package或class的概念也與此相同。以下介紹常見的class及其method:

  • java.lang.Object
  • Date and Time
  • Collection&Lambda式

Object是java中所有class的父類。
裡面提供了一些通用方法

method名 說明
boolean equals(Object obj) 比較是否為同一個物件
final Class<?> getClass() 返回此物件的class
int hashCode() 返回此物件的hashCode值
String toString() 返回物件的描述文字列。

範例:

class Foo {}
class Sample7_1 {
  public static void main(String[] args) {
    int[] ary = {1,2,3};
    Class obj1 = ary.getClass();
    System.out.println(obj1.getName());
    Foo foo = new Foo();
    Class obj2 = foo.getClass();
    System.out.println(obj2.getName());
  }
}

執行結果

[I
Foo

本範例中使用getClass()取得實例化物件的來源class後,再使用getName()方法得到class的名稱。
結果的第一行中,"["表示陣列,"I"則表示int型態。合起來即為儲存int資料的陣列。
Foo則為class名稱。

equals()方法

Object class裡面的equals()方法用來比較兩個物件是否為同一個。然而在String類中,這個方法卻被用來比較值是否相等。
這是因為equals()方法在String類中被覆寫了的關係。
下面幾個範例,比較equals()在String,StringBuilder及Integer類中的不同。

String類的equals()方法

class Sample7_2 {
  public static void main(String[] args) {
    String s1 = "Tom";
    String s2 = new String("Tom");
    String s3 = "tom";
    System.out.println("s1 == s2 : " + (s1 == s2));
    System.out.println("s1.equals(s2) : " + s1.equals(s2));
    System.out.println("s1.equals(s3) : " + s1.equals(s3));
    System.out.println("s1.equalsIgnoreCase(s3) : " + s1.equalsIgnoreCase(s3));
  }
}

執行結果

s1 == s2 : false
s1.equals(s2) : true
s1.equals(s3) : false
s1.equalsIgnoreCase(s3) : true

說明
s1與s2不是同一個物件,返回false
s1與s2的值相同,返回true
s1與s3的大小寫不同,返回false
忽略大小寫,返回true

StringBuilder類的equals()方法

class Sample7_3 {
  public static void main(String[] args) {
    //StringBuilder sb1 = "Tom";   //error
    StringBuilder sb2 = new StringBuilder("Tom");
    StringBuilder sb3 = new StringBuilder("Tom");
    System.out.println("sb2 == sb3  :" + (sb2 == sb3));
    System.out.println("sb2.equals(sb3)  :" + sb2.equals(sb3));
    System.out.println("sb2.toString().equals(sb3.toString())  :" + sb2.toString().equals(sb3.toString()));
  }
}

執行結果

sb2 == sb3  :false
sb2.equals(sb3)  :false
sb2.toString().equals(sb3.toString())  :true

說明
"==" 比較是否為同一物件,傳回false。
StringBuilder的equals()未被覆寫(跟Object類一樣),比較是否為同一物件,傳回false。
要比較StringBuilder的值,可以運用toString()方法轉為String物件後,即可用equals()進行比較,傳回true。

Integer類的equals()方法

class Sample7_4 {
  public static void main(String[] args) {
    Integer val1 = 1;
    Integer val2 = 1;
    System.out.println("val1 == val2  :" + (val1 == val2));
    
    Integer val3 = 150;
    Integer val4 = 150;
    System.out.println("val3 == val4  :" + (val3 == val4));
    System.out.println("val3.equals(val4)  :" + val3.equals(val4));
    
    Integer val5 = new Integer(1);
    System.out.println("val1 == val5  :" + (val1 == val5));
    System.out.println("val1.equals(val5)  :" + val1.equals(val5));
    
    Long val6 = new Long(150);
    Double val7 = new Double(150.0);
    System.out.println("val3.equals(val6)  :" + val3.equals(val6));
    System.out.println("val3.equals(val7)  :" + val3.equals(val7));
    
    System.out.println("val3.equals(val6.intValue())  :" + val3.equals(val6.intValue()));
    System.out.println("val3.equals(val7.intValue())  :" + val3.equals(val7.intValue()));
  }
}

執行結果

val1 == val2  :true
val3 == val4  :false
val3.equals(val4)  :true
val1 == val5  :false
val1.equals(val5)  :true
val3.equals(val6)  :false
val3.equals(val7)  :false
val3.equals(val6.intValue())  :true
val3.equals(val7.intValue())  :true

說明
第一行比較了val1和val2兩個物件是否為同一物件,結果傳回true。
這是因為當參照值為int形式,且值介於-128 ~127之間(儲存空間小於1 byte),則參照此值的物件,都是參照記憶體上相同的位址,即同一物件。
第二行的值為150,超過了1 byte所能保存的範圍,因此不是同一物件。需使用equals()比較值大小,才會傳回true(第三行)。
這表示Integer類和String類的equals()方法一樣,都被覆寫為比較值的方法。
第四行&第五行:使用new()方法新增物件的話,即使值相同也不會被視為同一物件。因此val1和val5不是同物件,但值相同。
第六行以後比較Long,Double及Integer等不同類型的物件,由於無法直接比較,引此傳回false。
利用intValue()方法將Long,Double類轉為Integer類之後即可進行值的比較。

toString()方法

toString()也是Object提供的基本方法之一,會傳回物件的class名+@+hashcode。不過依照各個class需求的不同,通常這方法會被覆寫成其他樣子。
請看以下範例:

class Foo {}
class Bar {
  public String toString(){
    return "This is an object made from Bar.";
  }
}

class Sample7_5 {
  public static void main(String[] args) {
    String obj1 = "Tom";
    StringBuilder obj2 = new StringBuilder("Tom");
    Foo obj3 = new Foo();
    Bar obj4 = new Bar();
    System.out.println(obj1);
    System.out.println(obj2);
    System.out.println(obj3);
    System.out.println(obj4);
  }
}

執行結果

Tom
Tom
Foo@368239c8
This is an object made from Bar.

說明
String和StringBuilder類的toString()都被覆寫過,會傳回其保存的文字列。
沒被覆寫過的toString()會傳回物件的class名+@+hashcode。
也可以自定義要傳回的文字列。

Math class

提供數學計算的一些方法,例如:
int num1 = 100;
int num2 = 200;
int max = int Math.max(num1,num2); //傳回最大值
double randomVal = int Math.random(); //傳回小於1的隨機值

陣列方法

請看以下範例:

import java.util.*;
class Sample7_6 {
  public static void main(String[] args) {
    int[] i_array = {30,10,20,50,40};
    
    //使用arraycopy()方法複製陣列
    int[] copy = new int[3];
    System.arraycopy(i_array,2,copy,0,3);
    for(int val: copy){
      System.out.print(val + " ");
    }System.out.println();
    
    //使用sort()方法排序
    Arrays.sort(i_array);
    for(int val: i_array){
      System.out.print(val + " ");
    }System.out.println();
    
    //使用asList()方法
    String[] s_array = {"Tom","Hill","Cathy"};
    List<String> list = Arrays.asList(s_array);
    //list.add("Mary");
    for(String val: list){
      System.out.print(val + " ");
    }System.out.println();
  }
}

執行結果

20 50 40
10 20 30 40 50
Tom Hill Cathy

說明
arraycopy()方法屬於System類,文法為arraycopy(來源陣列,開始位置,目標陣列,開始位置,複製的長度)
sort()方法屬於Arrays類(需先import),可依內容值的大小重新排列陣列。
asList()方法屬於Arrays類(需先import),可將陣列轉為List(固定長)。由於固定長的List無法增加長度,因此list.add("Mary");這行若執行的話會報錯。
需要可變長的列表時,使用ArrayList如下例:

List<String> list = new ArrayList<>(Arrays.asList(s_array));
list.add("Mary");

執行結果

Tom Hill Cathy Mary

Date and Time API的基本

此類由java.time包提供,特徵如下:

  1. 分別提供日期、時間以及日期+時間等class
  2. Date and Time的class為不可修改的物件,因此在多線程環境下可安全使用。
  3. 提供充足的日期時間計算功能。

java.time包的主要class

  • LocalDate
  • LocalTime
  • LocalDateTime
  • Period(期間)

java.time.format包的主要class

  • DateTimeFomatter(負責日期時間的輸出格式)

來看看日期時間的範例吧:

import java.time.*;
class Sample7_7 {
  public static void main(String[] args) {
    LocalDate dateNow = LocalDate.now();
    LocalTime timeNow = LocalTime.now();
    LocalDateTime dateTimeNow = LocalDateTime.now();
    
    LocalDate dateOf = LocalDate.of(2021,2,25);
    LocalTime TimeOf = LocalTime.of(21,3,20);
    LocalDateTime dateTimeOf = LocalDateTime.of(2021,2,25,21,3,20);
    
    LocalDate dateP = LocalDate.parse("2021-02-25");
    LocalTime TimeP = LocalTime.parse("21:03:20");
    LocalDateTime dateTimeP = LocalDateTime.parse("2021-02-25T21:03:20");
    
    System.out.println("LocalDate.now  :" + dateNow);
    System.out.println("LocalTime.now  :" + timeNow);
    System.out.println("LocalDateTime.now  :" + dateTimeNow);
    
    System.out.println("LocalDate.of  :" + dateOf);
    System.out.println("LocalTime.of  :" + TimeOf);
    System.out.println("LocalDateTime.of  :" + dateTimeOf);
    
    System.out.println("LocalDate.parse  :" + dateP);
    System.out.println("LocalTime.parse  :" + TimeP);
    System.out.println("LocalDateTime.parse  :" + dateTimeP);
  }
}

執行結果

LocalDate.now  :2021-02-25
LocalTime.now  :19:24:11.679136400
LocalDateTime.now  :2021-02-25T19:24:11.679136400
LocalDate.of  :2021-02-25
LocalTime.of  :21:03:20
LocalDateTime.of  :2021-02-25T21:03:20
LocalDate.parse  :2021-02-25
LocalTime.parse  :21:03:20
LocalDateTime.parse  :2021-02-25T21:03:20

日期格式範例

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
class Sample7_8 {
  public static void main(String[] args) {
    LocalDateTime dt1 = LocalDateTime.now();
    
    DateTimeFormatter fmt1 = DateTimeFormatter.ISO_DATE;
    System.out.println("now()  :" + dt1);
    System.out.println("ISO_DATE  :" + fmt1.format(dt1));
    
    DateTimeFormatter fmt2 = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
    String target = "2021/02/25 21:03:20";
    LocalDateTime dt2 = LocalDateTime.parse(target, fmt2);
    System.out.println("ofPattern()  :" + dt2);
  }
}

執行結果

now()  :2021-02-25T19:34:43.799818400
ISO_DATE  :2021-02-25
ofPattern()  :2021-02-25T21:03:20

說明
ISO_DATE是標準的日期格式,使用DateTimeFormatter.format()方法將目標日期修改為標準格式。
自訂格式可用DateTimeFormatter.ofPattern()方法來製作。
再用LocalDateTime.parse(目標日期,自訂格式)來套用格式。

日期時間計算範例

import java.time.LocalDate;
class Sample7_9 {
  public static void main(String[] args) {
    LocalDate date = LocalDate.of(2020,02,25);
    System.out.println("date  :" + date);
    System.out.println("After 3 day  :" + date.plusDays(3));
    System.out.println("After 5 months  :" + date.plusMonths(5));
    System.out.println("After 2 weeks  :" + date.plusWeeks(2));
    System.out.println("After 10 years  :" + date.plusYears(10));
  }
}

執行結果

date  :2020-02-25
After 3 day  :2020-02-28
After 5 months  :2020-07-25
After 2 weeks  :2020-03-10
After 10 years  :2030-02-25

Collection&Lambda式

函數型interface
定義的抽象方法只有一個(包含static及default方法)的interface,稱為函數型interface。

Lambda式
有些class只使用一次而不會在其他地方使用,此時可以不用特別定義class,而採用Lambda式來處理,文法如下:
{引數} -> {處理};
處理後的返回值,會被丟給函數型interface承接。
※函數型interface意指只含有一個抽象方法(或是static,default方法)的interface
請看下例:

import java.util.function.Function;

public class Sample7_10 {
  public static void main(String[] args) {
    Function<String, String> obj = (String str) -> {
      return "Hello " + str;
    };
    String str = obj.apply("Tom");
    System.out.println(str);
  }
}

執行結果

Hello Tom

此範例中,從Function<String, String> obj 開始的三行程式碼,相當於class定義。
在這個匿名class(沒有名字的class)中使用了Lambda式{引數} -> {處理},然後把返回值丟給Function<String, String> obj。
Function是一個JAVA提供的函數型interface,因此裡面只有一個抽象方法叫做apply()。
我們在Lambda式裡面把這個抽象方法給覆寫了,內容就是return "Hello " + str;

因此,當obj.apply()方法被執行時,"Tom"作為引數被丟進方法裡,加工後返回。
※Function<String, String>的意思是Function<引數的型態, 返回值的型態>

JAVA SE8導入了以下幾個函數型interface,有興趣可以google一下。
Function<T,R>
Consumer
Predicate
Supplier
UnaryOperator

另外Lambda式還有各式各樣的簡寫,範例如下:
未簡寫

(String str) ->

省略資料型態(因為interface那邊已經宣告過了)

(str) ->

省略括號(引數只有一個時可省略)

str ->

沒有引數的時候

() ->

接下來看->右邊的簡寫
未簡寫

{
  return "Hello" + str;
}

省略中括號和retrun(處理只有一行時)

"Hello" + str;

簡寫後的範例如下:

import java.util.function.Function;

public class Sample7_10 {
  public static void main(String[] args) {
    //函數型interface<T,R> obj = 引數 -> 處理內容;
    Function<String, String> obj = str -> "Hello " + str;  
    String str = obj.apply("Tom");
    System.out.println(str);
  }
}

再來看看其他函數型interface的使用範例吧

import java.util.*;
import java.util.function.*;

public class Sample7_11 {
  public static void main(String[] args) {
    List<String> words = Arrays.asList("Tom","Mary");
    words.replaceAll( str -> str.toUpperCase());
    System.out.println(words);
  }
}

執行結果

[TOM, MARY]

※replaceAll()是List interface提供的default方法

import java.util.*;
import java.util.function.*;

public class Sample7_12 {
  public static void main(String[] args) {
    List<Integer> data = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5));
    data.removeIf( i -> i % 2 != 0);
    System.out.println(data);
  }
}

執行結果

[2, 4]

※removeIf是Collection提供的default方法

以上是第七章 API的學習心得,下一章會介紹例外的處理。

參考教材: JAVAプログラマSilver SE8 - 山本 道子


尚未有邦友留言

立即登入留言