Fork me on GitHub

2017java面试题(技术面试)

1.Servlet执行流程

客户端发出http请求,web服务器将请求转发到servlet容器,servlet容器解析url并根据web.xml找到对应的servlet,并将request、response对象传递给找到的servlet,servlet根据request就可以知道是谁发出请求,请求信息及其他信息,当servlet处理完业务逻辑后将信息放入到response并响应到客户端。


2.SpringMVC的执行流程

SpringMVC是由dispatchservlet为核心的分层控制框架。首先客户端发出一个请求web服务器解析请求URL并去匹配dispatchservlet的映射URL,如果匹配的上就将这个请求放入到dispatchservlet,dispatchservlet根据mapping映射配置去寻找到相应得handle,然后把处理权交给找到的handle,handle封装了处理业务逻辑的代码,当handle处理完后会返回一个逻辑视图modelandview给dispatchservlet,此时的modelandview是一个逻辑视图不是一个正式视图,所以dispatchservlet会通过viewersource视图资源去解析modelandview,然后将解析后的参数放到view中返回到客户端并展现。


3.给定一个txt文件,如何得到某字符出现的次数

1
2
3
4
5
6
7
8
9
10
11
12
File file = new File("E://test.txt");
InputStream is = new FileInputStream(file);
byte[] b= new byte[1024];
int a = is.read(b);
String str[] = new String(b,0,a).split("");
int count = 0;
for(int i =0;i<str.length;i++){
if("a".equals(str[i])){
count ++;
}
}
System.out.println(count);

4.Java设计模式思想(单例模式,工厂模式,策略模式,共23中设计模式)

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class SingletonClass{
    private static volatile SingletonClass instance=null;
    public static SingletonClass getInstance(){
    synchronized(SingletonClass.class){
    if(instance==null){
    instance=new SingletonClass();
    }
    }
    return instance;
    }
    private SingletonClass(){}
    }
  • 策略模式:
    就是将几个类中公共的方法提取到一个新的类中,从而使扩展更容易,保证代码的可移植性,可维护性强。比如有个需求是写鸭子对象,鸭子有叫、飞、外形这三种方法,如果每个鸭子类都写这三个方法会出现代码冗余,这时候我们把鸭子中的叫、飞、外形这三个方法提取出来,放到鸭子父类中,让每个鸭子都继承这个鸭父类。重新这三个方法,这样封装的代码可移植性强,当用户提出新的需求时,比如说鸭子游泳,那么对于我们程序员来讲就非常简单,只需要在鸭父类中添加一个游泳的方法,所有继承这个父类的鸭子都有了这个方法,重写游泳方法即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//简单工厂
public class Factory{
public static ISample creator(int which){
if (which==1)
return new SampleA();
else if (which==2)
return new SampleB();
}
}

//抽象工厂
public abstract class Factory{
public abstract Sample creator();
public abstract Sample2 creator(String name);
}
public class SimpleFactory extends Factory{
public Sample creator(){
.........
return new SampleA
}
public Sample2 creator(String name){
.........
return new Sample2A
}
}

public class BombFactory extends Factory{
public Sample creator(){
......
return new SampleB
}
public Sample2 creator(String name){
......
return new Sample2B
}
}

以上两种为简单和抽象工厂模式代码

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

5.冒泡排序、二分查找

  • 冒泡

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public static void mp(int a[]){
    int swap = 0;
    for(int i=0;i<a.length;i++){
    for(int j=i;j<a.length;j++){
    if(a[j]>a[i]){
    swap = a[i];
    a[i] = a[j];
    a[j] = swap;
    }
    }
    }
    }
  • 二分法查找

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public static int ef(int a[],int tag){
    int first = 0;
    int end = a.length;
    for(int i=0;i<a.length;i++){
    int middle = (first+end)/2;
    if(tag == a[middle]){
    return middle;
    }
    if(tag>a[middle]){
    first = middle + 1;
    }
    if(tag<a[middle]){
    end = middle - 1;
    }
    }
    return 0;
    }

代码如上所示


6.对ajax的理解

Ajax为 异步请求,即局部刷新技术,在传统的页面中,用户需要点击按钮或者事件触发请求,到刷新页面,而异步技术为不需要点击即可触发事件。(不需要刷新整个网页,而刷新部分网页数据)


7.父类与子类之间的调用顺序(打印结果)

  • 父类静态代码块
  • 子类静态代码块
  • 父类的构造方法
  • 子类的构造方法
  • 子类的普通方法
  • 重写父类的方法,则打印重写后的方法

8.内部类与外部类的调用

  • 内部类可以直接调用外部类包括平private的成员变量,使用外部类引用的this.关键字调用即可
  • 外部类调用内部类需要建立内部类对象

9.多线程

  • 一个进程是一个独立的运行环境,可以看做是一个程序,而线程可以看做是进程的一个任务,比如QQ是一个进程,而一个QQ窗口是一个线程。
  • 在多线程程序中,多线程并发可以提高程序的效率,CPU不会因为某个线程等待资源而进入空闲状态,它会把资源让给其他的线程。
  • 用户线程就是我们开发程序是创建的线程,而守护线程为系统线程,如JVM虚拟中的GC。
  • 线程的优先级别:每一个线程都有优先级别,有限制级高的可以先获取CPU资源使该线程从就绪状态转为运行状态。也可以自定义线程的优先级别。
  • 死锁:至少两个以上线程争取两个以上CPU资源,避免死锁就避免使用嵌套锁,只需要在他们需要同步的地方加锁和避免无限等待。(发生死锁的四个条件:互斥条件、请求和保存条件、不剥夺条件、环路等条件)。
    1 互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某个资源只由一个进程占用。若果此时还有其他进程请求资源,则请求者只能等待,直到战友的进程用完释放。
    2 请求和保持条件:指进程以及保持了至少一个资源,但又提出了新的资源请求,而该资源已经被其他进程占有,此时请求进程阻塞,但又对自己已获得的其他资源保持不放。
    3 不剥夺条件:指进程已获得的资源,在未使用完之前,不能剥夺,只能在使用完时由自己释放。
    4 环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{p0,p1,p2,….,pn}中的p0正在等待一个p1占用的资源;p1正在等待p2占用的资源,….,pn增在等待已被p0占用的资源。

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

  • IOC:Spring是开源框架,使用框架可以使我们减少工作量,提高工作效率并且它是分层结构,即相对应的层处理对应的业务逻辑,减少代码的耦合度。而spring的核心是IOC控制反转和AOP面向切面编程。IOC控制反转主要强调的是程序之间的关系是由容器控制的,容器控制对象,控制了对外部资源的获取。而反转即为,在传统的编程中都是由我们创建对象获取依赖对象,而在IOC中是由容器帮我们创建好对象并注入依赖对象,正是容器帮我们查找和注入对象,对象是被获取,所以叫反转。
  • AOP:面向切面编程,主要是管理系统层的业务,比如日志,权限,事物等。AOP是将封装好的对象剖开,找出其中对多个对象产生影响的公共行为,并将其封装为一个可重用的模块,这个模块被命名为切面(aspect),切面讲那些与业务逻辑无关,却被业务模块共同调用的逻辑提取并封装起来,减少了系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。

11.hibernate的核心思想

hibernate的核心思想就是ROM对象关系映射机制,它是将表与表之间的操作映射成对象与对象之间的操作。也就是从数据库中提取的信息会自动按照你设置的映射要求封装成特定的对象。所以hibernate就是通过将数据表实体类的映射,使得对对象的修改对应数据行的修改。


12.最优删除某字符串的某个字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
///////////////////////////////////////////////////////////////////////  
// Delete all characters in pStrDelete from pStrSource
///////////////////////////////////////////////////////////////////////
void DeleteChars(char* pStrSource, const char* pStrDelete)
{
if(NULL == pStrSource || NULL == pStrDelete)
return;

// Initialize an array, the index in this array is ASCII value.
// All entries in the array, whose index is ASCII value of a
// character in the pStrDelete, will be set as 1.
// Otherwise, they will be set as 0.
const unsigned int nTableSize = 256;
int hashTable[nTableSize];
memset(hashTable, 0, sizeof(hashTable));

const char* pTemp = pStrDelete;
while ('\0' != *pTemp)
{
hashTable[*pTemp] = 1;
++ pTemp;
}

char* pSlow = pStrSource;
char* pFast = pStrSource;
while ('\0' != *pFast)
{
// if the character is in pStrDelete, move both pStart and
// pEnd forward, and copy pEnd to pStart.
// Otherwise, move only pEnd forward, and the character
// pointed by pEnd is deleted
if(1 != hashTable[*pFast])
{
*pSlow = *pFast;
++ pSlow;
}

++pFast;
}

*pSlow = '\0';
}

代码如上


13.ArrayList与LinkedList的区别

都是实现List接口的列表。ArrayList是基于数组的数据结构,LinkedList是基于链表的数据结构,当获取特定元素时,ArrayList效率比较快,它通过数组下标即可获取,而LinkedList则需要移动指针。当存储和删除元素时LinkedList效率比较快,只需要移动指针指定位置即可增加和删除,而ArrayList需要移动数据。


14.数据库优化

  • 选择合适的字段,比如邮箱字段可以设定char(6),尽量把字段设置为not null,这样查询的时候数据库就不需要比较null值
  • 使用关联查询(Left join on)查询代替子查询
  • 使用union联合查询手动创建临时表
  • 开启事物,当数据库执行多条语句出现错误时,事物会回滚,可以维护数据库的完整性
  • 使用外键,事物可以维护数据的完整性但是它却不能保证数据的关联性,使用外键可以保证数据的关联性
  • 使用索引,索引是提高数据库性能的常用方法,它可以令数据库服务器比没有索引快的多的速度检索特定的行,特别是对于max,min,order by查询时,效果更明显
  • 优化的查询语句,绝大多数情况下,使用索引可以提高查询的速度,但如果sql语句使用不恰当的话,索引无法发挥它的特性

15.Tomcat服务器优化(内存,并发连接数,缓存)

  • 内存优化:
    主要是对Tomcat启动参数进行优化,我们可以在Tomcat启动脚本中修改它的最大内存数等
  • 线程数优化:
    Tomcat的并发链接参数,主要在Tomcat配置文件中server.xml中配置,比如修改最小空闲连接线程数,用于提高系统处理能力等
  • 优化缓存:
    打开压缩功能,修改参数,比如压缩的输出内容大小默认为2KB,可以适当地修改

16.HTTP协议

  • 常用的请求方法:get、post
  • Get和Post的区别:
    传送数据,get携带参数与访问地址传送,用户是可以看见,这样的话信息会不安全,导致信息泄露。而Post则将字段与对应值封装在实体中传送,这个过程用户是不可见的。Get传递参数有限制,而Post无限制


17.TCP/UDP协议

TCP和UDP协议都属于传输层协议。其中TCP提供IP环境下的数据可靠传输。它提供的服务包括了数据流传送、可靠性、有效流控、全双工操作和多路复用。通过面向连接、端到端和可靠的数据包发送。通俗说,它事先为发送的数据开辟出连接好的通道,然后再进行数据发送。UDP则不为IP提供可靠性、流控或差错恢复功能。一般来说,TCP对应的可靠性要求高的应用,UDP对应的是可靠性要求低,传输经济的应用。

特点 TCP UDP
是否连接 面向连接 面向非连接
传输可靠性 可靠地 不可靠的
应用场合 传输大量的数据 少量数据
速度

18.Java集合类框架的基本接口有哪些

总共有两大接口:Collection Map ,一个元素集合,一个是键值对集合; 其中List和Set接口继承了Collection接口,一个是有序元素集合,一个是无序元素集合; 而ArrayList和 LinkedList 实现了List接口,HashSet实现了Set接口,这几个都比较常用; HashMap 和HashTable实现了Map接口,并且HashTable是线程安全的,但是HashMap性能更好。

java.util.Collection [I]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
java.util.Collection [I]

|—java.util.List [I]

|—java.util.ArrayList [C]

|—java.util.LinkedList [C]

|—java.util.Vector [C]

|—java.util.Stack [C]

|—java.util.Set [I]

|—java.util.HashSet [C]

|—java.util.SortedSet [I]

|—java.util.TreeSet [C]

java.util.Map [I]

1
2
3
4
5
6
7
8
9
10
11
|—java.util.SortedMap [I]

|—java.util.TreeMap [C]

|—java.util.Hashtable [C]

|—java.util.HashMap [C]

|—java.util.LinkedHashMap [C]

|—java.util.WeakHashMap [C]

Java集合类里最基本的接口有:
Collection:单列集合的根接口
List:元素有序 可重复
ArrayList:类似一个长度可变的数组 。适合查询,不适合增删
LinkedList:底层是双向循环链表。适合增删,不适合查询。
Set:元素无序,不可重复
HashSet:根据对象的哈希值确定元素在集合中的位置
TreeSet: 以二叉树的方式存储元素,实现了对集合中的元素排序
Map:双列集合的根接口,用于存储具有键(key)、值(value)映射关系的元素。
HashMap:用于存储键值映射关系,不能出现重复的键key
TreeMap:用来存储键值映射关系,不能出现重复的键key,所有的键按照二叉树的方式排列

19.类加载过程

遇到一个新的类时,首先会到方法区中去找class文件,如果没有找到就回去硬盘中找class文件,找到后返回,将class文件加载到方法区中。其次在类加载的时候,静态成员变量会被分配到方法区的静态区域,非静态成员变量分配到非静态区域,然后开始给静态成员变量初始化,赋默认值,赋完默认值后,会根据静态成员变量书写的位置赋显示值,然后执行静态代码。当所有的静态代码执行完,类加载才算完成。


20.对象的创建

  • 遇到一个新类时,会进行类的加载,定位到class文件
  • 对所有静态成员变量初始化,静态代码块也会执行,而且只有类加载的时候执行一次
  • New对象时,JVM会在堆中分配一个足够大的存储空间
  • 存储空间清空时,为所有的变量赋默认值,所有的对象引用赋值为null
  • 根据书写的位置给一些字段初始化操作
  • 调用构造器方法(没有继承)

21.JVM优化

  • 设置参数,设置JVM的最大内存数
  • 垃圾回收器的选择

22.高并发处理

了解一点高并发性问题,比如一万个人抢一张票时,如何保证票在没买走的情况下所有人都能看见这张票。显然,不能用同步机制,因为Synchronize是锁同步一次只能一个人进行。这时候可以用到锁机制,采用乐观锁可以解决这个问题。乐观锁的简单意思是在不锁定表的情况下,利用业务的控制来解决并发问题这样既保证了数据的可读性,又保证数据的排他性,保证性能的同事解决了并发带来的脏读问题。(注:乐观锁的实现原理是cas操作,java中轻量级锁也是基于cas实现的。悲观锁最大的问题就是阻塞问题。)


23.事物的理解

事物一般具有原子性、一致性、持久性、隔离性。

  • 原子性:是指一个事物中,要么全部执行成功,要么全部失败滚回。
  • 一致性:事物执行之前和执行之后都处于一致性状态
  • 持久性:事物多数据的操作是永久的
  • 隔离性:当一个事物正在对数据进行操作时,另一个事物不可以对数据进行操作,也就是多个并发书屋之间相互隔离

24.Struts工作流程

  • 客户端发出一个请求到servlet容器
  • 请求经过一系列过滤杯filterdispatcher调用,filterdispatch通过actionMapper去找相对应的action
  • Actionmapper找到对应的action返回给filterdispatch,dispatch吧处理权交给actionproxy
  • Actionproxy通过配置文件找到对应的action类
  • Actionproxy创建一个actioninvocation的实例处理业务逻辑
  • 一旦action处理完毕,actioninvocation负责根据struts.xml的配置找到对应的返回结果,返回结果一般是jsp页面
-------------本文结束感谢您的阅读-------------
您的支持将鼓励我继续创作!