JAVA面试题

Posted 皓月行空

tags:

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

一、基础部分

1、sleep和wait的区别

sleep是Thread中的方法,wait是Object的方法。

调用sleep正在执行的线程主动让出CPU(然后CPU就可以去执行其他任务),在sleep指定时间后CPU再回到该线程继续往下执行,此时,不会释放对象锁;wait()方法则是指当前线程让自己暂时退让出同步资源锁,以便其他正在等待该资源的线程得到该资源进而运行,只有调用了notify()方法,之前调用wait()的线程才会解除wait状态,可以去参与竞争同步资源锁,进而得到执行。sleep()方法可以在任何地方使用;wait()方法则只能在同步方法或同步块中使用。

2、cookie 和session的区别

cookie数据存放在客户的浏览器上,session数据放在服务器上

理解cookie和session的机制

3、hashMap和treeMap的区别

hashMap维护Entry的数组,插入结果不保证顺序,键类需要明确定义hashCode和equals的方法,允许键值 都是null,可以指定初始容量(16)和增长因子(0.75),

treemap维护Entry的根节点,实现了sortedMap接口,同时使用二叉树结构存储数据,升级版是自平衡,用红黑二叉树实现。避免查找的最坏情况是O(n). 使用了红黑二叉树,则查找的最快情况是O(log n

4、内部类的认知

 

  • 内部类提供了更好的封装,只有外部类能访问内部类
  • 内部类可以独立继承一个接口,不受外部类是否继承接口影响
  • 内部类中的属性和方法即使是外部类也不能直接访问,相反内部类可以直接访问外部类的属性和方法,即使private
  • 利于回调函数的编写

成员内部类:最普通的内部类,成员内部类中不能存在任何static的变量和方法,成员内部类是依附于外围类的,所以只有先创建了外围类才能够创建内部类,这也是内部类不能有static 的原因

方法内部类:方法内部类定义在外部类的方法中,局部内部类和成员内部类基本一致,只是它们的作用域不同,方法内部类只能在该方法中被使用,出了该方法就会失效。 对于这个类的使用主要是应用与解决比较复杂的问题,想创建一个类来辅助我们的解决方案,到那时又不希望这个类是公共可用的,所以就产生了局部内部类。所在的方法如果有参数,必须是final的。

匿名内部类:

    匿名内部类其实就是一个没有名字的方法内部类,所以它符合方法内部类的所有约束,初次之外,还有一些地方需要注意:

  • 匿名内部类是没有访问修饰符的。
  • 匿名内部类必须继承一个抽象类或者实现一个接口
  • 匿名内部类中不能存在任何静态成员或方法
  • 匿名内部类是没有构造方法的,因为它没有类名。

静态内部类:比如HashMap定义的Entry,treeMap定义的entry是 static final 类型的

 

  • 静态内部类的创建是不需要依赖于外围类,可以直接创建
  • 静态内部类不可以使用任何外围类的非static成员变量和方法,而内部类则都可以

 

5、如何理解拆箱装箱

 

装箱就是  自动将基本数据类型转换为包装器类型;拆箱就是  自动将包装器类型转换为基本数据类型。

JAVA为基本数据类型设计包装器类型 是因为基本数据类型不具有对象的特征(继承、封装、多态),另外是为了增加对泛型的支持,毕竟基本数据类型不是Object的子类

6、缓存怎么处理

7、hashset有什么特点  

     hashset 实现了set接口,不允许集合中有重复的值,底层使用的是HashMap,如果有重复的值添加,则返回的是false。

8、异常怎么处理 ,向上抛出或者捕获。常见的异常包括RUNtimeException,ClassNotFound,ClassCast,IllegalArgument,IndexOutOfBund,UnknownType

9、动态代理核心类 Proxy.newInstance  InvocationHandlerinvoke()  MethodInterceptorinterceptor()

10、回调的理解

回调通常和异步组合使用,A 调用B 的方法,一般而言,同步调用的话,则是B 的方法执行完毕,返回处理结果给A ,A 在继续执行,但是如果此时B 的方法是一个特别耗时的操作,那么A 调用B 的方法,不等B 返回结果,A 就继续执行其他的业务,此时B 接收到了A 的调用,B 的方法执行完毕,这时,就会调用A 的方法,完成操作,这里就是用到了回调。

一般在JAVA中使用回调的场景是 实现了Callable接口的类,该接口定义了call方法,可以看做是一种回调。

11、collection 的架构

12、Object 有哪几种 方法 getClass,hashCode,equals,clone,toString,wait,notify,notifyAll,finalize

13、jvm 调优 

xms:初始内存大小
xmx:最大内存大小
xmn:年轻代大小
-XX:newRaito=4 年轻代(Eden和2*Survivor)与老年代的比值 
-XX:SurvivorRatio=8 年轻代  Eden 与一个Survivor的比值
-XX:HeapDumpPath:指定Dump堆内存时的路径
-XX:+HeapDumpOnOutOfMemoryError:当首次遭遇内存溢出时Dump出此时的堆内存,与-XX:HeapDumpPath一起使用。
-XX:+PrintGC:每次GC时打印相关信息,与 -verbose:gc 是一样的,可以认为-verbose:gc 是 -XX:+PrintGC的别名,打印日志稍有差异。
-XX:+PrintGCDetails:每次GC时打印详细信息
-Xloggc:../logs/gc.log 垃圾收集日志文件的输出路径
-XX:PermSize=512M:初始化持久区
-XX:MaxPermSize=2048M:最大类持久区内存池大小;
-XX:MaxDirectMemorySize ---direct byte buffer用到的本地内存
-Xss128k:设置每个线程的堆栈大小
-XX:+UseConcMarkSweepGC
-XX:ParallelGCThreads=8 配置并行收集器的线程数,

-XX:MaxTenuringThreshold=0 设置垃圾最大年龄(在年轻代的存活次数)

14、Overload和Override的区别,构造器可否被override

    重写(Override)也称覆盖,它是父类与子类之间多态性的一种表现,而重载(Overload)是一个类中多态性的一种表现。 override从字面就可以知道,它是覆盖了一个方法并且对其重写,以求达到不同的作用。overload它是指我们可以定义一些名称相同的方法,通过定义不同的输入参数来区分这些方法,然后再调用时,VM就会根据不同的参数样式,来选择合适的方法执行。override是在不同类之间的行为,overload是在同一个类中的行为。

    override 方法被定义为final不能被重写。 被覆盖的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行覆盖。

    overload:

(1)参数类型、个数、顺序至少有一个不相同。 

(2)不能重载只有返回值不同的方法名。 

(3)针对于一个类而言。 

(4)不能通过访问权限、返回类型、抛出的异常进行重载; 

(5)方法的异常类型和数目不会对重载造成影响;

15、访问控制符public protected,private以及default的区别

public 具有公共访问权限,protected 仅限于父类和子类之间的访问权限, private是私有的,内部类可以访问,default是包访问权限。

16、可否继承String类,为什么?为什么设计成final的,

    String类为final的,则不允许覆盖和修改,处于设计安全和效率的考虑,设计成final的。

17、String、StringBuffer和StringBuilder的区别

    String 是不可变的对象,因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,每次结果都会对 StringBuffer 对象本身进行操作,而不是生成新的对象。

String维护一个final修饰的char数组,而StringBuffer是默认的char数组,不带修饰符,StringBuffer的方法是被Synchronized修饰的,StringBuilder是不安全的。

18、hashCode和equals的方法的关系

equals相等,则hashcode相等,hashcode相等,equals并不一定相等

19、抽象类和接口的区别

抽象类是用来捕捉子类的通用特性的 。它不能被实例化,只能被用作子类的超类。抽象类是被用来创建继承层级里子类的模板。

接口是抽象方法的集合。如果一个类实现了某个接口,那么它就继承了这个接口的抽象方法。这就像契约模式,如果实现了这个接口,那么就必须确保使用这些方法。接口只是一种形式,接口自身不能做任何事情。

接口的方法必须是public的,抽象方法可以有public,protected和default修饰。抽象类只能继承一个类和实现多个接口,接口只能继承一个或多个接口,接口定义的变量必须是常量,public static final

20、什么是泛型,为什么要使用以及泛型擦除

泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,以提高代码的重用率。泛型:本质是参数化类型。 

为什么要使用?创建集合的时候,往集合里面添加数据,再次取出时,集合会忘记这数据类型,该对象的编译类型就会变成Object类型,否则如果想要变回原来的数据类型的时候,就要强制进行转换。创建集合的时候,我们就指定集合类型,避免这个过程。  

Java中的泛型基本上都是在编译器这个层次来实现的。在生成的Java字节代码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会被编译器在编译的时候去掉。这个过程就称为类型擦除。类型擦除的基本过程也比较简单,首先是找到用来替换类型参数的具体类。这个具体类一般是Object。如果指定了类型参数的上界的话,则使用这个上界。把代码中的类型参数都替换成具体的类。同时去掉出现的类型声明,即去掉<>的内容。比如T get()方法声明就变成了Object get();List<String>就变成了List。接下来就可能需要生成一些桥接方法(bridge method)。这是由于擦除了类型之后的类可能缺少某些必须的方法。了解了类型擦除机制之后,就会明白编译器承担了全部的类型检查工作。编译器禁止某些泛型的使用方式,正是为了确保类型的安全性。

21、Java中的集合类及关系图

22、HashMap、HashTable、原理和区别以及HashTable如何实现线程安全

HashMap和Hashtable的底层实现都是数组+链表结构实现的

区别:

    Hashtable是线程安全,而HashMap则非线程安全。

    HashMap可以使用null作为key,而Hashtable则不允许null作为key

    HashMap的初始容量为16,Hashtable初始容量为11,两者的填充因子默认都是0.75

    HashMap扩容时是当前容量翻倍即:capacity*2,Hashtable扩容时是容量翻倍+1即:capacity*2+1

23、ArrayList、Vector的区别、LinkedList的区别 及使用场景

ArrayList 和Vector 维护的是Object [] 的数组,LinkedList维护的是Node节点,Vector是线程安全的

24、Collection和Collections的区别

 

Collections是个java.util下的类,它包含有各种有关集合操作的静态方法。 
Collection是个java.util下的接口,它是各种集合结构的父接口。 

25、Concurrenthashmap的实现原理

ConcurrentHashMap维护一个Segment<K,V>的数组segments

Segment 是 static final 类型的,并且继承了 ReentrantLock,又维护了一个transient voliate的HashEntry<K,V> 的数组table,HashEntry 则是链表节点,key 是final 的,value 是voliate 的,next也是voliate的。

在ConcurrentHashMap.get方法中,支持并发读,没有加锁,但是在put方法中,首先根据hash得到对应的segment的,然后锁住segment,注意此时锁住的是单个segment,并不是整个数组,所以被称为细粒度的锁。

26、多线程的实现方式、状态转换、停止线程、线程安全及如何保证

继承Thread,实现Runnable, 实现Callable,并用FutureTask接收返回结果使用如下,

 

27、Synchronized、lock的区别和使用

Synchronized:不可判断线程的状态,可重入,不能中断,非公平,线程阻塞,等待线程一直等待

Lock:可以判断线程状态,可重入,可中断,可选公平,线程阻塞,等待线程可以设置获取锁的超时时间,如果指定时间内没有获得锁,可以执行其他任务。

 

lock.lock加锁过程,以ReentranLock为例,ReentranLock中内置了Sync对象,继承自AQS,

在非公平策略下,首先设置在AQS声明的state用cas算法由 0 ---->1,设置独占线程为当前线程,如果失败,则调用acquire(1)方法,该方法首先判断 state 是否为0,如果为0,重新尝试获取锁,如果不为0,判断当前线程是否为独占线程,如果是,state+1,如果不是,则需要添加到等待队列中。

AQS 定义了node,对Thread 进行了封装,并且定义了一个双向链表,添加的时候添加到队尾,添加完毕,会再次尝试从队列中获取,这是会有两种情况,一种是添加到队列的时候,正好前面的等待线程执行完了,此时队列中只剩下当前的线程了,那么会再次尝试获取锁,然后返回,如果等待队列不止当前线程,那么将当前线程用LockSupport的park方法将线程阻塞。

调用unlock方法,则会state-1,如果state=0,那么获取等待队列的head节点,解除线程的阻塞状态LockSupport的unpark方法。

28、多线程如何进行信息交互、如何产生死锁、

wait,notify,notifyAll,LockSupport.park 和unpark,interrupt

29、什么是守护线程、用什么方法实现守护线程

所谓守护线程是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。因 此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。

将线程转换为守护线程可以通过调用Thread对象的setDaemon(true)方法来实现。

30、Java线程池技术及原理

java提供四种线程池,分别是Cached、Fixed、Scheduled、Singleton,

核心参数包括:int  coreSize, int maxSize,BlockingQueue,RejectHandler,

BlockingQueue 包括Array、Linked、Synchronous、DelayQueue,LinkedBlockingQueue作为Fixed和Single的缓存队列,Synch作为Cached缓存队列,DelayedWorkQueue作为Sheduled的缓存队列。

RejectHandler包括四种拒绝策略:分别是AbortPolicy(直接抛异常)、CallerRunsPolicy(直接调用Runnalbe的run 方法)、DiscardPolicy(不做任何处理,直接忽略)、DiscardOldestPolicy(忽略队列中久未执行的任务,直接调用线程池的execute方法)。

ExecutorService(简写为es)  调用submit --》ThreadPoolExecutor的execute方法。

判断是否小于core,如果小于,则直接添加到核心线程集合中,如果大于core,则往队列中添加,添加完之后如果核心线程组有空闲线程,则往核心线程集合中添加。如果往队列添加失败,则再次往核心线程集合中添加,如果这时也失败,则执行拒绝策略。

31、java并发包concurrent及常用的类

ConcurrentHashMap,Lock,ReentranLock,BlockingQueue,LinkedBlockingQueue,ArrayBlockingQueue

32、volatile关键字,内存屏障

被voliate关键字修饰的变量,不参与指令重排,能保证内存可见性,但不能保证原子性操作。

线程对voliate变量的修改,在写入主内存之后,能保证其他线程对voliate变量的副本失效,这样的话,其他线程使用的voliate变量都是最新的数据,但是如果其他线程正在对voliate的变量进行使用和赋值,此时变量不能保证是最新的。

33、Java中额NIO,BIO,AIO分别是什么,IO和NIO的区别

BIO ,阻塞式IO读写

Nio,非阻塞式IO读写

AIO,异步非阻塞IO读写

BIO,每个连接建立一个线程

NIO,每个连接占用一个通道,通道注册在Selector上,Selector通过轮询Selectionkey,得到请求之后可以再开启线程处理。

ServerSocket.accept()方法会阻塞,直到有连接请求,nio则会轮询Selection的key的状态,并不会阻塞线程。

 

34、序列化和反序列化,序列化协议列举

序列化是将java 对象转换为字节序列的过程,反序列化则是把字节序列恢复为java对象的过程

序列化协议包括:xml,json,protobuf,Thrift

35、内存溢出和内存泄露的区别

内存溢出,程序申请内存,但无足够的可用空间供其使用出现OOM

内存泄露,程序申请内存之后,无法释放。

36、JAVA内存模型、内存结构

java内存模型规定所有的变量都是存在主存当中(类似于前面说的物理内存),每个线程都有自己的工作内存(类似于前面的高速缓存)。线程对变量的所有操作都必须在工作内存中进行,而不能直接对主存进行操作。并且每个线程不能访问其他线程的工作内存。

支撑JAVA内存模型的基础原理:

指令重排、数据依赖、单线程串行化(不管如何排序,单线程下的执行结果不能被改变)、内存屏障(禁止指令重排、刷出工作内存的保存的变量副本,voliate就是基于内存屏障实现的)、先行发生规则(happen-before)。

happen-before:

(1)、程序顺序:一个线程的每个操作,先行发生于该线程后续的操作。

(2)、监视器锁规则:对一个锁的解锁操作,先行发生于随后对这个锁的加锁操作。

(3)、voliate规则:对一个voliate的写操作, 先行发生于任意线程后续对这个voliate变量的读

(4)、传递性规则:A 先行发生于B ,B先行发生于C  则A 先行发生于C

37、JAVA 内存管理及回收算法

java将内存划分为:堆,虚拟机栈,程序计数器,方法区,本地方法栈,直接内存。

回收算法包括 标记清除、标记复制、标记整理, 

CMS包括初始标记、并发标记、重新标记、并发清除,吃内存线程资源,内存碎片,浮动垃圾无法处理即清理过程进行但程序还在运行,此时产生的垃圾只能等待下一次进行清理。

38、JAVA 类加载器及如何类加载过程

根类加载器、扩展类加载器和应用类加载器,

加载、链接(验证、准备--为类变量分配内存并设置初始值、解析)、初始化

 

初始化顺序: 

 

父类静态变量、父类静态代码块、子类静态变量、子类静态代码块、

 

父类非静态变量、父类非静态代码块、父类构造器、

子类非静态变量、子类非静态代码块、子类构造器

39、servlet生命周期及各个方法  init service destroy,doget,doPost

40、如何自定义filter

实现Filter接口,编写实现方法init doFilter destroy,将Filter添加到web.xml文件中。

41、jsp原理,jsp和servlet的区别

 

42、http和https的区别,http报文内容,

43、get和post请求的区别,get提交是否有字节限制,如果有,是在哪限制的

44、Tcp的三次握手和四次挥手,TCP和UDP的区别

45、乐观锁和悲观锁、实现和使用场景

乐观锁,假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。 乐观锁不能解决脏读的问题。使用版本号和时间戳

悲观锁,假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。数据库的加锁机制。

46、hashmap的put的时间复杂度,O(1)~O(n),如何设计get的复杂度为O(n)则直接让集合内的对象的hash值一样,,这样就会存储在同一个链表中。

47、常见的OOM的代码:

list.add --java.lang.OutOfMemoryError:java heap space

string.intern() --- 方法去内存溢出

递归没有出口, stackoverflow  方法栈内存溢出

直接内存溢出

48、Thread的interrupt()、isInterrupt()、interrupted()区别,join、方法

interrupt:其他线程通知某个线程该中断了知识给该线程设置中断状态为true,并不会立即中断该线程

isinterrupt:获取线程的中断状态,如果线程的中断状态为true,则返回true,和实际线程有没有被中断没有关系,只是查看标志位

interrupted:返回线程的中断状态的标志位,然后复归false。

以上三个方法 都是围绕着状态来进行设计,并不会对线程有影响,除非线程的run代码有判断,

比如 while(!Thread.currentThread.isInterrupted())  这种情况下,才会对线程的运行有影响。

thread.join 方法,挂起当前线程,执行thread线程,thread线程执行完毕,会回到当前线程。

49、CountDownLatch和CycleBarrier的区别

CountDownLatch, 构造方法传入的参数是state的初始值,可以理解为n把锁,

线程组内的每个线程调用countDown方法的时候会将state-1,线程并不会停止运行,当state为0的时候,会获取到被阻塞的线程,然后解除该线程的阻塞状态。

主线程调用await方法 会一直阻塞,直到state=0,也就是每次 调用countDown方法都会有这么一个判断。

CycleBarrier,线程之间互相等待,调用await方法的时候,首先判断count是否为0,如果为0,则调用Condition的signalAll方法唤醒所有等待的线程。如果count不为0,则会调用condition的await的方法 释放持有的锁,并将当前线程等待。其他线程获取到锁,也是同样进入等待状态,直到最后一个线程调用await方法,count为0,此时会唤醒所有等待的线程。释放锁的过程则是state置为0,这样其他的线程调用lock.lock方法的时候,state为0,能直接获取锁。因为ConditionObject是AQS的内部类,所以可以直接方位在AQS定义的getState方法。

50、ConcurrentHashMap的迭代器是强一致还是弱一致的,为什么?

在迭代时,ConcurrentHashMap使用了不同于传统集合的快速失败迭代器(见之前的文章《JAVA API备忘---集合》)的另一种迭代方式,我们称为弱一致迭代器。在这种迭代方式中,当iterator被创建后集合再发生改变就不再是抛出ConcurrentModificationException,取而代之的是在改变时new新的数据从而不影响原有的数据,iterator完成后再将头指针替换为新的数据,这样iterator线程可以使用原来老的数据,而写线程也可以并发的完成改变,更重要的,这保证了多个线程并发执行的连续性和扩展性,是性能提升的关键。

51、ThreadLocal的原理、作用,避免内存泄露和弱引用

ThreadLocal维护了一ThreadLocalMap对象,针对每个Thread保留一个Entry。

应用场景:ThreadLocal对象通常用于防止对可变的单实例变量或全局变量进行共享。例如,由于JDBC的连接对象不是线程安全的,因此,当多线程应用程序在没有协同的情况下,使用全局变量时,就不是线程安全的。通过将JDBC的连接对象保存到ThreadLocal中,每个线程都会拥有属于自己的连接对象副本。

内存泄露原因:ThreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal没有外部强引用来引用它,那么系统 GC 的时候,这个ThreadLocal势必会被回收,这样一来,ThreadLocalMap中就会出现key为null的Entry,就没有办法访问这些key为null的Entry的value,如果当前线程再迟迟不结束的话,这些key为null的Entry的value就会一直存在。造成无法回收

避免内存泄露,则是每次使用完都要注意remove掉。

52、JAVA 多线程遇到异常会怎样,如何处理?

当线程代码抛出运行级别异常之后,线程会中断。

在run方法里 使用try catch 语句块捕获异常

Thread.setUncaughtExceptionHandler,当线程出现异常时,该线程会调用handler里的方法。

53、HTTP2.0

点击打开链接

二、框架部分

1、spring mvc 的controller  获取请求是 get 还是post

@requestMapping(value,method)如果不指定method ,则既可以处理get,也可以处理post,如果只是处理get请求,则需要设置@RequestMapping(value="/test",method=RequestMethod.GET)

2、spring核心思想---对spring的理解

ioc(控制反转):由spring管理和控制 对象的生命周期和对象之间的关系   。

对象之间的相互调用由对象来处理,比如new 一个要调用的对象,ioc 则是把对象的控制交由统一的容器来管理,并且通过依赖注入实现对象之间的依赖关系。

spring还提供了aop面向切面编程的框架,该框架底层使用的是jdk动态代理和cglib字节码技术,spring-tx提供了事务的支持,底层就是用的jdk动态代理。

3、分布式框架

4、mybatis 中$ 和#的区别

 #将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号

 $将传入的数据直接显示生成在sql中

 #方式能够很大程度防止sql注入

 $方式一般用于传入数据库对象,例如传入表名

5、resultMap和resultClass的区别,resultMap是建立查询结果和类属性的对应关系,resultClass则是直接指定类来接收查询结果。resultMap比resultClass具有更好的性能。

6、springmvc 的controller和struts的action 是单例吗?

SpringMVC默认是单例的。与Struts2不同,SpringMVC没有默认处理方法,也就是说SpringMVC是基于方法的开发,都是用形参接收值,一个方法结束参数就销毁了,多线程访问都会有一块内存空间产生,里面的参数也是不会共用的。由于SpringMVC默认使用了单例,所以Controller类中不适合定义属性,只要controller中不定义属性,那么单例完全是安全的。单例模式可以提高SpringMVC性能,不需要每次相应请求都创建一个对象。在特殊情况,需要在Controller类定义属性时,单例肯定会出现竞争访问,需要在类上面加上注解@Scope(“prototype”)改为多例的模式。

 

Struts2是基于类的属性进行发的,定义属性可以整个类通用。所以Struts2的Action是多实例的并非单例,也就是每次请求产生一个Action的对象。Action类中往往包含了数据属性,例如在页面填写的form表单的字段,Action中有对应的的属性来绑定页面form表单字段。显然如果Action是单实例的话,那么多线程的环境下就会相互影响,例如造成别人填写的数据被你看到了。

但是什么有人说Struts2的Action 默认是单例的?而且还可以进行配置呢? 因为在和Spring一起使用的时候,Action交给Spring进行管理,默认的就是单例,所以才会有人说Struts2默认是单例的。 所以在Spring整合Struts2开发时,如果需要用使用Struts2多例,就在spring的action bean配置的时候设置scope=”prototype”。 

7、mybatis如何释放数据库资源

 三种内建的数据源类型(也就是 type=”[UNPOOLED|POOLED|JNDI]”)

 UNPOOLED– 这个数据源的实现只是每次被请求时打开和关闭连接。虽然有点慢,但对于在数据库连接可用性方面没有太高要求的简单应用程序来说,是一个很好的选择。

 POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这是一种使得并发 Web 应用快速响应请求的流行处理方式。

poolMaximumActiveConnections – 在任意时间可以存在的活动(也就是正在使用)连接数量,默认值:10

poolMaximumIdleConnections – 任意时间可能存在的空闲连接数。

poolMaximumCheckoutTime – 在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000 毫秒

poolTimeToWait – 这是一个底层设置,如果获取连接花费了相当长的时间,连接池会打印状态日志并重新尝试获取一                                         个连接(避免在误配置的情况下一直安静的失败),默认值:20000 毫秒(即 20 秒)。

poolMaximumLocalBadConnectionTolerance – 这是一个关于坏连接容忍度的底层设置, 作用于每一个尝试从缓存                                            池 获取连接的线程. 如果这个线程获取到的是一个坏的连接,那么这个数据源允许这个线程尝试重                                新获取一个新的连接,但是这个重新尝试的次数不应该超过 poolMaximumIdleConnections 与                                               poolMaximumLocalBadConnectionTolerance 之和。 默认值:3 (新增于 3.4.5)

poolPingQuery – 发送到数据库的侦测查询,用来检验连接是否正常工作并准备接受请求。默认是“NO PING QUERY                                     SET”,这会导致多数数据库驱动失败时带有一个恰当的错误消息。

poolPingEnabled – 是否启用侦测查询。若开启,需要设置 poolPingQuery 属性为一个可执行的 SQL 语句(最好是一                                  个速度非常快的 SQL 语句),默认值:false。

poolPingConnectionsNotUsedFor – 配置 poolPingQuery 的频率。可以被设置为和数据库连接超时时间一样,来避免                                   不必要的侦测,默认值:0(即所有连接每一时刻都被侦测 — 当然仅当 poolPingEnabled 为 true 时适                                     用)。

 JNDI – 这个数据源的实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。

8、spring 封装JDBC 用模板设计模式,aop用到了适配器模式MethodBeforeAdviceAdapter,代理模式。创建bean用单例模式

9、Struts的请求处理过程

Struts需要在web.xml中配置filter,当接收请求的时候,则调用Filter的doFilter方法,根据url找到对应的ActionMapping,并创建ActionProxy代理,此时也定位到了具体的Action类,执行完Action类中的方法之后,如果配置了Result,则返回对应的Result内容。

10、mvc概念

11、Spring mvc 和Struts的区别

Springmvc 基于Servlet,Struts基于Filter,

Struts处理Action请求需要映射属性,不是单例的,Springmvc基于方法处理请求,属性作为方法的参数直接调用,是单例的。

12、hibernate和mybatis的区别

hibernate  数据库无关性。

mybatis, 需要手动维护实体表与类之间的映射,需要手写sql。

13、hibernate一级和二级缓存

Hibernate的缓存包括Session的缓存和SessionFactory的缓存,其中SessionFactory的缓存又可以分为两类:内置缓存和外置缓存。Session的缓存是内置的,不能被卸载,也被称为Hibernate的第一级缓存。

二级缓存:就是SessionFactory级别的缓存。顾名思义,就是查询的时候会把查询结果缓存到二级缓存中,如果同一个sessionFactory创建的某个session执行了相同的操作,hibernate就会从二级缓存中拿结果,而不会再去连接数据库。这是可选的插件式的缓存,在默认情况下,SessionFactory不会启用这个插件。可以在每个类或每个集合的粒度上配置。缓存适配器用于把具体的缓存实现软件与Hibernate集成。

缓存范围:事务、进程、集群

缓存的隔离级别:事务(可重复读)、读写(读已提交)、非严格读写、只读型。

什么样的数据适合存放到第二级缓存中?
  (1)、很少被修改的数据
  (2)、不是很重要的数据,允许出现偶尔并发的数据
  (3)、不会被并发访问的数据

  (4)、参考数据

Hibernate的二级缓存策略的一般过程如下:
  1) 条件查询的时候,总是发出一条select * from table_name where …. (选择所有字段)这样的SQL语句查询数据库,一次获得所有的数据对象。
  2) 把获得的所有数据对象根据ID放入到第二级缓存中。
  3) 当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查;查不到,如果配置了二级缓存,那么从二级缓存中查;查不到,再查询数据库,把结果按照ID放入到缓存。
  4) 删除、更新、增加数据的时候,同时更新缓存。

14、Hibernate如何实现声明式事务

点击打开链接

15、Hibernate的优化策略

点击打开链接

16、Spring bean的加载过程

首先加载所有的配置文件,将注册的bean全都封装为BeanDefinition的实例,

在调用getBean方法的时候,首先用反射获取到对象的实例,然后调用BeanPostProcessor后处理器进行处理,如果经过后处理器返回的Bean不为空,说明返回的是一个代理对象。Bean 实例化

17、spring 的事务管理

18、Spring 事务的传播特性

 

19、spring mvc 原理、注解、

20、Restful有几种请求,好处,理解 

     点击打开链接

21、memcached和redis的区别

22、分布式锁、

23、开源协议

24、分布式session的实现方式:

(1)、session复制

(2)、ip_hash,同一台机器的访问均落到同一个服务器上

(3)、基于数据库

(4)、基于缓存服务器

25、filter拦截器链。

Tomcat 的容器包括:Engine、Host、Context、Wrapper,Wrapper其实就是对servlet的进一步封装,

每种容器都会包含Pipeline  流水线,并且在构造方法中,默认设置流水线上的最后的阀门为对应的StandardEngineValve、StandardHostValve、StandardContextValve和StandardWrapperValve。

当Tomcat请求到达之后,解析完req和设置res之后,会依次调用阀门的invoke方法,执行到最后StandardWrapperValve的invoke方法时,首先创建servlet对象,然后获取ApplicationFilterFactory 的factory对象,然后调用factory的createFilterChain方法,传入req,wrapper和servlet ,调用FilterChain的doFilter方法。此时使用的设计模式是责任链模式,

即FilterChan维护一个Filter的数组,每次下标+1,得到对应的Filter,Filter里重写了doFilter,每次处理完自己的过滤器规则,都需要再调用FilterChain的doFilter方法。

26、序列化协议框架

点击打开链接

点击打开链接

三、设计模式,

1、装饰者模式伪代码

装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。      java io流是典型的装饰者模式

absctract or interface  A method1(),method2()

class B  extends A method1,method2

class C extends A method1,method2

class D exetends A A  a;method1();method2a.method2();syso()

BufferedReader br=new BufferedReader(new InputStreamReader(System.in=InputStream));

 

2、代理模式伪代码

abstract or interface A method1()

class B  extends A method1();

class Proxy extends A B b;method1(b.method1();syso());

jdk动态代理:

public class MyInvocationHandler implements InvocationHandler 

	private Object target;
	public MyInvocationHandler(Object target) 
		// TODO Auto-generated constructor stub
		this.target=target;
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable 
		// TODO Auto-generated method stub
		System.out.println("before");
		method.invoke(target, args);
		System.out.println("after");
		return proxy;
	
        public static void main(String[] args) 
		// TODO Auto-generated method stub
		UserService us=new UserServiceImpl();
		MyInvocationHandler mi=new MyInvocationHandler(us);
		UserService proxy=(UserService) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), us.getClass().getInterfaces(), mi);
		proxy.getName();
		
	

  cglib动态代理:

public class MethodInterceptorImpl implements MethodInterceptor
	@Override
	public Object intercept(Object cls, Method method, Object[] arg2,
			MethodProxy proxy) throws Throwable 
		// TODO Auto-generated method stub
		System.out.println("before");
		proxy.invokeSuper(cls, arg2);
		System.out.println("after");
		return null;
	
        public static void main(String[] args) 
		Enhancer en=new Enhancer();
		en.setSuperclass(Test.class);
		en.setCallback( new MethodInterceptorImpl());
		Test demo=(Test) en.create();
		demo.test();
		//demo.test2();
	


3、装饰者、适配器、 代理模式的区别联系认知

代理模式:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

装饰者模式:功能增强

适配器模式:兼容接口

4、六大原则

5、高内聚、低耦合的理解

四、数据库

1、防止sql注入

使用PrepareStatement 预处理,非法字符过滤,数据类型校验。

2、sql查询优化

         指定列名,去重,truncate代替delete,使用commit,exists代替in,避免全表检索,在where 和orderby 上建立索引

        or 用union代替,where 中避免对字段或者 = 号左边进行表达式操作

        sql查询优化

3、如何处理脏读,加锁的话对程序有什么影响

设置事务隔离级别为可重复度或者读已提交或者串行化。加锁在多线程情况下会带来竞争。

4、物化视图的刷新机制  

完全刷新(COMPLETE)会删除表中所有的记录(如果是单表刷新,可能会采用TRUNCATE的方式),然后根据物化视图中查询语句的定义重新生成物化视图

快速刷新(FAST)采用增量刷新的机制,只将自上次刷新以后对基表进行的所有操作刷新到物化视图中去。

强制刷新(force refresh)当进行强制刷新的时候系统会首先尝试进行快速刷新,如果快速刷新无法进行的时候系统将会进行完全刷新。其实就是一个快速刷新和完全刷新的结合体。

5、索引的数据结构 B树  B+树,R树

6、事务的原理:

针对数据库的一系列操作要么全执行,要么全不执行。事务特性ACID(原子、一致、隔离、持久)

事务的隔离级别:读未提交(脏读、幻读、不可重复读)读已提交(幻读、不可重复读)可重复读(幻读)序列化

7、事务的隔离级别

8、三大范式

9、数据库连接池的原理

10、

11、如何实现不同数据库的数据查询分页

12、索引的优缺点以及什么时候数据库索引失效

优点:提升查询效率,缺点,增加额外空间存储,并且数据插入和更新需要对索引进行额外的处理。

索引分类:唯一索引、主键索引和聚集索引

索引数据结构:B,B+,hash索引

索引失效:is NULL, LIKE  OR

13、redis的存储结构

14、union和union all 的区别, 左右连接查询的区别

(1)、对重复结果的处理:UNION在进行表链接后会筛选掉重复的记录,Union All不会去除重复记录。

(2)、对排序的处理:Union将会按照字段的顺序进行排序;UNION ALL只是简单的将两个结果合并后就返回。

15、redis的常用问题

redis 常见面试题点击打开链接

点击打开链接

redis 命令参考 点击打开链接

16、redis 持久化机制:

redis 持久化包括rdb 和aof

 

rdb:当redis需要做持久化时,redis会fork一个子进程;子进程将数据写到磁盘上一个临时RDB文件中;当子进程完成写临时文件后,将原来的RDB替换掉,这样的好处就是可以copy-on-write。

 

aof:Redis 执行 fork() ,现在同时拥有父进程和子进程。子进程开始将新 AOF 文件的内容写入到临时文件。对于所有新执行的写入命令,父进程一边将它们累积到一个内存缓存中,一边将这些改动追加到现有 AOF 文件的末尾: 这样即使在重写的中途发生停机,现有的 AOF 文件也还是安全的。当子进程完成重写工作时,它给父进程发送一个信号,父进程在接收到信号之后,将内存缓存中的所有数据追加到新 AOF 文件的末尾。搞定!现在 Redis 原子地用新文件替换旧文件,之后所有命令都会直接追加到新 AOF 文件的末尾。

17、redis 主从复制

修改配置文件的从服务器端口,添加slaveof ip port 启动服务

原理:接到 SYNC 命令的主服务器将开始执行 BGSAVE , 并在保存操作执行期间, 将所有新执行的写入命令都保存到一个缓冲区里面。当 BGSAVE 执行完毕后, 主服务器将执行保存操作所得的 .rdb 文件发送给从服务器, 从服务器接收这个 .rdb 文件, 并将文件中的数据载入到内存中。之后主服务器会以 Redis 命令协议的格式, 将写命令缓冲区中积累的所有内容都发送给从服务器。即使有多个从服务器同时向主服务器发送 SYNC , 主服务器也只需执行一次 BGSAVE 命令, 就可以处理所有这些从服务器的同步请求。从服务器可以在主从服务器之间的连接断开时进行自动重连, 在 Redis 2.8 版本之前, 断线之后重连的从服务器总要执行一次完整重同步(full resynchronization)操作, 但是从 Redis 2.8 版本开始, 从服务器可以根据主服务器的情况来选择执行完整重同步还是部分重同步(partial resynchronization)。

18、redis 集群

原理:Redis 集群使用数据分片(sharding)而非一致性哈希(consistency hashing)来实现: 一个 Redis 集群包含 16384 个哈希槽(hash slot), 数据库中的每个键都属于这 16384 个哈希槽的其中一个, 集群使用公式 CRC16(key) % 16384 来计算键 key 属于哪个槽, 其中 CRC16(key) 语句用于计算键 key 的 CRC16 校验和 。

Redis 集群不保证数据的强一致性(strong consistency): 在特定条件下, Redis 集群可能会丢失已经被执行过的写命令。

使用异步复制(asynchronous replication)是 Redis 集群可能会丢失写命令的其中一个原因

 

 

客户端向主节点 B 发送一条写命令。

 

 

主节点 B 执行写命令,并向客户端返回命令回复。

 

 

主节点 B 将刚刚执行的写命令复制给它的从节点 B1 、 B2 和 B3 。

节点对命令的复制工作发生在返回命令回复之后, 因为如果每次处理命令请求都需要等待复制操作完成的话, 那么主节点处理命令请求的速度将极大地降低 —— 我们必须在性能和一致性之间做出权衡。
 

 

 

Redis 集群另外一种可能会丢失命令的情况是, 集群出现网络分裂(network partition), 并且一个客户端与至少包括一个主节点在内的少数(minority)实例被孤立

19、mysql 引擎的区别

https://blog.csdn.net/ls5718/article/details/52248040

 

五、数据结构和算法

1、链表和数组对内存的要求

数组,在内存上给出了连续的空间.链表,内存地址上可以是不连续的,链表可以随意扩大,数组不能;

2、满树查找节点的复杂度 

3、链表找环的算法

   public static boolean hasLoop(Node n)
        //定义两个指针tmp1,tmp2
        Node tmp1 = n;
        Node tmp2 = n.next;

        while(tmp2!=null)

            int d1 = tmp1.val;
            int d2 = tmp2.val;
            if(d1 == d2)return true;//当两个指针重逢时,说明存在环,否则不存在。
            tmp1 = tmp1.next;  //每次迭代时,指针1走一步,指针2走两步
            tmp2 = tmp2.next.next;
            if(tmp2 == null)return false;//不存在环时,退出

        
        return true; 
    

 

4、红黑二叉树

 

 

插入方法 按照二叉树进行插入,左节点 <父节点<右节点

插入之后需要平衡二叉树,此时用的是红黑二叉树进行实现。

节点代码:

class Entry<K,V> 
        K key;
        V value;
        Entry<K,V> left = null;
        Entry<K,V> right = null;
        Entry<K,V> parent;
        boolean color = BLACK;
    private void fixAfterInsertion(Entry<K,V> x) 
        x.color = RED;

        while (x != null && x != root && x.parent.color == RED) 
            if (parentOf(x) == leftOf(parentOf(parentOf(x)))) 
                Entry<K,V> y = rightOf(parentOf(parentOf(x)));
                if (colorOf(y) == RED) 
                    setColor(parentOf(x), BLACK);
                    setColor(y, BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    x = parentOf(parentOf(x));
                 else 
                    if (x == rightOf(parentOf(x))) 
                        x = parentOf(x);
                        rotateLeft(x);
                    
                    setColor(parentOf(x), BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    rotateRight(parentOf(parentOf(x)));
                
             else 
                Entry<K,V> y = leftOf(parentOf(parentOf(x)));
                if (colorOf(y) == RED) 
                    setColor(parentOf(x), BLACK);
                    setColor(y, BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    x = parentOf(parentOf(x));
                 else 
                    if (x == leftOf(parentOf(x))) 
                        x = parentOf(x);
                        rotateRight(x);
                    
                    setColor(parentOf(x), BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    rotateLeft(parentOf(parentOf(x)));
                
            
        
        root.color = BLACK;
    
    /** From CLR */
    private void rotateLeft(Entry<K,V> p) 
        if (p != null) 
            Entry<K,V> r = p.right;
            p.right = r.left;
            if (r.left != null)
                r.left.parent = p;
            r.parent = p.parent;
            if (p.parent == null)
                root = r;
            else if (p.parent.left == p)
                p.parent.left = r;
            else
                p.parent.right = r;
            r.left = p;
            p.parent = r;
        
    

    /** From CLR */
    private void rotateRight(Entry<K,V> p) 
        if (p != null) 
            Entry<K,V> l = p.left;
            p.left = l.right;
            if (l.right != null) l.right.parent = p;
            l.parent = p.parent;
            if (p.parent == null)
                root = l;
            else if (p.parent.right == p)
                p.parent.right = l;
            else p.parent.left = l;
            l.right = p;
            p.parent = l;
        
    

5、深度优先、广度优先算法

6、排序算法及复杂度

7、查找算法

8、B+树和二叉树查找时间复杂度

9、hash算法有哪些

10、如何判断一个单链表是否有环

11、数据去重有哪些方法,复杂度分别是多少

12、给一个数组,查找和为K的两个数

for+bs(二分)

13、10W个数里找出最大或最小的10个

点击打开链接

14、100个硬币找出最轻的假币

点击打开链接

15、50个红球,50个黑球,两个一模一样的桶,采用什么样的策略可以抽到的红球的概率最高

16、top K 问题

点击打开链接

17、一致性hash算法

18、java 递归和回溯

点击打开链接

六、模块设计

1、浏览过商城的某些商品,在浏览新闻网站的时候正好有这些商品的广告推送,如何设计。

登录商城,用到手机号、邮箱等信息,商城记录用户的浏览记录。再登录新闻网站的时候,新闻系统拿到了用户的手机号和邮箱。然后调用商城那边的接口,得到返回数据,进行精准投放。

2、海量数据分析

点击打开链接

3、微信红包怎么实现

点击打开链接

4、接口幂等性设计

点击打开链接

 

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

Java 面试题:各个大厂的面试题都在这

软件测试面试题都有哪些?

面试JAVA程序员最基本的面试题都有哪些?

2020年最全java面试真题解析(980道),你没见过的面试题都在这

一般刷java题都是上力扣吗?

web前端面试经常问到的面试题都有哪些