美文网首页
面试总结

面试总结

作者: 海豚的小小海 | 来源:发表于2017-09-26 21:23 被阅读0次

    1、servlet生命周期

    (1) 初始化阶段,调用init()方法
    (2) 响应客户端请求阶段,调用service()方法
    (3) 终止阶段,调用destroy()方法

    servlet容器装载servlet

    1. Servlet容器启动时自动装载某些Servlet,实现它只需要在web.XML文件中的<Servlet></Servlet>之间添加如下代码:
      <loadon-startup>1</loadon-startup>
    2. 在Servlet容器启动后,客户首次向Servlet发送请求
    3. Servlet类文件被更新后,重新装载Servlet,Servlet被装载后,Servlet容器创建一个Servlet实例并且调用Servlet的init()方法进行初始化。在Servlet的整个生命周期内,init()方法只被调用一次。

    servlet工作原理
      首先客户发送一个请求,servlet调用service()方法对请求进行响应,service()方法对请求的方式进行匹配,选择调用doGet(), doPost()这些方。servlet接口中没有 doGet,doPost这些方法,httpServlet中有这些方法,但返回值都是error信息,所有需要将doPost,doGet重写。
      每一个自定义的servlet都需要实现servlet接口(init,service,destory就在这个接口中)。GenericServlet是一个通用的、不特定于任何协议的servlet,它实现了servlet接口,而httpServlet又继承与GenericServlet,所以我们定义servlet时只需要继承httpServlet即可。
      httpServlet是特定于http协议的类,它实现了service()方法,并将servletRequest和servletResponse强转为httpRequest和httpResponse。

    2、sql 左外连接、右外连接、内连接、全连接

    --左外连接,就算emp表中员工的deptno没有与dept表中的deptno对应也会查出来,不过显示emp表中的字段
    select e.*,d.*
    from emp e left join dept d on e.deptno=d.deptno;
    
    左连接.png
    --右外连接,就算emp表中员工的deptno没有与dept表中的deptno对应也会查出来,不过显示dept表中的字段
    select e.*,d.*
    from emp e right join dept d on e.deptno=d.deptno;
    
    右连接.png
    -- 内连接,只会查询出两表deptno一一对应的数据
    select e.*,d.*
    from emp e inner join dept d on e.deptno=d.deptno;
    
    内连接.png
    -- 全连接,不管两张表的数据是否一一对应,全都会查出来
    select e.*,d.*
    from emp e full join dept d on e.deptno=d.deptno;
    
    全连接.jpg

    3、数据库并发问题

    数据库带来的并发问题有:

    1. 丢失更新。
    2. 未确认的相关性(脏读)。
    3. 不一致的分析(非重复读)。
    4. 幻读。
    1.1 丢失更新

    当两个或多个事务选择同一行,然后基于最初选定的值进行更新,会发生丢失更新问题。每个事务都不知道其实事务的存在。最后进行提交的事务会将之前的更新覆盖,导致数据丢失。
      此时需要对数据库进行加锁,两种方式,一种是一开始就锁住,防的很彻底,但是一旦锁的时间过长会影响他人操作;另一种是在最后做更新提交时上锁,将更新失败视为小概率事件,只在最后一步才上锁。
    1.1.1 悲观锁
      当我们对数据进行修改时,首先需要将数据查询出来,在查询语中加锁,即 select .......for update nowwait,在一开始的查询中就开始加锁,避免其他用户更新
    1.1.2 乐观锁
    (a)旧值条件法:
      在更新语句中,使用旧的状态值作为更新的条件(不推荐)
    (b)版本列法:
      当我们设计表的时候往往会多设置两个number/date类型的列,以便以后对表进行拓展。我们将一个number列作为版本列,每次修改数据时,以 限制条件 = 主键 + 版本列号的形式进行修改,同时每修改一次数据,都对版本号进行更新。

    我们通常在冲突较为严重的系统中使用悲观锁,其他情况优先使用乐观锁。
    
    2.1 未确认的相关性(脏读)

    当一个事务读取到另一个事务还未提交的修改时候,产生脏读。
      张三的工资原本是2000,财务不小心将张三工资改成了5000,此时尚未提交,张三查询时发现自己工资变成5000后很开心,此时财务发现数据异常,回滚事务,张三工资又变成了2000,这就叫脏读。

    解决办法:在事务提交之前,其他任何事务都不能读取其修改过的值。

    3.1 不一致的分析(非重复读)
    4.1 幻读

    4、线程的两种创建方式

    第一种:new Thread 重写 Thread 中的 run() 方法。

    Thread thread = new Thread() {
                @Override
                public void run() {
                    while (true) {
                        try {
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                        System.out.println("1: " + Thread.currentThread().getName());
                        System.out.println("2: " + this.getName());
                    }
                }
            };
    

    第二种:Thread 的构造方法中有一个是以 Runnable对象为参数,Runnable是一个接口,当中有run()方法,Thread是implements了Runnable接口,继而实现了run()方法,我们可以在new Thread的时候,通过构造方法传一个Runnable对象,而Runnable对象中又有run()方法,通过这种方式,也能实现线程的创建。

    Thread thread2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        try {
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                        System.out.println("3: " + Thread.currentThread().getName());
                    }
                }
            });
    

    5、SVN多用户开发 --- 代码冲突&解决

    修改同一文件的不同位置引发的冲突

    现在有两个用户:zhangsan 和 lisi。他们需要同时修改一个文件,原始代码如下:

    public void Test{
        System.out.println("Test!");
        return 1;
    }
    

    此时 zhangsan 需要修改第二行代,

    public void Test{
        System.out.println("Test!xingoo");
        return 1;
    }
    

    lisi 则修改第三行代码,

    public void Test{
        System.out.println("Test!");
        return 0;
    }
    

    假设 zhangsan 先提交代码,当 lisi 提交代码时,会提示错误:
      提示用户该文件已经过期,需要先更新文件。
    此时 lisi 需要先进行更新代码,再提交代码。
      这种情况,是最简单的代码冲突样例。不同用户修改了不同位置的代码,因此只需要在提交前进行更新,就可以解决冲突。

    修改同一文件相同位置引发的冲突

    还是这个例子,zhangsan 当前代码如下,版本号为9,

    public void Test{
        System.out.println("Test!xingoo");
        return 1;
    }
    

    lisi 代码如下,版本号为10,

    public void Test{
        System.out.println("Test!xingoo");
        return 0;
    }
    

    此时 lisi 的代码是最新的版本。
    这时,zhangsan 想要修改第三行的返回值如下,

    public void Test{
        System.out.println("Test!xingoo");
        return 2;
    }
    

    这时提交代码会出现提示,发生错误,需要更新,OK,更新之后再次提交还是报错,提示 文件存在冲突!

    观察文件目录,会发现多了三个文件


    SVN文件冲突

    分别打开三个文件:
    其中<<<<<<.mine到====之间为当前用户修改的内容;
    ====到>>>>>.r10为版本库中的内容:

    public void Test{
        System.out.println("Test!xingoo");
    <<<<<<< .mine
        return 2;
    =======
        return 0;
    >>>>>>> .r10
    }
    

    test.txt.mine 记录当前用户修改后的文件内容快照

    public void Test{
        System.out.println("Test!xingoo");
        return 2;
    }
    

    test.txt.r9 记录当前用户修改前,版本库中的内容快照

    public void Test{
        System.out.println("Test!xingoo");
        return 1;
    }
    

    test.txt.r10 记录当前版本库中内容快照

    public void Test{
        System.out.println("Test!xingoo");
        return 0;
    }
    

    通过这四个文件就可以很快速的发现,哪里有冲突。

    至于如何修改:

    直接拷贝mine文件,然后把r10文件的r10后缀去掉,进行更新。更新后,对比mine中的内容在进行修改。

    也可以直接与其他人员进行协商沟通;更新版本库中的内容。

    6、排序算法

    冒泡排序

    左右相邻的两个数进行比较,将大的放到右边,小的放到左边,每次排序都会将最大的数放到序列的最后,最差时间复杂度为 O(n^2),最好情况为 O(n)

    public class BubbleSort {
        public static void sort(int[] num) {
            for (int i = 0; i < num.length - 1; i++) {
                int demo = 0;
                for(int j = 0; j < num.length -1-i; j++) {
                    if (num[j] > num[j + 1]) {
                        demo = num[j];
                        num[j] = num[j + 1];
                        num[j + 1] = demo;
                    }
                }
            }
            for(int k = 0; k < num.length; k++) {
                System.out.print(num[k] + ", ");
            }
        }   
    
        public static void main(String[] args) {
            int[] num = {8, 2, 3, 1, 6, 9, 7};
            sort(num);
        }
    }
    
    插入排序

    将序列的第一个数默认为已排序序列,从第二个数开始(目标数get),对已排序序列进行从后往前的比较,如果已排序序列的当前数大于get,则该元素移动到下一位,重复比较,直到已排序序列的当前数小于get,则在该元素后一个位置将get插入。

    最好空间复杂度为 O(n),最坏为O(n^2),与冒泡排序类似。

    这个过程类似与打扑克摸手牌,此时已排序序列表左手已经抓好的牌,get表示右手新摸到的牌,从右往左进行比较,直到有小于get的牌,在其后插入get。

    public class InsertionSort {
        public static void insertion(int[] arr) {
            int len = arr.length;
            for(int i = 1; i < len; i++) {
                int get = arr[i];
                int j = i -1;
                while(j >= 0 && get < arr[j]) {
                    arr[j + 1] = arr[j];
                    j--;
                }
                arr[j + 1] = get;
            }
        }
        
        public static void main(String[] args) {
            int[] arr = {2, 3, 1, 5, 6};
            insertion(arr);
            for(int i = 0; i < arr.length; i++) {
                System.out.println(arr[i]);
            }
        }
    }
    

    相关文章

      网友评论

          本文标题:面试总结

          本文链接:https://www.haomeiwen.com/subject/nqyeextx.html