对象实例在堆内存中,对象引用指向的是对象实例放在栈内存中
【一个对象引用可以执行 0 个或 1 个对象】
【一个对象也可以被 n 个引用指向】
对象相等比较的是内存中存放的内容是否相等
引用相等比较的是指向的内存地址是否相等
吧一个对象的属性隐藏在对象的内部,不让外部对象直接访问内部属性,但可以提供被外部访问的方法来操作属性【就好比,看不到空调里内部的零件,但是可以通过遥控器来操作。如果隐藏了属性且不提供外界访问的方法,那也就是有了空调但没有遥控器,那本身也就毫无意义了】
提取不同类型对象之间的公共部分,提高代码的复用性,节省创建新类的时间,提高开发效率【好比学生,老师登记信息的时候,都会存在姓名,年龄等相同的信息,就可以提取出来相同的信息】
一个对象具有多种形态,代码表示:父类类型指向子类对象
就是浅拷贝的情况下,被拷贝对象中还存在的引用类型和拷贝对象中的引用类型是同一个引用地址
hashCode有什么用?
获取哈希码,确定该某个对象在哈希表中的索引位置,提高检索的效率
为什么要有 hashCode?
提高了执行的速度。比如当吧对象添加到 HashSet 的时候,回先计算该对象的 hashCode 值来判断对象加入的位置,同时与已经存在对象的哈希值来做比较,如果没有相同的哈希值,则会添加。如果发现相同的 hashCode 值,会调用 equals() 来检查 hashCode 相等的对象是否真的相同,如果两者相同,则认为重复,则不会添加。这样就减少了 equals 的次数,提高了执行速度
为什么还会同时提供两个方法?
因为两个对象的 hashCode 值相等并不代表两个对象相等,而如果两个对象的 hashCode 值不相等,就可以直接认为两个对象是不相等的
为什么两个有相同对象的 hashCode 值,也不一定是相等的
因为 hashCode() 也许刚好会让多个对象传回相同的哈希值,所以需要继续使用 equals 来判断两个对象是否相等
因为两个相等的对象 hashCode 值也必须是相等的,就是说如果 equals 判断了两个对象是相等的,而两个对象的 hashCode 也要相等。比如当重写了 equals 却没有重写 hashCode,使用 HashMap,HashSet 的时候,就会出现问题【HashMap、HashSet存储对象位置的时候是根据 hashCode 来存放的,比如第一个对象存入了是正常的,而存储第二个相同对象的时候根据hashCode判断位置的时候,却不会放到第一个对象存入的位置,这样 hashCode 值不同,也就导致了会将相同的对象存入进去】
可变性
String 是不可变的。而 StringBuffer 和 StringBuilder 都继承自 AbstractStringBuilder 类,在该类中也是使用字符数组保存字符串的,但它是可变的。该类还提供了 很多修改字符串的方法【append】
线程安全性
String 中的对象是不可变的,也就是常量,线程是安全的。AbstractStringBuilder 是 StringBuffer 和 StringBuilder 的父类,定义了一些字符串的基本方法。StringBuffer 对方法加了同步锁,所以线程是安全的。StringBuilder 并没有对方法进行同步锁,线程是非安全的
性能
每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后指向新 String 对象。StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的。相同情况下 StringBuilder 要比 StringBuffer 性能更好,但付出的代价就是线程不安全的风险
String 类中使用 final 修饰字符数组来保存字符串,但保存的数组被 final 修饰且为私有,并且 String 类没有提供修改这个字符串的方法,再者就是 String 类被 final 修饰,导致其不能被继承,进而又避免了有子类破坏 String 不可变【在 Java 9 之后,String、StringBuffer、StringBuilder 的实现改用了 byte 数组存储字符串】
新版的 String 支持了两种编码方案:Latin-1 和 UTF-16。如果字符串包含的汉字没有超过 Latin-1 范围内的字符,那就拿 Latin-1 作为编码方案。而在 Latin-1 编码下,byte 占一个字节,char 占用两个字节,byte 相较 char 节省一半的内存空间
字符串对象通过 + 拼接的方式,实际上是 StringBuilder 调用了 append() 方法实现的,拼接完以后调用了 toString 得到新的 String 对象。但在循环内使用拼接的话,会导致创建过多的 StringBuilder 对象【编译器不会创建单个 StringBuilder 复用的情况,意味着每一次循环就会创建一个 StringBuilder 对象】
String 中的 equals 方法是被重写过的,比较的是值是否相等,而 Object 中的 equals 方法比较的是对象的内存地址是否相等
是 JVM 为了提升性能和减少内存消耗搞的一块区域,主要是为了避免字符串的重复创建
如果字符串常量池中不存在 "abc" 字符串,则会首先在字符串常量池中创建,然后在堆内存中创建,因此创建总共两个字符串对象
如果字符串常量池中已经存在 "abc" 字符串,则只会在堆内存中创建一个字符串对象
分两种情况:1.如果字符串常量池中保存了对应的字符串对象,就直接返回该对象2.如果常量池中没有保存对应字符串对象,就在常量池中创建一个指向该字符串对象并返回
// 在堆中创建字符串对象”Java“
// 将字符串对象”Java“的引用保存在字符串常量池中
String s1 = "Java";
// 直接返回字符串常量池中字符串对象”Java“对应的引用
String s2 = s1.intern();
// 在堆中在单独创建一个字符串对象
String s3 = new String("Java");
// 直接返回字符串常量池中字符串对象”Java“对应的引用
String s4 = s3.intern();
// s1 和 s2 指向的是堆中的同一个对象
System.out.println(s1 == s2); // true
// s3 和 s4 指向的是堆中不同的对象
System.out.println(s3 == s4); // false
// s1 和 s4 指向的是堆中的同一个对象
System.out.println(s1 == s4); //true