Java基础面试
整型 byte 占用1个字节
short 占用两个字节
int 占用4个字节
long 占用8个字节
浮点型 float 占用4个字节
double 占用8个字节
字符型 char 占用2 个字节
布尔型 Boolean 占用1个字节
String 类可以被继承吗
不能 因为String类是被final修饰的类
使用final修饰方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了。
String StringBuild StringBuffer 的区别
线程安全性
String中的对象是不可变的,所以是线程安全的
StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的
StringBuilder 并没有对方法进行加锁,所以是线程不安全的
三者在性能方面的比较:StringBuilder > StringBuffer > String
如果要操作少量的数据用 String
单线程操作字符串缓冲区下操作大量数据用 StringBuilder
多线程操作字符串缓冲区下操作大量数据用 StringBuffer
线程不安全性能更高,所以我们在开发中优先使用stringBuilder
什么是悲观锁,乐观锁
说一说你了解的集合
ArraryList 底层是object数组 存储有序可重复 最常用的 线程不安全 效率高,查询快增删慢
LinkedList 底层是双向链表 存储无序不可重复 线程不安全 效率高 增删快,查询慢
TreeSet 底层是树结构 存储有序
HashSet 无序不可重复 底层是使用hashMap 使用map 的key值来进行存储 通过hashcode和equals
HashMap 1.7 底层是数组加链表 key存储无序可以重复 可以为null value可以重复,Map 的key 是根据哈希和当前数组的长度计算出来的,所以它是无序的,如果key值相等,会做equals判断,如果相同则覆盖保存
1.8 之后底层是数组加链表加红黑树 存储无序键是唯一
ConcurrentHashMap 是线程线程安全的 1.7底层采用的分段式锁机制 1.8 是菜用CAS机制和synchronized同步代码块形式保证线程安全
Java1.5 引入泛型是解决了什么问题
将运行期遇到的问题转移到了编译期 省去了类型强转的麻烦 提高程序安全性
线程创建的方式有哪些?
一、继承Thread类创建线程类
(1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。
(2)创建Thread子类的实例,即创建了线程对象。
(3)调用线程对象的start()方法来启动该线程。
二、通过Runnable接口创建线程类
(1)定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
(2)创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
(3)调用线程对象的start()方法来启动该线程。
三、通过Callable和Future创建线程
(1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
(2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
(3)使用FutureTask对象作为Thread对象的target创建并启动新线程。
(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
线程启动的方式,sleep和wait的区别, 哪个需要被手动唤醒
1、sleep是Thread类的静态本地方法,wait则是Object类的本地方法
2、sleep方法不会释放lock,但是wait会释放,而且会加入到等待队列中
3、sleep方法不依赖与同步器synchronized ,但是wait需要依赖synchronized关键字
4、sleep不需要被唤醒(休眠之后推出阻塞),但是wait需要(不指定时间需要被别人打断)
5、sleep一般用于当前线程休眠,或者轮询暂停操作,wait则多用于多线程之间的通讯