24.Java 字符串相关类(String/StringBuilder/StringBuffer/字符串效率测试)/时间处理相关类(Date/DateFormat/SimpleDateFormat/Calendar 日历类)

字符串相关类

简述

字符串相关类分为 String 类、StringBuilder 类、StringBuffer 类。
String 类:不可变字符序列。
StringBuffer 类:可变字符序列,线程安全,效率低(做线程同步检查)。
StringBuilder 类:可变字符序列,线程不安全,效率高(不做线程同步检查)。

String 类

简述

String 类对象代表不可变的 Unicode 字符序列,因此称 String 对象为 "不可变对象"。
"不可变对象" 即指对象内部的成员变量不可改变。

String 类常用方法

String 类的简单使用

代码示例:String 类的简单使用

package com.jungle.ClassTest;

// 测试 String 类的简单使用
public class StringTest01 {
    public static void main(String[] args) {
        /*
        subString(int,int):返回字符串的子字符串。
        hashCode() :返回字符串的哈希码。
        toHexString(int): 以十六进制的无符号整数形式返回一个整数参数的字符串表示形式
        */
        String s1 = new String("abcndf");
        String s2 = s1.substring(2,5); //
        // 打印结果:ab19bdce   
        System.out.println(Integer.toHexString(s1.hashCode()));
        // 打印结果:18159 ,s1 和 s2 不是同一个对象。
        System.out.println(Integer.toHexString(s2.hashCode()));
    }
}

字符串常量拼接优化

遇到字符串常量之间的拼接时,编译器会在编译期间完成字符串的拼接,直接进行优化。

代码示例:字符串常量拼接时的优化

package com.jungle.ClassTest;

// 测试 String 类中字符串常量拼接时的优化
public class StringTest02 {
    public static void main(String[] args) {
        // 当编译器发现拼接的两个操作对象都是常量时,会进行优化,直接在编译时将字符串进行拼接
        String str1 = "hello" + " 阿jun"; // 相当于 String str1 = "hello 阿jun"
        String str2 = "hello 阿jun";
        System.out.println(str1 == str2);  // true
        String str3 = "hello";
        String str4 = " 阿jun";
        // 下方代码在编译时编译器不知道变量的存储内容是什么,所以没办法在编译时进行优化
        String str5 = str3 + str4;
        // 优化比较
        System.out.println(str2 == str5); // false
        System.out.println(str2); // "hello 阿jun"
        System.out.println(str5); // "hello 阿jun"
    }
}

StringBuffer/StringBuilder 常用类

StringBuffer:可变字符序列,线程安全,效率低(做线程同步检查)。
StringBuilder:可变字符序列,线程不安全,效率高(不做线程同步检查),建议使用。

StringBuffer/StringBuilder 常用方法

代码示例:StringBuilder/StringBuffer 常用方法

package com.jungle.ClassTest;

// 测试 StringBuilder/StringBuffer 的常用方法
public class StringTest03 {
    public static void main(String[] args) {
        String sb1 = null;          // 不可变字符序列
        StringBuffer sb2 = null;    // 可变字符序列,线程安全,效率低
        StringBuilder sb3 = null;   // 可变字符序列,线程不安全,效率高,推荐使用
        /* StringBuilder 代码 */
        // 创建一个新的 StringBuilder 对象
        sb3 = new StringBuilder("阿jun");
        // 调用 append() 方法,返回的是 this,自身
        sb3.append("修炼");
        sb3.append("手册");
        System.out.println(sb3);  // 阿jun修炼手册
        sb3.append("Zzz").append("zzZ");
        System.out.println(sb3);  // 阿jun修炼手册ZzzzzZ
        for (int i=0;i<=10;i++){
            sb3.append(i);  // append() 只修改里面的数组内容,不创建新对象
        }
        System.out.println(sb3); // 阿jun修炼手册ZzzzzZ012345678910

        /* StringBuffer 代码,下方代码同样适用于 StringBuilder */
        StringBuffer sb4 = new StringBuffer("阿jun");
        // 插入字符串
        sb4.insert(0,"Zzz").insert(0,"zzZ");
        System.out.println(sb4);   // zzZZzz阿jun
        // 删除子字符串,索引包头不包尾
        sb4.delete(0,3);
        System.out.println(sb4);   // Zzz阿jun
        // 删除某一个字符,操作后返回的是自身对象
        sb4.deleteCharAt(0).deleteCharAt(0).deleteCharAt(0);
        System.out.println(sb4);   // 阿jun
        // 获取某个字符,下方是获取索引位置为 0 的字符
        System.out.println(sb4.charAt(0));  // 阿
        // 字符串逆序
        System.out.println(sb4.reverse());  // nuj阿
    }
}

可变和不可变字符序列使用陷阱

简述

String 一经初始化以后,就不会改变其内容。
对于 String 字符串的操作实际是对其副本(原始拷贝)的操作,不影响原本字符串。

代码测试:String 和 StringBuilder 在字符串频繁修改时的效率测试

package com.jungle.ClassTest;

// 测试看可变和不可变字符序列在频繁修改时的效率问题
public class StringTest04 {
    public static void main(String[] args) {
        /* 使用 String 进行字符串的拼接 */
        String str1 = "";
        // 本质上是使用的 StringBuilder 拼接,但每次循环都会产生一个新的 StringBuilder 对象
        long num1 = Runtime.getRuntime().freeMemory(); // 获取系统剩余的内存空间
        long time1 = System.currentTimeMillis(); // 获取系统的当前时间
        // 通过 for 循环进行 5000 次 String 对象的拼接
        for (int i = 0;i<5000;i++){
            str1 = str1 + i;  // 相当于拼接 5000 次
        }
        long num2 = Runtime.getRuntime().freeMemory(); // 获取系统甚于的内存空间
        long time2 = System.currentTimeMillis(); // 获取系统的当前时间
        System.out.println("String 操作以后消耗内存为:" + (num1 - num2));
        System.out.println("String 操作以后消耗时间为:" + (time2 - time1));

        /* StringBuilder 进行字符串的拼接 */
        StringBuilder str2 = new StringBuilder("");   // 创建一个 StringBuilder 类的空字符串
        long num3 = Runtime.getRuntime().freeMemory(); // 获取系统剩余的内存空间
        long time3 = System.currentTimeMillis(); // 获取系统的当前时间
        // 通过 for 循环进行 5000 次 StringBuilder 类的字符串的拼接
        for (int i=0;i < 5000;i++){
            str2 = str2.append(i);
        }
        long num4 = Runtime.getRuntime().freeMemory(); // 获取系统剩余的内存空间
        long time4 = System.currentTimeMillis(); // 获取系统的当前时间
        System.out.println("StringBuilder 操作以后消耗内存为:" + (num3 - num4));
        System.out.println("StringBuilder 操作以后消耗时间为:" + (time4 - time3));
    }
}

时间处理相关类

Date 类用法

简述

计算机中,将 1970 年 1 月 1 日 00:00:00 定位基准时间(零刻度),单位是毫秒(1秒的千分之一)
使用 long 类型的变量表示时间。
获取当前时刻:

long now = System.currentTimeMills();

时间相关类如下:

核心类是 java.util.Date 类,其余都是围绕它转

Date 时间类(java.util.Date)

代码示例:

package com.test;

import java.util.Date;
// 测试时间类
public class TestTime01 {
    public static void main(String[] args) {
        // 计算 Integer 最大值表示多少年
        long a = Long.MAX_VALUE/(1000L * 3600 * 24 * 365);   // 除以一年的毫秒数
        System.out.println(a);   // 292471208 年,大约是 2.9 亿年

        long nowNum = System.currentTimeMillis();   // 代表当前时刻的毫秒数
        System.out.println(nowNum);   // 1640132064067

        /* 测试 Date */
        Date d1 = new Date();    // 创建时间类 Date 的对象时没有传参,则代表的是当前时刻
        System.out.println(d1); // Wed Dec 22 08:24:38 CST 2021
        System.out.println(d1.getTime());  // 1640132678369:gettime() 返回当前时间的毫秒数
        // 给时间类对象传参
        Date d2 = new Date(1000L * 3600 * 24 * 365 * 200);   // 从 1970 年开始,再加上 200 年的时间
        System.out.println(d2); // Mon Nov 13 08:00:00 CST 2169  (闰年会少那么几天,所以就会是 2169,而不是 2170)
    }
}

时间格式化类

DateFormat 类

作用是把时间对象与指定格式的字符串进行互相转化。
DateFormat 是一个抽象类,一般用它的子类 SimpleDateFormat 类来实现。

代码示例:

package com.test;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
 * 测试时间对象和字符串的互相转化
 * 使用 DateFormat、SimpleDateFormat
 * */
public class TestTime02 {
    public static void main(String[] args) throws ParseException {
        SimpleDateFormat  df1 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
//        SimpleDateFormat  df2 = new SimpleDateFormat("yyyy-MM-dd");

        // 将字符串转成 Date 对象
        Date d1 = df1.parse("1992-10-22 10:56:50");
        System.out.println(d1.getTime()); // 719722610000

        // 将 Date 对象转为字符串
        Date d2 = new Date();   // 如果不传参,默认就是现在时刻
        String str = df1.format(d2);
        System.out.println(str);  // 1970-01-02 02:00:00

        // 利用不同的格式化字符,输出不同的时间元素值,比如某年、某月、第多少天、第多少周,详见格式化字符图表
        DateFormat df2 = new SimpleDateFormat("这周是今年第w周,今年的第D天,今天是星期中的E。");  // 字母即代表详细的格式化字符串含义
        System.out.println(d2);
        System.out.println(df2.format(d2));
    }
}

格式化字符的含义表:

Calendar 日历类

Calendar 类是一个抽象类,提供了关于日期计算的相关功能,比如年、月、日、时、分、秒的展示和计算。
GregorianCalendar 是 Calendar 的一个具体子类,提供了标准的日历系统
注意点:
月份用 0-11 表示,即 11 代表 12 月
星期用 1-7 表示,即 1 是周日,2 是周一

代码示例:Calendar 日历类

package com.test;

import java.util.*;
/* 测试 Calendar 类和 GregorianCalendar 类的使用 */
public class TestCalendar {
    public static void main(String[] args) {
        // 创建 GregorianCalendar 子类对象,传入一个日期参数
        GregorianCalendar calendar1 = new GregorianCalendar(2021,11,23,05,27,35);
        int year = calendar1.get(Calendar.YEAR);  // 打印年份:2021
        // 月份用 0-11 表示,即 11 代表 12 月
        int month = calendar1.get(Calendar.MONTH); // 打印月份:11
        int day = calendar1.get(Calendar.DAY_OF_MONTH); // 打印当月的第多少号: 23
        int day2 = calendar1.get(Calendar.DATE); // 打印当月的第多少号:23
        // 日:Calendar.DATE 与 Calendar.DAY_OF_MONTH 同等意义
        int date = calendar1.get(Calendar.DAY_OF_WEEK); // 打印今天是周几:5
        // 星期用 1-7 表示,即 1 是周日,2 是周一,5 是周四
        System.out.println(year);  // 2021
        System.out.println(month); // 11
        System.out.println(day);   // 23
        System.out.println(day2);  // 23
        System.out.println(date);  // 5
        printCalendar(calendar1);  // 2021年12月23日,星期4 5:27:35

        // 自定义日期:set()
        // 创建 GregorianCalendar 子类对象,传入一个日期参数
        GregorianCalendar calendar2 = new GregorianCalendar();
        calendar2.set(Calendar.YEAR,2021);  // 设置年号
        calendar2.set(Calendar.MONTH,Calendar.DECEMBER);   // 设置月份:12月
        calendar2.set(Calendar.DAY_OF_MONTH,23); // 设置当月的第多少号
        calendar2.set(Calendar.DATE,23);  // 设置当月的第多少号
        calendar2.set(Calendar.DAY_OF_WEEK,5); // 设置周几
        printCalendar(calendar2);  // 2021年12月23日,星期4 6:17:27

        // 日期计算:add()
        GregorianCalendar calendar3 = new GregorianCalendar(2021,11,23,05,27,35);
        calendar3.add(Calendar.YEAR,-2);  // 年份减 2 年
        calendar3.add(Calendar.DATE,7);   // 增加 7 天
        printCalendar(calendar3);  // 2019年12月30日,星期1 5:27:35

        // 日历对象和时间对象转化
        Date d = calendar3.getTime();
        GregorianCalendar calendar4 = new GregorianCalendar();
        calendar4.setTime(new Date());
    }
    static void printCalendar(Calendar calendar){
        int year = calendar.get(Calendar.YEAR);  // 设置年份
        // 月份用 0-11 表示,即 11 代表 12 月
        int month = calendar.get(Calendar.MONTH) + 1; // 设置月份
        int day = calendar.get(Calendar.DAY_OF_MONTH); // 设置当月的第多少号
        int day2 = calendar.get(Calendar.DATE); // 设置当月的第多少号
        // 日:Calendar.DATE 与 Calendar.DAY_OF_MONTH 同等意义
        int date = calendar.get(Calendar.DAY_OF_WEEK) - 1; // 设置今天是周几
        String week = "" + ((date==0)?"日":date);
        // 星期用 1-7 表示,即 1 是周日,2 是周一,5 是周四
        int hour = calendar.get(Calendar.HOUR); // 设置多少时
        int minute = calendar.get(Calendar.MINUTE); // 设置多少分
        int second = calendar.get(Calendar.SECOND); // 设置多少秒
        // 打印多个拼接变量,用 printf
        System.out.printf("%d年%d月%d日,星期%s %d:%d:%d\n",year,month,day,date,hour,minute,second);
    }
}
posted @ 2021-12-23 06:22  阿jun  阅读(64)  评论(0编辑  收藏  举报