• 10_常用类


    前言

    参考


    Object

    身为一切的祖宗,Object当然是不能不说的,到了以后,会经常使用Object的方法

    常用方法

    clone()

    深拷贝和浅拷贝

    在讲解一切之前,首先要了解一下深拷贝和浅拷贝

    拷贝,顾名思义就是复制。拷贝分为深拷贝和浅拷贝。这个概念其实并不是Java独有的。

    假设现在有一个A,我们对A进行拷贝,得到B

    这个时候,假如A发生了改变,而B也跟着改变,这就叫做浅拷贝。假设A发生了改变而B不改变,这就叫做深拷贝

    clone()

    在之前,我们对对象的创建通常使用new关键字。new的意思是分配给这个对象一块和它类型对应的内存空间,并对这个对象进行初始化。

    现在我们如果想创建一个新的对象,有了一个新的方法:clone()。这个方法是存在于Object类中的,这意味着一切对象都可以使用这个方法。

    但是这个方法的访问修饰符是protected,这意味着我们要修改为public

    使用这个方法一般需要两步骤

    1. 实现Cloneable,否则会爆出CloneNotSupportedException
    2. 重写clone()方法

    我们来测试一下clone()方法:

    package com.howling;
    
    public class CommonClass {
    
        public static void main(String[] args) {
    
            CloneClass clonedClass = new CloneClass("小明",18);
    
            CloneClass cloneClass = null;
            try {
                cloneClass = clonedClass.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
    
            clonedClass.setName("小刚");
            clonedClass.setAge(20);
    
            System.out.println(cloneClass.getName());//小明
            System.out.println(cloneClass.getAge());//18
    
        }
    }
    
    class CloneClass implements Cloneable{
    
        private String name;
        private int age;
    
        public CloneClass(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        @Override
        public CloneClass clone() throws CloneNotSupportedException {
    
            return (CloneClass) super.clone();
        }
    }
    

    表面看上去仿佛没有任何问题,那么这究竟是深克隆还是浅克隆呢?只需要一步简单的验证即可

    我们只需要查看在对象的使用上是否为一个对象就可以判定

    • 假如两个对象的地址值相同,就是浅克隆,这意味着直接把对象的地址值引用过去的
    • 假如两个对象的地址值不同,就是深克隆,这意味着直接复制了一份新的对象
    package com.howling;
    
    public class CommonClass {
    
        public static void main(String[] args) {
    
            CloneClass clonedClass = new CloneClass(new Student(18));
    
            CloneClass cloneClass = null;
            try {
                cloneClass = clonedClass.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
    
            //com.howling.Student@1b6d3586
            System.out.println(clonedClass.getStudent().toString());
    
            //com.howling.Student@1b6d3586
            System.out.println(cloneClass.getStudent().toString());
    
        }
    }
    
    class CloneClass implements Cloneable{
    
    
        private Student student;
    
        public CloneClass(Student student) {
            this.student = student;
        }
    
        public Student getStudent() {
            return student;
        }
    
        public void setStudent(Student student) {
            this.student = student;
        }
    
        @Override
        public CloneClass clone() throws CloneNotSupportedException {
    
            return (CloneClass) super.clone();
        }
    }
    
    class Student{
        private int age;
    
        public Student(int age) {
            this.age = age;
        }
    }
    

    地址值相同,所以clone方法实现的是浅克隆

    clone方法仅仅只是拷贝了基本数据类型,而对于深层次的引用类型其实并没有作为一个真正的拷贝,真正存在的还是原来的地址值


    toString()

    作用

    没有覆写的时候 覆写的时候
    返回对象的地址值 一般覆写的时候就是返回对象的具体内容
    对象类型@内存地址 具体内容
    com.howling.Student@1b6d3586 Student{age=18}

    一般重写的形式为:

    @Override
    public String toString() {
    return "Student{" +
           "age=" + age +
           '}';
    }
    

    一般不是太垃圾的编辑器都会帮助我们进行重写,比如IDEA,比如远古的Eclipse


    equals

    作用

    比较和其他对象是否相等的方法

    • 默认的话,比较的是地址值,因此来比较两个对象是否相等

    • 现在我们也会将这个方法重写,用来比较对象之间具体的内容

      @Override
      public boolean equals(Object o) {
          if (this == o) return true;
          if (o == null || getClass() != o.getClass()) return false;
          Student student = (Student) o;
          return age == student.age;
      }
      

    ==和equals的比较

    两者的分析:

    equals() ==
    基本类型 比较内容 比较内容
    引用类型 比较地址(没有覆写的时候)/ 比较内容(覆写的时候) 比较地址

    再来看Object中,equals的源码:

    public boolean equals(Object obj) {
    return (this == obj);
    }
    

    可以看到,equals最终的本质其实也是==

    现在,系统类大部分类都覆写了equals这个方法


    hashcode()

    作用

    返回对象的哈希值

    Hash

    Hash,在中文翻译中为散列,程序员一般直接叫音译哈希。

    哈希是指把任意长度的输入通过一定的算法压缩成固定长度的输出(哈希值)的函数(算法)。哈希算法有多种。

    也就是说,只要算法相同,输入相同,哈希值必定相同

    而且,算法不同,输入不同,哈希值在理论上也有可能相同,只不过概率很低,因为哈希值是算出来的

    而hashcode就是根据内存的地址值算出来的一个数值

    hashcode的意义

    假如我已经存放了一万个数,这时候我要存放一万零一个数,能想到最笨的方法就是挨个遍历一遍。

    但是现在有了hashcode,我只需要找到对应的位置

    • 假如这个位置没有数字,直接填写上
    • 假如这个位置有数字,比对hashcode相同的数字就好了

    image-20200702184229614


    Objects

    compare

    /*-------------------比较-------------------*/
    
    /*
                Objects.compare(参数1,参数2,Comparator比较方法)
                执行的结果:-1
                Comparator自定义
                使用普通的值来进行演示
    */
    int i = 0, i2 = 1;
    
    System.out.println(Objects.compare(i, i2, new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o1 - o2;
        }
    }));
    
    /*-------------------------------------------------------------------*/
    
    /*
                一个函数式接口,可以使用lambda表达式进行简写
    */
    int[] arr = {1, 2, 3, 4, 5};
    int[] arr2 = {1, 2, 3, 4, 5};
    
    System.out.println(Objects.compare(arr, arr2, (o1, o2) -> Arrays.equals(o1, o2) ? 1 : 0));//1
    

    isNull

    /*-------------------判断是否为null-------------------*/
    
    System.out.println(Objects.isNull(123));//false
    
    System.out.println(Objects.isNull(null));//true
    

    equals

    /*----------------------判断是否相同----------------------*/
    System.out.println(Objects.equals(1, 1));//true
    System.out.println(Objects.equals(1, 2));//false
    System.out.println(Objects.equals("1", "1"));//true
    System.out.println(Objects.equals(new Person(),new Person()));//false
    
    class Person{}
    

    String

    构造方式

    • 直接创建
    • 使用byte[]创建
    • 使用char[]创建
    /*-------------------构造方法的比较-------------------*/
    
    String str1 = new String("abcd");
    String str2 = "abcd";
    System.out.println("str:" + str1 + "--" + str2);//str:abcd--abcd
    
    
    String strByte1 = new String(new byte[]{97, 98, 99, 100});
    String strByte2 = new String(new byte[]{97, 98, 99, 100}, 0, 1);
    System.out.println("strByte:" + strByte1 + "--" + strByte2);//strByte:abcd--a
    
    
    String strChar1 = new String(new char[]{'a', 'b', 'c', 'd'});
    String strChar2 = new String(new char[]{'a', 'b', 'c', 'd'}, 0, 1);
    System.out.println("strChar:" + strChar1 + "--" + strChar2);//strChar:abcd--a
    

    常用方法

    查找

    获取字符串长度

    /*--------------------------长度--------------------------*/
    
    String str = "1234567123456712345671234567";
    
    System.out.println(str.length());//28
    

    指定字符第一次/最后一次出现的位置

    /*--------------------------指定字符第一次/最后一次出现的位置--------------------------*/
    
    String str = "1234567123456712345671234567";
    
    System.out.println(str.indexOf("1"));//0
    System.out.println(str.indexOf("1", 1));//7
    
    System.out.println(str.lastIndexOf("1"));//21
    System.out.println(str.lastIndexOf("1", 7));//7
    

    返回指定位置的字符

    /*--------------------------指定字符第一次/最后一次出现的位置--------------------------*/
    
    
    /*charAt:返回指定位置的字符*/
    String str = "1234567123456712345671234567";
    
    System.out.println(str.charAt(0));//1
    

    断言

    是否以指定字符开头/结尾

    /*--------------------------确认是否以指定的字符串开头/结尾--------------------------*/
    
    String str = "ABCABCABC";
    
    /*
                	startsWith(String prefix):确认是否以指定的字符串开头
                	startsWith(String prefix, int toffset):确认在指定的所引处是否以指定的字符串开头
                    endsWith(String suffix):确认指定的字符串是否在指定的后缀结尾
    */
    System.out.println(str.startsWith("ABC"));//true
    
    System.out.println(str.startsWith("ABC", 1));//false
    
    System.out.println(str.endsWith("BC"));//true
    

    是否与其他字符串相等(忽略大小写/不忽略大小写)

    /*--------------------------比较--------------------------*/
    String str1 = "ABCABCABC";
    String str2 = "abcabcabc";
    
    
    /*
                equals:比较两个字符串
                equalsIgnoreCase:比较两个字符串,忽略大小写
    */
    System.out.println(str1.equals(str2));//false
    
    System.out.println(str1.equalsIgnoreCase(str2));//true
    

    注意,使用equals==是不同的,前者比较值,后者比较地址

    是否包含指定的字符串

    /*--------------------------确认是否包含--------------------------*/
    
    String str1 = "ABCABCABC";
    String str2 = "abcabcabc";
    
    /*
                	contains(CharSequence s)
                	    CharSequence的所有已知实现类:
                        CharBuffer , Segment , String , StringBuffer , StringBuilder
    */
    System.out.println(str1.contains("A"));//true
    

    是否为空

    /*--------------------------是否为空--------------------------*/
    
    String str = "1234567123456712345671234567";
    
    System.out.println(str.isEmpty());//false
    

    是否遵从指定的正则表达式

    /*--------------------------是否遵从指定的正则表达式--------------------------*/
    
    String str = "1234567123456712345671234567";
    
    System.out.println(str.matches(".*"));//true
    

    替换

    字符替换

    /*--------------------------替换--------------------------*/
    
    String str = "1234567123456712345671234567";
    
    //*234567*234567*234567*234567
    System.out.println(str.replace('1', '*'));
    
    //-4567-4567-4567-4567
    System.out.println(str.replaceAll("123", "-"));
    
    //?4567123456712345671234567
    System.out.println(str.replaceFirst("(123)", "?"));
    

    字符串拼接

    /*--------------------------连接--------------------------*/
    
    String str1 = "ABCABCABC";
    String str2 = "abcabcabc";
    
    System.out.println(str1.concat(str2));//ABCABCABCabcabcabc
    

    去除首尾空格

    /*--------------------------去除首尾空格--------------------------*/
    
    String str1 = "     ABCABCABC    ";
    
    System.out.println(str1.trim());//ABCABCABC
    

    分割

    拆分(直接拆分/根据索引拆分)

    String str = "ABCABCABC";
    
    /*
                	split(String regex):根据正则直接拆分
                		split(String regex, int limit):根据正则拆分,并指定分成几份
    */
    Arrays.stream(str.split("A")).forEach(i-> System.out.print(i+"-"));//-BC-BC-BC-
    
    Arrays.stream(str.split("A",4)).forEach(i-> System.out.print(i+"-"));//-BC-BC-BC-
    

    转换

    转字符数组

    /*--------------------------转换--------------------------*/
    
    String str = "AbcaBCabC";
    
    /*转为字符数组*/
    for (char c : str.toCharArray()) {
        System.out.print(c);//AbcaBCabC
    }
    

    转小写

    /*--------------------------转换--------------------------*/
    
    String str = "AbcaBCabC";
    
    /*转小写*/
    System.out.println(str.toLowerCase());//abcabcabc
    

    转大写

    /*--------------------------转换--------------------------*/
    
    String str = "AbcaBCabC";
    
    /*转大写*/
    System.out.println(str.toUpperCase());//ABCABCABC
    

    特性

    字符串的不可变性和字符串常量池

    在刚才比较字符串的时候我们已经说过,equals比较值,==比较地址,我们可以使用==来做一个说明

    第一题:

            String s1 = "abc";            		// 常量池
            String s2 = new String("abc");     	// 堆内存中
            System.out.println(s1 == s2);        // false两个对象的地址值不一样。
            System.out.println(s1.equals(s2)); // true
    

    第二题:

            String s1 = "a" + "b" + "c";
            String s2 = "abc";
            System.out.println(s1 == s2);//true
            System.out.println(s1.equals(s2));//true
    

    第三题:

    String s1="ab";
    String s2="abc";
    String s3=s1+"c";
    System.out.println(s3==s2);         // false
    System.out.println(s3.equals(s2));  // true
    

    以上三道题充分考到了字符串的底层。

    现在我们来看一下字符串的创建方式:两种

    String str = "str";  				//第一种方式,进行字面量赋值
    String str = new String("str");		//第二种方式,进行对象的创建
    

    字符串常量池

    我们都知道,字符串是不可变的,但是字符串又是需要大量使用的,这个时候考虑到时间和空间上的开销,我们果断的选择将一些字符串放入到一块对应的空间中,以后只要出现相同的字符串,就可以直接从这块空间中拿到。

    这块空间就是字符串常量池。

    那么再回头说我们创建对象的两种方式:

    第一种方式

    进行字面量赋值,这种方式会将之前没有创建过的字符串创建出来并且放入到字符串常量池中。

    以后只要需要值相同的字符串,直接从字符串常量池中取得即可

    String str = "abc";
    String str1 = "abc";
    
    System.out.println(str == str1);//true
    

    image-20201104191154291

    第二种方式

    第二种方式一定会进行对象的创建,但是在这种情况下有两种情况:

    1、情况一:之前已经有字符串存在于字符串常量池中了,那么在堆中开辟一块内存空间A指向字符串常量池中的这个地址B,然后把A交上去

    这种情况下有个问题,就是我们最终引用的地址确实是字符串常量池中的地址,但是因为我们最后交上去的其实是堆的地址,所以即使最终引用的是字符串常量池中的地址,和之前的地址也不可能相同!

    String str1 = "abc";
    String str = new String("abc");
    
    System.out.println(str == str1);//false
    

    image-20201105090053780

    2、情况二:之前没有字符串在字符串常量池中,这个时候直接在堆中开辟一块空间,但是不将数据放到常量池中

    String str1 = new String("abc");
    

    image-20201104191354879

    池化:强制字符串使用字符串常量池

    上面我们说,当使用对象的方式创建一个字符串时,分为两种情况,字符串没有的时候会直接在堆中而不会放到字符串常量池中。

    假如我们想要将这个字符串放入到字符串常量池中,需要使用池化技术。

    池化技术也分为两种情况:

    情况一

    我们将对象方式创建的字符串进行池化,发现字符串常量池中没有这个字符串,那么会在字符串常量池中开辟一块空间,然后指向堆中的字符串

    String str = new String("abc");
    
    String str1 = str.intern();
    
    String str2 = "abc";
    
    System.out.println(str2 == str1);//true
    

    image-20201104191806998

    情况二

    我们将对象方式创建的字符串进行池化,发现字符串常量池中竟然存在这个字符串,那么就直接交出字符串常量池中的这个字符串。

    String str = "abc";
    String str1 = new String("abc");
    String str2 = str1.intern();
    System.out.println(str == str2);//true
    

    image-20201104192020499


    字符串中的加号

    字符串中的加号在本质上是使用了StringBuilderappend操作

    我们来看下面一段代码:

    String str1 = "ab";
    
    String str2 = str1 + "c";
    

    在这里,我们的str1肯定存在于字符串常量池中,但是str2却不是,因为它使用了加号的操作。

    我们说,加号的本质其实是使用了StringBuilder的append操作,上面的str做了这样的操作:

    1、从字符串常量池中取出ab

    2、新建一个StringBuilder

    3、进行StringBuilder的append操作

    4、将StringBuilder转换为字符串的地址赋值给str2

    所以,str2和str1的地址值肯定是不相同的,除非我们使用字符串的池化技术,强制将str2使用字符串常量池。

    字符串加号的效率十分低下

    加号的操作其实本质上是StringBuilder的append操作,但是有一个问题,那就是它只要使用一个加号就会创建并销毁一次StringBuilder,直到最后剩下的那个才会转换为字符串,然后将地址值交出去

    我们来看下面一段代码

    String c = "c";
    String str = "a" + "b" + c + "d" + "e";
    

    这段代码进行了如下操作:

    1、在字符串常量池中增加了c字符串,并且赋予了变量c

    2、str的操作

    3、首先创建一个StringBuilder,是这样的:StringBuilder builder1 = new StringBuilder("ab")

    我们可以看到,StringBuilder其实进行了一个优化,直接创建了变量之前的所有数据

    4、builder1.append(c)

    5、销毁builder1,创建builder2

    6、builder2进行append操作,增加一个值,也就是d这个字符串(在变量之后,就会一个一个增加)

    7、销毁builder2

    ....

    最后剩下的builder转换了字符串,然后将地址交给了str

    StringBuffer和StringBuilder

    体系结构

    1594284732991

    • String:实现SerializableCharSequence
    • StringBuilder:实现SerializableCharSequence,继承AbstractStringBuilder
    • StringBuffer:实现SerializableCharSequence,继承AbstractStringBuilder

    构造方法

    • StringBuilder():构造一个没有字符的,初始容量为16个字符
    • StringBuilder(int capacity):指定容量构建
    • StringBuilder(String str):构建一个初始化为指定字符串的

    两者差不多,以一个举例子

    常用方法

    添加

    追加

    StringBuilder builder = new StringBuilder();
    
    
    /*追加字符串*/
    builder.append("ab");
    builder.append("bc");
    builder.append("CD");
    
    /*追加字符*/
    builder.append('a');
    
    /*追加字符数组*/
    builder.append(new char[]{'a', 'b', 'c'});
    
    /*指定字符数组的范围追加*/
    builder.append(new char[]{'a', 'b', 'c'}, 0, 1);
    
    
    /*追加不是字符串的其他常量,以字符串形式追加,事实上可以追加Object*/
    builder.append(1);
    builder.append(1.0);
    builder.append(false);
    builder.append(1L);
    
    
    /*追加StringBuffer和StringBuilder*/
    builder.append(new StringBuilder("...StringBuilder..."));
    builder.append(new StringBuffer("...StringBuffer..."));
    
    
    System.out.println(builder.toString());
    /*abbcCDaabca11.0false1...StringBuilder......StringBuffer...*/
    

    插入

    StringBuilder builder = new StringBuilder();
    
    builder.append("_|--|_");
    
    /*
                插入方法就是插队,原本的数据被顶到了后面去
                insert有多个重载:
                    - 	insert(int offset, boolean b):指定位置插入,插入boolean类型的参数
                    - 	insert(int offset, char c):指定位置插入,插入char类型的参数
                    - 	insert(int offset, char[] str):指定位置插入,插入char数组
                    - 	insert(int index, char[] str, int offset, int len):指定位置插入,插入指定范围的数组
                    -   可以插入的类型还有int,double,float,object ....
    */
    builder.insert(3,"0false1...StringBuilder......StringBuffer");
    System.out.println(builder.toString());//_|-0false1...StringBuilder......StringBuffer-|_
    

    查找

    返回容量

    StringBuilder builder = new StringBuilder();
    
    builder.append("_|--|_");
    builder.insert(3, "0false1...StringBuilder......StringBuffer");
    
    /*返回容量*/
    System.out.println(builder.capacity());//47
    

    返回字符长度

    StringBuilder builder = new StringBuilder();
    
    builder.append("_|--|_");
    builder.insert(3, "0false1...StringBuilder......StringBuffer");
    
    /*返回字符长度*/
    System.out.println(builder.length());//47
    

    根据索引查询字符

    StringBuilder builder = new StringBuilder();
    
    builder.append("_|--|_");
    builder.insert(3, "0false1...StringBuilder......StringBuffer");
    
    /*查询指定索引处的字符*/
    System.out.println(builder.charAt(10));//.
    

    根据字符查询索引

    StringBuilder builder = new StringBuilder();
    
    builder.append("_|--|_");
    builder.insert(3, "0false1...StringBuilder......StringBuffer");
    
    /*查询字符首次出现的位置*/
    System.out.println(builder.indexOf("."));//10
    
    
    /*查询字符首次出现的位置,从指定的位置开始*/
    System.out.println(builder.indexOf(".", 15));//26
    
    
    /*查询字符最后一次出现的位置*/
    System.out.println(builder.lastIndexOf("_"));//46
    
    
    /*查询字符最后一次出现的位置,到指定的位置结束*/
    System.out.println(builder.lastIndexOf("_", 2));//0
    

    替换

    替换

    StringBuilder builder = new StringBuilder("1234567890");
    
    /*替换指定范围的字符,使用指定的字符串代替*/
    System.out.println(builder.replace(0, 4, "."));//.567890
    
    /*指定位置,替换*/
    builder.setCharAt(0,'_');
    System.out.println(builder.toString());//_567890
    

    删除

    StringBuilder builder = new StringBuilder("1234567890");
    
    /*----------------------------删除----------------------------*/
    
    /*删除指定范围的字符*/
    System.out.println(builder.delete(0, 3).toString());//4567890
    
    /*删除指定位置的字符*/
    System.out.println(builder.deleteCharAt(0).toString());//567890
    

    删除其实也是另类的替换,你完全可以使用空字符串替换掉原有的


    分割

    分割

    StringBuilder builder = new StringBuilder("123456789");
    
    /*截取为String,从指定位置开始截取*/
    System.out.println(builder.substring(0));//123456789
    
    /*截取为String,指定范围截取,左闭右开,包括左边不包括右边*/
    System.out.println(builder.substring(0, 1));//1
    

    转换

    转换为字符串

    StringBuilder builder = new StringBuilder("0123456789");
    
    System.out.println(builder.toString());//0123456789
    

    翻转

    StringBuilder builder = new StringBuilder("0123456789");
    
    /*翻转*/
    System.out.println(builder.reverse().toString());
    

    特性

    三者的联系

    这三者指的是:

    • String
    • StringBuilder
    • StringBuffer

    String,StringBuilder和StringBuffer

    在讲String的时候我们曾经说过,有一个字符串常量池专门存放字符串的东西,但是字符串常量池其实还达不到我们的需求

    因为字符串常量池底层是存放的字符串,只有当字符串相同的时候才可以复用,但是如果需要这个字符串的子字符串就不能复用

    image-20200712102030050

    即使可以复用,也是复用的相同字符串,同样是创建的新的对象

    于是StringBuilder和StringBuffer出现了

    StringBuffer和StringBuilder的对象可以被多次修改而不产生新的对象,用于更好的复用

    String

    private final char value[];
    
    public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }
    

    final的char[],不可变

    StringBuilder

    //构造1,直接创造一个容量为16的StringBuilder
    public StringBuilder() {
        super(16);
    }
    
    //构造2,创造一个容量为	字符串长度+16的	StringBuilder
    public StringBuilder(String str) {
        super(str.length() + 16);
        append(str);
    }
    
    //增加的方法,直接调用的父类方法
    @Override
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }
    
    //父类的追加方法
    public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();//假如为空,返回
    
        int len = str.length();
        ensureCapacityInternal(count + len);
    
        /*
                使用的是System.arraycopy(上一个char[],0,value,count,len-0);
                简单来讲就是原有的字符数组+新的字符数组形成一个字符数组
            */
        str.getChars(0, len, value, count);
    
        count += len;
        return this;//返回
    }
    

    StringBuffer

    public StringBuffer(String str) {
        super(str.length() + 16);
        append(str);
    }
    
    @Override
    public synchronized int capacity() {
        return value.length;
    }
    
    @Override
    public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);
        return this;
    }
    
    @Override
    public synchronized String toString() {
        if (toStringCache == null) {
            toStringCache = Arrays.copyOfRange(value, 0, count);
        }
        return new String(toStringCache, true);
    }
    
    @Override
    public synchronized StringBuffer reverse() {
        toStringCache = null;
        super.reverse();
        return this;
    }
    

    和StringBuilder高度相似,不过所有的方法都加上了synchronized


    StringBuilder和StringBuffer

    • StringBuilder是线程不安全的,但是效率高,所以单线程下一般用这个
    • StringBuffer是线程安全的,但是效率相对StringBuilder要低

    这个我们在查看源码的时候就已经看到了,StringBuffer的所有方法全部都加上了synchronized



    Scanner

    构造方法

    Scanner:一个扫描器,可以通过不同源得到文本

    @Test
    public void Step1() throws IOException {
        File file = new File("hello.txt");
    
        InputStream inputStream = new FileInputStream(file);
        
        //从指定的文件得到值
        Scanner fileScanner = new Scanner(file);
        
        //从输入流中得到值
        Scanner streamScanner = new Scanner(inputStream);
    
        //从指定的字符串的导致
        Scanner strScanner = new Scanner("hello world");
    
        //从系统输入得到值
        Scanner scanner = new Scanner(System.in);
    
        inputStream.close();
    }
    

    常用API

    打印

    @Test
    public void Step2() throws FileNotFoundException {
        File file = new File("src/hello.txt");
    
        Scanner scanner = new Scanner(file);
    
        /*
                结果:
                    Hello
                    World
                - scanner.hashNext():作用是判断是否存在下一个值,到空格和回车停止
                - scanner.next():指针指向下一个值,也就是读取下一个值,到空格和回车停止,也就是不读取回车和空格
             */
        while (scanner.hasNext()) {
            System.out.println(scanner.next());
        }
    
        /*
                结果:Hello World
    
                - scanner.hasNextLine():作用是判断是否存在下一个值,空格不停止回车停止
                - scanner.nextLine():指针指向下一个值,也就是读取下一个值,可以读取空格,到回车停止
             */
        while (scanner.hasNextLine()){
            System.out.println(scanner.nextLine());
        }
    }
    

    人机交互

    @Test
    public void Step3() {
        Scanner scanner = new Scanner(System.in);
    
        String next = scanner.next();
    
        int i = scanner.nextInt();
    }
    

    Scanner的nextXXX方法的作用就是读取控制台中输入的字符,其中字符串为next,其他为nextXXX,比如nextIntnextDouble等等

    Random

    伪随机和真随机

    • 区分伪随机和真随机的只有一点:结果是否由算法得出来的

    计算机使用的是算法得出来的,也就是说,计算是伪随机的

    当我们调用产生随机数的方法时,计算机会抓出一些数据,比如当前时间,CPU的温度,风扇的转动等等,然后将这些数据投入到算法中,最后得出来最终的结果,产生最后给出你的这个数据。

    也就是说,假如你可以完美的模拟出来当时的情景,计算机得到的数据就是确定的。

    人脑得到的随机数是真随机的

    我们想要得到一个随机数,就算你模拟出来所有的情景,也不见得人给你的一个随机数是那个确定的随机数

    有了确定的输入,但是输出是不确定的,所以这个随机是真随机


    构造方法和常用API

    @Test
    public void Step1() {
        //构造方法
        Random random = new Random();
    
    
        /*
                - random.nextInt():随机出一个int类型的数据
                - random.nextInt(100):随机出一个在[0,100)之间的数据,左闭右开(不包括100)
                - random.nextFloat():随机出一个float类型的数据
                - random.nextDouble():随机出一个double类型的数据
                - random.nextBoolean():随机出一个boolean类型的数据
                - random.nextBytes(bytes):随机出byte类型的数据,并且充满到byte数组中,没有返回值
             */
        int nextI = random.nextInt();
    
        int nextIRandom = random.nextInt(100);
    
    
        byte[] bytes = new byte[10];
        random.nextBytes(bytes);
        System.out.println(Arrays.toString(bytes));
    
    }
    

    Math

    @Test
    public void test1() {
    
        //返回绝对值,返回double
        int abs = Math.abs(-5);
    
        //向下取整,返回double
        double ceil = Math.ceil(-5.5);
    
        //向上取整,返回double
        double floor = Math.floor(-5.5);
    
        //随机,返回double,随机返回0~1
        double random = Math.random();
    
        //四舍五入,返回long
        long round = Math.round(5.5);
    
    }
    

    日期和时间

    Date

    构造方法

    • Date():新建一个Date,时间是当前时间
    • Date(long date):输入一个long类型,时间根据输入的来
    • Date(year,month,day):输入年月日,时间按照输入来

    常用API

    Date date = new Date();
    
    System.out.println("当前时间" + date.getTime());
    
    System.out.println("当前年:" + date.getYear());
    
    System.out.println("当前月" + date.getMonth());
    

    事实上,Data类已经有非常多过时的方法了,现在了解即可,没有必要深究

    SimpleDateFormat

    配合Date使用

    定义格式化日期的规则,首先来看一下格式化的规则:

    字符 意义
    y
    M
    d
    H
    m
    s

    定义规则

    Date date = new Date();
    
    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
    //将字符串解析为date对象
    Date parse = format.parse("2020-7-28 15:17:20");
    System.out.println(parse.getTime());
    
    //将date解析为字符串
    System.out.println(format.format(date));
    

    Calendar

    在Date之后出现,替换了非常多的Date方法,更近一层

    构造

    Calendar是抽象类,使用getInstance()进行创建,时间为默认的时区和语言环境来创建的

    Calendar calendar = Calendar.getInstance();
    

    常用API

    @Test
    public void test1() throws ParseException {
        Calendar calendar = Calendar.getInstance();
    
    
        //通过日历获得date
        System.out.println(calendar.getTime());
    
    
        //获得年
        System.out.println(calendar.get(Calendar.YEAR));
    
        //获得月,注意,这里的月是要+1才符合中国人的习惯,外国人是0~11
        System.out.println(calendar.get(Calendar.MONTH) + 1);
    
        //获得天,月中的天
        System.out.println(calendar.get(Calendar.DAY_OF_MONTH));
    
        //获得天,周中的天
        System.out.println(calendar.get(Calendar.DAY_OF_WEEK));
    
        //获得天:年中的天
        System.out.println(calendar.get(Calendar.DAY_OF_YEAR));
    
    
        //手动将时间进行改变,也可以设置月,天,等等
        calendar.add(Calendar.YEAR, -3);
    
        //可以直接设置
        calendar.set(2019, 10, 11);
    
    }
    

    LocalDateTime

    别再用之前的时间操作了,现在要使用新时代的时间操作:LocalDateTime,并且LocalDateTime是线程安全的

    接下来的内容来自于程序羊:https://mp.weixin.qq.com/s/v-Va_GuSUGr9HVAW84kloQ

    构造

    //指定时间构造:2019-10-12 9:21:32
    LocalDateTime time = LocalDateTime.of(2019, 10, 12, 9, 21, 32);
    
    //获得当前时间
    LocalDateTime now = LocalDateTime.now();
    

    获得当前时间

    //获得当前时间
    LocalDateTime now = LocalDateTime.now();
    
    System.out.println("当前时间" + now);
    
    System.out.println("当前年份" + now.getYear());
    
    //月份按照英文,比如JULY
    System.out.println("当前月份" + now.getMonth());
    
    System.out.println("当前日,按月" + now.getDayOfMonth());
    
    System.out.println("当前日,按周" + now.getDayOfWeek());
    
    System.out.println("当前日,按年" + now.getDayOfYear());
    
    System.out.println("当前时" + now.getHour());
    
    System.out.println("当前分" + now.getMinute());
    
    System.out.println("当前秒" + now.getSecond());
    

    时间加减

    //减少1年
    LocalDateTime minusYears = now.minusYears(1);
    //增加一年
    LocalDateTime plusYears = now.plusYears(1);
    //直接设置
    LocalDateTime withYear = now.withYear(2020);
    

    格式化

    //格式化:yyyy-MM-dd
    String format1 = now.format(DateTimeFormatter.ISO_DATE);
    System.out.println(format1);
    
    //格式化:yyyyMMdd
    String format2 = now.format(DateTimeFormatter.BASIC_ISO_DATE);
    System.out.println(format2);
    
    //格式化:自定义
    String format3 = now.format(DateTimeFormatter.ofPattern("yyyy__MM__dd"));
    System.out.println(format3);
    

    时间反解析

    //时间反解析
    LocalDateTime parse = LocalDateTime.parse
        ("2020-12-21  11:22:31", DateTimeFormatter.ofPattern("yyyy-MM-dd  HH:mm:ss"));
    
    System.out.println(parse);
    
  • 相关阅读:
    什么是整型数据类型
    什么是布尔型(boolean)数据类型?
    PHP的八种数据类型
    php中session时间设置详解
    php中session数据库存储实例详解
    php中session临时文件和缓存说明
    php自定义Session的信息处理说明
    如何通过Session对用户操作权限进行判断的实例详解
    php中的Session是什么
    php中如何传递Session ID
  • 原文地址:https://www.cnblogs.com/howling/p/14230583.html
Copyright © 2020-2023  润新知