java面试题总结
java基础
1 Java 中多态的实现原理
多态机制包括静态多态(编译时多态)和动态多态(运行时多态)
- 静态多态比如说重载,动态多态一般指在运行时才能确定调用哪个方法。
- 我们通常所说的多态一般指运行时多态,也就编译时不确定究竟调用哪个具体方法,一直等到运行时才能确定。
- 多态实现方式:子类继承父类(extends)和类实现接口(implements)
- 多态核心之处就在于对父类方法的改写或对接口方法的实现,以取得在运行时不同的执行效果。
- Java 里对象方法的调用:依靠类信息里的方法表实现的,对象方法引用调用和接口方法引用调用的大致思想一样的。当调用对象的某个方法时,JVM 查找该对象类的方法表以确定该方法的直接引用地址,有了地址后才真正调用该方法。
2 String、StringBuffer、StringBuilder
关于String、StringBuffer、StringBuilder的区别,我想从四个角度来说明。
第一个,可变性。
String内部的value值是final修饰的,所以它是不可变类。所以每次修改String的值,都会产生一个新的对象。
StringBuffer和StringBuilder是可变类,字符串的变更不会产生新的对象。
第二个,线程安全性。
String是不可变类,所以它是线程安全的。
StringBuffer是线程安全的,因为它每个操作方法都加了synchronized同步关键字。
StringBuilder不是线程安全的,所以在多线程环境下对字符串进行操作,应该使用StringBuffer,否则使用StringBuilder
第三个,性能方面。
String的性能是最低的,因为不可变意味着在做字符串拼接和修改的时候,需要重新创建新的对象以及分配内存。
其次是StringBuffer要比String性能高,因为它的可变性使得字符串可以直接被修改。最后是StringBuilder,它比StringBuffer的性能高,因为StringBuffer加了同步锁。
第四个,存储方面。
String存储在字符串常量池里面
StringBuffer和StringBuilder存储在堆内存空间。
最后再补充一下,StringBuilder和StringBuffer都是派生自AbstractStringBuilder这个抽象类。
3 Comparable和Comparator
一、Comparable可以认为是一个内比较器,实现了Comparable接口的类有一个特点,就是这些类是可以和自己比较的,至于具体和另一个实现了Comparable接口的类如何比较,则依赖compareTo方法的实现
package com.awakeyo;
/**
* @author awakeyoyoyo
* @className Person
* @description TODO
* @date 2020-01-15 16:25
*/
public class Person implements Comparable<Person>{
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Person o) {
return this.age-o.age;
}
}
二、Comparator可以认为是是一个外比较器,个人认为有两种情况可以使用实现Comparator接口的方式:
1、一个对象不支持自己和自己比较(没有实现Comparable接口),但是又想对两个对象进行比较
2、一个对象实现了Comparable接口,但是开发者认为compareTo方法中的比较方式并不是自己想要的那种比较方式
Collections.sort(list, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o2.getAge()-o1.getAge();
}
});
list.stream().forEach(e->System.out.println(e.toString()));
4 Integer和int 的区别
Integer和int的区别有很多,我简单说3个方面
Integer的初始值是null,int的初始值是0
Integer存储在堆内存,int类型是直接存储在栈空间
Integer是对象类型,它封装了很多的方法和属性,我们在使用的时候更加灵活。
5 守护线程
守护线程,它是一种专门为用户线程提供服务的线程,它的生命周期依赖于用户线程。
只有JVM中仍然还存在用户线程正在运行的情况下,守护线程才会有存在的意义。
否则,一旦JVM进程结束,那守护线程也会随之结束。
也就是说,守护线程不会阻止JVM的退出。但是用户线程会!
守护线程和用户线程的创建方式是完全相同的,我们只需要调用用户线程里面的setDaemon方法并且设置成true,就表示这个线程是守护线程。
因为守护线程拥有自己结束自己生命的特性,所以它适合用在一些后台的通用服务场景里面。
比如JVM里面的垃圾回收线程,就是典型的使用场景。
这个场景的特殊之处在于,当JVM进程技术的时候,内存回收线程存在的意义也就不存在了。
所以不能因为正在进行垃圾回收导致JVM进程无法技术的问题。
但是守护线程不能用在线程池或者一些IO任务的场景里面,因为一旦JVM退出之后,守护线程也会直接退出。
就会可能导致任务没有执行完或者资源没有正确释放的问题。
6 JDK 动态代理与 cglib 实现的区别
要点
- java 动态代理:利用反射机制生成一个实现代理接口的匿名类,在调用具体方法
前调用 InvokeHandler 来处理。 - cglib 动态代理:利用 asm 开源包,对代理对象类的class 文件加载进来,通过修改其字节码生成子类来处理。
- JDK 动态代理只能对实现了接口的类生成代理,而不能针对类
- cglib 针对类实现代理,主要对指定的类生成一个子类,覆盖其中的方法。因
为是继承,所以该类或方法最好不要声明成 final
区别
第1点:JDK Proxy是实现目标对象的接口,而GGLib是继承目标对象
第2点:JDK Proxy和CGLib都是在运行期生成字节码。
第3点:JJDK Proxy是通过反射调用目标对象的方法,而CGLib是采用FastClass机制来调用
7 浅拷贝和深拷贝
深拷贝和浅拷贝是用来描述对象或者对象数组这种引用数据类型的复制场景的。
浅拷贝,就是只复制某个对象的指针,而不复制对象本身。
这种复制方式意味着两个引用指针指向被复制对象的同一块内存地址。
深拷贝,会完全创建一个一模一样的新对象,新对象和老对象不共享内存,也就意味着对新对象的修改不会影响老对象的值。
在Java里面,无论是深拷贝还是浅拷贝,都需要通过实现Cloneable接口,并实现clone()方法。
然后我们可以在clone()方法里面实现浅拷贝或者深拷贝的逻辑。
实现深拷贝的方法有很多,比如通过序列化的方式实现,也就是把一个对象先序列化一遍,然后再反序列化回来,就会得到一个完整的新对象。
在clone()方法里面重写克隆逻辑,也就是对克隆对象内部的引用变量再进行一次克隆。
如何实现对象克隆?
- 实现 Cloneable 接口,重写 clone() 方法。
- Object 的clone() 方法的浅拷贝,即如果类中属性有自定义引用类型,只拷贝
引用,不拷贝引用指向的对象。 - 对象的属性的Class 也实现 Cloneable 接口,在克隆对象时也手动克隆属性,
完成深拷贝 - 结合序列化(JDK java.io.Serializable 接口、JSON 格式、XML 格式等),完成深拷贝
8 String 类的常用方法都有那些呢?
- indexOf():返回指定字符的索引。
- charAt():返回指定索引处的字符。
- replace():字符串替换。
- trim():去除字符串两端空白。
- split():分割字符串,返回一个分割后的字符串数组。
- getBytes():返回字符串的 byte 类型数组。
- length():返回字符串长度。
- toLowerCase():将字符串转成小写字母。
- toUpperCase():将字符串转成大写字符。
- substring():截取字符串。
- equals():字符串比较。
9 设计模式
大致按照模式的应用目标分类,设计模式可以分为创建型模式、结构型模式和行为型模式。
创建型模式,是对对象创建过程的各种问题和解决方案的总结,包括各种工厂模式、单例模式、构建器模式、原型模式。
结构型模式,是针对软件设计结构的总结,关注于类、对象继承、组合方式的实践经验。常见的结构型模式,包括桥接模式、适配器模式、装饰者模式、代理模式、组合模式、外观模式、享元模式等。
行为型模式,是从类或对象之间交互、职责划分等角度总结的模式。
比较常见的行为型模式有策略模式、解释器模式、命令模式、观察者模式、迭代器模式、模板方法模式、访问者模式。
10 Spring中用到了哪些设计模式?
8种设计模式在Spring中的应用:
1、工厂模式。
Spring的BeanFactory类,就是使用了简单工厂模式。它主要提供getBean()方法,用来创建对象的实例;我们见得比较多的ApplicationContext也是继承自BeanFactory。
2、单例模式。
Spring中的Bean默认为singleton单例。我们可以通过配置Bean的作用域scope参数来进行修改。Spring Bean一共有5种内置的作用域,分别是singleton、prototype、request、session、globalSession。
3、装饰器模式。
在Spring中,只要见到以Wrapper命名的类基本都是使用装饰器模式。比如BeanWrapper,用来访问Bean的属性和方法。
4、策略模式。
Spring中Bean的实例化采用的就是策略模式。因为Bean的实例化包含原生对象的实例化,和代理对象的实例化,不同对象实例化的逻辑也不一样,所以实例化策略也不一样,比如SimpleInstantiationStrategy就是Spring中默认的实例化策略。
5、适配器模式。
在Spring,只要是以Adapter命名的类基本都是适配器模式的应用。比如MVC模块中的HandlerAdapter
6、代理模式。
比如AOP模块中的AopProxy,用到了JDK的动态代理和CGLIB字节码生成技术;
7、模板方法模式。
主要用来解决代码重复的问题。Spring提供了非常多的模板类来减少重复代码,基本都是以Template结尾,比如RestTemplate,JmsTemplate,JdbcTemplate。
8、观察者模式。
主要用于当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知,在Spring中一般以Listener结尾,比如ApplicationListener等等
11 object 中定义了哪些方法?
- getClass(); 获取类结构信息
- hashCode() 获取哈希码
- equals(Object) 默认比较对象的地址值是否相等,子类可以重写比较规则
- clone() 用于对象克隆
- toString() 对象转变成字符串
- notify() 多线程中唤醒功能
- notifyAll() 多线程中唤醒所有等待线程的功能
- wait() 让持有对象锁的线程进入等待
- wait(long timeout) 让持有对象锁的线程进入等待,设置超时毫秒数时间
- wait(long timeout, int nanos) 让持有对象锁的线程进入等待,设置超时纳秒
数时间 - finalize() 垃圾回收前执行的方法