解析String s=new String(“abc”)
我们知道想知道两个对象是不是同一个对象,直接用等号比较就可以
public static void main(String[] args) {
String strABC = new String("abc");
String abc = "abc";
System.out.println(strABC == abc); //结果flase,就表示不是一个对象,内存地址不同
System.out.println(strABC.equals(abc)); //结果为true
System.out.println("abc" == "abc"); //结果为true
System.out.println("abc".getClass().getName()); //java.lang.String
}
而且一旦你这样写,ide就会有提示,翻译过来是这样的
初始化一个新创建的String对象,使其表示与参数相同的字符序列; 换句话说,新创建的字符串是参数字符串的副本。 除非需要显式的原始副本,否则不需要使用此构造函数,因为字符串是不可变的
这个问题的根本原因就在于String的特殊性,而且初学编程的时候,有时候也会记错String是8种基本数据类型之一;也是因为他的特殊性,我们正常想得到一个类型的对象的时候都是需要new + class类型,但是8中数据类型和String确不需要用new的方式,直接可以写出来;像这样:
写个"abc"这就是一个对象,就可以调用java.lang.String类的各种方法了,所以证明"abc",就已经是一个对象了,而且我们知道 java.lang.String一般都是存储在常量池中的,也就是我们写的"abc"是存储在常量池中的;
当我们写 new String(“abc”); 这个的时候,里面包含了"abc",就已经在常量池创建好了一个"abc"对象,new String()又在堆里面创建了一个String类型的对象,所以就是创建了两个对象;而且新创建的字符串是参数字符串的副本,副本的意思就是内容一模一样,但是又不是同一个;
如何证明新创建的字符串是参数字符串的副本?
我们来看这样的一段代码
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
String c = "abc";
String cc = new String(c);
// 利用反射获取String类型里的char数组
Field field = String.class.getDeclaredField("value");
field.setAccessible(true);//设置权限 可以对非public修饰的变量操作
Object o = field.get(c);
System.out.println(o);
Object o2 = field.get(cc);
System.out.println(o2);
System.out.println("=========================");
char[] abc = {'a', 'b', 'c'};
Object abcc = abc;
char[] abc2 = {'a', 'b', 'c'};
Object abcc2 = abc2;
System.out.println(abcc);
System.out.println(abcc2);
}
从结果我们可以看到o和o2的内容是一样的,然后我们分别打印他们的内存地址:
结果如下
也就是我们的c对象和new出来的cc对象里面实际存储的是同一个char[]数组,我们自己后创建的abc和abc2 他们的内容相同,但是hashcode不同,所以打印的结果不同;
我们在做一波骚操作:
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
String c = "abc";
String cc = new String(c);
Field field = String.class.getDeclaredField("value");
field.setAccessible(true);//设置权限 可以对非public修饰的变量操作
Object o = field.get(c);
Object o2 = field.get(cc);
System.out.println(o);//打印结果 [C@330bedb4
System.out.println(o2);//打印结果 [C@330bedb4
System.out.println(o2 == o); // 打印结果true
char[] ewfowjeif = (char[]) o2;
ewfowjeif[0] = 'g';
System.out.println(c);// 打印结果 gbc
System.out.println(cc);// 打印结果 gbc
}
就这样把 c和cc两个String对象内部存储的char数组的值改了,结果就是打印他们的时候,他们的值也改了
拓展:运行时常量和编译时常量
1、先来看String
其实就是看合在一起几个对象是否有变量或者需要运行,如果是确定值那结果就是确定的,如果有变量或需要运行程序,那就是运行时常量;运行时常量的内存地址是实时运行的到的,不是确定的
public static void main(String[] args) {
String a = "a";
String c = a + "c";//运行时常量
String cc = "a" + "c";//编译时常量
String c2 = a + "c";//运行时常量
String cc2 = "a" + "c";//编译时常量
System.out.println(c == c2); // 结果为false
System.out.println(cc == cc2); // 结果为true
System.out.println(c == cc); // 结果为false
}
还有一种特殊情况
public class StringABC {
public static void main(String[] args) {
System.out.println(Two.a);
}
}
class Two {
public static final char a = 'a';//这里除了char还可以是其他8种数据类型,另外String类型也可以
static {
System.out.println("Two.class加载了");
}
}
运行结果
只打印了a,表示改类没有被加载,我们把代码做一个小小的改动
public class StringABC {
public static void main(String[] args) {
System.out.println(Two.a);
}
}
class Two {
public static final Object a = 'a';
static {
System.out.println("Two.class加载了");
}
}
打印结果
类被加载了,只是声明类型不同