String类相关
Posted shawnyue-08
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了String类相关相关的知识,希望对你有一定的参考价值。
final修饰引用类型变量
package org.westos.demo;
import java.util.Arrays;
/**
* 1、final的测试类
* @author lwj
* @date 2020/4/18 12:39
*/
public class MyTest {
public static void main(String[] args) {
final int[] value = {1,2,3};
//final修饰引用类型,引用/地址值不变,value不能再指向其他对象
int[] another = {4,5,6};
//value = another;
//不允许把value指向内存的另一个区域
//但是元素却是可以改变的
value[2] = 5;
System.out.println(Arrays.toString(value));
//[1, 2, 5]
}
}
String实例化的两种方式及差异
- 直接赋值
- 通过构造函数,可以直接将字符串的值传入,也可以传入一个char数组
package org.westos.demo;
/**
* 2、String实例化的两种方式
* @author lwj
* @date 2020/4/18 12:46
*/
public class MyDemo {
public static void main(String[] args) {
String str = "Hello";
String str2 = new String("Hello");
char[] chars = {‘H‘, ‘e‘, ‘l‘, ‘l‘, ‘o‘};
String str3 = new String(chars);
}
}
package org.westos.demo;
/**
* @author lwj
* @date 2020/4/18 12:53
*/
public class MyDemo2 {
public static void main(String[] args) {
String s1 = "Hello";
String s2 = "Hello";
System.out.println(s1 == s2);
//true
//直接赋值,是在字符串常量池
String s3 = new String("Hello");
String s4 = new String("Hello");
System.out.println(s3 == s4);
//false
//new对象,是在堆内存中
}
}
Java为了避免产生大量的String对象,设计了一个字符串常量池。工作原理是这样的,创建一个字符串时,JVM首先会检查字符串常量池中是否有值相等的字符串,如果有,则不再创建,直接返回该字符串的引用地址;若没有,则创建,然后放到字符串常量池中,并返回新创建的字符串的引用地址。所以上面s1与s2引用地址相同。
String s3=new String("Hello"); JVM首先是在字符串常量池中找"Hello" 字符串,如果没有创建字符串常量,然后放到常量池中,若已存在,则不需要创建;当遇到 new 时,还会在内存(不是字符串常量池中,而是在堆里面)上创建一个新的String对象,存储"Hello",并将内存上的String对象引用地址返回。
String s1 = new String("abc")创建了几个对象
创建了两个对象。
package org.westos.demo;
/**
* @author lwj
* @date 2020/4/18 13:12
*/
public class MyDemo4 {
public static void main(String[] args) {
String s1 = new String("abc");
String s2 = "abc";
System.out.println(s1 == s2);
//false,一个是堆内存的地址,一个是常量池的地址
System.out.println(s1.equals(s2));
//true
}
}
解释:
先有字符串"abc"放入常量池,然后new了一份字符串"abc"放入Java堆(字符串常量在编译期就已经放入常量池),然后Java栈的s指向Java堆上的"abc"。
new一个String对象,是需要先在字符串常量中查找相同值或创建一个字符串常量,然后再在内存中创建一个String对象,所以String str = new String("abc"); 会创建两个对象。
equal方法和intern方法
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
String类对equals方法进行了重写,所以我们可以直接调用String类的equals方法来判断两个字符串是否相等。
String类型的常量池比较特殊,它的主要使用方式有两种:
- 直接用双引号声明出来的String对象会存储在常量池中;
- 如果不是用双引号声明出来的String对象,可以使用String提供的intern()方法:如果运行时常量池中已经包含一个等于此String对象内容的字符串,则返回常量池中该字符串的引用;如果没有,则在常量池中创建与此String内容相同的字符串,并返回常量池中创建的该字符串引用。
package org.westos.demo;
/**
* @author lwj
* @date 2020/4/18 12:59
*/
public class MyDemo3 {
public static void main(String[] args) {
String s1 = "你好";
String s2 = new String("你好");
String s3 = s2.intern();
System.out.println(s1 == s3);
//true,因为s1和s3都是常量池中的
System.out.println(s1 == s2);
//false,因为s1是在常量池,s2是在堆内存中
}
}
String类的常用方法
1、字符串截取(方法名全部都是小写)
public String substring(int beginIndex)
public String substring(int beginIndex, int endIndex)
前者:从beginIndex位置开始截取,一直到字符串结尾;
后者:从beginIndex位置开始截取,一直到endIndex位置结束,同时不包含endIndex。
package org.westos.demo;
/**
* @author lwj
* @date 2020/4/18 13:39
*/
public class MyDemo5 {
public static void main(String[] args) {
String s = "HelloWorld";
System.out.println(s.substring(4));
//oWorld
System.out.println(s.substring(2, 7));
//lloWo
//不包含endIndex
}
}
2、字符串分割
将目标字符串按照某个分割符,切割成一个字符串数组。
public String[] split(String regex) // 正则表达式
package org.westos.demo;
import java.util.Arrays;
/**
* @author lwj
* @date 2020/4/18 13:46
*/
public class MyDemo6 {
public static void main(String[] args) {
String str = "Hello,World,Hello,Java";
String[] split = str.split(",");
System.out.println(Arrays.toString(split));
//[Hello, World, Hello, Java]
}
}
split方法支持参数传入正则表达式。
class MyDemo7 {
public static void main(String[] args) {
String s = "Hello,World;Hello-Java";
String[] split = s.split("[,;-]");
System.out.println(Arrays.toString(split));
//[Hello, World, Hello, Java]
}
}
3、String的常用方法
方法 | 描述 |
---|---|
public int length() | 返回字符串的长度 |
public boolean isEmpty() | 返回字符串是否为空 |
public char charAt(int index) | 返回字符串中指定位置的字符 |
public byte[] getBytes() | 将字符串转为byte型数组 |
public boolean equals(Object anObject) | 判断两个字符串是否相等 |
public boolean equalsIgnoreCase(String anotherString) | 判断两个字符串是否相等并忽略大小写 |
public int compareTo(String anotherString) | 对两个字符串进行排序 |
public boolean startsWith(String prefix) | 判断是否以指定的值开头 |
public boolean endsWith(String suffix) | 判断是否以指定的值结尾 |
public int hashCode() | 获取字符串的散列值 |
public int indexOf(int ch) | 从头开始查找指定字符的位置 |
public int indexOf(int ch, int fromIndex) | 从指定位置开始查找指定字符的位置 |
public int indexOf(String str) | 从头开始查找指定字符串的位置 |
public int indexOf(String str, int fromIndex) | 从指定位置开始查找指定字符串的位置 |
public String concat(String str) | 追加字符串 |
public String replaceAll(String regex, String replacement) | 替换字符串 |
public String toLowerCase() | 将字符串转为小写 |
public String toUpperCase() | 将字符串转为大写 |
public char[] toCharArray() | 将字符串转为字符数组 |
经典面试题
1、"=="和equals()的区别
对于基本数据类型,"=="比较值是否相等,对于引用数据类型,"=="比较引用的内存地址是否相等。
equals()方法是Object类的方法,本质是用"==",但是Java中的类可以重写Object的equals()方法,可以重新定义逻辑。
package org.westos.demo;
import java.util.Objects;
/**
* @author lwj
* @date 2020/4/18 14:21
*/
public class MyDemo8 {
public static void main(String[] args) {
Cat cat1 = new Cat(1, "小咪");
Cat cat2 = new Cat(1, "小咪");
System.out.println(cat1 == cat2);
//false
System.out.println(cat1.equals(cat2));
//true,Cat类重写了equals()方法
}
}
class Cat {
private int id;
private String name;
public Cat(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) {return true;}
if (o == null || getClass() != o.getClass()) {return false;}
Cat cat = (Cat) o;
return id == cat.id &&
Objects.equals(name, cat.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
}
2、下面代码的运行结果是
package org.westos.demo1;
/**
* @author lwj
* @date 2020/4/18 17:42
*/
public class MyDemo1 {
public static void main(String[] args) {
String s1 = "Hello World";
String s2 = "Hello" + " World";
System.out.println(s1 == s2);
//true
//字符串常量,字符串字面量拼接的结果还是在字符串常量池中
}
}
class MyDemo2 {
public static void main(String[] args) {
String s1 = "Hello";
String s2 = "Hello";
s2 += " World";
System.out.println(s1 == s2);
//false
//字符串字面量和String类型变量拼接时,得到的新字符串不再保存在字符串常量池中,而是在堆中开辟一段内存存储
}
}
class MyDemo3 {
public static void main(String[] args) {
String str1 = "Hello World";
String str2 = " World";
String str3 = "Hello" + str2;
System.out.println(str1 == str3);
//false
//str2是变量,"Hello"是字符串字面值,字符串字面值拼接字符串变量的结果在堆内存
}
}
class MyDemo4 {
public static void main(String[] args) {
String s1 = "Hello World";
final String s2 = " World";
//常量,s2引用不能指向别的对象
String s3 = "Hello" + s2;
System.out.println(s1 == s3);
//true
//字符串字面值+常量的结果仍然保存在字符串常量池中
}
}
class MyDemo5 {
public static void main(String[] args) {
String s1 = "Hello World";
final String s2 = new String(" World");
String s3 = "Hello" + s2;
System.out.println(s1 == s3);
//false
//s2虽然是常量,但是是在堆内存,则s3也保存在堆内存
}
}
3、String是线程安全的吗?
String是不可变类,一旦创建了String对象,我们就无法改变它的值。因此,它是线程安全的。
String的不可变性
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
}
在Java中,String类其实是对字符数组的封装。
在Java中,数组也是对象,所以value也只是一个引用,它指向一个真正的数组对象,在执行String s = "abc"后真正的内存布局应该为:
value、hash都是private,而且没有提供公共的set、get方法,所以在String类的外部无法修改String。也就是说一旦初始化就不能修改, 并且在String类的外部不能访问。
String s = "hello";
s = "world";
这种情况, s的值貌似改变了, 从hello
变成了world
,其实这里s所改变的是他所引用的对象, 并不是String对象的值改变了。
我们在外界是无法访问到String对象内部的value引用的,String对象初始化完毕,我们就不能再访问到value引用,也就不能修改value数组的值。
以上是关于String类相关的主要内容,如果未能解决你的问题,请参考以下文章
spring练习,在Eclipse搭建的Spring开发环境中,使用set注入方式,实现对象的依赖关系,通过ClassPathXmlApplicationContext实体类获取Bean对象(代码片段