Java基础知识(面向对象,Object,String)

目录

1. 面向对象

1.1 面向对象和面向过程的区别

1.2 对象的相等和引用相等的区别

1.3 构造函数的特点?是否可以被override?

特点

是否可被override?

1.4 多态

1.5 接口和抽象类

共同点:

不同点:

1.6 深拷贝,浅拷贝,引用拷贝

2. Object

2.1 ==和equals()的区别

2.2 为什么重写equals()方法时必须重写hashcode()方法?

3. String

3.1 String、StringBuffer、StringBuilder的区别

可变性

线程安全性

性能

总结

3.2 为什么String不可变?

原因:

3.3 字符串拼接使用“+”还是StringBuilder?

原理

3.4 new String()和 " " 创建字符串的区别?

3.5 String的intern方法


1. 面向对象

1.1 面向对象和面向过程的区别

  • 面向过程把解决问题的过程拆成一个个方法,通过一个个方法的执行解决问题。
  • 面向对象会先抽象出对象,然后用对象执行方法的方式解决问题。
  • 面向对象开发的程序一般更易维护、易复用、易扩展。

1.2 对象的相等和引用相等的区别

  • 对象的相等一般比较的是内存中存放的内容是否相等(str1.equals(str2))。
  • 引用相等一般比较的是他们指向的内存地址是否相等。

1.3 构造函数的特点?是否可以被override?

特点

  • 名字与类名相同。
  • 没有返回值,但不能用 void 声明构造函数。
  • 生成类的对象时自动执行,无需调用。

是否可被override?

构造方法不能被 override(重写),但是可以 overload(重载),所以你可以看到一个类中有多个构造函数的情况。

1.4 多态

多态,顾名思义,表示一个对象具有多种的状态,具体表现为父类的引用指向子类的实例。

特点:

  • 对象类型和引用类型之间具有继承(类)/实现(接口)的关系;
  • 引用类型变量发出的方法调用的到底是哪个类中的方法,必须在程序运行期间才能确定;
  • 多态不能调用“只在子类存在但在父类不存在”的方法;
  • 如果子类重写了父类的方法,真正执行的是子类覆盖的方法,如果子类没有覆盖父类的方法,执行的是父类的方法。

1.5 接口和抽象类

共同点:

  • 都不能被实例化。
  • 都可以包含抽象方法。
  • 都可以有默认实现的方法(Java 8 可以用 default 关键字在接口中定义默认方法)。

不同点:

  • 接口主要用于对类的行为进行约束,你实现了某个接口就具有了对应的行为。抽象类主要用于代码复用,强调的是所属关系。
  • 一个类只能继承一个类,但是可以实现多个接口。
  • 接口中的成员变量只能是 public static final 类型的,不能被修改且必须有初始值,而抽象类的成员变量默认 default,可在子类中被重新定义,也可被重新赋值。

1.6 深拷贝,浅拷贝,引用拷贝

  • 浅拷贝:浅拷贝会在堆上创建一个新的对象(区别于引用拷贝的一点),不过,如果原对象内部的属性是引用类型的话,浅拷贝会直接复制内部对象的引用地址,也就是说拷贝对象和原对象共用同一个内部对象。
  • 深拷贝:深拷贝会完全复制整个对象,包括这个对象所包含的内部对象。
  • 引用拷贝:两个不同的引用指向同一个对象。

2. Object

2.1 ==和equals()的区别

  • 对于基本数据类型来说,== 比较的是值。
  • 对于引用数据类型来说,== 比较的是对象的内存地址。
  • equals()不能用于判断基本数据类型的变量,只能用来判断两个对象是否相等。equals()方法存在于Object类中,而Object类是所有类的直接或间接父类,因此所有的类都有equals()方法。
  • 我们一般都会在类中重写了equals()方法,以比较两个对象的属性是否相等。

2.2 为什么重写equals()方法时必须重写hashcode()方法?

  • 因为两个相等的对象的 hashCode 值必须是相等。也就是说如果 equals 方法判断两个对象是相等的,那这两个对象的 hashCode 值也要相等。
  • 如果重写 equals() 时没有重写 hashCode() 方法的话就可能会导致 equals 方法判断是相等的两个对象,hashCode 值却不相等。
  • 当向HashMap中插入一个对象时,它会根据该对象的哈希码(通过hashCode()方法生成)计算出一个存储位置(桶)。但是,如果两个对象在逻辑上相等(即equals()方法返回true),但它们的哈希码不同(因为hashCode()方法没有被正确重写),那么这两个对象可能会被放置在HashMap的不同位置。这违反了HashMap的假设,即相等的对象必须具有相同的哈希码。

例子:

public class Person {  
    private String name;  
    private int age;  
  
    // 构造函数、getter和setter方法等...  
  
    @Override  
    public boolean equals(Object obj) {  
        if (this == obj) return true;  
        if (obj == null || getClass() != obj.getClass()) return false;  
        Person person = (Person) obj;  
        return age == person.age &&  
               Objects.equals(name, person.name);  
    }  
  
    @Override  
    public int hashCode() {  
        // 使用 Objects.hash() 方法来组合 name 和 age 字段的哈希码值,从而得到一个唯一的哈希码值
        // 只要 name 和 age 相同,哈希码值就相同
        return Objects.hash(name, age);  
    }  
}

3. String

3.1 String、StringBuffer、StringBuilder的区别

可变性

  • String 是不可变的
  • StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,其中也是使用字符数组保存字符串。这个 AbstractStringBuilder 类还提供了很多修改字符串的方法比如 append 方法。

注:在 Java 9 之后,StringStringBuilder 与 StringBuffer 的实现改用 byte 数组存储字符串。新版的 String 其实支持两个编码方案:Latin-1 和 UTF-16。如果字符串中包含的汉字没有超过 Latin-1 可表示范围内的字符,那就会使用 Latin-1 作为编码方案。Latin-1 编码方案下,byte 占一个字节(8 位),char 占用 2 个字节(16),byte 相较 char 节省一半的内存空间。如果字符串中包含的汉字超过 Latin-1 可表示范围内的字符,byte 和 char 所占用的空间是一样的。

线程安全性

  • String不可变,线程安全
  • StringBuffer 对AbstractStringBuilder中的方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。
  • StringBuilder并没有对方法进行加同步锁,所以是非线程安全

性能

  • 每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。
  • StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。
  • 相同情况下使用 StringBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。

总结

  • 操作少量的数据: 适用 String
  • 单线程操作字符串缓冲区下操作大量数据: 适用 StringBuilder
  • 多线程操作字符串缓冲区下操作大量数据: 适用 StringBuffer

3.2 为什么String不可变?

        final在修饰的变量是引用类型时则不能再指向其他对象。虽然String是被final修饰,但是不是String不可变的真正原因,因为这个数组保存的字符串是可变的。

原因

  • 保存字符串的数组被 final 修饰且为私有的,并且String 类没有提供/暴露修改这个字符串的方法。
  • String 类被 final 修饰导致其不能被继承,进而避免了子类破坏 String 不可变。

3.3 字符串拼接使用“+”还是StringBuilder?

Java 语言本身并不支持运算符重载,“+”和“+=”是专门为 String 类重载过的运算符,也是 Java 中仅有的两个重载过的运算符。

原理

  • “+”的底层仍是调用StringBuilder.append().toString()方法(相当于重新创建了一个String对象),然而在循环内使用“+”进行字符串的拼接的话,存在比较明显的缺陷:编译器不会创建单个 StringBuilder 以复用,会导致创建过多的 StringBuilder 对象
  • 直接使用 StringBuilder 对象进行字符串拼接的话,就不会存在这个问题了。
  • 使用 “+” 进行字符串拼接会产生大量的临时对象的问题在 JDK9 中得到了解决,字符串相加 “+” 改为了用动态方法 makeConcatWithConstants() 来实现,这样就可以放心使用“+” 进行字符串拼接了。

3.4 new String()和 " " 创建字符串的区别?

  • new String() 时是在堆内存中创建了一个新的String对象。
  • " " 进行创建时,会先检查字符串常量池(也在堆中)中是否存在“ ”中的内容的引用。若不存在,则在堆中创建对应的字符串对象并将该字符串对象的引用保存到字符串常量池中。若存在,则直接返回对应的字符串引用。

3.5 String的intern方法

其作用是将指定的字符串对象的引用保存在字符串常量池中,可以简单分为两种情况:

  • 如果字符串常量池中保存了对应的字符串对象的引用,就直接返回该引用。
  • 如果字符串常量池中没有保存了对应的字符串对象的引用,那就在常量池中创建一个指向该字符串对象的引用并返回。

注:intern() 是看String对象的内容,即看new String("Java")中的"Java"是否在字符串常量池中存在。

参考:Java基础常见面试题总结(中) | JavaGuide

  • 48
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值