0%

常用类-1

常用类

★第十三章 常用类

包装类

  • 针对八种基本数据类型的相应的引用类型就是包装类。

    有了类的特点,就可以调用类中的方法。

    基本数据类型包装类
    booleanBoolean
    charCharacter
    byteByte
    shortShort
    intInteger
    longLong
    floatFloat
    doubleDouble
  • Byte、Short、Integer、Long、Float、Double 都是 Number 的子类。

装箱和拆箱
  • 装箱:基本数据类型 -> 包装类型

    拆箱:包装类型 -> 基本数据类型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    package com.f.chapter13;

    public class Integer01 {
    public static void main(String[] args) {
    //演示 int <---> Integer 的装箱与拆箱
    //jdk5以前用的是手动装箱和手动拆箱的方式
    int n1 = 100;
    Integer integer1 = new Integer(n1); //手动装箱方式1
    Integer integer2 = Integer.valueOf(n1); //手动装箱方式2
    int n2 = integer1.intValue(); //手动拆箱方式

    //jdk5以后可以用自动装箱和自动拆箱
    Integer integer3 = n2; //自动装箱:底层使用的就是 Integer.valueOf(n2)
    int n3 = integer3; //自动拆箱:底层使用的就是 intValue() 方法
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    package com.f.chapter13;

    public class String01 {
    public static void main(String[] args) {
    //包装类(以Integer为例)和 String 的相互转换
    //Integer -> String
    Integer i1 = 100; //自动装箱
    String s1 = i1 + ""; //方式1
    String s2 = i1.toString(); //方式2
    String s3 = String.valueOf(i1); //方式3
    //String -> Integer
    Integer i2 = Integer.parseInt(s1); //方式1 parseInt方法返回的是 int 类型数据,所以这里还用到了自动装箱,将 int 装箱为 Integer
    Integer i3 = Integer.valueOf(s1); //方式2
    }
    }
包装类常用方法(Integer、Character)
  • Integer 类常用方法:

    1
    2
    3
    4
    5
    6
    7
    8
    package com.f.chapter13;

    public class IntegerMethod {
    public static void main(String[] args) {
    System.out.println(Integer.MIN_VALUE); //Integer的最小值
    System.out.println(Integer.MAX_VALUE); //Integer的最大值
    }
    }
  • Character 类常用方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    package com.f.chapter13;

    public class CharacterMethod {
    public static void main(String[] args) {
    System.out.println(Character.isDigit('1')); //判断是不是数字
    System.out.println(Character.isLetter('a')); //判断是不是字母
    System.out.println(Character.isUpperCase('A')); //判断是不是大写
    System.out.println(Character.isLowerCase('a')); //判断是不是小写
    System.out.println(Character.isWhitespace(' ')); //判断是不是空格

    System.out.println(Character.toUpperCase('a')); //转换为大写
    System.out.println(Character.toLowerCase('A')); //转换为小写
    }
    }
★Integer创建机制
  • 看如下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    package com.f.chapter13;

    /**
    * @author fzy
    * @date 2023/4/23 10:26
    */
    public class Integer02 {
    public static void main(String[] args) {
    Integer i = new Integer(1);
    Integer j = new Integer(1);
    System.out.println(i == j); //false

    Integer m = 1;
    Integer n = 1;
    System.out.println(m == n); //true

    Integer x = 128;
    Integer y = 128;
    System.out.println(x == y); //false
    }
    }

    发现输出分别为 false、true、false。输出结果与 Integer 的创建机制有关。

    1. 对于第一种情况,因为是 new 了两个对象,这两个对象并不相同,所以为 false。

    2. 对于第二种和第三种情况,前面已经说过,这里用到了自动装箱:底层使用的就是 Integer.valueOf() 方法,所以看源码:

      1
      2
      3
      4
      5
      public static Integer valueOf(int i) {
      if (i >= IntegerCache.low && i <= IntegerCache.high)
      return IntegerCache.cache[i + (-IntegerCache.low)];
      return new Integer(i);
      }

      发现:

      • 如果 i >= IntegerCache.low && i <= IntegerCache.high,那么就从 IntegerCache.cache[] 数组中取出相应的 Integer 对象。
      • 否则,直接 new 一个 Integer 对象并返回。

      而源码中设定 IntegerCache.low = -128IntegerCache.high = 127,所以:

      • 对于第二种情况,因为 -128 < 1 < 127,所以都返回的是 IntegerCache.cache[] 中的 Integer 对象,因此 m 和 n 指向的是同一个 Integer 对象,结果为 true。
      • 对于第三种情况,因为 128 > 127,所以都是直接 new 了一个 Integer 对象,x 和 y 指向的是不同的 Integer 对象,结果为 false。

★String

String结构剖析
  1. String 对象用于保存字符串,也就是一组字符序列。

  2. 字符串常量对象是用双引号括起来的字符序列,例如:“你好”、“boy”、“12345” 等。

  3. 字符串的字符使用 Unicode 字符编码,一个字符(不论是字母还是汉字)占两个字节。

  4. String 类较为常用的构造方法:

    1
    2
    3
    4
    5
    String s1 = new String();
    String s2 = new String(String original);
    String s3 = new String(char[] a);
    String s4 = new String(char[] a, int startIndex, int count);
    String s5 = new String(byte[] b);
  5. String 类实现了接口 Serializable(String 可以串行化,可以在网络传输),实现了接口 Comparable(String 对象可以比较大小)。

  6. String 是 final 类,不能被其他的类继承。

  7. String 有属性 private final char value[];,用于存放字符串内容,注意 value[]final 类型,不可以修改(即 value 不能指向新的地址,但是它的单个字符内容是可以变化的)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    package com.f.chapter13;

    public class StringMethod03 {
    public static void main(String[] args) {
    String s = new String("john");
    System.out.println(String.class.getName() + "@" + Integer.toHexString(System.identityHashCode(s)));
    s = new String("tom");
    System.out.println(String.class.getName() + "@" + Integer.toHexString(System.identityHashCode(s)));
    }
    }

    首先变量 s 指向了堆中的一个 String 对象,该 String 对象中的 value 指向了常量池中的字符串 “john”;

    然后在 s = new String("tom"); 创建了一个新的 String 对象,变量 s 变为指向这个新的 String 对象,该新的 String 对象中的 value 指向了常量池中的字符串 “tom”,亦即,第一个 String 对象中的 value 指向的地址并没有发生改变,不可以修改

★★★String创建剖析
  • 创建 String 对象的两种方式:

    1. 直接赋值String s = "hello";

      先从常量池中查看是否有 ”hello“ 数据空间,如果有,则直接指向;如果没有则重新创建,然后指向。s 最终指向的是常量池的空间地址

    2. 调用构造器String s = new String("hello");

      先在堆中创建空间,里面维护了 value 属性,value 指向常量池的 ”hello“ 空间,如果常量池中没有 ”hello“ 则重新创建,如果有则直接通过 value 指向,最终指向的是堆中的空间地址

★String对象特性
  1. String 是一个 final 类,代表不可变的字符序列。

  2. 字符串是不可变的,一个字符串对象一旦被分配,其内容是不可变的。

    字符串对象 ”hello“ 的内容并没有变化,而是让字符串变量 s1 指向了新的字符串对象 ”haha“,所以是创建了两个字符串对象。

  3. 下面的例子中,只在常量池中创建了一个字符串对象,因为编译器做了一个优化。

  4. 下面的例子中,在常量池中创建了三个字符串对象,但是需要注意 c 指向的是堆中的对象,而不是常量池中的对象

    • 注意:一个变量加一个字符串常量,如

      String s = "hello,";

      String s2 = s + "world!";

      s2 的创建过程和两个变量相加的创建过程是一样的,即最终结果为:s2 指向堆中的对象,堆中对象的 value[] 指向常量池中的 "hello,world!"

String常用方法
  1. intern():当调用 intern 方法时,如果常量池中已经包含一个内容等于此 String 对象的字符串(用 equals(Object) 方法确定),则返回常量池中的字符串地址;否则,将此 String 对象添加到池中,并返回此 String 对象的引用地址。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    package com.f.chapter13;

    public class String02 {
    public static void main(String[] args) {
    String s1 = new String("1") + new String("1"); //常量池中生成”1“,s1指向堆中的对象,堆中对象的value内容为”11“
    s1.intern(); //常量池中没有”11“,所以将s1的地址添加到常量池中
    String s2 = "11"; //因为常量池中存在”11“的引用s1,所以s2指向常量池中s1的地址
    System.out.println(s1 == s2); //返回true
    }
    }
  2. s1.equals(s2):判断内容是否相等(区分大小写)

  3. s1.equalsIgnoreCase(s2):判断内容是否相等(忽略大小写)。

  4. s1.length():获取字符串的长度。

  5. s1.indexOf(s2):获取字符在字符串中第一次出现的索引,索引从 0 开始,如果找不到,返回 -1

  6. s1.lastIndexOf(s2):获取字符在字符串中最后一次出现的索引,索引从 0 开始,如果找不到,返回 -1。

  7. substring:截取指定范围的字串。

  8. s1.trim():去除前后空格。

  9. s1.charAt(index):获取某索引处的字符,注意不能使用 Str[index] 这种方式

  10. s1.toUpperCase():转换成大写。

  11. s1.toLowerCase():转换成小写。

  12. s1.replace(oldChar,newChar):替换字符串中的内容,但需要注意的是,replace 方法执行后,返回的结果才是替换过的,而对原来的字符串对象没有影响,从源码可以看到,是返回了一个新的字符串对象:return new String(buf, true);

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    package com.f.chapter13;

    public class StringMethod01 {
    public static void main(String[] args) {
    String s1 = "hello";
    String s2 = s1.replace("he","ol");
    System.out.println(s1); //s1指向的常量池中的 “hello”对象 并没有发生变化
    System.out.println(s2); //是在堆中创建了一个新的 String 对象,对象的 value 指向常量池中的 “olllo”
    }
    }

    输出结果为

    1
    2
    hello
    olllo
  13. s1.concat(s2):拼接字符串。和 replace 类似,会返回一个新的字符串对象,而对原来的字符串对象无影响。

  14. s1.split("ch"):分割字符串,返回结果为字符串数组。对于某些分割字符,我们需要转义,比如 “|” “\” 等

  15. s1.toCharArray():将字符串转换为字符数组。

  16. s1.compareTo(s2):比较两个字符串的大小,如果前者大,则返回正数;后者大,则返回负数;如果相等,则返回 0

    • 如果长度相同,并且每个字符也相同,就返回 0
    • 如果长度不相同,但是在进行比较时,不能区分大小(对应字符都一样),就返回 len1 - len2
    • 如果长度相同或者不相同,但是在进行比较时,可以区分大小,就返回 c1 - c2

    看源码即可理解:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public int compareTo(String anotherString) {
    int len1 = value.length;
    int len2 = anotherString.value.length;
    int lim = Math.min(len1, len2);
    char v1[] = value;
    char v2[] = anotherString.value;

    int k = 0;
    while (k < lim) {
    char c1 = v1[k];
    char c2 = v2[k];
    if (c1 != c2) {
    return c1 - c2;
    }
    k++;
    }
    return len1 - len2;
    }
  17. String.format():格式化字符串。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    package com.f.chapter13;

    public class StringMethod02 {
    public static void main(String[] args) {
    String name = "小明";
    int age = 10;
    double score = 100 / 3;
    String formatStr = "我的姓名是%s 年龄是%d 成绩是%.2f";
    String info = String.format(formatStr, name, age, score);
    System.out.println(info);
    }
    }

    %s%d%.2f 等称为占位符,这些占位符由后面的变量来替换。

★StringBuffer

  • java.lang.StringBuffer 代表可变的字符序列,可以对字符串的内容进行增删。

    StringBuffer 的很多方法和 String 相同,但是 StringBuffer 是可变长度的。 StringBuffer 是一个容器。

StringBuffer结构剖析
  1. StringBuffer 的直接父类是 AbstractStringBuilder。
  2. StringBuffer 实现了 Serializable,即 StringBuffer 的对象可以串行化。
  3. 在父类 AbstractStringBuilder 中,有属性 char[] value;value 数组不是 final 类型(和 String 不同)。该 value 数组存放字符串内容,且存放在堆中(而非常量池中)。
  4. StringBuffer 是 final 类,不能被继承。
  • 注意:String 保存的是字符串常量,value 的地址不能修改,每次 String 类的更新实际上就是创建新的 String 对象,然后让 String 变量的地址更改为新的 String 对象的地址,效率较低(private final char value[];);

    StringBuffer 保存的是字符串变量,里面的值可以更改,每次 StringBuffer 的更新实际上可以更新内容,而不用更新地址,效率高(char[] value;)。

StringBuffer和String的转换
  • StringBuffer的构造器

    • StringBuffer():构造一个其中不带字符的字符串缓冲区,其初始容量为16个字符
    • StringBuffer(CharSequence seq)public java.lang.StringBuilder(CharSequence seq) 构造一个字符串缓冲区,它包含与指定的 CharSequence 相同的字符。
    • StringBuffer(int capacity):构造一个不带字符,但具有指定初始容量的字符串缓冲区。即对 char[] 大小进行指定。
    • StringBuffer(String str):构造一个字符串缓冲区,并将其内容初始化为指定的字符串内容。char[] 大小为 str.length() + 16
  • String 和 StringBuffer 的转换:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    package com.f.chapter13;

    /**
    * @author fzy
    * @date 2023/4/23 10:18
    */
    public class StringAndStringBuffer {
    public static void main(String[] args) {
    //String -> StringBuffer
    String str = new String("Tom");
    //方式1:使用 StringBuffer 的构造器
    StringBuffer stringBuffer1 = new StringBuffer(str);
    //方式2:使用 StringBuffer 的 append 方法
    StringBuffer stringBuffer2 = new StringBuffer();
    stringBuffer2.append(str);

    //StringBuffer -> String
    StringBuffer stringBuffer3 = new StringBuffer("Jerry");
    //方式1:使用 StringBuffer 的 toString 方法
    String s1 = stringBuffer3.toString();
    //方式2:使用 String 的构造器
    String s2 = new String(stringBuffer3);

    System.out.println(stringBuffer1);
    System.out.println(stringBuffer2);
    System.out.println(stringBuffer1 == stringBuffer2); //false

    System.out.println(s1);
    System.out.println(s2);
    System.out.println(s1 == s2); //false
    }
    }
StringBuffer常用方法
  1. 增:s.append(),在 StringBuffer 对象中增加内容。

  2. 删:s.delete(start, end),删除索引为 [start, end) 处的字符。

  3. 改:s.replace(start, end, "str"),使用 ”str“ 替换索引为 [start, end) 处的字符。

  4. 查:s.indexOf("str"),查找指定的子串在字符串中第一次出现的索引,如果找不到则返回 -1。

  5. 插:s.insert(index, "str"),在索引为 index 的位置插入字符串 “str”,原来索引为 index 的内容自动后移。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    package com.f.chapter13;

    /**
    * @author fzy
    * @date 2023/4/24 10:21
    */
    public class StringBufferMethod {
    public static void main(String[] args) {
    //增:append
    StringBuffer s = new StringBuffer("hello"); //hello
    s.append(","); //hello,
    s.append("world!").append(100).append(true).append(1.1); //hello,world!100true1.1
    System.out.println(s); //输出 "hello,world!100true1.1"

    //删:delete
    s.delete(12, 15); //删除 "100"
    System.out.println(s); //输出 "hello,world!true1.1"

    //改:replace
    if (s.indexOf("true") != -1) {
    s.replace(s.indexOf("true"), s.indexOf("true") + "true".length(), "false");
    System.out.println(s); //输出 "hello,world!false1.1"
    }

    //查:indexOf
    System.out.println(s.indexOf("false")); //输出 12

    //插:insert
    s.insert(s.indexOf("false"), "true, ");
    System.out.println(s); //输出 "hello,world!true, false1.1"
    }
    }

★StringBuilder

  • java.lang.StringBuilder 表示一个可变的字符序列,此类提供一个与 StringBuffer 兼容的 API,但不保证同步(StringBuilder 不是线程安全的)。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候。如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。
  • 在 StringBuilder 上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。
StringBuilder结构剖析
  1. StringBuilder 的直接父类是 AbstractStringBuilder。
  2. StringBuilder 实现了 Serializable,即 StringBuilder 的对象可以串行化。
  3. 在父类 AbstractStringBuilder 中,有属性 char[] value;value 数组不是 final 类型(和 String 不同)。该 value 数组存放字符串内容,且存放在堆中(而非常量池中)。
  4. StringBuilder 是 final 类,不能被继承。
  5. StringBuilder 的方法没有做互斥处理,既没有 synchronized 关键字,因此只在单线程的情况下使用 StringBuilder

★String、StringBuffer、StringBuilder对比

  1. StringBuilder 和 StringBuffer 非常类似,均代表可变的字符序列,而且方法也一样。

  2. String:不可变字符序列,效率低,但是复用率高

  3. StringBuffer:可变字符序列,效率较高,线程安全。

  4. StringBuilder:可变字符序列,效率最高,线程不安全。

  5. String 使用注意:

    String s = "a"; //创建了一个字符串

    s += "b"; //实际上原来的 "a" 字符串对象已经丢弃了,现在又产生了一个字符串 s+"b" (也就是 "ab" )

    如果多次执行这些改变串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率。

    如果这样的操作放到循环中,会极大影响程序的性能 => 结论 : 如果我们对 String 做大量修改,不要使用 String

  6. String、StringBuffer、StringBuilder 使用原则

    1. 如果字符串存在大量的修改操作,一般使用 StringBufferStringBuilder
      • 如果字符串存在大量的修改操作,并在单线程的情况,则使用 StringBuilder
      • 如果字符串存在大量的修改操作,并在多线程的情况,则使用 StringBuffer
    2. 如果我们字符串很少修改,被多个对象引用,则使用String,比如配置信息等。
---------------The End---------------