Java语法相关知识汇总(四)

第九章、String 类(不可变)

一、概述

1、在Java中,java.lang.String类代表字符串。它是不可变(immutable)的,即一旦创建,就不能修改其内容。

2、在程序中出现双引号包括的字符串,不一定就会在方法区常量池中创建一个String对象,要看它是否进行拼接操作,查看源码编译后的class字节码文件的结果。

字符串常量池常量折叠技术详解

1、String表示字符串类型,属于引用数据类型,不属于基本数据类型

2、在java中随意使用双引号括起来的都是String对象。例如:“abc”,“def”,都是String对象

3、java中规定,双引号括起来的字符串,是不可变的,也就是说“abc”自出生到死亡,都是不可变的

1、字符串必须使用双引号

        在 Java 中,字符串的表示必须使用双引号 " " 来包围,而不支持使用单引号 ' ' 来表示字符串。这是 Java 语言的规定,单引号在 Java 中用于表示字符(char)类型,而不是字符串(String)类型。

以下是一个示例,说明在 Java 中字符串必须使用双引号表示:

public class StringExample {
    public static void main(String[] args) {
        String myString = "This is a string.";
        System.out.println(myString);
    }
}

在上面的示例中,myString 是一个字符串变量,它被双引号包围。

如果你想在 Java 字符串中包含双引号,可以使用转义字符 \" 来表示:

String stringWithQuotes = "He said, \"Hello!\"";
System.out.println(stringWithQuotes);

        但是请注意,Java 不支持使用单引号来表示字符串。单引号在 Java 中用于表示字符常量,例如 'a' 表示字符 'a'。

所以,在 Java 中,字符串必须使用双引号来表示,而单引号用于字符常量。

2、字符串不可变解释

//字符串不可变是说“”双引号里面的字符串对象一旦创建不可变
//而不是会所引用s中保存的内存地址不可变,除非s是被final修饰的
String s = "abc"; //“abc”放到了方法区字符串常量池中,“abc”不可变

//s是可以指向其他字符串对象的
s = "xyz"; //“xyz”放到了方法区字符串常量池中,“xyz”不可变

        在 Java 中,字符串(String)是不可变的数据类型,这意味着一旦创建了字符串对象,就不能更改它的内容。当你对字符串进行操作(例如连接、截取、替换等),实际上是创建了一个新的字符串对象,而不是在原始的字符串对象上进行修改。

这种字符串不可变性的特性具有一些重要的影响和优势:

  1. 保证数据的安全性: 不可变性确保了字符串内容在创建后不能被意外修改,从而避免了在程序运行时发生意外的数据更改。

  2. 优化内存使用: 由于字符串不可变,Java 可以在适当的情况下共享字符串的内存,从而减少内存的使用,提高性能。

  3. 字符串池: 由于字符串是不可变的,Java 使用了字符串池(String Pool)来共享相同值的字符串对象,从而节省内存和提高性能。

  4. 多线程安全: 字符串的不可变性使得多线程操作字符串时不会出现竞争条件,从而减少了线程安全问题。

下面是一些示例来说明字符串的不可变性:

String originalString = "Hello";
String modifiedString = originalString + ", World!";  // 创建新的字符串对象
System.out.println(originalString);  // 输出仍然是 "Hello"
System.out.println(modifiedString);  // 输出 "Hello, World!"

        在上面的示例中,我们对 originalString 进行了连接操作,得到了一个新的字符串 modifiedString。原始的字符串 originalString 的内容没有被修改,仍然保持不变。

        总之,Java 中的字符串不可变性确保了字符串对象一旦创建就不会被修改,这种特性在编程中有很多优势,包括数据的安全性、内存优化、字符串池和线程安全。

3、字符串对象内存优化:字符串常量池

在java中双引号括起来的字符串,例如:“abc”都是直接存储在JVM的方法区的字符串常量池中。因为字符串在实际的开发中使用太频繁,为了执行效率,所以吧字符串放到了方法区的常量池中。如果在创建相同值的字符串对象时可以共享

4、字符串对象是可索引,可迭代的

        在 Java 中,字符串是可索引和可迭代的。这意味着你可以通过索引访问字符串中的单个字符,并且你可以使用循环来迭代字符串中的每个字符。

        在 Java 中,字符串的索引从 0 开始,即第一个字符的索引是 0,第二个字符的索引是 1,依此类推。你可以使用 charAt() 方法来访问特定索引位置的字符。

String str = "Hello, World!";
char firstChar = str.charAt(0);  // 获取第一个字符 'H'
char secondChar = str.charAt(1);  // 获取第二个字符 'e'

此外,你还可以使用增强型的 for-each 循环来迭代字符串中的每个字符:

String str = "Hello";

        for (char c : str.toCharArray()) {
            System.out.print(c + " ");  // 输出每个字符,依次为 'H', 'e', 'l', 'l', 'o'

        }
        System.out.println();
        for (int i = 0; i < str.length(); i++) {
            System.out.print(str.charAt(i) + " "); // 输出每个字符,依次为 'H', 'e', 'l', 'l', 'o'
        }

二、创建字符串对象

1、使用字面值创建  String  引用名  =  “字面值”;

public class StringTest01 {
    public static void main(String[] args) {
        //这两行代码表示底层创建了2个字符串对象,都在字符串常量池中。
        String s1 = "abcdef";  //一个字符串对象 "abcdef"
        String s2 = "abcdef" + "xy"; //另一个字符串对象 "abcdefxy"
    }
}

2、使用构造方法创建  String  引用名 =  new  String(“字面值”);

package com.javase.String;

public class StringTest01 {
    public static void main(String[] args) {

        //使用构造方法创建String对象
        //凡是双引号括起来的都是在字符串常量池中有一份。
        //new 对象的时候一定在堆内存当中开辟内存空间

        String s3 = new String("xy");

        //i变量中保存的是100这个值。
        int i = 100;
        //s变量中保存的是字符串对象的内存地址
        //s引用中保存的不是“abc“,而是 0x1111
        //而0x1111是“abc”字符串对象在“字符串常量池”当中的内存地址、 
        String s = "abc";
    }
}

3、两种方式内存图分析

package com.javase.String;

public class StringTest02 {
    public static void main(String[] args) {
        //“hello”是存储在方法区的字符串常量池中
        //所以这个“hello”不会新建,因为这个对象已经存在了
        String s1 = "hello";
        String s2 = "hello";

        //== 双引号比较的变量中保存的值(该处为内存地址)
        System.out.println(s1 == s2); //true
        System.out.println(s1.equals(s2)); //true

        String x = new String("xyz");
        String y = new String("xyz");
        //== 双引号比较的变量中保存的值(该处为内存地址)
        System.out.println(x == y); //false

        //通过这个案例的学习,我们知道了,字符串对象之间的比较不能使用 ==
        //应使用 equals 方法
        //String类已经重写了equals方法,以下的equals方法调用的是String重写之后的equals方法
        System.out.println(x.equals(y)); //true

        String k = new String("testString");
        //"testString"这个字符串可以后面加“.”尼?
        //因为“testString”是一个String对象,只要是对象都可以调用方法
        
        //使用这种方式,因为这个可以避免空指针异常。
        System.out.println("testString".equals(k)); //true
    }
}

 

 4、String类型的引用中同样也是保存String类型的内存地址

类中成员变量(实例或静态变量)为String类型,则也是保存的是字符串对象的内存地址,而不是字符串对象的值,也不是字符串对象本身

User.java

package com.javase.String;

public class User {
    private int id;
    private String name;

    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

UserTest.java

package com.javase.String;

public class UserTest {
    public static void main(String[] args) {
        User user = new User(110,"张三");
    }
}

 5、面试题分析

package com.javase.String;

public class StringTest03 {
    public static void main(String[] args) {

        /**
         * 一共创建了3个对象
         *      方法区字符串常量池中有1个:“hello”
         *      堆内存中有两个String对象
         *      一共3个
         */
        String s1 = new String("hello");
        String s2 = new String("hello");
    }
}

三、String类的构造方法 (数组元素拼接成字符串)

1、public String(byte[] bytes)

String 类的构造方法 public String(byte[] bytes) 允许使用指定的字节数组创建一个新的字符串对象。

        该构造方法的作用是将字节数组中的内容解码为字符串,使用默认的字符集来进行解码。在解码过程中,每个字节都被解释为字符的 ASCII 值。

源码实现:

public String(byte bytes[]) {
        this(bytes, 0, bytes.length);
    }

其实就是调用了该public String(byte[] bytes, int offset, int length)构造方法

参数说明:

  • bytes:要用于构造字符串的字节数组。

构造方法的行为如下:

  1. 字节数组中的每个字节将被解释为相应的字符,使用默认字符集进行解码。
  2. 解码后的字符将按照顺序组合成一个新的字符串对象。

以下是一个示例使用该构造方法的代码:

byte[] bytes = {97, 98, 99};  // 字节数组 [97, 98, 99] 对应 ASCII 字符编码中的 'a', 'b', 'c'

String str = new String(bytes);

System.out.println(str);  // 输出: "abc"

在上述示例中,bytes 字节数组被解码为字符串对象 str,其值为 "abc"。

        需要注意的是,使用该构造方法时,默认使用的字符集是平台默认字符集。如果字节数组是使用不同的字符集编码的,可以使用其他构造方法 public String(byte[] bytes, Charset charset) 来指定特定的字符集进行解码。

2、public String(byte[] bytes, int offset, int length)

String 类的构造方法 public String(byte[] bytes, int offset, int length) 允许使用指定的字节数组的子数组创建一个新的字符串对象。

        该构造方法的作用是将字节数组中指定范围的内容解码为字符串,使用默认的字符集来进行解码。在解码过程中,每个字节都被解释为字符的 ASCII 值。

参数说明:

  • bytes:要用于构造字符串的字节数组。
  • offset:子数组的起始偏移量,表示从字节数组的哪个位置开始解码。
  • length:子数组的长度,表示要解码的字节个数。

构造方法的行为如下:

  1. 从字节数组中的指定偏移量开始,(包括该偏移值对象的byte类型元素)取连续的 length 个字节。
  2. 解码这些字节为相应的字符,使用默认字符集进行解码。
  3. 解码后的字符按照顺序组合成一个新的字符串对象。

以下是一个示例使用该构造方法的代码:

byte[] bytes = {97, 98, 99, 100, 101};  // 字节数组 [97, 98, 99, 100, 101] 对应 ASCII 字符编码中的 'a', 'b', 'c', 'd', 'e'

String str = new String(bytes, 1, 3);

System.out.println(str);  // 输出: "bcd"

        在上述示例中,bytes 字节数组的子数组从索引 1 开始,长度为 3,即取字节 [98, 99, 100]。这些字节被解码为字符串对象 str,其值为 "bcd"。

        需要注意的是,使用该构造方法时,默认使用的字符集是平台默认字符集。如果字节数组是使用不同的字符集编码的,可以使用其他构造方法 public String(byte[] bytes, int offset, int length, Charset charset) 来指定特定的字符集进行解码。

3、public String(char[] value)

String 类的构造方法 public String(char[] value) 允许使用指定的字符数组创建一个新的字符串对象。

该构造方法的作用是将字符数组中的内容组合成一个字符串。

参数说明:

  • value:要用于构造字符串的字符数组。

构造方法的行为如下:

  1. 将字符数组中的每个字符按照顺序组合成一个新的字符串对象。

以下是一个示例使用该构造方法的代码:

char[] chars = {'a', 'b', 'c'};

String str = new String(chars);

System.out.println(str);  // 输出: "abc"

在上述示例中,字符数组 chars 中的字符被组合为字符串对象 str,其值为 "abc"。

        需要注意的是,字符数组中的每个字符都会被按照顺序连接起来,形成一个字符串。如果字符数组中包含空字符 ('\0') 或者在构造方法之后还有其他字符,它们将会包含在字符串中。例如,char[] chars = {'a', 'b', 'c', '\0', 'd'} 构造出的字符串将包含 "abc\0d"。

        此构造方法还有其他变体,可以通过提供字符数组的起始偏移量和长度来创建子串。例如,public String(char[] value, int offset, int count) 构造方法允许从字符数组的指定位置开始,取连续的指定长度的字符,将其组合成一个新的字符串。

4、public String(char[] value, int offset, int count)

String 类的构造方法 public String(char[] value, int offset, int count) 允许使用指定的字符数组的子数组创建一个新的字符串对象。

该构造方法的作用是将字符数组中指定范围的内容组合成一个字符串。

参数说明:

  • value:要用于构造字符串的字符数组。
  • offset:子数组的起始偏移量,表示从字符数组的哪个位置开始构造字符串。
  • count:子数组的长度,表示要构造的字符个数。

构造方法的行为如下:

  1. 从字符数组中的指定偏移量开始,取连续的 count 个字符。
  2. 将这些字符按照顺序组合成一个新的字符串对象。

以下是一个示例使用该构造方法的代码:

char[] chars = {'a', 'b', 'c', 'd', 'e'};

String str = new String(chars, 1, 3);

System.out.println(str);  // 输出: "bcd"

        在上述示例中,字符数组 chars 的子数组从索引 1 开始,长度为 3,即取字符 ['b', 'c', 'd']。这些字符被组合为字符串对象 str,其值为 "bcd"。

        需要注意的是,如果提供的偏移量 offset 超出了字符数组的有效范围,或者指定的长度 count 超过了字符数组的剩余长度,将会引发 IndexOutOfBoundsException 异常。

        此构造方法还有其他变体,可以通过提供字符数组的起始偏移量和长度来创建子串。例如,public String(char[] value, int offset, int count) 构造方法允许从字符数组的指定位置开始,取连续的指定长度的字符,将其组合成一个新的字符串。

四、String类常用方法

1、int  length():返回字符串的长度,即字符串中字符的个数

判断数组长度和判断字符串长度不一样:

判断数组长度是length属性,判断字符串长度是length() 方法

String str = "Hello";
int length = str.length();  // 返回 5

2、char charAt(int index):返回指定索引位置处的字符(相当于字符串取索引)

String str = "Hello";
char ch = str.charAt(1);  // 返回 'e'

        Python 中的字符串允许取索引和切片,而 Java 中的字符串不允许取索引和切片,这是因为两种编程语言对字符串的设计和实现方式不同。以下是解释这种差异的一些原因:

  1. 不可变性和安全性:

    • Python 的字符串是不可变的,意味着一旦创建后不能修改。因此,可以安全地允许访问和切片字符串中的字符,因为操作不会影响原始字符串。
    • Java 的字符串也是不可变的,但 Java 语言设计者可能认为允许直接访问和修改字符串中的字符可能会导致不必要的风险,因此没有提供索引和切片的功能。
  2. 语言设计理念:

    • Python 的设计理念之一是“简洁性胜过复杂性”,鼓励直接访问和操作数据,以方便编程。因此,Python 允许对字符串进行索引和切片操作。
    • Java 的设计理念之一是“显式胜过隐式”,强调代码的安全性和稳定性。Java 可能更倾向于限制对不可变对象的直接访问,以避免潜在的问题。
  3. 数据类型的不同:

    • 在 Python 中,字符串是一种内置的数据类型,由解释器直接支持。这使得 Python 可以提供方便的字符串操作。
    • 在 Java 中,字符串是一个类(String 类),由 Java 标准库提供支持。因此,Java 可能更加注重在类的设计和用法上进行控制。

        需要注意的是,虽然 Java 中的字符串不允许直接取索引和切片,但 Java 提供了一些方法来进行类似的操作,如 charAt() 方法用于获取指定位置的字符,以及 substring() 方法用于获取子字符串。

        总之,Python 和 Java 之所以在字符串索引和切片方面存在差异,是因为它们的设计和实现理念不同,以及对编程体验和数据安全性的不同权衡。

2.1、java.lang.StringIndexOutOfBoundsException

字符串索引越界异常,当传入的索引大于字符串的最大索引时会引发该异常

3、String substring(int beginIndex)String substring(int beginIndex, int endIndex):字符串切片

截取子字符串,索引左闭右开(开始索引能取到,结束索引取不到)

返回从指定索引开始到字符串末尾(或指定索引前)的子字符串。

String str = "Hello World";
String sub1 = str.substring(6);         // 返回 "World"
String sub2 = str.substring(0, 5);      // 返回 "Hello"
3.1、java.lang.StringIndexOutOfBoundsException

字符串索引越界异常,当传入的索引大于字符串的最大索引时会引发该异常

4、String concat(String str):将指定的字符串连接到原始字符串的末尾

String s1 = "hello";
String s2 = " world";
String newStr = s1.concat(s2);
System.out.println(newStr); //hello world

5、String toUpperCase() 和String toLowerCase():将字符串转换为大写或小写

String str = "Hello";
String upperCase = str.toUpperCase();    // 返回 "HELLO"
String lowerCase = str.toLowerCase();    // 返回 "hello"

6、String trim():去除字符串两端的空格

String str = "   Hello   ";
String trimmedStr = str.trim();          // 返回 "Hello"

7、boolean equals(Object obj)boolean equalsIgnoreCase(String str)

比较字符串是否相等,区分大小写或不区分大小写。

String str1 = "Hello";
String str2 = "hello";
boolean equals = str1.equals(str2);                   // 返回 false
boolean equalsIgnoreCase = str1.equalsIgnoreCase(str2);  // 返回 true

8、boolean startsWith(String prefix)boolean endsWith(String suffix)

判断字符串是否以指定的前缀或后缀开始或结束。

String str = "Hello World";
boolean startsWithHello = str.startsWith("Hello");   // 返回 true
boolean endsWithWorld = str.endsWith("World");       // 返回 true

9、int indexOf(String str)int lastIndexOf(String str)

返回指定子字符串在字符串中第一次出现和最后一次出现的索引位置。

  • indexOf():检查字符串中是否包含指定的子字符串,包含则返回(子字符串从左到右的第一个字符的)在原字符串中首次出现的索引值,否则返回-1
  • lastIndexOf():检查字符串中是否包含指定的子字符串,包含则返回(子字符串从左到右的第一个字符的)在原字符串中最后一次出现的索引值,否则返回-1
String str = "Hello World";
int firstIndex = str.indexOf("o");         // 返回 4
int lastIndex = str.lastIndexOf("o");      // 返回 7

10、String replace(char oldChar, char newChar) 将字符串中所有出现的指定字符替换

replace(char oldChar, char newChar) 是Java中String类的方法之一。该方法用于将字符串中所有出现的指定字符 oldChar 替换为新字符 newChar,并返回替换后的新字符串。

 方法签名为:

public String replace(char oldChar, char newChar)

返回类型为 String,表示替换后的新字符串。

下面是 replace(char oldChar, char newChar) 方法的详细解释:

  1. replace() 方法会扫描字符串中的每个字符,将所有与 oldChar 匹配的字符替换为 newChar。替换后的新字符串将被返回,原始字符串不会被修改。

  2. 方法会从字符串的开头开始,依次检查每个字符。当找到与 oldChar 匹配的字符时,将其替换为 newChar

  3. 字符串中所有与 oldChar 匹配的字符都会被替换,而不仅仅是第一个匹配到的字符。

  4. 如果字符串中没有与 oldChar 匹配的字符,即 oldChar 不存在于字符串中,那么不会进行任何替换,原始字符串将被返回。

  5. 该方法接受两个参数:oldChar 表示需要被替换的字符,newChar 表示用于替换的新字符。

下面是一个使用 replace(char oldChar, char newChar) 方法的示例:

String str = "Hello, World!";
String replacedStr = str.replace('o', '0');

System.out.println(str); // 输出 "Hello, World!"
System.out.println(replacedStr); // 输出 "Hell0, W0rld!"

        运行上述代码,将会输出原始字符串和替换后的新字符串。在示例中,字符 'o' 被替换为字符 '0',字符串中所有的 'o' 都被替换成了 '0'

        需要注意的是,replace(char oldChar, char newChar) 方法只会替换字符,而不会替换子字符串。如果需要替换子字符串,可以使用 replace(CharSequence target, CharSequence replacement) 方法。

11、String replace(CharSequence target, CharSequence replacement)将字符串中所有出现的指定子字符串替换

replace(CharSequence target, CharSequence replacement) 是Java中String类的方法之一。该方法用于将字符串中所有与目标字符序列(target)匹配的子字符串替换为指定的替换字符序列(replacement),并返回替换后的新字符串。

方法签名为:

public String replace(CharSequence target, CharSequence replacement)

返回类型为 String,表示替换后的新字符串。

下面是 replace(CharSequence target, CharSequence replacement) 方法的详细解释:

  1. replace() 方法会扫描字符串中的每个子字符串,将所有与目标字符序列(target)匹配的子字符串替换为指定的替换字符序列(replacement)。

  2. 该方法会从字符串的开头开始,依次检查每个子字符串是否与目标字符序列(target)相同。如果相同,则将匹配的子字符串替换为替换字符序列(replacement)。

  3. 字符串中所有与目标字符序列(target)匹配的子字符串都会被替换,而不仅仅是第一个匹配到的子字符串。

  4. 如果字符串中没有与目标字符序列(target)匹配的子字符串,即目标字符序列不存在于字符串中,那么不会进行任何替换,原始字符串将被返回。

  5. 该方法接受两个参数:target 表示需要被替换的目标字符序列,replacement 表示用于替换的字符序列。

下面是一个使用 replace(CharSequence target, CharSequence replacement) 方法的示例:

String str = "Hello, World!";
        String replacedStr = str.replace("llo", "0");

        System.out.println(str); // 输出 "Hello, World!"
        System.out.println(replacedStr); // 输出 "He0, World!"

        运行上述代码,将会输出原始字符串和替换后的新字符串。在示例中,目标字符序列 "o" 被替换为替换字符序列 "0",字符串中所有的 "o" 都被替换成了 "0"

        需要注意的是,replace(CharSequence target, CharSequence replacement) 方法可以替换普通的字符串,而不仅限于单个字符的替换。如果只需要替换单个字符,可以使用 replace(char oldChar, char newChar) 方法。

12、public String replaceAll(String regex, String replacement)

replaceAll(String regex, String replacement) 是Java中String类的方法之一。该方法用于通过正则表达式匹配的方式,将字符串中所有符合匹配规则的子字符串替换为指定的字符串,并返回替换后的新字符串。

方法签名为:

public String replaceAll(String regex, String replacement)

返回类型为 String,表示替换后的新字符串。

下面是 replaceAll(String regex, String replacement) 方法的详细解释:

  1. replaceAll() 方法使用正则表达式 regex 来匹配字符串中的子字符串。匹配成功的子字符串将被替换为指定的字符串 replacement

  2. 该方法会从字符串的开头开始,依次检查每个子字符串是否与正则表达式 regex 匹配。如果匹配成功,则将匹配的子字符串替换为 replacement

  3. 字符串中所有与正则表达式 regex 匹配的子字符串都会被替换,而不仅仅是第一个匹配到的子字符串。

  4. 如果字符串中没有与正则表达式 regex 匹配的子字符串,即正则表达式没有匹配到任何内容,那么不会进行任何替换,原始字符串将被返回。

  5. 该方法接受两个参数:regex 表示用于匹配子字符串的正则表达式,replacement 表示用于替换匹配到的子字符串的字符串。

下面是一个使用 replaceAll(String regex, String replacement) 方法的示例:

String str = "Hello, World!";
String replacedStr = str.replaceAll("o", "0");

System.out.println(str); // 输出 "Hello, World!"
System.out.println(replacedStr); // 输出 "Hell0, W0rld!"

        运行上述代码,将会输出原始字符串和替换后的新字符串。在示例中,正则表达式 "o" 匹配字符串中所有的 "o",并将其替换为 "0"

        需要注意的是,replaceAll(String regex, String replacement) 方法使用的是正则表达式进行匹配和替换。因此,regex 参数应该是一个有效的正则表达式。如果需要替换普通的字符串,可以使用 replace(CharSequence target, CharSequence replacement) 方法。

13、int compareTo(): 用于比较两个字符串的字典顺序

1、字典顺序:a b c d e f g .......    从前往后依次增大

2、拿着第一个字符串的相同位置字符和后面字符串的相同位置字符进行比较,若能分出大小,则后续就不用比较了

3、字符串之间的比较不能使用“>”或“<”号,需要使用compareTo方法

4、equals方法只能看出字符串相等与否,conpareTo方法不但可以看出是否相等,还可以看出谁大谁小

方法签名:

public int compareTo(String anotherString)

参数说明:

  • anotherString:要与当前字符串进行比较的另一个字符串。

返回值:

  • 如果当前字符串按字典顺序小于 anotherString,则返回一个负整数。
  • 如果当前字符串按字典顺序大于 anotherString,则返回一个正整数。
  • 如果当前字符串与 anotherString 相等,则返回 0。

比较规则:

  • 比较是基于字符的 Unicode 值进行的。
  • 字符串中的每个字符都被按照顺序进行比较。
  • 当找到两个字符串对应位置的字符不相等时,比较结果就已经确定了。
  • 如果一个字符串是另一个字符串的前缀(即一个字符串是另一个字符串的开头部分),则较短的字符串被认为小于较长的字符串。

以下是一些示例使用 compareTo 方法的代码:

package com.javase.String;

public class StringTest05 {
    public static void main(String[] args) {
        //String类中的常用方法

        //字符串之间比较大小不能
        int result1 = "abc".compareTo("abc");
        System.out.println(result1); //0 (等于0) 前后一致  10 - 10 = 0

        int result2 = "abcd".compareTo("abce");
        System.out.println(result2); //-1 (小于0) 前小后打  8 - 9 = -1

        int result3 = "abce".compareTo("abcd");
        System.out.println(result3); //1 (大于0) 前大后小  9 - 8 = 1

        System.out.println("xyz".compareTo("yzx")); //-1
    }
}

14、boolean contains():用于检查字符串是否包含指定的字符序列

方法签名:

public boolean contains(CharSequence sequence)

参数说明:

  • sequence:要检查的字符序列。

返回值:

  • 如果字符串包含指定的字符序列,则返回 true
  • 如果字符串不包含指定的字符序列,则返回 false

以下是一些示例使用 contains 方法的代码:

String str = "Hello World";

boolean contains1 = str.contains("Hello");     // 返回 true,因为字符串包含 "Hello"
boolean contains2 = str.contains("world");     // 返回 false,因为字符串不包含 "world"
boolean contains3 = str.contains("lo");        // 返回 true,因为字符串包含 "lo"

需要注意的是,contains 方法区分大小写。如果要进行不区分大小写的包含检查,可以先将字符串转换为小写或大写,然后再使用 contains 方法。例如:

String str = "Hello World";

boolean containsIgnoreCase = str.toLowerCase().contains("hello");  // 返回 true,不区分大小写地检查是否包含 "hello"

此外,contains 方法还可以用于判断字符串中是否包含某个字符。例如:

String str = "Hello World";

boolean containsChar = str.contains("o");   // 返回 true,因为字符串中包含字符 'o'

        总结:contains 方法用于检查字符串是否包含指定的字符序列,可以用于判断子字符串、字符等是否存在于原字符串中。

15、byte[ ] getBytes() 将字符串转换为字节数组,并返回表示该字符串的字节数组

getBytes() 是Java中String类的方法之一。该方法用于将字符串转换为字节数组,并返回表示该字符串的字节数组。

方法签名为:

public byte[] getBytes()

返回类型为 byte[],表示字节数组。

        该方法没有参数,它会将字符串中的字符按照默认的字符编码方式(通常是UTF-8)转换为字节数组。

下面是 getBytes() 方法的详细解释:

  1. getBytes() 方法将字符串转换为字节数组的过程中,使用了默认的字符编码方式。字符编码是一种将字符转换为字节的规则,不同的编码方式会使用不同的字节序列来表示字符。在Java中,默认的字符编码方式通常是UTF-8。

  2. 该方法返回的是字符串的字节数组,每个字节表示一个字符的编码。如果字符串中包含非ASCII字符或Unicode字符,那么字节数组的长度可能会大于字符串的长度。

  3. 返回的字节数组的长度等于字符串的长度,可以通过 byteArray.length 获取。

  4. 字符串中的每个字符会被转换为相应的字节,存储在字节数组中。对于ASCII字符,每个字符占用一个字节;对于非ASCII字符,可能需要多个字节来表示。

  5. 如果字符串中包含无法使用默认编码方式表示的字符,那么会使用特殊的替代字符(例如 ?)来表示这些字符的字节序列。

下面是一个使用 getBytes() 方法的示例:

String s = "abc";
byte[] bytes = s.getBytes(); // 将字符串转换为字节数组
System.out.println("字节数组长度为:" + bytes.length); //字节数组长度为:3
System.out.println(Arrays.toString(bytes)); //[97, 98, 99]
for (int i = 0; i < bytes.length; i++) {
    System.out.println(bytes[i]);
            /**
             * 97
             * 98
             * 99
             */
}

        需要注意的是,getBytes() 方法在转换过程中使用了默认的字符编码方式,如果需要使用其他编码方式,可以使用 getBytes(String charsetName) 方法,并指定所需的字符编码方式。例如:str.getBytes("UTF-16") 将字符串转换为UTF-16编码的字节数组。

16、boolean isEmpty() 用于检查字符串是否为空

即判断字符串的长度是否为0,如果字符串为空,则返回true;否则返回false。

带有空格的“   ”不算空,返回false

源码:

public boolean isEmpty() {
    return value.length == 0;
}

        需要注意的是,isEmpty() 方法在判断字符串是否为空时是通过检查字符串的长度来实现的。如果字符串中只包含空格字符,那么 isEmpty() 方法会返回false,因为字符串的长度不为0。如果需要判断字符串中是否只包含空格字符,可以先使用 trim() 方法去除字符串首尾的空格,然后再调用 isEmpty() 方法进行判断。

String s1 = "";
        String s2 = " ";
        System.out.println(s1.isEmpty()); //true
        System.out.println(s2.isEmpty()); //false
        System.out.println(s1.length()); //0
        System.out.println(s2.length()); //1

17、 String[ ] split(String regex) 将字符串拆分为多个子字符串,并将这些子字符串存储在一个字符串数组中返回

split(String regex) 是Java中String类的方法之一。该方法通过使用正则表达式 regex,将字符串拆分为多个子字符串,并将这些子字符串存储在一个字符串数组中返回。

方法签名为:

public String[] split(String regex)

返回类型为 String[],表示包含拆分后的子字符串的字符串数组。

下面是 split(String regex) 方法的详细解释:

  1. split() 方法使用正则表达式 regex 对字符串进行拆分。该正则表达式用于确定拆分的位置。

  2. 当字符串中的某个子字符串与正则表达式 regex 匹配时,该子字符串即为拆分的位置。

  3. 字符串将从匹配到的位置处被拆分,并将拆分后的子字符串存储在一个字符串数组中。

  4. 拆分后的子字符串不包含正则表达式匹配到的位置处的字符。

  5. 如果字符串中没有与正则表达式 regex 匹配的子字符串,那么将返回包含整个原始字符串的字符串数组,其中只有一个元素。

下面是一个使用 split(String regex) 方法的示例:

String str = "Hello,World,Java";
String[] parts = str.split(",");

System.out.println(str); // 输出 "Hello,World,Java"
System.out.println(Arrays.toString(parts)); // 输出 "[Hello, World, Java]"

        运行上述代码,将会输出原始字符串和拆分后的字符串数组。在示例中,通过使用正则表达式 "," 对字符串进行拆分,将字符串分割为三个子字符串,并存储在字符串数组 parts 中。

        需要注意的是,split(String regex) 方法使用的是正则表达式进行字符串的拆分。因此,regex 参数应该是一个有效的正则表达式。如果只需要根据普通的字符串进行拆分,可以使用 split(String separator) 方法。

18、 char[ ] toCharArray() 将字符串转换为字符数组

toCharArray() 是Java中 String 类的方法之一。它用于将字符串转换为字符数组。

方法签名为:

public char[] toCharArray()

返回类型为 char[],表示包含字符串中字符的字符数组。

下面是 toCharArray() 方法的详细解释:

  1. toCharArray() 方法将字符串中的每个字符提取出来,并将它们存储在一个字符数组中。

  2. 返回的字符数组的长度与字符串的长度相同。

  3. 字符数组中的每个元素都对应着字符串中相应位置的字符。

  4. 返回的字符数组是一个新的数组,与原始字符串没有任何关联。

下面是一个使用 toCharArray() 方法的示例:

String str = "Hello";
char[] charArray = str.toCharArray();

System.out.println(str); // 输出 "Hello"
System.out.println(Arrays.toString(charArray)); // 输出 "[H, e, l, l, o]"

        在上述示例中,将字符串 "Hello" 使用 toCharArray() 方法转换为字符数组 charArray。然后,分别打印原始字符串和字符数组的内容。

        需要注意的是,字符数组中的每个元素都是一个单独的字符,并不像字符串中的字符那样以字符串的形式表示。因此,当打印字符数组时,使用 Arrays.toString() 方法或循环来逐个输出字符。

五、将其他数据类型转换为字符串类型  valueOf方法(String类唯一静态方法)

valueOf() 是Java中 String 类的一个静态方法。它用于将其他数据类型的值转换为字符串表示。底层主要调用了toString方法

valueOf() 方法有多个重载形式,用于处理不同的数据类型,如整数、浮点数、字符、布尔值等。

下面是 valueOf() 方法的一般形式:

public static String valueOf(Type value)

其中 Type 是要转换的数据类型,例如 intdoublechar 等。

返回类型为 String,表示转换后的字符串。

valueOf() 方法的详细解释如下:

  1. valueOf() 方法接受一个值作为参数,并将该值转换为字符串。

  2. 转换后的字符串与原始值的表示形式相同。

  3. 对于大多数数据类型,转换后的字符串将包含与该值相等的字符序列。

  4. 对于基本数据类型的包装类,如 IntegerDoubleCharacter 等,valueOf() 方法与对应包装类的 toString() 方法效果相同。

下面是几个示例使用 valueOf() 方法的例子:

int num = 42;
String str1 = String.valueOf(num); // 使用 valueOf() 将整数转换为字符串

double value = 3.14;
String str2 = String.valueOf(value); // 使用 valueOf() 将浮点数转换为字符串

char ch = 'A';
String str3 = String.valueOf(ch); // 使用 valueOf() 将字符转换为字符串

boolean bool = true;
String str4 = String.valueOf(bool); // 使用 valueOf() 将布尔值转换为字符串

在上述示例中,分别使用 valueOf() 方法将整数、浮点数、字符和布尔值转换为字符串。

        需要注意的是,valueOf() 方法适用于大多数数据类型的转换,但对于自定义的类和对象,需要在类中重写 toString() 方法来定义对象的字符串表示形式。

1、转换自定义的类和对象

源码如下:会自动调用该类的toString方法,因此需要重写toString方法

public static String valueOf(Object obj) {
        return (obj == null) ? "null" : obj.toString();
    }

2、System.out.println方法原理

1、本质上System.out.println()这个方法在输出任何数据的时候都会调用String.valueOf()方法,都会先将数据转换为字符串,然后输出。

2、凡是能够输出到控制台上的都是字符串

六、格式化字符串

1.、使用 String.format()

String.format() 方法允许您使用格式化字符串来创建格式化的字符串。与 Python 中的 % 格式化类似,您可以在格式化字符串中使用格式控制符。

示例:

String name = "Alice";
int age = 30;
double height = 1.75;
String message = String.format("My name is %s, I am %d years old, and I am %.2f meters tall.", name, age, height);
System.out.println(message);
// 输出: My name is Alice, I am 30 years old, and I am 1.75 meters tall.

2、 使用 System.out.printf()

System.out.printf() 方法允许您将格式化的字符串直接输出到控制台。它的使用方式类似于 C 语言的 printf() 函数。

示例:

String name = "Bob";
int score = 85;
System.out.printf("Hello, my name is %s and my score is %d.\n", name, score);
// 输出: Hello, my name is Bob and my score is 85.

        在这两种方法中,您可以使用不同的格式化控制符来插入变量的值,如 %s 表示字符串、%d 表示整数、%.2f 表示浮点数等等。

这些方法可以在 Java 中实现类似于 Python 中的字符串格式化输出,使代码更具可读性和维护性。

第十章、StringBuffer和StringBuilder类

以StringBuffer类为例

一、概述

StringBuffer 是Java中的一个可变字符串类用于处理可变的字符串操作它是线程安全的,支持在已有字符串的基础上进行插入、删除、替换等操作,而不会创建新的字符串对象。优化内存的使用

1、StringBuffer类的意义

由于在java中的字符串String是不可变的,因此每一次的字符串拼接都会产生新的字符串,这样会占用大量的方法去内存。造成内存空间的浪费。

例如:

  • String  s  =  "abc";
  • s += "hello";
  • 以上两行代码,就会导致在方法区字符串常量池中创建了3个String对象
  • “abc”,“hello”,“abchello”

如下代码:

String s = "";
for (int i = 0; i < 100; i++) {
    s += i;
    System.out.println(s);
}

频繁的使用字符串拼接就会占用大量的内存空间,会给java方法区字符串常量池带来很大压力,造成资源浪费

2、StringBuffer类的特点

  1. 可变性StringBuffer 对象的内容可以进行修改,而不会创建新的对象。这使得它适用于频繁修改字符串内容的场景。

  2. 线程安全性StringBuffer 类的方法都是同步的,可以在多线程环境下使用,保证线程安全。

  3. 可变字符串操作StringBuffer 提供了许多方法来进行字符串的插入、删除、替换等操作,例如 append()insert()delete()replace() 等。

  4. 字符串连接:通过 append() 方法,可以方便地将其他数据类型转换为字符串并连接到当前字符串的末尾。

  5. 容量管理StringBuffer 会自动调整内部的字符缓冲区的大小以适应字符串的长度,不需要手动管理容量。

  6. 性能优化:由于 StringBuffer 是可变的,避免了频繁创建新的字符串对象,因此在大量字符串拼接的情况下,性能比直接使用字符串操作更高效。

二、StringBuffer类的原理解析

1、StringBuffer底层实际上一个byte[ ]数组

往StringBuffer中存放的字符串,实际上是放到了byte数组中了。

 2、StringBuffer实例的无参默认初始化容量为16

public StringBuffer() {
        super(16);
    }

3、append方法会自动进行扩容

4、StringBuffer底层扩容原理分析

5、优化StringBuffer容量

在创建StringBuffer的时候尽可能得给定一个初始化的容量

最好减少底层数组的扩容次数,预估以下,给一个大一点的初始化容量

关键点:给一个合适的初始化容量

三、字符串缓冲区对象(多线程 StringBuffer实例对象)

        在Java中,字符串缓冲区对象是由 StringBufferStringBuilder 类表示的可变字符串序列。它们允许进行高效的字符串操作,例如拼接、插入、删除和修改,而不会创建新的字符串对象。

        字符串缓冲区对象的主要特点是可变性。与 String 类型的不可变字符串不同,字符串缓冲区可以动态地修改其内容,而无需创建新的对象。这对于需要频繁修改字符串的场景非常有用,例如在循环中拼接字符串或构建长字符串。

        在字符串缓冲区对象中,操作不会生成新的中间字符串,而是直接在原始缓冲区上进行修改。这样可以避免频繁地创建和销毁字符串对象,提高性能和内存效率。

StringBuffer 类是线程安全的,适用于多线程环境,它的方法使用了同步机制,保证了线程安全性,但也带来了一定的性能开销。在单线程环境下,可以使用性能更好的 StringBuilder 类,它提供了与 StringBuffer 类相同的功能,但没有同步开销。

StringBuffer sb = new StringBuffer(100);
        sb.append("Hello");
        sb.append(" ");
        sb.append("World!");

        System.out.println(sb.toString()); // 输出: Hello World!

        在上述示例中,通过 append() 方法将字符串逐步追加到字符串缓冲区 sb 中,然后使用 toString() 方法将缓冲区内容转换为 String 类型,并打印输出结果。

        使用字符串缓冲区对象可以避免频繁地创建新的字符串对象,提高性能和内存效率,特别适用于需要频繁修改字符串内容的场景。

四、StringBuffer类和StringBuilder类的区别与联系

StringBuffer 类和 StringBuilder 类都是 Java 中用于操作可变字符串的类,它们有相似的功能,但在线程安全性、性能和可变性方面存在一些区别。

1、区别:

StringBuffer类中的方法都有 synchronized 修饰,表示同步,线程安全,使用于多线程

StringBuilder类中的方法没有 synchronized  修饰,线程不安全,适用于单线程

  1. 线程安全性:StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的。StringBuffer 的方法使用了同步机制,可以在多线程环境下安全使用,但会带来一定的性能开销。而 StringBuilder 没有同步机制,适用于单线程环境,性能更好。

  2. 性能:由于 StringBuilder 不需要进行同步,所以在单线程环境下比 StringBuffer 性能更好。因此,如果在单线程环境下使用可变字符串,应优先选择 StringBuilder

  3. 可变性:StringBufferStringBuilder 都可以对字符串进行修改操作,包括追加、插入、删除和替换等。它们提供了类似的方法,如 append()insert()delete()replace() 等。

2、联系:

  1. 继承关系:StringBuilder 类是 StringBuffer 类的一个子类。它们都继承自 AbstractStringBuilder 类,并且提供了类似的方法。

  2. 方法和功能:StringBufferStringBuilder 具有相似的方法和功能,可以进行字符串的拼接、插入、删除、替换等操作。它们都提供了类似的方法命名和用法,因此在使用上没有太大的差异。

3、总结:

  • 如果在多线程环境下需要对字符串进行修改操作,应选择使用线程安全的 StringBuffer
  • 如果在单线程环境下需要对字符串进行修改操作,应选择使用性能更好的 StringBuilder
  • StringBufferStringBuilder 提供了类似的方法和功能,用法基本相同,可以根据需求选择适合的类

五、创建字符串缓冲区对象

1、StringBuffer():创建一个空的字符串缓冲区对象

StringBuffer sb = new StringBuffer();

示例中创建了一个空的字符串缓冲区对象 sb,可以在后续的操作中向该缓冲区添加字符串内容。 

2、StringBuffer(int capacity):创建一个指定容量的字符串缓冲区对象

StringBuffer sb = new StringBuffer(20);

        示例中创建了一个容量为 20 的字符串缓冲区对象 sb,可以根据需要指定缓冲区的初始容量,以避免在追加大量字符串时频繁调整容量。

3、StringBuffer(String str):创建一个包含指定字符串内容的字符串缓冲区对象

String str = "Hello";
StringBuffer sb = new StringBuffer(str);

        示例中创建了一个包含字符串 "Hello" 内容的字符串缓冲区对象 sb,可以直接在构造方法中传入一个字符串,将该字符串内容作为初始内容添加到缓冲区。

        这些构造方法可以根据需要选择,根据是否需要初始内容或指定容量来创建相应的字符串缓冲区对象。创建后,可以使用字符串缓冲区对象的方法来操作和修改字符串内容。请注意,StringBuilder 类也具有相同的构造方法,可以根据需求选择使用 StringBufferStringBuilder

六、字符串缓冲区对象常用方法

        字符串缓冲区对象(StringBufferStringBuilder)提供了一系列常用的方法,用于操作可变字符串。以下是这些常用方法的详细解释和示例:

1、append():将指定的字符串追加到缓冲区的末尾

StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(" World!");

System.out.println(sb.toString()); // 输出: Hello World!

2、insert():在指定位置插入字符串

在指定索引位置处的左侧插入

StringBuilder sb = new StringBuilder("Hello World!");
        sb.insert(5, " Awesome");

        System.out.println(sb.toString()); // 输出: Hello Awesome World!

3、delete():删除指定范围内的字符

StringBuilder sb = new StringBuilder("Hello World!");
sb.delete(6, 11);

System.out.println(sb.toString()); // 输出: Hello!

4、replace():用指定的字符串替换指定范围内的字符

StringBuilder sb = new StringBuilder("Hello World!");
sb.replace(6, 11, "Everyone");

System.out.println(sb.toString()); // 输出: Hello Everyone!

5、reverse():反转缓冲区中的字符顺序

StringBuilder sb = new StringBuilder("Hello World!");
sb.reverse();

System.out.println(sb.toString()); // 输出: !dlroW olleH

6、length():返回缓冲区中的字符数

StringBuilder sb = new StringBuilder("Hello World!");
int length = sb.length();

System.out.println(length); // 输出: 11

7、charAt():返回指定位置的字符

StringBuilder sb = new StringBuilder("Hello World!");
char ch = sb.charAt(6);

System.out.println(ch); // 输出: W

8、toString():将缓冲区内容转换为字符串

StringBuilder sb = new StringBuilder("Hello World!");
String str = sb.toString();

System.out.println(str); // 输出: Hello World!

        这些方法可以帮助您对字符串缓冲区进行各种操作,包括拼接、插入、删除、替换、反转等。通过使用这些方法,您可以在不创建新的字符串对象的情况下修改可变字符串,并且具有较好的性能和效率。

七、面试题

1、String为什么是不可变的?

我看过源代码,String类中又一个byte[ ]数组,这个byte[ ]数组采用了final修饰,因为数据一旦创建其长度不可变。并且别final修饰的引用一旦指向某个对象之后,不能再指向其他对象,所以String是不可变的。

2、StringBuffer/StringBuilder为什么是可变的?

我看过源代码,StringBuffer/StringBuilder内部实际上是一个byte[ ]数组,这个byte[ ]数组没有被funal修饰,StringBuffer/StringBuilder的初始化容量我记得应该是16,当存满之后会进行扩容,底层调用了数组拷贝的方法System.arraycopy()...是这样扩容的,所以StringBuffer/StringBuilder适合于使用字符串的频繁拼接操作中。

第十一章、包装类

一、概述

        在Java中,基本数据类型(如intdoubleboolean等)是不具备面向对象的特性的,无法直接参与面向对象的操作。为了解决这个问题,Java提供了对应的包装类(Wrapper Class),将基本数据类型封装为对象,使其具备面向对象的特性。

        这些包装类提供了丰富的方法和功能,使得基本数据类型可以像对象一样进行操作。此外,包装类还提供了静态方法用于基本数据类型和字符串之间的转换,例如 Integer.parseInt()Double.valueOf() 等。包装类还可以用于集合框架中,使得基本数据类型可以作为集合的元素。

        需要注意的是,包装类和基本数据类型之间可以通过自动装箱(Autoboxing)和自动拆箱(Unboxing)进行转换,方便了基本数据类型和包装类之间的转换操作。

        总而言之,包装类提供了对基本数据类型的封装和扩展,使其具备了面向对象的特性和丰富的功能。

1、包装类存在的意义

1、java中8中基本数据类型又对应准备了8中包装类型,8中包装类型属于引用数据类型,父类是Object

2、因为8种基本数据类型不够用,所以SUN公司又提供了8中包装类型

MyInt.java

package com.javase.包装类;

//这种包装类目前是我们自己写的,实际开发中我们不需要自己写。
//8中基本数据类型对应的8种包装类,SUN公司已经写好了,直接用
public class MyInt {
    int value;
    public MyInt(){
    }
    public MyInt(int value) {
        this.value = value;
    }
    public String toString(){
        return String.valueOf(this.value);
    }
}

IntegerTest01.java

package com.javase.包装类;

public class IntegerTest01 {

    public static void main(String[] args) {
        //例如这种情况:调用doSome方法的时候,需要传一个数字进去
        //但是数字属于基本数据类型,而doSome方法的参数类型是Object
        //可见doSome方法无法接收基本数据类型的数组,那怎么办尼?可以传入一个数字对象的包装类进去

        //把100这个数字经过构造方法包装成对象
        MyInt myInt = new MyInt(100);
        //doSome方法虽然不能直接传100,但是可以传入一个包装类的对象
        doSome(myInt);

    }

    public static void doSome(Object obj){
        System.out.println(obj);
    }
}

2、8种基本数据类型对应的包装类的类型名

包装类名称对照表
基本数据类型包装类型
bytejava.lang.Byte(父类为Number)
short        java.lang.Short(父类为Number)
intjava.lang.Integer(父类为Number)
longjava.lang.Long(父类为Number)
floatjava.lang.Float(父类为Number)
doublejava.lang.Double(父类为Number)
booleanjava.lang.Boolean(父类为Object)
charjava.lang.Character(父类为Object)
  1. Boolean:封装了基本数据类型 boolean 的包装类。提供了操作和表示布尔值的方法,如比较、转换等。

  2. Character:封装了基本数据类型 char 的包装类。提供了对字符的操作和表示,包括大小写转换、字符类型判断等。

  3. Byte:封装了基本数据类型 byte 的包装类。提供了对字节的操作和表示,如比较、转换为其他数据类型等。

  4. Short:封装了基本数据类型 short 的包装类。提供了对短整型数据的操作和表示,如比较、转换为其他数据类型等。

  5. Integer:封装了基本数据类型 int 的包装类。提供了对整型数据的操作和表示,如比较、转换为其他数据类型、数值运算等。

  6. Long:封装了基本数据类型 long 的包装类。提供了对长整型数据的操作和表示,如比较、转换为其他数据类型、数值运算等。

  7. Float:封装了基本数据类型 float 的包装类。提供了对单精度浮点数的操作和表示,如比较、转换为其他数据类型、数值运算等。

  8. Double:封装了基本数据类型 double 的包装类。提供了对双精度浮点数的操作和表示,如比较、转换为其他数据类型、数值运算等。

二、Number类(主要是拆箱的实例方法)

        在Java中,Number 是一个抽象类,是所有数值型包装类(如 ByteShortIntegerLongFloatDouble)的父类。Number 类提供了一些共同的方法和属性,用于操作和处理数值类型的数据。

Number 类的存在主要是为了提供一个通用的父类,方便对各种数值类型进行统一的处理和转换。在实际使用中,通常会使用具体的数值类型的子类,如 IntegerDouble 等,而较少直接使用 Number 类。

        需要注意的是,Number 类是一个抽象类,无法直接实例化。它的主要作用是为其子类提供统一的接口和行为。子类可以继承 Number 类,并实现其抽象方法,同时可以添加自己的特定方法和属性,以满足具体的数值类型的需求。

        总结而言,Number 类是所有数值型包装类的父类,提供了一些通用的方法和属性,用于对数值类型进行操作和转换。它起到了统一和规范的作用,方便了数值类型的处理和使用。

1、intValue(): 返回该数值对象的整数值

Integer num = new Integer(10);
int intValue = num.intValue(); // 返回整数值 10

2、longValue(): 返回该数值对象的长整数值

Double num = new Double(3.14);
long longValue = num.longValue(); // 返回长整数值 3L

3、floatValue(): 返回该数值对象的单精度浮点数值

Integer num = new Integer(100);
float floatValue = num.floatValue(); // 返回单精度浮点数值 100.0F

4、doubleValue(): 返回该数值对象的双精度浮点数值

Long num = new Long(123456789);
double doubleValue = num.doubleValue(); // 返回双精度浮点数值 1.23456789E8

5、byteValue(): 返回该数值对象的字节值

Short num = new Short(50);
byte byteValue = num.byteValue(); // 返回字节值 50

6、shortValue(): 返回该数值对象的短整数值

Float num = new Float(3.14);
short shortValue = num.shortValue(); // 返回短整数值 3

二、Integer包装类

Integer 是 Java 中提供的用于表示整数类型的包装类。它是 java.lang 包下的一个类,继承自 Number 类,实现了 Comparable<Integer> 接口。Integer 类提供了一些常用的方法和属性,用于操作和转换整数数据

1、构造方法  手动装箱 boxing(已弃用)

实现了  基本数据类型  向  引用数据类型  转换(又叫装箱 boxing)

1.1、Integer(int value): 根据指定的整数值创建一个 Integer 对象

//第一种构造方法
        Integer int1 = new Integer(123);
        System.out.println(int1); //123

1.2、Integer(String s): 根据指定的字符串创建一个 Integer 对象

//第二种构造方法
        Integer int2 = new Integer("456");
        System.out.println(int2); //456

1.3、NumberFormatException(数字格式化异常)

//编译的时候没问题,符合java语法,运行时报错
        //java.lang.NumberFormatException
        Integer a = new Integer("中文");

2、常量

2.1、最小值

public static final int   MIN_VALUE = 0x80000000; 
//最小值
        System.out.println(Integer.MIN_VALUE); //-2147483648

2.2、最大值

public static final int   MAX_VALUE = 0x7fffffff;
//最大值
        System.out.println(Integer.MAX_VALUE); //2147483647

3、实例方法(继承父类Number中的6种 手动拆箱 unboxing

实现了  引用数据类型  向  基本数据类型  转换(又叫拆箱 unboxing)

3.1、compareTo(Integer anotherInteger)

        方法作用:将该整数对象与另一个 Integer 对象进行比较,返回一个整数值,表示它们的相对顺序。

Integer num1 = 10;
Integer num2 = 5;
int result = num1.compareTo(num2); // result = 1,num1 大于 num2

4、静态方法

4.1、parseInt(String s):将字符串解析为整数值,并返回一个 int 类型的结果

String str = "123";
int value = Integer.parseInt(str); // value = 123

//网页上文本框中输入的100实际上是“100”字符串,后台数据库中要求存储100数字,此时就需要使用Integer.parseInt方法,将字符串100转换成数字100
        int retValue = Integer.parseInt("123");
        System.out.println(retValue + 100); //223

        double retValue2 = Double.parseDouble("3.14");
        System.out.println(retValue2 + 1); //4.140000000000001(精度问题)

        float retValue3 = Float.parseFloat("1.0");
        System.out.println(retValue3 + 1); //2.0

4.2、valueOf(int i)手动装箱 boxing: 将指定的整数值转换为 Integer 对象

int value = 10;
Integer num = Integer.valueOf(value); // num = 10

4.3、valueOf(String s)手动装箱 boxing将指定的字符串解析为 Integer 对象

String str = "123";
Integer num = Integer.valueOf(str); // num = 123

4.4、toHexString(int i) : 将十进制数字转换成十六进制字符串

String hexString = Integer.toHexString(17);
        System.out.println(hexString); //"11"
package com.javase.包装类;

public class IntegerTest02 {
    public static void main(String[] args) {
        // 123这个基本数据类型,进行构造方法的包装达到了:基本数据类型向引用数据类型的转换
        //基本数据类型--(转换为)--引用数据类型

        //第一种构造方法
        Integer int1 = new Integer(1231);
        System.out.println(int1); //123

        //第二种构造方法
        Integer int2 = new Integer("456");
        System.out.println(int2); //456

        //最小值
        System.out.println(Integer.MIN_VALUE); //-2147483648

        //最大值
        System.out.println(Integer.MAX_VALUE); //2147483647

        //实例方法
        //将引用数据类型--(转换为)--》基本数据类型
        byte byteValue = int1.byteValue();
        System.out.println(byteValue); //-49  强制类型转换,精度损失

        short shortValue = int1.shortValue();
        System.out.println(shortValue); //1231

        int intValue = int1.intValue();
        System.out.println(intValue); //1231

        long longValue = int1.longValue();
        System.out.println(longValue); //1231L

        float floatValue = int1.floatValue();
        System.out.println(floatValue); //1231.0F

        double doubleValue = int1.doubleValue();
        System.out.println(doubleValue); //1231.0

        Integer num1 = 10;
        Integer num2 = 5;
        int result = num1.compareTo(num2); // result = 1,num1 大于 num2
        System.out.println(result);

//        IntegerTest02 aa = 10;

        //静态方法
        int value = 10;
        Integer num = Integer.valueOf(value); // num = 10
        System.out.println(num);
        
        System.out.println(IntegerTest02.class);
    }
}

三、自动装箱和自动拆箱

        在 Java 中,自动装箱(Autoboxing)和自动拆箱(Unboxing)是 Java 编译器提供的两个特性,用于简化基本数据类型和对应包装类之间的转换

1、自动装箱(Autoboxing)

1、自动装箱是指将基本数据类型自动转换为对应的包装类对象。

2、当需要将基本数据类型赋值给对应的包装类对象时,编译器会自动进行装箱操作。

3、引用中保存的还是对象的内存地址

Integer obj = 100;  等同于  Integer obj = new Integer(1000);

int num = 10;
Integer obj = num; // 自动装箱

        在这个例子中,基本数据类型 int 的值 10 被自动装箱为 Integer 类型的对象。编译器在后台自动执行了 Integer.valueOf(num) 的操作。

2、自动拆箱(Unboxing)

自动拆箱是指将包装类对象自动转换为对应的基本数据类型。

当需要将包装类对象赋值给基本数据类型时,编译器会自动进行拆箱操作。

Integer obj = 20;
int num = obj; // 自动拆箱

         在这个例子中,Integer 类型的对象 obj 被自动拆箱为基本数据类型 int。编译器在后台自动执行了 obj.intValue() 的操作。

        自动装箱和自动拆箱能够使基本数据类型和包装类之间的转换更加方便和简洁,减少了开发者手动进行装箱和拆箱的繁琐工作。它们使得在需要使用包装类对象的地方可以直接使用基本数据类型,反之亦然,编译器会在必要时进行自动的类型转换。这样可以提高代码的可读性和简洁性。

3、触发自动拆箱机制的条件

3.1、算数运算符(+ - * / %)

//z引用保存的是对象的内存地址
        Integer z = 1000;  //等同关于 Integer z = new Integer(1000);
        //分析为什么这个没有报错尼?
        // + 两边要求是基本数据类型的数字,z是包装类,不属于基本数据类型,这里会进行自动拆箱,将z转换成基本数据类型
        System.out.println(z + 1); //1001

        System.out.println(z - 1); //999

        System.out.println(z * 2); //2000

        System.out.println(z / 4); //250

        System.out.println(z % 300); //100

3.2、关系运算符(>  >=  <  <=)不包括 == 和 !=

Integer a = 1000; //Integer
        Integer b = 1000;
        System.out.println(a == b); //false

        System.out.println(a != b); //true

        System.out.println(a > b); //false
        System.out.println(a >= b); //true
        System.out.println(a < b); //false
        System.out.println(a <= b); //true
package com.javase.包装类;

/**
 * 在java5之后,引入了一种新特性,自动装箱和自动拆箱
 * 自动装箱:基本数据类型自动转换成包装类
 * 自动拆箱:包装类自动转换成基本数据类型
 *
 * 有了自动拆箱之后,Number类中的方法就用不着了
 */
public class IntegerTest05 {
    public static void main(String[] args) {
        //900是基本数据类型
        //x是包装类型
        //基本数据类型 --(自动转换)--》包装类型:自动装箱
        Integer x = 900;
        System.out.println(x.compareTo(90));

        //x是包装类型
        //y是基本数据类型
        //包装类型 --(自动转换)--》基本数据类型:自动拆箱
        int y = x;
        System.out.println(y);

        //z引用保存的是对象的内存地址
        Integer z = 1000;  //等同关于 Integer z = new Integer(1000);
        //分析为什么这个没有报错尼?
        // + 两边要求是基本数据类型的数字,z是包装类,不属于基本数据类型,这里会进行自动拆箱,将z转换成基本数据类型
        System.out.println(z + 1); //1001

        System.out.println(z - 1); //999

        System.out.println(z * 2); //2000

        System.out.println(z / 4); //250

        System.out.println(z % 300); //100

        Integer a = 1000; //Integer a = new Integer(1000); a是个引用,保存内存地址指向对象
        Integer b = 1000; //Integer b = new Integer(1000); b是个引用,保存内存地址指向对象
        //==比较的对象的内存地址,a和b两个引用中保存的对象的内存地址不同
        System.out.println(a == b); //false

        System.out.println(a != b); //true

        System.out.println(a > b); //false
        System.out.println(a >= b); //true
        System.out.println(a < b); //false
        System.out.println(a <= b); //true
    }
}

4、数字缓存(Integer Cache)方法区整数型常量池

        在 Java 中,存在一个类似于字符串常量池的机制,称为数字缓存(Integer Cache)。

        在 Java 中,范围为 -128127 的整数值会被缓存起来,作为 Integer 类型的对象。这意味着,如果使用自动装箱将一个处于该范围内的整数赋给一个 Integer 对象,那么这个对象实际上会引用缓存中已经存在的对象,而不是创建一个新的对象。

        这个数字缓存的目的是为了提高性能和节省内存。由于整数在程序中经常被使用,缓存机制可以避免频繁地创建和销毁 Integer 对象,从而提高代码的执行效率。

以下是一个示例来说明数字缓存的工作方式:

Integer a = 100;
Integer b = 100;

System.out.println(a == b); // 输出 true,因为 a 和 b 引用了同一个缓存的 Integer 对象

Integer c = 200;
Integer d = 200;

System.out.println(c == d); // 输出 false,因为 c 和 d 超出了缓存范围,会创建不同的 Integer 对象

        在上述示例中,当赋值为 100 时,ab 引用了相同的缓存 Integer 对象,因此 a == b 返回 true。然而,当赋值为 200 时,cd 超出了缓存范围,会创建两个不同的 Integer 对象,因此 c == d 返回 false

        需要注意的是,数字缓存仅适用于 Integer 类型,对于其他包装类(如 LongShortByte 等)则没有类似的缓存机制。另外,如果显式地使用构造函数创建 Integer 对象,将不会使用数字缓存,而是创建一个新的对象。

5、内存图分析

package com.javase.包装类;

public class IntegerTest06 {
    public static void main(String[] args) {
        Integer a = 128;
        Integer b = 128;
        System.out.println(a == b); //false

        /**
         * java中为了提高程序的执行效率,将(-128到127)之间的所有的包装对象提前创建好。
         * 放到了一个方法区“整数型常量池”中,目的是只要用这个区间的数据不需要再new对象了,直接从整数型常量池中取
         *
         * 原理:x引用中保存的内存地址和y引用中保存的内存地址是一样的
         */
        Integer x = 127;
        Integer y = 127;
        // == 永远判断的是两个对象的内存地址是否相同
        System.out.println(x == y); //true
    }
}

四、String,Integer,int互相转换

package com.javase.包装类;

/**
 * String转int  Integer.parseInt(str)
 * int转String  String.valueOF(int)  数字 + “”
 *
 * int转Integer  Integer.valueOf(int)  自动装箱
 * Integer转int  Integer实例.intValue()  自动拆箱
 *
 * Integer转String  String.valueOf(Integer对象)
 * String转Integer  Integer.valueOf(str)
 */
public class IntegerTest08 {
    public static void main(String[] args) {
        //String转int
        String s1 = "100";
        int i1 = Integer.parseInt(s1);
        System.out.println(i1 + 1); //101

        //int转String
        String s2 = String.valueOf(1);
        System.out.println(s2 + 1); //11

        String s3 = i1 + "";
        System.out.println(s3 + 1); //1001

        //int转Integer
        //自动装箱
        Integer x = 100;

        Integer x1 = Integer.valueOf(1000);

        //Integer转int
        //自动拆箱
        int y = x;

        //Integer转String
        Integer a = 200;
        String a1 = String.valueOf(a);
        System.out.println(a1 + 1); //2001

        //String转Integer
        //Integer b1 = Integer.valueOf("中文"); //java.lang.NumberFormatException
        Integer b = Integer.valueOf("123");
        System.out.println(b + 1); //124
    }
}

第十二章、SimpleDateFormat类

一、简介

SimpleDateFormat类是Java中用于格式化和解析日期和时间的类。它位于java.text包中。SimpleDateFormat允许你指定自定义的日期和时间格式模式,以便将日期对象格式化为字符串,或者将字符串解析为日期对象

二、构造方法

SimpleDateFormat类提供了多个构造函数,可以根据不同的需求创建实例。其中最常用的构造函数是SimpleDateFormat(String pattern),它接受一个字符串参数pattern,用于指定日期和时间的格式模式。

三、格式模式

日期和时间的格式模式由一系列的字母组成,这些字母代表不同的日期和时间元素。常用的格式模式字母包括:

  • y:年份
  • M:月份
  • d:日期
  • H:24小时制的小时数
  • h:12小时制的小时数
  • m:分钟
  • s:秒
  • S:毫秒

你可以根据自己的需求组合这些字母来创建自定义的日期和时间格式。

四、格式化日期(实例方法 format)

        使用SimpleDateFormatformat(Date date)方法,可以将Date对象格式化为字符串,按照指定的日期和时间格式进行格式化。例如:

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Date date = new Date();
        String formattedDate = sdf.format(date);
        System.out.println(formattedDate); //2023-05-30

这将输出当前日期的字符串表示,格式为"yyyy-MM-dd",例如:"2023-05-30"。 

五、解析日期(实例方法 parse)

        使用SimpleDateFormatparse(String text)方法,可以将字符串解析为Date对象,按照指定的日期和时间格式进行解析。例如:

public static void main(String[] args) throws Exception {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        String dateString = "2023-05-30";
        Date parsedDate = sdf.parse(dateString);
        System.out.println(parsedDate); //Tue May 30 00:00:00 CST 2023
    }

这将输出解析后的Date对象。

六、线程安全性

SimpleDateFormat类是非线程安全的,不建议在多线程环境下共享一个实例。如果需要在多线程环境下使用,可以考虑使用ThreadLocal来保证每个线程拥有自己的SimpleDateFormat实例。

七、异常处理

        在使用SimpleDateFormat时,需要注意处理可能抛出的ParseException异常。当解析日期字符串时,如果字符串的格式与指定的格式模式不匹配,将抛出该异常。

        总结来说,SimpleDateFormat类是Java中一个方便的日期和时间格式化工具。通过指定格式模式,它可以将日期对象格式化为字符串,或者将字符串解析为日期对象。在使用时需要注意线程安全性和异常处理。

第十三章、Date类

一、简介

        在 Java 中,Date 类是用于表示日期和时间的类。它位于 java.util 包中,并提供了各种方法来操作日期和时间数据。

二、构造方法

1、Date():创建一个表示当前日期和时间的 Date 对象

//获取系统当前时间(精确到毫秒的系统当前时间)
        //直接调用无参数构造方法即可
        Date nowTime = new Date();
        System.out.println(nowTime); //Tue May 30 16:07:14 CST 2023

2、Date(long milliseconds):根据给定的毫秒数创建一个 Date 对象

Date time = new Date(1); //注意:参数是一个毫秒
        System.out.println(time);

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
        //北京是东八区,差8个小时
        System.out.println(sdf.format(time)); //1970-01-01 08:00:00:001

        //获取昨天的此时的时间
        Date yesterdayTime = new Date(System.currentTimeMillis() - 1000 * 60 * 60 * 24);
        System.out.println(sdf.format(yesterdayTime));

三、Date类型的处理

1、Date转换成String (按指定格式格式化日期)

package com.javase.日期;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateTest01 {
    public static void main(String[] args) {
        //获取系统当前时间(精确到毫秒的系统当前时间)
        //直接调用无参数构造方法即可
        Date nowTime = new Date();
        System.out.println(nowTime); //Tue May 30 16:07:14 CST 2023

        //格式化日期
        //将日期Date,按照指定的格式进行转换:Date --转换成具有一定格式的日期字符串 --》String

        //SimpleDateFormat是java.text包下的,专门负责日期格式化的
        /**
         * yyyy 年 年是4位
         * MM 月 月是2位
         * dd 日
         * HH 时
         * mm 分
         * ss 秒
         * SSS 毫秒 (豪秒 3位,最高999,1000豪秒为1秒)
         * 注意,除了以上赋能随便写之外,其他符号都可随便写
         */
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
        System.out.println(sdf); //java.text.SimpleDateFormat@f180c119

        String nowTimeStr = sdf.format(nowTime);
        System.out.println(nowTimeStr); //2023-05-30 16:53:49:836
    }
}

2、String转换成Date

package com.javase.日期;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateTest02 {
    public static void main(String[] args) throws Exception{
        //假设现在有一个日期字符串String,怎么转换成Date类型
        String time = "1996-09-24 12:30:00:888";

        //SimpleDateFormat sdf = new SimpleDateFormat("格式不能随便写,必须和String中的格式一致");
        //注意:SimpleDateFormat的参数传入的日期格式必须和字符串中日期格式一致,否则会出现java.text.ParseException 解析异常
        //SimpleDateFormat sdf2 = new SimpleDateFormat("MM-dd HH:mm:ss:SSS"); //java.text.ParseException
        //Date datetime = sdf2.parse(time);

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
        Date datetime = sdf.parse(time);
        System.out.println(datetime); //Tue Sep 24 12:30:00 CST 1996
    }
}

3、统计方法耗费时长

package com.javase.日期;

/**
 * 获取自1970年1月1日 00:00:00 000 到当前系统时间的总毫秒数
 * 1秒 = 1000毫秒
 */
public class DateTest03 {
    public static void main(String[] args) {
        long nowTimeMillis = System.currentTimeMillis();
        System.out.println(nowTimeMillis);

        //统计一个方法耗时
        //在调用方法之前记录一下毫秒数
        long begin = System.currentTimeMillis();
        print();
        //在执行完成目标方法后记录一下毫秒数
        long end = System.currentTimeMillis();
        System.out.println("耗费时长为" + (end - begin) + "毫秒");
    }

    //需求:统计一个方法执行所耗费的时间
    public static void print(){
        for (int i = 0; i < 1000; i++) {
            System.out.println("i= " + i);
        }
    }
}

第十四章、DecimalFormat类

一、简介

DecimalFormat类是Java中用于格式化数字的类,它位于java.text包中。DecimalFormat提供了灵活的方式来格式化数字,并允许你指定自定义的数字格式模式。

二、构造函数

DecimalFormat类提供了多个构造函数,可以根据不同的需求创建实例。其中最常用的构造函数是DecimalFormat(String pattern),它接受一个字符串参数pattern,用于指定数字的格式模式。

三、格式模式

        数字的格式模式由一系列的符号和特殊字符组成,用于指定数字的显示方式。常用的格式模式符号和特殊字符包括:

  • 0:表示数字占位符,如果对应位置没有数字,则显示为0。
  • #:表示数字占位符,如果对应位置没有数字,则不显示。
  • .:表示小数点。
  • ,:表示千位分隔符。
  • %:表示百分比。
  • -:表示负号。

你可以根据自己的需求组合这些符号和特殊字符来创建自定义的数字格式。

四、格式化数字

        使用DecimalFormatformat(double number)方法,可以将double类型的数字格式化为字符串,按照指定的数字格式进行格式化。例如:

DecimalFormat df = new DecimalFormat("#,##0.00");
        double number = 12345.6789;
        String formattedNumber = df.format(number);
        System.out.println(formattedNumber); //12,345.68

这将输出格式化后的数字字符串,按照"#,##0.00"的格式进行格式化,例如:"12,345.68"。 

五、解析数字

        使用DecimalFormatparse(String text)方法,可以将字符串解析为double类型的数字,按照指定的数字格式进行解析。例如:

DecimalFormat df = new DecimalFormat("#,##0.00");
        String numberString = "12,345.68";
        //df.parse()方法实际返回Double包装类的实例,属于Number类型
        double parsedNumber = df.parse(numberString).doubleValue();
        System.out.println(parsedNumber); //12345.68

这将输出解析后的数字。

        总结来说,DecimalFormat类是Java中用于格式化数字的类,它允许你根据指定的格式模式来格式化数字,并提供了解析格式化后的数字的能力。通过设置不同的格式模式和其他属性,你可以满足各种数字格式化的需求。

六、案例

package com.javase.Number;
import java.text.DecimalFormat;

public class DecimalFormatTest01 {
    public static void main(String[] args) throws Exception {
        //java.text.DecimalFormat专门负责数字格式化的。
        //需要在构造方法中指定数字格式
        //DecimalFormat df = new DecimalFormat("数字格式");

        /**
         * 数字格式类型:
         *  #:代表任意数字
         *  ,:代表千分位
         *  .:代表小数点
         *  0:代表不够补0
         *
         *  ###,###.##:表示加入千分位,保留2位小数
         */

        DecimalFormat df = new DecimalFormat("###,###.##");
        String s = df.format(1234.567);
        System.out.println(s); //1,234.57

        DecimalFormat df2 = new DecimalFormat("###,###.0000");
        System.out.println(df2.format(1234.56)); //1,234.5600
    }
}

第十五章、BigDecimal类

一、简介

BigDecimal 是 Java 中的一个用于精确计算的类,它提供了高精度的浮点数运算和任意精度的整数运算。BigDecimal 类属于 Java 的核心库 java.math

1、主要特点

  1. 高精度计算:BigDecimal 支持任意精度的计算,可以处理大数和小数,避免了浮点数运算中的精度丢失问题。

  2. 不可变性:BigDecimal 对象是不可变的,一旦创建,其值不能被修改。任何对 BigDecimal 对象的运算操作都会生成一个新的 BigDecimal 对象。

  3. 精确舍入:BigDecimal 提供了多种舍入模式,可以通过指定舍入模式来控制计算结果的舍入行为。

  4. 支持运算符重载:BigDecimal 支持与其他 BigDecimal 对象以及基本数据类型之间的加减乘除等常见运算,运算符可以直接应用于 BigDecimal 对象。

二、常用方法(实例方法)

1、add(BigDecimal augend):将当前 BigDecimal 对象与指定的参数相加。

//这个100不是普通的100,是精度极高的100
        BigDecimal a1 = new BigDecimal(100);
        //精度极高的200
        BigDecimal a2 = new BigDecimal(200);
        //求和,调用方法来求和
        BigDecimal a3 = a1.add(a2);
        System.out.println(a3); //300

2、subtract(BigDecimal subtrahend):从当前 BigDecimal 对象中减去指定的参数

//减法运算
        BigDecimal b1 = new BigDecimal(200);
        BigDecimal b2 = new BigDecimal(50);
        BigDecimal b3 = b1.subtract(b2);
        System.out.println(b3); //150

3、multiply(BigDecimal multiplicand):将当前 BigDecimal 对象与指定的参数相乘

//乘法运算
        BigDecimal c1 = new BigDecimal(30);
        BigDecimal c2 = new BigDecimal(3);
        BigDecimal c3 = c1.multiply(c2);
        System.out.println(c3); //90

4、divide(BigDecimal divisor, RoundingMode roundingMode):将当前 BigDecimal 对象除以指定的参数,使用指定的舍入模式进行舍入。

//除法运算
        BigDecimal d1 = new BigDecimal(100);
        BigDecimal d2 = new BigDecimal(4);
        BigDecimal d3 = d1.divide(d2,2);
        System.out.println(d3); //25

第十六章、Random类

一、简介

Random 类是 Java 中用于生成伪随机数的类,它属于 Java 的核心库 java.utilRandom 类提供了多种方法来生成随机数,包括整数、浮点数、布尔值和字节等。

二、常用方法(实例方法)

1、nextInt():生成一个int类型取值范围内的随机的整数

System.out.println("int最大值:" + Integer.MAX_VALUE);
        System.out.println("int最小值:" + Integer.MIN_VALUE);
        Random random = new Random();
        //随机产生一个int类型取值范围内的数字
        int num1 = random.nextInt();
        System.out.println(num1);

2、nextInt(int bound):生成一个介于 0(包含)和指定边界值(不包含)之间的随机整数。

//产生[0,100]之间的随机整数,不包括101
        //nextInt 翻译为:下一个int类型的数据是101,表示只能取到100
        Random random = new Random();
        int num2 = random.nextInt(101);
        System.out.println(num2);

三、案例:一个数组中包含5个不重复的随机数

1、数组去重思想:

        先初始化一个新数组,遍历原始数组,判断原始数组中的每个元素是否存在于新数组中,不存在则添加至新数组,否则不添加。完成去重

2、数组判断元素存在算法:
第一种:遍历数组中的每个元素,判断元素是否与指定元素相等,相等即存在,否则不存在
第二种:二分法,需要将数组按照升序排序,对半查找(需要排序不适合此案例)

package com.javase.random;

import java.util.Arrays;
import java.util.Random;

/**
 * 编写程序,生成5个不重复的随机数,重复的话重新生成
 * 最终生成的5个随机数放到数组中,要求数组中的5个随机数不重复。-+
 */
public class RandomTest02 {
    public static void main(String[] args) {

        //创建random对象
        Random random = new Random();

        //准备一个长度为5的一维数组
        int[] ints = new int[5]; //默认值都是0
        for (int i = 0; i < ints.length; i++) {
            ints[i] = -1;
        }
        System.out.println(Arrays.toString(ints));
        int index = 0;
        int count = 0;
        //循环,生成随机数
        while (index < ints.length){
            //生成随机数
            int num = random.nextInt(5);
            //判断ints数组中没有这个num的话就将该num放进数组中
            if(!(contains(ints,num))){
                ints[index++] = num;
            }
            count++;
        }
        System.out.println(Arrays.toString(ints));
        System.out.println("循环次数:" + count);
    }

    //单独编写一个方法,这个方法专门用来判断数组中是否包含某个元素

    /**
     *
     * @param arr 需要传入的数组
     * @param key 判断存在的元素
     * @return true表示包含,false表示不包含
     */
    public static boolean contains(int[] arr,int key){
        //不能使用二分法查找,二分法是建立在排序的基础上的,排序有问题
        for (int i = 0; i < arr.length; i++) {
            if(arr[i] == key){
                //条件成立表示包含,返回true
                return true;
            }
        }
        //否则表示不包含,返回false
        return false;
    }
}

第十七章、Scanner类

一、键盘接受用户输入

//System.out.println();  负责向控制台输出(从内存到控制台)
        //接受用户键盘输入 :从键盘到内存 (到内存中去)

        //第一步:创建键盘扫描器
        java.util.Scanner s = new java.util.Scanner(System.in);
        //第二步:调用Scanner对象的next()方法开始接受用户键盘输入
        //程序执行到这里会停下来,等待用户输入
        //当用户输入,并且最终敲回车键的时候,键入的信息会自动赋值给userInputContent

        //程序执行到这里,用户输入的信息已经到内存中了
        //接受文本:以字符串形式接受
//        String userInputContent = s.next();

        System.out.println("请输入数字:");
        //接受数字:以整数型
        int userInputContent = s.nextInt();

        //将内存中的数据输出到控制台
        System.out.println("您输入了:" + userInputContent);
        System.out.println("计算结果为:" + (userInputContent + 100));

这段代码使用 Java 编写,目的是从用户输入中读取一个数字,并将其加上100并打印结果。

        首先,代码使用 Java 标准库中的 Scanner 类,创建一个 Scanner 对象 s,并将其绑定到标准输入流 System.in,以便从控制台读取用户的输入。

        然后,代码打印一条提示信息 "请输入数字:",以提示用户输入一个数字。接下来,使用 nextInt() 方法从 Scanner 对象 s 中读取下一个整数,将其存储在变量 userInputContent 中。

        接着,代码使用 println() 方法将 "您输入了:" 和 userInputContent 打印出来,以显示用户输入的数字。

        最后,代码使用 println() 方法将 "计算结果为:" 和 userInputContent + 100 的值打印出来,以显示将用户输入的数字加上100的计算结果。其中,userInputContent + 100 的计算结果使用了括号以确保优先级正确。

第十八章、枚举类型(enum)

一、枚举类型的意义

package com.javase.枚举;

//本案例没有使用 枚举类型,分析缺陷
public class EnumTest01 {
    public static void main(String[] args) {
        //System.out.println(10 / 0); //java.lang.ArithmeticException
        //System.out.println(divide(2,1)); //1
        //System.out.println(divide(2,0)); //0

        boolean success = divide(10, 0);
        System.out.println(success ? "计算成功" : "计算失败");
    }

    /**
     * 需求:以下程序,计算两个int类型数据的商,计算成功返回1,计算失败返回0
     *
     * @param a int类型的数据
     * @param b int类型的数据
     * @return 返回1表示成功,返回0表示失败
     */
    /*public static int divide (int a,int b){
        try{
            int c = a / b;
            //程序执行到此处表述以上代码没有发生异常,表示执行成功
            return 1;
        }catch (Exception e){
            //程序执行到此处表示以上程序出现了一场
            //表示执行失败
            return 0;
        }
    }*/

    //涉及缺陷在这个方法的返回值类型商,返回一个int类型不恰当
    //主要是想知道程序执行最后的结果是成功还是失败,最好返回boolean类型
    //因为boolean类型的true和false正好可以表示两种不同的状态
    /*public static int divide (int a,int b){
        try{
            int c = a / b;
            //必须,将1误写成了10,实际上已经偏离了需求,但是编译器并没有检查出来
            //我们尽可能追求的是:所有的错误尽可能让编译器找出来,所有的错误越早发现越好
            return 1;
        }catch (Exception e){
            return 0;
        }
    }*/
    public static boolean divide(int a, int b) {
        try {
            int c = a / b;
            //必须,将1误写成了10,实际上已经偏离了需求,但是编译器并没有检查出来
            //我们尽可能追求的是:所有的错误尽可能让编译器找出来,所有的错误越早发现越好
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 思考:以上的这个方法涉及没毛病,挺好,返回true和false表示两种情况,
     * 但是在以后的开发中,有可能遇到一个方法的执行结果可能包含三种情况,
     * 四种情况,五种情况不等,但是每一个都是可以数清楚的,可以一枚一枚的列举出来的,
     * 这个布尔类型就不能满足需求了,此时就需要使用java中的枚举类型
     */
}

二、概述

枚举是一种引用数据类型

        在Java中,枚举类型(Enumeration)是一种特殊的引用数据类型,用于定义一组常量。枚举类型在程序中表示一组固定的值,这些值称为枚举常量。枚举常量是该枚举类型的实例,同时又相当于静态变量,通过  枚举类型的名称.常量名称  来引用。

        枚举类型在程序中常用于表示一组固定的选项或状态,它提供了类型安全性和可读性,避免了使用常量或字符串表示的硬编码。此外,枚举类型还可以用于开发更具可读性和可维护性的代码。

        需要注意的是,Java中的枚举类型是一个特殊的类(编译后也是个class字节码文件),可以添加字段、构造方法和其他方法。枚举类型也可以实现接口,使用枚举常量来实现接口的方法。

三、定义枚举类型

使用关键字enum来定义枚举类型,后面跟着枚举类型的名称和一组枚举常量。

enum Day {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

1、枚举常量

        枚举常量是枚举类型的实例,每个枚举常量都有一个名称和一个与之关联的值。枚举常量之间使用逗号分隔。

四、访问枚举常量

可以使用枚举类型的名称和枚举常量的名称来访问枚举常量。

Day today = Day.MONDAY;
System.out.println(today); // 输出: MONDAY

五、枚举常量的比较

可以使用==运算符来比较枚举常量。

Day day1 = Day.MONDAY;
Day day2 = Day.TUESDAY;
System.out.println(day1 == day2); // 输出: false

六、枚举类型的方法

可以为枚举类型定义方法,枚举常量可以调用这些方法。

enum Day {
            MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY; //此处要写分号,语句结束

            public boolean isWeekend() {
                return this == SATURDAY || this == SUNDAY;
            }
        }
        Day day = Day.FRIDAY;
        System.out.println(day.isWeekend()); //false

        Day day1 = Day.SATURDAY;
        System.out.println(day1.isWeekend()); //true

七、遍历枚举常量

可以使用values()方法获取枚举类型中的所有枚举常量,并进行遍历。

方法一:

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

方法二:

for (Day day : Day.values()) {
    System.out.println(day);
}

八、枚举类型的使用条件

1、结果是有两种情况的,建议使用布尔类型

2、结果超过两种并且还是可以一枚一枚列举出来的,建议使用枚举类型

        例如:颜色,四季,星期等

九、案例

1、判断程序是否执行成功

package com.javase.枚举;

//采用枚举的方法改造程序
public class EnumTest02 {
    public static void main(String[] args) {
        Result r = devide(10,0);
        System.out.println(r == Result.SUCCESS?"计算成功":"计算失败");
    }

    /**
     * 计算两个int类型数据的商
     * @param a int数据
     * @param b int数据
     * @return Result.SUCCESS表示成功,Result.FAIL表示失败
     */
    public static Result devide(int a,int b){
        try{
            int c = a / b;
            return Result.SUCCESS;
        }catch (Exception e){
            return Result.FAIL;
        }
    }
}

//枚举:一枚一枚可以列举出来的,才建议使用枚举类型
//枚举编译之后也是生成class字节码文件
//枚举也是一种引用数据类型。
//枚举中的每一个值可以看做是常量(枚举常量),相当于该枚举类的实例对象,但同时又是该枚举类型静态变量,使用枚举名. 来访问
enum Result{
    //SUCCESS 是枚举Result类型中的一个值
    //FAIL 是枚举Result类型中的一个值
    SUCCESS,FAIL;
}

2、switch语句也支持枚举

Season.java

package com.javase.枚举;

/**
 * 四季:春夏秋冬
 */
public enum Season {
    SPRING,SUMMER,AUTUMN,WINTER;
}

SwitchTest.java

package com.javase.枚举;

public class SwitchTest {
    public static void main(String[] args) {
        //switch语句支持枚举类型
        //switch也支持String,int
        //低版本的JDK,只支持int
        //高版本的JDK,支持int,String,枚举
        //byte,short,char也可以,因为存在自动类型转换
        switch (Season.SUMMER){
            //必须省略Season
            case SPRING:
                System.out.println("春天");
                break;
            case SUMMER:
                System.out.println("夏天");
                break;
            case AUTUMN:
                System.out.println("秋天");
                break;
            case WINTER:
                System.out.println("冬天");
                break;
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值