字符串

通用的理论:

对于非基本类型(对象类型|引用类型)

  1. 只定义,不new(实例化):默认值都是Null

  2. new实例化:Xxx xx = new Xxx();

xx值:不是null

xx内部的值都是数据类型的默认值

String s3 = new String(); s3 : ""

常用的String方法

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

String str = "Hello, World!";

// 使用length()方法获取字符串的长度
int length = str.length();
System.out.println("字符串的长度为:" + length);

// 使用charAt()方法获取指定索引位置的字符
char ch = str.charAt(7);
System.out.println("索引位置为7的字符为:" + ch);

// 使用substring()方法获取子字符串
String subStr = str.substring(7, 12);
System.out.println("从索引位置7到12的子字符串为:" + subStr);

// 使用concat()方法将字符串连接到末尾
String newStr = str.concat(" Welcome!");
System.out.println("连接后的字符串为:" + newStr);

// 使用toUpperCase()方法将字符串转换为大写
String upperCaseStr = str.toUpperCase();
System.out.println("转换为大写后的字符串为:" + upperCaseStr);

// 使用toLowerCase()方法将字符串转换为小写
String lowerCaseStr = str.toLowerCase();
System.out.println("转换为小写后的字符串为:" + lowerCaseStr);

// 使用trim()方法去除字符串两端的空格
String trimmedStr = "   Hello, World!   ".trim();
System.out.println("去除空格后的字符串为:" + trimmedStr);

// 使用startsWith()方法判断字符串是否以指定前缀开头
boolean startsWithHello = str.startsWith("Hello");
System.out.println("字符串是否以Hello开头:" + startsWithHello);

// 使用endsWith()方法判断字符串是否以指定后缀结尾
boolean endsWithWorld = str.endsWith("World!");
System.out.println("字符串是否以World!结尾:" + endsWithWorld);

// 使用indexOf()方法获取指定字符串第一次出现的索引位置
int indexOfComma = str.indexOf(",");
System.out.println("逗号第一次出现的索引位置为:" + indexOfComma);

// 使用lastIndexOf()方法获取指定字符串最后一次出现的索引位置
int lastIndexOfSpace = str.lastIndexOf(" ");
System.out.println("空格最后一次出现的索引位置为:" + lastIndexOfSpace);

// 使用replace()方法将指定字符替换为新的字符
String replacedStr = str.replace("o", "e");
System.out.println("将字符o替换为e后的字符串为:" + replacedStr);

// 使用split()方法根据指定正则表达式拆分字符串为字符串数组
String[] splitStr = str.split(",");
System.out.println("拆分后的字符串数组为:");
for (String s : splitStr) {
    System.out.println(s);
}

// 使用equals()方法判断字符串是否与指定对象相等
boolean isEqual = str.equals("Hello, World!");
System.out.println("字符串是否与指定对象相等:" + isEqual);

// 使用compareTo()方法按字典顺序比较两个字符串
int compareResult = str.compareTo("Hello");
System.out.println("字符串与Hello比较的结果为:" + compareResult);

字符串常量池

字符串常量池是Java中的一个特殊的内存区域,用于存储字符串常量。在Java中,字符串是不可变的,即一旦创建就不能被修改。为了提高性能和节省内存,Java使用了字符串常量池来重用字符串对象。

当我们创建一个字符串常量时,Java会首先检查字符串常量池中是否已经存在相同值的字符串。如果存在,则返回常量池中的字符串对象的引用;如果不存在,则在常量池中创建一个新的字符串对象,并返回其引用。

字符串常量池的特点如下:

  1. 字符串常量池中的字符串对象是唯一的,即相同值的字符串只会在常量池中存在一份。

  2. 字符串常量池中的字符串对象是不可变的,一旦创建就不能被修改。这是因为字符串常量池的设计初衷是为了提高性能和节省内存,如果允许修改字符串对象,可能会导致其他引用到该对象的地方出现问题。

  3. 字符串常量池中的字符串对象是全局可见的,即可以在整个程序中访问到。这是因为字符串常量池是在Java虚拟机启动时就创建的,而且所有的类共享同一个字符串常量池。

在Java中,有多种方式可以将字符串放入字符串常量池:

  1. 直接使用双引号创建字符串常量,例如:String str = "Hello";

  2. 调用String类的intern()方法,该方法会将字符串添加到常量池中,并返回常量池中的字符串引用。例如:String str = new String("Hello").intern();

需要注意的是,使用new String()方式创建的字符串对象不会放入常量池中,而是在堆内存中创建一个新的字符串对象。如果需要将其放入常量池中,可以调用intern()方法。

字符串常量池的使用可以提高性能和节省内存,特别是在频繁创建相同值的字符串时。但是需要注意的是,过度使用字符串常量池可能会导致内存泄漏问题,因为常量池中的字符串对象是全局可见的,一旦被引用,就不会被垃圾回收器回收。因此,在使用字符串常量池时,需要注意合理使用和及时释放不再使用的字符串对象。

字符串对象的创建

面试题:

String str4 = new String(“abc”) 创建多少个对象?

1、在常量池中查找是否有“abc”对象。

1)有则返回对应的引用实例;

2)没有则创建对应的实例对象。

2、在堆中 new 一个 String("abc") 对象。

3、将对象地址赋值给str4,创建一个引用。

所以,常量池中没有“abc”字面量则创建两个对象,否则创建一个对象,以及创建一个引用。

根据字面量,往往会提出这样的变式题:

String str1 = new String("A"+"B") ; 会创建多少个对象?

String str2 = new String("ABC") + "ABC"; 会创建几个对象?

str1:

字符串常量池:"A","B","AB" : 3个

堆:new String("AB") :1个

引用: str1 :1个

总共 : 5个

str2 :

字符串常量池:"ABC" “ABCABC”: 2个

堆:new String("ABC") :1个

引用: str2 :1个

总共 : 4个

String s = new String("1")+new String("1")的,会创建几个对象?

intern()的作用

intern() 方法是 String 类的一个方法,它的作用是将字符串对象添加到字符串常量池中,并返回常量池中的字符串引用。

当调用 intern() 方法时,Java 会首先检查字符串常量池中是否已经存在相同值的字符串。如果存在,则返回常量池中的字符串对象的引用;如果不存在,则在常量池中创建一个新的字符串对象,并返回其引用。

使用 intern() 方法可以实现字符串的重用,即相同值的字符串只会在常量池中存在一份。这样可以节省内存空间,并提高程序的性能。

需要注意的是,调用 intern() 方法并不会改变原始字符串对象的值,而是返回一个新的字符串对象的引用。因此,如果需要使用 intern() 方法的返回值,应该将其赋给一个新的字符串引用。

以下是 intern() 方法的示例代码:

String str1 = new String("Hello");
String str2 = str1.intern();

System.out.println(str1 == str2);  // false
System.out.println(str1.equals(str2));  // true

在上面的示例中,首先创建了一个字符串对象 str1,并调用 intern() 方法将其添加到字符串常量池中。然后,将 intern() 方法的返回值赋给 str2。最后,通过比较引用和比较值的方式验证了 intern() 方法的作用。

需要注意的是,由于字符串常量池是在 Java 虚拟机启动时就创建的,并且所有的类共享同一个字符串常量池,因此 intern() 方法只能将字符串添加到当前类所在的字符串常量池中。如果需要将字符串添加到其他类所在的字符串常量池中,可以使用其他类的类名调用 intern() 方法。

StringBuffer使用

StringBuffer

StringBuffer 是 Java 中一个用于处理字符串的可变对象,它的主要作用是在需要频繁对字符串进行修改时,提供了高效的字符串操作方式。与 String 不同的是,StringBuffer 是可变的,可以动态修改其内容而不会创建新的对象。

StringBuffer 的特点

  1. 可变性:StringBuffer 对象的内容可以被修改,可以进行插入、追加、删除等操作,而不会创建新的对象。

  2. 线程安全:StringBuffer 是线程安全的,即多个线程可以同时访问一个 StringBuffer 对象而不会出现数据不一致的情况。这是因为 StringBuffer 的方法都是使用 synchronized 关键字进行同步的。

  3. 性能:由于 StringBuffer 是可变的,对字符串的修改操作不会频繁创建新的对象,因此在需要频繁操作字符串时,使用 StringBuffer 可以提高性能。

StringBuffer 的常用方法

以下是一些常用的 StringBuffer 方法:

  • append():追加字符串到当前 StringBuffer 对象的末尾。

  • insert():在指定位置插入字符串到当前 StringBuffer 对象中。

  • delete():删除指定位置范围内的字符。

  • deleteCharAt():删除指定位置的字符。

  • replace():替换指定位置范围内的字符序列。

  • reverse():反转当前 StringBuffer 对象中的字符序列。

  • charAt():获取指定位置的字符。

  • substring():获取指定位置范围内的子字符串。

示例代码

以下是一个使用 StringBuffer 的示例代码:

// 创建一个空的 StringBuffer 对象
StringBuffer sb = new StringBuffer();

// 使用 append() 方法追加字符串
sb.append("Hello");
sb.append("World");

// 使用 insert() 方法在指定位置插入字符串
sb.insert(5, ", ");

// 使用 delete() 方法删除指定位置范围内的字符
sb.delete(5, 7);

// 使用 replace() 方法替换指定位置范围内的字符序列
sb.replace(5, 10, "Java");

// 使用 reverse() 方法反转字符串
sb.reverse();

// 使用 charAt() 方法获取指定位置的字符
char ch = sb.charAt(0);

// 使用 substring() 方法获取指定位置范围内的子字符串
String subStr = sb.substring(0, 5);

System.out.println(sb.toString());  // 输出结果为:avaJ, olleH
System.out.println(ch);  // 输出结果为:a
System.out.println(subStr);  // 输出结果为:avaJ,

在上面的示例中,首先创建了一个空的 StringBuffer 对象 sb,然后通过不同的方法对其内容进行了追加、插入、删除、替换、反转、获取字符等操作,最后通过 toString() 方法将 StringBuffer 对象转换为字符串并输出。

使用 StringBuffer 可以方便地进行字符串的修改和操作,特别适用于需要频繁修改字符串内容的场景。

String 和 StringBuffer 互相转换

在 Java 中,可以通过以下方法实现 String 和 StringBuffer 之间的互相转换:

String 转换为 StringBuffer

  1. 使用构造方法: 可以通过 StringBuffer 的构造方法将 String 转换为 StringBuffer。示例代码如下:

String str = "Hello, World!";
StringBuffer sb = new StringBuffer(str);
  1. 使用 append 方法: 可以通过 StringBuffer 的 append 方法将 String 转换为 StringBuffer。示例代码如下:

String str = "Hello, World!";
StringBuffer sb = new StringBuffer();
sb.append(str);

StringBuffer 转换为 String

  1. 使用 toString 方法: 可以通过 StringBuffer 的 toString 方法将 StringBuffer 转换为 String。示例代码如下:

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

2.StringBuffer + “” 强转String

StringBuffer sb = new StringBuffer("Hello, World!");
String str = sb + "";

通过以上方法,可以方便地在 String 和 StringBuffer 之间进行互相转换,根据实际需求选择合适的方法进行转换。

package和访问修饰符

package

在Java中,package是一种用于组织和管理类的机制。它可以将相关的类放在同一个包中,以便更好地组织和管理代码。

package的作用

  1. 命名空间管理:package可以将类组织在不同的命名空间中,避免类名冲突。

  2. 访问控制:package可以限制类的访问范围,只有在同一个包中的类才能访问包中的类。

  3. 代码组织:package可以将相关的类放在同一个包中,方便代码的组织和管理。

package的命名规范

在命名package时,应遵循以下规范:

  1. 小写字母:package的名称应使用小写字母。

  2. 全限定名:package的名称应使用全限定名,即使用点号(.)将各级包名分隔开。

  3. 有意义:package的名称应具有一定的意义,能够反映出所包含的类的功能或用途。

package的声明

在Java源文件的开头,可以使用package关键字声明所属的包。例如:

package com.example.myapp;

在声明package时,应将源文件放在与package对应的目录结构中。例如,上述的package声明应将源文件放在com/example/myapp目录下。

package的导入

在Java源文件中,可以使用import关键字导入其他包中的类。例如:

import com.example.myapp.MyClass;

导入类后,就可以直接使用类名来引用该类,而不需要使用完整的包名。

访问修饰符

在Java中,访问修饰符用于控制类、方法和变量的访问权限。Java提供了四种访问修饰符:

  1. public:公共的,可以被任何类访问。

  2. protected:受保护的,可以被同一个包中的类和子类访问。

  3. default:默认的,没有修饰符,可以被同一个包中的类访问。

  4. private:私有的,只能被当前类访问。

访问修饰符的使用

访问修饰符可以用于类、方法和变量的声明。以下是访问修饰符的使用规则:

  1. 类的访问修饰符:类的访问

修饰符可以是public或默认(包访问权限)。

  1. 方法的访问修饰符:方法的访问修饰符可以是public、protected、default或private。这些修饰符决定了方法可以被哪些类访问。

  2. 变量的访问修饰符:变量的访问修饰符可以是public、protected、default或private。这些修饰符决定了变量可以被哪些类访问。

访问修饰符的作用

  • public:公共的访问修饰符表示该类、方法或变量可以被任何类访问,没有访问限制。

  • protected:受保护的访问修饰符表示该类、方法或变量可以被同一个包中的类和子类访问,但不能被其他包中的类访问。

  • default:默认的访问修饰符表示该类、方法或变量可以被同一个包中的类访问,但不能被其他包中的类访问。如果没有指定访问修饰符,默认为default。

  • private:私有的访问修饰符表示该类、方法或变量只能被当前类访问,其他类无法访问。

访问修饰符的选择取决于设计的需要,合理使用访问修饰符可以提高代码的安全性和可维护性。

// 定义一个公共类
public class PublicClass {
    public void publicMethod() {
        System.out.println("This is a public method.");
    }
}

// 定义一个受保护类
class ProtectedClass {
    protected void protectedMethod() {
        System.out.println("This is a protected method.");
    }
}

// 定义一个默认类
class DefaultClass {
    void defaultMethod() {
        System.out.println("This is a default method.");
    }
}

// 定义一个私有类
class PrivateClass {
    private void privateMethod() {
        System.out.println("This is a private method.");
    }
}

// 在同一个包中使用不同访问修饰符的类
public class AccessModifiersExample {
    public static void main(String[] args) {
        PublicClass publicObj = new PublicClass();
        publicObj.publicMethod();  // 可以访问公共方法

        ProtectedClass protectedObj = new ProtectedClass();
        protectedObj.protectedMethod();  // 可以访问受保护方法

        DefaultClass defaultObj = new DefaultClass();
        defaultObj.defaultMethod();  // 可以访问默认方法

        PrivateClass privateObj = new PrivateClass();
        // privateObj.privateMethod();  // 无法访问私有方法,会编译错误
    }
}