Java面试题(日常更新)
文章目录
前言
每日一个面试题
一、java中String、StringBuffer、StringBuilder的区别
1.可变与不可变
String类中使用字符数组保存字符串,如下就是,因为有“final”修饰符,所以可以知道string对象是不可变的。
private final char value[];
StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,如下就是,可知这两种对象都是可变的。
char[] value;
2.是否多线程安全
String中的对象是不可变的,也就可以理解为常量,显然线程安全。
AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。
StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。看如下源码:
1 public synchronized StringBuffer reverse() {
2 super.reverse();
3 return this;
4 }
5
6 public int indexOf(String str) {
7 return indexOf(str, 0); //存在 public synchronized int indexOf(String str, int fromIndex) 方法
8 }
StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。
3.为什么尽量用StringBuilder和StringBuffe
进行多个字符串对象拼接时会产生多个中间对象,造成了内存的消耗
StringBuilder和StringBuffe 是为解决上面提到拼接产生太多中间对象的问题而提供的类,我们可以用append或者add方法,把字符串添加到已有序列的末尾或者指定位置。
4.使用StringBuilder与StringBuffer的不同
StringBuilder、StringBuffer的方法都会调用AbstractStringBuilder中的公共方法,如super.append(…)。只是StringBuffer会在方法上加synchronized关键字,进行同步。
最后,如果程序不是多线程的,那么使用StringBuilder效率高于StringBuffer。
jdk1.8及以前String使用的是char数组,jdk1.9及以后使用的是byte数组。
二、int和Integer的不同
- int是Java八大基本数据类型之一;Integer是int的包装类
- int的默认值是0;Integer的默认值的null
- int直接存储数值;Integer实际是对象的引用
- JDK1.5之后自动拆箱、自动装箱。javac替我们自动把装箱转换为Integer.valueOf(),把拆箱替换为Integer.intValue()
- Java定义在装箱的过程中,将-128~127之间的数值进行重复利用,超出这个范围的就会new出一个新的Integer对象
- -128~127之间的数值是保存在静态常量池中的IntegerCache数组中的。
三、Java如何保证集合是线程安全的
在Java中绝大部分的集合像什么ArrayList、HashMap等绝大部分都是线程不安全的。仅有的线程安全的实现,像HashTable、Vector等在性能上又不好。
1.在传统的集合框架:除了Hashtable、Vector等同步容器,他们是将自己的方法都加上了同步锁。
List list = Collections.synchronizedList(new ArrayList<>());
我们可以使用同步包装器创建一个线程安全的容器。但是这种方式用的是非常粗的同步方式,在高并发情况下,性能比较低下。
2.并发包:适合高并发,
//关于map的ConcurrentHashMap ConcurrentHashMap<String,Object> map2 = new ConcurrentHashMap<>(); map2.put("1","我是并发包直接构建的"); map2.put("2","我是线程安全的Map容器,ConcurrentHashMap"); map2.forEach((key,value) ->{ System.out.println("map2:" + key + "," + value); }); //关于list的CopyOnWriteArrayList CopyOnWriteArrayList<Integer> list2 = new CopyOnWriteArrayList<>(); list2.add(67612); list2.add(67362); list2.forEach(list ->{ System.out.println(list); }); /** * 并发包中的线程安全队列 */ //ArrayBlockingQueue ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue(10); arrayBlockingQueue.add("1"); arrayBlockingQueue.add("3"); arrayBlockingQueue.forEach(queue->{ System.out.println(queue); }); }
ConcurrentHashMap如何实现高效地线程安全
ConcurrentHashMap的设计实现是一直都在不断的演化,性能也是在不断的提高。
早期的ConcurrentHashMap,其实现主要是基于:
分离锁。在内部进行分段(Segment),里面则是HashEntry的数组,和HashMap类似,哈希相同的
条目也是以链表的形式存放。不对整个Map加锁,而是为每个Segment加锁。
那么在Java8中,这个有什么变化呢?
在结构上,哈希表+红黑树,虽然仍然有Segment定义,但是仅仅是为了给旧版本兼容。
并且引入了懒加载机制。(JDK1.7一上来就初始化,JDK1.8 在第一次put时才初始化),有效避免了初始化开销。
- 由原先的ReentrantLock替换为Sychronized+CAS:
四、谈谈你知道的设计模式?
设计模式是人们对软件开发中出现相同表征的问题,抽象出的可重复利用的解决方案。在某种程度上,设计模式已经代表了一些特定情况的最佳实践,同时也起到了软件工程师之间沟通的“行话”的作用。理解和掌握典型的设计模式,有利于我们提高沟通、设计的效率和质量。
大致按照模式的应用目标分类,设计模式可以分为创建型模式、结构型模式和行为型模式。
- 创建型模式,是对对象创建过程的各种问题和解决方案的总结,包括各种工厂模式(Factory、Abstract Factory)、单例模式(Singleton)、构建器模式(Builder)、原型模式( Prototype)。
- 结构型模式,是针对软件设计结构的总结,关注于类、对象继承、组合方式的实践经验。常见的结构型模式,包括桥接模式( Bridge)、适配器模式( Adapter)、装饰者模式(Decorator)、代理模式(Proxy)、组合模式(Composite)、外观模式(Facade)、享元模式( Flyweight)等。
- 行为型模式,是从类或对象之间交互、职责划分等角度总结的模式。比较常见的行为型模式有策略模式( Strategy)、解释器模式( Interpreter)、命令模式( Command)、观察者模式( Observer)、迭代器模式( Iterator)、模板方法模式( Template Method)、访问者模式( Visitor)。
单例模式:
public class Singleton {
private static volatile Singleton singleton = null;
private Singleton() {
}
public static Singleton getSingleton() {
if (singleton == null) { // 尽量避免重复进入同步块
synchronized (Singleton.class) { // 同步.class,意味着对同步类方法调用
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
public class Singleton {
private Singleton(){}
public static Singleton getSingleton(){
return Holder.singleton;
}
private static class Holder {
private static Singleton singleton = new Singleton();
}
}
常见的设计模式应用场景:
- AOP 领域则是使用了代理模式、装饰器模式、适配器模式等。
- 各种事件监听器,是观察者模式的典型应用。
- 类似 JdbcTemplate 等则是应用了模板模式。
总结
提示:面试题每日一篇,话说大前端最近两年是真🔥