JAVA后端面试笔记
1.JAVA中==和equals的区别
区别:一个是运算符,一个是方法
==比较变量的值是否相同
①如果比较的对象是基本数据类型,则比较数值是否相等
②如果比较的是引用数据类型,则比较的是对象的内存地址是否相等
equals方法比较对象的内容是否相同
equals方法存在于Object类中,而Object类定义了equals方法
public boolean equals(Object obj) {return (this == obj);}
①如果类未重写equals方法,会调用Object父类中的equals方法(实际使用的也是==操作符)
②如果类重写了equals方法,则调用自己的equals方法(一般是比较对象的内容是否相等)
@SpringBootApplication
public class SpringBootDemoApplication {public static void main(String[] args) {SpringApplication.run(SpringBootDemoApplication.class, args);System.out.println("Hello World");User user1 = new User("18","520");User user2 = new User("18","520");System.out.println(user1.equals(user2));}
}
实体类
@ApiModel(value = "用户实体类")
public class User {@ApiModelProperty(value = "用户姓名")public String userName;@ApiModelProperty(value = "用户密码")public String passWord;public User(String userName, String passWord) {this.userName = userName;this.passWord = passWord;}
}
比较两个对象是否相等,结果如下
Hello World
false
虽然两个变量的内容是一样的,但由于User类没有重写equals方法,导致调用的equals是父类Object的方法,结果会返回false
重写equals可以使用到@Data,相当于@Getter @Setter @RequiredArgsConstructor @ToString @EqualsAndHashCode这5个注解的合集, @EqualsAndHashCode默认是false,表示不调用父类的属性
对添加了@Data的注解的,刚开始测试的操作会返回true,因为user1和user2的内容是一样的
2.String,StringBuffer,StringBuilder区别
2.1 String
String数组是final修饰的,所以线程是安全的,但是不可变的
@SpringBootApplication
public class SpringBootDemoApplication {public static void main(String[] args) {SpringApplication.run(SpringBootDemoApplication.class, args);System.out.println("Hello World");String s1 = "Hello";String s2 = s1;System.out.println("修改前 s1 的身份哈希码:" + System.identityHashCode(s1));System.out.println("修改前 s2 的身份哈希码:" + System.identityHashCode(s2)); // 与 s1 相同s1 += " World";System.out.println("修改后 s1 的身份哈希码:" + System.identityHashCode(s1)); // 新值(新对象)System.out.println("修改后 s2 的身份哈希码:" + System.identityHashCode(s2)); // 旧值(原对象未变)}
}
结果如下,String是引用数据类型,刚开始s1和s2的指向的内容是一样的,所以修改前的身份哈希值是相等的,当s1拼接字符后引用地址发生变化,s1地址身份也发生了变化,但是s2不变
Hello World
修改前 s1 的身份哈希码:1988584481
修改前 s2 的身份哈希码:1988584481
修改后 s1 的身份哈希码:205010614
修改后 s2 的身份哈希码:1988584481
2.2 StringBuffe
可变的,父类AbstractStringBuilder的数组是可变的
方法都用了synchronized,所以线程是安全的
2.3 StringBuilder
可变的,父类AbstractStringBuilder的数组是可变的
线程不安全,无锁,无final修饰符
3.Java之String系列--创建对象的个数及其原理
待梳理透
4.Java之String系列--intern方法的作用及原理
不同版本的jdk对intern方法是有差异的(待梳理)
intern() 方法行为(Java 1.8):
当调用 str.intern() 时,会先检查常量池中是否存在与 str 内容相同的字符串:
- 存在:返回常量池中该字符串的引用。
- 不存在:将 str 的引用 添加到常量池(而非复制对象),并返回 str 本身的引用。
== 比较:
比较的是 对象引用地址,而非字符串内容(内容比较需用 equals())。
@SpringBootApplication
public class SpringBootDemoApplication {public static void main(String[] args) {SpringApplication.run(SpringBootDemoApplication.class, args);String s1 = new String("Hello World");s1.intern();String s2 = "Hello World";System.out.println(s1 == s2);System.out.println("---------------------------------------");String s3 = new String("Hello") + new String("World");s3.intern();String s4 = "HelloWorld";System.out.println(s3 == s4);}
}
false
---------------------------------------
true----- s1 == s2
String s1 = new String("Hello World")
这个操作会创建两个对象
对象1:字符串字面量 "Hello World" 被加入 常量池(首次出现时自动入池)。
对象2:在 堆内存 中创建一个新的 String 对象(s1 指向该对象)。
此时:
常量池中存在 "Hello World"(引用地址记为 P)。
s1 指向堆中新建的对象(引用地址记为 H1)。
s1.intern()
调用intern(),常量池存在"Hello world"即地址P
itern()直接返回P,但未改变s1的指向,H1
String s2 = "Hello World"
直接使用字面量赋值,JVM会优先检查常量池
发现常量池存在"Hello World"地址P,因为s2指向P
s1指向H1(堆中新建对象),s2指向P(常量池的引用),地址不同----- s3 == s4
+ 操作符对字符串对象进行拼接时,底层通过StringBuilder 实现,最终返回一个新的堆对象为("HelloWorld")。
关键的是:此时常量池不存在"HelloWorld"字面量,因为拼接是动态生成,未出现字面量"HelloWorld"
s3指向新建的堆对象(引用地址为H3,内容为"HelloWorld"),此时常量池无引用
s3的intern()
s3调用intern(),但是常量池不存在"HelloWorld",因此会将s3的引用H3添加到常量池
但s3的指向仍是堆对象H3
String s4 = "HelloWorld";
s4使用字面量赋值,此时常量池存在"HelloWorld"的引用,即H3,因此s4直接指向H3
s3和s4的指向均指向H3,结果true
5.Java之String系列--String不可变的含义、原因、好处
5.1 String不可变的含义
String不可变的含义是:将一个已有字符串"123"重新赋值成"456",不是在原内存地址上修改数据,而是重新指向一个新对象,新地址。
5.2 String为什么不可变
String的内部数据是一个char数组,被final修饰的,创建后不可改变。
package java.lang;public final class Stringimplements 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// 其他代码
}
5.3 String不可变的好处
使多线程安全
加快字符串的处理:这也就是一般将String作为Map的Key的原因,处理速度要快过其它的键对象,所以HashMap中的键往往都使用String。
避免安全问题等等