0
点赞
收藏
分享

微信扫一扫

#yyds干货盘点# Java八股文第二弹

闲云困兽 2022-03-11 阅读 60

String 为什么不可变?

先看下Java8 String类的源码:

public final class String
implements 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
}

String类是final的,它的所有成员变量也都是final的。为什么是final的?

  1. 线程安全。同一个字符串实例可以被多个线程共享,因为字符串不可变,本身就是线程安全的。
  2. 支持hash映射和缓存。因为String的hash值经常会使用到,比如作为 Map 的键,不可变的特性使得 hash 值也不会变,不需要重新计算。
  3. 字符串常量池优化。String对象创建之后,会缓存到字符串常量池中,下次需要创建同样的对象时,可以直接返回缓存的引用。

String, StringBuffer 和 StringBuilder区别

1. 可变性

  • String 不可变
  • StringBuffer 和 StringBuilder 可变

2. 线程安全

  • String 不可变,因此是线程安全的
  • StringBuilder 不是线程安全的
  • StringBuffer 是线程安全的,内部使用 synchronized 进行同步

String 类的常用方法有哪些?

  • indexOf():返回指定字符的索引。
  • charAt():返回指定索引处的字符。
  • replace():字符串替换。
  • trim():去除字符串两端空白。
  • split():分割字符串,返回一个分割后的字符串数组。
  • getBytes():返回字符串的 byte 类型数组。
  • length():返回字符串长度。
  • toLowerCase():将字符串转成小写字母。
  • toUpperCase():将字符串转成大写字符。
  • substring():截取字符串。
  • equals():字符串比较。

new String("dabin")会创建几个对象?

使用这种方式会创建两个字符串对象(前提是字符串常量池中没有 "dabin" 这个字符串对象)。

  • "dabin" 属于字符串字面量,因此编译时期会在字符串常量池中创建一个字符串对象,指向这个 "dabin" 字符串字面量;
  • 使用 new 的方式会在堆中创建一个字符串对象。

什么是字符串常量池?

字符串常量池(String Pool)保存着所有字符串字面量,这些字面量在编译时期就确定。字符串常量池位于堆内存中,专门用来存储字符串常量。在创建字符串时,JVM首先会检查字符串常量池,如果该字符串已经存在池中,则返回其引用,如果不存在,则创建此字符串并放入池中,并返回其引用。

object常用方法有哪些?

Java面试经常会出现的一道题目,Object的常用方法。下面给大家整理一下。
object常用方法有:​​toString()​​、​​equals()​​、​​hashCode()​​、​​clone()​​等。

toString

默认输出对象地址。

public class Person {
private int age;
private String name;

public Person(int age, String name) {
this.age = age;
this.name = name;
}

public static void main(String[] args) {
System.out.println(new Person(18, "程序员大彬").toString());
}
//output
//me.tyson.java.core.Person@4554617c
}

可以重写toString方法,按照重写逻辑输出对象值。

public class Person {
private int age;
private String name;

public Person(int age, String name) {
this.age = age;
this.name = name;
}

@Override
public String toString() {
return name + ":" + age;
}

public static void main(String[] args) {
System.out.println(new Person(18, "程序员大彬").toString());
}
//output
//程序员大彬:18
}

equals

默认比较两个引用变量是否指向同一个对象(内存地址)。

public class Person {
private int age;
private String name;

public Person(int age, String name) {
this.age = age;
this.name = name;
}

public static void main(String[] args) {
String name = "程序员大彬";
Person p1 = new Person(18, name);
Person p2 = new Person(18, name);

System.out.println(p1.equals(p2));
}
//output
//false
}

可以重写equals方法,按照age和name是否相等来判断:

public class Person {
private int age;
private String name;

public Person(int age, String name) {
this.age = age;
this.name = name;
}

@Override
public boolean equals(Object o) {
if (o instanceof Person) {
Person p = (Person) o;
return age == p.age && name.equals(p.name);
}
return false;
}

public static void main(String[] args) {
String name = "程序员大彬";
Person p1 = new Person(18, name);
Person p2 = new Person(18, name);

System.out.println(p1.equals(p2));
}
//output
//true
}

hashCode

将与对象相关的信息映射成一个哈希值,默认的实现hashCode值是根据内存地址换算出来。

public class Cat {
public static void main(String[] args) {
System.out.println(new Cat().hashCode());
}
//out
//1349277854
}

clone

java赋值是复制对象引用,如果我们想要得到一个对象的副本,使用赋值操作是无法达到目的的。Object对象有个clone()方法,实现了对
象中各个属性的复制,但它的可见范围是protected的。

protected native Object clone() throws CloneNotSupportedException;

所以实体类使用克隆的前提是:

  • 实现Cloneable接口,这是一个标记接口,自身没有方法,这应该是一种约定。调用clone方法时,会判断有没有实现Cloneable接口,没有实现Cloneable的话会抛异常CloneNotSupportedException。
  • 覆盖clone()方法,可见性提升为public。
public class Cat implements Cloneable {
private String name;

@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}

public static void main(String[] args) throws CloneNotSupportedException {
Cat c = new Cat();
c.name = "程序员大彬";
Cat cloneCat = (Cat) c.clone();
c.name = "大彬";
System.out.println(cloneCat.name);
}
//output
//程序员大彬
}

getClass

返回此 Object 的运行时类,常用于java反射机制。

public class Person {
private String name;

public Person(String name) {
this.name = name;
}

public static void main(String[] args) {
Person p = new Person("程序员大彬");
Class clz = p.getClass();
System.out.println(clz);
//获取类名
System.out.println(clz.getName());
}
/**
* class com.tyson.basic.Person
* com.tyson.basic.Person
*/
}

wait

当前线程调用对象的wait()方法之后,当前线程会释放对象锁,进入等待状态。等待其他线程调用此对象的notify()/notifyAll()唤醒或者等待超时时间wait(long timeout)自动唤醒。线程需要获取obj对象锁之后才能调用 obj.wait()。

notity

obj.notify()唤醒在此对象上等待的单个线程,选择是任意性的。notifyAll()唤醒在此对象上等待的所有线程。

讲讲深拷贝和浅拷贝?

浅拷贝:拷⻉对象和原始对象的引⽤类型引用同⼀个对象。
以下例子,Cat对象里面有个Person对象,调用clone之后,克隆对象和原对象的Person引用的是同一个对象,这就是浅拷贝。

public class Cat implements Cloneable {
private String name;
private Person owner;

@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}

public static void main(String[] args) throws CloneNotSupportedException {
Cat c = new Cat();
Person p = new Person(18, "程序员大彬");
c.owner = p;

Cat cloneCat = (Cat) c.clone();
p.setName("大彬");
System.out.println(cloneCat.owner.getName());
}
//output
//大彬
}

深拷贝:拷贝对象和原始对象的引用类型引用不同的对象。
以下例子,在clone函数中不仅调用了super.clone,而且调用Person对象的clone方法(Person也要实现Cloneable接口并重写clone方法),从而实现了深拷贝。可以看到,拷贝对象的值不会受到原对象的影响。

public class Cat implements Cloneable {
private String name;
private Person owner;

@Override
protected Object clone() throws CloneNotSupportedException {
Cat c = null;
c = (Cat) super.clone();
c.owner = (Person) owner.clone();//拷贝Person对象
return c;
}

public static void main(String[] args) throws CloneNotSupportedException {
Cat c = new Cat();
Person p = new Person(18, "程序员大彬");
c.owner = p;

Cat cloneCat = (Cat) c.clone();
p.setName("大彬");
System.out.println(cloneCat.owner.getName());
}
//output
//程序员大彬
}

两个对象的hashCode()相同,则 equals()是否也一定为 true?

equals与hashcode的关系:

  1. 如果两个对象调用equals比较返回true,那么它们的hashCode值一定要相同;
  2. 如果两个对象的hashCode相同,它们并不一定相同。

hashcode方法主要是用来提升对象比较的效率,先进行hashcode()的比较,如果不相同,那就不必在进行equals的比较,这样就大大减少了equals比较的次数,当比较对象的数量很大的时候能提升效率。
之所以重写equals()要重写hashcode(),是为了保证equals()方法返回true的情况下hashcode值也要一致,如果重写了equals()没有重写hashcode(),就会出现两个对象相等但hashcode()不相等的情况。这样,当用其中的一个对象作为键保存到hashMap、hashTable或hashSet中,再以另一个对象作为键值去查找他们的时候,则会查找不到。

Java创建对象有几种方式?

Java创建对象有以下几种方式:

  • 用new语句创建对象。
  • 使用反射,使用Class.newInstance()创建对象。
  • 调用对象的clone()方法。
  • 运用反序列化手段,调用java.io.ObjectInputStream对象的readObject()方法。

说说类实例化的顺序

Java中类实例化顺序:

  1. 静态属性,静态代码块。
  2. 普通属性,普通代码块。
  3. 构造方法。
public class LifeCycle {
// 静态属性
private static String staticField = getStaticField();

// 静态代码块
static {
System.out.println(staticField);
System.out.println("静态代码块初始化");
}

// 普通属性
private String field = getField();

// 普通代码块
{
System.out.println(field);
System.out.println("普通代码块初始化");
}

// 构造方法
public LifeCycle() {
System.out.println("构造方法初始化");
}

// 静态方法
public static String getStaticField() {
String statiFiled = "静态属性初始化";
return statiFiled;
}

// 普通方法
public String getField() {
String filed = "普通属性初始化";
return filed;
}

public static void main(String[] argc) {
new LifeCycle();
}

/**
* 静态属性初始化
* 静态代码块初始化
* 普通属性初始化
* 普通代码块初始化
* 构造方法初始化
*/
}

equals和==有什么区别?

  • 对于基本数据类型,==比较的是他们的值。基本数据类型没有equal方法;
  • 对于复合数据类型,==比较的是它们的存放地址(是否是同一个对象)。equals()默认比较地址值,重写的话按照重写逻辑去比较。
举报

相关推荐

Java八股文

java八股文

JAVA八股文

Java基础八股文

java八股文(并发)

Java八股文(Maven)

八股文|Java基础

0 条评论