【JavaSE】常用类:String、LocalDateTime......

尚硅谷JavaSE笔记合集

文章名链接
【JavaSE】异常文章地址
【JavaSE】常用类:String、LocalDateTime…文章地址
【JavaSE】枚举文章地址
【JavaSE】注解文章地址
【JavaSE】集合框架文章地址 | HashMap源码解析 | List相关实现类源码解析
【JavaSE】泛型文章地址
【JavaSE】IO流文章地址 | 字符编码详解
【JavaSE】网络编程,BIO需求演进文章地址
【JavaSE】反射文章地址
【JavaSE】jdk8新特性文章地址

一、String

1.1 概述

/**
 * String:
 *      1.String声明为final,不可被继承
 *      2.String实现Serializable接口,表示字符串是支持序列化的
 *              实现Comparable接口,表示String是可以比较大小的
 *      3.String内部定义了 final char value[] 用于存储字符串数据
 *		4.不可变的字符序列,具有不可变性。
 *
 */
public class StringTest {
    @Test
    public void test(){
    }
}

1.2 不可变性

/**
 * String:
 *      4.不可变的字符序列,具有不可变性。
 *          (1) 重新赋值时,需要重写指定内存区域赋值,不是改变原有的value进行赋值。
 *          (2) 进行连接时,需要重新指定内存区域赋值,不是改变原有的value完成链接。
 *          (3) 调用replace()方法时,需要重新指定内存区域赋值,不是改变原有的value进行替换。
 *      5.通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中。
 *      6.字符串常量池中是不会存储相同内容的字符串的。
 *
 */
public class StringTest {
    @Test
    public void test(){
        //4.1 重新赋值时,需要重写指定内存区域赋值,不是改变原有的value进行赋值。
        String str1=new String("hello");
        String str2=str1;
        str2="hi";
        System.out.println(str1==str2); //false

        //4.2 进行连接时,需要重新指定内存区域赋值,不是改变原有的value完成链接。
        String str3=new String("hello");
        String str4=str3;
        str3+=" String";
        System.out.println(str3==str4); //false

        //4.3 调用replace()方法时,需要重新指定内存区域赋值,不是改变原有的value进行替换。
        String str5=new String("hello");
        String str6=str5.replace("hello","hi");
        System.out.println(str5==str6); //false

        //6. 字符串常量池中是不会存储相同内容的字符串的
        String str7="abc";
        String str8="abc";
        System.out.println(str7==str8); //true
    }
}

在这里插入图片描述

1.3 对象创建内存分析

第二类:堆String对象可以是自定义对象

/**
 * 对象创建内存模型:栈变量,堆String对象,堆char数组对象,字符串常量池(不会存在相同内容)
 *	第一类:
 *		栈变量——>堆String对象——>堆char数组对象
 *	第二类:
 *		栈变量——>堆String对象——>字符串常量池
 *	第三类:
 *		栈变量——>字符串常量池
 *	第四类:
 *		栈变量=null
 */

在这里插入图片描述

1.4 字符串拼接内存分析

/**
 * 常量池:不会存在相同内容
 * 
 * 字符串拼接内存模型:
 *	  属于第二类情况
 *		- 其中有一个是变量
 *	  属于第三类情况
 *		- 全部都是常量(注意:包括变量名引用常量值)
 */
public class StringTest {
    @Test
    public void test3(){
        String a="a";
        String b="b";
        final String C="c";	//常量
        //1.属于第二类情况
        String ab=a+b;
        ab=a+"b";
        ab="a"+b;
        //2.属于第三类情况:注意,包括变量名引用常量值
        ab="a"+"b";
        ab="a"+C;
    }
}

1.5 面试题

/**
 *  1.填写程序的输出内容?
 *	2.String str=new String("abc")一共创建了多少个对象?
 *		- 两个。一个为堆内存的String对象,另一个为常量池中的引用
 */
public class StringTest {
    String str = new String("good");
    char[] ch = { 't', 'e', 's', 't' };

    public void change(String str, char ch[]) {
        str = "test ok";
        ch[0] = 'b';
    }
    public static void main(String[] args) {
        StringTest ex = new StringTest();
        ex.change(ex.str, ex.ch);
        System.out.println(ex.str); //good
        System.out.println(ex.ch);  //best
    }
}

1.6 JVM相关内存结构

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.7 常用方法

1.7.1 返回char

  • 获取字符:charAt(int)
@Test
public void test1(){
    String str="abc";
    System.out.println(str.charAt(0)); //a
}

1.7.2 返回String

  • 去掉空白:去掉首尾空白
  • 连接:concat(String)
  • 转变大小写
    • toLowerCase():返回小写字符串,使用默认语言环境
    • toUpperCase():返回大写字符串,使用默认语言环境
  • 替换
    • replace(char , char ):用新字符替换所有旧字符
    • replace(CharSequence , CharSequence ):用新字符串替换所有旧字符串
    • replaceAll(String regex, String ):用新字符串替换所有正则匹配子字符串
    • replaceFirst(String regex, String ):用新字符串替换第一个正则匹配子字符串
  • 截取
    • substring(int):截取索引到结尾的子字符串
    • substring(int ,int ):截取 [int ,int ) 的子字符串
@Test
public void test2(){
    String str=" aaa ";
    //1.去掉空白
    str=str.trim();
    System.out.println(str); //aaa
    //2.连接
    System.out.println(str.concat("AAA")); //aaaAAA
    //3.转变大小写
    System.out.println(str.toLowerCase()); //aaa
    System.out.println(str.toUpperCase()); //AAA
    //4.替换
    System.out.println(str.replace('a','b')); //bbb
    System.out.println(str.replace("aa","bb")); //bba
    System.out.println(str.replaceAll(".","b")); //bbb
    System.out.println(str.replaceFirst(".","b")); //baa
    //5.截取
    System.out.println(str.substring(0)); //aaa
    System.out.println(str.substring(0,3)); //aaa
}

1.7.3 返回String[]

  • 拆分(String[])
    • split(String regex):使用正则拆分
    • split(String regex, int ):使用正则拆分成有限个数,剩余的为最后字符串元素
@Test
public void test3(){
    String str="a-a-a";
    //1.拆分
    System.out.println(Arrays.toString(str.split("-"))); //[a, a, a]
    System.out.println(Arrays.toString(str.split("-",2))); //[a, a-a]
}

1.7.4 返回int

  • 获取长度:length()
  • 获取索引
    • indexOf(String str):返回指定字符串首次出现的索引。未找到返回-1
    • indexOf(String , int ):返回指定字符串首次出现的索引,从指定的索引开始。未找到返回-1
    • lastIndexOf(String ):返回指定字符串最后出现的索引。未找到返回-1
    • lastIndexOf(String , int ):返回指定字符串最后出现的索引,从指定索引结束。未找到返回-1
  • 比较
    • compareTo(String):比较两个字符串的大小
/**
 *   什么情况下,indexOf(str)和lastIndexOf(str)返回值相同?
 *       - 情况一:存在唯一的一个str。
 *		 - 情况二:不存在str
 */
@Test
public void test4(){
    String str="aaa";
    //1.获取长度
    System.out.println(str.length()); //3
    //2.获取索引
    System.out.println(str.indexOf("a")); //0
    System.out.println(str.indexOf("a",1)); //1
    System.out.println(str.lastIndexOf("a")); //2
    System.out.println(str.lastIndexOf("a",2)); //2
    //3.比较
    System.out.println(str.compareTo("bbb")); //-1,小于
}

1.7.5 返回boolean

  • 判断(boolean)
    • isEmpty():判断是否是空字符串
    • equalsIgnoreCase(String):判断两个字符串内容是否相同,忽略大小写
    • matches(String regex):判断是否与正则匹配
    • contains(CharSequence ):判断是否包含指定字符串
    • endsWith(String ):判断是否以指定后缀结尾
    • startsWith(String ):判断是否以指定后缀开始
    • startsWith(String , int ):判断从指定索引开始的子字符串是否以指定后缀开始
@Test
public void test5(){
    String str="aaa";
    //1.判断
    System.out.println(str.isEmpty()); //false
    System.out.println(str.equalsIgnoreCase("a")); //false
    System.out.println(str.matches("\\w+")); //true
    System.out.println(str.contains("a")); //true
    System.out.println(str.startsWith("aa")); //true
    System.out.println(str.startsWith("aa",1)); //true
    System.out.println(str.endsWith("aa")); //true
}

1.7.6 其他

  • toCharArray():String转换为char[]
  • getBytes():String转化为byte[]
  • valueOf(Object):Object转换为String

1.8 类型转换

  • 基本数据类型包装类
    • String——>基本数据类型包装类:包装类.parseXxx(str)、包装类.valueOf(str)
    • 基本数据类型包装类——>String:String.valueOf(int)
  • char[]
    • String——>char[]:str.toCharArray()
    • char[]——>String:new String(arr)
  • byte[]
    • String——>byte[]:编码,str.getBytes()
    • byte[]——>String:解码,new String(arr)
  • StringBuilder、StringBuffer
    • String->StringBuilder、StringBuffer:构造器,StringBuilder(String)、StringBuffer(String)
    • StringBuilder、StringBuffer->String:构造器,String(StringBuilder)、String(StringBuffer)或者toString()
/**
 * 说明:解码时,要求解码使用的字符集必须与编码时使用的字符集一致,否则会出现乱码。
 * utf-8:包含所有国家的字符集,涵盖广   gbk:中国制定的编码标准,涵盖较少
 */
public class StringTypeTurnTest {
    @Test
    public void test1(){
        //1.1 String——>基本数据类型包装类
        String str="123";
        System.out.println(Integer.valueOf(str)); //123
        System.out.println(Integer.parseInt(str)); //123
        //1.2 基本数据类型包装类——>String
        int i=123;
        Integer inte=123;
        System.out.println(String.valueOf(i)); //123
        System.out.println(String.valueOf(inte)); //123
    }
    @Test
    public void test2(){
        //2.1 String——>char[]
        String str="123";
        System.out.println(Arrays.toString(str.toCharArray())); //[1, 2, 3]
        //2.2 char[]——>String
        char[] arr={'1','2','3'};
        System.out.println(new String(arr)); //123
    }
    @Test
    public void test3(){
        //3.1 String——>byte[]:编码
        String str="abc";
        System.out.println(Arrays.toString(str.getBytes())); //[97, 98, 99]
        //3.2 byte[]——>String:解码
        byte[] bt={97,98,99};
        System.out.println(new String(bt)); //abc
    }
}

1.9 面试题

1、模拟一个trim方法,去除字符串两端的空格。

public String trimTest(String str){
    if (str==null){
        throw new RuntimeException("字符串为null!");
    }
    //1.保存头部开始第一个非空索引
    int start=0;
    //2.保存尾部开始第一个非空索引
    int end=str.length()-1;
    //3.计算头部开始第一个非空索引
    while(start<str.length() && str.startsWith(" ",start)){
        start++;
    }
    //4.计算尾部开始第一个非空索引
    while(end>=0 && str.substring(0,end+1).endsWith(" ")){
        end--;
    }
    if (start>end){
        return "";
    }
    return str.substring(start,end+1);
}

2、将一个字符串进行反转。将字符串中指定部分进行反转。比如“abcdefg”反转为”abfedcg”

public String reverseTest(String str,int startIndex,int endIndex){
    if (str==null){
        throw new RuntimeException("字符串为null!");
    }
    if (startIndex<0 || endIndex<0 || startIndex>endIndex || startIndex>=str.length() || endIndex>=str.length()){
        throw new RuntimeException("整型参数不合法!");
    }
    //方式一:转换为数组,将元素进行交换
    char[] chars1 = str.toCharArray();
    char flush=' ';
    int time=(endIndex-startIndex)/2;
    for (int i = 0; i < time; i++) {
        flush=chars1[endIndex-i-1];
        chars1[endIndex-i-1]=chars1[startIndex+i];
        chars1[startIndex+i]=flush;
    }
    String way1=new String(chars1);
    //方式二:转换为数组,从endIndex开始进行拼接
    char[] chars2 = str.toCharArray();
    String way2=str.substring(0,startIndex);
    for (int i = 0; i < endIndex-startIndex; i++) {
        way2+=chars2[endIndex-i-1];
    }
    way2+=str.substring(endIndex);
    //方式三:转换为StringBuilder,从endIndex开始进行添加
    StringBuilder sb=new StringBuilder(str.length());
    sb.append(str.substring(0,startIndex));
    for (int i = 0; i < endIndex-startIndex; i++) {
        sb.append(str.charAt(endIndex-i-1));
    }
    sb.append(str.substring(endIndex));
    String way3=sb.toString();
    return way1;
}

3、获取一个字符串在另一个字符串中出现的次数

public int counter(String str,String src){
    if (str==null || src==null){
        throw new RuntimeException("参数值为null!");
    }
    if(str.length()>=src.length() && str.contains(src)){
        //1.定义计数器
        int count=0;
        int fromIndex=0;
        while((fromIndex=str.indexOf(src,fromIndex))!=-1){
            count++;
            fromIndex+=src.length();
        }
        return count;
    }
    return 0;
}

4、获取两个字符串中最大相同子串。

/**
 *  注意-其他方式:将小串从大到小进行拆分,然后与大串进行匹配,如果存在则就是最大相同子串
 */
public String sameMax(String src1,String src2){
    //判断小串,保证src1为大串,src2为小串
    if (src1.length()<src2.length()){
        String flush=src1;
        src1=src2;
        src2=flush;
    }
    //1.保存最大子串
    String result="";
    //2.保存最大子串长度
    int length=0;
    //3.循环遍历src1,在每次循环中遍历匹配src2
    char[] chars1 = src1.toCharArray();
    char[] chars2 = src2.toCharArray();
    for (int i = 0; i < chars1.length-chars2.length+1; i++) {
        for (int j = 0; j < chars2.length; j++) {
            for (int k = 0; k < chars2.length-j; k++) {
                if(chars1[i+k]!=chars2[j+k]){
                    break;
                }
                if (k+1>length){
                    length=k+1;
                    result=src2.substring(j,k+1);
                }
            }
        }
    }
    return result;
}

5、对字符串中字符进行自然顺序排序

public String sort(String src){
    char[] chars = src.toCharArray();
    Arrays.sort(chars);
    return new String(chars);
}

二、可变字符序列

2.1 比较

/**
 * String、StringBuffer、StringBuilder三者的异同?
 * 
 *	相同点:底层使用char[]存储
 * 
 *	不同点:
 *		String VS StringBuilder
 *			- String不可变,效率低。StringBuilder可变,效率高。
 *			- 都是线程不安全
 * 		StringBuffer VS StringBuilder
 *			- StringBuffer线程安全,效率低;StringBuilder线程不安全,效率高。
 *			- 都是可变的字符序列
 * 			- StringBuilder是jdk5.0新增的
 */

2.2 源码分析

/**
 * 1.对象创建,底层数组长度
 * 		- new String():new char[0]
 *		  new StringBuffer():new char[16]
 * 		- new String("abc"):new char[]{'a','b','c'};
 * 		  new StringBuffer("abc"):new char["abc".length() + 16];
 * 2.字符串修改
 *		- str="a":重新创建对象返回
 * 		- strBuffer.append('a'):value[0] = 'a'
 *
 * 注意:
 * 		1.StringBuffer的方法:length(),
 * 		2.扩容问题:新数组扩容为原来 2倍+2 或 所需容量,并复制到新数组中。消耗性能
 *
 * 建议:开发中建议大家使用:StringBuffer(int)、StringBuilder(int),减少扩容次数
 *
 */

//1.1 StringBuffer():底层数组长度16
public StringBuffer() {
    super(16);
}
//1.2 StringBuffer(int):自定义底层数组长度
public StringBuffer(int capacity) {
    super(capacity);
}
AbstractStringBuilder(int capacity) {
    value = new char[capacity];
}
//1.3 StringBuffer(String):底层数组长度 str.length+16
public StringBuffer(String str) {
    super(str.length() + 16);
    append(str);
}

//2.1添加元素
public synchronized StringBuffer append(String str) {
    toStringCache = null;
    //2.2 添加元素
    super.append(str);
    return this;
}
	//2.2 添加元素
	public AbstractStringBuilder append(String str) {
        //2.2.1 判断是否为null
	    if (str == null)
	        return appendNull();
	    int len = str.length();
        //2.2.2 判断长度是否足够
	    ensureCapacityInternal(count + len);
        //2.2.3 拷贝元素到数组中
	    str.getChars(0, len, value, count);
	    count += len;
	    return this;
	}
		//2.2.1 判断是否为null添加"null"
		private AbstractStringBuilder appendNull() {
    	    int c = count;
    	    ensureCapacityInternal(c + 4);
    	    final char[] value = this.value;
    	    value[c++] = 'n';
    	    value[c++] = 'u';
    	    value[c++] = 'l';
    	    value[c++] = 'l';
    	    count = c;
    	    return this;
    	}
		//2.2.2 判断长度是否足够
		private void ensureCapacityInternal(int minimumCapacity) {
    	    if (minimumCapacity - value.length > 0) {
                //2.2.2.1 扩容
    	        value = Arrays.copyOf(value,
    	                newCapacity(minimumCapacity));
    	    }
    	}
			//2.2.2.1 扩容为原来的2倍+2 或者 为所需长度
			private int newCapacity(int minCapacity) {
    		    int newCapacity = (value.length << 1) + 2;
    		    if (newCapacity - minCapacity < 0) {
    		        newCapacity = minCapacity;
    		    }
    		    return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
    		        ? hugeCapacity(minCapacity)
    		        : newCapacity;
    		}
		//2.2.3 拷贝元素到数组中
		public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
        	if (srcBegin < 0) {
        	    throw new StringIndexOutOfBoundsException(srcBegin);
        	}
            //判断长度是否足够
        	if (srcEnd > value.length) {
        	    throw new StringIndexOutOfBoundsException(srcEnd);
        	}
        	if (srcBegin > srcEnd) {
        	    throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
        	}
            //拷贝元素到数组中
        	System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
    	}

2.3 常用方法

/**
 * 常用方法:
 *  1.增:append(Object)
 *  2.删:delete(int,int)、deleteCharAt(int)
 *  3.改:setCharAt(int,char)、replace(int, int, String)
 *  4.查:charAt(int)
 *  5.插:insert(int,Object)
 *  6.长度:length()
 *  7.逆序、截取、获取索引下标:reverse()、substring(int,int)、indexOf(char)
 */
public class StringBuildTest {
    @Test
    public void test1(){
        StringBuilder sb=new StringBuilder(32);
        //1.增:append(Object)
        System.out.println(sb.append("abc")); //abc
        //2.删:delete(int,int)、deleteCharAt(int)
        System.out.println(sb.delete(0,1)); //bc
        //3.改:setCharAt(int,char)、replace(int, int, String)
        sb.setCharAt(0,'a');
        System.out.println(sb); //ac
        //4.查:charAt(int)
        System.out.println(sb.charAt(0)); //a
        //5.插:insert(int,Object)
        System.out.println(sb.insert(0,'a')); //aac
        //6.长度:length()
        System.out.println(sb.length()); //3
        //7.逆序、截取、获取索引下标
        System.out.println(sb.reverse()); //caa
        System.out.println(sb.substring(0,1)); //c
        System.out.println(sb.indexOf("a")); //1
    }
}

2.4 效率对比

public class StringBuildTest {
    /**
     * 效率对比:String、StringBuffer、StringBuilder,循环拼接20000次
     */
    @Test
    public void test2(){
        String str="";
        StringBuilder sbuilder=new StringBuilder();
        StringBuffer sbuffer=new StringBuffer();
        Long start=0L;
        Long end=0L;
        //1.String
        start=System.currentTimeMillis();
        for (int i = 0; i < 20000; i++) {
            str+=i;
        }
        end=System.currentTimeMillis();
        System.out.println("String消耗时间:"+(end-start)); //1299
        //2.StringBuffer
        start=System.currentTimeMillis();
        for (int i = 0; i < 20000; i++) {
            sbuffer.append(i);
        }
        end=System.currentTimeMillis();
        System.out.println("StringBuffer消耗时间:"+(end-start)); //2
        //3.StringBuilder
        start=System.currentTimeMillis();
        for (int i = 0; i < 20000; i++) {
            sbuilder.append(i);
        }
        end=System.currentTimeMillis();
        System.out.println("StringBuilder消耗时间:"+(end-start)); //1
    }
}

三、日期时间-jdk8前

在这里插入图片描述

3.1 currentTimeMillis()

System.currentTimeMillis():返回此时与1970年1月1日0时0分0秒的时间差,单位毫秒

  • 此方法适于计算时间差。
  • 计算世界时间的主要标准有:
    • UTC(Coordinated Universal Time)
    • GMT(Greenwich Mean Time)
    • CST(Central Standard Time)
/**
 * System.currentTimeMillis():返回此时与1970年1月1日0时0分0秒的时间差,单位毫秒
 */
public class CurrentTimeTest {
    @Test
    public void test1(){
        long l = System.currentTimeMillis();
        System.out.println(l); //1664805955763
    }
}

3.2 Date

/**
 * jdk8前的时间类测试
 *      1.java.util.Date(表示特定的瞬间,精确到毫秒)
 *           - Date():实例化当前时间
 *           - Date(Long):实例化指定时间
 *           - toString():返回时间内容
 *           - getTime():返回毫秒数
 *      2.java.sql.Date(对应数据库中日期类型的变量)
 *           - Date(Long):实例化指定时间
 *           - java.util.Date转换为java.sql.Date?
 */
public class earlyDateTimeTest {
    @Test
    public void test1(){
        //1.实例化当前时间
        Date date1=new Date();
        //2.实例化指定时间
        Date date2=new Date(15643874812627L);
        //3.返回时间内容
        System.out.println(date1.toString()); //Fri Sep 25 16:46:52 CST 2465
        //4.返回毫秒数
        System.out.println(date2.getTime()); //15643874812627
    }
    @Test
    public void test2(){
        //1.实例化指定时间
        java.sql.Date sqlDate=new java.sql.Date(15643874812627L);
        System.out.println(sqlDate); //2465-09-25
        //2.java.util.Date转换为java.sql.Date
        Date date=new Date();
        sqlDate=new java.sql.Date(date.getTime());
        System.out.println(sqlDate); //2022-10-04
    }
}

3.3 SimpleDateFormat

  • Date类的API不易于国际化,大部分被废弃了,java.text.SimpleDateFormat类是一个不与语言环境有关的方式来格式化和解析日期的具体类。
  • 它允许进行
    • 格式化:日期—>文本
    • 解析:文本—>日期
/**
 * jdk8前的时间类测试
 *      3.SimpleDateFormat(格式化)
 *           - SimpleDateFormat():默认格式
 *           - SimpleDateFormat(String):自定义格式
 *           - 格式化:format(Date)
 *           - 解析:parse(String)
 *      4.Calendar
 */
public class earlyDateTimeTest {
    @Test
    public void test3() throws ParseException {
        //1.默认格式实例化
        SimpleDateFormat sdf1=new SimpleDateFormat();
        //2.自定义格式实例化
        SimpleDateFormat sdf2=new SimpleDateFormat("GGG yyyy-MM-dd aaa hh:mm:ss");
        //3.格式化
        Date date=new Date();
        System.out.println(sdf1.format(date)); //22-10-4 下午4:17
        System.out.println(sdf2.format(date)); //公元 2022-10-04 下午 04:17:38
        //4.解析
        System.out.println(sdf1.parse(sdf1.format(date))); //Tue Oct 04 16:17:00 CST 2022
        System.out.println(sdf2.parse(sdf2.format(date))); //Tue Oct 04 16:17:00 CST 2022
    }
}

3.4 Calendar日历类

  • intfield:YEAR、MONTH、DAY_OF_WEEK、HOUR_OF_DAY 、MINUTE、SECOND…
  • 注意:
    • 获取月份时:一月是0,二月是1,以此类推
    • 获取星期时:周日是1,周二是2,以此类推
/**
 *	Calendar:抽象基类,主用用于完成日期字段之间相互操作的功能。
 *           - getInstance():获取子类GregorianCalendar实例
 *           - setTime(Date):设置代表的时间
 *           - getTime():获取代表的时间
 *           - get(intfield):获取时间信息。
 *           - set(intfield,intvalue):设置时间信息。
 *           - add(intfield,intamount):修改时间信息。
 */
public class earlyDateTimeTest {
    @Test
    public void test4(){
        //1.获取子类GregorianCalendar实例
        Calendar instance = Calendar.getInstance();
        //2.设置代表的时间
        instance.setTime(new Date());
        //3.获取代表的时间
        System.out.println(instance.getTime()); //Tue Oct 04 16:56:31 CST 2022
        //4.获取时间信息
        System.out.println(instance.get(Calendar.YEAR)); //2022
        System.out.println(instance.get(Calendar.MONTH)); //9
        System.out.println(instance.get(Calendar.DAY_OF_MONTH)); //4
        //5.设置时间信息
        instance.set(Calendar.YEAR,2050);
        //6.修改时间信息
        instance.add(Calendar.YEAR,1);
        System.out.println(instance.getTime()); //Wed Oct 04 16:56:31 CST 2051
    }
}

3.5 练习

练习1:字符串"2020-09-08"转换为java.sql.Date

public class earlyDateTimePratise {
    @Test
    public void test1() throws ParseException {
        //1.获得格式化器
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
        //2.解析字符串获得毫秒数
        long millis = sdf.parse("2022-09-08").getTime();
        //3.通过毫秒数创建Date实例
        java.sql.Date sqlDate=new java.sql.Date(millis);
        System.out.println(sqlDate); //2022-09-08
    }
}

练习2:1990-01-01 开始"三天打渔两天晒网", 2020-09-08 是打渔还是晒网?

public class earlyDateTimePratise {
    //方式一:通过间隔毫秒数计算花费天数
    @Test
    public void test2() throws ParseException {
        //1.获得格式化器
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
        //2.计算花费的毫秒数
        Long useMillis=sdf.parse("2020-09-08").getTime()-sdf.parse("1990-01-01").getTime();
        //3.计算天数
        Long useDay=(useMillis/(1000*60*60*24))+1;
        //4.判断结果
        switch (String.valueOf (useDay%5)){
            case "1":
                System.out.println("打渔第一天");break;
            case "2":
                System.out.println("打渔第二天");break;
            case "3":
                System.out.println("打渔第三天");break;
            case "4":
                System.out.println("晒网第一天");break;
            case "0":
                System.out.println("晒网第二天");break;
        }
    }
    //方式二:1990-01-01  --> 2019-12-31  +  2020-01-01 -->2020-09-08
}

四、日期时间-jdk8中

4.1 概述与转换

jdk1.8之前面临的问题是:

  • 可变性:像日期和时间这样的类应该是不可变的。

  • 偏移性:Date中的年份是从1900开始的,而月份都从0开始。

    Date date2 = new Date(2020 - 1900,9 - 1,8);
    System.out.println(date2); //Tue Sep 08 00:00:00 CST 2020
    
  • 格式化:格式化只对Date有用,Calendar则不行。

  • 此外,它们也不是线程安全的;不能处理闰秒等。

解决

  • java.time API 已经纠正了过去的缺陷,将来很长一段时间内它都会为我们服务。

  • 吸收了Joda-Time 的精华,以一个新的开始为Java 创建优秀的API。

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

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

转换

在这里插入图片描述

4.2 LocalDateTime

表示公历的日期、时间

  • 不可变的对象
  • LocalDateTime 为常用类,类似Calendar
/**
 *  LocalDate、LocalTime、LocalDateTime:公历日期、时间。类似于Calendar
 *    1.now():获取实例
 *    2.of(int,int,int,int,int):获取实例
 *    3.getXxx():获取相关属性
 *    4.withXxx():设置相关属性
 *    5.plusXxx():增加
 *    6.minusXxx():减少
 *	  7.format(DateTimeFormatter):格式化
 */
public class NewDateTimeTest {
    @Test
    public void test1(){
        //1.now():获取实例
        LocalDateTime dateTime1 = LocalDateTime.now();
        //2.of(int,int,int,int,int):获取实例
        LocalDateTime dateTime2 = LocalDateTime.of(2022, 10, 4, 17, 27);
        System.out.println(dateTime1); //2022-10-04T17:36:53.914
        System.out.println(dateTime2); //2022-10-04T17:27
        //3.getXxx():获取相关属性
        System.out.println(dateTime1.getYear()); //2022
        System.out.println(dateTime1.getMonthValue()); //10
        System.out.println(dateTime1.getDayOfMonth()); //4
        //体现不可变性
        //4.withXxx():设置相关属性
        System.out.println(dateTime1.withYear(2023)); //2023-10-04T17:36:53.914
        //5.plusXxx():增加
        System.out.println(dateTime1.plusYears(1)); //2023-10-04T17:36:53.914
        //6.minusXxx():减少
        System.out.println(dateTime1.minusYears(1)); //2021-10-04T17:36:53.914
    }
}

4.3 Instant类

  • 时间线上的一个瞬时点。类似于Date
  • 自1970年1月1日0时0分0秒[UTC](北京时间1970年01月01日08时00分00秒)开始的秒数
  • 精度可以达到纳秒级:1秒= 1000毫秒=1000*1000微秒=1000*1000*1000
/**
 *  Instant:
 *    1.now:获取当前时间的实例。默认返回UTC时区
 *    2.ofEpochMilli(Long):获取指定时间的实例
 *    3.atOffset(ZoneOffset):根据时间偏移量创建OffsetDateTime
 *    4.toEpochMilli:获取代表的毫秒数
 */
public class NewDateTimeTest {
    @Test
    public void test2(){
        //1.获取当前时间的实例
        Instant instant=Instant.now();
        System.out.println(instant); //2022-10-04T09:58:01.344Z
        //2.获取指定时间的实例
        Instant instant1 = Instant.ofEpochMilli(5445454545L);
        //3.根据时间偏移量创建OffsetDateTime
        //东八区
        OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8));
        System.out.println(offsetDateTime); //2022-10-04T17:58:01.344+08:00
        //4.获取代表的毫秒数
        System.out.println(instant.toEpochMilli()); //1664877481344
    }
}

在这里插入图片描述

4.4 DateTimeFormatter

  • 预定义的标准格式的格式化器
  • 本地特定时间格式的格式化器
  • 自定义的格式的格式化器
/**
 *  DateTimeFormatter:
 *    方式一.ISO_LOCAL_Xxx、ISO_Xxx:预定义的标准格式的格式化器
 *    方式二.ofLocalizedXxx:本地特定时间格式的格式化器
 *    方式三.ofPattern(String):自定义的格式的格式化器
 *    1.格式化:format(LocalDateTime)
 *    2.解析:parse(String)
 */
public class NewDateTimeTest {
    @Test
    public void test3(){
        //方式一:预定义的标准格式
        DateTimeFormatter dtf1 = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
        //方式二.:本地特定时间格式的格式化器
        DateTimeFormatter dtf2 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG);
        DateTimeFormatter dtf3 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT);
        DateTimeFormatter dtf4 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM);
        //方式三:自定义格式的格式化器
        DateTimeFormatter dtf5 = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
        //1.格式化
        System.out.println(dtf1.format(LocalDateTime.now())); //2022-10-04T23:43:59.808
        System.out.println(dtf2.format(LocalDateTime.now())); //2022年10月4日 下午11时43分59秒
        System.out.println(dtf3.format(LocalDateTime.now())); //22-10-4 下午11:43
        System.out.println(dtf4.format(LocalDateTime.now())); //2022-10-4 23:43:59
        System.out.println(dtf5.format(LocalDateTime.now())); //2022-10-04 11:43:59
        //2.解析
        //{MinuteOfHour=43, NanoOfSecond=0, MicroOfSecond=0, HourOfAmPm=11, SecondOfMinute=59, MilliOfSecond=0},ISO resolved to 2022-10-04
        System.out.println(dtf5.parse(dtf5.format(LocalDateTime.now())));
    }
}

4.5 其它

  • ZoneId:该类中包含了所有的时区信息,一个时区的ID,如Europe/Paris
  • ZonedDateTime:带时区的日期时间(公历)
    • 其中每个时区都对应着ID,地区ID都为“{区域}/{城市}”的格式,例如:Asia/Shanghai等
public class OthersTest {
	@Test
	public void test1(){
		//1.ZoneId:类中包含了所有的时区信息
		Set<String> zoneIds= ZoneId.getAvailableZoneIds(); //获取所有的ZoneId
		Object[] objects = zoneIds.toArray();
        //[Asia/Aden, America/Cuiaba, Etc/GMT+9,...
        System.out.println(Arrays.toString(objects));
		//2.ZoneId的of():获取指定时区的时间
		LocalDateTime localDateTime= LocalDateTime.now(ZoneId.of("Asia/Tokyo"));
		System.out.println(localDateTime); //2022-10-05T00:53:37.558
		//3.ZonedDateTime:带时区的日期时间
		ZonedDateTime zonedDateTime= ZonedDateTime.now();//获取本时区的ZonedDateTime对象
		System.out.println(zonedDateTime); //2022-10-04T23:53:37.605+08:00[Asia/Shanghai]
		//4.获取指定时区的ZonedDateTime对象
		ZonedDateTime zonedDateTime1= ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
		System.out.println(zonedDateTime1);
	}
}
  • Clock:使用时区提供对当前即时、日期和时间的访问的时钟。
  • Duration:时间日期间隔,以天、秒、纳秒为单位
  • Period:日期间隔,以年、月、日衡量。只能计算LocalDate
public class OthersTest {
    @Test
    public void test2(){
        //1.Duration:用于计算两个“时间日期”的间隔,以天、秒、纳秒为单位
        LocalDateTime localDateTime1= LocalDateTime.of(2022, 10, 5, 8, 0, 0,1);
        LocalDateTime localDateTime2= LocalDateTime.of(2022, 10, 6, 8, 0, 1,2);
        Duration duration= Duration.between(localDateTime1, localDateTime2);
        System.out.println(duration); //PT24H1.000000001S
        System.out.println(duration.getSeconds()); //86401
        System.out.println(duration.getNano()); //1
        System.out.println(duration.toDays()); //1
        //2.Period:用于计算两个“时间日期”的间隔,以年、月、日为单位
        LocalDate localDate1= LocalDate.now();
        LocalDate localDate2= LocalDate.of(2025, 11, 6);
        Period period= Period.between(localDate1, localDate2);
        System.out.println(period); //P3Y1M1D
        System.out.println(period.getYears()); //3
        System.out.println(period.getMonths()); //1
        System.out.println(period.getDays()); //1
        System.out.println(period.withYears(4)); //P4Y1M1D
    }
}
  • TemporalAdjuster : 时间校正器。有时我们可能需要获取例如:将日期调整到“下一个工作日”等操作。
  • TemporalAdjusters : 该类通过静态方法(firstDayOfXxx()/lastDayOfXxx()/nextXxx())提供了大量的常用TemporalAdjuster 的实现。

五、Java比较器

5.1 Comparable自然排序

  • 保证实现类的对象在任何位置都可以比较大小。

  • 考虑定制排序

    • 没有实现 Comparable接口 且不方便修改代码
    • 实现了 Comparable接口 的排序规则不适合当前的操作
/**
 *   1.String、包装类等:实现Comparable接口,重写compareTo(obj),实现了从小到大的排列
 *   2.自定义类:实现Comparable接口,重写compareTo(obj)的规则,
 *      - this > obj,return 正整数
 *      - this < obj,return 负整数
 *      - this = obj,return 0
 *
 */
public class ComparableTest {
    @Test
    public void test1(){
        //1.String、包装类等:实现Comparable接口,重写compareTo(obj),实现了从小到大的排列
        String[] strs={"ddd","cc","bbbb","rr","aaa"};
        Arrays.sort(strs);
        System.out.println(Arrays.toString(strs)); //[aaa, bbbb, cc, ddd, rr]
    }
    @Test
    public void test2(){
        //2.自定义类:实现Comparable接口,重写compareTo(obj)
        Student[] stus=new Student[]{new Student("aa",6),new Student("cc",2),new Student("aa",3)};
        Arrays.sort(stus);
        System.out.println(Arrays.toString(stus)); //[Student{name='aa', age=3}, Student{name='aa', age=6}, Student{name='cc', age=2}]
    }
}
//2.自定义类:实现Comparable接口,重写compareTo(obj)
class Student implements Comparable{
    String name;
    int age;
    public Student() {}
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public int compareTo(Object o) {
        if(o instanceof Student){
            Student s= (Student) o;
            int i = this.name.compareTo(s.name);
            if(i==0){
                return Integer.compare(this.age,s.age);
            }
            return i;
        }
        throw new RuntimeException("类型转换错误!");
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

5.2 Comparator定制排序

  • 属于临时性的比较。
/**
 *  定制排序:属于临时性的比较
 *      - 没有实现 Comparable接口 且不方便修改代码时
 *      - 实现了 Comparable接口 的排序规则不适合当前的操作时
 *      1.重写compare(Object o1,Object o2)方法:
 *          o1 > o2,return 正整数
 *          o1 < o2,return 负整数
 *          o1 = o2,return 0
 *
 */
public class ComparatorTest {
    @Test
    public void test1(){
        Student[] stus=new Student[]{new Student("aa",6),new Student("cc",2),new Student("aa",3)};
        //1.重写compare(Object o1,Object o2)方法
        Arrays.sort(stus, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return Integer.compare(o1.age,o2.age);
            }
        });
        System.out.println(Arrays.toString(stus)); //[Student{name='cc', age=2}, Student{name='aa', age=3}, Student{name='aa', age=6}]
    }
}

六、System、Math、BigInteger与BigDecimal

6.1 System类

  • 位于 java.lang 包。系统级的很多属性和控制方法都放置在该类的内部。

  • 构造器私有,无法实例化该类。成员变量成员方法都是 static 的,很方便进行调用

  • 成员变量

    • in:标准输入流(键盘输入)
    • out:标准输出流(显示器)
    • err:标准错误输出流(显示器)
  • 成员方法

    • currentTimeMillis():返回当前计算机的GMT(格林威治)时间,1970年1月1号0时0分0秒所差的毫秒数

    • exit(int status):图形界面实现程序的退出功能。

    • status=0:正常退出

      • status!=0:异常退出
  • gc():请求系统进行垃圾回收

    • 是否立刻回收,取决于系统中垃圾回收算法的实现以及系统执行时的情况。
  • getProperty(String key):获得系统中属性名为key的属性对应的值。
    在这里插入图片描述

public class OtherTest {
    @Test
    public void test1() {
        //1.java.version=1.8.0_333
        System.out.println("java.version=" + System.getProperty("java.version"));
        //2.java.home=F:\Java\jdk1.8.0_333\jre
        System.out.println("java.home=" + System.getProperty("java.home"));
        //3.os.name=Windows 10
        System.out.println("os.name=" + System.getProperty("os.name"));
        //4.os.version=10.0
        System.out.println("os.version=" + System.getProperty("os.version"));
        //5.user.name=李泽伟
        System.out.println("user.name=" + System.getProperty("user.name"));
        //6.user.home=C:\Users\李泽伟
        System.out.println("user.home=" + System.getProperty("user.home"));
        //7.user.dir=F:\IDEA\JavaProjects\JavaseTest\CommonClass_Demo
        System.out.println("user.dir=" + System.getProperty("user.dir"));
    }
}

6.2 Math类

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

  • abs:绝对值
  • acos,asin,atan,cos,sin,tan:三角函数
  • sqrt:平方根
  • pow(double a,doble b):a的b次幂
  • log:自然对数
  • exp(double a):e为底数 ,a为对数,返回真数
  • 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):角度—>弧度

6.3 BigInteger与BigDecimal

BigInteger

  • Integer:最大整型值为 2^31 -1。Long:最大为 2^63 -1
  • java.math.BigInteger:不可变的任意精度的整数。
    • 提供所有Java 的基本整数操作符的对应物,并提供 java.lang.Math 的所有相关方法。
    • 提供以下运算:模算术、GCD 计算、质数测试、素数生成、位操作…
  • 构造器
    • BigInteger(String val):根据字符串构建BigInteger对象
  • 常用方法
    在这里插入图片描述

BigDecimal

  • java.math.BigDecimal:在商业计算中,要求数字精度比较高
  • java.math.BigDecimal:不可变的、任意精度的有符号十进制定点数。
  • 构造器
    • public BigDecimal(double val)
    • public BigDecimal(String val)
  • 常用方法
    • add(BigDecimal augend):相加
    • subtract(BigDecimal subtrahend):相减
    • multiply(BigDecimal multiplicand):相乘
    • divide(BigDecimal divisor, int scale, int roundingMode):相除
public class OtherTest {
    @Test
    public void test2(){
        //1.BigInteger
        //Integer integer=5465465464321353437737357373867984213213; 编译不通过
        BigInteger bi=new BigInteger("5465465464321353437737357373867984213213");
        System.out.println(bi); //5465465464321353437737357373867984213213
        //2.BigDecimal
        BigDecimal bd1=new BigDecimal("56465.5454");
        BigDecimal bd2=new BigDecimal("11");
        System.out.println(bd1.divide(bd2,8,BigDecimal.ROUND_HALF_UP)); //5133.23140000
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
package com.aapoint.util; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.time.temporal.TemporalAdjusters; public class LocalDateTimeUtil { /** * 比较 localDateTime2 是否在localDateTime1之前(比较大小) * @param localDateTime1 * @param localDateTime2 * @return */ public static Boolean compare(LocalDateTime localDateTime1,LocalDateTime localDateTime2){ return localDateTime1.isBefore(localDateTime2); } /** * 获取当前月份前/后的月份的第一天 * @param i 指定距离当前月份的时间 * @param state 状态 0.当月 1.前 2.后 * @return */ public static String firstDay(Integer state,Integer i){ LocalDateTime date = null; //type 型 0.月 1.天 2.小时 3.分钟 4.秒 date = getLocalDateTime(state,0,i); //获取该月份的第一天 String firstDay = date.with(TemporalAdjusters.firstDayOfMonth()).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); // System.out.println("第一天为:"+firstDay); return firstDay; } /** * 获取当前月份前/后的月份的最后一天 * @param i 指定距离当前月份的时间 * @param state 状态 0.当月 1.前 2.后 * @return */ public static String lastDay(Integer state,Integer i){ LocalDateTime date = null; //type 型 0.月 1.天 2.小时 3.分钟 4.秒 date = getLocalDateTime(state,0,i); //获取该月份的最后一天 String lastDay = date.with(TemporalAdjusters.lastDayOfMonth()).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); // System.out.println("最后一天为:"+lastDay); return lastDay; } /** * 获取当时间前/后的时间(天) * @param i 指定距离当前月份的时间 * @param state 状态 0.当月 1.前 2.后 * @return */ public static String obtainDay(Integer state,Integer i){ LocalDateTime date = null; //type 型 0.月 1.天 2.小时 3.分钟 4.秒 date = getLocalDateTime(state,1,i); //获取天 String day = date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); // System.out.println("获取的时间为(天):"+day); return day; } /** * 获取当时间前/后的时间(小时) * @param i 指定距离当前月份的时间 * @param state 状态 0.当月 1.前 2.后 * @return */ public static String obtainHours(Integer state,Integer i){ LocalDateTime date = null; //type 型 0.月 1.天 2.小时 3.分钟 4.秒 date = getLocalDateTime(state,2,i); //获取该月份的最后一天 String hours = date.format(DateTimeFormatter.ofPattern("HH:mm:ss")); // System.out.println("获取的时间为(小时):"+hours); return hours; } /** * 获取当时间前/后的时间(小时) * @param i 指定距离当前月份的时间 * @param state 状态 0.当月 1.前 2.后 * @return */ public static String obtainMinutes(Integer state,Integer i){ LocalDateTime date = null; //type 型 0.月 1.天 2.小时 3.分钟 4.秒 date = getLocalDateTime(state,3,i); //获取该月份的最后一天 String minutes = date.format(DateTimeFormatter.ofPattern("HH:mm:ss")); // System.out.println("获取的时间为(分钟):"+minutes); return minutes; } /** * 获取当时间前/后的时间(小时) * @param i 指定距离当前月份的时间 * @param state 状态 0.当月 1.前 2.后 * @return */ public static String obtainSeconds(Integer state,Integer i){ LocalDateTime date = null; //type 型 0.月 1.天 2.小时 3.分钟 4.秒 date = getLocalDateTime(state,4,i); //获取该月份的最后一天 String seconds = date.format(DateTimeFormatter.ofPattern("HH:mm:ss")); // System.out.println("获取的时间为(秒):"+seconds); return seconds; } public static void main(String[] args) { System.out.println("当前时间为:"+LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); System.out.println("前一个月份的第一天为:"+LocalDateTimeUtil.firstDay(1,1)); System.out.println("前一个月份的最后一天为:"+LocalDateTimeUtil.lastDay(1,1)); System.out.println("当前时间的前一天为:"+LocalDateTimeUtil.obtainDay(1,1)); System.out.println("当前时间的后一天为:"+LocalDateTimeUtil.obtainDay(2,1)); System.out.println("当前时间的前一小时为:"+LocalDateTimeUtil.obtainHours(1,1)); System.out.println("当前时间的后一小时为:"+LocalDateTimeUtil.obtainHours(2,1)); System.out.println("当前时间的前一分钟为:"+LocalDateTimeUtil.obtainMinutes(1,1)); System.out.println("当前时间的后一分钟为:"+LocalDateTimeUtil.obtainMinutes(2,1)); System.out.println("当前时间的前一秒为:"+LocalDateTimeUtil.obtainSeconds(1,1)); System.out.println("当前时间的后一秒为:"+LocalDateTimeUtil.obtainSeconds(2,1)); } private static LocalDateTime getLocalDateTime(Integer state,Integer type,Integer i) { LocalDateTime date; if(state == 0){ date = LocalDateTime.now(); }else if(state == 1){ if(type == 0) { //获取月 date = LocalDateTime.now().minusMonths(i); }else if(type == 1){ //获取天 date = LocalDateTime.now().minusDays(i); }else if(type == 2){ //获取小时 date = LocalDateTime.now().minusHours(i); }else if(type == 3){ //获取分钟 date = LocalDateTime.now().minusMinutes(i);

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

愿你满腹经纶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值