Java面试题冲刺第一天--基础篇1

目录
  • 面试题1:Java 中操作字符串都有哪些类?它们之间有什么区别? 正经回答:
    • 深入追问:
      • 追问1:这三者在效率上怎么说?
      • 追问2:那StringBuffer和StringBuffer线程安全主要差在哪里呢?
  • 面试题2:请你说一下Error 和 Exception 区别是什么?
    • 正经回答:
  • 面试题3:== 和 equals 的区别是什么
    • 正经回答:
    • 深入追问:
      • 追问1:如果我们不重写equals() 方法,会怎么样?
      • 追问2:重写equals的同时,我们需要重写hashCode()方法么?为什么?
  • 总结

面试题1:Java 中操作字符串都有哪些类?它们之间有什么区别? 正经回答:

操作字符串的类有:StringStringBufferStringBuilder

String 和 StringBuffer、StringBuilder 的区别在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象,而 StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。

而StringBuffer 和 StringBuilder 最大的区别在于,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。

String StringBuffer StringBuilder
类是否可变 不可变(Final) 可变 可变
功能介绍 每次对String的操作都会在“常量池”中生成新的String对象 任何对它指向的字符串的操作都不会产生新的对象。每个StringBuffer对象都有一定的缓冲区容量,字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,自动扩容 功能与StringBuffer相同,相比少了同步锁,执行速度更快
线程安全性 线程安全 线程安全 线程不安全
使用场景推荐 单次操作或循环外操作字符串 多线程操作字符串 单线程操作字符串

深入追问:

追问1:这三者在效率上怎么说?

StringBulider > StringBuffer > String

String <(StringBuffer,StringBuilder)的原因?

  • String:字符串常量
  • StringBuffer:字符串变量(有同步锁)
  • StringBuilder:字符串变量(无同步锁)

从上面的名字可以看到,String是"字符串常量",也就是不可改变的对象。源码如下:

public final class String{}

对于上面这句话的理解你可能会产生这样一个疑问 ,比如这段代码:

String str = "唐伯虎";
str = str + "点香烟";
System.out.print(str); // result : "唐伯虎点香烟"

我们明明改变了String型的变量str啊,为什么说是没有改变呢?我们来看一下这张对String操作时内存变化的图:

我们可以看到,初始String值为"唐伯虎",然后在这个字符串后面加上新的字符串"点香烟",这个过程是需要重新在栈堆内存中开辟内存空间的,最终得到了"唐伯虎点香烟"字符串也相应的需要开辟内存空间,这样短短的两个字符串,却需要开辟三次内存空间,不得不说这是对内存空间的极大浪费,执行效率同理。

为了应对经常性操作字符串的场景,Java才提供了其他两个操作字符串的类 —— StringBuffer、StringBuilder。

他们俩均属于字符串变量,是可改变的对象,每当我们用它们对字符串做操作时,实际上是在一个对象上操作的,这样就不会像String一样创建一些而外的对象进行操作了,速度自然就相对快了。

我们一般在StringBuffer、StringBuild类上的主要操作是 append 和 insert 方法,这些方法允许被重载,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串缓冲区中。append 方法始终将这些字符添加到缓冲区的末端;而 insert 方法则在指定的点(index)添加字符。

  1. StringBuilder一个可变的字符序列是JDK1.5新增的。此类提供一个与 StringBuffer 兼容的 API,但不保证同步。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。如果可能,建议优先采用S
  2. tringBuilder类,因为在大多数实现中,它比 StringBuffer 要快。且两者的方法基本相同。然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。

String 类型和 StringBuffer、 StringBuild类型的主要性能区别其实在于 String 是不可变的对象(final), 因此在每次对 String 类型进行改变的时候其实都等同于在堆中生成了一个新的 String 对象,然后将指针指向新的 String 对象,这样不仅效率低下,而且大量浪费有限的内存空间,所以经常改变内容的字符串最好不要用 String 。因为每次生成对象都会对系统性能产生影响,特别是当内存中的无引用对象过多了以后, JVM 的 GC 开始工作,那速度是一定会相当慢的。另外当GC清理速度跟不上new String的速度时,还会导致内存溢出Error,会直接kill掉主程序!报错如下:

Caused by: java.lang.OutOfMemoryError: GC overhead limit exceeded

Exception in thread "I/O dispatcher 3797236" java.lang.OutOfMemoryError: GC overhead limit exceeded

追问2:那StringBuffer和StringBuffer线程安全主要差在哪里呢?

StringBuffer和StringBuilder可以算是双胞胎了,这两者的方法没有很大区别。但在线程安全性方面,StringBuffer允许多线程进行字符操作。这是因为在源代码中StringBuffer的很多方法都被关键字synchronized 修饰了,而StringBuilder没有。

synchronized的含义:   

每一个类对象都对应一把锁,当某个线程A调用类对象O中的synchronized方法M时,必须获得对象O的锁才能够执行M方法,否则线程A阻塞。一旦线程A开始执行M方法,将独占对象O的锁。使得其它需要调用O对象的M方法的线程阻塞。只有线程A执行完毕,释放锁后。那些阻塞线程才有机会重新调用M方法。这就是解决线程同步问题的锁机制。 >  了解了synchronized的含义以后,大家可能都会有这个感觉。多线程编程中StringBuffer比StringBuilder要安全多了 ,事实确实如此。如果有多个线程需要对同一个字符串缓冲区进行操作的时候,StringBuffer应该是不二选择。

注意:是不是String也不安全呢?事实上不存在这个问题,String是不可变的。线程对于堆中指定的一个String对象只能读取,无法修改。试问:还有什么不安全的呢?

实际应用场景中:

  • 如果不是在循环体中进行字符串拼接的话,直接使用 String 的 “+” 就好了;
  • 单线程循环中操作大量字符串数据 → StringBuilder.append();
  • 多线程循环中操作大量字符串数据 → StringBuffer.append();

面试题2:请你说一下Error 和 Exception 区别是什么?

正经回答:

Error 和 Exception 都是 Throwable 的子类,在Java中只有Throwable类型的实例才可以被抛出或者捕获,它是异常处理机制的基本类型。

  • Exception和Error体现了java平台设计者对不同异常情况的分类,Exception是程序正常运行中,可以预料的意外情况,可能并且应该被捕获,进行相应的处理。
  • Error是指正常情况下,不大可能出现的情况,绝大部分的Error都会导致程序处于非正常的、不可恢复的状态。既然是非正常情况,不便于也不需要捕获。常见的比如OutOfMemoryError之类都是Error的子类。
  • Exception又分为可检查(checked)异常和不可检查(unchecked)异常。可检查异常在源代码里必须显式的进行捕获处理,这是编译期检查的一部分。不可检查时异常是指运行时异常,像NullPointerException、ArrayIndexOutOfBoundsException之类,通常是可以编码避免的逻辑错误,具体根据需要来判断是否需要捕获,并不会在编译期强制要求。

面试题3:== 和 equals 的区别是什么

正经回答:

  • == : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象。(基本数据类型 == 比较的是值,引用数据类型 == 比较的是内存地址)
  • equals(): 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:

情况1:类没有覆盖 equals() 方法。则通过 equals() 比较该类的两个对象时,等价于调用了Object类的equals() 方法,也就是通过“==”比较这两个对象。

// Object类中的equals() 方法
public boolean equals(Object obj) {
    return (this == obj);
}

情况2:类覆盖了 equals() 方法。一般,我们都会覆盖 equals() 方法来两个对象的内容相等;若它们的内容相等,则返回 true (即,认为这两个对象相等)。

// String类中的equals() 方法,已覆盖,用于比较内容
public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

深入追问:

追问1:如果我们不重写equals() 方法,会怎么样?

举例说明:

重点说明:是否重写Object类中的equals方法,会对结果造成的影响

public static void main(String[] args) {
    // 字符串比较
    String a = "陈哈哈";
    String b = "陈哈哈";
    if (a == b) {// true  a==b
        System.out.println("a==b");
    }
    if (a.equals(b)) {// true  a.equals(b)
        System.out.println("a.equals(b)");
    }
    // StringBuffer 对象比较,由于StringBuffer没有重写Object的equal方法,因此结果出现错误
    StringBuffer c = new StringBuffer("陈哈哈");
    StringBuffer d = new StringBuffer("陈哈哈");
    if (c == d) {// false  c != d
        System.out.println("c == d");
    } else {
        System.out.println("c != d");
    }
    if (c.equals(d)) { // false 调用了Object类的equal方法
        System.out.println("StringBuffer equal true");
    }else {
        System.out.println("StringBuffer equal false");
    }
}
  • object的equals方法是比较的对象的内存地址,而String的equals方法比较的是对象的值。
  • 因为String中的equals方法是被重写过的,而StringBuilder没有重写equals方法,从而调用的是Object类的equals方法,也就相当于用了 ==;

追问2:重写equals的同时,我们需要重写hashCode()方法么?为什么?

在重写equals()方法时,也有必要对hashCode()方法进行重写,尤其是当我们自定义一个类,想把该类的实例存储在集合中时。  

hashCode方法的常规约定为:值相同的对象必须有相同的hashCode,也就是equals()结果为相同,那么hashcode也要相同,equals()结果为不相同,那么hashcode也不相同;

当我们使用equals方法比较说明对象相同,但hashCode不同时,就会出现两个hashcode值,比如在HashMap中,就会认为这是两个对象,因此会出现矛盾,说明equals方法和hashCode方法应该成对出现,当我们对equals方法进行重写时,也要对hashCode方法进行重写。

可以通过ide快捷键快速生成两个方法,假设现在有一个学生Student类,其中有 age 和 name 两个特征。生成代码如下:

@Override
public boolean equals(Object o){
//首先比较两个的地址值是否相同,如果相同,那内容也一定相同
	if(this == o) return true;
//如果o为空值或者两个对象的类型是否相同,如果类型不同或者o为空值则内容一定不同
	if(o == null || getClass() != o.getClass()) return false;
//将object类型的实例强转为Student类型
  	Student student = (Student)o;
//比较两个实例的age是否相同
	if(age != student.age) return false;
//在比较name是否相同
	return name != null ? name.equals(student.name ) : student.name == null;
}
@Override
public int hashCode() {
	int result = age;
	result = 31 * result + (name != null ? name.hashCode() : 0);
	return result;
}

总结

本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • Java面试题及答案集锦(基础题122道,代码题19道)

    Java基础面试题及答案集锦(基础题122道,代码题19道),具体详情如下所示: 1.面向对象的特征有哪些方面 1.抽象: 抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面.抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节.抽象包括两个方面,一是过程抽象,二是数据抽象. 2.继承: 继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法.对象的一个新类可以从现有的类中派生,这个过程称为类继承.新类继承了原始类的特性

  • 经典的Java面试题及回答集锦(基础篇)

    1.问题:如果A和B对象循环引用,是否可以被GC? 回答:可以,现在的虚拟机基本都是通过可达性分析算法来判断对象是否存活的,而不是通过简单的引用计数法来判断对象是否存活.可达性分析算法使用一系列的"GC Roots"对象(虚拟机栈中引用的对象.静态属性引用对象)作为起始点,这些节点向下搜索的路径称为引用链,当一个对象到GC Roots没有任何引用链连接,则证明对象是不可用的. 2.Java中的内存溢出是如何造成的? OutOfMemoryError: (1)PerGern Space

  • JAVA基础面试题整理

    1.Java中final.finally.finalize的区别 简单区别: final用于声明属性,方法和类,分别表示属性不可交变,方法不可覆盖,类不可继承. finally是异常处理语句结构的一部分,表示总是执行. finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,供垃圾收集时的其他资源回收,例如关闭文件等. 2.error和exception有什么区别? Error(错误)表示系统级的错误和程序不必处理的异常,是java运行环境中的内部错误或者硬件

  • Java 面试题基础知识集锦

    经典的Java基础面试题集锦,欢迎收藏和分享. 问题:如果main方法被声明为private会怎样? 答案:能正常编译,但运行的时候会提示"main方法不是public的". 问题:Java里的传引用和传值的区别是什么? 答案:传引用是指传递的是地址而不是值本身,传值则是传递值的一份拷贝. 问题:如果要重写一个对象的equals方法,还要考虑什么? 答案:hashCode. 问题:Java的"一次编写,处处运行"是如何实现的? 答案:Java程序会被编译成字节码组成

  • Java基础之Integer使用的注意事项及面试题

    JAVA中Integer对象的引用 JAVA中没有指针一说,但也有引用的概念.这里要说的主要是Integer是不是同一个对象. 1.先看一段代码: public static void main(String[] args){ Integer a1 = 100; Integer b1 = a1;//另一种也可以b1=100 Field field = null; try { field = a1.getClass().getDeclaredField("value"); } catch

  • Java面试题冲刺第一天--基础篇1

    目录 面试题1:Java 中操作字符串都有哪些类?它们之间有什么区别? 正经回答: 深入追问: 追问1:这三者在效率上怎么说? 追问2:那StringBuffer和StringBuffer线程安全主要差在哪里呢? 面试题2:请你说一下Error 和 Exception 区别是什么? 正经回答: 面试题3:== 和 equals 的区别是什么 正经回答: 深入追问: 追问1:如果我们不重写equals() 方法,会怎么样? 追问2:重写equals的同时,我们需要重写hashCode()方法么?为

  • Java面试题冲刺第二天--Redis篇

    目录 面试题1:为什么要用 Redis ?业务在哪块儿用到的? 正经回答: 深入追问: 追问1:Redis里有哪些数据类型? 追问2:Redis与Memcached有哪些区别? 追问3:那Redis怎样防止异常数据不丢失的?如何持久化? 面试题2:Redis为啥是单线程的? 正经回答: 深入追问: 追问1:单线程只使用了单核CPU,太浪费,有什么办法发挥多核CPU的性能嘛? 面试题3:聊一下对缓存穿透.缓存击穿.缓存雪崩的理解吧 正经回答: 深入追问: 追问1:那你说一下针对缓存击穿的解决方法

  • Java面试题冲刺第五天--基础篇2

    目录 面试题1:说一下抽象类和接口有哪些区别? 正经回答: 深入追问: 追问1:说一说你对抽象类的理解吧,他到底是干啥用的 追问2:用抽象类实现一个接口,和普通类实现接口会有什么不同么? 追问3:抽象类能使用 final 修饰吗? 面试题2:final 在 Java 中有什么作用? 正经回答: 1.修饰类 2.修饰方法 3.修饰变量 深入追问: 追问1:能分别说一下final.finally.finalize的区别么? 面试题3:你对Java序列化了解么? 正经回答: 深入追问: 追问1:Jav

  • Java面试题冲刺第十四天--基础篇3

    目录 面试题1:JDK1.8的新特性有哪些? 接口的默认和静态方法: Lambda 表达式: 方法与构造函数引用: 函数式接口: Annotation 注解:支持多重注解: 新的日期时间 API: Base64编码: JavaScript引擎Nashorn: Stream的使用: Optional: 扩展注解的支持: 并行(parallel)数组: 编译器优化: 其他核心 API 的改进 Java IO改进 集合 API 的改进 面试题2:什么是内部类?内部类的作用? 内部类的作用 内部类特点

  • Java面试题冲刺第十七天--基础篇3

    目录 面试题1:JDK1.8的新特性有哪些? 接口的默认和静态方法: Lambda 表达式: 方法与构造函数引用: 函数式接口: Annotation 注解:支持多重注解: 新的日期时间 API: Base64编码: JavaScript引擎Nashorn: Stream的使用: Optional: 扩展注解的支持: 并行(parallel)数组: 编译器优化: 其他核心 API 的改进 Java IO改进 集合 API 的改进 面试题2:什么是内部类?内部类的作用? 内部类的作用 内部类特点

  • Java面试题冲刺第十一天--集合框架篇(2)

    目录 面试题1:说一下 HashMap 的实现原理? 正经回答: 深入追问: 追问1:如何实现HashMap的有序? 追问2:那TreeMap怎么实现有序的? 追问3:put方法原理是怎么实现的? 追问4:HashMap扩容机制原理 追问5:HashMap在JDK1.8都做了哪些优化? 追问6:链表红黑树如何互相转换?阈值多少? 面试题2:HashMap是线程安全的吗? 正经回答: 追问1:你是如何解决这个线程不安全问题的? 总结 面试题1:说一下 HashMap 的实现原理? 正经回答: 众所

  • Java面试题冲刺第三天--集合框架篇

    目录 面试题1:对比 Vector.ArrayList.LinkedList 有何区别?适合在什么场景下使用 正经回答: 深入追问: 追问1:多线程场景下就不能使用ArrayList么? 追问2:说一下 HashSet 的实现原理? 追问3:HashSet是如何保证Key不重复的? 面试题2:List 和 Set 有哪些区别? 正经回答: 面试题3:Array 和 ArrayList 有何区别? 正经回答: 总结 面试题1:对比 Vector.ArrayList.LinkedList 有何区别?

  • Java面试题冲刺第二十一天--JVM

    目录 面试题1:你遇到过哪些OOM情况,什么原因造成的?怎么解决的? Java heap space GC overhead limit exceeded Permgen space Metaspace Unable to create new native thread Out of swap space? Kill process or sacrifice child Requested array size exceeds VM limit Direct buffer memory 面试题

  • Java面试题冲刺第九天--MyBatis2

    目录 面试题1:说说你对Mybatis的理解? 追问1:说一下MyBatis的工作原理和流程吧. 追问2:列举几个MyBatis的核心组件,说说分别干啥用? 面试题2:(问几个实际使用的问题)Mybatis动态sql是做什么的?都有哪些动态sql? 追问1:Xml映射文件中,除了常见的select|insert|updae|delete标签之外,你还常用哪些标签? 追问2:Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式? 追问3:MyBatis中接口绑定你都用过哪几

随机推荐