Java的几个常考的基础面试题
目录
二、String和StringBuffer、StringBuilder的区别?( String为什么是不可变的?)
一、方法重载与重写的区别?
重载:在同一个类中,方法名相同,但 参数的类型,个数,或 顺序不同;
重载方法的返回值 和 权限修饰符可以相同也可以不同;
重载发生在编译时。重写:即指子类重写父类的方法,方法名、参数列表 都必须一样;
子类重写方法的 返回值的范围、抛出异常的范围 都必须小于等于 父类中被重写的方法;
子类重写方法的 权限修饰符范围 必须大于等于 父类中被重写的方法;
若父类中的方法的权限修饰符为 private,那子类就不能重写该方法;
二、String和StringBuffer、StringBuilder的区别?( String为什么是不可变的?)
区别1: 可变性
String对象是不可变的,而StringBuilder和StringBuffer对象是可变的。
原因在于,虽然三者的底层都是使用字符数组保存字符串,
但是 String 中用了final修饰,private final char value[] ,
而 StringBuilder和StringBuffer 都是继承自 AbstractStringBuilder 类,该类中没用final修饰, char[] value ;
String源码:StringBuilder和StringBuffer源码:
区别2:线程安全性
①String对象是不可变的,可以理解为常量,线程安全;
StringBuilder和StringBuffer 都是继承自 AbstractStringBuilder 类,该类定义了一些字符串的基本操作,如append、insert、indexOf
等公共方法。
②StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以 线程安全;
③StringBuilder没有给方法加同步锁,所以 线程不安全;
区别3:性能
String类型改变时,都要生成一个新的String对象,并将指针指向新的String对象,性能一般;
StringBuffer类型改变时,会对StringBuffer对象本身进行操作,而不是去生成新的对象操作,所以性能较高;
StringBuilder操作类型时,相同情况下,会比StringBuffer提高10%~15%的性能,但是要从承担多线程不安全的风险;使用总结:
- 操作少量数据时,用 String;
- 单线程操作 字符串缓冲区下的大量数据,用 StringBuilder;(单线程不存在线程不安全问题,所以不怕线程不安全;)
- 多线程操作 字符串缓冲区下的大量数据,用StringBuffer;(多线程可能存在线程不安全问题,所以用线程安全的StringBuffer)
三、自动装箱与拆箱
装箱:将基本数据类型转换为对应的包装类型;
拆箱:将包装类型转换为对应的基本数据类型;
基本数据类型 与 包装类 对应表:
四、 == 与 equals 的区别
== 是用来判断两个对象是不是同一个对象,即比较的是两个对象的地址;
(基本数据类型比较的是值,引用数据类型比较的是内存地址。)equals 如果没有被重写,底层其实就是 ==
可以顺便看下equals源码:
不过,我们一般不会直接使用equals (直接使用可以用 == 呀~),
一般我们都是使用 重写之后的 equals 。
重写之后的equals 判断的是两个对象的值是否相等;
像String,List,等官方的,其实已经帮我们重写好了 equals ,默认就算比较两个对象的值了;
我们可以顺便看一下String中帮我们重写好的equals方法:
五、说说 final关键字 。
final关键字主要用在三个地方:变量、方法、类
final修饰变量:如果是基本数据类型的变量,则其数值一旦初始化之后就不能修改了;
如果是引用类型的变量,则在对其初始化之后就不能再让其指向其他对象了(但指向的对象的内容是可以变化的。因 为引用的值是一个地址,不变的是地址,而该地址上的内容是可以变的。);
final修饰方法:一是可以锁定方法,防止任何继承类修改它的含义,类中所有的private方法都隐式地指定都为final方法;
二是可以提升效率,不过现在的Java版本已经不需要使用final方法来进行优化了;
final修饰类:表明这个类不能被继承,且该类中的所有成员方法都会被隐式地指定为final方法。
六、Object类的常用方法有哪些?
Object类是所有类的父类,所有类都默认继承了Object类。
Object类实现的方法:
- getClass 方法
final修饰,用于返回当前运行时对象的class对象。- hashCode 方法
用于返回对象的哈希码,主要使用在哈希表中,如 JDK中的HashMap。- equals 方法
用于比较两个对象的内存地址是否相等;
但一般都是使用重写后的equals方法,用于比较两个对象的值是否相等。- clone 方法
用于创建并返回对象的一份拷贝(浅复制);
调用clone方法前需要先实现Cloneable接口并重写clone方法,否则会报CloneNotSupportedException异常。- toString 方法
会返回 类的名字@实例的哈希码的16进制 的字符串;
一般都是使用重写后的toString方法。- notify 方法
final修饰,用于唤醒在该对象上等待的线程,如果有多个线程在等待,则随机唤醒其中一个。- notifyAll 方法
final修饰,用于唤醒在该对象上等待的所有线程。- wait 方法 (3种)
final修饰,用于暂停线程的执行,相比sleep方法,wait方***释放锁;
1. wait() 方***一直暂停线程执行;
2. wait(long timeout) 方***暂停线程,timeout 是超时时间(毫秒);
3. wait(long timeout,int nanos) 方法和 wait(long timeout) 一样,但是超时时间要加上nanos(毫微秒);- finalize 方法
实例被垃圾回收器回收的时候触发的操作
七、键盘输入的常用获取方法?
- Scanner
Scanner input = new Scanner(System.in); String str = input.nextLine(); input.close();
- BufferedReader
BufferedReader input = new BufferedReader(new InputStreamReader(System.in)); String s = input.readLine();
八、接口和抽象类的区别?
- 接口中的方法默认是public,没有具体实现(java8之后可以有默认实现);抽象类中可以有非抽象的方法。
- 接口中的实例变量默认是final类型的,抽象类中则不是;
- 一个类可以实现多个接口,但只能继承一个抽象类;
- 接口的实现类必须实现接口中的所有普通方法;而抽象类的继承类如果也是一个抽象类,可以不需要实现抽象类中的所有抽象方法。
- 接口不能用new实例化,但是可以声明,不过必须引用一个实现该类接口的对象。
从设计层面来看,抽象是对类的抽象,是一种模板设计;接口是对行为的抽象,是一种行为的规范。PS:
在JDK8中,接口也可以定义静态方法,可以直接用接口名调用;实现类和实现是不可以调用的;
如果同时实现两个接口,两个接口中有一样的默认方法,则必须重写该方法,否则会报错。