【左京淳的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 - 山本 道子


<<:  How to Reset forgotten root password in RHEL/CentOS 7/8

>>:  (PYTHON)请问 CKIPtagger 如何断"句"

Day 0xD - 解开建立订单回覆的讯息,建立订单的 Amount 要注意

0x1 前言 昨天订单总算建立完成了,今天来把回覆的讯息解开并验证 0x2 处理流程 回覆的结构如下...

[Day 3]开胃沙拉-Python安装及Vagrant虚拟环境架设

在上一篇我们下载完了准备工具後 这篇我们要来开始架设我们的程序环境了 这一篇我们会教大家 如何下载P...

[DAY 12] 幸福时光~手作甜汤

幸福时光~手作甜汤 地点:台南市新营区民权路118号 时间:14:00~20:30 这家店可以看作是...

Day 20:如何撰写测试

今天就书中描述与我个人的开发经验,来谈谈该如何撰写测试吧。有时候我们可能会遇到,软件在开发之初并没有...

[Day 23] - Django-REST-Framework Concrete View Classes 介绍

前言 上一篇中我们学习到了 GenericAPIView 以及 Mixins 的使用,让我们建立 R...