面试题经典版

Posted 蒙面侠1024

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面试题经典版相关的知识,希望对你有一定的参考价值。

面试题

一、JAVA基础

1. ==和 equals 的区别?

  • ==是判断两个变量或实例是不是指向同一个内存空间,equals是判断两个变量或实例所指向的内存空间的值是不是相同
  • ==是指对内存地址进行比较 , equals()是对字符串的内容进行比较
  • ==指引用是否相同, equals()指的是值是否相同

2. 说说抽象类和接口

相同点:

① 抽象类和接口都不能被实例化

② 抽象类和接口都可以定义抽象方法,子类/实现类必须覆写这些抽象方法

不同点:

① 抽象类有构造方法,接口没有构造方法

② 抽象类可以包含普通方法,接口中只能是public abstract修饰抽象方法(Java8之后可以)

③ 抽象类只能单继承,接口可以多继承

④ 抽象类可以定义各种类型的成员变量,接口中只能是public static final修饰的静态常量

抽象类和接口的使用场景

1.抽象类的使用场景

既想约束子类具有共同的行为(但不再乎其如何实现),又想拥有缺省的方法,又能拥有实例变量

如:模板方法设计模式,模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中某些步骤的具体实现。

2.接口的应用场景

① 约束多个实现类具有统一的行为,但是不在乎每个实现类如何具体实现

② 作为能够实现特定功能的标识存在,也可以是什么接口方法都没有的纯粹标识。

③ 实现类需要具备很多不同的功能,但各个功能之间可能没有任何联系。

④ 使用接口的引用调用具体实现类中实现的方法(多态)

3.重写和重载的区别

重载(Overload)是让类以统一的方式处理不同类型数据的一种手段,实质表现就是多个具有不同的参数个数或者类型的同名函数(返回值类型可随意,不能以返回类型作为重载函数的区分标准)同时存在于同一个类中,是一个类中多态性的一种表现(调用方法时通过传递不同参数个数和参数类型来决定具体使用哪个方法的多态性)。

重写(Override)是父类与子类之间的多态性,实质是对父类的函数进行重新定义,如果在子类中定义某方法与其父类有相同的名称和参数则该方法被重写,不过子类函数的访问修饰权限不能小于父类的;若子类中的方法与父类中的某一方法具有相同的方法名、返回类型和参数表,则新方法将覆盖原有的方法,如需父类中原有的方法则可使用 super 关键字。

4.public,protected,private的作用范围

public公共的。权限最大,外界可以引用

private 私有的。只能被本类自己调用,类外都不可以调用,子类也不可以

protected 受保护的。只能被子类(子类可以在其他包下面)或者同一个包下的其他类引用。其他的都不可以

5.常见的异常类有哪些?

java 中,所有的异常都有一个共同的祖先 java.lang 包中的 Throwable 类。Throwable 类有两个重要的子类 Exception(异常)和 Error(错误)。Exception 能被程序本身处理(try-catch), Error 是无法处理的(只能尽量避免)。

Exception 和 Error 二者都是 Java 异常处理的重要子类,各自都包含大量子类。

  • Exception :程序本身可以处理的异常,可以通过 catch 来进行捕获。Exception 又可以分为 受检查异常(必须处理) 和 不受检查异常(可以不处理)。

  • Error :Error 属于程序无法处理的错误 ,我们没办法通过 catch 来进行捕获 。例如,Java 虚拟机运行错误(Virtual MachineError)、虚拟机内存不够错误(OutOfMemoryError)、类定义错误(NoClassDefFoundError)等 。这些异常发生时,Java 虚拟机(JVM)一般会选择线程终止。

6. 什么是反射?有什么作用?

Java 反射,就是在运行状态中

  • 获取任意类的名称、package 信息、所有属性、方法、注解、类型、类加载器、modifiers(public、static)、父类、现实接口等
  • 获取任意对象的属性,并且能改变对象的属性
  • 调用任意对象的方法
  • 判断任意一个对象所属的类
  • 实例化任意一个类的对象

Java 的动态就体现在反射。通过反射我们可以实现动态装配,降低代码的耦合度;动态代理等。反射的过度使用会严重消耗系统资源。

JDK 中 java.lang.Class 类,就是为了实现反射提供的核心类之一。

一个 jvm 中一种 Class 只会被加载一次。

7.你知道java8的新特性吗,请简单介绍一下?

  • Lambda 表达式 − Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中。
  • 方法引用− 方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
  • 默认方法− 默认方法就是一个在接口里面有了一个实现的方法。
  • 新工具− 新的编译工具,如:Nashorn引擎 jjs、 类依赖分析器jdeps。
  • Stream API −新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。
  • Date Time API − 加强对日期与时间的处理。
  • Optional 类 − Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。
  • Nashorn, javascript 引擎 − Java 8提供了一个新的Nashorn javascript引擎,它允许我们在JVM上运行特定的javascript应用。

8.String,StringBuffer和StringBuilder。

相同点:

  • 都可以储存和操作字符串
  • 都使用 final 修饰,不能被继承
  • 提供的 API 相似

区别:

  • String 是只读字符串,String 对象内容是不能被改变的
  • StringBuffer 和 StringBuilder 的字符串对象可以对字符串内容进行修改,在修改后的内存地址不会发生改变
  • StringBuilder 线程不安全;StringBuffer 线程安全
    方法体内没有对字符串的并发操作,且存在大量字符串拼接操作,建议使用 StringBuilder,效率较高。

二、JAVA容器

1. 集合了解吧,说说集合有几大类,分别介绍一下

Java 集合, 也叫作容器,主要是由两大接口派生而来:一个是 Collecton接口,主要用于存放单一元素;另一个是 Map 接口,主要用于存放键值对。对于Collection 接口,下面又有三个主要的子接口:List、Set 和 Queue。

  • List(对付顺序的好帮手): 存储的元素是有序的、可重复的。
  • Set(注重独一无二的性质): 存储的元素是无序的、不可重复的。
  • Queue(实现排队功能的叫号机): 按特定的排队规则来确定先后顺序,存储的元素是有序的、可重复的。
  • Map(用 key 来搜索的专家): 使用键值对(key-value)存储,类似于数学上的函数 y=f(x),“x” 代表 key,“y” 代表 value,key 是无序的、不可重复的,value 是无序的、可重复的,每个键最多映射到一个值。

2. hashMap底层实现了解过吗?具体讲讲

  • HashMap 基于 Hash 算法实现,通过 put(key,value) 存储,get(key) 来获取 value
  • 当传入 key 时,HashMap 会根据 key,调用 hash(Object key) 方法,计算出 hash 值,根据 hash 值将 value 保存在 Node 对象里,Node 对象保存在数组里
  • 当计算出的 hash 值相同时,称之为 hash 冲突,HashMap 的做法是用链表和红黑树存储相同 hash 值的 value
  • 当 hash 冲突的个数:小于等于 8 使用链表;大于 8 且 tab length 大于等于 64 时,使用红黑树解决链表查询慢的问题

ps:

  • 上述是 JDK 1.8 HashMap 的实现原理,并不是每个版本都相同,比如 JDK 1.7 的 HashMap 是基于数组 + 链表实现,所以 hash 冲突时链表的查询效率低
  • hash(Object key) 方法的具体算法是 (h = key.hashCode()) ^ (h >>> 16),经过这样的运算,让计算的 hash 值分布更均匀

3. HashMap 和 hashTable的区别?

JDK 1.8 中 HashMapHashtable 主要区别如下:

  • 线程安全性不同。HashMap 线程不安全;Hashtable 中的方法是 synchronized 的。
  • key、value 是否允许 null。HashMap 的 key 和 value 都是可以是 null,key 只允许一个 null;Hashtable 的 key 和 value 都不可为 null。
  • 迭代器不同。HashMap 的 Iterator 是 fail-fast 迭代器;Hashtable 还使用了 enumerator 迭代器。
  • hash的计算方式不同。HashMap 计算了 hash值;Hashtable 使用了 key 的 hashCode方法。
  • 默认初始大小和扩容方式不同。HashMap 默认初始大小 16,容量必须是 2 的整数次幂,扩容时将容量变为原来的2倍;Hashtable 默认初始大小 11,扩容时将容量变为原来的 2 倍加 1。
  • 是否有 contains 方法。HashMap 没有 contains 方法;Hashtable 包含 contains 方法,类似于 containsValue。
  • 父类不同。HashMap 继承自 AbstractMap;Hashtable 继承自 Dictionary。

4. 说说ConcurrentHashMap的底层实现

ConcurrentHashMap1.7 实现原理
ConcurrentHashMap 采用分段锁设计、将一个大的 HashMap 集合拆分成 n 多个不同的小的 HashTable(Segment),默认的情况下是分成 16 个不同的 Segment,每个Segment 中都有自己独立的 HashEntry<K,V>[] table
数组+Segments 分段锁+HashEntry 链表实现
使用 Lock 锁+CAS 乐观锁+UNSAFE 类
PUT 方法流程

  • 第一次需要计算出:key 出存放在那个 Segment 对象中
  • 还需要计算 key 存放在 Segment 对象中具体 index 位置。

ConcurrentHashMap1.8 实现原理
Put 原理 锁的粒度非常小,对每个数组 index 位置上锁 对 1.7ConcurrentHashMap 实现优化

  1. 取消 segment 分段设计,使用 synchronized 锁
  2. synchronized 在 JDK1.6 开始做了优化 默认实现锁的升级过程

JDK 1.7 到 JDK 1.8 中的 ConcurrentHashMap 最大的改动:

链表上的 Node 超过 8 个改为红黑树,查询复杂度 O(logn)
ReentrantLock 显示锁改为 synchronized,说明 JDK 1.8 中 synchronized 锁性能赶上或超过 ReentrantLock

5. Jdk中map的实现都有什么:

HashMap、TreeMap、Hashtable、LinkedHashMap。

6.JUC包下面的集合类了解哪些

三、多线程

1. java实现多线程的方式有几种?

有4种方式可以用来创建线程:

  • 继承Thread类
class MyThread extends Thread
	public void run()
		System.out.println("线程运行");
	

public class Test
	public static void main(String[] args)
		MyThread thread=new MyThread();
		thread.start();//开启线程
	

  • 实现Runnable接口
class MyThread implements Runnable

	public void run()
		System.out.println("线程运行");
	

public class Test
	public static void main(String[] args)
		MyThread thread=new MyThread();
		Thread t=new Thread(thread);
		t.start();//开启线程
	

  • 还有一种方式是实现Callable接口
    实现Runnable接口这种方式更受欢迎,因为这不需要继承Thread类。在应用设计中已经继承了别的对象的情况下,这需要多继承(而Java不支持多继承),只能实现接口。同时,线程池也是非常高效的,很容易实现和使用。
import java.util.concurrent.*;
public class CallableAndFuture
	//创建线程
	public static class CallableTest implements Callable<String>
		public String call() throws Exception
			return "Hello World";
		
	
	public static void main(String[] args)
		ExecutorService threadPool=Executors.newSingleThreadExecutor();
		//启动线程
		Future<String> future=threadPool.submit(new CallableTest());
		try
			System.out.println("等待线程执行完成");
			System.out.println(future.get());//等待线程结束,并获取返回结果
		
		catch(Exception e)
			e.printStackTrace();
		
	

2. Runnable和Callable有什么区别?

主要区别

  • Runnable 接口 run 方法无返回值;Callable 接口 call 方法有返回值,支持泛型
  • Runnable 接口 run 方法只能抛出运行时异常,且无法捕获处理;Callable 接口 call 方法允许抛出异常,可以获取异常信息

3. 线程和进程的区别

  • 进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即为一个进程的创建、运行以及消亡的过程。

  • 线程是比进程更小的执行单位。一个进程在其执行的过程中可以产生多个线程,多个线程共享进程的堆和方法区内存资源,每个线程都有自己的程序计数器、虚拟机栈和本地方法栈。由于线程共享进程的内存,因此系统产生一个线程或者在多个线程之间切换工作时的负担比进程小得多,线程也称为轻量级进程。

  • 进程和线程最大的区别是,各进程是独立的,而各线程则不一定独立,因为同一进程中的多个线程极有可能会相互影响。线程执行开销小,但不利于资源的管理和保护,进程则相反

4.进程之间的通信方式

1、管道,匿名管道,命名管道
2、信号
3、信号量
4、消息队列
5、共享内存
6、socket

5. 线程间通讯方式

1:同步(synchronized)

2:共享变量(volatile)

2:wait/notify()机制

3:管道通信就是使用java.io.PipedInputStream 和 java.io.PipedOutputStream进行通信

6. java的线程大概有几种状态?

线程在运行的生命周期中的任何时刻只能是5 种不同状态的其中一种。

  • 初始状态(NEW):线程已经构建,尚未启动。
  • 运行状态(RUNNABLE):包括就绪(READY)和运行中(RUNNING)两种状态,统称为运行状态。
  • 阻塞状态(BLOCKED):线程被锁阻塞。
  • 等待状态(WAITING):线程需要等待其他线程做出特定动作(通知或中断)。
  • 终止状态(TERMINATED):当前线程已经执行完毕。

7. sleep 和 wait方法的区别?

  • sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,把执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。

  • wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。

8. 死锁的四个条件?

  • 互斥条件:一个锁一次只能由一个进程占有
  • 不可剥夺条件:一个进程占有的资源在使用完之前不可以被其他进程剥夺,只能由该进程释放之后才能被其他进程获取。
  • 请求和保持条件:一个进程在申请资源的同时保持已经占有的资源不释放。
  • 循环等待条件:同时需要A、B两个资源的进程分别占有了A和B,形成了两个进程都阻塞并等待对方释放资源的状态。

9.线程安全是什么?如何保证线程安全?

当多个线程访问某个方法时,不管你通过怎样的调用方式或者说这些线程如何交替的执行,我们在主程序中不需要去做任何的同步,这个类的结果行为都是我们设想的正确行为,那么我们就可以说这个类是线程安全的。

synchronized关键字,就是用来控制线程同步的,保证我们的线程在多线程环境下,不被多个线程同时执行,确保我们数据的完整性,使用方法一般是加在方法上。

就是我们在需要的时候去手动的获取锁和释放锁,甚至我们还可以中断获取以及超时获取的同步特性,但是从使用上说Lock明显没有synchronized使用起来方便快捷。

10. ThreadLocal有什么作用?有哪些使用场景?

ThreadLocal 是线程本地存储,在每个线程都创建了一个ThreadLocalMap对象,每个线程可以访问自己内部ThreadLocalMal对象内的value. 通过这种方式,避免资源在多线程见共享。

如果使用ThreadLocal管理变量,则每一个使用该变量的线程都会获得该变量的副本
副本之间相互独立,这样每一个线程都可以随意更改自己的变量副本,而不会对其他线程产生影响。

经典的使用场景是为每个线程分配一个 JDBC 连接 Connection。这样就可以保证每个线程的都在各自的 Connection 上进行数据库的操作,不会出现 A 线程关了 B线程正在使用的 Connection; 还有 Session 管理 等问题。

11. synchronized 和 volatile 的区别是什么?

作用:

  • synchronized 表示只有一个线程可以获取作用对象的锁,执行代码,阻塞其他线程。
  • volatile 表示变量在 CPU 的寄存器中是不确定的,必须从主存中读取。保证多线程环境下变量的可见性;禁止指令重排序。

区别:

  • synchronized 可以作用于变量、方法、对象;volatile 只能作用于变量。
  • synchronized 可以保证线程间的有序性(个人猜测是无法保证线程内的有序性,即线程内的代码可能被 CPU 指令重排序)、原子性和可见性;volatile 只保证了可见性和有序性,无法保证原子性。
  • synchronized 线程阻塞,volatile 线程不阻塞。
  • volatile 本质是告诉 jvm 当前变量在寄存器中的值是不安全的需要从内存中读取;sychronized 则是锁定当前变量,只有当前线程可以访问到该变量其他线程被阻塞。
  • volatile 标记的变量不会被编译器优化;synchronized 标记的变量可以被编译器优化。

12什么是线程池?

Java线程池主要用于管理线程组及其运行状态,以便Java虚拟机更好地利用CPU资源。Java线程池的工作原理为:JVM先根据用户的参数创建一定数量的可运行的线程任务,并将其放入队列中,在线程创建后启动这些任务,如果正在运行的线程数量超过了最大线程数量(用户设置的线程池大小),则超出数量的线程排队等候,在有任务执行完毕后,线程池调度器会发现有可用的线程,进而再次从队列中取出任务并执行。
线程池的主要作用是线程复用、线程资源管理、控制操作系统的最大并发数,以保证系统高效(通过线程资源复用实现)且安全( 通过控制最大线程并发数实现)地运行。

13.线程池的参数

corePoolSize(核心线程数)
maxPoolSize(最大线程数)
keepAliveTime(线程存活时间)
unit(时间单位)
workQueue(任务队列)
threadFactory (线程工厂)
handler(任务拒绝策略)

14.线程池的拒绝策略

若线程池中的核心线程数被用完且阻塞队列已排满,则此时线程池的线程资源已耗尽,线程池将没有足够的线程资源执行新的任务。为了保证操作系统的安全,线程池将通过拒绝策略处理新添加的线程任务。JDK内置的拒绝策略有AbortPolicy、CallerRunsPolicy、DiscardOldestPolicy 、DiscardPolicy 这4种,默认的拒绝策略在ThreadPoolExecutor中作为内部类提供。在默认的拒绝策略不能满足应用的需求时,可以自定义拒绝策略。
AbortPolicy
线程池的默认拒绝策略为AbortPolicy,即丢弃任务并抛出RejectedExecutionException异常
CallerRunsPolicy
由调用线程处理该任务,如果任务被拒绝了,则由调用线程(提交任务的线程)直接执行此任务,
DiscardPolicy
丢弃任务,但是不抛出异常。如果线程队列已满,则后续提交的任务都会被丢弃,且是静默丢弃。
DiscardOldestPolicy
丢弃队列最前面的任务,然后重新提交被拒绝的任务。

15.Java中的锁了解哪些

公平锁
    是指多个线程申请锁的顺序来获取锁,类似排队,先来后到。
    在并发环境中,每个线程在获取锁时会查看此锁维护的等待队列,如果为空,或者当前线程是等待队列的第一个,就占有锁,否则就会加入到等待队列中,以后会按照FIFO的规则从队列中取到自己。

非公平锁
    是指多个线程获取锁的顺序不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁;在高并发的情况下,有可能会造成优先级反转或者接现象
    非公平锁比较粗鲁,上来就直接尝试占有锁,如果尝试失败,就再采用类似公平锁那种方式
    
可重入锁(递归锁)
指的是统一线程外层函数获取锁之后,内层递归函数仍然能获取该锁的代码,在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。也就是说,线程可以进入任何一个它已经拥有的锁所同步着的代码块。
ReentrantLock/Synchronized 就是典型的可重入锁,可重入锁最大的作用是避免死锁

自旋锁
    自旋锁(spinlock):是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少上下文切换的消耗,缺点就是循环会消耗CPU

独占锁(写锁)/共享锁(读锁)/互斥锁
独占锁:指该锁一次只能被一个线程所持有。对 ReentrantLock/Synchronized 而言都是独占锁
共享锁:指该锁可被多个线程所持有。对 ReentrantReadWriteLock 其读锁是共享锁,其写锁是独占锁。读锁的共享锁可保证并发读是非常高效的,读写、写读、写写的过程是互斥的。

16. 介绍一下AQS和CAS

AQS 是一个抽象的队列同步器通过维护一个共享资源状态(Volatile Int State) 和一个先进先出(FIFO)的线程等待队列来实现一个多线程访问共享资源的同步框架。

AQS 的原理

AQS为每个共享资源都设置一个共享 资源锁,线程在需要访问共享资源时首先需要获取共享资源锁,如果获取到了共享资源锁,便可以在当前线程中使用该共享资源,如果获取不到,则将该线程放入线程等待队列,等待下一次资源调度,具体的流程如图3-14所示。许多同步类的实现都依赖于AQS,例如常用的ReentrantLock 、Semaphore 和CountDownLatch。

CAS的概念:比较并交换

CAS ( Compare And Swap)指比较并交换。CAS算法CAS(V,E,N)包含3个参数,V表示要更新的变量,E表示预期的值,N表示新值。在且仅在V值等于E值时,才会将V值设为N,如果V值和E值不同,则说明已经有其他线程做了更新,当前线程什么都不做。最后,CAS返回当前V的真实值。

CAS 的特性:乐观锁

CAS操作采用了乐观锁的思想,总是认为自己可以成功完成操作。在有多个线程同时使用CAS操作-一个变量时,只有一个会胜出并成功更新,其余均会失败。失败的线程不会被挂起,仅被告知失败,并且允许再次尝试,当然,也允许失败的线程放弃操作。基于这样的原理,CAS操作即使没有锁,也可以发现其他线程对当前线程的干扰,并进行恰当的处理。

17.什么是ABA问题

对CAS算法的实现有一个重要的前提:需要取出内存中某时刻的数据,然后在下一时刻进行比较、替换,在这个时间差内可能数据已经发生了变化,导致产生ABA问题。ABA问题指第1个线程从内存的V位置取出A,这时第2个线程也从内存中取出A,并将v位置的数据首先修改为B,接着又将v位置的数据修改为A,这时第1个线程在进行CAS操作时会发现在内存中仍然是A,然后第1个线程操作成功。尽管从第1个线程的角度来说,CAS操作是成功的,但在该过程中其实V位置的数据发生了变化,只是第1个线程没有感知到罢了,这在某些应用场景下可能出现过程数据不–致的问题。

部分乐观锁是通过版本号( version)来解决ABA问题的,具体的操作是乐观锁每次在执行数据的修改操作时都会带上一个版本号,在预期的版本号和数据的版本号一致时就可以执行修改操作,并对版本号执行加操作,否则执行失败。因为每次操作的版本号都会随之增加,所以不会出现ABA问题,因为版本号只会增加,不会减少。

四、网络编程

1. TCP和UDP的区别?

TCP,Transmission Control Protocol 的缩写,即传输控制协议。

  • 面向连接,即必须在双方建立可靠连接之后,才会收发数据
  • 信息包头 20 个字节
  • 建立可靠连接需要经过3次握手
  • 断开连接需要经过4次挥手
  • 需要维护连接状态
  • 报文头里面的确认序号、累计确认及超时重传机制能保证不丢包、不重复、按序到达
  • 拥有流量控制及拥塞控制的机制

UDP,User Data Protocol 的缩写,即用户数据报协议。

  • 不建立可靠连接,无需维护连接状态
  • 信息包头 8 个字节
  • 接收端,UDP 把消息段放在队列中,应用程序从队列读消息
  • 不受拥挤控制算法的调节
  • 传送数据的速度受应用软件生成数据的速率、传输带宽、源端和终端主机性能的限制
  • 面向数据报,不保证接收端一定能收到

2. 输入一次url过程,用到哪些协议?

  1. DNS 解析:浏览器查询 DNS,获取域名对应的 IP 地址:具体过程包括浏览器搜索自身的 DNS 缓存、搜索操作系统的 DNS 缓存、读取本地的 Host 文件和向本地 DNS 服务器进行查询等。对于向本地 DNS 服务器进行查询,如果要查询的域名包含在本地配置区域资源中,则返回解析结果给客户机,完成域名解析(此解析具有权威性);如果要查询的域名不由本地 DNS 服务器区域解析,但该服务器已缓存了此网址映射关系,则调用这个 IP 地址映射,完成域名解析(此解析不具有权威性)。如果本地域名服务器并未缓存该网址映射关系,那么将根据其设置发起递归查询或者迭代查询;

  2. TCP 连接:浏览器获得域名对应的 IP 地址以后,浏览器向服务器请求建立链接,发起三次握手;

  3. 发送 HTTP 请求:TCP 连接建立起来后,浏览器向服务器发送 HTTP 请求;

  4. 服务器处理请求并返回 HTTP 报文:服务器接收到这个请求,并根据路径参数映射到特定的请求处理器进行处理,并将处理结果及相应的视图返回给浏览器;

  5. 浏览器解析渲染页面:浏览器解析并渲染视图,若遇到对 js 文件、css 文件及图片等静态资源的引用,则重复上述步骤并向服务器请求这些资源;浏览器根据其请求到的资源、数据渲染页面,最终向用户呈现一个完整的页面。

  6. 连接结束。

3. 说一说HTTP和HTTPS的区别。

  • 安全性上,HTTPS是安全超文本协议,在HTTP基础上有更强的安全性。简单来说,HTTPS是使用TLS/SSL加密的HTTP协议
  • 申请证书上,HTTPS需要使用ca申请证书
  • 传输协议上, HTTP是超文本传输协议,明文传输;HTTPS是具有安全性的 SSL 加密传输协议
  • 连接方式与端口上,http的连接简单,是无状态的,端口是 80; https 在http的基础上使用了ssl协议进行加密传输,端口是 443

4. TCP的三次握手,四次挥手

客户端–发送带有 SYN 标志的数据包–一次握手–服务端
服务端–发送带有 SYN/ACK 标志的数据包–二次握手–客户端
客户端–发送带有带有 ACK 标志的数据包–三次握手–服务端

客户端-发送一个 FIN,用来关闭客户端到服务器的数据传送
服务器-收到这个 FIN,它发回一 个 ACK,确认序号为收到的序号加 1 。和 SYN 一样,一个 FIN 将占用一个序号
服务器-关闭与客户端的连接,发送一个 FIN 给客户端
客户端-发回 ACK 报文确认,并将确认序号设置为收到序号加 1

5. 什么是 Cookie 和 Session ?

什么是 Cookie

HTTP Cookie(也叫 Web Cookie或浏览器 Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。通常,它用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态。Cookie 使基于无状态的 HTTP 协议记录稳定的状态信息成为了可能。

Cookie 主要用于以下三个方面:

  • 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
  • 个性化设置(如用户自定义设置、主题等)
  • 浏览器行为跟踪(如跟踪分析用户行为等)

什么是 Session

Session 代表着服务器和客户端一次会话的过程。Session 对象存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当客户端关闭会话,或者 Session 超时失效时会话结束。

6. Cookie和Session的区别?

  • 作用范围不同,Cookie 保存在客户端(浏览器),Session 保存在服务器端。
  • 存取方式的不同,Cookie 只能保存 ASCII,Session 可以存任意数据类型,一般情况下我们可以在 Session 中保持一些常用变量信息,比如说 UserId 等。
  • 有效期不同,Cookie 可设置为长时间保持,比如我们经常使用的默认登录功能,-Session 一般失效时间较短,客户端关闭或者 Session 超时都会失效。
  • 隐私策略不同,Cookie 存储在客户端,比较容易遭到不法获取,早期有人将用户的登录名和密码存储在 Cookie 中导致信息被窃取;Session 存储在服务端,安全性相对 Cookie 要好一些。
  • 存储大小不同, 单个 Cookie 保存的数据不能超过 4K,Session 可存储数据远高于 Cookie。

7. SQL注入是什么,如何避免SQL注入?

SQL 注入就是在用户输入的字符串中加入 SQL 语句,如果在设计不良的程序中忽略了检查,那么这些注入进去的 SQL 语句就会被数据库服务器误认为是正常的 SQL 语句而运行,攻击者就可以执行计划外的命令或访问未被授权的数据。

SQL注入的原理主要有以下 4 点

  • 恶意拼接查询
  • 利用注释执行非法命令
  • 传入非法参数
  • 添加额外条件

避免SQL注入的一些方法:

  • 限制数据库权限,给用户提供仅仅能够满足其工作的最低权限。
  • 对进入数据库的特殊字符(’”\\尖括号&*;等)转义处理。
  • 提供参数化查询接口,不要直接使用原生SQL。

8.forward和redirct的区别

1.forward是什么
forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容在发给浏览器,浏览器根本不知道服务器发送的内容从哪里来,所以它的地址栏还是原来的地址。

2.redirect是什么
redirect是服务器根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址,所以地址栏显示的是新的URL。所以redirct等于客户端向服务器发出两次request,同时也接收两次response。

3.应用的差别
forward:一般用于用户登录的时候,根据角色转发到响应的模块。

redirect:一般用于用户注销登录时返回和跳转到其他的网站等。

9.什么是Servlet?

Servlet(Servlet Applet),全称Java Servlert .是用Java编写的服务器端程序。其主要功能在与交互式的浏览和修改数据,生成动态Web内容。狭义的servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet的类,一般情况下,人们将Servlet理解为后者。比如HttpServlet类继承自Servlet类,可以利用继承Http Servlet 来实现Http请求,当不是Http请求的时候,也可以定义其他形式的Servlet。

10.常见常态码

200 OK:表示从客户端发送给服务器的请求被正常处理并返回;

204 No Content:表示客户端发送给客户端的请求得到了成功处理,但在返回的响应报文中不含实体的主体部分(没有资源可以返回);

206 Patial Content:表示客户端进行了范围请求,并且服务器成功执行了这部分的GET请求,响应报文中包含由Content-Range指定范围的实体内容。

301 Moved Permanently:永久性重定向,表示请求的资源被分配了新的URL,之后应使用更改的URL;

302 Found:临时性重定向,表示请求的资源被分配了新的URL,希望本次访问使用新的URL;

303 See Other:表示请求的资源被分配了新的URL,应使用GET方法定向获取请求的资源;

304 Not Modified:表示客户端发送附带条件(是指采用GET方法的请求报文中包含if-Match、If-Modified-Since、If-None-Match、If-Range、If-Unmodified-Since中任一首部)的请求时,服务器端允许访问资源,但是请求为满足条件的情况下返回改状态码;

307 Temporary Redirect:临时重定向,与303有着相同的含义,307会遵照浏览器标准不会从POST变成GET;(不同浏览器可能会出现不同的情况);

400 Bad Request:表示请求报文中存在语法错误;

401 Unauthorized:未经许可,需要通过HTTP认证;

403 Forbidden:服务器拒绝该次访问(访问权限出现问题)

404 Not Found:表示服务器上无法找到请求的资源,除此之外,也可以在服务器拒绝请求但不想给拒绝原因时使用;

500 Inter Server Error:表示服务器在执行请求时发生了错误,也有可能是web应用存在的bug或某些临时的错误时;

503 Server Unavailable:表示服务器暂时处于超负载或正在进行停机维护,无法处理请求;

五、JVM

1. 说说JVM内存区域分为几大块,分别讲一下

Java 虚拟机在执行 Java 程序的过程中会把他所管理的内存划分为若干个不同的数据区域:

  • 程序计数器:可以看作是当前线程所执行的字节码文件(class)的行号指示器,它会记录执行痕迹,是每个线程私有的
  • 方法区:主要存储已被虚拟机加载的类的信息、常量、静态变量和即时编译器编译后的代码等数据,该区域是被线程共享的,很少发生垃圾回收
  • :栈是运行时创建的,是线程私有的,生命周期与线程相同,存储声明的变量
  • 本地方法栈:为 native 方法服务,native 方法是一种由非 java 语言实现的 java 方法,与 java 环境外交互,如可以用本地方法与操作系统交互
  • :堆是所有线程共享的一块内存,是在 java 虚拟机启动时创建的,几乎所有对象实例都在此创建,所以经常发生垃圾回收操作

JDK8 之前,Hotspot 中方法区的实现是永久代(Perm)

JDK8 开始使用元空间(Metaspace),以前永久代所有内容的字符串常量移至堆内存,其他内容移至元空间,元空间直接在本地内存分配。

2. Java中类加载过程是什么样的?

类加载的步骤为,加载 -> 验证 -> 准备 -> 解析 -> 初始化。

1、加载:

  • 获取类的二进制字节流
  • 将字节流代表的静态存储结构转化为方法区运行时数据结构
  • 在堆中生成class字节码对象

2、验证:连接过程的第一步,确保 class 文件的字节流中的信息符合当前 JVM 的要求,不会危害 JVM 的安全

3、准备:为类的静态变量分配内存并将其初始化为默认值

4、解析:JVM 将常量池内符号引用替换成直接引用的过程

5、初始化:执行类构造器的初始化的过程

4. JVM 如何确定垃圾对象:

JVM 采用的是可达性分析算法,通过 GC Roots 来判定对象是否存活,从 GC Roots 向下追溯、搜索,会产生 Reference Chain。当一个对象不能和任何一个 GC Root 产生关系时,就判定为垃圾。

软引用和弱引用,也会影响对象的回收。内存不足时会回收软引用对象;GC 时会回收弱引用对象。

5. Java中的垃圾回收算法有哪些?

Java中有四种垃圾回收算法,分别是标记清除法、标记整理法、复制算法、分代收集算法;
标记清除法
第一步:利用可达性去遍历内存,把存活对象和垃圾对象进行标记;
第二步:在遍历一遍,将所有标记的对象回收掉;
特点
效率不行,标记和清除的效率都不高;
标记和清除后会产生大量的不连续的空间分片,
可能会导致之后程序运行的时候需分配大对象而找不到连续分片而不得不触发一次GC;

标记整理法
第一步:利用可达性去遍历内存,把存活对象和垃圾对象进行标记;
第二步:将所有的存活的对象向一段移动,将端边界以外的对象都回收掉;
特点
适用于存活对象多,垃圾少的情况;需要整理的过程,无空间碎片产生;

复制算法
将内存按照容量大小分为大小相等的两块,每次只使用一块,当一块使用完了,就将还存活的对象移到另一块上,然后在把使用过的内存空间移除;
特点
不会产生空间碎片;内存使用率极低;

分代收集算法
根据内存对象的存活周期不同,将内存划分成几块,java虚拟机一般将内存分成新生代和老生代,在新生代中,有大量对象死去和少量对象存活,所以采用复制算法,只需要付出少量存活对象的复制成本就可以完成收集;老年代中因为对象的存活率极高,没有额外的空间对他进行分配担保,所以采用标记清理或者标记整理算法进行回收;

6.常见的垃圾回收器

直达

6. 什么是双亲委派模型?为什么需要双亲委派模型?

双亲委派的意思是如果一个类加载器需要加载类,那么首先它会把这个类请求委派给父类加载器去完成,每一层都是如此。一直递归到顶层,当父加载器无法完成这个请求时,子类才会尝试去加载。

为了防止内存中出现多个相同的字

以上是关于面试题经典版的主要内容,如果未能解决你的问题,请参考以下文章

面试题经典版

面试题经典版

数据结构与算法(Java版) | 几个经典的算法面试题(下)

数据结构与算法(Java版) | 几个经典的算法面试题(下)

数据结构与算法(Java版) | 几个经典的算法面试题(下)

BFS经典面试题——C++版