这篇文章给大家分享的是有关Java中String如何创建对象的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。
本文研究的主要是Java String创建对象的问题,具体介绍如下。
首先我们要明白两个概念,引用变量和对象,对象一般通过new在堆中创建,String只是一个引用变量。
所有的字符串都是String对象,由于字符串常量的大量使用,java中为了节省时间,在编译阶段,会把所有字符串常量放在字符串常量池中,字符串常量池的一个好处就是可以把相同的字符串合并,占用一个空间。
虽然在Java中无法直接获取变量的地址,但是可以用==判断一下两个引用变量是否指向了一个地址即一个对象。
栈内存 | 堆内存 |
---|---|
基础类型,对象引用( 堆内存地址 ) | 由new 创建的对象和数组 |
存取速度快 | 相对于栈内存较慢 |
数据大小在声明周期必须确定 | 分配的内存由java 虚拟机自动垃圾回收器管理。动态分配内存大小 |
共享特性,栈中如果有字符串,则直接引用;如果没有,开辟新的空间存入值 | 每new一次都在堆内存中生成一个新的对象。不存在任何复用 |
package com.demo.test;
import java.lang.reflect.Field;
public class StringDemo {
public static void main(String[] args) {
//先在内存中查找有没有这个字符串对象存在,如果存在就指向这个字符串对象;
String str1 = "abc";
String str2 = "abc";
/*
public String toString() {
return this;
}
*/
String str3 = "abc".toString();
//不论内存中是否已经存在这个字符串对象,都会新建一个对象。
String str4 = new String("abc");
String str5 = new String("abc");
String str6 = str5;
String str7 = "a" + "b" + "c";
String str8 = "a" + "b" + new String("c");
//String是不可变字符串对象,StringBuilder和StringBuffer是可变字符串对象(其内部的字符数组长度可变),StringBuffer线程安全,StringBuilder非线程安全
String str9 = new StringBuilder().append("a").append("b").append("c").toString();
String str10 = new StringBuffer().append("a").append("b").append("c").toString();
System.out.println("--------> ==");
System.out.println("---> 1");
System.out.println(str1==str2);//true
System.out.println("---> 3");
System.out.println(str3==str1);//true
System.out.println("---> 4");
System.out.println(str4==str1);//false
System.out.println(str4==str3);//false
System.out.println(str4==str5);//false
System.out.println(str4==str6);//false
System.out.println("---> 7");
System.out.println(str7==str1);//true
System.out.println(str7==str3);//true
System.out.println(str7==str4);//false
System.out.println("---> 8");
System.out.println(str8==str1);//false
System.out.println(str8==str3);//false
System.out.println(str8==str4);//false
System.out.println(str8==str7);//false
System.out.println("---> 9");
System.out.println(str9==str1);//false
System.out.println(str9==str3);//false
System.out.println(str9==str4);//false
System.out.println(str9==str7);//false
System.out.println(str9==str8);//false
System.out.println("---> 10");
System.out.println(str10==str1);//false
System.out.println(str10==str3);//false
System.out.println(str10==str4);//false
System.out.println(str10==str7);//false
System.out.println(str10==str8);//false
System.out.println(str10==str9);//false
System.out.println("--------> equals");
System.out.println(str1.equals(str4));//true
System.out.println(str1.equals(str7));//true
System.out.println(str1.equals(str8));//true
System.out.println("--------> hashCode");
/*
hashCode计算公式: s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
因此hashCode都是一样的,而且是每次运行都一样
*/
System.out.println(str1.hashCode());//96354
System.out.println(str2.hashCode());
System.out.println(str3.hashCode());
System.out.println(str4.hashCode());
System.out.println(str5.hashCode());
System.out.println(str6.hashCode());
System.out.println(str7.hashCode());
System.out.println("--------> normal change value");
//String是不可变类,string只是指向堆内存中的引用,存储的是对象在堆中的地址,而非对象本身,给string赋值只是改变其引用对象而非对象本身
str6 = "123";
System.out.println(str5);//abc
System.out.println(str6);//123
System.out.println("--------> reflect change value");
/*
如果非要改变String的值,也不是不可行。只能使用反射了。
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
// The value is used for character storage.
private final char value[];
……
}
*/
str6 = str5;
try {
Field field = String.class.getDeclaredField("value");
// Field field = str6.getClass().getDeclaredField("value");
if(!field.isAccessible()) {
field.setAccessible(true);
}
char[] value = (char[])field.get(str6);
value[0] = '0';
System.out.println(str5);//0bc
System.out.println(str6);//0bc
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
System.out.println("--------> obj.toString()");
Object obj = new Object();
/*
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
*/
System.out.println(obj.toString());//java.lang.Object@15db9742
String[] arr1 = {"0"};
String[] arr2 = {"0"};
System.out.println(arr1.equals(arr2));//false
}
}
总结
如果String指向的是一个字符串常量,那么会先在字符串常量池(栈)中查找,如果有就直接指向它;没有则在字符串常量池中创建该常量,然后String指向该常量。
如果String使用关键字new初始化,则会在堆中开辟固定的空间存放该字符串的值,然后String指向该常量。
使用字符串常量拼接,由于表达式先计算右值,因此相当于将String指向一个新拼接好的字符串常量。同样会在字符串常量池中查找,如果有就直接指向它;没有则在字符串常量池中创建该常量,然后String指向该常量。但是如果拼接中存在使用new生成的字符串,则新的字符串就等价于使用new在堆中创建的一样。
修改String的值,只能改变String的指向,不会改变String指向的对象本身。如果非要改变指向的对象本身,可以使用反射。
如果是数组,由于它是对象,那么equals只比较其数组指针地址,并不会去比较其中的元素是否相等。
感谢各位的阅读!关于“Java中String如何创建对象”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。