点击上方“Java知音”,选择“置顶公众号”
技术文章第一时间送达!
即将到来金三银四人才招聘的高峰期,渴望跳槽的朋友肯定跟我一样四处找以往的面试题,但又感觉找的又不完整,在这里我将把我所见到的题目做一总结,并尽力将答案术语化、标准化。预祝大家面试顺利。
另:如果觉得本文有用,欢迎点好看或者分享出去!
术语会让你的面试更有说服力,让你感觉更踏实,建议大家多记背点术语。
1. 简单说下什么是跨平台
术语:操作系统指令集、屏蔽系统之间的差异
由于各种操作系统所支持的指令集不是完全一致,所以在操作系统之上加个虚拟机可以来提供统一接口,屏蔽系统之间的差异。
2. Java有几种基本数据类型
有八种基本数据类型。
各自占用几字节也记一下。
3. 面向对象特征
面向对象的编程语言有封装、继承 、抽象、多态等4个主要的特征。
4. 为什么要有包装类型
术语:让基本类型也具有对象的特征
为了让基本类型也具有对象的特征,就出现了包装类型(如我们在使用集合类型Collection时就一定要使用包装类型而非基本类型)因为容器都是装object的,这是就需要这些基本类型的包装器类了。
自动装箱:
new Integer(6);
,底层调用:
Integer.valueOf(6)
自动拆箱:
int i = new Integer(6);
,底层调用
i.intValue();
方法实现。
Integer i = 6;
Integer j = 6;
System.out.println(i==j);
答案在下面这段代码中找:
public static Integer valueOf(int i) {
if (i = IntegerCache.low && i = IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
二者的区别:
5. ==和equals区别
我们来看看String重写的equals方法:
它不止判断了内存地址,还增加了字符串是否相同的比较。
public boolean equals(Object anObject) {
//判断内存地址是否相同
if (this == anObject) {
return true;
}
// 判断参数类型是否是String类型
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;
}
6. String、StringBuffer和StringBuilder区别
java中String、StringBuffer、StringBuilder是编程中经常使用的字符串类,他们之间的区别也是经常在面试中会问到的问题。现在总结一下,看看他们的不同与相同。
1. 数据可变和不可变
StringBuffer
和
StringBuilder
都继承了
AbstractStringBuilder
底层使用的是可变字符数组:
char[] value;
2. 线程安全
通过他们的
append()
方法来看,
StringBuffer
是有同步锁,而
StringBuilder
没有:
@Override
public synchronized StringBuffer append(Object obj) {
toStringCache = null;
super.append(String.valueOf(obj));
return this;
}
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
3. 相同点
StringBuilder
与
StringBuffer
有公共父类
AbstractStringBuilder
。
最后,操作可变字符串速度:
StringBuilder StringBuffer String
,这个答案就显得不足为奇了。
7. 讲一下Java中的集合
set根据equals和hashcode判断,一个对象要存储在Set中,必须重写equals和hashCode方法
8. ArrayList和LinkedList区别?
之前专门有写过ArrayList和LinkedList源码的文章。
对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
9. ConcurrentModificationException异常出现的原因
public class Test {
public static void main(String[] args) {
ArrayListInteger list = new ArrayListInteger();
list.add(2);
IteratorInteger iterator = list.iterator();
while(iterator.hasNext()){
Integer integer = iterator.next();
if(integer==2)
list.remove(integer);
}
}
}
执行上段代码是有问题的,会抛出
ConcurrentModificationException
异常。
原因:调用
list.remove()
方法导致
modCount
和
expectedModCount
的值不一致。
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
解决办法:在迭代器中如果要删除元素的话,需要调用
Iterator
类的
remove
方法。
public class Test {
public static void main(String[] args) {
ArrayListInteger list = new ArrayListInteger();
list.add(2);
IteratorInteger iterator = list.iterator();
while(iterator.hasNext()){
Integer integer = iterator.next();
if(integer==2)
iterator.remove(); //注意这个地方
}
}
}
10. HashMap和HashTable、ConcurrentHashMap区别?
相同点:
都可以存储key-value数据
不同点:
HashMap线程不安全,效率高。HashTable线程安全,效率低。
什么是fail-fast? 就是最快的时间能把错误抛出而不是让程序执行。
10.2 如何保证线程安全又效率高?
Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。
ConcurrentHashMap将整个Map分为N个segment(类似HashTable),可以提供相同的线程安全,但是效率提升N倍,默认N为16。
10.3 我们能否让HashMap同步?
HashMap可以通过下面的语句进行同步:
Map m = Collections.synchronizeMap(hashMap);
11. 拷贝文件的工具类使用字节流还是字符流
答案:字节流
11.1 什么是字节流,什么是字符流?
字节流:传递的是字节(二进制),
字符流:传递的是字符
11.2 答案
我们并不支持下载的文件有没有包含字节流(图片、影像、音源),所以考虑到通用性,我们会用字节流。
12. 线程创建方式
这个之前自己做过总结,也算比较全面。
方法一:继承Thread类,作为线程对象存在(继承Thread对象)
public class CreatThreadDemo1 extends Thread{
/**
* 构造方法: 继承父类方法的Thread(String name);方法
* @param name
*/
public CreatThreadDemo1(String name){
super(name);
}
@Override
public void run() {
while (!interrupted()){
System.out.println(getName()+"线程执行了...");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
CreatThreadDemo1 d1 = new CreatThreadDemo1("first");
CreatThreadDemo1 d2 = new CreatThreadDemo1("second");
d1.start();
d2.start();
d1.interrupt(); //中断第一个线程
}
}
常规方法,不多做介绍了,interrupted方法,是来判断该线程是否被中断。(终止线程不允许用stop方法,该方法不会施放占用的资源。所以我们在设计程序的时候,要按照中断线程的思维去设计,就像上面的代码一样)。
让线程等待的方法
方法二:实现runnable接口,作为线程任务存在
public class CreatThreadDemo2 implements Runnable {
@Override
public void run() {
while (true){
System.out.println("线程执行了...");
}
}
public static void main(String[] args) {
//将线程任务传给线程对象
Thread thread = new Thread(new CreatThreadDemo2());
//启动线程
thread.start();
}
}
Runnable 只是来修饰线程所执行的任务,它不是一个线程对象。想要启动Runnable对象,必须将它放到一个线程对象里。
方法三:匿名内部类创建线程对象
public class CreatThreadDemo3 extends Thread{
public static void main(String[] args) {
//创建无参线程对象
new Thread(){
@Override
public void run() {
System.out.println("线程执行了...");
}
}.start();
//创建带线程任务的线程对象
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程执行了...");
}
}).start();
//创建带线程任务并且重写run方法的线程对象
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("runnable run 线程执行了...");
}
}){
@Override
public void run() {
System.out.println("override run 线程执行了...");
}
}.start();
}
}
创建带线程任务并且重写run方法的线程对象中,为什么只运行了Thread的run方法。我们看看Thread类的源码,
,我们可以看到Thread实现了Runnable接口,而Runnable接口里有一个run方法。
所以,我们最终调用的重写的方法应该是Thread类的run方法。而不是Runnable接口的run方法。
方法四:创建带返回值的线程
public class CreatThreadDemo4 implements Callable {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CreatThreadDemo4 demo4 = new CreatThreadDemo4();
FutureTaskInteger task = new FutureTaskInteger(demo4); //FutureTask最终实现的是runnable接口
Thread thread = new Thread(task);
thread.start();
System.out.println("我可以在这里做点别的业务逻辑...因为FutureTask是提前完成任务");
//拿出线程执行的返回值
Integer result = task.get();
System.out.println("线程中运算的结果为:"+result);
}
//重写Callable接口的call方法
@Override
public Object call() throws Exception {
int result = 1;
System.out.println("业务逻辑计算中...");
Thread.sleep(3000);
return result;
}
}
Callable接口介绍:
public interface CallableV {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
返回指定泛型的call方法。然后调用FutureTask对象的get方法得道call方法的返回值。
方法五:定时器Timer
public class CreatThreadDemo5 {
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("定时器线程执行了...");
}
},0,1000); //延迟0,周期1s
}
}
方法六:线程池创建线程
public class CreatThreadDemo6 {
public static void main(String[] args) {
//创建一个具有10个线程的线程池
ExecutorService threadPool = Executors.newFixedThreadPool(10);
long threadpoolUseTime = System.currentTimeMillis();
for (int i = 0;i10;i++){
threadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程执行了...");
}
});
}
long threadpoolUseTime1 = System.currentTimeMillis();
System.out.println("多线程用时"+(threadpoolUseTime1-threadpoolUseTime));
//销毁线程池
threadPool.shutdown();
threadpoolUseTime = System.currentTimeMillis();
}
}
方法七:利用java8新特性 stream 实现并发
lambda表达式不懂的,可以看看我的java8新特性文章:
java8-lambda:
https://www.jianshu.com/p/3a08dc78a05f
java8-stream:
https://www.jianshu.com/p/ea16d6712a00
public class CreatThreadDemo7 {
public static void main(String[] args) {
ListInteger values = Arrays.asList(10,20,30,40);
//parallel 平行的,并行的
int result = values.parallelStream().mapToInt(p - p*2).sum();
System.out.println(result);
//怎么证明它是并发处理呢
values.parallelStream().forEach(p- System.out.println(p));
}
}
输出:
200 40 10 20 30
怎么证明它是并发处理呢,他们并不是按照顺序输出的 。
文集介绍
该专题分为Java基础、计算机网络、操作系统、数据结构、算法精读、数据库面试题、框架面试题、服务高可用、分布式事务、分布式锁、消息队列等部分,尽量将全网的面试题一网打尽,方便大家手机阅读和收藏。
每篇会精讲18个问题,数量可以商讨,评论区见。
注:该面试题系列文章后续收录至公众号菜单栏,面试季专栏
加入Java知音技术交流,戳这里:
更多Java技术文章,尽在【Java知音】网站。
网址:www.javazhiyin.com ,搜索Java知音可达!
看完本文有收获?请转发分享给更多人
原文始发于微信公众号(Java知音):