初识常用类
大家好呀!我是小笙!我学习了韩顺平老师的常用类的知识,收获颇丰!现在来和大家分享笔记!
常用类
包装类(Wrapper)
包装类的分类
包装类 | 基本数据类型 | 直接父类 |
---|---|---|
boolean | Boolean | Object |
char | Character | Object |
byte | Byte | Number |
short | Short | Number |
int | Int | Number |
long | Long | Number |
float | Float | Number |
double | Double | Number |
Boolean
Character
Number父类下的直接子类
装箱 & 拆箱
- 自动拆装箱
- 手动拆装箱
public class Wrapper01 {
public static void main(String[] args) {
// jdk5以前手动装箱&手动拆箱;jdk5之后可以自动拆装箱
// 以Character为例
char name = 'n';
// 手动装箱
Character ch1 = new Character(name); // 不推荐
Character ch2 = Character.valueOf(name);
// 手动拆箱
char name2 = Character.valueOf(ch2); // 本质就是使用charValue方法
char name3 = ch1.charValue();
// 自动装箱
Character ch3 = name; // 本质使用的就是valueOf方法
// 自动拆箱
char CH4 = ch3; // 本质就是使用charValue方法
}
}
接下来我对于自动拆装箱的底层进行追踪结果
首先打四个断点,分别探索这四个断点的跳转
以下是依次跳转的函数
总结
- 手动拆装箱和自动拆装箱底层没有本质区别
- 包装类转 <=>基本数据类型
- 基本数据类型 --> 包装类 装箱 本质:valueOf函数
- 包装类 --> 基本数据类型 拆箱 本质:charValue函数
习题
// 如下输出结果是什么
习题1
Object obj = true? new Integer(1):new Double(2.0); // 三元运算符是一个整体
System.out.println(obj); // 1.0
习题2
Object obj1;
if(true){
obj1 = new Integer(1);
}else{
obj1 = new Double(2.0);
}
System.out.println(obj); // 1
包装类 <=> String类
Wrapper Vs String
public class WrapperVsString {
public static void main(String[]args){
// String类 转换成 包装类
String age = "120";
Integer age2 = Integer.valueOf(age); // 方式一:valueOf函数 本质上就是parseInt()方法
Integer a2 = Integer.parseInt(age); // 方式二:parseInt函数
Integer age3 = new Integer(age); //不推荐,本质就是parseInt()方法
// 包装类 转换成 String类
Integer height = 180; // 自动装箱
String h = String.valueOf(height); // 方式一:valueOf函数 本质就是调用toString()方法
String h2 = height + ""; // 方式二: 类型转换 Integer + ""
String h3 = height.toString(); // 方式三: toString()函数
/*
* String.valueOf()源码
* public static String valueOf(Object obj) {
* return (obj == null) ? "null" : obj.toString();
* }
*
* Integer.valueOf()源码
* public static Integer valueOf(String s) throws NumberFormatException {
* return Integer.valueOf(parseInt(s, 10)); // 10指的是传入的数字是十进制数
* }
*
* new Integer()源码
* @Deprecated(since="9")
* public Integer(String s) throws NumberFormatException {
* this.value = parseInt(s, 10);
* }
*/
}
}
Wrapper类的常用方法
以Integer包装类为例
包装类的相关面试题
public class Wrapper02 {
public static void main(String[] args) {
/*
* 源码:IntegerCache.low -128 IntegerCache.high 127
* public static Integer valueOf(int i) {
* if (i >= IntegerCache.low && i <= IntegerCache.high)
* return IntegerCache.cache[i + (-IntegerCache.low)];
* return new Integer(i);
* }
* 如果valueOf(value) value > -128 && value < 127 则 返回 IntegerCache.cache[i + (-IntegerCache.low)]
* 否则 返回新对象Integer
*/
System.out.println(new Integer(1) == new Integer(1)); // false
Integer a = 1;
Integer b = 1;
System.out.println(a==b); // true
Integer m = 128;
Integer n = 128;
System.out.println(m==n); // false
Integer x = 128;
int y = 128;
System.out.println(x==y); // true
}
}
String类
String类的概述
public static void main(String[] args) {
/**
* String
* 概念:是一组字符序列 本质上是char[] value 字符数组实现
* "Al_tair"被称为字符常量 用双引号括起来的字符序列
* 一个字符占用两个字节(每个字符不区分字母和汉字)
* public final class String 说明String的final类,不能被其它类继承
* private final byte[] value 用于存放字符串 value是用final修饰的类型,该数组不能指向新地址,但是能修改它的值
*/
String name = "Al_tair";
}
接口和构造器
String内存图
// 运行代码,内存图如下
class code{
public static void main(String[] args){
String a = "Al_tair";
String b = new String("Al_tair");
}
}
内存图: 字符串 VS 字符数组
结合代码和内存图分析
class Text{
String str = new String("lns");
// final指的是char类型数据存储的地址不能改变,但是值是可以改变的
final char[] ch = {'j','a','v','a'};
public void change(String str,char[] ch){
str = "zlr";
ch[1] = 'c';
}
public static void main(String[] args) {
Text text = new Text();
text.change(text.str,text.ch);
System.out.println(text.str.toString()+" and "+text.ch[1]); // lnsandc
}
}
String类的常用方法
- equals 区别大小写,判断字符串的内容是否相同
- equalsIgnoreCase 忽略大小写 来判断字符串的内容是否相同
- length 获取字符串的个数,或者称为字符串长度
- indexOf 获取字符在字符串中第一次出现的索引,索引从0开始,如果没有找到则返回-1
- lastindexOf 获取字符在字符串中最后一次出现的索引,索引从0开始,如果没有找到则返回-1
- substring 截取指定范围的字串
- trim 去掉字符串前后的空格
- charAt 获取某索引处的字符
- compareTo 比较两个字符串的大小,如果前者大于等于后者,则返回自然数;反之后者大,则返回负数
- intern 如果常量池中已经包含值相同的字符串,则返回常量池中的字符串引用地址,否则将String对象添加到常量池中,并返回String对象的引用
// equals()方法源码
public boolean equals(Object anObject) {
if (this == anObject) { // 地址是否相同
return true;
}
if (anObject instanceof String) { // 是否为String类或者String父类
String aString = (String)anObject;
if (!COMPACT_STRINGS || this.coder == aString.coder) {
return StringLatin1.equals(value, aString.value);
}
}
return false;
}
@HotSpotIntrinsicCandidate
public static boolean equals(byte[] value, byte[] other) {
if (value.length == other.length) {
for (int i = 0; i < value.length; i++) {
if (value[i] != other[i]) {
return false;
}
}
return true;
}
return false;
}
// 占位符的讲解 涉及方法format <=> c语言输出
// %s,%d,%.3f,%c
String name = "lns";
int age = 18;
double height = 185.35;
char gender = '男';
String Info = "姓名:%s\t年龄:%d\t身高:%.3f\t性别:%c";
String show = String.format(Info,name,age,height,gender);
System.out.println(show); // 姓名:lns 年龄:18 身高:185.350 性别:男
相关习题
// 习题1
String a = "l";
String b = new String("l");
System.out.println(a.equals(b)); // true
System.out.println(a == b); // false
System.out.println(a == b.intern()); // true
System.out.println(b == b.intern()); // false
// 习题2
// 2.1创建了几个对象 答:2
String s = "hello";
s = "haha";
// 2.2 创建了几个对象 答:1 结论:编译器会做优化,判断常量池对象是否有引用指向
String str = "hello" + "haha"; // 等价于 String str = "hellohaha";
// 2.3 创建了几个对象 答:3 结论:字符串常量相加地址存放在常量池,字符串变量相加地址存放在String对象中
// sum 指向的是value[](String对象),再指向常量池中"HelloString"字符串
public static void main(String[]args){
String m = "Hello";
String n = "String";
/*
* 解读:
* 1. 创建新对象 new StringBuilder();
* 2. 通过append函数添加字符串 “Hello”
* 3. 通过append函数添加字符串 “String”
* 4. 返回new String("HelloString");
*/
String sum = m + n;
}
// 分析sum 的指向和底层源码
// debug test
// first insert
public StringBuilder() {
super(16);
}
//secong insert str = "Hello"
public StringBuilder append(String str) {
super.append(str);
return this;
}
// third insert str = "String"
public StringBuilder append(String str) {
super.append(str);
return this;
}
// last one
public String toString() {
// Create a copy, don't share the array
return isLatin1() ? StringLatin1.newString(value, 0, count): StringUTF16.newString(value, 0, count);
}
StringBuffer类
概念:代表可变的字符序列,可以对字符串内容进行增删,是一个容器
构造方法
Constructor and Description |
---|
StringBuffer() 构造一个没有字符的字符串缓冲区,初始容量为16个字符。 |
StringBuffer(CharSequence seq) 构造一个包含与指定的相同字符的字符串缓冲区 CharSequence 。 |
StringBuffer(int capacity) 构造一个没有字符的字符串缓冲区和指定的初始容量。 |
StringBuffer(String str) 构造一个初始化为指定字符串内容的字符串缓冲区。 |
/*
* Constructs a string buffer with no characters in it and an
* initial capacity of 16 characters.
* StringBuffer()构造器
*/
@HotSpotIntrinsicCandidate
public StringBuffer() {
super(16); // 初始容量为16个字符 存储在父类的value数组中
}
String类 <=> StringBuffer类
String类和StringBuffer类的区别
- String保存的是字符串常量,里面的值不能更改,每次值的更新实际上就是更改地址,效率低
- Stringbuffer保存的是字符串变量,里面的值是可以改变的,不需要每次都更改地址,效率高
String类和StringBuffer类的相互转换
public static void main(String[] args) {
// String和StringBuffer的相互转换
// String => StringBuffer
String str = "lns";
StringBuffer stringBuffer = new StringBuffer(str); // 方式一: 使用StringBuffer构造器
StringBuffer append = new StringBuffer().append(str); // 方式二: 使用的是append方法
// StringBuffer => String
StringBuffer sbr = new StringBuffer("zlr");
String s = sbr.toString(); // 方式一: 使用toString方法
String s1 = new String(sbr); // 使用String构造器
}
常用方法
public static void main(String[] args) {
// 常用方法
// append 增
StringBuffer stringBuffer = new StringBuffer("");
stringBuffer.append("lns"); // lns
/*
* append源码
* 不管传入什么数据类型,返回StringBuffer类型
* public synchronized StringBuffer append(String str) {
* toStringCache = null;
* super.append(str);
* return this;
* }
*/
// delete 删除
// 删除索引范围 [start,end)
stringBuffer.delete(0,1); // 删除第一个字符 ns
// replace 替换
// 替换范围[start,end)
stringBuffer.replace(0, 1,"ln"); // lns
// indexOf 查找
// 查找第一次在字符串中出现的索引,如果查找到会返回你查找的字符串首个字母索引,如果找不到返回-1
stringBuffer.indexOf("ns"); // 1
// length 长度
System.out.println(stringBuffer.length()); // 3
}
相关习题
// 习题1
String str = null;
StringBuffer sb = new StringBuffer();
sb.append(str);
System.out.println(sb); // null
System.out.println(sb.length()); // 4
/*
* // 底层分析
* // StingBuffer类
* public synchronized StringBuffer append(String str) {
* toStringCache = null;
* super.append(str); // 跳转到父类
* return this;
* }
* // AbstractStringBuilder抽象类
* public AbstractStringBuilder append(String str) {
* if (str == null) {
* return appendNull(); // 跳转到该方法
* }
* int len = str.length();
* ensureCapacityInternal(count + len);
* putStringAt(count, str);
* count += len;
* return this;
* }
* // appendNull方法
* private AbstractStringBuilder appendNull() {
* ensureCapacityInternal(count + 4);
* int count = this.count;
* byte[] val = this.value;
* if (isLatin1()) {
* val[count++] = 'n';
* val[count++] = 'u';
* val[count++] = 'l';
* val[count++] = 'l';
* } else {
* count = StringUTF16.putCharsAt(val, count, 'n', 'u', 'l', 'l');
* }
* this.count = count;
* return this;
* }
*/
StringBuffer sb = new StringBuffer(str); // 抛出空指针异常 NullPointerException
/*
* AbstractStringBuilder(String str) {
* int length = str.length(); // str为null
* int capacity = (length < Integer.MAX_VALUE - 16)
* ? length + 16 : Integer.MAX_VALUE;
* final byte initCoder = str.coder();
* coder = initCoder;
* value = (initCoder == LATIN1)
* ? new byte[capacity] : StringUTF16.newBytesFor(capacity);
* append(str);
* }
*/
StringBuilder类
概念:一个可变的字符序列。 线程不安全。 此类设计用作简易替换为StringBuffer
在正在使用由单个线程字符串缓冲区的地方。 在可以的情况下,建议使用这个类别优先于StringBuffer
,因为它在大多数实现中将更快。
大部分与 StringBuffer类似
特殊点:没有做互斥处理,因此在单线程下使用
// 源码剖析 区别在于关键字 synchronized 保证线程安全
// StringBuffer 的append方法
@Override
@HotSpotIntrinsicCandidate
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
// StringBuilder 的append方法
@Override
@HotSpotIntrinsicCandidate
public StringBuilder append(String str) {
super.append(str);
return this;
}
String,StringBuffer,StringBuilder的区别
- String:不可变字符序列,效率低,但是因为存在常量池所以复用率高
- StringBuffer:可变字符序列,效率较高(增删),线程安全 、
- StringBuilder:可变字符序列,效率最高,线程不安全
使用原则
- 如果字符串存在大量的修改操作,一般使用StringBuffer或者StringBuider
- 如果字符串存在大量的修改操作,并在单线程的情况,使用StringBuilder
- 如果字符串存在大量的修改操作,并在多线程的情况,使用StringBuffer
- 如果字符串很少修改,被多个对象引用,使用String 比如:配置信息等
Math类
概念:Math类包含执行基本数学运算的方法
常用方法
public static void main(String[] args) {
// Math类中大部分是静态方法,可以直接通过类名.方法名访问
// abs 绝对值
int abs = Math.abs(-10);
System.out.println(abs); // 10
// pow 求幂
double pow = Math.pow(2,4);
System.out.println(pow); // 16.0
// ceil 向上取整,返回>=该参数的最小整数(整数会转换成double型)
double ceil = Math.ceil(-3.002);
System.out.println(ceil); // -3.0
// floor 向下取整,返回<=该参数的最大整数(整数会转换成double型)
double floor = Math.floor(3.2);
System.out.println(floor); // 3.0
// round 四舍五入 <=> Math.floor(参数+0.5)
double round = Math.round(3.24);
System.out.println(round); // 3.0
// sqrt 求开平方
double sqrt = Math.sqrt(4);
System.out.println(sqrt); // 2.0
// random 随机数 [0,1)
int random = (int)(Math.random()*50+50);
System.out.println(random); // 整数范围 [50,100)
}
Arrays类
概念:该类包含用于操作数组的各种方法(如排序和搜索),大部分方法也是静态方法
常用方法
toString方法
作用:输出数组
Integer[] array = {3,5,6,47,8};
// toString 输出数组
System.out.println(Arrays.toString(array)); // [3, 5, 6, 47, 8]
/*
* // toString方法源码
* public static String toString(int[] a) {
* if (a == null)
* return "null";
* int iMax = a.length - 1;
* if (iMax == -1)
* return "[]";
*
* StringBuilder b = new StringBuilder();
* b.append('[');
* for (int i = 0; ; i++) {
* b.append(a[i]);
* if (i == iMax)
* return b.append(']').toString();
* b.append(", ");
* }
* }
*/
sort方法
作用:排序数组默认从小到大
// sort重载,可以通过传入一个接口Comparator实现定制排序
Integer[] array = {3,5,6,47,8};
Arrays.sort(array);
System.out.println(Arrays.toString(array)); // [3, 5, 6, 8, 47]
Arrays.sort(array,new Comparator(){
@Override
public int compare(Object o1, Object o2) {
Integer i1 = (Integer)o1;
Integer i2 = (Integer)o2;
return i2 - i1; // 决定是升序还是降序
}
});
System.out.println(Arrays.toString(array)); // [47, 8, 6, 5, 3]
/**
* MySort的冒泡实现
* public class MySort {
* public static void main(String[] args) {
* int[] arr = {6,4,5,6,845,4,51};
* bubble(arr, new Comparator() {
* @Override
* public int compare(Object o1, Object o2) {
* int i1 = (Integer)o1;
* int i2 = (Integer)o2;
* return i1 - i2;
* }
* });
* System.out.println(Arrays.toString(arr));
* }
*
* public static void bubble(int[] arr, Comparator c){
* int temp = 0;
* for (int i = 0; i < arr.length - 1; i++) {
* for (int j = 0; j < arr.length - 1 - i; j++) {
* if(c.compare(arr[j],arr[j+1]) >= 0){
* temp = arr[j];
* arr[j] = arr[j+1];
* arr[j+1] = temp;
* }
* }
* }
* }
* }
*/
binarySearch方法
作用:通过二分搜索法进行查找,要求必须升序,如果数组中不存在,则返回 -(应该在的索引位置 + 1)
Integer[] array = {3,5,6,47,8};
Arrays.sort(array); // [3, 5, 6, 8, 47]
int index = Arrays.binarySearch(array,9);
System.out.println(index); // -5 应该在索引4位置(8和471之间),返回-(4+1)
/**
* binarySearch 源码
* private static int binarySearch0(Object[] a, int fromIndex, int toIndex, Object key) {
* int low = fromIndex;
* int high = toIndex - 1;
*
* while (low <= high) {
* int mid = (low + high) >>> 1;
* @SuppressWarnings("rawtypes") // 抑制警告
* Comparable midVal = (Comparable)a[mid];
* @SuppressWarnings("unchecked")
* int cmp = midVal.compareTo(key);
*
* if (cmp < 0)
* low = mid + 1;
* else if (cmp > 0)
* high = mid - 1;
* else
* return mid; // key found
* }
* return -(low + 1); // key not found.
* }
*/
其他方法
// copeOf 数组的赋值 如果赋值的长度大于原数组的长度,则多余的数据用null填入
Integer[] integers = Arrays.copyOf(array, array.length-1);
System.out.println(Arrays.toString(integers)); // [3, 5, 6, 8]
// fill 数组的填充 替换数组中的所有数据
int[] fillNum = {2,45,78,85,15};
Arrays.fill(fillNum,2);
System.out.println(Arrays.toString(fillNum)); // [2, 2, 2, 2, 2]
// equals 比较两个数组元素内容是否相同
int[] equalsNum = {2,45,78,85,15};
int[] equalsNum2 = {2,45,78,85,15};
System.out.println(Arrays.equals(equalsNum,equalsNum2)); // true
System类
概念:System
类包含几个有用的类字段和方法。 它不能被实例化。
常用方法
public static void main(String[] args) {
// gc 方法 垃圾回收器
new System01();
System.gc(); // 我已经被销毁了...
// currentTimeMillis 方法 在1970年1月1日UTC之间的当前时间和午夜之间的差异,以毫秒为单位。
System.out.println(System.currentTimeMillis()); // 1645776480314
// arraycopy 方法 复制数组
int[] src = {1,2,3};
int[] desc = {0,0,0};
/*
* 从左到右的五个参数描述
* src the source array. 被复制内容的数组
* srcPos starting position in the source array. 源数组索引位置(从哪个位置开始拷贝)
* dest the destination array. 复制内容得到的数组
* destPos starting position in the destination data. 目标数组的索引位置
* length the number of array elements to be copied. 拷贝的数组长度
*/
System.arraycopy(src,0,desc,0,3);
System.out.println(Arrays.toString(desc)); //[1, 2, 3]
System.out.println(src == desc); // false
// exit 方法 退出
System.out.println("程序开始");
/*
* status说明例子
* 在一个if-else判断中,如果我们程式是按照我们预想的执行,
* 到最后我们需要停止程式,那么我们使用System.exit(0),
* 而System.exit(1)一般放在catch块中,当捕获到异常,需要停止程式,
* 我们使用System.exit(1)。这个status=1是用来表示这个程式是非正常退出。
*/
System.exit(0); // System.exit(0)是正常退出程序,而System.exit(1)或者说非0表示非正常退出程序
System.out.println("程序结束"); // 不执行
}
@Override
protected void finalize(){
System.out.println("我已经被销毁了...");
}
BigIneger和BigDecimal类
概念:BigIneger 适合保存比较大的整型数据;BigDecimal 适合保存精度更高的浮点型数据
// BigIneger 适合保存比较大的整型数据 long数据类型无法存储
BigInteger bigInteger = new BigInteger("998456349564561256465489");
System.out.println(bigInteger); // 998456349564561256465489
// + - * / 运算 => 方法实现 add subtract multiply divide
bigInteger = bigInteger.add(new BigInteger("1"));
System.out.println(bigInteger); // 998456349564561256465490
bigInteger = bigInteger.divide(new BigInteger("2"));
System.out.println(bigInteger); // 499228174782280628232745
bigInteger = bigInteger.subtract(new BigInteger("2"));
System.out.println(bigInteger); // 499228174782280628232743
bigInteger = bigInteger.multiply(new BigInteger("2"));
System.out.println(bigInteger); // 998456349564561256465486
// BigDecimal 适合保存精度更高的浮点数 double数据类型无法存储
BigDecimal bigDecimal = new BigDecimal("9980.2561295645485648548485646541");
System.out.println(bigDecimal); // 9980.2561295645485648548485646541
// + - * / 运算 => 方法实现 add subtract multiply divide
bigDecimal = bigDecimal.add(new BigDecimal("1"));
System.out.println(bigDecimal); // 9981.2561295645485648548485646541
bigDecimal = bigDecimal.divide(new BigDecimal("2")); // 如果除不尽则返回算术异常
System.out.println(bigDecimal); // 4990.62806478227428242742428232705
bigDecimal = bigDecimal.subtract(new BigDecimal("2"));
System.out.println(bigDecimal); // 4988.62806478227428242742428232705
bigDecimal = bigDecimal.multiply(new BigDecimal("2"));
System.out.println(bigDecimal); // 9977.25612956454856485484856465410
// 解决小数除法异常问题:指定精度(JDK9以后不建议使用)
bigDecimal = bigDecimal.divide(new BigDecimal("2.3326"),BigDecimal.ROUND_CEILING);
System.out.println(bigDecimal); // 4277.31121047952866537548167909376
日期类
第一代日期类
Date:精确到毫秒,代表瞬间
SimpleDateFormat:格式和解析日期类(日期 <=> 文本)
public static void main(String[] args) throws ParseException {
// Date 日期类
Date date = new Date(); // 当前日期
System.out.println(date); // Fri Feb 25 16:58:51 CST 2022
Date date2 = new Date(4564956); // 输入距离1970年1月1日的毫秒数
System.out.println(date2); // Thu Jan 01 09:16:04 CST 1970
// SimpleDateFormat 格式和解析日期类 按照自己的格式的日期 年 月 日 时 分 秒 星期 (规定如下图)
SimpleDateFormat sdf = new SimpleDateFormat("YYYY年MM月DD日 hh:mm:ss E");
System.out.println(sdf.format(date)); // 2022年02月56日 05:07:32 周五
String dateStr = "2021年02月56日 05:07:32 周一";
System.out.println(sdf.format(sdf.parse(dateStr))); // 2021年12月363日 05:07:32 周一 会存在编译异常
}
SimpleDateFormat的规定格式
第二代日期类
Calendar类(日历) 是一个抽象类
// 抽象类 可以通过getInstance方法获取实例
Calendar calendar = Calendar.getInstance();
System.out.println(calendar);
System.out.println("年:"+calendar.get(calendar.YEAR)); // 年:2022
System.out.println("月:"+calendar.get(calendar.MONTH)+1); // 月:2 源码:JANUARY} which is 0
System.out.println("日:"+calendar.get(calendar.DAY_OF_MONTH)); // 日:25
System.out.println("小时:"+calendar.get(calendar.HOUR)); // 小时:8
System.out.println("分钟:"+calendar.get(calendar.MINUTE)); // 分钟:11
System.out.println("秒:"+calendar.get(calendar.SECOND)); // 秒:46
第三代日期类 (JDK8)
LocalDate 日期:年月日
LocalTime 时间:时分秒
LocalDateTime:年月日 时分秒
LocalDateTime localDateTime = LocalDateTime.now();
LocalTime localTime = LocalTime.now();
LocalDate localDate = LocalDate.now();
// localDateTime: 2022-02-25T20:30:19.250574 LocalTime: 20:30:19.250574 LocalDate: 2022-02-25
System.out.println("localDateTime: "+localDateTime+" LocalTime: "+
localTime+" LocalDate: "+localDate);
System.out.println("年: "+localDateTime.getYear()); // 年: 2022
System.out.println("月: "+localDateTime.getMonth()); // 月: FEBRUARY
System.out.println("日: "+localDateTime.getDayOfMonth()); // 日: 25
System.out.println("时: "+localDateTime.getHour()); // 时: 20
System.out.println("分: "+localDateTime.getMinute()); // 分: 33
System.out.println("秒: "+localDateTime.getSecond()); // 秒: 45
DateTimeFormatter格式日期类
// DateTimeFormatter 格式日期类
LocalDateTime localDateTime = LocalDateTime.now();
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("YYYY年MM月DD日 hh:mm:ss E");
System.out.println(dateTimeFormatter.format(localDateTime)); // 2022年02月56日 08:39:43 周五
// 所有字母“A”至“Z”和“a”至“z”保留为图案字母。 定义了以下图案字母:
Symbol Meaning Presentation Examples
------ ------- ------------ -------
G era text AD; Anno Domini; A
u year year 2004; 04
y year-of-era year 2004; 04
D day-of-year number 189
M/L month-of-year number/text 7; 07; Jul; July; J
d day-of-month number 10
Q/q quarter-of-year number/text 3; 03; Q3; 3rd quarter
Y week-based-year year 1996; 96
w week-of-week-based-year number 27
W week-of-month number 4
E day-of-week text Tue; Tuesday; T
e/c localized day-of-week number/text 2; 02; Tue; Tuesday; T
F week-of-month number 3
a am-pm-of-day text PM
h clock-hour-of-am-pm (1-12) number 12
K hour-of-am-pm (0-11) number 0
k clock-hour-of-am-pm (1-24) number 0
H hour-of-day (0-23) number 0
m minute-of-hour number 30
s second-of-minute number 55
S fraction-of-second fraction 978
A milli-of-day number 1234
n nano-of-second number 987654321
N nano-of-day number 1234000000
V time-zone ID zone-id America/Los_Angeles; Z; -08:30
z time-zone name zone-name Pacific Standard Time; PST
O localized zone-offset offset-O GMT+8; GMT+08:00; UTC-08:00;
X zone-offset 'Z' for zero offset-X Z; -08; -0830; -08:30; -083015; -08:30:15;
x zone-offset offset-x +0000; -08; -0830; -08:30; -083015; -08:30:15;
Z zone-offset offset-Z +0000; -0800; -08:00;
p pad next pad modifier 1
' escape for text delimiter
'' single quote literal '
[ optional section start
] optional section end
# reserved for future use
{ reserved for future use
} reserved for future use
Instant 时间戳
// Instant -> Date
Instant instant = Instant.now();
System.out.println(instant); // 2022-02-25T14:48:47.557358800Z
java.util.Date from = Date.from(instant);
System.out.println(from); // Fri Feb 25 22:48:47 CST 2022
// Date -> Instant
Instant instant1 = from.toInstant();
System.out.println(instant1); // 2022-02-25T14:55:27.377Z
相关面试题
1.String类有哪些方法?
String类是Java最常用的API,它包含了大量处理字符串的方法,比较常用的有:
- char charAt(int index):返回指定索引处的字符;
- String substring(int beginIndex, int endIndex):从此字符串中截取出一部分子字符串;
- String[] split(String regex):以指定的规则将此字符串分割成数组;
- String trim():删除字符串前导和后置的空格;
- int indexOf(String str):返回子串在此字符串首次出现的索引;
- int lastIndexOf(String str):返回子串在此字符串最后出现的索引;
- boolean startsWith(String prefix):判断此字符串是否以指定的前缀开头;
- boolean endsWith(String suffix):判断此字符串是否以指定的后缀结尾;
- String toUpperCase():将此字符串中所有的字符大写;
- String toLowerCase():将此字符串中所有的字符小写;
- String replaceFirst(String regex, String replacement):用指定字符串替换第一个匹配的子串;
- String replaceAll(String regex, String replacement):用指定字符串替换所有的匹配的子串。
2.String可以被继承吗?
String类由final修饰,所以不能被继承。
扩展阅读
在Java中,String类被设计为不可变类,主要表现在它保存字符串的成员变量是final的。
- Java 9之前字符串采用char[]数组来保存字符,即 private final char[] value;
- Java 9做了改进,采用byte[]数组来保存字符,即 private final byte[] value;
之所以要把String类设计为不可变类,主要是出于安全和性能的考虑,可归纳为如下4点。
- 由于字符串无论在任何 Java 系统中都广泛使用,会用来存储敏感信息,如账号,密码,网络路径,文件处理等场景里,保证字符串 String 类的安全性就尤为重要了,如果字符串是可变的,容易被篡改,那我们就无法保证使用字符串进行操作时,它是安全的,很有可能出现 SQL 注入,访问危险文件等操作。
- 在多线程中,只有不变的对象和值是线程安全的,可以在多个线程中共享数据。由于 String 天然的不可变,当一个线程”修改“了字符串的值,只会产生一个新的字符串对象,不会对其他线程的访问产生副作用,访问的都是同样的字符串数据,不需要任何同步操作。
- 字符串作为基础的数据结构,大量地应用在一些集合容器之中,尤其是一些散列集合,在散列集合中,存放元素都要根据对象的 hashCode() 方法来确定元素的位置。由于字符串 hashcode 属性不会变更,保证了唯一性,使得类似 HashMap,HashSet 等容器才能实现相应的缓存功能。由于 String 的不可变,避免重复计算 hashcode,只要使用缓存的 hashcode 即可,这样一来大大提高了在散列集合中使用 String 对象的性能。
- 当字符串不可变时,字符串常量池才有意义。字符串常量池的出现,可以减少创建相同字面量的字符串,让不同的引用指向池中同一个字符串,为运行时节约很多的堆内存。若字符串可变,字符串常量池失去意义,基于常量池的 String.intern() 方法也失效,每次创建新的字符串将在堆内开辟出新的空间,占据更多的内存。
因为要保证String类的不可变,那么将这个类定义为final的就很容易理解了。如果没有final修饰,那么就会存在String的子类,这些子类可以重写String类的方法,强行改变字符串的值,这便违背了String类设计的初衷。
3.说一说String和StringBuffer有什么区别
String类是不可变类,即一旦一个String对象被创建以后,包含在这个对象中的字符序列是不可改变的,直至这个对象被销毁。
StringBuffer对象则代表一个字符序列可变的字符串,当一个StringBuffer被创建以后,通过StringBuffer提供的append()、insert()、reverse()、setCharAt()、setLength()等方法可以改变这个字符串对象的字符序列。一旦通过StringBuffer生成了最终想要的字符串,就可以调用它的toString()方法将其转换为一个String对象。
4.说一说StringBuffer和StringBuilder有什么区别
tringBuffer、StringBuilder都代表可变的字符串对象,它们有共同的父类 AbstractStringBuilder,并且两个类的构造方法和成员方法也基本相同。不同的是,StringBuffer是线程安全的,而StringBuilder是非线程安全的,所以StringBuilder性能略高。一般情况下,要创建一个内容可变的字符串,建议优先考虑StringBuilder类。
5.使用字符串时,new和""推荐使用哪种方式?
先看看 “hello” 和 new String(“hello”) 的区别:
- 当Java程序直接使用 “hello” 的字符串直接量时,JVM将会使用常量池来管理这个字符串;
- 当使用 new String(“hello”) 时,JVM会先使用常量池来管理 “hello” 直接量,再调用String类的构造器来创建一个新的String对象,新创建的String对象被保存在堆内存中。
显然,采用new的方式会多创建一个对象出来,会占用更多的内存,所以一般建议使用直接量的方式创建字符串。
6.两个字符串相加的底层是如何实现的?
如果拼接的都是字符串直接量,则在编译时编译器会将其直接优化为一个完整的字符串,和你直接写一个完整的字符串是一样的。
如果拼接的字符串中包含变量,则在编译时编译器采用StringBuilder对其进行优化,即自动创建StringBuilder实例并调用其append()方法,将这些字符串拼接在一起。
7.遇到过异常吗,如何处理?
在Java中,可以按照如下三个步骤处理异常:
-
捕获异常
将业务代码包裹在try块内部,当业务代码中发生任何异常时,系统都会为此异常创建一个异常对象。创建异常对象之后,JVM会在try块之后寻找可以处理它的catch块,并将异常对象交给这个catch块处理。
-
处理异常
在catch块中处理异常时,应该先记录日志,便于以后追溯这个异常。然后根据异常的类型、结合当前的业务情况,进行相应的处理。比如,给变量赋予一个默认值、直接返回空值、向外抛出一个新的业务异常交给调用者处理,等等。
-
回收资源
如果业务代码打开了某个资源,比如数据库连接、网络连接、磁盘文件等,则需要在这段业务代码执行完毕后关闭这项资源。并且,无论是否发生异常,都要尝试关闭这项资源。将关闭资源的代码写在finally块内,可以满足这种需求,即无论是否发生异常,finally块内的代码总会被执行。
8.请介绍Java的异常接口
Throwable是异常的顶层父类,代表所有的非正常情况。它有两个直接子类,分别是Error、Exception。
Error是错误,一般是指与虚拟机相关的问题,如系统崩溃、虚拟机错误、动态链接失败等,这种错误无法恢复或不可能捕获,将导致应用程序中断。通常应用程序无法处理这些错误,因此应用程序不应该试图使用catch块来捕获Error对象。在定义方法时,也无须在其throws子句中声明该方法可能抛出Error及其任何子类。
Exception是异常,它被分为两大类,分别是Checked异常和Runtime异常。所有的RuntimeException类及其子类的实例被称为Runtime异常;不是RuntimeException类及其子类的异常实例则被称为Checked异常。Java认为Checked异常都是可以被处理(修复)的异常,所以Java程序必须显式处理Checked异常。如果程序没有处理Checked异常,该程序在编译时就会发生错误,无法通过编译。Runtime异常则更加灵活,Runtime异常无须显式声明抛出,如果程序需要捕获Runtime异常,也可以使用try…catch块来实现。
9.int和Integer有什么区别,二者在做==运算时会得到什么结果?
int是基本数据类型,Integer是int的包装类。二者在做==运算时,Integer会自动拆箱为int类型,然后再进行比较。届时,如果两个int值相等则返回true,否则就返回false。
10.说一说自动装箱、自动拆箱的应用场景
自动装箱、自动拆箱是JDK1.5提供的功能。
自动装箱:可以把一个基本类型的数据直接赋值给对应的包装类型;
自动拆箱:可以把一个包装类型的对象直接赋值给对应的基本类型;
通过自动装箱、自动拆箱功能,可以大大简化基本类型变量和包装类对象之间的转换过程。比如,某个方法的参数类型为包装类型,调用时我们所持有的数据却是基本类型的值,则可以不做任何特殊的处理,直接将这个基本类型的值传入给方法即可。
11.为啥要有包装类?
Java语言是面向对象的语言,其设计理念是“一切皆对象”。但8种基本数据类型却出现了例外,它们不具备对象的特性。正是为了解决这个问题,Java为每个基本数据类型都定义了一个对应的引用类型,这就是包装类。