1、Object
1.1 概念
Object类是所有Java类的祖先,也就是说我们所说的”顶级父类”
它存在于java.lang.Object,这个包不需要我们手动导包
需要注意的是:每个类都使用Object作为超类.所有对象(包括数组)都实现这个类的方法.
在不明确给出超类的情况下,Java会自动把Object类作为要定义类的超类。
1.2 常用方法介绍
toString() —— 本方法用于返回对应对象的字符串表示。
hashCode() —— 本方法用于返回对应对象的哈希码值
小贴士:哈希码值的得出是通过一种算法,意在让不同的对象具有不同的哈希码值,用于区分不同的对象.但是有时候也存在不同对象哈希码值相同的特殊情况,我们称之为”哈希碰撞”现象
equals() —— 本方法用于指示其他某个对象是否与当前对象”相等”。
2、String
2.1 特点
String是一个封装char[]数组的对象,字符串不可变
对于Java中的字符串类
String
而言,字符串的不可变性是通过使用final
修饰类和私有化内部字符数组实现的。因此,可以说既是final
的原因,也是private
的原因。
final
修饰类:String
类被声明为final
,意味着它不能被继承。这样做的原因是为了防止对字符串类进行子类化,因为字符串的不可变性是字符串类设计的核心特性之一。通过将String
类声明为final
,可以确保不会有子类修改和破坏字符串的不可变性。私有化内部字符数组:
String
类内部使用一个私有的char
数组来存储字符串的字符序列,并将其声明为final
。私有化数组意味着其他类无法直接访问和修改该数组,从而保护字符串的内容不被外部代码修改。- 一定要注意:value是一个final类型,不可以修改:即value不能指向新的地址,但是单个字符内容是可以变化的。
2.2 创建String对象的方式
方式一:通过字面量定义的方式
1. String s1="hsp"
2. 通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中
3. 再次使用相同的内容时,会直接访问堆中常量池中存在的对象。
String s1="hsp";
String s2="hsp";
System.out.println(s1==s2);//true
方式二:通过new + 构造器() 的方式
1. String s3 = new String("hsp")
2. 首先,字符串常量池会检查是否存在值为"hsp"的字符串对象。如果字符串常量池中不存在该对象,它将在常量池中创建一个新的字符串对象。无论字符串常量池中是否存在值为"hsp"的字符串对象,使用new
关键字和构造器调用String
类的构造器会在堆内存中创建一个新的字符串对象。这个对象将拥有与常量池中的对象相同的内容,但是它们是两个不同的对象,分别在不同的内存位置上。
结论:
- 常量与常量的拼接结果在常量池,且常量池中不会存在相同内容的常量
- 只要其中有一个结果是变量,结果就在堆中
- 如果拼接的结果调用intern()方法,返回值就在常量池中
public class StringExercise04 {
public static void main(String[] args) {
Person p1 = new Person();
p1.name = "lyedu";
Person p2 = new Person();
p2.name = "lyedu";
System.out.println(p1.name.equals(p2.name)); //比较的内容 true
System.out.println(p1.name == p2.name); //true
//"lyedu"返回的地址:就是在常量池中的地址
//p1.name 指向的也是常量池中的"lyedu" 对应的地址
System.out.println(p1.name == "lyedu"); //true
String s1 = new String("bcde");
String s2 = new String("bcde");
System.out.println(s1 == s2); //false
}
}
class Person {
public String name;
}
3、面试题
题1:String a = "hello" + "abc" 创建了几个对象?
1个,常量与常量的拼接结果在常量池,且常量池中不会存在相同内容的常量。常量相加,看的是池。
题2:String a = "hello"; String b = "abc"; String c = a + b;创建了几个对象?画出内存图?
解析:在不考虑常量池中存在的情况下,创建了4个对象。变量相加,是在堆中。
public class StringExercise05 {
public static void main(String[] args) {
String a = "hello"; //创建a对象 1
String b = "abc"; //创建b对象 2
//解读
//1.先创建一个StringBuilder sb = new StringBuilder(); 3
//2.执行 sb.append("hello");
//3.执行sb.append("abc");
//4.String ret = sb.toString(); 4
//最后其实是 c 指向堆中对象的(String) value[]->池中 "helloabc"
String c = a + b;
String d = "helloabc";
System.out.println(c == d); //false
String e = "hello" + "abc";
System.out.println(d == e);//true
}
}
题3:
public class StringExercise06 {
public static void main(String[] args) {
String s1 = "lyedu";//指向常量池中的"lyedu"
String s2 = "java";//指向常量池中的"java"
String s5 = "lyedujava"; //指向常量池中的"lyedujava"
String s6 = (s1 + s2).intern(); //指向池中的 "lyedujava"
System.out.println(s5 == s6); //true
System.out.println(s5.equals(s6)); //true
}
}
题4:
public class StringExercise07 {
public static void main(String[] args) {
Test1 ex = new Test1();
ex.change(ex.str, ex.ch);
System.out.println(ex.str + " and ");
System.out.println(ex.ch);
}
}
class Test1 {
String str = new String("ly");
final char[] ch = {'j', 'a', 'v', 'a'};
public void change(String str, char ch[]) {
str = "java";
ch[0] = 'h';
}
}
输出结果为:
ly and
hava
题5:String str = new String(“a”) + new String(“b”),创建多少个对象
解析:
对象1:new StringBuilder()
对象2:new String("a")
对象3:常量池中的"a"
对象4:new String("b")
对象5:常量池中的"b"
深入剖析:StringBuilder中的toString():
对象6:new String("ab")
强调一下,toString()的调用,在字符串常量池中,没有生成"ab"。只有在直接使用字符串字面量(如
"abc"
)进行赋值或声明时,才会将字符串对象存储在字符串常量池中。对于动态生成的字符串,如拼接操作的结果,会在堆中创建新的对象。