07 Java常用类

151 阅读29分钟

泛型

泛型,即“参数化类型”。就是将类型由原来的具体的类型参数化,类似于方法中变量参数,此时类型也就定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。

泛型类

public class ClassName<T> {
    private T data;

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}

泛型接口

public interface InterfaceName<T> {
    T getData();
}

实现接口时,可以选择制定泛型类型,也可以选择不指定,如下:

// 指定类型
public class Interface1 implements InterfaceName<String> {
    private String text;
    @Override
    public String getData() {
        return text;
    }
}
// 不指定类型
public class Interface<T> implements InterfaceName<T> {
    private T data;
    @Override
    public T getData() {
        return data;
    }
}

泛型方法

private static <T> T 方法名(T a, T b) {}
public class Demo {
    public static void main(String[] args) {
        print(123);
    }
    public static <A> void print(A a) {
        System.out.println(a);
    }
}

泛型限制类型

在使用泛型时,可以指定泛型的限定区域。

例如:必须是某某类的子类 或 某某接口的实现类,格式:

<T extends 类或接口1 & 接口2>

public class Demo {
    public static void main(String[] args) {
        Plate<Apple> p = new Plate<>();
    }
}

interface Fruit{}
class Apple implements Fruit {}
class Plate<T extends Fruit> {
    T data;
}

泛型中的通配符 ?

泛型通配符是使用?代替方法具体的类型实参。

  1. <? extends Parent>指定类泛型类型的上届
  2. <? super Child>指定了泛型类型的下届
  3. <?>指定类没有限制的泛型类型
public class Demo {
    public static void main(String[] args) {
        // 错误!不能讲一个装着苹果的盘子,看作一个装着水果的盘子
        Plate<Fruit> p = new Plate<Apple>();
        // 修改成
        Plate<? extends Fruit> p = new Plate<Apple>();
        // 下层限定
        Plate<? super Apple> p = new Plate<Fruit>();
        
    }
}

interface Fruit{}
class Apple implements Fruit {}
class Plate<T extends Fruit> {
    T data;
}

作用

  1. 提高代码复用率。
  2. 泛型中类型在使用时指定,不需要强制类型转换(类型安全,编译器会检查类型)。

注意

在编译之后程序会采取去泛型化的措施。

也就是说Java中的泛型,只在编译阶段有效。

在编译过程中,正确检验泛型结果后,会将泛型信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不回进入到运行时阶段。

Objects

import java.util.Objects;

public class Person {
    private String name;

    @Override
    public boolean equals(Object o) {
        if(this == o) return true;
        if(o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name);
    }
}
public static void main(String[] args) {
    Person p1 = null;
    Person p2 = new Person();
    // 异常
    // System.out.print(p1.equals(p2));
    System.out.print(Objects.equals(p1, p2));
}

Math

Math.abs(-100);
Math.min(100, 200);

Arrays

BigDecimal

0.1 + 0.2 不回精准的等于 0.3

public static void main(String[] args) {
    BigDecimal b1 = new BigDecimal("0.1");
    BigDecimal b2 = new BigDecimal("0.2");
    BigDecimal b3 = b1.add(b2);
}

常用方法

下述所有的运算方法,不会影响参与运算的数据本身,运算的结果会被封装卫一个新的BigDecimal对象,这个对象会通过return返回出去。

public BigDecimal add(BigDecimal augend)	加法运算
public BigDecimal subtract(BigDecimal augend)	减法运算
public BigDecimal multiply(BigDecimal augend)	乘法运算
public BigDecimal divide(BigDecimal augend)	除法运算
...

Date

public static void main(String[] args) {
    // 创建一个当前时间的Date
    Date date = new Date();

    System.out.println(date.getTime());

    
}

DateFormat

public class Demo {

    public static void main(String[] args) {
        // DateFormat
        /**
        *	y	:	年
        *	M	:	月
        *	d	:	日
        *	H	:	时
        *	m	:	分
        *	s	:	秒
        *
        *	2020年12月12日 12:12	50
        */
        SimpleDateFormat format = new SimpleDateFormate("yyyy-MM-dd HH:mm ss");
        // format:将date对象 转换为字符串(yyyy-MM-dd HH:mm ss)
        String text = format.format(new Date());
        System.out.println(text);
        // parse: 将yyyy-MM-dd HH:mm ss 日期字符串,转换为date对象
        format.parse("2021-12-12 12:12 12");
    }
    
}

Calendar

public static void main(String[] args) {
    // Calendar
    Calendar cl = Calendar.getInstance();
    c1.set(Calender.YEAR, 2021);
    int year = c1.get(Calendar.YEAR);
    int day = c1.get(Calendar.DAY_OF_YEAR);
    System.out.println(year);
    System.out.println(day);
    // 运算
    // set
    // get
    // add
    // getTime : 获取日历时间 表示的Date对象
    // getActualMaxmum
    Calendar cl = Calendar.getInstance();
    cl.add(Calendar.MONTH, 3);
    c1.get(Calendar.MONTH);


    c1.set(Calendar.MonTH, 4);
    int m = cl.getActualMaximum(Calendar.DAY_OF_MONTH);
    System.out.println(m);
}

System

1. 字符串相关的类

String的特性

  • String类:代表字符串。
  • String是一个final类,代表不可变的字符序列。
  • Serializable:可序列化的接口。凡是实现此接口的类的对象可以通过网络或本地流进行数据传输。
  • Comparable:凡是实现此接口的类,其对象都可以比较大小。
  • 字符串是常量,用双引号引起来表示。它们的值在创建之后不能更改。
  • String对象的字符串内容是存储在一个字符数组value[]中的。
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];
    
    /** Cache the hash code for the string */
	private int hash;	// Default to 0
}
  • private final char value[]; // 存储字符串数据的容器
    • final:知名此value数组一旦初始化,其地址就不可变。
  • jdk9开始:为了节省内存空间,做了优化
    • private final byte[] value; // 存储字符串数据的容器

String对象的创建

String str = "hello";

// 本质上this.value = new char[0];
String s1 = new String();

// this.value = original.value;
String s2 = new String(String original);

// this.value = Arrays.copyOf(value, value.length);
String s3 = new String(char[] a);

String s4 = new String(char[] a, int startIndex, int count);

String str1 ="abc";与String str2 = new String("abc");的区别?

  • 字符串常量存储在字符串常量池,目的是共享。
  • 字符串非常量对象存储在堆中。

字符串常量的存储位置

  • 字符串常量都存储在字符串常量池(StringTable)中。
  • 字符串常量池不允许存放两个相同的字符串常量。
  • 字符串常量池,在不同的jdk版本中,存放位置不同。
    • jdk7之前:字符串常量池存放在方法区
    • jdk7及之后:字符串常量池存放在堆空间。

字符串对象时如何存储的

Person p1 = new Person("Tom", 12);
Person p2 = new Person("Tom", 12);

System.out.println(p1.name == p2.name);	// true

图片

String s1 = "hello";
String s2 = "world";
String s3 = "hello" + "world";
String s4 = s1 + "world";
String s5 = s1 + s2;
String s6 = (s1 + s2).intern();
System.out.println(s3==s4);	// false
System.out.println(s3==s5);	// false
System.out.println(s4==s5);	// false
System.out.println(s3==s6);	// true

图片

结论:

  • 常量与常量的拼接结果在常量池。且常量池中不会存在相同内容的常量。
  • 只要其中有一个是变量,结果就在堆中
  • 如果拼接的结果调用intern()方法,返回值就在常量池中

String实例化的两种方式

  1. String s1 = "hello";
  2. String s2 = new String("hello");

String使用陷阱

  • String s1 = "a"
    说明:在字符串常量池中创建了一个字面量为“a”的字符串。 
  • s1 = s1 + "b"
    说明:实际上原来的“a”字符串对象已经丢弃了,现在在堆空间中产生了一个字符串s1 + "b"(也就是“ab”)。如果多次执行这些改变串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率。如果这样的操作放到循环中,会极大影响程序的性能。 
  • String s2 = "a" + "b"
    说明:直接在字符串常量池中创建一个字面量为“ab”的字符串。 
  • String s3 = "a" + "b"
    说明:s3指向字符串常量池中已经创建的“ab”的字符串。 
  • String s4 = s1.intern();
    说明:堆空间的s1对象在调用intern()之后,会将常量池中已经存在的“ab”字符串赋值给s4。 

String连接操作

情况1:常量 + 常量:结果仍然存储在字符串常量池中。

情况2:常量 + 变量 或 变量 + 变量:都会通过new的方式创建一个新的字符串,返回堆空间中此字符串对象的地址。

情况3:调用字符串的intern():返回的是字符串常量池中字面量的地址。

String常用方法1

  • int length():返回字符串的长度:return value.length
  • char charAt(int index):返回某索引出的字符:return value[index]
  • boolean isEmpty():判断是否是空字符串:return value.length == 0
  • String toLowerCase():使用默认语言环境,将String中的所有字符转换为小写
  • String toUpperCase():使用默认语言环境,将String中的所有字符串转换为大写
  • String trim():返回字符串的副本,忽略前导空白和尾部空白
  • boolean equals(Object obj):比较字符串的内容是否相同
  • boolean equalsIgnoreCase(String anotherString):与equals方法类似,忽略大小写
  • String concat(String str):将制定字符串连接到此字符串的结尾。等价于用"+"
  • int compareTo(String anotherString):比较两个字符串的大小
  • String substring(int beginIndex):返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串。
  • String substring(int beginIndex, int endIndex):返回一个新字符串,它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。

String常用方法2

  • boolean endsWith(String suffix):测试此字符串是否以指定的后缀结束
  • boolean startsWith(String prefix):测试此字符串是否以指定的前缀开始
  • boolean startsWith(String prefix, int toffset):测试此字符串从指定索引开始的子字符串是否以指定前缀开始
  • boolean contains(CharSequence s):当且仅当此字符串包含指定的char值序列时,返回true
  • int indexOf(String str):返回指定子字符串在此字符串中第一次出现处的索引
  • int indexOf(String str, int fromIndex):返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始
  • int lastIndexOf(String str):返回指定子字符串在此字符串中最右边出现处的索引
  • int lastIndexOf(String str, int fromIndex):返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索。

注: indexOf和lastIndexOf方法如果未找到都是返回 -1

String常用方法3

  • String replace(char oldChar, char newChar):返回一个新的字符串,它是通过用newChar替换词字符串中出现的所有oldChar得到的。
  • String replace(CharSequence target, CharSequence replacement):使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串。
  • String replaceAll(String regex, String replacement):使用给定的replacement替换此字符串所有匹配给定的正则表达式的子字符串。
  • String replaceFirst(String regex, String replacement):使用给定的replacement替换此字符串匹配给定的正则表达式的第一个子字符串。
  • boolean matches(String regex):告知此字符串是否匹配给定的正则表达式。
  • String[] split(String regex):根据给定正则表达式的匹配拆分此字符串。
  • String[] split(String regex, int limit):根据匹配给定的正则表达式来拆分此字符串,最多不超过limit个,如果超过了,剩下的全部都放到最后一个元素中。
String str = "12hello34world5java7891mysql456";
//把字符串中的数字替换成,,如果结果中开头和结尾有,的话去掉
String string = str.replaceAll("\d+", ",").replaceAll("^,|,$", "");
System.out.println(string);
String str = "12345";
// 判断str字符串中是否全部由数字组成,即有1-n个数字组成
boolean matches = str.matches("\d+");
System.out.println(matches);
String tel = "0571-4534289";
//判断这是否是一个杭州的固定电话
boolean result = tel.matches("0571-\d{7,8}");
System.out.println(result);
String str = "hello|world|java";
String[] strs = str.split("\|");
for(int i = 0; i < strs.length; i++) {
    System.out.println(strs[i]);
}
System.out.println();
String str2 = "hello.world.java";
String[] strs2 = str2.split("\.");
for(int i = 0; i < strs2.length; i++) {
    System.out.println(strs2[i]);
}

复习

  • 字符串 -> 基本数据类型、包装类 
    • Integer包装类的public static int parseInt(String s):可以将由“数字”字符串组成的字符串转换为整型。
    • 类似的,使用java.lang包中的ByteShortLongFloatDouble类调相应的类方法可以将由“数字”字符组成的字符串,转化为相应的基本数据类型。
  • 基本数据类型、包装类 -> 字符串 
    • 调用String类的public String valueOf(int n)可将int型转换为字符串
    • 响应的valueOf(byte b)valueOf(long l)valueOf(float f)valueOf(double d)valueOf(boolean b)可由参数的相应类型到字符串的转换。

String与字符数组转换

字符数组 -> 字符串

  • String类的构造器:String(char[])String(char[], int offset, int length)分别用字符数组中的全部字符和部分字符创建字符串对象。

字符串 -> 字符数组

  • public char[] toCharArray():将字符串中的全部字符存放在一个字符数组中的方法。
  • public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin):提供了将制定索引范围内的字符串存放到数组中的方法。

字节数组 -> 字符串

  • String(byte[]):通过使用平台的默认字符集解码指定的byte数组,构造一个新的String。
  • String(byte[], int offset, int length):用指定的字节数组的一部分,即从数组起始位置offset开始取length个字节构造一个字符串对象。

字符串 -> 字节数组

  • public byte[] getBytes():使用平台的默认字符集将此String编码为byte序列,并将结果存储到一个新的byte数组中。
  • public byte[] getBytes(String charsetName):使用指定的字符集将此String编码到byte序列,并将结果存储到新的byte数组。
String str = "中";
System.out.println(str.getBytes("ISO8859-1").length);	// -128~127
System.out.println(str.getBytes("GBK").length);
System.out.println(str.getBytes("UTF-8").length);

System.out.println(new String(str.getBytes("ISO8859-1"), "ISO8859-1"));	// 乱码,表示不了中文
System.out.println(new String(str.getBytes("GBK"), "GBK"));
System.out.println(new String(str.getBytes("UTF-8"), "UTF-8"));

StringBuffer类

  • java.lang.StringBuffer代表可变的字符序列,JDK1.0中声明,可以对字符串内容进行增删,此时不会产生新的对象。
  • 很多方法与String相同。
  • 作为参数传递时,方法内部可以改变值。
char[] value;	// value没有final声明,value可以不断扩容。
int count;		// count记录有效字符的个数

StringBuffer类

  • StringBuffer类不同于String,其对象必须使用构造器生成。有三个构造器: 
    • StringBuffer():初始容量为16的字符串缓冲区
    • StringBuffer(int size):构造指定容量的字符串缓冲区
    • StringBuffer(String str):将内容从初始化为指定字符串内容
String s = new String("我喜欢学习");
StringBuffer buffer = new StringBuffer("我喜欢学习");
buffer.append("数学");

图片 

StringBuffer类的常用方法

StringBuffer append(xxx):提供了很多的append()方法,用于进行字符串拼接
StringBuffer delete(int start, int end):删除指定位置的内容
StringBuffer replace(int start, int end, String str):把[start, end)位置替换为str
StringBuffer insert(int offset, xxx): 在指定位置插入xxx
StringBuffer reverse():把当前字符序列逆转
  • 当append和insert时,如果原来value数组长度不够,可扩容。
  • 如上这些方法支持方法链操作。
  • 方法链的原理
@Override
public StringBuilder append(String str) {
    super.append(str);
    return this;
}

此外,还定义了如下的方法:

public int indexOf(String str)
public String substring(int start, int end)
public int length()
public char charAt(int n)
public void setCharAt(int n, char ch)

StringBuilder类

  • StringBuilder和StringBuffer非常类似,均代表可变的字符序列,而且提供相关功能的方法也一样
  • 对比String、StringBuffer、StringBuilder 
    • String(JDK1.0):不可变字符序列
    • StringBuffer(JDK1.0):可变字符序列、效率低、线程安全
    • StringBuilder(JDK5.0):可变字符序列、效率高、线程不安全

注意:作为参数传递的话,方法内部String不会改变其值,StringBuffer和StringBuilder会改变其值

// 初始设置
long startTime = 0L;
long endTime = 0L;
String text = "";
StringBuffer buffer = new StringBuffer("");
StringBuilder builder = new StringBuilder("");
// 开始对比
startTime = System.currentTimeMillis();
for(int i = 0; i < 20000; i++) {
    buffer.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuffer的执行时间:" + (endTime - startTime));
startTime = System.currentTimeMillis();
for(int i = 0; i < 20000; i++) {
    builder.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuilder的执行时间:" + (endTime - startTime));
startTime = System.currentTimeMillis();
for(int i = 0; i < 20000; i++) {
    text = text + i;
}
endTime = System.currentTimeMillis();
System.out.println("String的执行时间:" + (endTime - startTime));

2. JDK8 之前的日期时间API

图片

1. java.lang.System类

System类提供的public static long currentTimeMillis()用来返回当前时间与1970年1月1日0时0分0秒之间以毫秒为单位的时间差。

此方法适用于计算时间差

  • 计算世界时间的主要标准有: 
    • UTC(Coordinated Universal Time)
    • GMT(Greenwich Mean Time)
    • CST(Central Standard Time)

2. java.util.Date类

表示特定的瞬间,精确到毫秒

构造器:

  • Date():使用无参构造器创建的对象可以获取本地当前时间。
  • Date(long date)

常用方法:

  • getTime():返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象 表示的毫秒数。
  • toString():把此Date对象转换为以下形式的String:dow mon dd hh:mm:ss zzz yyyy 其中:dow是一周中的某一天(Sun,Mon,Tue,Wed,Thu,Fri,Sat),zzz是时间标准。
  • 其他很多方法都过时了
import java.util.Date;

Date date = new Date();
System.out.println(date);

System.out.println(System.currentTimeMillis());
System.out.println(date.getTime());

Date date1 = new Date(date.getTime());

System.out.println(date1.getTime());
System.out.println(date1.toString());

3. java.text.SimpleDateFormat

  • Date类的API不易于国际化,大部分被废弃了,java.text.SimpleDateFormat类是一个捕鱼语言环境有关的方式来格式化和解析日期的具体类。
  • 它允许进行格式化:日期 -> 文本解析:文本 -> 日期
  • 格式化: 
    • SimpleDateFormat():默认的模式和语言环境创建对象
    • public SimpleDateFormat(String pattern):该构造方法可以用参数pattern指定的格式创建一个对象,该对象调用;
    • public String format(Date date):方法格式化事件对象date
  • 解析: 
    • public Date parse(String source):从给定字符串的开始解析文本,以生成一个日期。

图片

Date date = new Date();	// 产生一个Date实例
// 产生一个formater格式化的实例
SimpleDateFormat formater = new SimpleDateFormat();
System.out.println(formater.format(date));	// 打印输出默认的格式
SimpleDateFormat formater2 = new SimpleDateFormat("yyyy年MM月dd日 EEE HH:mm:ss");
System.out.println(formater2.format(date));
try {
    // 实例化一个指定的格式对象
    Date date2 = formater2.parse("2008年08月08日 星期一 08:08:08")
    // 将指定的日期解析后格式化按指定的格式输出
    System.out.println(date2.toString());
} catch (ParseException e) {
    e.printStackTrace();
}

4. java.util.Calendar(日历)类

  • Calendar是一个抽象基类,主要用于完成日期字段之间相互操作的功能。
  • 获取Calendar实例的方法 
    • 使用Calendar.getInstance()方法
    • 调用它的子类GregorianCalendar的构造器。
  • 一个Calendar的实例是系统时间的抽象表示,通过get(int field)方法来取得想要的时间信息。比如YEAR、MONTH、DAY_OF_WEEK、HOUR_OF_DAY、MINUTE、SECOND 
    • public void set(int field, int value)
    • public void add(int field, int amount)
    • public final Date getTime()
    • public final void setTime(Date date)

注意:

  • 获取月份时:一月是0,二月是1,以此类推,12月是11
  • 获取星期时:周日是1,周二是2 , 。。。。周六是7
Calendar calendar = Calendar.getInstance();
// 从一个 Calendar 对象中获取 Date 对象
Date date = calendar.getTime();
// 使用给定的 Date 设置此 Calendar 的时间
date = new Date(234234235235L);
calendar.setTime(date);
calendar.set(Calendar.DAY_OF_MONTH, 8);
System.out.println("当前时间日设置为8后,时间是:" + calendar.getTime());
calendar.add(Calendar.HOUR, 2);
System.out.println("当前时间加2小时后,时间是:" + calendar.getTime());
calendar.add(Calendar.MONTH, -2);
System.out.println("当前日期减2个月后,时间是:" + calendar.getTime());

3. JDK8中新日期时间API

JDK 1.0中包含了 一个java.util.Date类,但是它的大多数方法已经在JDK 1.1引入Calendar类之后被弃用 了。而Calendar并不比Date好多少。它们面临的问题是:

  • 可变性:像日期和时间这样的类应该是不可变的。
  • 偏移性:Date中的年份是从1900开始的,而月份都从0开始。
  • 格式化:格式化只对Date有用,Calendar则不行。
  • 此外,它们也不是线程安全的;不能处理闰秒等。

总结:对日期和时间的操作一直是Java程序员最痛苦的地方之一。

  • 第三次引入的API是成功的,并且Java 8中引入的java.time API 已经纠正了 过去的缺陷,将来很长一段时间内它都会为我们服务。
  • Java 8 吸收了 Joda-Time 的精华,以一个新的开始为 Java 创建优秀的 API。 新的 java.time 中包含了所有关于本地日期(LocalDate)、本地时间 (LocalTime)、本地日期时间(LocalDateTime)、时区(ZonedDateTime) 和持续时间(Duration)的类。历史悠久的 Date 类新增了 toInstant() 方法, 用于把 Date 转换成新的表示形式。这些新增的本地化时间日期 API 大大简 化了日期时间和本地化的管理。

新时间日期API

  • java.time:包含值对象的基础包
  • java.time.chrono:提供对不同的日历系统的访问
  • java.time.format:格式化和解析时间和日期
  • java.time.temporal:包括底层框架和扩展特性
  • java.time.zone:包含时区支持的类

说明:大多数开发者只会用到基础包和format包,也可能会用到temporal包。因此,尽管68个新的公开类型,大多数开发者,大概将只会用到其中的三分之一。

LocalDate、LocalTime、LocalDateTime

  • LocalDate、LocalTime、LocalDateTime 类是其中较重要的几个类,它们的实例是不可变的对象,分别表示使用ISO-8601日历系统的日期、时间、日期和时间。它们提供了简单的本地日期或时间,并不包含当前的时间信息,也不包含与时区相关的信息。 
    • LocalDate代表IOS格式(yyyy-MM-dd)的日期,可以存储 生日、纪念日等日期。
    • LocalTime表示一个事件,而不是日期。
    • LocalDateTime是用来表示日期和时间的,这是一个最常用的类之一。

注:ISO-8601日历系统是国际标准化组织制定的现代公民的日期和时间的表示法,也就是公历。

方法描述
now() / * now(ZoneId zone)静态方法,根据当前时间创建对象/指定时区的对象
of()静态方法,根据指定日期/时间创建对象
getDayOfMonth()/getDayOfYear()获得月份天数(1-31)/获得年份天数(1-366)
getDayOfWeek()获得星期几(返回一个DayOfWeek枚举值)
getMonth()获得月份,返回一个Month枚举值
getMonthValue()/getYear()获得月份(1-12)/ 获得年份
getHour()/getMinute()/getSecond()获得当前对象应用的小时、分钟、秒
withDayOfMonth()/withDayOfYear()/withMonth()/withYear()将月份天数、年份天数、月份、年份修改为指定的值并返回新的对象
plusDays(),plusWeeks(),plusMonths(),plusYears(),plusHours()向当前对象添加几天、几周、几个月、几年、几小时
minusMonths()/minusWeeks()/minusDays()/minusYears()/minusHours()从当前对象减去几月、几周、几天、几年、几小时。

瞬时:Instant

  • Instant:时间线上的一个瞬间点。这可能被用来记录应用程序中的事件时间戳。
  • 在处理事件和日期的时候,我们通常会想到年,月,日,时,分,秒。然而,这只是 时间的一个模型,是面向人类的。第二种通用模型是面向机器的,或者说是连 续的。在此模型中,时间线中的一个点表示为一个很大的数,这有利于计算机 处理。在UNIX中,这个数从1970年开始,以秒为的单位;同样的,在Java中, 也是从1970年开始,但以毫秒为单位。
  • java.time包通过值类型Instant提供机器视图,不提供处理人类意义上的时间单位。Instant表示时间线上的一点,而不需要任何上下文信息,例如,时区。 概念上讲,它只是简单的表示自1970年1月1日0时0分0秒(UTC)开始的秒 数。因为java.time包是基于纳秒计算的,所以Instant的精度可以达到纳秒级。
  • (1 ns = 10-9 s) 1秒 = 1000毫秒 =106微秒=109纳秒
方法描述
now()静态方法,返回默认UTC时区的Instant类的对象
ofEpochMilli(long epochMilli)静态方法,返回在1970-01-01 00:00:00基础上加上指定毫秒 数之后的Instant类的对象
atOffset(ZoneOffset offset)结合即时的偏移来创建一个OffsetDateTime
toEpochMilli()返回1970-01-01 00:00:00到当前时间的毫秒数,即为时间戳

时间戳是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01 日08时00分00秒)起至现在的总秒数。

格式化与解析日期或时间

java.time.format.DateTimeFormatter类:该类提供了三种格式化方法:

  • 预定义的标准格式。如:
    ISO_LOCAL_DATE_TIME;ISO_LOCAL_DATE;ISO_LOCAL_TIME 
  • 本地化相关的格式。如:ofLocalizedDateTime(FormatStyle.LONG) 
  • 自定义的格式。如:ofPattern("yyyy-MM-dd hh:mm:ss")
方法描述
ofPattern(String pattern)静态方法,返回一个指定字符串格式的DateTimeFormatter
format(TemporalAccessor t)格式化一个日期、时间,返回字符串
parse(CharSequence text)将指定格式的字符序列解析为一个日期、时间

其他API

  • ZoneId:该类中包含了所有的时区信息,一个时区的ID,如Europe/Paris 
  • ZonedDateTime:一个在ISO-8601日历系统时区的日期时间,如 2007-12- 03T10:15:30+01:00 Europe/Paris。 
    • 其中每个时区都对应着ID,地区ID都为“{区域}/{城市}”的格式,例如:Asia/Shanghai等
  • Clock:使用时区提供对当前即时、日期和时间的访问的时钟。 
  • 持续时间:Duration,用于计算两个“时间”间隔 
  • 日期间隔:Period,用于计算两个“日期”间隔 
  • TemporalAdjuster:时间校正器。有时我们可能需要获取例如:将日期调整到“下一个工作日”等操作。 
  • TemporalAdjusters:改类通过静态方法
    (firstDayOfXxx()/lastDayOfXxx()/nextXxx())提供了大量的常用 TemporalAdjuster 的实现。 

JDK8中新日期时间API

// ZoneId:类中包含了所有的时区信息
// ZoneId的getAvailableZoneIds():获取所有的ZoneId
Set<String> zoneIds = ZoneId.getAvailableZoneIds();
for(String s : zoneIds) {
    System.out.println(s);
}
// ZoneId的of():获取指定时区的事件
LocalDateTime localDateTime = LocalDateTime.now(ZoneId.of("Asia/Tokyo"));
System.out.println(localDateTime);

// ZonedDateTime:带时区的日期时间
// ZonedDateTime的now():获取本时区的ZonedDateTime对象
ZonedDateTime zonedDateTime = ZonedDateTime.now();
System.out.println(zonedDateTime);
// ZonedDateTime的now(ZoneId id):获取指定时区的ZonedDateTime对象
ZonedDateTime zonedDateTime1 = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
System.out.println(zonedDateTime1);
// Duration:用于计算两个“时间”间隔,以秒和纳秒为基准
LocalTime localTime = LocalTime.now();
LocalTime localTime1 = LocalTime.of(15, 23, 32);
// between():静态方法,返回Duration对象,表示两个时间的间隔
Duration duration = Duration.between(localTime1, localTime);
System.out.println(duration);

System.out.println(duration.getSeconds());
System.out.println(duration.getNano());

LocalDateTime localDateTime = LocalDateTime.of(2016, 6, 12, 15, 23, 32);
LocalDateTime localDateTime1 = LocalDateTime.of(2017, 6, 12, 15, 23, 32);

Duration duration1 = Duration.between(localDateTime1, localDateTime);
System.out.println(duration1.toDays());
// Period: 用于计算两个 "日期" 间隔,以年、月、日衡量
LocalDate localDate = LocalDate.now();
LocalDate localDate1 = LocalDate.of(2028, 3, 18);

Period period = Period.between(localDate, localDate1);
System.out.println(period);

System.out.println(period.getYears());
System.out.println(period.getMonths());
System.out.println(period.getDays());

Period period1 = period.withYears(2);
System.out.println(period1);
// TemporalAdjster:时间校正器
// 获取当前日期的下一个周日是哪天?
TemporalAdjuster temporalAdjuster = TemporalAdjusters.next(DayOfWeek.SUNDAY);
LocalDateTime localDateTime = LocalDateTime.now().with(temporalAdjuster);
System.out.println(localDateTime);
// 获取下一个工作日是哪天?
LocalDate localDate = LocalDate.now().with(new TemporalAdjuster() {
    @Override
    public Temporal adjustInto(Temporal temporal) {
        LocalDate date = (LocalDate) temporal;
        if(date.getDayOfWeek().equals(DayOfWeek.FRIDAY)) {
            return date.plusDays(3);
        } else if(date.getDayOfWeek().equals(DayOfWeek.SATURDAY)) {
            return date.plusDay(2);
        } else {
            return date.plusDays(1);
        }
    }
})
System.out.println("下一个工作日是:" + localDate);

与传统日期处理的转换

To遗留类From遗留类
java.time.Instant与java.util.DateDate.from(instant)date.toInstant()
java.time.Instant与java.sql.TimestampTimestamp.from(instant)timestamp.toInstant()
java.time.ZonedDateTime与java.util.GregorianCalendarGregorianCalendar.from(zonedDateTime)cal.toZonedDateTime()
java.time.LocalDate与java.sql.TimeDate.valueOf(localDate)date.toLocalDate()
java.time.LocalTime与java.sql.TimeDate.valueOf(localDate)date.toLocalTime()
java.time.LocalDateTime与 java.sql.TimestampTimestamp.valueOf(localDateTime)timestamp.toLocalDateTime()
java.time.ZoneId与java.util.TimeZoneTimezone.getTimeZone(id)timeZone.toZoneId()
java.time.format.DateTimeFormatter与 java.text.DateFormatformatter.toFormat()

4. Java比较器

在Java中经常会涉及到对象数组的排序问题,name就涉及到对象之间的比较问题。

Java实现对象排序的方式有两种:

  • 自然排序:java.lang.Comparable
  • 定制排序:java.util.Comparator
方式一:自然排序:java.lang.Comparable
  • Comparable接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序。
  • 实现Comparable的类必须实现compareTo(Object obj)方法,两个对象即通过compareTo(Object obj)方法的返回值来比较大小。如果当前对象this大于形参对象obj,则返回正整数,如果当前对象this小于形参对象obj,则返回负整数,如果当前对象this等于形参对象obj,则返回零。
  • 实现Comparable接口的对象列表(和数组)可以通过Collections.sort或Arrays.sort进行自动排序。实现此接口的对象可以用作有序映射中的键或有序集合中的元素,无需指定比较器。
  • 对于类C的每一个e1和e2来说,当且晋档e1.compareTo(e2) == 0e1.equals(e2)具有相同的boolean值时,类C的自然排序才叫做equals一致。建议(虽然不是必需的)最好使自然排序与equals一致。

Comparable的类型实现:(默认都是从小到大排列的)

  • String:按照字符串中字符的Unicode值进行比较 
  • Character:按照字符的Unicode值来进行比较 
  • 数值类型对应的包装类以及BigInteger、BigDecimal:按照它们对应的数值大小进行比较 
  • Boolean: true 对应的包装类实例大于 false 对应的包装类实例 
  • Date、Time等:后面的日期时间比前面的日期时间大 
class Goods implements Comparable {
    private String name;
    private double price;
    
    // 按照价格,比较商品的大小
    @Override
    public int compareTo(Object o) {
        if(o instanceof Goods) {
            Goods other = (Goods) o;
            if(this.price > other.price) {
                return 1;
            } else if(this.price < other.price) {
                return -1;
            }
            return 0;
        }
        throw new RuntimeException("输入的数据类型不一致");
    }
    // 构造器、getter、setter、toString()方法略
}
public class ComparableTest {
    public static void main(String[] args) {
        Goods[] all = new Goods[4];
        all[0] = new Goods("《红楼梦》", 100);
		all[1] = new Goods("《西游记》", 80);
		all[2] = new Goods("《三国演义》", 140);
		all[3] = new Goods("《水浒传》", 120);

        Arrays.sort(all);
        
        System.out.println(Arrays.toString(all));
    }
}

方式二:定制排序:java.util.Comparator

  • 当元素的类型没有实现java.lang.Comparable接口而又不方便修改代码,或者实现了java.lang.Comparable接口的排序规则不适合当前的操作,name可以考虑使用Comparator的对象来排序,强行对多个对象进行整体排序的比较。
  • 重写compare(Object o1, Object o2)方法,比较o1和o2的大小:如果方法返回正整数,则表示o1大于o2;如果返回0,表示相等;返回负整数,表示o1小于o2。
  • 可以将Comparator传递给sort方法(如Collections.sort或Arrays.sort),从而允许在排序顺序上实现精确控制。
  • 还可以使用Comparator来控制某些数据结构(如有序set或有序映射)的顺序,或者为那些没有自然顺序的对象collection提供排序。
Goods[] all = new Goods[4];
all[0] = new Goods("War and Peace", 100);
all[1] = new Goods("Childhood", 80);
all[2] = new Goods("Scarlet and Black", 140);
all[3] = new Goods("Notre Dame de Paris", 120);

Arrays.sort(all, new Comparator() {
    @Override
    public int compare(Object o1, Object o2) {
        Goods g1 = (Goods) o1;
        Goods g2 = (Goods) o2;
        
        return g1.getName().compareTo(g2.getName());
    }
})

System.out.println(Arrays.toString(all));

5. System类

  • System类代表系统,系统级的很多属性和控制方法都放置在该类的内部。 该类位于java.lang包。 
  • 由于该类的构造器时private的,所以无法创建该类的对象,也就是无法实例化该类。其内部的成员变量和成员方法都是static的,所以也可以很方便的进行调用。 
  • 成员变量 
    • System类内部包含in、out和err三个成员变量,分别代表标准输入流(键盘输入),标准输出流(显示器)和标准错误输出流(显示器)。
  • 成员方法 
    • native long currentTimeMillis()
      该方法的作用是返回当前的计算机时间,时间的表达格式为当前计算机时 间和GMT时间(格林威治时间)1970年1月1号0时0分0秒所差的毫秒数。 
    • void exit(int status)
      该方法的作用是退出程序。其中status的值为0代表正常退出,非零代表 异常退出。使用该方法可以在图形界面编程中实现程序的退出功能等。 
    • void gc()
      该方法的作用是请求系统进行垃圾回收。至于系统是否立刻回收,则 取决于系统中垃圾回收算法的实现以及系统执行时的情况。 
    • String getProperty(String key)
      该方法的作用是获得系统中属性名为key的属性对应的值。系统中常见的属性名以及属性的作用如下表: 
属性名属性说明
java.versionjava运行时环境版本
java.homejava安装目录
os.name操作系统的名称
os.version操作系统的版本
user.name用户的账户名称
user.home用户的主目录
user.dir用户的当前工作目录
String javaVersion = System.getProperty("java.version");
System.out.println("java的version:" + javaVersion);
String javaHome = System.getProperty("java.home");
System.out.println("java的home:" + javaHome);
String osName = System.getProperty("os.name");
System.out.println("os的name:" + osName);
String osVersion = System.getProperty("os.version");
System.out.println("os的version:" + osVersion);
String userName = System.getProperty("user.name");
System.out.println("user的name:" + userName);
String userHome = System.getProperty("user.home");
System.out.println("user的home:" + userHome);
String userDir = System.getProperty("user.dir");
System.out.println("user的dir:" + userDir);

6. Math类

java.lang.Math提供了一系列静态方法用于科学计算。其方法的参数和返回 值类型一般为double型。

  • abs 绝对值
  • acos,asin,atan,cos,sin,tan    三角函数
  • sqrt    平方根
  • pow(double a, double b)    a的b次幂
  • log    自然对数
  • exp    e为底指数
  • max(double a, double b)
  • min(double a, double b)
  • random()    返回0.0到1.0的随机数
  • long round(double a)    double型数据a转换为long型(四舍五入)
  • toDegrees(double angrad)    弧度 -> 角度
  • toRadians(double angdeg)    角度 -> 弧度

7. BigInteger与BigDecimal

BigInteger类

  • Integer类作为int的包装类,能存储的最大整型值为2 31-1,Long类也是有限的, 最大为2 63-1。如果要表示再大的整数,不管是基本数据类型还是他们的包装类 都无能为力,更不用说进行运算了。 
  • java.math包的BigInteger可以表示不可变的任意精度的整数。 BigInteger提供所有Java的基本整数操作符的对应物,并提供java.lang.Math的所有相关方法。另外,BigInteger 还提供以下运算:模算术、GCD 计算、质数测试、素数生成、 位操作以及一些其他操作。 
  • 构造器 
    • BigInteger(String val):根据字符串构建BigInteger对象

常用方法

  • public BigInteger abs():返回此BigInteger的绝对值的BigInteger。
  • BigInteger add(BigInteger val):返回其值为(this + val)的BigInteger。
  • BigInteger subtract(BigInteger val):返回其值为(this - val)的BigInteger。
  • BigInteger multiply(BigInteger val):返回其值为(this * val)的BigInteger。
  • BigInteger divide(BigInteger val):返回其值为(this / val)的BigInteger。整数相除只保留整数部分。
  • BigInteger remainder(BigInteger val):返回其值为(this % val)的BigInteger。

BigDecimal类

  • 一般的Float类和Double类可以用来做科学计算或工程计算,但在商业计算中,要求数字精度比较高,故用到java.math.BigDecimal类。
  • BigDecimal类支持不可变的、任意精度的有符号十进制定点数。
  • 构造器 
    • public BigDecimal(double val)
    • public BigDeciamal(String val)
  • 常用方法 
    • public BigDecimal add(BigDecimal augend)
    • public BigDecimal subtract(BigDecimal subtrahend)
    • public BigDecimal multipy(BigDecimal multiplicand)
    • public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode)
public void testBigInteger() {
    BigInteger bi = new BigInteger("12433241123");
    BigDecimal bd = new BigDecimal("12435.351");
    BigDecimal bd2 = new BigDecimal("11");
    System.out.println(bi);
    // System.out.println(bd.divide(bd2));
    System.out.println(bd.divide(bd2, BigDecimal.ROUND_HALF_UP));
    System.out.println(bd.divide(bd2, 15, BigDecimal.ROUND_HALF_UP));
}

面试题

转换字符串中的顺序

// 方式一:转换为 char[]
public String reverse(String str, int startIndex, int endIndex) {
    if(str != null) {
        char[] arr = str.toCharArray();
        for(int x = startIndex, y = endIndex; x < y; x++, y--) {
            char temp = arr[x];
            arr[x] = arr[y];
            arr[y] = temp;
        }
        return new String(arr);
    }
    return null;
}
// 方式二:使用String的拼接
public String reverse1(String str, int startIndex, int endIndex) {
    if(str != null) {
        // 第一部分
        String reverseStr = str.substring(0, startIndex);
        // 第二部分
        for(int i = endIndex; i >= startIndex; i--) {
            reverseStr += str.charAt(i);
        }
        // 第三部分
        reverseStr += str.substring(endIndex + 1);
        return reverseStr;
    }
    return null;
}
// 方式三:使用StringBuffer/StringBuilder替换String
public String reverse1(String str, int startIndex, int endIndex) {
    if(str != null) {
        StringBuilder builder = new StringBuilder(str.length());
        
        // 第一部分
        builder.append(str.substring(0, startIndex));
        // 第二部分
        for(int i = endIndex;i >= startIndex; i--) {
            builder.append(str.charAt(i));
        }
        // 第三部分
        builder.append(str.substring(endIndex + 1));
        
        return builder.toString();
    }
    return null;
}