【面试】Java面试归纳总结

版权声明:亚里士多德说过两句话:“抄袭是不对的,要自己思考和创造。”和“前一句话我没说过。” https://blog.csdn.net/weixin_43494978/article/details/88679104

“你需要的不是睡眠,而是充实” 3:26am突然脑子里冒出这么一句话,再一次获得了中二少年的无穷力量。还没入职先秃头,秃就秃吧,毕竟聪明才绝顶。

知识块

多线程

算法

  • 排序和查找需要分开看待。

  • 数组、链表是基础,栈和队列深入一些但也不难,树挺重要的,比较重要的树AVL树、红黑树,可以不了解它们的具体实现,但是要知道什么是二叉查找树、什么是平衡树,AVL树和红黑树的区别。

冒泡排序

快速排序

二分查找

常见问题

“总是看着很明白,面对面问出来就不知道怎么回答?是需要背下来准确描述还是只是需要多开口表达,或者需要理解再深一些再久一些?”

Java支持的数据类型有哪些?什么是自动拆装箱?

  • byte,short,int,long,float,double,char,boolean

  • 自动装箱是Java编译器在基本数据类型和对应的对象包装类型之间做的一个转化。比如:把int转化成Integer,double转化成double,等等。反之就是自动拆箱。

  • String是引用类型不是基本类型,引用类型声明的变量是指该变量在内存中实际存储的是一个引用地址,实体在堆中。引用类型包括类、接口、数组等。String类还是final修饰的。而包装类就属于引用类型,自动装箱和拆箱就是基本类型和引用类型之间的转换,至于为什么要转换,因为基本类型转换为引用类型后,就可以new对象,从而调用包装类中封装好的方法进行基本类型之间的转换或者toString(当然用类名直接调用也可以,便于一眼看出该方法是静态的),还有就是如果集合中想存放基本类型,泛型的限定类型只能是对应的包装类型。

重写和重载的区别是什么

什么是临界区

值类型和引用类型的区别是什么

从内存管理的角度,什么是堆什么是栈

内连接和左连接区别

SQL注入是什么

什么是强类型编程语言

线程与进程的关系

进程是执行着的应用程序,而线程是进程内部的一个执行序列。一个进程可以有多个线程。线程又叫做轻量级进程。

“不可变”是什么意思

什么是版本控制

MVC中V代表什么?有什么意义

类和对象的区别是什么

接口和抽象类的区别

链表和数组的主要区别是什么

举一个递归算法的例子

什么是时间复杂度

什么是关联数组

什么是正则表达式

什么是无向图

问题

JDK源码

要想拿高工资,JDK源码不可不读。总结一下比较重要的源码:
1、List、Map、Set实现类的源代码
2、ReentrantLock、AQS的源代码
3、AtomicInteger的实现原理,主要能说清楚CAS机制并且AtomicInteger是如何利用CAS机制实现的
4、线程池的实现原理
5、Object类中的方法以及每个方法的作用

扫描二维码关注公众号,回复: 5986627 查看本文章

这些其实要求蛮高的,学习源码,短时间来说,是一件费事费力的事情,从长远角度来看----这是非常值得的,不仅仅是为了应付面试。

设计模式

单例模式

单例模式核心只需要new一个实例对象的模式,比如数据库连接,在线人数等,一些网站上看到的在线人数统计就是通过单例模式实现的,把一个计时器存放在数据库或者内存中,当有人登陆的时候取出来加一再放回去,有人退出登陆的时候取出来减一再放回去,但是当有两个人同时登陆的时候,会同时取出计数器,同时加一,同时放回去,这样的话数据就会错误,所以需要一个全局变量的对象给全部人使用,只需要new出一个实例对象,这就是单例模式的应用,并且单例模式节省资源,因为它控制了实例对象的个数,并有利于gc回收。

手写单例模式(饿汉和饱汉模式)和工厂模式?

  • 单例饿汉模式://饿汉式单例类.在类初始化时,已经自行实例化
    1 {
    2 public class Singleton
    3 //私有的默认构造子
    4 private Singleton1() {}
    5 //已经自行实例化
    6 private static final Singleton1 single = new Singleton1();
    7 //静态工厂方法
    8 public static Singleton1 getInstance() {
    9 return single;
    10 }
    11 }

  • 懒汉模式://懒汉式单例类.在第一次调用的时候实例化
    2 public class Singleton2 {
    3 //私有的默认构造子
    4 private Singleton2() {}
    5 //注意,这里没有final
    6 private static Singleton2 single=null;
    7 //静态工厂方法
    8 public synchronized static Singleton2 getInstance() {
    9 if (single == null) {
    10 single = new Singleton2();
    11 }
    12 return single;
    13 }
    14 }

  • 工厂模式:
    interface IFactory{
    public IProduct createProduct();}
    Class Factory implements IFactory{
    public IProduct createProduct(){return new Product();}}
    Public class client{
    Public Static void main (String [] args){IFactory factory=new Factory();
    IProduct product=factory.createProduct();
    product.ProductMethod();}}

  • 懒汉模式
    public class Singleton {

    private static Singleton instance = null;
    private Singleton(){}

    public static synchronized Singleton getInstance(){
    //如果还没有被实例化过,就实例化一个,然后返回
    if(instance == null){
    instance = new Singleton();
    }
    return instance;
    }
    }

  • 饿汉模式
    public class Singleton {
    //类加载的时候instance就已经指向了一个实例
    private static Singleton instance = new Singleton();
    private Singleton(){}

    public static Singleton getInstance(){
    return instance;
    }
    }

  • 双重检验锁
    public class Singleton {

    private static Singleton instance = null;
    private Singleton(){}

    public static Singleton getInstance(){
    if(instance == null){
    synchronized (Singleton.class){
    if(instance == null){
    instance = new Singleton();
    }
    }
    }
    return instance;
    }
    }

静态内部类:因为JAVA静态内部类的特性,加载的时候不会加载内部静态类,使用的时候才会加载,而使用的时候类加载又是线程安全的,这就完美达到了效果;

public class Singleton {

private static class SingletonHolder{
    private static Singleton instance = new Singleton();
}

private Singleton(){}

public static Singleton getInstance(){
    return SingletonHolder.instance;
}

}

策略模式

将几个类中公共的方法提取到一个新的类中,从而使扩展更容易,保证代码的可移植性,可维护性强。比如有个需求是写鸭子对象,鸭子有叫,飞,外形这三种方法,如果每个鸭子类都写这三个方法会出现代码的冗余,这时候我们可以把鸭子中的叫,飞,外形这三个方法提取出来,放到鸭父类中,让每个鸭子都继承这个鸭父类,重写这三个方法,这样封装的代码可移植性强,当用户提出新的需求比如鸭子会游泳,那么对于我们oo程序员来讲就非常简单了我们只需要在鸭父类中加一个游泳的方法,让会游泳的鸭子重写游泳方法就可以了。

工厂模式

简单的工厂模式主要是统一提供实例对象的引用,通过工厂模式接口获取实例对象的引用。比如一个登陆功能,后端有三个类,controller类,interface类,实现接口的实现类。当客户端发出一个请求,当请求传到controller类中时,controller获取接口的引用对象,而实现接口的实现类中封装好了登陆的业务逻辑代码。当你需要加一个注册需求的时候只需要在接口类中加一个注册方法,实现类中实现方法,controller获取接口的引用对象即可,不需要改动原来的代码,这种做法是的可拓展性强。

AOP与IOC的概念(即spring的核心)

  • IOC:Spring是开源框架,使用框架可以使我们减少工作量,提高工作效率并且它是分层结构,即相对应的层处理对应的业务逻辑,减少代码的耦合度。而spring的核心是IOC控制反转和AOP面向切面编程。IOC控制反转主要强调的是程序之间的关系是由容器控制的,容器控制对象,控制了对外部资源的获取。而反转即为,在传统的编程中都是由我们创建对象获取依赖对象,而在IOC中是容器帮我们创建对象并注入依赖对象,正是容器帮我们查找和注入对象,对象是被获取,所以叫反转。

  • AOP:面向切面编程,主要是管理系统层的业务,比如日志,权限,事物等。AOP是将封装好的对象剖开,找出其中对多个对象产生影响的公共行为,并将其封装为一个可重用的模块,这个模块被命名为切面(aspect),切面将那些与业务逻辑无关,却被业务模块共同调用的逻辑提取并封装起来,减少了系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。

多线程

  • 说说对多线程的理解

a)一个进程是一个独立的运行环境,可以看做是一个程序,而线程可以看做是进程的一个任务,比如QQ是一个进程,而一个QQ窗口是一个线程。

b)在多线程程序中,多线程并发可以提高程序的效率,cpu不会因为某个线程等待资源而进入空闲状态,它会把资源让给其他的线程。

c)用户线程就是我们开发程序是创建的线程,而守护线程为系统线程,如JVM虚拟中的GC

d)线程的优先级别:每一个线程都有优先级别,有限级别高的可以先获取CPU资源使该线程从就绪状态转为运行状态。也可以自定义线程的有限级别

e)死锁:至少两个以上线程争取两个以上cpu资源,避免死锁就避免使用嵌套锁,只需要在他们需要同步的地方加锁和避免无限等待

  • [第10题] 进程和线程的区别是什么?

进程是执行着的应用程序,而线程是进程内部的一个执行序列。一个进程可以有多个线程。线程又叫做轻量级进程。

  • [第11题] 创建线程有几种不同的方式?你喜欢哪一种?为什么?

有三种方式可以用来创建线程:
继承Thread类
实现Runnable接口
应用程序可以使用Executor框架来创建线程池
实现Runnable接口这种方式更受欢迎,因为这不需要继承Thread类。在应用设计中已经继承了别的对象的情况下,这需要多继承(而Java不支持多继承),只能实现接口。同时,线程池也是非常高效的,很容易实现和使用。

使用ExecutorService、Callable、Future实现有返回结果的多线程

  • [第12题] 概括的解释下线程的几种可用状态

线程在执行过程中,可以处于下面几种状态:
就绪(Runnable):线程准备运行,不一定立马就能开始执行。
运行中(Running):进程正在执行线程的代码。
等待中(Waiting):线程处于阻塞的状态,等待外部的处理结束。
睡眠中(Sleeping):线程被强制睡眠。
I/O阻塞(Blocked on I/O):等待I/O操作完成。
同步阻塞(Blocked on Synchronization):等待获取锁。
死亡(Dead):线程完成了执行。

  • [第13题] 同步方法和同步代码块的区别是什么?

在Java语言中,每一个对象有一把锁。线程可以使用synchronized关键字来获取对象上的锁。synchronized关键字可应用在方法级别(粗粒度锁)或者是代码块级别(细粒度锁)。

  • [第14题] synchronized关键字的用法,优缺点

答:java关键字,当它用来修饰一个方法或者代码块的时候,能够保证在同一时刻最多只有一个线程执行该代码段的代码;

synchronized修饰的方法或者对象,只能以同步的方式执行,会引起性能问题;无法中断一个正在等候获得锁的线程,也无法通过投票获得锁;一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能风险;

  • [第15题] Lock接口有哪些实现类,使用场景是什么

答:Lock接口有三个实现类,一个是ReentrantLock,另两个是ReentrantReadWriteLock类中的两个静态内部类ReadLock和WriteLock。

使用场景:一般应用于多度少写,因为读的线程之间没有竞争,所以比起synchronzied,性能要好很多;

  • [第16题] ABC三个线程如何保证顺序执行

答:用Thread.join() 方法,或者线程池newSingleThreadExecutor(原理是会将所有线程放入一个队列,而队列则保证了FIFO),也可以通过ReentrantLock,state整数用阿里判断轮到谁来执行
[第17题] 在监视器(Monitor)内部,是如何做线程同步的?程序应该做哪种级别的同步?
监视器和锁在Java虚拟机中是一块使用的。监视器监视一块同步代码块,确保一次只有一个线程执行同步代码块。每一个监视器都和一个对象引用相关联。线程在获取锁之前不允许执行同步代码。

  • [第18题] 悲观锁、乐观锁的优缺点,CAS有什么缺陷,该如何解决

悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次拿数据的时候都会上锁,这样别人拿数据的时候就会阻塞知道它拿到锁;比如关系型数据库的行锁、表锁、读锁、写锁;比如java里面的同步原语synchronized关键字的实现也是悲观锁;

乐观锁:每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下再次期间别人有没有更新这个数据。乐观锁适用于多读的应用类型,可以提高吞吐量。java中java.util.conncurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的;

CAS:CAS是乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其他线程都失败,失败的线程不会被挂起,而是被告知这次竞争失败,并可以再次尝试;

CAS的缺陷:ABA问题、循环时间长开销大,只能保证一个共享变量的原子操作;

  • [第19题] ABC三个线程如何保证顺序执行

答:用Thread.join() 方法,或者线程池newSingleThreadExecutor(原理是会将所有线程放入一个队列,而队列则保证了FIFO),也可以通过ReentrantLock,state整数用阿里判断轮到谁来执行
[第20题] 假如有Thread1、Thread2、Thread3、Thread4四条线程分别统计C、D、E、F四个盘的大小,所有线程都统计完毕交给Thread5线程去做汇总,应当如何实现?

  • [第21题] 线程池的问题

另外,线程池也是比较常问的一块,常用的线程池有几种?这几种线程池之间有什么区别和联系?线程池的实现原理是怎么样的?实际一些的,会给你一些具体的场景,让你回答这种场景该使用什么样的线程池比较合适。

  • [第22题] 线程的状态都有哪些(五大状态)

新建状态(new):当用new操作符创建一个线程时,如new Thread(),线程还没有开始运行,此时处于仙剑状态;

就绪状态(runnable):一个新创建的线程并不自动开始运行,要执行线程,必须要调用线程的start()方法,当线程对象调用start()方法即启动了线程,start()方法创建线程运行的系统资源,并调度线程运行run()方法。当start()方法返回后,线程就处于就绪状态;

运行状态(running):当线程获得cpu时间后,他才进入运行状态,真正开始实行run()方法

阻塞状态(blocked):当线程运行过程中,可能由于各种原因进入阻塞状态;

a.线程通过调用sleep方法进入睡眠状态

b.线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者

c.线程试图得到一个锁,而该锁正被其他线程持有

d.线程正等待某个触发条件

死亡状态(dead):run方法自然退出而自然死亡,或者一个未捕获的异常终止了run方法而使线程猝死

  • [第23题] 什么是死锁(deadlock)?

两个进程都在等待对方执行完毕才能继续往下执行的时候就发生了死锁。结果就是两个进程都陷入了无限的等待中。

  • [第24题] 如何确保N个线程可以访问N个资源同时又不导致死锁?

使用多线程的时候,一种非常简单的避免死锁的方式就是:指定获取锁的顺序,并强制线程按照指定的顺序获取锁。因此,如果所有的线程都是以同样的顺序加锁和释放锁,就不会出现死锁了。

  • [第25题] 什么是线程局部变量?

线程局部变量是局限于线程内部的变量,属于线程自身所有,不在多个线程间共享。Java 提供 ThreadLocal 类来支持线程局部变量,是一种实现线程安全的方式。但是在管理环境下(如 web 服务器)使用线程局部变量的时候要特别小心,在这种情况下,工作线程的生命周期比任何应用变量的生命周期都要长。任何线程局部变量一旦在工作完成后没有释放,Java 应用就存在内存泄露的风险。

  • [第26题] 用 wait-notify 写一段代码来解决生产者-消费者问题?

请参考答案中的示例代码。只要记住在同步块中调用 wait() 和 notify()方法,如果阻塞,通过循环来测试等待条件。

【生产者】

package com.edu.chapter03.test;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Producer implements Runnable {

private final Vector sharedQueue; 
private final int SIZE; 
 
public Producer(Vector sharedQueue, int size) { 
    this.sharedQueue = sharedQueue; 
    this.SIZE = size; 
} 

@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 7; i++) {
System.out.println(“Produced:” + i);
try {
produce(i);
} catch (InterruptedException ex) {
Logger.getLogger(Producer.class.getName()).log(Level.SEVERE, null, ex);
}
}
}

private void produce(int i) throws InterruptedException { 
     
    //wait if queue is full 
    while (sharedQueue.size() == SIZE) { 
        synchronized (sharedQueue) { 
            System.out.println("Queue is full " + Thread.currentThread().getName() 
                    + " is waiting , size: " + sharedQueue.size()); 
            sharedQueue.wait(); 
        } 
    } 
     
    //producing element and notify consumers 
    synchronized (sharedQueue) { 
        sharedQueue.add(i); 
        sharedQueue.notifyAll(); 
    } 
} 

}

【消费者】

package com.edu.chapter03.test;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Consumer implements Runnable {

private final Vector sharedQueue; 
private final int SIZE; 
 
public Consumer(Vector sharedQueue, int size) { 
    this.sharedQueue = sharedQueue; 
    this.SIZE = size; 
} 

@Override 
public void run() { 
    // TODO Auto-generated method stub 
    while (true) { 
        try { 
            System.out.println("Consumer: " + consume()); 
            Thread.sleep(50); 
        } catch (InterruptedException ex) { 
            Logger.getLogger(Consumer.class.getName()).log(Level.SEVERE, null, ex); 
        } 
    } 
} 
 
private int consume() throws InterruptedException { 
     
    //wait if queue is empty 
    while (sharedQueue.isEmpty()) { 
        synchronized (sharedQueue) { 
            System.out.println("Queue is empty " + Thread.currentThread().getName() 
                    + " is waiting , size: " + sharedQueue.size()); 
            sharedQueue.wait(); 
        } 
    } 
     
    //otherwise consume element and notify waiting producer 
    synchronized (sharedQueue) { 
        sharedQueue.notifyAll(); 
        return (Integer) sharedQueue.remove(0); 
    } 
} 

}
【测试函数】

package com.edu.chapter03.test;
import java.util.Vector;

public class ProducerConsumerSolution {

public static void main(String[] args) { 
    Vector sharedQueue = new Vector(); 
    int size = 4; 
    Thread prodThread = new Thread(new Producer(sharedQueue, size), "Producer"); 
    Thread consThread = new Thread(new Consumer(sharedQueue, size), "Consumer"); 
    prodThread.start(); 
    consThread.start(); 
} 

}

[第27题] 用 Java 写一个线程安全的单例模式(Singleton)?

请参考答案中的示例代码,这里面一步一步教你创建一个线程安全的 Java 单例类。当我们说线程安全时,意思是即使初始化是在多线程环境中,仍然能保证单个实例。Java 中,使用枚举作为单例类是最简单的方式来创建线程安全单例模式的方式。

立即加载/饿汉式:

【在调用方法前,实例就已经被创建】

package com.weishiyao.learn.day8.singleton.ep1;

public class MyObject {
// 立即加载方式==恶汉模式
private static MyObject myObject = new MyObject();

private MyObject() {
}

public static MyObject getInstance() {
// 此代码版本为立即加载
// 此版本代码的缺点是不能有其他实例变量
// 因为getInstance()方法没有同步
// 所以有可能出现非线程安全的问题
return myObject;
}
}
【创建线程类】

package com.weishiyao.learn.day8.singleton.ep1;

public class MyThread extends Thread {
@Override
public void run() {
System.out.println(MyObject.getInstance().hashCode());
}
}
【创建运行类】
package com.weishiyao.learn.day8.singleton.ep1;

public class Run {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
t1.start();
t2.start();
t3.start();
}
}
延迟加载/懒汉式

【建对象的实例】

package com.weishiyao.learn.day8.singleton.ep2;

public class MyObject {
private static MyObject myObject;

private MyObject() {

}

public static MyObject getInstance() {
// 延迟加载
if (myObject != null) {

} else {
  myObject = new MyObject();
}
return myObject;

}
}
【创建线程类】

package com.weishiyao.learn.day8.singleton.ep2;

public class MyThread extends Thread {
@Override
public void run() {
System.out.println(MyObject.getInstance().hashCode());
}
}
【创建运行类】

package com.weishiyao.learn.day8.singleton.ep2;

public class Run {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start();
}
}
【运行测试类】

package com.weishiyao.learn.day8.singleton.ep2;

public class Run {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
MyThread t4 = new MyThread();
MyThread t5 = new MyThread();
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}

  • [第28题] Java 中 sleep 方法和 wait 方法的区别?

虽然两者都是用来暂停当前运行的线程,但是 sleep() 实际上只是短暂停顿,因为它不会释放锁,而 wait() 意味着条件等待,这就是为什么该方法要释放锁,因为只有这样,其他等待的线程才能在满足条件时获取到该锁。

答:首先,sleep()方法属于Thread类的,而wait()方法是属于Object类的;sleep()方法导致了程序暂停执行指定的时间,让出cpu给其他线程,但是他的监控状态依然保持,当指定的时间到了又自动回恢复运行状态,调用了sleep()方法的过程中,线程不会释放对象锁;而当调用了wait()方法的时候,线程回放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备。

  • [第29题] notify()和notifyAll()的区别

答:notify()方法表示,当前线程已经放弃对资源的占有,通知等待的线程来获取对资源的占有权,但是只有一个线程能够从wait状态中恢复;notifyAll()方法表示,当前的线程已经放弃对资源的占有,通知所有的等待线程从wait()方法后的语句开始执行,但最终只有一个线程能竞争获得锁并执行;notify()是对notifyAll()的一个优化,

  • [第30题] ThreadLocal的了解,实现原理。

ThreadLocal,线程本地变量。定义了一个ThreadLocal,每个线程往这个ThreadLocal中读写都是线程隔离的,互相之间不会影响,他提供了一种将可变数据通过每个线程有自己的独立副本从而实现线程封闭的机制;实现的思路,Thread类有一个类型为ThreadLocal.ThreadLocalMap的实例变量threadLocals,也就是说每个线程都有一个自己的ThreadLocalMap。ThreadLocalMap有自己的独立实现,可以简单的将它的key视作ThreadLocal,value为代码中放入的值(实际上key并不是ThreadLocal本省,而是它的一个弱引用)。每个线程在往ThreadLocal里set值的时候,都会往自己的ThreadLocalMap里存,读也是已某个ThreadLocal作为引用,在自己的map里找对应的key,从而实现了线程的隔离。

  • [第31题] Java 中 ++ 操作符是线程安全的吗?

不是线程安全的操作。它涉及到多个指令,如读取变量值,增加,然后存储回内存,这个过程可能会出现多个线程交差。

操作系统

Java web

高赞文章

  1. 【优秀】做了很多外链,全面而清晰
    https://blog.csdn.net/qq_24095055/article/details/88529066
  2. 【推荐】 牛客网Java题总结120题精简版:
    https://blog.csdn.net/u012456528/article/details/79759487
  3. 【推荐】 总结了Java面试大全但是转载并没有标明原作者:
    https://blog.csdn.net/baidu_35684456/article/details/80248801
  4. 两个自称2019最新的面试题:
    基础:
    https://blog.csdn.net/qq_17321295/article/details/86237385
    流程:(不行,答案很不规范,题目算全面)https://blog.csdn.net/crazymakercircle/article/details/82555359
  5. 常见简单问题:
    https://blog.csdn.net/linzhiqiang0316/article/details/80473906

富强民主文明和谐

  • 用脑真的很容易饿

猜你喜欢

转载自blog.csdn.net/weixin_43494978/article/details/88679104