week03_day04补充:StringBuffer、Date、Math

对于String的每次修改,其实都是创建新内存空间,将修改后的值存入新内存空间,然后再将引用变量指向新内存空间的首地址。

我们如果对字符串进行拼接操作,每次拼接,都会构建一个新的String对象,既耗时,又浪费空间。
为解决上述问题,Java语言引入了StringBuffer。
StringBuffer默认分配参数字符串长度 + 16个字符的存储空间
StringBuffer
在这里插入图片描述

  • 线程安全(多线程环境下访问数据的正确性)的 可变 字符序列
  • 一个类似于 String 的字符串缓冲区,但可以修改(内容可变)
  • 虽然在任意时间点上它都包含某种特定的字符序列,但通过某些方法调用可以改变该序列的 长度 和 内容

讲解构造方法的区别的时候,可以把长度功能和容量功能先解释一下。
长度:实际值
容量:理论值

  1. 构造方法
    public StringBuffer()
    public StringBuffer(int capacity)
    public StringBuffer(String str)
public class Demo01 {

    public static void main(String[] args) {

        //public StringBuffer()
        // capacity: 字符缓冲去本身的大小() 创建16个字符大小的缓冲区
        // length:字符缓冲区中真正包含的字符个数
        StringBuffer stringBuffer = new StringBuffer();
        System.out.println(stringBuffer.capacity());  //16
        System.out.println(stringBuffer.length()); //0

        // public StringBuffer(int capacity)  可以指定StringBuffer初始容量
        stringBuffer = new StringBuffer(20);
        System.out.println(stringBuffer.capacity());
        System.out.println(stringBuffer.length());

        //public StringBuffer(String str)
        stringBuffer = new StringBuffer("helloworld"); //默认分配参数字符串长度 + 16个字符的存储空间
        System.out.println(stringBuffer.capacity()); //26
        System.out.println(stringBuffer.length());
    }

}

···························································································································································································································

StringBuffer能够自动扩容
2. 添加功能
不管任何数据类型都可以被添加到字符缓冲区中:

  1. 如果该类型不是字符串类型,就将该类型转化为字符串类型
  2. 然后再放入字缓冲区中

public StringBuffer append(String str) //向字符缓冲区中添加字符序列
public StringBuffer insert(int offset,String str)

虽然说,StringBuffer可以自动扩容,但是通常在开发中建议
public StringBuffer(int capacity)
如果在开发中,能有效的预估字符缓冲区所需的长度,因为每一次扩容,其实都比较耗时

public class Demo02 {

    public static void main(String[] args) {

        //StringBuffer 可以自己根据需要扩容
        StringBuffer stringBuffer = new StringBuffer(); //capacity 16
        stringBuffer.append("12345678901234567890abc");
        String s = stringBuffer.toString();  //注意StringBuffer也有toString()功能
        System.out.println(s);
        System.out.println(stringBuffer.capacity());

        // 测试尾部追加 append
        int i = 10;
        stringBuffer.append(i);
        System.out.println(stringBuffer.toString());

        boolean b = true;
        stringBuffer.append(b);
        System.out.println(stringBuffer.toString());

        // insert
        // public StringBuffer insert(int offset,String str)
        String s1 = "jiang";
        stringBuffer.insert(3, s1);
        System.out.println(stringBuffer.toString());

        //链式调用 向stringbuffer插入 ‘a’ 1 true
        stringBuffer.insert(1, 'a')
                .append(1)
                .append(false);
        System.out.println(stringBuffer.toString());

        //我们自己测试自己写的链式调用方法
        ChainedCall chainedCall = new ChainedCall();
        ChainedCall print = chainedCall.print("123").print(new Demo02());
        System.out.println(print == chainedCall);
    }

}

class ChainedCall {

    public ChainedCall print(Object o) {

        System.out.println(o.toString());
        return this;
    }

}


···························································································································································································································

  1. 删除功能
  • public StringBuffer deleteCharAt(int index)
  • public StringBuffer delete(int start,int end) [start, end)
  1. 替换功能
  • public StringBuffer replace(int start,int end,String str)
    // 用所给字符串,替换掉字符缓冲区中,指定范围的字符串虚列
  1. 反转功能
  • public StringBuffer reverse()
    //反转字符缓冲区中的字符序列
  1. String.split(String str)
    //以str为分隔符,对s进行分割
  2. 截取功能
  • public String substring(int start)
  • public String substring(int start,int end) [start, end)
    这个功能String类也有。

截取功能和前面几个功能的不同
返回值类型是String类型,本身没有发生改变

public class Demo03 {

    public static void main(String[] args) {
        String s = "zhang";
        StringBuffer stringBuffer = new StringBuffer(s);
        //测试删除字符
        //StringBuffer stringBuffer1 = stringBuffer.deleteCharAt(s.length() - 1);
        //System.out.println(stringBuffer1.toString());

        //测试删除某个[start, end)
        //StringBuffer delete = stringBuffer.delete(2, s.length());
        //System.out.println(delete.toString());

        //替换功能
        StringBuffer abcd = stringBuffer.replace(0, 2, "abcd");
        System.out.println(abcd.toString());

        //测试字符串反转
        String str = reverseStr("abcd");
        System.out.println(str);

        //测试一下
        str = "hansh";
        StringBuffer sb1 = new StringBuffer(str);
        System.out.println(sb1.reverse().toString());

        //String.split(String str)
        //以str为分隔符,对s进行分割
        String path = "/zs/web/file";
        String[] split = path.split("/");

        for (int i = 0; i < split.length; i++) {
            System.out.println(split[i]);
        }

    }

    public static String reverseStr(String s) {
        char[] temp = new char[s.length()];
        int len = s.length();
        for (int i = 0; i < len; i++) {
            temp[i] = s.charAt(len - 1 - i);
        }
        return new String(temp);
    }

}

···························································································································································································································

练习题:

package StringBuffer;

/**
 * @author shihao
 * @create 2020-04-24 15:46
 * String和StringBuffer的相互转换
 * 1.把数组拼接成一个字符串
 * 2.把字符串反转
 * 3.判断一个字符串是否是对称字符串
 * 例如"abc"不是对称字符串,"aba"、"abba"、"aaa"、"mnanm"是对称字符串
 */


public class Demo05Exercise {

    public static void main(String[] args) {
        int[] a = {1, 2, 3, 4};
        exercise1(a);

        System.out.println(reverse2("1234"));
        exercise2("1234");

        exercise3("abbac");
    }


    //1.把数组拼接成一个字符串 {1, 2, 3}
    public static void exercise1(int[] a) {
        StringBuffer buffer = new StringBuffer("{");
        if(a.length>0)
            buffer.append(a[0]);

        //这样可以先拼接逗号再拼接value,不用再最后一步删最后一个逗号
        for (int i = 1; i < a.length; i++) {
            int value = a[i];
            buffer.append(',').append(value);
        }
        buffer.append("}");

        System.out.println(buffer.toString());
    }

    //2. 把字符串反转
    public static void exercise2(String s) {
        StringBuffer buffer = new StringBuffer();
        // insert(0)  "abc"
        // "a" -> "ba" -> "cba"
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            buffer.insert(0, c);
        }
        System.out.println(buffer.toString());
    }

    //我的做法:
    public static String reverse2(String s){
        int len=s.length();
        StringBuffer sb=new StringBuffer(len);
        for (int i = 0; i < len; i++) {
            sb.append(s.charAt(len-1-i));
        }
        return new String(sb);
    }

    //3.判断一个字符串是否是对称字符串
    public static void exercise3(String s) {

        StringBuffer stringBuffer = new StringBuffer(s);
        StringBuffer reverse = stringBuffer.reverse();
        System.out.println(reverse.toString().equals(s));
    }

}

···························································································································································································································

从 JDK 5 开始,为该类(StringBuffer)补充了一个单个线程使用的 等价类(在API的角度看,使用方式一模一样) ,即StringBuilder

  • StringBuilder针对 单线程 运行环境,它的api和StringBuffer几乎没有差别
  • VS
  • StringBuffer 针对多线程运行环境

1. StringBuffer和StringBuilder从效率上来说哪个更快?
StringBuilder效率更快,因为StringBuffer适用于多线程,它会控制设备对一片内存的读写,读完才能写,写完才能读,不予许同时访问。
而StringBuilder适用于单线程,没有这种限制。
所以,如果你确保你的程序运行环境是单线程,就用StringBuilder

2.String, StringBuffer和StringBuilder有啥区别
(一)三者速度比较
String:(遍历100万次)

  String aa = "";
  long startTime = System.currentTimeMillis();
  for(int i=0;i<100*100*10;i++){
      //字符串拼接
      aa = aa + "aa";
  }
  long endTime = System.currentTimeMillis();
  System.out.println("耗时:"+String.valueOf(endTime - startTime));

运行结果:

耗时:7614
StringBuffer:(遍历一亿次)

  StringBuffer aa = new StringBuffer();
  String ss = "ss";
  long startTime = System.currentTimeMillis();
  for(int i=0;i<100*100*100*100;i++){
      //字符串拼接
      aa.append(ss);
  }
  long endTime = System.currentTimeMillis();
  System.out.println("耗时:"+String.valueOf(endTime - startTime));

运行结果:

耗时:3128
StringBuilder:(遍历一亿次)

  StringBuilder aa = new StringBuilder();
  String ss = "ss";
  long startTime = System.currentTimeMillis();
  for(int i=0;i<100*100*100*100;i++){
      //字符串拼接
      aa.append(ss);
  }
  long endTime = System.currentTimeMillis();
  System.out.println("耗时:"+String.valueOf(endTime - startTime));

运行结果:

耗时:1240
结论:
(1)速度比较:String < StringBuffer < StringBuilder
(2)String的处理速度比StringBuffer、StringBuilder要慢的多。

(二)String的处理速度为什么要比StringBuffer、StringBuilder慢的多?
String是不可变的对象
StringBuffer是可变对象
StringBuiler是可变对象
请结合上面的代码理解这个问题:
(1)String本身就是一个对象,因为String不可变对象,所以,每次遍历对字符串做拼接操作,都会重新创建一个对象,循环100万次就是创建100万个对象,非常的消耗内存空间,而且创建对象本身就是一个耗时操作,创建100万次对象就相当的耗时了。
(2)StringBuffer和StringBuilder只需要创建一个StringBuffer或StringBuilder对象,然后用append拼接字符串,就算拼接一亿次,仍然只有一个对象。

(三)是不是可以抛弃使用String,转而使用StringBuffer和StringBuilder呢?
答案是否定的。
上文的总结只是针对于数据量比较多的情况,但是数据量比较少的情况呢?
我们分析一下代码:
(1)String遍历代码:一开始定义一个String常量(创建一个String对象), 再开始遍历;
(2)StringBuffer代码:一开始定义一个String常量(创建一个String对象)和一个创建StringBuffer对象,再开始遍历;
(3)StringBuiler代码:一开始定义一个String常量(创建一个String对象)和一个创建StringBuiler对象,再开始遍历;

(2)和(3)比(1)多了一个创建对象流程,所以,如果数据量比较小的情况建议使用String。

(四)是StringBuffer和StringBuilder的区别?
StringBuffer是线程安全的
StringBuilder是非线程安全的, 这也是速度比StringBuffer快的原因
注:不知道线程安全的可以自行查找资料补脑

(五)使用场景
(1)如果要操作少量的数据用 String
(2)单线程操作字符串缓冲区 下操作大量数据 StringBuilder
(3)多线程操作字符串缓冲区 下操作大量数据 StringBuffer

···························································································································································································································

类 Date 表示特定的瞬间,精确到毫秒
构造方法
Date()
分配 Date 对象并初始化此对象,以表示分配它的时间(精确到毫秒)。

Date(long date)
分配 Date 对象并初始化此对象,以表示自从标准基准时间(称为“历元(epoch)”,即 1970 年 1 月 1 日 00:00:00 GMT)以来的指定毫秒数。

public long getTime()
返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数。

···························································································································································································································

DateFormat类概述

  1. 是日期/时间 格式化 子类的 抽象类
  2. 它以与语言无关的方式格式化并解析日期或时间
  3. 因为是抽象类,所以实际使用的是SimpleDateFormat这个实现DateFormat抽象类的子类
  • y 年
  • M 表示年中的月份
  • d 表示月份中的天数
  • H 表示一天中的小时数
  • m 小时中的分钟
  • s 分钟中的秒数
    如:yyyy/MM/dd HH:mm:ss

构造方法:
public Date parse(String source) 把一个用字符串表示的时间转化成一个Date对象,该对象表示的时间点,就是你用字符串表示的那个时间点。

        //"yyyy/MM/dd HH:mm:ss"告诉format它应当以什么样的格式解析你给的字符串
          SimpleDateFormat format = new SimpleDateFormat("yyyy--MM--dd  HH:mm:ss");
        //如果"2020--09--01  17:00:00"的格式不对会抛出格式解析异常
        Date parse = format.parse("2020--09--01  17:00:00");       

public final String format(Date date) 把一个Date对象表示成一个指定格式的表示时间的字符串

···························································································································································································································

练习:制作一个工具类。DateUtil,算一下你来到这个世界多少天?

public class Demo03DateUtil {

    public static void main(String[] args) throws ParseException {

        Scanner in = new Scanner(System.in);
        System.out.println("请以yyyy-MM-dd的格式输入你的出生日期:");
        String src = in.nextLine();

        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        Date srcdate = format.parse(src);
        Date destdate = new Date();
        String dest2 = format.format(destdate);
        int days = myCode(src, dest2);
        System.out.println(days);
    }

	public static int myCode(String src, String dest) throws ParseException {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");

        Date srcDate = format.parse(src);
        Date destDate = format.parse(dest);

        long seconds = destDate.getTime() - srcDate.getTime();
        int days = (int) (seconds / (1000L * 60 * 60 * 24));
        return days;
    }

···························································································································································································································

Math工具类
成员方法
public static int abs(int a) //求绝对值
public static double ceil(double a) // 取整 向大的方向取整
public static double floor(double a)// 取整 向小的方向取
public static int max(int a,int b) //min自学
public static double pow(double a,double b) //a^b
public static double random() // 返回带正号的 double 值,该值大于等于 0.0 且小于 1.0 [0.0 1.0) 随机数。
public static int round(float a) //取整 4舍五入的取整
public static double sqrt(double a) //返回正确舍入的 double 值的正平方根

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

-玫瑰少年-

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

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

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

打赏作者

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

抵扣说明:

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

余额充值