面试必考题

文章目录

  • 面试必考题(持续更新中...)
    • 一 基础
      • 1. 数组排序
      • 2. 设计模式
      • 3. JS中的数据类型
      • 4. 常见的RuntimeException
      • 5. ==与equals的区别
      • 6. StringBuilder与StringBuffer的异同点
      • 7. List接口
      • 8. Set接口
      • 9. Map接口
      • 10. HashMap与HashTable的区别
      • 11. Map中的key相同,value怎么处理
      • 12. 线程安全与不安全的集合
      • 13. 关于集合的一道笔试题(经典)
      • 14. 关键字
      • 15. 代码块
      • 16. 类初始化时机
    • 二 框架
      • 1. SpringMVC的工作流程
      • 2. SpringMVC中配置三大组件
      • 3. SpringMVC中常见注解
      • 4. SpringMVC中注解一个类,给类中属性赋默认值
    • 三 MySQL专题
      • 1. MySQL查询基本通式(适用于大部分情况)
      • 2. 多表查询练习
      • 3. MySQL

面试必考题(持续更新中…)

鲁迅说:去年没考,今年一定考,不信我们赌台面包机。

一 基础

1. 数组排序

  • 简单选择排序:对于一组给定的一组记录,经过第一轮比较后等到最小的记录,然后将记录与第一个记录的位置进行交换,接着对不包括第一个记录的其他记录进行第二轮比较,得到最小的记录并与第二个记录进行位置交换;重复过程,直到进行比较的记录只有一个为止。

    public static int[] Sort(int[] arr) {
        for(int i = 0; i < arr.length - 1; i++) {
            for(int j = i + 1; j < arr.length; j++) {
                if(arr[j] < arr[i]) {
                    int temp = arr[j];
                    arr[j] = arr[i];
                    arr[i] = temp;
                }
            }
        }
        return arr;
    }
    
  • 直接插入排序:对于给定一组记录,初始化的时候假设第一个记录自成一个有序序列,其余记录均为无序序列。接着从第二个记录开始依次将当前处理的记录与前面的有序序列进行比较后插入到前面的有序序列中,直到最后一个记录插入到有序序列中为止。

    public static int[] Sort(int[] arr) {
        for(int i = 1; i < arr.length; i++) {
            for(int j = 0; j < i; j++) {
                if(arr[i] < arr[j]) {
                    int temp = arr[j];
                    arr[j] = arr[i];
                    arr[i] = temp;
                }
            }
        }
        return arr;
    }
    
  • 冒泡排序:顾名思义就是整个过程就像泡泡一样上升,单向冒泡排序的基本思想是(假设从小到大),对于给定的n个记录,从第一个记录开始依次对相邻的两个记录进行比较,当前面的记录大于后面的记录时,交换位置,进行一轮比较和交换后,n个记录中最大的记录将位于第n位;然后对前面的(n-1)个记录进行第二轮比较,重复该过程直到进行比较的记录只剩一个为止。

    public static int[] Sort(int[] arr) {
        for(int i = 0; i < arr.length - 1; i++) {
            for(int j = 0; j < arr.length - 1 - i; j++) {
                if(arr[j] > arr[j + 1]) {
                    int temp = arr[j+1];
                    arr[j+1] = arr[j];
                    arr[j] = temp;
                }
            }
            System.out.println("第" + i + "趟:" + Arrays.toString(arr));
        }
        return arr;
    }
    
  • 快速排序:是一种非常高效地排序算法,它采用"分而治之"的思想,把大的拆分成小的,小的再拆分成更小的。

    • 基本思想:
      • 选择一个基准元素,通常选择第一个元素或者最后一个元素
      • 通过一趟排序将待排序的记录分割成独立的两部分,其中一部分记录值均比基准元素小,另一部分均比基准元素大
      • 此时基准元素在其排好序的正确位置
      • 然后分别对这两部分记录用同样的方法继续进行排序,直到整个序列有序
    • 步骤:
      • 设置两个指针left和right,分别指向数组的头部和尾部,并以头部元素元素为基准数base
      • 从右至左扫描,通过偏移right指针,寻找比基准数小的元素,则将其赋值给left指针所指的位置
      • 从左至右扫描,通过偏移left指针,寻找比基准数大的元素,则将其赋值给right指针所指的位置
      • 不断重复2,3步骤,直到left和right指针重合。这样,所有的元素都被扫描了一遍,则将基准数赋值给重复位置。此时,基准数左边都是比它小的数,而右边都是比它大的数
    public static int[] quickSort(int[] arr, int low, int high) {
        int l = low;
        int h = high;
        int base = arr[low];
        while(l < h) {
            while(l < h && arr[h] >= base) {
                h --;
            }
            arr[l] = arr[h];
            while(l < h && arr[l] <= base) {
                l ++;
            }
            arr[h] = arr[l];
        }
        arr[l] = base;
        System.out.println(Arrays.toString(arr));
        if(l - 1 > low) {
            quickSort(arr,low,l - 1);
        }
        if(h + 1 < high) {
            quickSort(arr, h + 1, high);
        }
        return arr;
    }
    

2. 设计模式

  • 单例模式:是一种对象创建型模式,使用单例模式,可以保证一个类只生成唯一的实例对象。也就是说,在整个程序空间中,该类只存在一个实例对象。其实,GOF对单例模式的定义是:保证一个类只有一个实例存在,同时提供能对该实例加以访问的全局访问方法。

    • 饿汉式:所谓饿汉模式就是立即加载,一般情况下再调用getInstance()方法之前就已经产生了实例,也就是在类加载的时候已经产生了。适用于多线程,但这种模式的缺点很明显,就是当单例类很大的时候很占用资源,没有使用该类的时候也会创建该类的实例。因此这种方式适合占用资源少,在初始化的时候就被用到的类。

      //持有私有静态变量,防止被引用
      private static final SingleTonHungary singleTon = new SingleTonHungary();
      //构造方法私有化,防止被实例化
      private SingleTonHungary() {
      
      }
      //提供全局的静态访问方法
      public static SingleTonHungary getInstance() {
          try {
              Thread.sleep(1000);//模拟在创建对象时做的一些准备
          }catch (Exception e) {
              e.printStackTrace();
          }
          return singleTon;
      }
      
    • 懒汉式:懒汉模式就是延迟加载,也叫懒加载。在程序中需要用到的时候再创建实例,这样保证内存不会浪费。针对懒汉模式,直接通过设置同步代码块,使用双重检查锁机制,可以成功的解决线程不安全和效率问题,这也是大多数多线程结合单例模式使用的解决方案。

      //持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载
      //添加volatile关键字,保证内存的可见性,所有线程都会去主存中取数据而不是在线程的缓存中取数据,保证了数据的更新能实时对任何线程可见
      private volatile static SingleTonLazy singleTon = null;
      //构造方法私有化,防止被实例化
      private SingleTonLazy() {
      
      }
      //提供全局的静态访问方法,创建实例,只对第一次创建这个对象的时候上锁,这样效果更好
      public static SingleTonLazy getInstance() {
          try {
              if(null == singleTon) {
                  Thread.sleep(1000);//模拟在创建对象时做的一些准备
                  synchronized (SingleTonLazy.class) {
                      if(null == singleTon) {
                          singleTon = new SingleTonLazy();
                      }
                  }
              }
          }catch (Exception e) {
              e.printStackTrace();
          }
          return singleTon; 
      }
      
    • 静态私有内部类:可以看到这种方式没有显示的进行任何同步操作,如何保证线程安全呢?和饿汉式一样,是靠JVM保证类的静态成员只能被加载一次的特点,这样就从JVM的层面保证了只会有一个对象。在加载一个类时,其内部类不会同时被加载。一个类被加载,当且仅当其某个静态成员(静态域,构造器,静态方法等)被调用时发生。可以说这种方式时实现单例模式的最优解。

      private SingletonStaticInner() {
      		
      }	
      
      private static class SingletonInner {
          private static SingletonStaticInner singleTon = new SingletonStaticInner();
      }
      
      public static SingletonStaticInner getInstance() {
          try {
              Thread.sleep(1000);
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
          return SingletonInner.singleTon;
      }
      
    • 静态代码块:可以说就是一种饿汉方式。

      private static SingletonStaticBlock singleTon = null;
      private SingletonStaticBlock() {
      
      }
      static {
          singleTon = new SingletonStaticBlock();
      }
      public static SingletonStaticBlock getInstance() {
          return singleTon;
      }
      
  • 工厂模式

    • 工厂方法模式:属于类的创建型模式,又被称为多态工厂方法模式,工厂方法模式的意义是定义一个创建一个产品对象的接口,将实际创建工作推迟到子类中,核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色仅仅只负责具体工厂子类必须实现的接口,这样进一步抽象化的好处是使得工厂方法模式可以使在不修改具体工厂角色的情况下引进新的产品,这样解决了简单工厂模式扩展性不好的问题。

      • 模式中包含的角色及其职责:

        • 抽象工厂角色:工厂方法模式的核心,任何工厂都必须实现此接口
        • 具体工厂角色:具体工厂角色是抽象工厂类的一个实现,负责实例化产品对象
        • 抽象角色:工厂方法模式所创建的所有产品对象的父类,它负责描述所有实例所共有的接口
    • 具体角色:工厂方法模式所创建的具体实例化对象

    • 场景:对mysql/orcale数据库中的User表进行操作

      //定义User类
      public class User {
      	private int uid;
      	private String name;
      	
      	public int getUid() {
      		return uid;
      	}
      	public void setUid(int uid) {
      		this.uid = uid;
      	}
      	public String getName() {
      		return name;
      	}
      	public void setName(String name) {
      		this.name = name;
      	}
      }
      
      //定义一个对User表操作的接口
      public interface IUserOpertion {
      	public void insert(User user);
      	public User getUser(int uid);
      }
      
      //定义mysql对User表操作的类
      public class MysqlUserOpertion implements IUserOpertion {
      	@Override
      	public void insert(User user) {
      		System.out.println("在mysql中的user表中插入一条元素");
      	}
      	@Override
      	public User getUser(int uid) {
      		System.out.println("在mysql中的user表得到id为"+uid+"的一条数据");
              return null;
      	}
      }
      
      //定义orcale对User表操作的类
      public class OrcaleUserOpertion implements IUserOpertion {
      	@Override
      	public void insert(User user) {
      		System.out.println("在oracle中的user表中插入一条元素");
      	}
      	@Override
      	public User getUser(int uid) {
      		System.out.println("在oracle中的user表得到id为"+uid+"的一条数据");
      		return null;
      	}
      }
      
      //定义一个工厂接口,用来编写生产访问User表的对象的接口
      public interface ISqlFactory {
      	public IUserOpertion createUserOpertion();
      }
      
      //定义生产mysqlUserOpertion对象的mysql工厂类
      public class MysqlFactory implements ISqlFactory {
      	@Override
      	public IUserOpertion createUserOpertion() {
      		return new MysqlUserOpertion();
      	}
      }
      
      //定义生产orcaleUserOpertion对象的orcale工厂类
      public class OrcaleFactory implements ISqlFactory {
      	@Override
      	public IUserOpertion createUserOpertion() {
      		return new OrcaleUserOpertion();
      	}
      }
      
      //定义一个用户测试类
      public class TestFactory {
      	public static void main(String[] args) {
      		ISqlFactory sqlFactory = new MysqlFactory();
      		IUserOpertion userOpertion = sqlFactory.createUserOpertion();
      		userOpertion.insert(new User());
      		userOpertion.getUser(0);
      	}
      }
      
      //返回结果
      在mysql中的user表中插入一条元素
      在mysql中的user表得到id为0的一条数据
      
  • 抽象工厂模式:是所有形态的工厂模式中最为抽象和最具一般性的模式,抽象工厂模式可以向客户端提供一个接口,使得客户端在不必指定产品的具体类型的情况下,能够创建多个产品族的产品对象。

    • 模式中包含的角色及其职责:

      • 抽象工厂角色:抽象工厂模式的核心,包含对多个产品结构的声明,任何工厂都必须实现此接口
      • 具体工厂角色:具体工厂类时抽象工厂接口的一个实现,负责实例化某个产品族的产品对象
      • 抽象角色:抽象模式所创建的所有产品对象的父类,它负责描述所有实例所共有的公共接口
      • 具体产品角色:抽象工厂模式所创建的具体实例对象
    • 场景:在上面的基础上增加一张Login表,同样是在mysql和orcale中进行操作

      //定义Login类
      public class Login {
      	private int id;
      	private Date date;	
      	public int getId() {
      		return id;
      	}
      	public void setId(int id) {
      		this.id = id;
      	}
      	public Date getDate() {
      		return date;
      	}
      	public void setDate(Date date) {
      		this.date = date;
      	}
      }
      
      //定义一个对Login表操作的接口
      public interface ILoginOpertion {
      	public void insert(Login login);
      	public Login getLogin(int id); 
      }
      
      //定义mysql对Login表操作的类
      public class MysqlLoginOpertion implements ILoginOpertion {
      	@Override
      	public void insert(Login login) {
      		System.out.println("对 MySQL 里的 Login 表插入了一条数据");
      
      	}
      	@Override
      	public Login getLogin(int id) {
      		System.out.println("通过 id 在 MySQL 里的 Login 表得到了一条数据");
      		return null;
      	}
      }
      
      //定义orcale对Login表操作的类
      public class OrcaleLoginOpertion implements ILoginOpertion {
      	@Override
      	public void insert(Login login) {
      		System.out.println("对 Oracle 里的 Login 表插入了一条数据");
      	}
      	@Override
      	public Login getLogin(int id) {
      		System.out.println("通过 uid 在 Oracle 里的 Login 表得到了一条数据");
      		return null;
      	}
      }
      
      //修改工厂接口,增加对Login表操作的接口
      public interface ISqlFactory {
      	public IUserOpertion createUserOpertion();
      	public ILoginOpertion createLoginOpertion();
      }
      
      //修改mysql工厂,生产对Login表操作的类
      public class MysqlFactory implements ISqlFactory {
      	@Override
      	public IUserOpertion createUserOpertion() {
      		return new MysqlUserOpertion();
      	}
      	@Override
      	public ILoginOpertion createLoginOpertion() {
      		return new MysqlLoginOpertion();
      	}
      }
      
      //修改orcale工厂,生产对Login表操作的类
      public class OrcaleFactory implements ISqlFactory {
      	@Override
      	public IUserOpertion createUserOpertion() {
      		return new OrcaleUserOpertion();
      	}
      	@Override
      	public ILoginOpertion createLoginOpertion() {
      		return new OrcaleLoginOpertion();
      	}
      }
      
      //定义一个用户测试类
      public class TestFactory {
      	public static void main(String[] args) {
      		ISqlFactory sqlFactory = new MysqlFactory();
      		IUserOpertion userOpertion = sqlFactory.createUserOpertion();
      		userOpertion.insert(new User());
      		userOpertion.getUser(0);
      		ILoginOpertion loginOpertion = sqlFactory.createLoginOpertion();
      		loginOpertion.insert(new Login());
      		loginOpertion.getLogin(0);
      	}
      }
      
      //返回结果
      在mysql中的user表中插入一条元素
      在mysql中的user表得到id为0的一条数据
      对 MySQL 里的 Login 表插入了一条数据
      通过 id 在 MySQL 里的 Login 表得到了一条数据
      
    • 抽象工厂模式与工厂方法模式的区别:比如说有两个工厂分别生产鼠标与键盘,如果用工厂方法模式,替换生产键盘的工厂方法,就可以把键盘从罗技换到微软;如果用抽象工厂方法模式,只需要换家工厂,就可以替换鼠标与键盘一起。如果产品有10几种,当然用抽象工厂模式一次替换最方便。所以说,抽象工厂就像一家工厂,而工厂方法模式就像是工厂的一种产品生产线。

  • 23种设计模式,分为三大类:

    • 创建型模式,共5种:单例模式,工厂方法模式,抽象工厂模式,建造者模式,原型模式
    • 结构性模式,共七种:适配器模式,装饰者模式,代理模式,外观模式,桥接模式,组合模式,享元模式
    • 行为型模式,共十一种:观察者模式,策略模式,模板方法模式,迭代子模式,责任链模式,命令模式,备忘录模式,状态模式,访问者模式,中介者模式,解释器模式

3. JS中的数据类型

  • 值类型:字符串(String),数字(Number),布尔(Boolean),空(Null),未定义(Undefined)
  • 引用类型:对象(Object),数组(Array),函数(Function)

4. 常见的RuntimeException

  • 空指针异常(NullPointerException)
  • 数组下标越界异常(ArrayIndexOutOfBoundsException)
  • 算术异常(ArithmeticException)
  • 类型强制转换异常(ClassCastException)
  • 输入输出异常(IOException)
  • 操作数据库异常(SQLException)
  • 文件未找到异常(FileNotFoundException)
  • 方法传递参数异常(IllegalArgumentException)
  • 字符串转数字异常(NumberFormatException)

5. ==与equals的区别

  • ==:如果比较的是基本数据类型,则比较的数值是否相等;如果比较的是引用数据类型,则比较的是对象在内存中存放的地址(堆内存地址)。
  • equals():equals()方法不能用于基本数据类型的变量,如果没有对equals()方法重写,则比较的是引用数据类型变量所指向的对象的地址;如果重写了该方法,则根据重写要求来。而像String,Date,包装类(Integer,Byte,Short,Long,Float,Double,Character,Boolean)已经重写了equals()方法,所以比较的是该对象的成员变量值是否相等。

6. StringBuilder与StringBuffer的异同点

  • StringBuilder/StringBuffer表示的字符串对象可以直接进行修改。
  • StringBuilder是Java5引入的,它和StringBuffer的方法完全相同,区别在于它是在单线程环境下使用的,因为它的方法都没有被synchronized修饰,因此理论上它的效率比StringBuffer高。

7. List接口

  • 它是一个元素存取有序的集合
  • 它是一个带有索引的集合,通过索引就可以精确的操作集合中的元素
  • 集合中可以有重复的元素,通过元素的equals()方法,来判断是否为重复的元素
  • 常见子类:
    • ArrayList:数据存储的结构是数组结构。元素增删慢,查找快,由于日常开发中使用最多的功能为查询数据,遍历数据,所以ArrayList为最常用的集合,默认初始化容量为10。
    • LinkedList:数据储存的结构为链表结构。方便元素添加,删除的集合,实际开发中对一个集合元素的添加与删除,涉及到首尾操作,而此集合提供大量首尾操作的方法。
    • Vector:底层结构是数组,线程安全的,增删慢,查询慢

8. Set接口

  • 它是一个元素存取无序的集合
  • 它是一个不带索引的集合
  • 集合中不可以有重复的元素,通过圆的equals()方法来判断是否为重复元素
  • 常见子类:
    • HashSet:采用哈希表结构存储数据,保证元素唯一性的方式依赖于hashCode()与equals()方法。初始容量,数组长度默认16,加载因子为0.75,哈希表为链表数组结合体,存取元素较快,但不能保证迭代顺序与元素存储顺序相同。若存储自定义类型元素,需要重写对象中的hashCode()与equals()方法,建立自己的比较方式。
    • LinkedHashSet:继承于HashSet,它是链表和哈希表组合的一个数据存储结构,自身特性:存储有序,线程不安全集合,运行速度快。

9. Map接口

  • Collection接口中的集合,元素是孤立存在的,向集合中存储元素采用一个个元素的方式存储
  • Map中的集合,元素是成对存在的,每个元素由键与值两部分组成,通过键找到对应的值
  • Collection中的集合称为单列集合,Map中的集合称为双列集合
  • Map集合中不能有重复的键,值是可以重复的,每个键只能对应一个值
  • 常见子类:
    • HashMap:存储数据采用的哈希表结构,元素存储顺序不能保证一致,由于要保证键的唯一性,不重复,需要重写键的hashCode()与equals()方法。
    • LinkedHashMap:继承于HashMap,它是链表和哈希表组合的一个数据存储结构。链表结构可以保证元素的存储有序,通过哈希表结构可以保证键的唯一,不重复,需要重写键的hashCode()与equals()方法。

10. HashMap与HashTable的区别

  • HashMap是线程不安全的,HashTable是线程安全的,所以HashMap的效率比HashTable高。
  • HashMap的key与value均可为null,HashTable的key与value均不可为null。如果HashMap用get()返回null,并不能表明HashMap不存在这个key,如果需要判断HashMap中是否包含某个key,则需要用containsKey()来判断。
  • HashMap的初始容量为16,必须为2的指数;HashTable的初始容量为11,增加的方式为old*2+1。
  • HashMap在多个线程访问时需要为它的方法实现同步,而HashTable不用,其实就是第一条。

11. Map中的key相同,value怎么处理

  • HashMap中判断key是否存在是先比较key的hashCode,再比较equals()方法是否相等,所以需要重写hashCode和equals()方法方可在添加重复元素时,即添加key相同的键值对时,后面的value会自动覆盖,但不会报错;如果需要对value叠加,则在调用put()方法之前用containsKey()方法判断是否有重复的键值,如果有,则用get()方法获取原有的value,再加上新的value即可。

12. 线程安全与不安全的集合

  • 线程安全的集合:Vector,HashTable
  • 线程不安全的集合:ArrayList,LinkedList,HashSet,LinkedHashSet,HashMap,LinkedHashMap
  • 如何将线程不安全的集合变成线程安全的集合,在Collections工具类中已提供相关的API。

13. 关于集合的一道笔试题(经典)

  • 已知:一个HashMap集合,User有name和age属性。请写一个方法实现对HashMap的排序功能,该方法接收HashMap形参,返回类型为HashMap,要求对HashMap中的User的age倒序排序进行排序,排序时key=value键值对不得拆散。

  • 分析:集合排序可以使用Collections类中的sort()方法进行排序,因为HashMap集合存储无序,而它的子类LinkedHashMap是存储有序的,看似返回HashMap类型,实则利用多态返回是它的子类LinkedHashMap类型。

    public class User {
    	private String name;
    	private int age;	
    	public User() {
    	}	
    	public User(String name,int age) {
    		this.name = name;
    		this.age = age;
    	}	
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public int getAge() {
    		return age;
    	}
    	public void setAge(int age) {
    		this.age = age;
    	}
    	@Override
    	public String toString() {
    		return "User [name=" + name + ", age=" + age + "]";
    	}
    }
    
    public class Test {
    	public static void main(String[] args) {
    		HashMap<Integer, User> map = new HashMap<Integer, User>();
    		map.put(1, new User("张三",18));
    		map.put(2, new User("李四",19));
    		map.put(3, new User("王五",20));
    		map = sortHashMap(map);
    		Set<Entry<Integer, User>> entrySet = map.entrySet();
    		for (Entry<Integer, User> entry : entrySet) {
    			System.out.println(entry.getKey() + " " + 							entry.getValue().toString());
    		}
    	}	
    	public static HashMap<Integer, User> sortHashMap(HashMap<Integer, User> map){
    		Set<Entry<Integer, User>> entrySet = map.entrySet();
    		List<Entry<Integer, User>> list = new ArrayList<Entry<Integer, User>>(entrySet);
    		Collections.sort(list, new Comparator<Entry<Integer, User>>() {
    			@Override
    			public int compare(Entry<Integer, User> o1, Entry<Integer, User> o2) {
    				return o2.getValue().getAge() - o1.getValue().getAge();
    			}
    		});
    		HashMap<Integer, User> sortHashMap = new LinkedHashMap<Integer, User>(); 
    		for (Entry<Integer, User> entry : list) {
    			sortHashMap.put(entry.getKey(), entry.getValue());
    		}
    		return sortHashMap;
    	}
    }
    

14. 关键字

  • transient:用transient标记的成员变量不参与序列化过程。
  • volatile:① 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改某个变量的值,这新值对其他线程来说是立即可见的;② volatile的本质是告诉JVM当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取。
  • static:使用stai修饰的成员变量称为静态变量,该变量属于类,而不属于类的某个实例,可以通过类名直接调用。
  • synchronized:可用来给对象和方法或者代码块加锁,当它锁定一个方法或者一个代码块时,同一时刻最多只有一个线程执行这段代码,其他线程会被阻塞住,一直等到锁被释放。

15. 代码块

  • 局部代码块:定义在方法或语句中

    • 以“{}”划定的代码区域,此时只需要关注作用域不同即可
    • 方法和类都是以代码块划定边界的
  • 构造代码块:定义在类中成员位置

    • 优先于构造方法执行,用于执行所有对象均需要的初始化动作
    • 每创建一个对象均会执行一次构造代码块
  • 静态代码块:定义在类中成员位置,使用static修饰的代码块

    • 它优先于主方法,构造代码块,当以任意形式第一次使用该类时执行
    • 该类不管创建多少对象,静态代码块只执行一次
    • 可用于给静态变量赋值,用于给类进行初始化

16. 类初始化时机

  • 创建类的实例,也就是new一个对象

  • 访问某个类或接口的静态变量,或者对该静态变量赋值

  • 调用该类的静态方法

  • 反射

  • 初始化一个类的子类,会首先初始化该子类的父类

  • JVM启动时标明的启动类,即文件名和类名相同的类

二 框架

1. SpringMVC的工作流程

  1. 用户发送请求至前端控制器

  2. 前端控制器收到请求调用处理器映射器

  3. 处理器映射器根据请求url找到具体的处理器,生成处理器对象以及处理器拦截器(如果有则生成)一并返回给前端控制器

  4. 前端控制器通过处理器适配器调用处理器

  5. 执行处理器(Controller,也叫后端控制器)

  6. 处理器执行完成后返回ModelAndView

  7. 处理器适配器将处理器执行结果ModelAndView返回给前端控制器

  8. 前端控制器将ModelAndView传给视图解析器

  9. 视图解析器解析后返回具体View

  10. 前端控制器对View进行渲染视图(即将模型数据填充至视图中)

  11. 前端控制器响应用户


2. SpringMVC中配置三大组件

<!-- 直接配置注解式处理器引射器和处理器适配器比较麻烦,所以在springmvc.xml中使用注解驱动来加载 -->
<mvc:annotation-driven />
<!-- 视图解析器使用SpringMVC框架默认的InternalResourceViewResolver,支持JSP视图解析 -->
<!-- 最终jsp物理地址:前缀+逻辑视图名+后缀 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	<property name="prefix" value="/WEB-INF/jsp/" />
	<property name="suffix" value=".jsp" />
</bean>

3. SpringMVC中常见注解

//在使用注解前需要先配置web.xml,springmvc.xml文件
<!-- web.xml文件 -->
<!-- springmvc的前端控制器 -->
<servlet>
	<servlet-name>e3-manager-web</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<!-- contextConfigLocation不是必须的, 如果不配置contextConfigLocation, springmvc的配		置文件默认在:WEB-INF/servlet的name+"-servlet.xml" -->
	<init-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:spring/springmvc.xml</param-value>
    </init-param>
	<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
	<servlet-name>e3-manager-web</servlet-name>
	<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- springmvc.xml文件 -->
<!-- spring将自动扫描指定包及其子包下标注了@Component@Repository@Service@Controller注解的类,将其变成由spring管理的bean。多个包之间用,分隔 -->
<!-- 注解需要开启对应的解析器,比如依赖注入的时候使用注解就必须开启<context:annotation-config/>,但是此配置就已经默认开启了解析器 -->
<context:component-scan base-package="cn.e3mall.controller" />
@Component@Repository@Service@Controller:该四种注解都是注解在类上,被注解的类将被spring初始化为一个bean,将由spring统一管理。

@RequestMapping:用于处理请求地址映射,可以作用在类和方法上。在class上添加则指定通用请求前缀,限制此类下所有方法的请求url必须以请求前缀开头。

@RequestParam:用于将请求参数区数据映射到功能处理方法的参数上。

@PathVariable:可以根据value获取请求路径上相对应的值。

@RequestBody:用于读取http请求的json数据,将json数据转换为java对象并绑定到Controller方法的参数上。

@ResponseBody:实现将Controller方法返回java对象转换为json响应给客户端,即Controller方法返回字符串不经过视图解析器。

@Value:给bean中的属性赋默认值。

@Autowried:自动注入,默认按类型装配依赖对象,默认情况下它要求对象必须存在,如果允许null值,可以设置它的required属性为false,如果有多个类型一样的bean候选者-即一个接口或父类有多个实现类或者子类可以为其注入,则需要结合@Qualifier注解一起使用,就可以按名称进行装配。

@Resource:默认按照名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行安装名称查找;如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。

4. SpringMVC中注解一个类,给类中属性赋默认值

<!-- 加载配置文件 -->
<context:property-placeholder location="classpath:resource/resource.properties"/>
#resource.properties中内容
#图片上传服务器地址
IMAGE_SERVER_URL=http://192.168.25.133/
//controller中使用@value注解读取配置文件中键所对应的值赋给该属性
@Value("${IMAGE_SERVER_URL}")
private String IMAGE_SERVER_URL;

三 MySQL专题

1. MySQL查询基本通式(适用于大部分情况)

select 字段名1,字段名2,... from A表 a
left join B表 b on A.bid = b.id
where 条件1 and 条件2 
group by 字段n having 条件n
order by 字段n (desc)
limit 记录数;

2. 多表查询练习

/*创建部门表*/
CREATE TABLE dept(
	deptno		INT 	PRIMARY KEY,
	dname		VARCHAR(50),
	loc 		VARCHAR(50)
);

/*创建雇员表*/
CREATE TABLE emp(
	empno		INT 	PRIMARY KEY,
	ename		VARCHAR(50),
	job		VARCHAR(50),
	mgr		INT,
	hiredate	DATE,
	sal		DECIMAL(7,2),
	COMM 		DECIMAL(7,2),
	deptno		INT,
	CONSTRAINT fk_emp FOREIGN KEY(mgr) REFERENCES emp(empno)
);

/*创建工资等级表*/
CREATE TABLE salgrade(
	grade		INT 	PRIMARY KEY,
	losal		INT,
	hisal		INT
);

/*创建学生表*/
CREATE TABLE stu(
	sid		INT 	PRIMARY KEY,
	sname		VARCHAR(50),
	age		INT,
	gander		VARCHAR(10),
	province	VARCHAR(50),
	tuition		INT
);
#查出至少有一个员工的部门。显示部门编号、部门名称、部门位置、部门人数。
select d.deptno,d.dname,d.loc,count(e.deptno) from dept d
left join emp e on e.deptno = d.deptno
group by e.deptno having count(e.deptno) >= 1;

select d.deptno,d.name,d.loc,z.cnt from dept d
inner join (select deptno,count(*) cnt from emp group by deptno) z 
on d.deptno = e.deptno;
#列出薪金比关羽高的所有员工。
select * from emp e 
where e.sal > (select sal from emp where ename = '关羽') order by e.sal desc;
#列出所有员工的姓名及其直接上级的姓名。
 select e.ename,z.ename from emp e left join emp z on e.mgr = z.empno;
#列出受雇日期早于直接上级的所有员工的编号、姓名、上级姓名、受雇日期、上级受雇日期、部门名称。
select e.empno,e.ename,m.ename,e.hiredate,m.hiredate,d.dname from emp e 
inner join emp m on e.mgr = m.empno
inner join dept d on e.deptno = d.deptno
where e.hiredate < m.hiredate;
#列出部门名称和这些部门的员工信息,同时列出那些没有员工的部门。
select d.dname,e.* from dept d left join emp e on d.deptno = e.deptno;
#列出所有文员的姓名及其部门名称,部门的人数。
select e.ename,d.dname,m.cnt from emp e 
left join dept d on e.deptno = d.deptno 
left join (select deptno,count(*) cnt from emp group by deptno) m on e.deptno = m.deptno 
where e.job = '文员';
#列出最低薪金大于15000的各种工作及从事此工作的员工人数。
#即什么工作的最低工资大于15000,统计其人数
select job,count(*) from emp group by job having min(sal) > 15000;
#列出在销售部工作的员工的姓名,假定不知道销售部的部门编号。
select e.ename from emp e 
where e.deptno = (select deptno from dept where dname = '销售部');
#列出薪金高于公司平均薪金的所有员工信息,所在部门名称,上级领导,工资等级。
select e.*,d.dname,m.ename,s.grade from emp e 
left join dept d on d.deptno = e.deptno 
left join emp m on e.mgr = m.empno 
left join salgrade s on e.sal between s.losal and s.hisal 
where e.sal > (select avg(sal) from emp) order by sal desc;
#列出与庞统从事相同工作的所有员工及部门名称。
select e.*,d.dname from emp e 
left join dept d on e.deptno = d.deptno 
where e.job = (select job from emp where ename = '庞统');
#列出薪金高于在部门30工作的所有员工的薪金的员工姓名和薪金、部门名称。
select e.ename,e.sal,d.dname from emp e 
left join dept d on d.deptno = e.deptno 
where e.sal > (select max(sal) from emp group by deptno having deptno = 30) 
order by e.sal desc;
#列出每个部门的员工数量、平均工资。
select d.dname,e.avg,e.cnt from dept d 
left join (select avg(sal) avg,count(*) cnt,deptno from emp group by deptno) e on d.deptno = e.deptno; 

3. MySQL

你可能感兴趣的:(面试必考题)