版本 | 说明 | 发布日期 |
1.0 | 发布文章第一版 | 2020-11-07 |
1.1 | 新增一个小节:【不要被double骗了】 | 2020-11-24 |
之前【由此引申出的编译器常量优化机制】的答案是错的,进行了更正 |
文章目录
- 前言
- 常用类库总览
- java.lang包中的一些常用的类
- Object类——这就是万物皆对象~
- 基本概念
- 常用方法
- boolean equals(Object obj)
- int hashCode()
- 自动生成equals、hashCode、toString
- 包装类
- 基本概念
- Integer
- 常用常量
- 常用方法
- 自动装箱机制
- 自动装箱池(常量池)
- 自动拆箱机制
- Double
- Boolean
- Character
- 总而言之~
- Math
- 字符串相关类
- String!!!!!!!!!重点中的重点!!!!!!!!!
- 基本概念
- String的常量池的概念!!!重点!!!
- 由此引申出的String实例化问题
- 由此引申出的编译器常量优化机制
- 为了验证我又没有说明白,举个栗子折磨一下大家
- 常用方法
- 基础
- 判断型
- 与byte[]、char[]相互转换
- 变化型
- 查找型
- 截取子串
- 正则表达式相关
- 可变字符串类(StringBuilder和StringBuffer)
- 基本概念
- 构造方法——决定初始容量
- 常用方法
- 日期相关的类
- java 8之前的日期类。过气网红们,了解一下就行~
- System类中的currentTimeMills()
- Date类
- 基本概念
- 常用方法
- SimpleDateFormat类
- 基本概念
- 常用方法
- Calendar类
- 基本概念
- 常用方法
- java 8新出现的日期类。冉冉升起的新星
- 基本概念
- LocalDate类、LocalTime类、LocalDateTime类
- 基本概念
- 常用方法
- Instant类
- 基本概念
- 常用方法
- DateTimeFormatter类
- 基本概念
- 常用方法
- 给新伙伴们一个总的栗子~
- java.math包中常用的类
- BigDecimal
- 基本概念
- 常用方法
- 要点
- 除不尽就出问题!
- 不要被double骗了
- BigInteger
- 基本概念
- 常用方法
前言
- 这篇文章不是面面俱到的基础知识集合,只是我个人的学习笔记。学习资料来源于《拉勾教育Java就业急训营》。
- 以下所有内容仅代表个人观点,不一定正确。
常用类库总览
包名 | 说明 |
java.lang | 核心包,包含了一些比较重要的类,该包中的所有内容由Java虚拟机自动导入。例如System、String。这个lang其实是language的缩写。 |
java.util | 工具包,提供了工具类和集合类。如Scanner、Random、List。 |
java.io | 输入输出包,提供了大量流相关的类。如FileInputStream。 |
网络包,提供了网络编程相关的类。如ServerSocket、Socket。 | |
java.sql | 数据包,提供了大量数据库操作相关的类。如DriverManager、Connection |
java.lang包中的一些常用的类
Object类——这就是万物皆对象~
基本概念
- 位于java.lang.Object。
- 是java中所有类的根类,也就是说是java所有类的直接或者间接父类。
- 一个人只能有一个对象,但是一个java它可以有无数个对象(说到这里,有些小伙伴流下了羡慕的口水)。
- java表示自己对象太多,不好管啊!所以用一个Object类统一管理。这么一说Object有点像曾经的东厂哈哈哈。
- 如果一个类定义的时候没有extends关键字,则默认会extends Object。这就是上面说的直接父类。
常用方法
方法声明 | 功能介绍 |
Object() | 无参构造方法。 |
boolean equals(Object obj) | 用于判断两个对象是否相等。Object类中的逻辑是比较地址是否相等,与 == 运算符的结果一致。所以该方法通常需要重写。若该方法被重写,同时则应该重写hashCode方法,以保证结果的一致性。 |
int hashCode() | 用于获取调用对象的哈希码值(可通俗理解为内存地址的编号)。若两个对象调用equals方法相等,则各自调用该方法的结果必须相同;若两个调用对象equals方法不相等,则各自调用该方法的结果应该不相同。所以该方法通常需要与equals方法同时重写。 |
String toString() | 用于获取调用对象的字符串形式。该方法默认返回的字符串为:包名.类名@哈希码值的十六进制。为了返回更有意义的数据,该方法通常需要重写。使用print打印引用或使用字符串拼接引用时,都会自动调用该方法。 |
Class<?> getClass() | 用于返回调用对象执行时的Class实例,反射机制使用。 |
boolean equals(Object obj)
- Java API文档对于equals方法提出了几个特性,当我们重写equals时,应当自觉遵循这些特性。
- 自反性:对于任何非空引用x,x.equals(x)应该返回true。
- 对称性:对于任何非空引用x和y,x.equals(y)应该返回true,当且仅当y.equals(x)返回true。
- 传递性:对于任何非空引用x、y和z。如果x.equals(y)返回true、y.equals(z)返回true。则x.equals(z)也应该返回true。
- 一致性:对于任何非空引用x和y,如果未修改对象上用于equals比较的信息,则多次调用x.equals(y)应该始终返回true或始终返回false。
- 非空性:对于任何非空引用x,x.equals(null)应该返回false。
- 为了满足这些特性,下面给一个重写的例子。当然方法不是绝对的,只要能满足这些特性就OK啦~
public class Person {
private String id;
public Person(String id) {
this.id = id;
}
public void setId(String id) {
this.id = id;
}
//重写Object的equals方法
@Override
public boolean equals(Object obj) {
if (obj instanceof Person) {
//比较特征
return this.id.equals(((Person) obj).id);
}
return false;
}
//重写hashCode方法
@Override
public int hashCode(){
return id.hashCode();
}
public static void main(String[] args) {
Person yu = new Person("1996817");
Person angel = new Person("1997414");
System.out.println("angel.equals(yu):" + angel.equals(yu));
System.out.println("更改id");
yu.setId("1997414");
System.out.println("angel.equals(yu):" + angel.equals(yu));
System.out.println("yu.equals(angel):" + yu.equals(angel));
System.out.println("angel.equals(angel):" + angel.equals(angel));
System.out.println("angel.equals(null):" + angel.equals(null));
}
}- 运行结果如下:
angel.equals(yu):false
更改id
angel.equals(yu):true
yu.equals(angel):true
angel.equals(angel):true
angel.equals(null):falseint hashCode()
- hashCode是在使用哈希集合时,在散列表中用到的。这一点从API文档中也能看到。所以说,我们应该尽量让equals不等的对象,hashCode也不等。而equals相等的对象,hashCode必须相等。
- 上面说过,重写了equals方法,必然还需要重写hashCode方法。但是怎么重写呢?
- 最简单最偷懒的方法,就是直接返回equals比较的特征值。比如上面例子重写的hashCode。
- 更合理的重写方式呢,可以调用Objects类的一个静态方法:hash(Object… values)。
自动生成equals、hashCode、toString
- 如果你用的idea,恭喜你,这三个方法可以自动按照模板生成。生成方式和getter、setter类似。
- 下面这两个方法就是自动生成的,可以感受一下:
@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(id, );
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public String toString() {
return "Person{" +
"id='" + id + '\'' +
'}';
}包装类
基本概念
- 位于java.lang.Byte、java.lang.Short等。
- 用于将基本数据类型包装成对象,以满足Java“万物皆对象”的理念。
- 目前包装类有以下几种:Byte、Short、Integer、Long、Float、Double、Boolean、Character。分别对应一种基本数据类型。
- 数字类型的包装类统一继承了抽象类Number。
- 将基本数据类型转换为包装类,称为装箱;将包装类转换为基本数据类型,称为拆箱。从Java1.5开始,增加了大量自动拆箱和装箱的机制。
- 下面讲解几种典型的包装类。
Integer
常用常量
常量声明 | 功能 |
public static final int MAX_VALUE | 表示int类型可以描述的最大值,即2^31-1 |
public static final int MIN_VALUE | 表示int类型可以描述的最小值,即-2^31 |
public static final int SIZE | 表示int类型二进制形式的位数 |
public static final int BYTES | 表示int类型所占的字节个数 |
public static final Class TYPE | 表示int类型的Class实例 |
常用方法
方法声明 | 功能介绍 |
int intValue() | 获取调用对象中的整数值并返回。作为拆箱的标准方法。 |
static Integer valueOf(int i) | 根据参数指定整数值得到Integer类型对象。作为装箱的标准方法。 |
boolean equals(Object obj) | 比较调用对象与参数指定的对象是否相等 |
String toString() | 返回描述调用对象数值的字符串形式 |
static int parseInt(String s) | 将字符串类型转换为int类型并返回。字符串不合理时会抛出NumberFormatException。 |
static String toString(int i) | 获取参数指定整数的十进制字符串形式。注意和上面的toString的区别:静态且参数为int。 |
static String toBinaryString(int i) | 获取参数指定整数的二进制字符串形式 |
static String toHexString(int i) | 获取参数指定整数的十六进制字符串形式 |
static String toOctalString(int i) | 获取参数指定整数的八进制字符串形式 |
自动装箱机制
- Java1.5之后有了自动装箱机制,从而让下面两行代码的效果是等价的。也就是说Integer对整数类型的赋值运算,会自动调用valueOf。
Integer a = 100;
Integer b = Integer.valueOf(100);- 如果想看下自动装箱池是什么样的,可以去看源码:Integer类的静态内部类IntegerCache。
自动装箱池(常量池)
- 猜猜下面的结果是啥
public class IntegerTest {
public static void main(String[] args){
Integer a = 1;
Integer b = 1;
Integer c = Integer.valueOf(1);
Integer d = new Integer(1);
Integer e = 128;
Integer f = 128;
System.out.println(a==b);
System.out.println(c==b);
System.out.println(d==b);
System.out.println(f==e);
}
}- 结果是
true
true
false
false- 有的小伙伴又懵了:怎么着?说好的==比地址呢?怎么结果看起来如此木有规律!?
- 但是这个真的比的是地址!为什么呢?因为包装类有一个叫自动装箱池的东西,对于-128~127的整数,对应的包装类都作为常量存放在了内存当中。所以a、b、c都是直接指向的内存中的这些常量,地址也就理所当然是相同的了。
- 需要注意的是,如果是通过直接调用构造方法,则不会用到自动装箱池,而是指向的新new出来的内存。
自动拆箱机制
- 猜猜下面的结果是啥
Integer a = 1;
Integer b = 2;
Long g = 3L;
System.out.println(g == (a + b));
System.out.println(g.equals(a + b));- 结果如下:
true
false- 为什么呢?
- 对于第一个比较:大家可能第一时间会想到==比较的是地址,所以理应为false。但是包装类在进行运算符运算的时候,会触发“自动拆箱机制”,也就是说
a + b这东西,计算结果会从Integer变为int。而Long与int进行==比较,Long也会变为long。所以最终就变成了基本数据类型的比较,结果自然是true。 - 对于第二个比较:这个比较简单,因为Long类重写了equals方法,自己去看一下就知道了。如果比较的是非Long类型,则直接为false。
Double
- Double和Integer基本大同小异,只是有一个方法需要提及一下:
boolean isNaN() - 这个方法是非静态的,所以肯定是Double的引用来调用,也就是说调用这个方法的肯定是一个数字。所以为啥还要判断是否是数字呢?
- 然后看一下下面这个例子就明白了哈哈哈。此非数字不是彼非数字。
public class DoubleTest {
public static void main(String[] args) {
Double a = 0/0.0;
System.out.println(a);
System.out.println(a.isNaN());
}
}- 运行结果如下。也就是说这个方法是用来判断是否存在0/0.0的情况的。顺道提一句,0/0直接编译器报算术运算错误哈。之所以0.0可以除,0不能除,终究还是因为误差的问题。
NaN
true- 还有一个
boolean isInfinite()方法也是同理。无穷大嘛,一个非零数字除以0.0的情况。
Boolean
- 同样是大同小异,但是依然有一个小东东需要说明一下,这个东东就是
boolean parseBoolean(String s)方法 - 这个方法通过查看源码可以看到,当s是"true"时(且不区分大小写),则返回true;否则,其他任何情况下都是返回false。
- 还有一个小小细节。别的包装类都有常量SIZE和BYTES,但Boolean没有哦~看过我写的《Java基础(一)》的小伙伴肯定都明白是怎么肥四。
Character
- 大家猜猜我要说什么?没错!同样是大同小异!但是Character有几个方法还是比较常用的,下面列一下:
方法声明 | 功能 |
static boolean isUpperCase(char ch) | 判断是否为大写字符 |
static boolean isLowerCase(char ch) | 判断是否为小写字符 |
static boolean isDigit(char ch) | 判断是否为数字字符 |
static char toUpperCase(char ch) | 转换为大写字符 |
static char toLowerCase(char ch) | 转换为小写字符 |
总而言之~
- 总而言之,手动装箱方式:
valueOf() - 总而言之,手动拆箱方式:
xxxValue() - 总而言之,字符串转换为基本数据类型的方式:
parseXxx()
Math
- 位于java.lang.Math。提供了各种和数学相关的方法,例如对数、幂、根、三角函数等。
- 这个包其实没啥好讲的,因为用不用得好,和编程水平没啥关系,主要取决于数学水平。哈哈哈哈!!!不过还是放几个常用的方法来感受一下吧:
方法声明 | 功能 |
static int max(int a, int b) | 返回两个参数中的最大值 |
static int min(int a, int b) | 返回两个参数中的最小值 |
static double pow(double a, double b) | 返回a的b次幂 |
static int abs(int a) | 返回参数的绝对值 |
static long round(double a) | 返回参数四舍五入到整数的结果 |
static double sqrt(double a) | 返回参数的平方根 |
static double random() | 返回0.0到1.0的随机数。不过不推荐用这个方法,因为Random类更加强大。 |
- 哦对了,提一句。这个类除了构造方法,其他的所有东西都是static修饰的哦!
字符串相关类
String!!!!!!!!!重点中的重点!!!!!!!!!
基本概念
- 位于java.lang.String。该类被final修饰。
- 从jdk1.9开始,该类的底层不使用char[]来存储数据,而是改成byte[]加上编码标记,从而节约了一些空间。
- String实例描述的字符串内容是常量,无法改变。实际开发中String看起来可以改变,是因为String的引用改变了,而不是同一个String实例中的byte[]变化了。原因如下:
- String类的byte[]被final修饰,也就是说不能改变其引用。
- String类没有提供单独修改byte[]中某个下标值的方法。
- byte[]的长度不能变化。
String的常量池的概念!!!重点!!!
- 由于String描述的字符串内容不可改变,因此Java虚拟机将首次出现的字符串放入常量池(位于方法区)。若后续出现了相同字符串内容,则直接使用池中已有的字符串对象,从而提高了性能。
由此引申出的String实例化问题
- 像
String a = "123";这样的代码,只生成了一个String实例,并且这个实例存在于方法区。然后a指向了方法区的实例。 - 而
String a = new String("123");实际上产生了两个实例。因为行代码翻译一下是这样的:
- 在方法区中实例化常量字符串对象"123"。
- 构造方法使用常量池中的"123",在堆区中实例化了字符串对象"123"。
- a指向堆区中实例化的字符串对象。
由此引申出的编译器常量优化机制
-
String a = "ab" + "cd";这行代码,因为两个都是常量,所以javac在编译的时候,会将其优化为String a = "abcd";。 - 而
变量+字符串常量就不会有这种优化。
为了验证我又没有说明白,举个栗子折磨一下大家
- 下面的代码总共创建了几个String对象?分别在哪存储?在哪一行代码创建的?运行结果是什么?
public class StringTest {
public static void main(String[] args) {
String str1 = "常量池测试";
String str2 = "常量池测试";
String str3 = new String("常量池测试");
String srt4 = "常量池" + "测试";
String str5 = "常量池";
String str6 = str5 + "测试";
System.out.println(str1 == str2);
System.out.println(str3 == str2);
System.out.println(srt4 == str2);
System.out.println(str6 == str3);
}
}- 其实这个问题是我自己编的,所以没有参考答案,我也不保证我是100%正确的哈哈哈!大家如果有不同意见,随时交流~
- 创建了5个对象:
- 常量池中的对象"常量池测试",在str1的那一行创建;
- 堆区中的对象"常量池测试",在str3的那一行创建。
- 常量池中的对象"常量池",在str5的那一行创建;
- str6那一行稍微复杂一点:根据String的变量拼接原理:先是实例化了一个StringBuilder,依次append(str5)和append(“测试”)。最后再toString给str6。而StringBuilder的toString方法,其实是调用了new String(byte[] value)。所以整个过程创建了两个String对象:
- 常量池中的对象"测试";
- 堆区中的"常量池测试";
- 运行结果如下。如果没懂的,再看看我上面说的,就能懂啦~
true
false
true
false常用方法
基础
方法声明 | 功能 |
char charAt(int index) | 返回指定下标位置的字符。 |
int length() | 返回字符串字符序列的长度 |
boolean isEmpty() | 判断字符串是否为空 |
int compareTo(String anotherString) | 比较大小关系。调用字符串的字符依次减参数字符串字符,最后长度相减。 |
int compareToIgnoreCase(String str) | 不考虑大小写比较大小关系。 |
判断型
方法声明 | 功能 |
boolean contains(CharSequence s) | 用于判断当前字符串是否包含参数指定的内容 |
boolean startsWith(String prefix) | 判断字符串是否以参数字符串开头 |
boolean startsWith(String prefix, int toffset) | 从指定位置开始是否以参数字符串开头 |
boolean endsWith(String suffix) | 判断字符串是否以参数字符串结尾 |
boolean equals(Object anObject) | 用于比较字符串内容是否相等并返回 |
boolean equalsIgnoreCase(String anotherString) | 用于比较字符串内容是否相等并返回,不考虑大小写, 如:'A’和’a’是相等 |
与byte[]、char[]相互转换
方法声明 | 功能 |
byte[] getBytes() | 转换为byte数组并返回 |
char[] toCharArray() | 转换为char数组并返回 |
- 转为String的话,直接使用构造方法即可。
变化型
- 因为字符串的值不能改变,所以变化型的方法都是通过新实例化对象来实现的。也就是说调用方法前后,原字符串并不会发生变化。
方法声明 | 功能 |
String toLowerCase() | 返回字符串的小写形式 |
String toUpperCase() | 返回字符串的大写形式 |
String trim() | 返回去掉前导和后继空白的字符串 |
String replace(char oldChar, char newChar) | 使用newChar替换此字符串中出现的所有oldChar |
查找型
方法声明 | 功能 |
int indexOf(int ch) | 返回指定字符第一次出现的下标。不存在则返回-1。 |
int indexOf(int ch, int fromIndex) | 从fromIndex位置开始,返回指定字符第一次出现的下标。不存在则返回-1。 |
int indexOf(String str) | 返回指定字符串第一次出现的下标。不存在则返回-1。 |
int indexOf(String str, int fromIndex) | 从fromIndex位置开始,返回指定字符串第一次出现的下标。不存在则返回-1。 |
int lastIndexOf(int ch) | 返回指定字符最后一次出现的下标。不存在则返回-1。 |
int lastIndexOf(int ch, int fromIndex) | 从fromIndex位置开始,返回指定字符最后一次出现的下标。不存在则返回-1。 |
int lastIndexOf(String str) | 返回指定字符串最后一次出现的下标。不存在则返回-1。 |
int lastIndexOf(String str, int fromIndex) | 从fromIndex位置开始,返回指定字符串最后一次出现的下标。不存在则返回-1。 |
- 注意一下ch的参数类型是int,这是为了兼容不同的字符集,索性用4个字节的int类型来处理。
截取子串
方法声明 | 功能 |
String substring(int beginIndex, int endIndex) | 返回字符串中从下标beginIndex(包括)开始到endIndex(不包括)结束的子字符串 |
String substring(int beginIndex) | 返回字符串中从下标beginIndex(包括)开始到字符串结尾的子字符串 |
正则表达式相关
- 正则表达式是什么玩意儿?其实是一个用于校验字符串格式是否正确的一种表达式。这个我就不在这里赘述了,因为他又难记又麻烦。大家如果用到了,直接面向百度编程即可
方法名称 | 方法 |
boolean matches(String regex) | 判断字符串是否匹配参数指定的正则表达式规则 |
String[] split(String regex) | 参数regex为正则表达式,以regex所表示的字符串为分隔符,将字符串拆分成字符串数组 |
String replaceFirst(String regex, String replacement) | 替换与正则表达式匹配的第一个子字符串 |
String replaceAll(String regex, String replacement) | 替换与正则表达式匹配的所有子字符串 |
可变字符串类(StringBuilder和StringBuffer)
基本概念
- 位于java.lang.StringBuilder和java.lang.StringBuffer。
- 用于解决String的内容无法变更的问题。
- StringBuffer从java 1.0就存在,是线程安全的,所以效率较低。
- StringBuilder从Java 5.0开始出现,是非线程安全的,所以效率较高。
构造方法——决定初始容量
方法声明 | 功能 |
StringBuilder() | 无参构造,初始容量为16 |
StringBuilder(int capacity) | 容量为参数指定大小 |
StringBuilder(String str) | 根据字符串构造对象,容量为:16+字符串长度 |
- 通常来讲,后期使用中,如果容量不够了,扩容逻辑是:2 + 当前容量 * 2。
常用方法
方法声明 | 功能 |
int capacity() | 返回当前容量 |
int length() | 用于返回字符串的字符的个数 |
String toString() | 转换为字符串 |
StringBuilder insert(int offset, String str) | 在指定下标插入字符串,并返回自己 |
StringBuilder append(String str) | 在末尾追加字符串,并返回自己 |
StringBuilder deleteCharAt(int index) | 将下标为index的单个字符删除,并返回自己 |
StringBuilder delete(int start,int end) | 删除字符串,并返回自己。左闭右开区间 |
StringBuilder replace(int start,int end, String str) | 替换字符串,并返回自己。左闭右开区间 |
StringBuilder reverse() | 将字符串反转,并返回自己 |
查找方法同String | 查找方法同String |
- 可以看到,很多变化型的方法都会返回自己。这个作用是什么呢?其实是为了让对象能够在一行代码中连续调用。比如
str.append("xxx").deleteCharAt(1);。 - 需要注意的是,String类内容变化时,是会生成新的对象,而调用对象本身不会改变。而StringBuilder和StringBuffer,改变的是调用对象自己。
日期相关的类
java 8之前的日期类。过气网红们,了解一下就行~
System类中的currentTimeMills()
- System类位于java.lang.System
- 该类有一个方法
static long currentTimeMills(),用于返回当前时间与1970年1月1日0时0分0秒之间以毫秒为单位的时间差 - 这个类以前常用于测试算法的执行时间。
Date类
基本概念
- 位于java.util.Date
- 用于描述特定的时间点,也就是年月日时分秒(毫秒)。
常用方法
方法声明 | 功能 |
Date() | 使用无参的方式构造对象,也就是当前系统时间。会自动根据时区偏移。 |
Date(long date) | 根据参数指定毫秒数构造对象, 参数为距离1970年1月1日0时0分0秒的毫秒数。所以可以用currentTimeMills()。会自动根据时区偏移 |
long getTime() | 返回距离1970年1月1日0时0分0秒的毫秒数 |
void setTime(long time) | 设置时间为距离基准时间time毫秒的时间点。会自动根据时区偏移 |
- 为了说明什么叫根据时区偏移,我就举个栗子吧
public class DateTest {
public static void main(String[] args){
System.out.println(new Date());
System.out.println(new Date(0));
System.out.println(new Date(System.currentTimeMillis()));
}
}- 上面代码的运行结果如下。可以看到,因为我现在在东8区,所以第二行打印的不是70年0时,而是70年8时。同理,当前时间也是东8区的当前时间。
Fri Nov 06 12:38:00 CST 2020
Thu Jan 01 08:00:00 CST 1970
Fri Nov 06 12:38:00 CST 2020SimpleDateFormat类
基本概念
- 位于java.text.SimpleDateFormat。用于实现日期和文本之间的转换
常用方法
方法声明 | 功能 |
SimpleDateFormat() | 构造方法,提供了一种默认的格式 |
SimpleDateFormat(String pattern) | 根据参数指定的模式来构造对象,模式主要有: y-年、M-月、d-日、h-时、m-分、s-秒。例如"yyyy-MM-dd hh-mm-ss" |
final String format(Date date) | 用于将日期类型转换为文本类型 |
Date parse(String source) | 用于将文本类型转换为日期类型 |
void applyPattern(String pattern) | 将对象变更为指定的模式 |
- 举例如下:
public class SimpleDateFormatTest {
public static void main(String[] args){
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat();
System.out.println(sdf.format(date));
sdf.applyPattern("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.format(date));
}
}- 结果如下。注意,MM和mm不能搞反了哈!
2020/11/6 下午12:46
2020-11-06 12:46:30Calendar类
基本概念
- 位于java.util.Calendar
- 用于弥补Date类不能全球化的缺点,取代了Date类中过时的方法。
- 该类是抽象类。通过getInstance方法可以获取该类的子类的对象。不同的子类针对不同国家的日历系统。
- getInstance具体返回哪个子类呢?可以通过构造方法传参指定;也可以不传参,系统自动根据电脑所在位置,生成对应的子类。
- 这种返回值体现多态的用法,也是一种很常用的设计思路,小伙伴们可以自行领会一下~
常用方法
方法声明 | 功能 |
static Calendar getInstance() | 用于获取Calendar子类的实例 |
void set(int year, int month, int date, int hourOfDay, int minute, int second) | 用于设置年月日时分秒信息。注意,设置的月份需要固定-1 |
Date getTime() | 用于将Calendar类型转换为Date类型 |
void set(int field, int value) | 设置指定字段的数值。field为Calendar类中的常量,例如YEAR, MONTH等 |
void add(int field, int amount) | 向指定字段增加数值 |
- 举例如下:
public class CalendarTest {
public static void main(String[] args){
Calendar calendar = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat();
calendar.set(2018, 10-1, 9, 1, 1, 1);
System.out.println(sdf.format(calendar.getTime()));
calendar.add(Calendar.YEAR, 1);
System.out.println(sdf.format(calendar.getTime()));
}
}- 结果如下。
2018/10/9 上午1:01
2019/10/9 上午1:01java 8新出现的日期类。冉冉升起的新星
基本概念
- 这套新的日期类为什么会出现呢?
- java 1.0设计的Date类,并没有考虑全球化的问题,其日历系统仅仅是针对美国的。
- 后来为了全球化java 1.1设计了Calendar类,但是这个类用起来过于繁琐、非线程安全,遭到勤劳勇敢的打工人们的唾弃。
- 所以一套崭新、强大、简单、支持全球化的日期相关类就应运而生了。
- 主要成员:java.time包:日期/时间API的基础包。
- java.time.chrono包:该包提供对不同日历系统的访问。
- java.time.format包:该包能够格式化和解析日期时间对象。
- java.time.temporal包:该包包含底层框架和扩展特性。
- java.time.zone包:该包支持不同时区以及相关规则的类。
LocalDate类、LocalTime类、LocalDateTime类
基本概念
- 这三个类在使用上基本一致,只是LocalDate只描述日期、LocalTime只描述时间、LocalDateTime描述日期和时间。所以这三个就一起讲了。
- 这三个类和String类似,主要特征(年月日时间)均为final修饰。所以每次变更时间,实际上是返回了新创建的对象,原对象并未改变。
常用方法
- 以LocalDateTime类为例:
方法声明 | 功能 |
static LocalDateTime now() | 获取系统当前日期时间,自动根据时区偏移。 |
static LocalDateTime of(int year, int month, int dayOfMonth, int hour, int minute, int second) | 根据参数指定的年月日时分秒信息来设置日期时间 |
int getYear() | 获取年份数值 |
int getMonthValue() | 获取月份数值 |
Month getMonth() | 获取月份枚举值 |
int getDayOfMonth() | 获取日数值 |
int getHour() | 获取小时数 |
int getMinute() | 获取分钟数 |
int getSecond() | 获取秒数 |
LocalDateTime withYear(int year) | 设置为指定的年 |
LocalDateTime withMonth(int month) | 设置为指定的月 |
LocalDateTime withDayOfMonth(int dayOfMonth) | 设置为指定的日 |
LocalDateTime withHour(int hour) | 设置为指定的时 |
LocalDateTime withMinute(int minute) | 设置为指定的分 |
LocalDateTime withSecond(int second) | 设置为指定的秒 |
LocalDateTime plusYears(long years) | 加上指定的年 |
LocalDateTime plusMonths(long months) | 加上指定的月 |
LocalDateTime plusDays(long days) | 加上指定的日 |
LocalDateTime plusHours(long hours) | 加上指定的时 |
LocalDateTime plusMinutes(long minutes) | 加上指定的分 |
LocalDateTime plusSeconds(long seconds) | 加上指定的秒 |
LocalDateTime minusYears(long years) | 减去指定的年 |
LocalDateTime minusMonths(long months) | 减去指定的月 |
LocalDateTime minusDays(long days) | 减去指定的日 |
LocalDateTime minusHours(long hours) | 减去指定的时 |
LocalDateTime minusMinutes(long minutes) | 减去指定的分 |
LocalDateTime minusSeconds(long seconds) | 减去指定的秒 |
Instant类
基本概念
- 这个类也是用于描述日期时间的类,区别在于这个类默认创建的是本初子午线的时间,不会根据时区自动偏移。
常用方法
方法声明 | 功能 |
static Instant now() | 获取本初子午线当前时间 |
OffsetDateTime atOffset(ZoneOffset offset) | 将此瞬间与偏移量组合,以创建偏移的日期时间 |
static Instant ofEpochMilli(long epochMilli) | 根据距离1970年1月1日0时0分0秒的毫秒数来构造对象 |
long toEpochMilli() | 获取距离1970年1月1日0时0分0秒的毫秒数 |
DateTimeFormatter类
基本概念
- 用于格式化和转换新版的日期。使用方法和以前的SimpleDateFormat类似。
常用方法
方法声明 | 功能 |
static DateTimeFormatter ofPattern(String pattern) | 根据参数指定的模式来获取对象 |
String format(TemporalAccessor temporal) | 将日期时间转换为字符串。TemporalAccessor是一个接口,像LocalDateTime、Instant等都实现了该接口 |
TemporalAccessor parse(CharSequence text) | 将字符串转换为日期时间 |
给新伙伴们一个总的栗子~
public class NewDateSeriesTest {
public static void main(String[] args) {
LocalDate localDate = LocalDate.of(2008, 8, 8);
System.out.println(localDate);
System.out.println("==========================当前时区时间!!!==========================");
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDateTime);
System.out.println(localDateTime.getDayOfMonth());
System.out.println(localDateTime.getMonth());
System.out.println("======================在变了!在变了!====================");
System.out.println("with:" + localDateTime.withYear(2022));
System.out.println("我不变:" + localDateTime);
System.out.println("plus:" + localDateTime.plusYears(2022));
System.out.println("我就不变!" + localDateTime);
System.out.println("minus:" + localDateTime.minusDays(2));
System.out.println("我偏不变~" + localDateTime);
System.out.println("====================来看一看instant!=====================");
Instant instant = Instant.now();
System.out.println("本初子午线时间:" + instant);
System.out.println("东八区时间:" + instant.atOffset(ZoneOffset.ofHours(8)));
System.out.println("===================保持队形!==================");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
System.out.println(formatter.format(localDateTime));
}
}- 运行结果如下。比较简单,就不解释了。
2008-08-08
==========================当前时区时间!!!==========================
2020-11-07T17:04:52.634306100
7
NOVEMBER
======================在变了!在变了!====================
with:2022-11-07T17:04:52.634306100
我不变:2020-11-07T17:04:52.634306100
plus:4042-11-07T17:04:52.634306100
我就不变!2020-11-07T17:04:52.634306100
minus:2020-11-05T17:04:52.634306100
我偏不变~2020-11-07T17:04:52.634306100
====================来看一看instant!=====================
本初子午线时间:2020-11-07T09:04:52.697385200Z
东八区时间:2020-11-07T17:04:52.697385200+08:00
===================保持队形!==================
2020-11-07 05:04:52java.math包中常用的类
BigDecimal
基本概念
- 位于java.math.BigDecimal
- 用于弥补float和double类型在运算时的误差问题。比如0.1+0.2,如果是double相加,会有很小的误差,但是用BigDecimal就不会有任何误差。
- BigDecimal的运算逻辑比较复杂,所以性能开销会比基本运算符大很多。慎用!
常用方法
方法声明 | 功能 |
BigDecimal(String val) | 根据参数指定的字符串来构造对象 |
BigDecimal add(BigDecimal augend) | 加法运算 |
BigDecimal subtract(BigDecimal subtrahend) | 减法运算 |
BigDecimal multiply(BigDecimal multiplicand) | 乘法运算 |
BigDecimal divide(BigDecimal divisor) | 除法运算 |
要点
除不尽就出问题!
- 猜猜下面代码的结果:
public class BigDecimalTest {
public static void main(String[] args) {
BigDecimal a = new BigDecimal("0.1");
BigDecimal b = new BigDecimal("0.7");
double c = 0.1;
double d = 0.7;
System.out.println(c / d);
System.out.println(a.divide(b));
}
}- 结果如下。可以看到double可以算无限小数,但是BigDecimal默认不能算,因为他觉得不!精!确!
0.14285714285714288
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
at java.base/java.math.BigDecimal.divide(BigDecimal.java:1723)
at com.UsefulNativeClass.BigDecimalTest.main(BigDecimalTest.java:14)- 那我想让BigDecimal进行四舍五入计算怎么办呢?就需要用到math包下面的另外一个类——RoundingMode(java 9之后就需要用这个枚举类了,之前是可以用BigDecimal里面的常量的)。直接举个例子吧~
public class BigDecimalTest {
public static void main(String[] args) {
BigDecimal a = new BigDecimal("0.11");
BigDecimal b = new BigDecimal("0.7111");
System.out.println(a.divide(b, RoundingMode.HALF_UP));
}
}- 运行结果是
0.15。RoundingMode这个枚举类里面还有很多别的舍入模式,感兴趣的老铁们可以自己去看看。 - 需要注意的是,这个舍入模式保留的小数点位数,取决于被除数,也就是例子中的a。
不要被double骗了
- 其实BigDecimal的构造方法很多,例如还可以通过double类型来构造。
- 那么问题来了~小伙伴们觉得下面的代码执行结果是什么呢?
public class BigDecimalTest {
public static void main(String[] args) {
BigDecimalTest test = new BigDecimalTest();
test.doubleTest();
}
private void doubleTest(){
double d = 1.2;
BigDecimal a = new BigDecimal(d);
System.out.println(a);
}
}- 很多小伙伴感觉太简单了:1.2!真的是么?结果如下。小伙伴们恍然大悟:哦!!!!虽然
d = 1.2,但是d的值却不是精确的1.2,所以构造方法的入参,也并不是1.2,而是一个无限接近的数字。所以,尽量不要使用double来构造BigDecimal哟~
1.1999999999999999555910790149937383830547332763671875BigInteger
基本概念
- 位于java.math.BigInteger。
- 这个类就很浅显了,作用是用来处理long都无法处理的超超超超超超超超超超超超超超超级大整数的运算。
- 因为是整数运算嘛,所以提供了取余运算,别的真没啥好说的。
常用方法
方法声明 | 功能 |
BigInteger(String val) | 根据参数指定的字符串来构造对象 |
BigInteger add(BigInteger val) | 加法运算 |
BigInteger subtract(BigInteger val) | 减法运算 |
BigInteger multiply(BigInteger val) | 乘法运算 |
BigInteger divide(BigInteger val) | 除法运算 |
BigInteger remainder(BigInteger val) | 取余运算 |
BigInteger[] divideAndRemainder(BigInteger val) | 同时进行取商和取余运算。返回值中,下标为0的是商,1是余。 |
















