Java校招面试汇总

  • 一、JavaSE
    • Ⅰ、基础知识
      • 1、Java中引用数据类型有哪些,它们与基本数据类型有什么区别?
      • 2、Java中的自动装箱与拆箱
      • 3、==和equals()的区别
      • 4、static和final的区别
      • 5、什么是内部类?内部类的作用
      • 6、抽象类和接口的区别
      • 7、instanceof关键字的作用
      • 8、什么是泛型?泛型的好处
      • 9、Java中创建对象的几种方式?
      • 10、Java的四种引用
      • 11、Files的常用方法都有哪些?
      • 12、Java的反射机制
      • 13、反射的实现方式
      • 14、Java反射API有哪些?
      • 15、反射机制的优缺点
      • 16、如何利用反射创建对象?
      • 17、Java中哪些地方用到了反射?
    • Ⅱ、字符串
      • 1、String、StringBuffer和StringBuilder区别
      • 2、String str="i" 与 String str=new String("i")一样吗?
      • 3、String 类的常用方法都有那些?
      • 4、String s = new String("xyz"); 创建了几个StringObject?是否可以继承String类?
      • 5、String s = "a"+"b"+"c"+"d"; 创建了几个对象?
      • 6、equals ()使用时的注意事项
    • Ⅲ、集合
      • 1、说说Java中常用的容器有哪些?
        • Collection
        • Map
      • 3、迭代器 Iterator 是什么?
      • 4、如何边遍历边移除 Collection 中的元素?
      • 5、Iterator 和 ListIterator 有什么区别?
      • 6、如何实现数组和 List 之间的转换?
      • 7、ArrayList和LinkedList的区别
      • 8、ArrayList 和 Vector 的区别是什么?
      • 9、插入数据时,ArrayList、LinkedList、Vector谁速度较快?
      • 10、多线程场景下如何使用 ArrayList?
      • 11、说一下HashSet的实现原理?
      • 12、HashSet与HashMap的区别
      • 10、HashMap的实现原理
      • 11.说说HashMap的put方法执行流程?
      • 12.说说HashMap的get方法执行流程?
      • 13,哈希冲突有几种解决办法?
      • 14、HashMap 与 HashTable的区别
      • 15、ConcurrentHashMap 的实现原理
      • 16、ConcurrentHashMap 不支持 key 或者 value 为 null 的原因?
      • 17.说说ArrayList 的扩容机制?
      • 18.Array和ArrayList有何区别?
      • 19.comparable和comparator的区别?
      • 20.Collection和Collections有什么区别?
    • Ⅳ、异常
      • 1、如何选择异常类型?
      • 2、JVM 是如何处理异常的?
      • 3、throw 和 throws 的区别是什么?
      • 4、Java常见异常有哪些?
    • Ⅴ、IO流
      • 1、Java中的IO流
      • 2、什么是 java 序列化?
  • 二、多线程
    • 1、线程和进程的区别是什么?
    • 2、Java 实现线程有哪几种方式?
    • 3、线程有哪些状态?
    • 4、start()和 run()有什么区别?
    • 5、如何停止一个线程?
    • 6、如何避免死锁?
    • 7、 Runnable接口和Callable接口的区别
    • 8、如何确保线程安全?
    • 9、什么是线程池?有几种创建方式?
    • 10、sleep()和wait()有什么区别?
  • 三、数据库
    • 1、CHAR 和 VARCHAR 区别?
    • 2、CHAR 和 VARCHAR 如何选择?
    • 3、什么是索引?
    • 4、索引的几种类型或分类?
    • 5、索引的优缺点
    • 6、索引的设计原则?
    • 7、索引的数据结构?
    • 8、Hash索引和 B+ 树索引的区别及优劣
    • 9、为什么用 B+ 树做索引而不用哈希表做索引?
    • 10、MyISAM 和 InnoDB 的区别?
    • 11、什么是数据库的事务?
    • 12、什么是事务的四大特性(ACID)?
    • 13、Myql 中的事务回滚机制
    • 14、 对MySQL的锁了解吗?
    • 15、SQL语法
      • 基础操作
      • 数据库操作
      • 表的操作
      • 数据操作
      • 数据类型
      • 列属性
      • SELECT
      • UNION
      • 子查询
      • 连接查询(join)
      • TRUNCATE
      • 备份与还原
      • 视图
      • 锁表
  • 四、框架
    • Ⅰ、Spring
      • 1、使用Spring框架的好处是什么?
      • 2、Spring的IOC理解
      • 3、什么是DI?
      • 4、什么是AOP?
      • 5、Spring通知(Advice)有哪些类型?
      • 6、Spring容器的启动流程
      • 7、Spring框架使用了哪些设计模式?
      • 8、BeanFactory和ApplicationContext有什么区别?
      • 9、Spring Bean的生命周期?
      • 10、Spring中bean的作用域
      • 11、Spring框架中的Bean是线程安全的么?如果线程不安全,那么如何处理?
      • 12、Spring基于xml注入bean的几种方式
      • 13、Spring的自动装配
      • 14、 springmvc常用到的注解,作用、原理是什么?
      • 16、 SpringMVC执行流程和原理
      • 17、有哪些重要的Spring注解?
      • 18、Spring事务的实现方式和实现原理
      • 19、Spring如何管理事务的?
    • Ⅱ、 MyBaits
      • MyBaits的优缺点
      • 2、相对于JDBC,MyBatis做了哪些改进?
      • 3、 MyBatis编程步骤
      • 4、说说MyBatis的工作原理
      • 5、#{} 和 ${} 的区别是什么?
      • 6、当实体类中的属性名和表中的字段名不一样 ,怎么办 ?
      • 7、通常一个mapper.xml文件,都会对应一个Dao接口,这个Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗?
      • 8、MyBatis的Xml映射文件中,不同的xml映射文件,id是否可以重复?
      • 9、Mybatis是如何进行分页的?分页插件的原理是什么?
      • 10、 MyBatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?
      • 11、MyBatis是否支持延迟加载?如果支持,它的实现原理是什么?
      • 12、MyBatis动态sql有什么用?执行原理?有哪些动态sql?
      • 13、使用MyBatis的mapper接口调用时有哪些要求?
      • 14、在mapper中如何传递多个参数?
      • 15、 一对一,一对多的关联查询 ?
      • 16、MyBatis实现一对一和一对多有几种方式?具体怎么操作的?
      • 17、 什么是MyBatis的接口绑定?有哪些实现方式?
    • Ⅲ、SpringBoot
      • 1、SpringBoot 有哪些优点?
      • 2、SpringBoot 的核心注解是哪个?它主要由哪几个注解组成的?
      • 3、SpringBoot 的核心配置文件有哪几个?它们的区别是什么?
      • 4、SpringBoot Starter的工作原理
      • 5、 SpringBoot 配置加载顺序
      • 6、 SpringBoot 有哪几种读取配置的方式?
      • 7、 SpringBoot 实现热部署有哪几种方式?
      • 8、 如何重新加载SpringBoot上的更改,而无需重新启动服务器?
      • 9、SpringBoot事物的使用
      • 10、什么是 JavaConfig?
      • 11、SpringBoot 中如何实现定时任务 ?
      • 12、 有哪些spring-boot-maven-plugin命令?
      • 13、SpringBoot的自动配置原理是什么?
      • 14、 什么是YAML? 它有哪些优点?
      • 14、SpringBoot多数据源拆分的思路
      • 15、SpringBoot多数据源事务如何管理?
      • 16、SpringBoot 打成的 jar 和普通的 jar 有什么区别 ?

一、JavaSE

Ⅰ、基础知识

1、Java中引用数据类型有哪些,它们与基本数据类型有什么区别?

只要不是基本数据类型,都是引用数据类型;
创建引用数据类型时,在栈内存中存储引用地址,在堆内存存储具体的值;
栈内存引用地址指向堆内存空间;
用equals()方法比较内存地址,==和!=是比较数值的。

2、Java中的自动装箱与拆箱

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

3、==和equals()的区别

  • ==

    基本类型:比较的是值是否相同;
    引用类型:比较的是引用是否相同;
    
String x = "string";
String y = "string";
String z = new String("string");
System.out.println(x==y); // true
System.out.println(x==z); // false
System.out.println(x.equals(y)); // true
System.out.println(x.equals(z)); // true
  • equals()

    equals 本质上就是 ==,String 和 Integer 等类重写了 equals 方法,把它变成了值比较;
    String、Date、File、包装类都重写了Object类的equals方法。
    
class Cat {public Cat(String name) {this.name = name;
}
private String name;
public String getName() {return name;
}
public void setName(String name) {this.name = name;
}
}
Cat c1 = new Cat("叶痕秋");
Cat c2 = new Cat("叶痕秋");
System.out.println(c1.equals(c2)); // false

4、static和final的区别

5、什么是内部类?内部类的作用

  • 成员内部类

    可以无条件访问外部类的所有成员,包括私有的和静态的;
    当成员内部类拥有和外部类同名的成员时,访问时默认是成员内部类的成员。
    
  • 局部内部类

    定义在方法或者作用域里面的类,访问权限仅限于方法内或者该作用域内。
    
  • 静态内部类

    可以不依赖外部类而实例化,不可以有与外部类相同的成员,不可以访问外部类非静态的成员。
    

6、抽象类和接口的区别

1. 成员区别:

  • 抽象类

    抽象类有构造方法,可以用子类实例化;
    既可以有成员变量也可以有常量;
    成员方法既可以有抽象的也可以有非抽象的。
    
  • 接口

    接口没有构造方法,只能定义常量,默认修饰符:public static final;
    可以用default和static修饰。
    

2. 关系区别:

抽象类可以继承类,可以单继承,不能多继承,但可以多层继承;
类实现借口,可以实现单接口,也可以实现多接口;
一个类在继承父类的同时,还可以实现多个接口;
接口继承接口,可以多继承,因为接口的方法一般默认是抽象的。

3. 使用原理不同:

当关注事物本质的时候,用抽象类;当关注操作的时候,用接口;
抽象类表示这个对象是什么,例如Student类和Teacher都是Person类,都有人类的共有属性,可以定义一个人类的抽象类;
接口表示这个对象能做什么,是对类行为的约束;
即它的实现类可以做什么,但实现类是什么,怎么做的,接口并不关心;
例如人类吃东西,鸟类也吃东西,它们都有一个“吃东西”的方法,可以定义一个“吃东西”的接口,让人类和鸟类都去实现它。

4. 功能不同:

抽象类的功能要远大于接口,由于一个类只能继承一个类,所以在一个抽象类中,你必须继承或编写其所有子类的所有共有属性;
虽然接口的功能较少,但接口只是对一个动作的描述,一个类可以实现多个接口,从而在设计阶段降低难度。

7、instanceof关键字的作用

instanceof严格来说是Java中一个双目运算符,用来判断一个对象是否为一个类的实例。用法如下:
boolean result = obj(对象)instanceof  int i = 0;
System.out.println(i instanceof Integer);//编译不通过 i必须是引用类型,不能是基本类型System.out.println(i instanceof Object);//编译不通过
Integer integer = new Integer(1);
System.out.println(integer instanceof Integer);//true
//false ,在 JavaSE规范 中对 instanceof 运算符的规定就是:如果 obj 为 null,那么将返回false。
System.out.println(null instanceof Object);

8、什么是泛型?泛型的好处

泛型是编写代码时可以规定一种通用的数据类型,而不是具体的;
比如ArrayList就是一种泛型,提高代码的复用性。

9、Java中创建对象的几种方式?

  • 通过new。
  • 通过反射机制Class类的newInstance()方法或者Constructor类中的newInstance()方法。
String str1 = String.Class.newInstance();
String str2 = String.Class.getConstructor().newInstance();
  • 克隆

    实现 Cloneable 接口,重写 Object 类的 clone() 方法;
    没有实现 Cloneable 接口,会抛出CloneNotSupportedException异常;
    Object 类提供的 clone() 方法,访问权限是 protected,所以如果不重写 clone() 方法,是没有权限调用的。
    
  • 通过对象的反序列化

String str1 = new String("反序列化一个对象");
// 序列化一个girlFriend
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("String.obj"));
objectOutputStream.writeObject(String1);
objectOutputStream.close();
// 反序列化出来
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("String.obj"));
String str2 = (String) objectInputStream.readObject();
objectInputStream.close();

10、Java的四种引用

  • 强引用

    在内存不足时也不会被回收,使用方法为:String str = new String("str");
    
  • 软引用

    在内存不足时会被回收,使用方法为:
    SoftReference<String> wrf = new SoftReference<String>(new String("str"));
    
  • 弱引用

    只要jvm垃圾回收器发现了它,就将其回收,使用方法为:
    WeakReference<String> wrf = new WeakReference<String>(str);
    
  • 虚引用

    和弱引用差不多,不同的是虚引用在被JVM回收之前会被放入ReferenceQueue中,
    而其他引用是在被JVM回收之后放入ReferenceQueue。
    

11、Files的常用方法都有哪些?

Files. exists():检测文件路径是否存在。
Files. createFile():创建文件。
Files. createDirectory():创建文件夹。
Files. delete():删除一个文件或目录。
Files. copy():复制文件。
Files. move():移动文件。
Files. size():查看文件个数。
Files. read():读取文件。
Files. write():写入文件。

12、Java的反射机制

对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法。

13、反射的实现方式

1、类名.class,不会对类进行任何初始化;
2、Class.forName(“类的路径”),对类进行静态初始化;
3、对象名.getClass(),对类进行静态和非静态初始化;
4、基本类型的包装类,调用包装类的Type属性来获得该包装类的Class对象

14、Java反射API有哪些?

1、Class:表示正在运行的Java应用程序中的类和接口,所有获取对象的信息都需要Class类来实现。
2、Field:提供有关类和接口的属性信息,以及对它的动态访问权限。
3、Constructor:提供关于类的单个构造方法的信息以及它的访问权限 4、Method:提供类或接口中某个方法的信息

15、反射机制的优缺点

  • 优点:
    能动态的获取类的实例,提高代码灵活性

  • 缺点:
    ①反射性能较低,因为需要对字节码进行解析,对内存中的对象进行解析
    解决方案:

  • 通过setAccessible(true)关闭JDK的安全检查来提升反射速度;
  • 多次创建一个类的实例时,有缓存会快很多
  • ReflflectASM工具类,通过字节码生成的方式加快反射速度

②程序不安全,因为反射会获取类的私有属性和方法

16、如何利用反射创建对象?

  • 使用 Class 对象的 newInstance()方法来创建该 Class 对象对应类的实例,但是这种方法要求该 Class 对象对应的类有默认的空构造器。
  • 调用 Constructor 对象的 newInstance(),先使用 Class 对象获取指定的 Constructor 对象,再调用 Constructor 对象的 newInstance()方法来创建 Class
    对象对应类的实例,通过这种方法可以选定构造方法创建实例。

17、Java中哪些地方用到了反射?

  • 工厂模式中的简单工厂模式优化
  • 代理模式中的动态代理方式实现
  • Java JDBC数据库操作

Ⅱ、字符串

1、String、StringBuffer和StringBuilder区别

  1. 数据可变

    String 底层使用一个不可变的字符数组 private final char value[];
    StringBuffer 和 StringBuilder 都继承了 AbstractStringBuilder,
    底层使用的是可变字符数组: char[] value;
    
  2. 线程安全

    StringBuilder 是线程不安全的,效率较高;
    而 StringBuffer 是线程安全的,效率较低。
    通过他们的 append() 方法看, StringBuffer 有同步锁,而 StringBuilder 没有。
    

2、String str=“i” 与 String str=new String(“i”)一样吗?

不一样,因为内存的分配方式不一样。
String str="i"的方式,Java 虚拟机会将其分配到常量池中;
而String str=new String("i") 则会被分到堆内存中。

代码示例:

String x = "叶痕秋";
String y = "叶痕秋";
String z = new String("叶痕秋");
System.out.println(x == y); // true
System.out.println(x == z); // false
String x = "叶痕秋" 的方式,Java 虚拟机会将其分配到常量池中,而常量池中没有重复的元素,
比如当执行“叶痕秋”时,java虚拟机会先在常量池中检索是否已经有“叶痕秋”,
如果有那么就将“叶痕秋”的地址赋给变量,如果没有就创建一个,然后在赋给变量;
而 String z = new String(“叶痕秋”) 则会被分到堆内存中,即使内容一样还是会创建新的对象。

3、String 类的常用方法都有那些?

indexOf():返回指定字符的索引
charAt():返回指定索引处的字符
replace():字符串替换
trim():去除字符串两端空白
split():分割字符串,返回一个分割后的字符串数组
getBytes():返回字符串的 byte 类型数组
length():返回字符串长度
toLowerCase():将字符串转成小写字母
toUpperCase():将字符串转成大写字符
substring():截取字符串
equals():字符串比较

4、String s = new String(“xyz”); 创建了几个StringObject?是否可以继承String类?

先判断字符串常量池里面有没有"xyz"字符串对象,
如果有,就不会在常量池里面创建"xyz"对象,但是会在堆内存里面创建一"xyz"个对象,
并将对象地址返回给 str 变量,这种情况创建一个对象;
如果常量字符串没有,就会现在常量池里面创建"xyz"字符串,
然后再在堆内存里面创建"xyz"字符串对象,这种情况会创建两个对象
至于String类是否继承,答案是否定的,因为String默认final修饰,是不可继承的。

5、String s = “a”+“b”+“c”+“d”; 创建了几个对象?

String s1 = "a";
String s2 = s1 + "b";
String s3 = "a" + "b";
System.out.println(s2 == "ab");//flase
System.out.println(s3 == "ab");//true
这说明javac编译可以对字符串常量直接相加的表达式进行优化,
不必要等到运行期再去进行加法运算处理,而是在编译时去掉其中的加号,
直接将其编译成一个这些常量相连的结果。
String  s = "a"+"b"+"c"+"d"; 相当于直接定义了一个”abcd”的字符串,
所以,上面的代码应该只创建了一个String对象。
String s ="a" + "b" +"c" + "d";
System.out.println(s== "abcd");//true

6、equals ()使用时的注意事项

常量和确定值要放在equals的左边,如果把不确定的值放在左边,不确定值为null时,就会报错。
String str = null;
System.out.println(str.equals("123"));

Ⅲ、集合

1、说说Java中常用的容器有哪些?

容器主要包括 Collection 和 Map 两种,Collection 存储着对象的集合,而 Map 存储着键值对(两个对象)的映射表。

Collection

1、Set:

  • TreeSet

    基于红黑树实现,支持有序性操作,例如根据一个范围查找元素的操作。
    但是查找效率不如 HashSet,HashSet 查找的时间复杂度为 O(1),TreeSet 则为 O(logN)。
    
  • HashSet:

    基于HashMap实现,支持快速查找,但不支持有序性操作,
    并且失去了元素的插入顺序信息,也就是说使用 Iterator 遍历 HashSet 得到的结果是不确定的。
    
  • LinkedHashSet

    是 HashSet 的子类,并且其内部通过 LinkedHashMap 来实现的,
    内部使用双向链表维护元素的插入顺序。
    

2、List:

  • ArrayList

    基于动态数组实现,支持随机访问。
    
  • Vector

    和 ArrayList 类似,但它是线程安全的。
    
  • LinkedList

    基于双向链表实现,只能顺序访问,但是可以快速地在链表中间插入和删除元素,
    不仅如此,LinkedList 还可以用作栈、队列和双向队列。
    
Map
  • TreeMap
  • HashMap
    数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突).JDK1.8以后在解决哈希冲突时有了较
    大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间
  • HashTable
    数组+链表组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的
  • LinkedHashMap
    继承自 HashMap,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,LinkedHashMap 在上面结构的基础上,增加
    了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的
    操作,实现了访问顺序相关逻辑。

3、迭代器 Iterator 是什么?

Iterator 接口提供遍历任何 Collection 的接口,可以从一个 Collection 中使用迭代器方法来获取迭代器实例。
特点是只能单向遍历,但是更加安全,因为它可以确保在当前遍历的集合元素被更改
的时候,就会抛出 ConcurrentModificationException 异常。

4、如何边遍历边移除 Collection 中的元素?

使用 Iterator.remove() 方法,如下:

 Iterator<Integer> it = list.iterator();while(it.hasNext()){*// do something*it.remove();}

5、Iterator 和 ListIterator 有什么区别?

  • Iterator 可以遍历 Set 和 List 集合,而 ListIterator 只能遍历 List。
  • Iterator 只能单向遍历,而 ListIterator 可以双向遍历(向前/后遍历)。
  • ListIterator 实现 Iterator 接口,然后添加了一些额外的功能,比如添加一个元素、替换一个元素、获取前面或后面元素的索引位置。

6、如何实现数组和 List 之间的转换?

  • 数组转 List:使用 Arrays. asList(array) 进行转换。
  • List 转数组:使用 List 自带的 toArray() 方法。
    代码示例:
 // list to arrayList<String> list = new ArrayList<String>();list.add("123");list.add("456");list.toArray();// array to listString[] array = new String[]{"123","456"};Arrays.asList(array);

7、ArrayList和LinkedList的区别

  • 数据结构实现:
    ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实现。
  • 随机访问效率:
    ArrayList 比 LinkedList 在随机访问的时候效率要高,因为 LinkedList 是线性的数据存储方式,所以需要移动指针从前往后依次查找。
  • 增加和删除效率:
    在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为ArrayList 增删操作要影响数组内的其他数据的下标。
  • 内存空间占用:
    LinkedList 比 ArrayList 更占内存,因为 LinkedList 的节点除了存储数据,还存储了两个引用,一个指向前一个元素,一个指向后一个元素,所以,从双向链表中的任意一个结点开始,都可以很方便地访问该节点的前后元素。
  • 线程安全:
    ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;综合来说,在需要频繁读取集合中的元素时,更推荐使用 ArrayList,而在插入和删除操作较多时,更推荐使用LinkedList。

8、ArrayList 和 Vector 的区别是什么?

这两个类都实现了 List 接口,他们都是有序集合

  • 线程安全:
    Vector 使用了 Synchronized 来实现线程同步,是线程安全的,但效率较低,而 ArrayList 是非线程安全的。
  • 性能:
    ArrayList 在性能方面要优于 Vector。
  • 扩容:
    ArrayList 和 Vector 都会根据实际的需要动态地调整容量,只不过在 Vector 扩容每次会增加 1 倍,而 ArrayList 只会增加 50%。
  • 同步:
    Vector类的所有方法都是同步的,可以由两个线程安全地访问一个Vector对象、但是一个线程访问Vector的话代码要在同步操作上耗费大量的时间。Arraylist不是同步的,所以在不需要保证线程安全时时建议使用Arraylist。

9、插入数据时,ArrayList、LinkedList、Vector谁速度较快?

ArrayList和Vector 底层的实现都是使用数组方式存储数据,所以索引数据快而插入数据慢。
Vector 中的方法由于加了 synchronized 修饰,性能上较ArrayList差。
LinkedList 使用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,但插入数据时只需要记录当前项的前后项即可,所以 LinkedList 插入速度较快。

10、多线程场景下如何使用 ArrayList?

ArrayList 不是线程安全的,如果遇到多线程场景,可以通过 Collections 的 synchronizedList() 方法将其转换成线程安全的容器后再使用。

11、说一下HashSet的实现原理?

基于HashMap实现,默认构造函数是构建HashMap,封装了一个 HashMap 对象来存储所有元素,所有放入 HashSet 中的集合元素实际上由 HashMap 的 key 来保存。

12、HashSet与HashMap的区别

10、HashMap的实现原理

  1. HashMap是基于哈希表实现的,由数组+链表组成,jdk8后加入了红黑树;内部维护了一个存储数据的Entry数组,采用链表解决冲突,每一个Entry本质上是一个单向链表;每一个元素都是一个key-value对,内部通过单链表解决哈希冲突,容量不足时,会自动增长。
  2. 实现了Map、Cloneable(可克隆)、Serializable(可序列化)这三个接口,能被克隆,支持序列化。
  3. HashMap是非线程安全的,但只适用于单线程环境下,多线程环境下可以采用concurrent并发包下的concurrentHashMap。
  4. HashMap中key和value都允许为null,key为null的键值对永远都放在以table[0]为头结点的链表中。

HashMap的总体结构如下:

11.说说HashMap的put方法执行流程?

  1. 将给定的key值通过哈希算法和“与”操作计算得出数组下标,如果数组下标为空,就将对应的key和value封装成一个对象存入数组中。
  2. 如果数组下标不为空,在jdk8中,会先判断该node对象是在链表还是红黑树中,如果在红黑树中,就存入红黑树,还要判断该红黑树节点是否有key值,如果有,直接更新value值。
  3. 如果在链表中,则使用尾插法添加到链表最后一个位置,同时还要遍历链表,如果存在key值,则直接更新value值。
  4. 插入之后链表长度会更新,如果长度大于8,则将链表转为红黑树。
  5. 最后判断是否要进行扩容,如果需要就扩容,如果不需要就退出put()方法。

链表转为红黑树的原因是:当链表长度过大时,会增加查询的时间,使性能降低,因为每次插入链表的时候都会遍历。

12.说说HashMap的get方法执行流程?

根据key的hashcode算出元素在数组中的下标,之后遍历Entry对象链表,直到找到元素为止。

13,哈希冲突有几种解决办法?

  • 链地址法:

    对于相同的哈希值,使用链表进行连接;
    特点:处理冲突简单,无堆积现象,但查询效率低。
    
  • 再哈希法:

    提供多个哈希函数,当第一个哈希函数计算的hashcode的值相等时,使用第二个第三个第四个,直到所有的对象的hashcode的值都不相等。
    特点:不易产生聚集,但增加了计算时间。
    
  • 建立公共溢出区:

    将哈希表分为基本表和溢出表,将溢出的数据都放在溢出表中。
    
  • 开放定址法:

    遇到哈希冲突时,寻找新的空闲的哈希地址,有两种方法:
    
  • 线性探测法:

    当存放的地址被占用时,hashcode值往后面+1并对存储空间的长度取模,直到找到一个空余的地址,取模是为了保证找到的地址在有效空间之内。
    
  • 平方探测法:

    hash(key) = hash(key) ± 1²
    

14、HashMap 与 HashTable的区别

  • 线程安全:
    HashMap 是非线程安全的,HashTable 是线程安全的;HashTable 内部的方法基本
    都经过 synchronized 修饰。
  • 效率:
    因为线程安全的问题,HashMap 要比 HashTable 效率高一点。
  • 对Null key 和Null value的支持:
    HashMap 中,null 可以作为键,这样的键只有一个,可以有一个或多个键所对应的值为 null,但是在 HashTable 中 put 进的键值只要有一个 null,直接抛NullPointerException异常。
  • 初始容量大小和每次扩充容量大小的不同 :
    创建时如果不指定容量初始值,Hashtable 默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap 默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。
    创建时如果给定了容量初始值,那么 Hashtable 会直接使用你给定的大小,而 HashMap 会将其扩充为2的幂次方大小。
  • 底层数据结构: JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。Hashtable 没有这样的机制。
  • 推荐在单线程环境下使用 HashMap ,如果需要多线程使用则用 ConcurrentHashMap 替代。

15、ConcurrentHashMap 的实现原理

ConcurrentHashMap 在 JDK1.7 和 JDK1.8 的实现方式是不同的。
JDK1.7 中的 ConcurrentHashMap 是由Segment数组和HashEntry数组组成,
首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程访问一段数据时,其他段的数据也能被其他线程访问,实现了真正的并发访问。

JDK1.8 中的ConcurrentHashMap 选择了与HashMap相同的Node数组+链表+红黑树的结构,抛弃了原有的Segment分段锁,采用CAS + synchronized实现更加细粒度的锁。

http://t.csdn.cn/HfVeT

16、ConcurrentHashMap 不支持 key 或者 value 为 null 的原因?

因为 ConcurrentHashMap 是用于多线程的 ,如果ConcurrentHashMap.get(key)得到了 null ,就无法判断,是映射的value为 null ,还是没有找到对应的key而为 null ,而用于单线程环境下的 HashMap 却可以用containsKey(key) 判断到底是否包含了这个 null 。

17.说说ArrayList 的扩容机制?

18.Array和ArrayList有何区别?

Array可以容纳基本数据类型和对象,而ArrayList只能容纳对象
Array是指定大小的,ArrayList 的容量是根据需求自动扩展
ArrayList有addAll(),removeAll(),iterator()等等
如果大小指定,并且主要存储和遍历数据,使用Array;
对于基本类型数据,ArrayList 使用自动装箱来减少编码工作量;而当处理固定大小的基本数据类型的时候,这种方式相对比较慢,这时候应该使用Array;
如果使用多维数组,使用[]数组比 List更容易。

19.comparable和comparator的区别?

comparable接口出自java.lang包,可以理解为一个类比较器,因为实现了comparable接口的类可以和自己比较,要和其他实现了Comparable接口类比较,可以使用compareTo方法。compareTo方法的返回值是int,有三种情况:
返回正整数(比较者大于被比较者)
返回0(比较者等于被比较者)
返回负整数(比较者小于被比较者)
comparator接口出自java.util包,它有一个compare方法用来排序,如果对类自带的自然排序不满意,而又不能修改其源代码的情况下,使用comparator就比较合适

20.Collection和Collections有什么区别?

**Collection:**是最基本的集合接口,提供了对集合进行基本操作的通用方法。一个Collection代表一组Object,直接继承接口有List,Set 和Queue。
**Collections:**它是集合类的一个工具类,不能被实例化,包含集合操作的方法,实现对各种集合的搜索、排序、线程安全等操作。

Ⅳ、异常

1、如何选择异常类型?

2、JVM 是如何处理异常的?

在一个方法中如果发生异常,这个方法会创建一个异常对象,并转交给 JVM,该异常对象包含异常名称,异常描述以及异常发生时应用程序的状态。
JVM 会查找是否有可以处理异常的代码,如果有,则调用异常处理代码,如果没有,JVM 就会终止应用程序。

3、throw 和 throws 的区别是什么?

throw用在方法内部,只能用于抛出方法或代码块中的一种异常,受查异常和非受查异常都可以被抛出。
throws用在方法声明上,可以抛出多个异常,用来标识该方法可能抛出的异常列表。

4、Java常见异常有哪些?

  • java.lang.NullPointerException (空指针异常)
    调用了未经初始化的对象或者是不存在的对象,比如new了一个空的字符串或数组,在调用时可能会报空指针异常。
  • java.lang.ClassNotFoundException 指定的类不存在
    通常都是程序试图通过字符串来加载某个类时可能引发异常。比如:调用Class.forName()。
  • java.lang.NumberFormatException 字符串转换为数字异常
    当试图将一个String转换为指定的数字类型,而该字符串确不满足数字类型要求的格式时,抛出该异常。
  • java.lang.IndexOutOfBoundsException 数组下标越界异常
  • java.lang.IllegalArgumentException 方法的参数错误
  • java.lang.IllegalAccessException 没有访问权限
  • java.lang.ArithmeticException 运算异常
  • java.lang.ClassCastException 数据类型转换异常
  • java.lang.FileNotFoundException 文件未找到异常
    当程序试图打开一个不存在的文件进行读写时将会引发该异常。该异常由FileInputStream,FileOutputStream,RandomAccessFile的构造器声明抛出,即使被操作的文件存在,但是由于某些原因不可访问,比如打开一个只读文件进行写入,这些构造方法仍然会引发异常。
  • java.lang.ArrayStoreException 数组存储异常
    当试图将类型不兼容的对象存入一个Object[]数组时将引发异常。
  • java.lang.EOFException 文件已结束异常
    当程序在输入的过程中遇到文件或流的结尾时,引发异常。因此该异常用于检查是否达到文件或流的结尾
  • java.lang.InstantiationException 实例化异常
    当试图通过Class的newInstance()方法创建某个类的实例,但程序无法通过该构造器来创建该对象时引发。Class对象表示一个抽象类,接口,数组类,基本类型 。该Class表示的类没有对应的构造器。
  • java.lang.InterruptedException 被中止异常
    当某个线程处于长时间的等待、休眠或其他暂停状态,而此时其他的线程通过Thread的interrupt方法终止该线程时抛出该异常。
  • java.lang.CloneNotSupportedException 不支持克隆异常
    当没有实现Cloneable接口或者不支持克隆方法时,调用其clone()方法则抛出该异常。
  • java.lang.OutOfMemoryException 内存不足错误
    当可用内存不足以让Java虚拟机分配给一个对象时抛出该错误。
  • java.lang.NoClassDefFoundException 未找到类定义错误
    当Java虚拟机或者类装载器试图实例化某个类,而找不到该类的定义时抛出该错误。

Ⅴ、IO流

1、Java中的IO流

2、什么是 java 序列化?

  • 序列化:将 Java 对象转换成字节流的过程。

  • 反序列化:将字节流转换成 Java 对象的过程。

  • 当对象需要在网络上传输或者持久化存储到文件中时,就需要对 Java 对象进行序列化处理。

  • 序列化的实现:类实现 Serializable 接口。

  • 注意事项:
    某个类可以被序列化,则其子类也可以被序列化
    声明为 static 和 transient 的成员变量,不能被序列化。static 成员变量是描述类级别的属性,transient 表示临时数据
    反序列化读取序列化对象的顺序要保持一致。

二、多线程

1、线程和进程的区别是什么?

进程包含线程,有独立的地址空间,一个进程崩溃后,不会对其它进程产生影响;
线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,进程之间切换消耗的资源较大,如果要求同时进行又要共享某些变量,只能用线程。

2、Java 实现线程有哪几种方式?

  • 继承 Thread 类实现多线程

    定义Thread类的子类,并重写该类的run()方法,该方法的方法体就是线程需要完成的任务,run()方法也称为线程执行体;
    创建Thread子类的实例,也就是创建了线程对象;
    启动线程,即调用线程的start()方法。
    
public class MyThread extends Thread{//继承Thread类public void run(){//重写run方法}
}public class Main {public static void main(String[] args){new MyThread().start();//创建并启动线程 }}
  • 实现 Runnable 接口方式实现多线程

    定义Runnable接口的实现类,一样要重写run()方法,这个run()方法和Thread中的run()方法一样是线程的执行体;
    创建Runnable实现类的实例,并用这个实例创建Thread对象,这个Thread对象才是真正的线程对象;依然是通过调用线程对象的start()方法来启动线程。
    
public class MyThread2 implements Runnable {//实现Runnable接口public void run(){//重写run方法 }
}public class Main {public static void main(String[] args){//创建并启动线程MyThread2 myThread=new MyThread2();Thread thread=new Thread(myThread); thread().start();//也可以这样new Thread(new MyThread2()).start();}
}
  • 使用 ExecutorService、Callable、Future 实现有返回结果的多线程

3、线程有哪些状态?

NEW 尚未启动
RUNNABLE 正在执行中
BLOCKED 阻塞的(被同步锁或者IO锁阻塞)
WAITING 永久等待状态
TIMED_WAITING 等待指定的时间重新被唤醒的状态
TERMINATED 执行完成

4、start()和 run()有什么区别?

star()方法是开启多线程的方法,而run()方法直接调用的话只是一个普通的方法,必须等待一个线程的 run()方法里面的代码全部执行完毕之后,另外一个线程才可以执行其 run()方法里面的代码,只有在调用了star()方法,多个线程的run()方法才会同时执行。

5、如何停止一个线程?

  • 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。
  • 使用stop方法强行终止,但是不推荐这个方法,因为stop和suspend及resume一样都是过期作废的方法。
  • 使用interrupt方法中断线程。

6、如何避免死锁?

当两个或多个线程同时访问同一个资源的时候就会发生死锁。
死锁的四个条件:
互斥:资源X和资源Y只能有一个被线程a访问;
请求和等待:一个线程在已经占有资源的情况下去访问其他资源。
不可剥夺:一个线程在已经占有资源的情况下不能被其他线程抢占。
循环等待:线程a等待线程B释放资源,线程B等待线程a释放资源。

避免死锁的方法:
破坏请求和等待条件,一个线程直接访问所有的资源;
破坏不可剥夺条件,线程主动某些资源
破坏循环等待条件:线程访问资源的时候,先访问优先级高的资源,再访问优先级低的资源。

7、 Runnable接口和Callable接口的区别

1、Runnable接口中的run()方法无返回值,只是纯粹地去执行run()方法中的代码而已;
2、Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果。
Callable+Future/FutureTask可以获取多线程运行的结果,可以在等待时间太长没获取到需要的数据的情况下取消该线程的任务,真的是非常有用。

8、如何确保线程安全?

对非安全的代码进行加锁控制
使用线程安全的类
多线程并发情况下,线程共享的变量改为方法级的局部变量

9、什么是线程池?有几种创建方式?

  • 什么是线程池:

    线程池就是创建若干个可执行的线程放入一个池(容器)中,有任务需要处理时,会提交到线程池中的任务队列,处理完之后线程并不会被销毁,
    

而是仍然在线程池中等待下一个任务。

  • 创建线程池的方式:

newCachedThreadPool创建一个可缓存线程池
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务。

  • 线程池的优点:

    复用已创建的线程,减少对象的创建与销毁。
    控制并发线程数,避免过多的资源竞争,避免线程的阻塞。
    使线程定时,定期执行和并发控制。
    

10、sleep()和wait()有什么区别?

  • 类不同:sleep()是Thread下的静态方法,wait()是Object类下的方法
  • 是否释放锁:sleep()不释放锁,wait()释放锁
  • 用处不同:wait()常用于线程间的通信,sleep()常用于暂停执行。
  • 用法不同:wait()用完后,线程不会自动执行,必须调用notify()或notifyAll()方法才能执行,sleep()方法调用后,线程经过过一定时间会自动苏醒,wait(参数)也可以传参数使其苏醒。它们苏醒后还有所区别,因为wait()会释放锁,所以苏醒后没有获取到锁就进入堵塞状态,获取到锁就进入就绪状态,而sleep苏醒后之间进入就绪状态,但是如果cpu不空闲,则进入的是就绪状态的堵塞队列中。

三、数据库

1、CHAR 和 VARCHAR 区别?

CHAR 是定长的,而 VARCHAR 是可以变长。
CHAR类型因为长度固定,所以存储效率高于 VARCHAR 类型。
在存储方式上,CHAR 对英文字符(ASCII)占用 1 字节,对一个汉字使用用 2 字节,而 VARCHAR 对每个字符均使用 2 字节。

2、CHAR 和 VARCHAR 如何选择?

对于经常变更的数据来说,CHAR 比 VARCHAR更好,因为 CHAR 消耗内存更少。
对于非常短的列或固定长度的数据(如 MD5),CHAR 比 VARCHAR 在存储空间上更有效率。

3、什么是索引?

索引是对数据库表中一列或多列的值进行排序的数据结构,用于快速访问数据库表中的特定信息。

4、索引的几种类型或分类?

  1. 从物理结构上可以分为聚集索引和非聚集索引两类:

    聚簇索引指索引的键值的逻辑顺序与表中相应行的物理顺序一致,即每张表只能有一个聚簇索引,也就是我们常说的主键索引;
    非聚簇索引的逻辑顺序则与数据行的物理顺序不一致
    
  2. 从应用上可以划分为一下几类:

  • 普通索引:
    MySQL 中的基本索引类型,没有什么限制,允许在定义索引的列中插入重复值和空值,纯粹为了提高查询效率。通过 ALTER TABLE table_name ADD INDEX index_name (column) 创建;
  • 唯一索引:
    索引列中的值必须是唯一的,但是允许为空值。通过 ALTER TABLE table_name ADD UNIQUE index_name (column) 创建;
  • 主键索引:
    特殊的唯一索引,也成聚簇索引,不允许有空值,并由数据库帮我们自动创建;
  • 组合索引:
    组合表中多个字段创建的索引,遵守最左前缀匹配规则;
  • 全文索引:
    只有在 MyISAM 引擎上才能使用,同时只支持 CHAR、VARCHAR、TEXT 类型字段上使用。

5、索引的优缺点

  • 索引的优点:

    1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。
    2. 可以大大加快数据的检索速度,这也是创建索引的最主要的原因。
    3. 可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。
    4. 在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。
    5. 可以在查询的过程中,使用优化隐藏器,提高系统的性能。
    
  • 索引的缺点:

    1. 创建和维护索引需要耗费时间,这种时间随着数据量的增加而增加,这样就降低了数据的维护速度。
    2. 索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间。如果要建立聚簇索引,那么需要的空间就会更大。
    

6、索引的设计原则?

  • 选择唯一性索引;

    唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录。
    
  • 为常作为查询条件的字段建立索引;

    如果某个字段经常用来做查询条件,那么该字段的查询速度会影响整个表的查询速度。因此,为这样的字段建立索引,可以提高整个表的查询速度。
    
  • 为经常需要排序、分组和联合操作的字段建立索引;

    经常需要 ORDER BY、GROUP BY、DISTINCT 和 UNION 等操作的字段,排序操作会浪费很多时间。如果为其建立索引,可以有效地避免排序操作。
    
  • 限制索引的数目;

    每个索引都需要占⽤用磁盘空间,索引越多,需要的磁盘空间就越大,修改表时,对索引的重构和更新很麻烦。
    
  • 小表不建议索引(如数量级在百万以内);

    由于数据较小,查询花费的时间可能比遍历索引的时间还要短,索引可能不会产生优化效果。
    
  • 尽量使用数据量少的索引;

    如果索引的值很长,那么查询的速度会受到影响。此时尽量使用前缀索引。
    

7、索引的数据结构?

  • 索引的数据结构和具体存储引擎的实现有关,MySQL 中常用的是 Hash 和 B+ 树索引。

    Hash 索引底层就是 Hash 表,进行查询时调用 Hash 函数获取到相应的键值(对应地址),然后回表查询获得实际数据.
    B+ 树索引底层实现原理是多路平衡查找树,对于每一次的查询都是从根节点出发,查询到叶子节点方可以获得所查键值,最后查询判断是否需要回表查询.
    

8、Hash索引和 B+ 树索引的区别及优劣

  • 首先要知道Hash索引和B+树索引的底层实现原理:

    hash索引底层就是hash表,进行查找时,调用一次hash函数就可以获取到相应的键值,之后进行回表查询获得实际数据。
    B+树底层实现是多路平衡查找树,对于每一次的查询都是从根节点出发,查找到叶子节点方可以获得所查键值,然后根据查询判断是否需要回表查询数据。
    
  • 区别:

    hash索引进行等值查询更快(一般情况下),但是却无法进行范围查询,因为在hash索引中经过hash函数建立索引之后,索引的顺序与原顺序无法保持一致,不能支持范围查询,而
    

B+树的的所有节点皆遵循。
hash索引不支持使用索引进行排序,原理同上。
hash索引不支持模糊查询以及多列索引的最左前缀匹配,原理也是因为hash函数的不可预测.AAAA
和AAAAB的索引没有相关性.
hash索引任何时候都避免不了回表查询数据,而B+树在符合某些条件(聚簇索引,覆盖索引等)的时候
可以只通过索引完成查询.
hash索引虽然在等值查询上较快,但是不稳定.性能不可预测,当某个键值存在大量重复的时候,发生
hash碰撞,此时效率可能极差.而B+树的查询效率比较稳定,对于所有的查询都是从根节点到叶子节
点,且树的高度较低.
因此,在大多数情况下,直接选择B+树索引可以获得稳定且较好的查询速度.而不需要使用hash索引.

9、为什么用 B+ 树做索引而不用哈希表做索引?

1、哈希表是把索引字段映射成对应的哈希码然后再存放在对应的位置,这样的话,如果我们要进行模糊查找的话,显然哈希表这种结构是不支持的,只能遍历这个表。而B+树则可以通过最左前缀原则快速找到对应的数据。
2、如果我们要进行范围查找,例如查找ID为100 ~ 400的人,哈希表同样不支持,只能遍历全表。
3、索引字段通过哈希映射成哈希码,如果很多字段都刚好映射到相同值的哈希码的话,那么形成的索引结构将会是一条很长的链表,这样的话,查找的时间就会大大增加。

10、MyISAM 和 InnoDB 的区别?

  1. InnoDB 支持事务,而 MyISAM 不支持。
  2. InnoDB 支持外键,而 MyISAM 不支持。因此将一个含有外键的 InnoDB 表 转为 MyISAM 表会失败。
  3. InnoDB 和 MyISAM 均支持 B+ Tree 数据结构的索引。但 InnoDB 是聚集索引,而 MyISAM 是非聚集索引。
  4. InnoDB 不保存表中数据行数,执行 select count(*) from table 时需要全表扫描。而 MyISAM 用一个变量记录了整个表的行数,速度相当快(注意不能有 WHERE 子句);那为什么 InnoDB 没有使用这样的变量呢?因为InnoDB的事务特性,在同一时刻表中的行数对于不同的事务而言是不一样的。
  5. InnoDB 支持表、行(默认)级锁,而 MyISAM 支持表级锁;
    InnoDB 的行锁是基于索引实现的,而不是物理行记录上。即访问如果没有命中索引,则也无法使用行锁,将要退化为表锁。
  6. InnoDB 必须有唯一索引(如主键),如果没有指定,就会自动寻找或生产一个隐藏列 Row_id 来充当默认主键,而 Myisam 可以没有主键。

11、什么是数据库的事务?

事务是逻辑上的一组操作,要么都执行,要么都不执行。

12、什么是事务的四大特性(ACID)?

  • 原子性:就是上面说的,要么全部成功,要么全部失败,不可能只执行一部分操作。
  • 一致性:系统(数据库)总是从一个一致性的状态转移到另一个一致性的状态,不会存在中间状态。
  • 隔离性:一般情况下,一个事务在完全提交之前,对其他事务是不可见的。
  • 持久性:一旦事务提交,那么就永远是这样子了,哪怕系统崩溃也不会影响到这个事务的结果。

13、Myql 中的事务回滚机制

事务回滚是指将该事务已经完成的对数据库的更新操作撤销。

要同时修改数据库中两个不同表时, 如果它们不是一个事务的话, 当第一个表修改完, 可能第二个表修改过程中出现了异常而没能修改, 此时就只有第二个表依旧是未修改之前的状态, 而第一个表已经被修改完毕,而当你把它们设定为一个事务的时候, 当第一个表修改完, 第二表修改出现异常而没能修改, 第一个表和第二个表都要回到未修改的状态, 这就是所谓的事务回滚。

14、 对MySQL的锁了解吗?

当数据库有并发事务的时候,可能会产生数据的不一致,这时候需要一些机制来保证访问的次序,锁机制就是这样的一个机制。
就像酒店的房间,如果大家随意进出,就会出现多人抢夺同一个房间的情况,而在房间上装上锁,申请到钥匙的人才可以入住并且将房间锁起来,其他人只有等他使用完毕才可以再次使用。

15、SQL语法

基础操作
/* Windows服务 */
-- 启动MySQL
net start mysql
-- 创建Windows服务
sc create mysql binPath= mysqld_bin_path//注意:等号与值之间有空格
/* 连接与断开服务器 */
mysql -h 地址 -P 端口 -u 用户名 -p 密码
SHOW PROCESSLIST -- 显示哪些线程正在运行
SHOW VARIABLES -- 显示系统变量信息
数据库操作
/* 数据库操作 */ ------------------
-- 查看当前数据库
SELECT DATABASE();
-- 显示当前时间、用户名、数据库版本
SELECT now(), user(), version();
-- 创建库
CREATE DATABASE[ IF NOT EXISTS] 数据库名 数据库选项
数据库选项:
CHARACTER SET charset_name
COLLATE collation_name
-- 查看已有库
SHOW DATABASES[ LIKE 'PATTERN']
-- 查看当前库信息
SHOW CREATE DATABASE 数据库名
-- 修改库的选项信息
ALTER DATABASE 库名 选项信息
-- 删除库
DROP DATABASE[ IF EXISTS] 数据库名
//同时删除该数据库相关的目录及其目录内容
表的操作
-- 创建表
CREATE [TEMPORARY] TABLE[ IF NOT EXISTS] [库名.]表名 ( 表的结构定义 )[ 表选项]
/*每个字段必须有数据类型,最后一个字段后不能有逗号*/
TEMPORARY //临时表,会话结束时表自动消失
--对于字段的定义:
字段名 数据类型 [NOT NULL | NULL] [DEFAULT default_value]
[AUTO_INCREMENT] [UNIQUE [KEY] | [PRIMARY] KEY] [COMMENT 'string']
-- 表选项
-- 字符集
CHARSET = charset_name//如果表没有设定,则使用数据库字符集
-- 存储引擎
ENGINE = engine_name
/*表在管理数据时采用的不同的数据结构,结构不同会导致处理方式、提供的特性操作等不同*/
--常见的引擎:
InnoDB MyISAM Memory/Heap BDB Merge Example CSV MaxDB Archive//不同的引擎在保存表的结构和数据时采用不同的方式MyISAM表文件含义:.frm表定义,.MYD表数据,.MYI表索引
InnoDB表文件含义:.frm表定义,表空间数据和日志文件
SHOW ENGINES -- 显示存储引擎的状态信息
SHOW ENGINE 引擎名 {LOGS|STATUS} -- 显示存储引擎的日志或状态信息
-- 自增起始数
AUTO_INCREMENT = 行数
-- 数据文件目录
DATA DIRECTORY = '目录'
-- 索引文件目录
INDEX DIRECTORY = '目录'
-- 表注释
COMMENT = 'string'
-- 分区选项
PARTITION BY ... (详细见手册)
-- 查看所有表
SHOW TABLES[ LIKE 'pattern']
SHOW TABLES FROM 库名
-- 查看表结构
SHOW CREATE TABLE 表名 (信息更详细)
DESC 表名 / DESCRIBE 表名 / EXPLAIN 表名 / SHOW COLUMNS FROM 表名 [LIKE
'PATTERN']
SHOW TABLE STATUS [FROM db_name] [LIKE 'pattern']
-- 修改表
-- 修改表本身的选项
ALTER TABLE 表名 表的选项
eg: ALTER TABLE 表名 ENGINE=MYISAM;
-- 对表进行重命名
RENAME TABLE 原表名 TO 新表名
RENAME TABLE 原表名 TO 库名.表名 (可将表移动到另一个数据库)
-- RENAME可以交换两个表名
-- 修改表的字段机构(13.1.2. ALTER TABLE语法)
ALTER TABLE 表名 操作名
-- 操作名
ADD[ COLUMN] 字段定义 -- 增加字段
AFTER 字段名 -- 表示增加在该字段名后面
FIRST -- 表示增加在第一个
ADD PRIMARY KEY(字段名) -- 创建主键
ADD UNIQUE [索引名] (字段名)-- 创建唯一索引
ADD INDEX [索引名] (字段名) -- 创建普通索引
DROP[ COLUMN] 字段名 -- 删除字段
MODIFY[ COLUMN] 字段名 字段属性 -- 支持对字段属性进行修改,不能修改字段名
(所有原有属性也需写上)
CHANGE[ COLUMN] 原字段名 新字段名 字段属性 -- 支持对字段名修改
DROP PRIMARY KEY -- 删除主键(删除主键前需删除其AUTO_INCREMENT属性)
DROP INDEX 索引名 -- 删除索引
DROP FOREIGN KEY 外键 -- 删除外键
-- 删除表
DROP TABLE[ IF EXISTS] 表名 ...
-- 清空表数据
TRUNCATE [TABLE] 表名
-- 复制表结构
CREATE TABLE 表名 LIKE 要复制的表名
-- 复制表结构和数据
CREATE TABLE 表名 [AS] SELECT * FROM 要复制的表名
-- 检查表是否有错误
CHECK TABLE tbl_name [, tbl_name] ... [option] ...
-- 优化表
OPTIMIZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ...
-- 修复表
REPAIR [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ... [QUICK]
[EXTENDED] [USE_FRM]
-- 分析表
ANALYZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ...
数据操作
/* 数据操作 */ ------------------
-- 增
INSERT [INTO] 表名 [(字段列表)] VALUES (值列表)[, (值列表), ...]
-- 如果要插入的值列表包含所有字段并且顺序一致,则可以省略字段列表。
-- 可同时插入多条数据记录!
REPLACE 与 INSERT 完全一样,可互换。
INSERT [INTO] 表名 SET 字段名=值[, 字段名=值, ...]
-- 查
SELECT 字段列表 FROM 表名[ 其他子句]
-- 可来自多个表的多个字段
-- 其他子句可以不使用
-- 字段列表可以用*代替,表示所有字段
-- 删
DELETE FROM 表名[ 删除条件子句]
没有条件子句,则会删除全部
-- 改
UPDATE 表名 SET 字段名=新值[, 字段名=新值] [更新条件]
数据类型
/* 数据类型(列类型) */ ------------------
1. 数值类型
-- a. 整型 ----------
类型 字节 范围(有符号位)
tinyint 1字节 -128 ~ 127 无符号位:0 ~ 255
smallint 2字节 -32768 ~ 32767
mediumint 3字节 -8388608 ~ 8388607
int 4字节
bigint 8字节
int(M) M表示总位数
- 默认存在符号位,unsigned 属性修改
- 显示宽度,如果某个数不够定义字段时设置的位数,则前面以0补填,zerofill 属性修改
例:int(5) 插入一个数'123',补填后为'00123'
- 在满足要求的情况下,越小越好。
- 1表示bool值真,0表示bool值假。MySQL没有布尔类型,通过整型0和1表示。常用tinyint(1)表
示布尔型。
-- b. 浮点型 ----------
类型 字节 范围
float(单精度) 4字节
double(双精度) 8字节
浮点型既支持符号位 unsigned 属性,也支持显示宽度 zerofill 属性。
不同于整型,前后均会补填0.
定义浮点型时,需指定总位数和小数位数。
float(M, D) double(M, D)
M表示总位数,D表示小数位数。
M和D的大小会决定浮点数的范围。不同于整型的固定范围。
M既表示总位数(不包括小数点和正负号),也表示显示宽度(所有显示符号均包括)。
支持科学计数法表示。
浮点数表示近似值。
-- c. 定点数 ----------
decimal -- 可变长度
decimal(M, D) M也表示总位数,D表示小数位数。
保存一个精确的数值,不会发生数据的改变,不同于浮点数的四舍五入。
将浮点数转换为字符串来保存,每9位数字保存为4个字节。
2. 字符串类型
-- a. char, varchar ----------
char 定长字符串,速度快,但浪费空间
varchar 变长字符串,速度慢,但节省空间
M表示能存储的最大长度,此长度是字符数,非字节数。
不同的编码,所占用的空间不同。
char,最多255个字符,与编码无关。
varchar,最多65535字符,与编码有关。
一条有效记录最大不能超过65535个字节。
utf8 最大为21844个字符,gbk 最大为32766个字符,latin1 最大为65532个字符
varchar 是变长的,需要利用存储空间保存 varchar 的长度,如果数据小于255个字节,则采用一个
字节来保存长度,反之需要两个字节来保存。
varchar 的最大有效长度由最大行大小和使用的字符集确定。
最大有效长度是65532字节,因为在varchar存字符串时,第一个字节是空的,不存在任何数据,然后还
需两个字节来存放字符串的长度,所以有效长度是65535-1-2=65532字节。
例:若一个表定义为 CREATE TABLE tb(c1 int, c2 char(30), c3 varchar(N))
charset=utf8; 问N的最大值是多少? 答:(65535-1-2-4-30*3)/3
-- b. blob, text ----------
blob 二进制字符串(字节字符串)
tinyblob, blob, mediumblob, longblob
text 非二进制字符串(字符字符串)
tinytext, text, mediumtext, longtext
text 在定义时,不需要定义长度,也不会计算总长度。
text 类型在定义时,不可给default值
-- c. binary, varbinary ----------
类似于char和varchar,用于保存二进制字符串,也就是保存字节字符串而非字符字符串。
char, varchar, text 对应 binary, varbinary, blob.
3. 日期时间类型
一般用整型保存时间戳,因为PHP可以很方便的将时间戳进行格式化。
datetime 8字节 日期及时间 1000-01-01 00:00:00 到 9999-12-31 23:59:59
date 3字节 日期 1000-01-01 到 9999-12-31
timestamp 4字节 时间戳 19700101000000 到 2038-01-19 03:14:07
time 3字节 时间 -838:59:59 到 838:59:59
year 1字节 年份 1901 - 2155
datetime YYYY-MM-DD hh:mm:ss
timestamp YY-MM-DD hh:mm:ss
YYYYMMDDhhmmss
YYMMDDhhmmss
YYYYMMDDhhmmss
YYMMDDhhmmss
date YYYY-MM-DD
YY-MM-DD
YYYYMMDD
YYMMDD
YYYYMMDD
YYMMDD
time hh:mm:ss
hhmmss
hhmmss
year YYYY
YY
YYYY
YY
4. 枚举和集合
-- 枚举(enum) ----------
enum(val1, val2, val3...)
在已知的值中进行单选。最大数量为65535.
枚举值在保存时,以2个字节的整型(smallint)保存。每个枚举值,按保存的位置顺序,从1开始逐一递
增。
表现为字符串类型,存储却是整型。
NULL值的索引是NULL。
空字符串错误值的索引值是0。
-- 集合(set) ----------
set(val1, val2, val3...)
create table tab ( gender set('男', '女', '无') );
insert into tab values ('男, 女');
最多可以有64个不同的成员。以bigint存储,共8个字节。采取位运算的形式。
当创建表时,SET成员值的尾部空格将自动被删除。
列属性
/* 列属性(列约束) */ ------------------
1. PRIMARY 主键
- 能唯一标识记录的字段,可以作为主键。
- 一个表只能有一个主键。
- 主键具有唯一性。
- 声明字段时,用 primary key 标识。
也可以在字段列表之后声明
例:create table tab ( id int, stu varchar(10), primary key (id));
- 主键字段的值不能为null。
- 主键可以由多个字段共同组成。此时需要在字段列表后声明的方法。
例:create table tab ( id int, stu varchar(10), age int, primary key
(stu, age));
2. UNIQUE 唯一索引(唯一约束)//使得某字段的值也不能重复。
3. NULL 约束
null不是数据类型,是列的一个属性。
表示当前列是否可以为null,表示什么都没有。
null, 允许为空。默认。
not null, 不允许为空。
insert into tab values (null, 'val');
-- 此时表示将第一个字段的值设为null, 取决于该字段是否允许为null
4. DEFAULT 默认值属性
当前字段的默认值。
insert into tab values (default, 'val'); -- 此时表示强制使用默认值。
create table tab ( add_time timestamp default current_timestamp );
-- 表示将当前时间的时间戳设为默认值。
current_date, current_time
5. AUTO_INCREMENT 自动增长约束
自动增长必须为索引(主键或unique)
只能存在一个字段为自动增长。
默认为1开始自动增长。可以通过表属性 auto_increment = x进行设置,或 alter table tbl
auto_increment = x;
6. COMMENT 注释
例:create table tab ( id int ) comment '注释内容';
7. FOREIGN KEY 外键约束
用于限制主表与从表数据完整性。
alter table t1 add constraint `t1_t2_fk` foreign key (t1_id) references
t2(id);
-- 将表t1的t1_id外键关联到表t2的id字段。
-- 每个外键都有一个名字,可以通过 constraint 指定
存在外键的表,称之为从表(子表),外键指向的表,称之为主表(父表)。
作用:保持数据一致性,完整性,主要目的是控制存储在外键表(从表)中的数据。
MySQL中,可以对InnoDB引擎使用外键约束:
语法:
foreign key (外键字段) references 主表名 (关联字段) [主表记录删除时的动作] [主表记录
更新时的动作]
此时需要检测一个从表的外键需要约束为主表的已存在的值。外键在没有关联的情况下,可以设置为
null.前提是该外键列,没有not null。
可以不指定主表记录更改或更新时的动作,那么此时主表的操作被拒绝。
如果指定了 on update 或 on delete:在删除或更新时,有如下几个操作可以选择:
1. cascade,级联操作。主表数据被更新(主键值更新),从表也被更新(外键值更新)。主表记录被
删除,从表相关记录也被删除。
2. set null,设置为null。主表数据被更新(主键值更新),从表的外键被设置为null。主表记录
被删除,从表相关记录外键被设置成null。但注意,要求该外键列,没有not null属性约束。
3. restrict,拒绝父表删除和更新。
注意,外键只被InnoDB存储引擎所支持。其他引擎是不支持的。
SELECT
/* SELECT */ ------------------
SELECT [ALL|DISTINCT] select_expr FROM -> WHERE -> GROUP BY [合计函数] -> HAVING -
> ORDER BY -> LIMIT
a. select_expr
-- 可以用 * 表示所有字段。
select * from tb;
-- 可以使用表达式(计算公式、函数调用、字段也是个表达式)
select stu, 29+25, now() from tb;
-- 可以为每个列使用别名。适用于简化列标识,避免多个列标识符重复。
- 使用 as 关键字,也可省略 as.
select stu+10 as add10 from tb;
b. FROM 子句
用于标识查询来源。
-- 可以为表起别名。使用as关键字。
SELECT * FROM tb1 AS tt, tb2 AS bb;
-- from子句后,可以同时出现多个表。
-- 多个表会横向叠加到一起,而数据会形成一个笛卡尔积。
SELECT * FROM tb1, tb2;
-- 向优化符提示如何选择索引
USE INDEX、IGNORE INDEX、FORCE INDEX
SELECT * FROM table1 USE INDEX (key1,key2) WHERE key1=1 AND key2=2 AND
key3=3;
SELECT * FROM table1 IGNORE INDEX (key3) WHERE key1=1 AND key2=2 AND
key3=3;
c. WHERE 子句
-- 从from获得的数据源中进行筛选。
-- 整型1表示真,0表示假。
-- 表达式由运算符和运算数组成。
-- 运算数:变量(字段)、值、函数返回值
-- 运算符:
=, <=>, <>, !=, <=, <, >=, >, !, &&, ||,
in (not) null, (not) like, (not) in, (not) between and, is (not),
and, or, not, xor
is/is not 加上ture/false/unknown,检验某个值的真假
<=>与<>功能相同,<=>可用于null比较
d. GROUP BY 子句, 分组子句
GROUP BY 字段/别名 [排序方式]
分组后会进行排序。升序:ASC,降序:DESC
以下[合计函数]需配合 GROUP BY 使用:
count 返回不同的非NULL值数目 count(*)、count(字段)
sum 求和
max 求最大值
min 求最小值
avg 求平均值
group_concat 返回带有来自一个组的连接的非NULL值的字符串结果。组内字符串连接。
e. HAVING 子句,条件子句
与 where 功能、用法相同,执行时机不同。
where 在开始时执行检测数据,对原数据进行过滤。
having 对筛选出的结果再次进行过滤。
having 字段必须是查询出来的,where 字段必须是数据表存在的。
where 不可以使用字段的别名,having 可以。因为执行WHERE代码时,可能尚未确定列值。
where 不可以使用合计函数。一般需用合计函数才会用 having
SQL标准要求HAVING必须引用GROUP BY子句中的列或用于合计函数中的列。
f. ORDER BY 子句,排序子句
order by 排序字段/别名 排序方式 [,排序字段/别名 排序方式]...
升序:ASC,降序:DESC
支持多个字段的排序。
g. LIMIT 子句,限制结果数量子句
仅对处理好的结果进行数量限制。将处理好的结果的看作是一个集合,按照记录出现的顺序,索引从0开
始。
limit 起始位置, 获取条数
省略第一个参数,表示从索引0开始。limit 获取条数
h. DISTINCT, ALL 选项
distinct 去除重复记录
默认为 all, 全部记录
UNION
/* UNION */ ------------------
将多个select查询的结果组合成一个结果集合。
SELECT ... UNION [ALL|DISTINCT] SELECT ...
默认 DISTINCT 方式,即所有返回的行都是唯一的
建议,对每个SELECT查询加上小括号包裹。
ORDER BY 排序时,需加上 LIMIT 进行结合。
需要各select查询的字段数量一样。
每个select查询的字段列表(数量、类型)应一致,因为结果中的字段名以第一条select语句为准。
子查询
/* 子查询 */ ------------------
- 子查询需用括号包裹。
-- from型
from后要求是一个表,必须给子查询结果取个别名。
- 简化每个查询内的条件。
- from型需将结果生成一个临时表格,可用以原表的锁定的释放。
- 子查询返回一个表,表型子查询。
select * from (select * from tb where id>0) as subfrom where id>1;
-- where型
- 子查询返回一个值,标量子查询。
- 不需要给子查询取别名。
- where子查询内的表,不能直接用以更新。
select * from tb where money = (select max(money) from tb);
-- 列子查询
如果子查询结果返回的是一列。
使用 in 或 not in 完成查询
连接查询(join)
TRUNCATE
exists 和 not exists 条件
如果子查询返回数据,则返回1或0。常用于判断条件。
select column1 from t1 where exists (select * from t2);
-- 行子查询
查询条件是一个行。
select * from t1 where (id, gender) in (select id, gender from t2);
行构造符:(col1, col2, ...) 或 ROW(col1, col2, ...)
行构造符通常用于与对能返回两个或两个以上列的子查询进行比较。
-- 特殊运算符
!= all() 相当于 not in
= some() 相当于 in。any 是 some 的别名
!= some() 不等同于 not in,不等于其中某一个。
all, some 可以配合其他运算符一起使用。
连接查询(join)
/* 连接查询(join) */ ------------------
将多个表的字段进行连接,可以指定连接条件。
-- 内连接(inner join)
- 默认就是内连接,可省略inner。
- 只有数据存在时才能发送连接。即连接结果不能出现空行。
on 表示连接条件。其条件表达式与where类似。也可以省略条件(表示条件永远为真)
也可用where表示连接条件。
还有 using, 但需字段名相同。 using(字段名)
-- 交叉连接 cross join
即,没有条件的内连接。
select * from tb1 cross join tb2;
-- 外连接(outer join)
- 如果数据不存在,也会出现在连接结果中。
-- 左外连接 left join
如果数据不存在,左表记录会出现,而右表为null填充
-- 右外连接 right join
如果数据不存在,右表记录会出现,而左表为null填充
-- 自然连接(natural join)
自动判断连接条件完成连接。
相当于省略了using,会自动查找相同字段名。
natural join
natural left join
natural right join
select info.id, info.name, info.stu_num, extra_info.hobby, extra_info.sex from
info, extra_info where info.stu_num = extra_info.stu_id;
TRUNCATE
/* TRUNCATE */ ------------------
TRUNCATE [TABLE] tbl_name
清空数据
删除重建表
区别:
1,truncate 是删除表再创建,delete 是逐条删除
2,truncate 重置auto_increment的值。而delete不会
3,truncate 不知道删除了几条,而delete知道。
4,当被用于带分区的表时,truncate 会保留分区
备份与还原
/* 备份与还原 */ ------------------
备份,将数据的结构与表内数据保存起来。
利用 mysqldump 指令完成。
-- 导出
mysqldump [options] db_name [tables]
mysqldump [options] ---database DB1 [DB2 DB3...]
mysqldump [options] --all--database
1. 导出一张表
mysqldump -u用户名 -p密码 库名 表名 > 文件名(D:/a.sql)
2. 导出多张表
mysqldump -u用户名 -p密码 库名 表1 表2 表3 > 文件名(D:/a.sql)
3. 导出所有表
mysqldump -u用户名 -p密码 库名 > 文件名(D:/a.sql)
4. 导出一个库
mysqldump -u用户名 -p密码 --lock-all-tables --database 库名 > 文件名(D:/a.sql)
可以-w携带WHERE条件
-- 导入
1. 在登录mysql的情况下:
source 备份文件
2. 在不登录的情况下
mysql -u用户名 -p密码 库名 < 备份文件
视图
/*视图是一个虚拟表,其内容由查询定义。同真实的表一样,视图包含一系列带有名称的列和行数据。但
是,视图并不在数据库中以存储的数据值集形式存在。行和列数据来自由定义视图的查询所引用的表,并且在
引用视图时动态生成。
视图具有表结构文件,但不存在数据文件。
对其中所引用的基础表来说,视图的作用类似于筛选。定义视图的筛选可以来自当前或其它数据库的一个
或多个表,或者其它视图。通过视图进行查询没有任何限制,通过它们进行数据修改时的限制也很少。
视图是存储在数据库中的查询的sql语句,它主要出于两种原因:安全原因,视图可以隐藏一些数据,
如:社会保险基金表,可以用视图只显示姓名,地址,而不显示社会保险号和工资数等,另一原因是可使复杂的查询易于理解和使用。*/
-- 创建视图
CREATE [OR REPLACE] [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}] VIEW view_name
[(column_list)] AS select_statement
- 视图名必须唯一,同时不能与表重名。
- 视图可以使用select语句查询到的列名,也可以自己指定相应的列名。
- 可以指定视图执行的算法,通过ALGORITHM指定。
- column_list如果存在,则数目必须等于SELECT语句检索的列数
-- 查看结构
SHOW CREATE VIEW view_name
-- 删除视图
- 删除视图后,数据依然存在。
- 可同时删除多个视图。
DROP VIEW [IF EXISTS] view_name ...
-- 修改视图结构
- 一般不修改视图,因为不是所有的更新视图都会映射到表上。
ALTER VIEW view_name [(column_list)] AS select_statement
-- 视图作用
1. 简化业务逻辑
2. 对客户端隐藏真实的表结构
-- 视图算法(ALGORITHM)
MERGE 合并
将视图的查询语句,与外部查询需要先合并再执行!
TEMPTABLE 临时表
将视图执行完毕后,形成临时表,再做外层查询!
UNDEFINED 未定义(默认),指的是MySQL自主去选择相应的算法。
锁表
/* 锁表 */
表锁定只用于防止其它客户端进行不正当地读取和写入
MyISAM 支持表锁,InnoDB 支持行锁
-- 锁定
LOCK TABLES tbl_name [AS alias]
-- 解锁
UNLOCK TABLES

四、框架

Ⅰ、Spring

1、使用Spring框架的好处是什么?

  • 控制反转:Spring通过控制反转实现了松散耦合,对象们给出它们的依赖,而不是创建或查找依赖的对象们。
  • 面向切面的编程(AOP):
    Spring支持面向切面的编程,并且把应用业务逻辑和系统服务分开。
  • 容器:
    Spring 包含并管理应用中对象的生命周期和配置。
  • MVC框架:
    Spring的WEB框架是个精心设计的框架,是Web框架的一个很好的替代品。
  • 事务管理:
    Spring 提供一个持续的事务管理接口,可以扩展到上至本地事务下至全局事务(JTA)。
  • 异常处理:
    Spring 提供方便的API把具体技术相关的异常(比如由JDBC,Hibernate or JDO抛出的)转化为一致的unchecked 异常。

2、Spring的IOC理解

控制反转,指将对象的控制权转移给Spring框架,由 Spring 来负责控制对象的生命周期(比如创建、销毁)和对象间的依赖关系。
对于某个具体的对象而言,以前是由自己控制它所引用对象的生命周期,而在IOC中,所有的对象都被 Spring 控制,控制对象生命周期的不再是引用它的对象,而是Spring容器,由 Spring 容器帮我们创建、查找及注入依赖对象,而引用对象只是被动的接受依赖对象,所以这叫控制反转。

3、什么是DI?

IOC的重要特性,即动态的向某个对象提供它所需要的其他对象,通过反射实现注入,反射允许程序在运行的时候动态生成对象、执行对象的方法、改变对象的属性。

4、什么是AOP?

用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,减少系统中的重复代码,降低了模块间的耦合度,提高系统的可维护性。
  • 连接点(Join point):

    指程序运行过程中所执行的方法。在Spring AOP中,一个连接点总代表一个方法的执行。
    
  • 切面(Aspect):

    被抽取出来的公共模块,可以用来会横切多个对象。Aspect切面可以看成 Pointcut切点 和 Advice通知 的结合,一个切面可以由多个切点和通知组成。
    在Spring AOP中,切面可以在类上使用 @AspectJ 注解来实现。
    
  • 切点(Pointcut):

    切点用于定义 要对哪些Join point进行拦截。
    切点分为execution方式和annotation方式。execution方式可以用路径表达式指定对哪些方法拦截,比如指定拦截add*、search*。annotation方式可以指定被哪些注解修饰的代码进行拦截。
    
  • 通知(Advice):

    指要在连接点(Join Point)上执行的动作,即增强的逻辑,比如权限校验和、日志记录等。通知有各种类型,包括Around、Before、After、After returning、After throwing。
    
  • 目标对象(Target):

    包含连接点的对象,也称作被通知(Advice)的对象。 由于Spring AOP是通过动态代理实现的,所以这个对象永远是一个代理对象。
    
  • 织入(Weaving):

    通过动态代理,在目标对象(Target)的方法(即连接点Join point)中执行增强逻辑(Advice)的过程。
    
  • 引入(Introduction):

    添加额外的方法或者字段到被通知的类。Spring允许引入新的接口(以及对应的实现)到任何被代理的对象。例如,你可以使用一个引入来使bean实现 IsModified 接口,以便简化缓存机制。
    

5、Spring通知(Advice)有哪些类型?

  • 前置通知(Before Advice):
    在连接点(Join point)之前执行的通知。
  • 后置通知(After Advice):
    当连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
  • 环绕通知(Around Advice):
    包围一个连接点的通知,这是最强大的一种通知类型。 环绕通知可以在方法调用前后完成自定义的行为。它也可以选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。
  • 返回后通知(AfterReturning Advice):
    在连接点正常完成后执行的通知(如果连接点抛出异常,则不执行)
  • 抛出异常后通知(AfterThrowing advice):
    在方法抛出异常退出时执行的通知

6、Spring容器的启动流程

  • 初始化Spring容器,注册内置的BeanPostProcessor的BeanDefinition到容器中:
  1. 实例化BeanFactory【DefaultListableBeanFactory】工厂,用于生成Bean对象
  2. 实例化BeanDefinitionReader注解配置读取器,用于对特定注解(如==@Service、@Repository==)的类进行读取转化成BeanDefinition 对象,(BeanDefinition 是 Spring 中极其重要的一个概念,它存储了 bean对象的所有特征信息,如是否单例,是否懒加载factoryBeanName 等)
  3. 实例化ClassPathBeanDefinitionScanner路径扫描器,用于对指定的包目录进行扫描查找 bean 对象
  • 将配置类的BeanDefinition注册到容器中:

  • 调用refresh()方法刷新容器:

  1. prepareRefresh()刷新前的预处理:
  2. obtainFreshBeanFactory():获取在容器初始化时创建的BeanFactory:
  3. prepareBeanFactory(beanFactory):BeanFactory的预处理工作,向容器中添加一些组件:
  4. postProcessBeanFactory(beanFactory):子类重写该方法,可以实现在BeanFactory创建并预处理完成以后做进一步的设置
  5. invokeBeanFactoryPostProcessors(beanFactory):在BeanFactory标准初始化之后执行BeanFactoryPostProcessor的方法,即BeanFactory的后置处理器:
  6. registerBeanPostProcessors(beanFactory):向容器中注册Bean的后置处理器BeanPostProcessor,它的主要作用是干预Spring初始化bean的流程,从而完成代理、自动注入、循环依赖等功能
  7. initMessageSource():初始化MessageSource组件,主要用于做国际化功能,消息绑定与消息解析:
  8. initApplicationEventMulticaster():初始化事件派发器,在注册监听器时会用到:
  9. onRefresh():留给子容器、子类重写这个方法,在容器刷新的时候可以自定义逻辑
  10. registerListeners():注册监听器:将容器中所有的ApplicationListener注册到事件派发器中,并派发之前步骤产生的事件:
  11. finishBeanFactoryInitialization(beanFactory):初始化所有剩下的单实例bean,核心方法是preInstantiateSingletons(),会调用getBean()方法创建对象;
  12. finishRefresh():发布BeanFactory容器刷新完成事件:

7、Spring框架使用了哪些设计模式?

单例模式 原型模式 工厂模式 适配器模式 包装模式 代理模式 观察者模式 策略模式 模板模式

8、BeanFactory和ApplicationContext有什么区别?

ApplicationContext提供了更完整的功能:

  • 继承MessageSource,因此支持国际化。

  • 统一的资源文件访问方式。

  • 提供在监听器中注册bean的事件。

  • 同时加载多个配置文件。

  • 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。

    1. BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean加载实例化,这样,就不能发现一些存在的Spring配置问题,如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次调用getBean方法时才会抛出异常。
    2. ApplicationContext,在容器启动时,一次性创建了所有的Bean,这样,在容器启动时,就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。ApplicationContext启动后预载入所有的单实例Bean,确保当你需要的时候,你就不用等待,因为它们已经创建好了。
    3. 相对于基本的BeanFactory,ApplicationContext 唯一的不足是占用内存空间,当应用程序配置Bean较多时,程序启动较慢。
    4. BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建,如使用ContextLoader。
    5. BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。
    

9、Spring Bean的生命周期?

简单来说,Spring Bean的生命周期只有四个阶段:实例化 Instantiation --> 属性赋值 Populate  --> 初始化 Initialization  --> 销毁 Destruction

  1. 实例化Bean:

    对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。
    对于ApplicationContext容器,当容器启动结束后,通过获取BeanDefinition对象中的信息,实例化所有的bean。
    
  2. 设置对象属性(依赖注入):

    实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息 以及 通过BeanWrapper提供的设置属性的接口完成属性设置与依赖注入。
    
  3. 处理Aware接口:

    Spring会检测该对象是否实现了xxxAware接口,通过Aware类型的接口,可以让我们拿到Spring容器的一些资源:
    
  • 如果这个Bean实现了BeanNameAware接口,会调用它实现的==setBeanName(String beanId)==方法,传入Bean的名字;
  • 如果这个Bean实现了BeanClassLoaderAware接口,调用==setBeanClassLoader()==方法,传入ClassLoader对象的实例。
  • 如果这个Bean实现了BeanFactoryAware接口,会调用它实现的==setBeanFactory()==方法,传递的是Spring工厂自身。
  • 如果这个Bean实现了ApplicationContextAware接口,会调用==setApplicationContext(ApplicationContext)==方法,传入Spring上下文;
  1. BeanPostProcessor前置处理:

    如果想对Bean进行一些自定义的前置处理,那么可以让Bean实现了BeanPostProcessor接口,那将会调用postProcessBeforeInitialization(Object obj, String s)方法。
    
  2. InitializingBean:

    如果Bean实现了InitializingBean接口,执行afeterPropertiesSet()方法。
    
  3. init-method:

    如果Bean在Spring配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法。
    
  4. BeanPostProcessor后置处理:

    如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法;由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术;
    

以上几个步骤完成后,Bean就已经被正确创建了,之后就可以使用这个Bean了。

  1. DisposableBean:

    当Bean不再需要时,会经过清理阶段,如果Bean实现了==DisposableBean==这个接口,会调用其实现的destroy()方法;
    
  2. destroy-method:

    最后,如果这个Bean的Spring配置中配置了==destroy-method==属性,会自动调用其配置的销毁方法。
    

10、Spring中bean的作用域

  1. singleton :

    这种 bean 范围是默认的,这种范围确保不管接受到多少个请求,每个容器中只有一个bean 的实例,单例的模式由 bean factory 自身来维护 。
    
  2. prototype :

     原形范围与单例范围相反,为每一个 bean 请求提供一个实例 。
    
  3. request :

    在请求 bean 范围内会每一个来自客户端的网络请求创建一个实例,在请求完成以后, bean会失效并被垃圾回收器回收 。
    
  4. Session :

    与请求范围类似,确保每个 session 中有一个 bean 的实例,在 session 过期后, bean 会随之失效 。
    
  5. global-session :

    global-session 和 Portlet 应用相关 。当你的应用部署在 Portlet 容器中工作时,它包含很多 portlet。
    如果你想要声明让所有的 portlet 共用全局的存储变量的话,那么这全局变量需要存储在 global-session 中 。
    

11、Spring框架中的Bean是线程安全的么?如果线程不安全,那么如何处理?

Spring容器本身并没有提供Bean的线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全的特性,但是具体情况还是要结合Bean的作用域来讨论。
  • 对于prototype作用域的Bean,每次都创建一个新对象,也就是线程之间不存在Bean共享,因此不会有线程安全问题。

  • 对于singleton作用域的Bean,所有的线程都共享一个单例实例的Bean,因此是存在线程安全问题的。但是如果单例Bean是一个无状态Bean,也就是线程中的操作不会对Bean的成员执行查询以外的操作,那么这个单例Bean是线程安全的。比如Controller类、Service类和Dao等,这些Bean大多是无状态的,只关注于方法本身。

    有状态Bean(Stateful Bean) :就是有实例变量的对象,可以保存数据,是非线程安全的。
    无状态Bean(Stateless Bean):就是没有实例变量的对象,不能保存数据,是不变类,是线程安全的。
    

12、Spring基于xml注入bean的几种方式

  • set()方法注入;
  • 构造器注入:
    ①通过index设置参数的位置;
    ②通过type设置参数类型;
  • 静态工厂注入;
  • 实例工厂;

13、Spring的自动装配

  • 在Spring框架xml配置中共有5种自动装配:
  1. no:默认的方式是不进行自动装配的,通过手工设置ref属性来进行装配bean。
  2. byName:通过bean的名称进行自动装配,如果一个bean的 property 与另一bean 的name 相同,就进行自动装配。
  3. byType:通过参数的数据类型进行自动装配。
  4. constructor:利用构造函数进行装配,并且构造函数的参数通过byType进行装配。
  5. autodetect:自动探测,如果有构造方法,通过 construct的方式自动装配,否则使用 byType的方式自动装配。
  • 基于注解的自动装配方式:

    使用@Autowired、@Resource注解来自动装配指定的bean。
    在使用@Autowired注解之前需要在Spring配置文件进行配置,在启动spring IoC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IoC容器自动查找需要的bean,并装配给该对象的属性。
    在使用@Autowired时,首先在容器中查询对应类型的bean:
    如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据;
    如果查询的结果不止一个,那么@Autowired会根据名称来查找;
    如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。
    

@Autowired可用于:构造函数、成员变量、Setter方法
注:@Autowired和@Resource之间的区别:

  1. @Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。
  2. @Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入。

14、 springmvc常用到的注解,作用、原理是什么?

  • @Controller注解

    是在Spring的org.springframework.stereotype包下,用于指示Spring类的实例是一个控制器,使用@Controller注解的类不需要继承特定的父类或者实现特定的接口,相对之前的版本实现Controller接口变的更加简单。
    而Controller接口的实现类只能处理一个单一的请求动作,而@Controller注解的控制器可以同时支持处理多个请求动作,使程序开发变的更加灵活。
    @Controller用户标记一个类,使用它标记的类就是一个Spring MVC Controller对象,即:一个控制器类。Spring使用扫描机制查找应用程序中所有基于注解的控制器类,分发处理器会扫描使用了该注解的方法,并检测该方法是否使用了@RequestMapping注解,而使用@RequestMapping注解的方法才是真正处理请求的处理器。
    
  • @RequestParam注解

    用于将指定的请求参数赋值给方法中的形参。它有4种属性:
    
  • name属性:String类型,它可以指定请求头绑定的名称;
  • value属性:String类型,它可以设置是name属性的别名;
  • required属性:boolean类型,它可以设置指定参数是否必须绑定;
  • defalutValue属性:String类型,它可以设置如果没有传递参数可以使用默认值。
  • @PathVaribale注解

    可以非常方便的获得请求url中的动态参数。@PathVaribale注解只支持一个属性value,String类型,表示绑定的名称,如果省略则默认绑定同名参数。
    

16、 SpringMVC执行流程和原理

  1. 用户发送出请求到前端控制器DispatcherServlet。
  2. DispatcherServlet收到请求调用HandlerMapping(处理器映射器)。
  3. HandlerMapping找到具体的处理器(可查找xml配置或注解配置),生成处理器对象及处理器拦截器(如果有),再一起返回给DispatcherServlet(处理器适配器)。
  4. DispatcherServlet调用HandlerAdapter(处理器适配器)。
  5. HandlerAdapter经过适配调用具体的处理器(Handler/Controller)。
  6. Controller执行完成返回ModelAndView对象。
  7. HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet。
  8. DispatcherServlet将ModelAndView传给ViewReslover(视图解析器)。
  9. ViewReslover解析后返回具体View(视图)。
  10. DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
  11. DispatcherServlet响应用户。

17、有哪些重要的Spring注解?

@Controller:用于 Spring MVC 项目中的控制器类。 @Service: 用于服务类。
@RequestMapping:用于在控制器处理程序方法中配置 URI 映射。
@ResponseBody:用于发送 Object 作为响应,通常用于发送 XML 或 JSON 数据作为响应。
@PathVariable:用于将动态值从 URI 映射到处理程序方法参数。
@Autowired:用于在 spring bean 中自动装配依赖项。
@Scope:用于配置 spring bean 的范围。
@Configuration、@ComponentScan 和 @Bean:用于基于 java 的配置。
@Aspect、@Before、@After、@Around、@Pointcut:用于切面编程(AOP)

18、Spring事务的实现方式和实现原理

Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。真正的数据库层的事务提交和回滚是通过binlog或者redo log实现的。

  • Spring事务的种类
  1. 编程式事务管理使用TransactionTemplate。
  2. 声明式事务管理建立在AOP之上的。其本质是通过AOP功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。
   声明式事务最大的优点就是不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明或通过@Transactional注解的方式,便可以将事务规则应用到业务逻辑中。声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式,使业务代码不受污染,只要加上注解就可以获得完全的事务支持。唯一不足地方是,最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。
  • Spring的事务传播行为
  1. PROPAGATION_REQUIRED: 如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。
  2. PROPAGATION_SUPPORTS: 支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。‘
  3. PROPAGATION_MANDATORY: 支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。
  4. PROPAGATION_REQUIRES_NEW: 创建新事务,无论当前存不存在事务,都创建新事务。
  5. PROPAGATION_NOT_SUPPORTED: 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  6. PROPAGATION_NEVER: 以非事务方式执行,如果当前存在事务,则抛出异常。
  7. PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。
  • Spring中的隔离级别
  1. ISOLATION_DEFAULT:这是个 PlatfromTransactionManager 默认的隔离级别,使用数据库默认的事务隔离级别。
  2. ISOLATION_READ_UNCOMMITTED:读未提交,允许另外一个事务可以看到这个事务未提交的数据。
  3. ISOLATION_READ_COMMITTED:读已提交,保证一个事务修改的数据提交后才能被另一事务读取,而且能看到该事务对已有记录的更新。
  4. ISOLATION_REPEATABLE_READ:可重复读,保证一个事务修改的数据提交后才能被另一事务读取,但是不能看到该事务对已有记录的更新。
  5. ISOLATION_SERIALIZABLE:一个事务在执行的过程中完全看不到其他事务对数据库所做的更新。

19、Spring如何管理事务的?

Spring事务管理主要包括3个接口,Spring事务主要由以下三个共同完成的:

  1. PlatformTransactionManager:事务管理器,主要用于平台相关事务的管理。主要包括三个方法:
    ①commit:事务提交
    ②rollback:事务回滚。
    ③getTransaction:获取事务状态。
  2. TransacitonDefinition:事务定义信息,用来定义事务相关属性,给事务管理器 PlatformTransactionManager使用这个接口有下面四个主要方法:
    ①getIsolationLevel:获取隔离级别。
    ②getPropagationBehavior:获取传播行为。
    ③getTimeout获取超时时间。
    ④isReadOnly:是否只读(保存、更新、删除时属性变为false–可读写,查询时为true–只读)事务管理器能够根据这个返回值进行优化,这些事务的配置信息,都可以通过配置文件进行配置。
  3. TransationStatus:事务具体运行状态,事务管理过程中,每个时间点事务的状态信息。例如:
    ①hasSavepoint():返回这个事务内部是否包含一个保存点。
    ②isCompleted():返回该事务是否已完成,也就是说,是否已经提交或回滚。
    ③isNewTransaction():判断当前事务是否是一个新事务。

Ⅱ、 MyBaits

MyBaits的优缺点

  • 优点
  1. 基于SQL语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL写在XML里,解除sql与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并可重用。
  2. 与JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连接;
  3. 很好的与各种数据库兼容(因为MyBatis使用JDBC来连接数据库,所以只要JDBC支持的数据库,MyBatis都支持)。
  4. 能够与Spring很好的集成;
  5. 提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象关系组件维护。
  • 缺点
  1. SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求。
  2. SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。

2、相对于JDBC,MyBatis做了哪些改进?

  1. 数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能。
    解决:在mybatis-config.xml中配置数据链接池,使用连接池管理数据库连接。
  2. Sql语句写在代码中造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。
    解决:将Sql语句配置在mapper.xml文件中与Java代码分离。
  3. 向sql语句传参数麻烦,因为sql语句的where条件不一定,可能多也可能少,占位符需要和参数一一对应。
    解决: Mybatis自动将java对象映射至sql语句。
  4. 对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成 pojo 对象解析比较方便。
    解决:Mybatis自动将sql执行结果映射至java对象。

3、 MyBatis编程步骤

  1. 创建SqlSessionFactory
  2. 通过SqlSessionFactory创建SqlSession
  3. 通过sqlsession执行数据库操作
  4. 调用session.commit()提交事务
  5. 调用session.close()关闭会话

4、说说MyBatis的工作原理

  1. 读取 MyBatis 配置文件:
    mybatis-config.xml 为 MyBatis 的全局配置文件,配置了 MyBatis 的运行环境等信息,例如数据库连接信息。
  2. 加载映射文件:
    映射文件即 SQL 映射文件,该文件中配置了操作数据库的 SQL 语句,需要在MyBatis 配置文件 mybatis-config.xml 中加载。mybatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。
  3. 构造会话工厂:
    通过 MyBatis 的环境等配置信息构建会话工厂 SqlSessionFactory。
  4. 创建会话对象:
    由会话工厂创建 SqlSession 对象,该对象中包含了执行 SQL 语句的所有方法。
  5. Executor 执行器: MyBatis 底层定义了一个 Executor 接口来操作数据库,它将根据 SqlSession 传 递的参数动态地生成需要执行的 SQL 语句,同时负责查询缓存的维护。
  6. MappedStatement 对象:
    在 Executor 接口的执行方法中有一个 MappedStatement 类型的参数, 该参数是对映射信息的封装,用于存储要映射的 SQL 语句的 id、参数等信息。
  7. 输入参数映射:
    输入参数类型可以是 Map、List 等集合类型,也可以是基本数据类型和 POJO 类 型。输入参数映射过程类似于 JDBC 对 preparedStatement 对象设置参数的过程。
  8. 输出结果映射:
    输出结果类型可以是 Map、 List 等集合类型,也可以是基本数据类型和 POJO 类 型。输出结果映射过程类似于 JDBC 对结果集的解析过程。

5、#{} 和 ${} 的区别是什么?

  • #{} 是预编译处理

  • ${} 是字符串替换。

    MyBatis在处理 #{} 时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
    MyBatis在处理${}时,就是把${}替换成变量的值。
    使用#{}可以有效的防止SQL注入,提高系统安全性。
    

6、当实体类中的属性名和表中的字段名不一样 ,怎么办 ?

  • 通过在查询的sql语句中定义字段名的别名,让字段名的别名和实体类的属性名一致。
  • 通过< resultMap >来映射字段名和实体类属性名的一一对应的关系。

7、通常一个mapper.xml文件,都会对应一个Dao接口,这个Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗?

Mapper 接口的工作原理是JDK动态代理,Mybatis运行时会使用JDK动态代理为Mapper接口生成代理对象 MappedProxy,代理对象会拦截接口方法,根据类的全限定名+方法名,唯一定位到一个MapperStatement并调用执行器执行所代表的sql,然后将sql执行结果返回。
Mapper接口里的方法,是不能重载的,因为是使用 全限名+方法名 的保存和寻找策略。
  • Dao接口,就是Mapper接口。

  • 接口的全限名,就是映射文件中的namespace的值;

  • 接口的方法名,就是映射文件中Mapper的Statement的id值;

  • 接口方法内的参数,就是传递给sql的参数。

    当调用接口方法时,通过 “接口全限名+方法名”拼接字符串作为key值,可唯一定位一个MapperStatement,因为在Mybatis中,每一个SQL标签,都会被解析为一个MapperStatement对象。
    举例:com.mybatis3.mappers.StudentDao.findStudentById,可以唯一找到namespace为 com.mybatis3.mappers.StudentDao下面 id 为 findStudentById 的 MapperStatement。
    

8、MyBatis的Xml映射文件中,不同的xml映射文件,id是否可以重复?

不同的Xml映射文件,如果配置了namespace,那么id可以重复;
如果没有配置namespace,那么id不能重复;原因就是namespace+id是作为Map的key使用的,如果没有namespace,就剩下id,那么,id重复会导致数据互相覆盖。
有了namespace,自然id就可以重复,namespace不同,namespace+id自然也就不同。

9、Mybatis是如何进行分页的?分页插件的原理是什么?

  • Mybatis 使用 RowBounds 对象进行分页,也可以直接编写 sql 实现分页,也可以使用
    Mybatis 的分页插件。
  • 分页插件的原理:实现 Mybatis 提供的接口,实现自定义插件,在插件的拦截方法内拦
    截待执行的 sql,然后重写 sql。

10、 MyBatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?

  • 使用 < resultMap > 标签,逐一定义数据库列名和对象属性名之间的映射关系。

  • 是使用sql列的别名功能,将列的别名书写为对象属性名。

    有了列名与属性名的映射关系后,MyBatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。
    

11、MyBatis是否支持延迟加载?如果支持,它的实现原理是什么?

MyBatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。
在Mybatis配置文件中,可以配置是否启用延迟加载 lazyLoadingEnabled = true|false。
延迟加载的基本原理是,使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b属性就有值了,接着完成a.getB().getName()方法的调用。

12、MyBatis动态sql有什么用?执行原理?有哪些动态sql?

MyBatis动态sql可以在Xml映射文件内,以标签的形式编写动态sql,执行原理是根据表达式的值 完成逻辑判断 并动态拼接sql的功能。
MyBatis提供了9种动态sql标签:trim | where | set | foreach | if | choose | when | otherwise | bind。

13、使用MyBatis的mapper接口调用时有哪些要求?

  • Mapper接口方法名和mapper.xml中定义的每个sql的id相同;
  • Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同;
  • Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同;
  • Mapper.xml文件中的namespace即是mapper接口的类路径。

14、在mapper中如何传递多个参数?

//第一种:
//DAO层的函数
Public UserselectUser(String name,String area);
//对应的xml,#{0}代表接收的是dao层中的第一个参数,#{1}代表dao层中第二参数,更多参数一致往后加即可。
<select id="selectUser"resultMap="BaseResultMap">  select*fromuser_user_t whereuser_name = #{0} anduser_area=#{1}
</select>  //第二种: 使用 @param 注解:
public interface usermapper {user selectuser(@param(“username”) string username,@param(“hashedpassword”) string hashedpassword);
}
//然后,就可以在xml像下面这样使用(推荐封装为一个map,作为单个参数传递给mapper):
<select id="selectuser" resulttype="user">select id, username, hashedpasswordfrom some_tablewhere username = #{username}and hashedpassword = #{hashedpassword}
</select>//第三种:多个参数封装成map
try{//映射文件的命名空间.SQL片段的ID,就可以调用对应的映射文件中的SQL
//由于我们的参数超过了两个,而方法中只有一个Object参数收集,因此我们使用Map集合来装载我们的参数
Map<String, Object> map = new HashMap();map.put("start", start);map.put("end", end);return sqlSession.selectList("StudentID.pagination", map);}catch(Exception e){e.printStackTrace();sqlSession.rollback();throw e; }
finally{MybatisUtil.closeSqlSession();}

15、 一对一,一对多的关联查询 ?

<mapper namespace="com.lcb.mapping.userMapper">  <!--association  一对一关联查询 -->  <select id="getClass" parameterType="int" resultMap="ClassesResultMap">  select * from class c,teacher t where c.teacher_id=t.t_id and c.c_id=#{id}  </select>  <resultMap type="com.lcb.user.Classes" id="ClassesResultMap">  <!-- 实体类的字段名和数据表的字段名映射 -->  <id property="id" column="c_id"/>  <result property="name" column="c_name"/>  <association property="teacher" javaType="com.lcb.user.Teacher">  <id property="id" column="t_id"/>  <result property="name" column="t_name"/>  </association>  </resultMap>  <!--collection  一对多关联查询 -->  <select id="getClass2" parameterType="int" resultMap="ClassesResultMap2">  select * from class c,teacher t,student s where c.teacher_id=t.t_id and c.c_id=s.class_id and c.c_id=#{id}  </select>  <resultMap type="com.lcb.user.Classes" id="ClassesResultMap2">  <id property="id" column="c_id"/>  <result property="name" column="c_name"/>  <association property="teacher" javaType="com.lcb.user.Teacher">  <id property="id" column="t_id"/>  <result property="name" column="t_name"/>  </association>  <collection property="student" ofType="com.lcb.user.Student">  <id property="id" column="s_id"/>  <result property="name" column="s_name"/>  </collection>  </resultMap>
</mapper>

16、MyBatis实现一对一和一对多有几种方式?具体怎么操作的?

  • 实现一对一:

    联合查询是几个表联合查询,只查询一次,通过在resultMap里面配置association节点配置一对一的类就可以完成。
    嵌套查询是先查一个表,根据这个表里面的结果的 外键id,去再另外一个表里面查询数据,也是通过association配置,但另外一个表的查询通过select属性配置。
    
  • 实现一对多:

    联合查询是几个表联合查询,只查询一次,通过在resultMap里面的collection节点配置一对多的类就可以完成。
    嵌套查询是先查一个表,根据这个表里面的结果的外键id,去再另外一个表里面查询数据,也是通过配置collection,但另外一个表的查询通过select节点配置。
    

17、 什么是MyBatis的接口绑定?有哪些实现方式?

接口绑定,就是在MyBatis中任意定义接口,然后把接口里面的方法和SQL语句绑定,我们直接调用接口方法就可以,这样比起原来了SqlSession提供的方法我们可以有更加灵活的选择和设置。
接口绑定有两种实现方式,一种是通过注解绑定,就是在接口的方法上面加上 @Select、@Update等注解,里面包含Sql语句来绑定;
另外一种就是通过xml里面写SQL来绑定,在这种情况下,要指定xml映射文件里面的namespace必须为接口的全路径名。
当Sql语句比较简单时候,用注解绑定,当SQL语句比较复杂时候,用xml绑定,一般用xml绑定的比较多。

Ⅲ、SpringBoot

1、SpringBoot 有哪些优点?

  • 减少开发,测试时间和努力。
  • 使用JavaConfig有助于避免使用XML。
  • 避免大量的Maven导入和各种版本冲突。
  • 没有单独的Web服务器需要。这意味着你不再需要启动Tomcat,Glassfish或其他任何东西。
  • 需要更少的配置,因为没有web.xml文件,只需添加@Configuration注解的类,然后添加@Bea注解的方法,Spring将自动加载对象并像以前一样对其进行管理。@Autowired添加到bean方法中,以使Spring自动装入需要的依赖关系中。

2、SpringBoot 的核心注解是哪个?它主要由哪几个注解组成的?

启动类上面的注解@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解:

  • @SpringBootConfiguration:
    组合了 @Configuration 注解,实现配置文件的功能。
  • @EnableAutoConfiguration:
    打开自动配置的功能,也可以关闭某个自动配置的选项, 例如: Java 中关闭数据源自动配置功能: @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class })。
  • @ComponentScan:
    Spring组件扫描。

3、SpringBoot 的核心配置文件有哪几个?它们的区别是什么?

SpringBoot 的核心配置文件是 application 和 bootstrap 配置文件。
application 配置文件这个容易理解,主要用于 Spring Boot 项目的自动化配置。

bootstrap 配置文件有以下几个应用场景:

  • 使用 Spring Cloud Config 配置中心时,这时需要在 bootstrap 配置文件中添加连接到配置中心的配置属性来加载外部配置中心的配置信息;
  • 一些固定的不能被覆盖的属性;
  • 一些加密/解密的场景;

4、SpringBoot Starter的工作原理

我个人理解SpringBoot就是由各种Starter组合起来的,我们自己也可以开发Starter
在sprinBoot启动时由@SpringBootApplication注解会自动去maven中读取每个starter中的spring.factories文件,该文件里配置了所有需要被创建spring容器中的Bean,并且进行自动配置把Bean注入SpringContext中 (SpringContext是Spring的配置文件)

5、 SpringBoot 配置加载顺序

  1. 开发者工具 Devtools 全局配置参数;
  2. 单元测试上的 @TestPropertySource 注解指定的参数;
  3. 单元测试上的 @SpringBootTest 注解指定的参数;
  4. 命令行指定的参数,如 java -jar springboot.jar --name="Java技术栈"
  5. 命令行中的 SPRING_APPLICATION_JSONJSON 指定参数, 如 java - Dspring.application.json='{"name":"Java技术栈"}' -jar springboot.jar
  6. ServletConfig 初始化参数;
  7. ServletContext 初始化参数;
  8. JNDI参数(如 java:comp/env/spring.application.json);
  9. Java系统参数(来源:System.getProperties());
  10. 操作系统环境变量参数;
  11. RandomValuePropertySource 随机数,仅匹配:ramdom.*
  12. JAR包外面的配置文件参数(application-{profile}.properties(YAML)
  13. JAR包里面的配置文件参数(application-{profile}.properties(YAML)
  14. JAR包外面的配置文件参数(application.properties(YAML)
  15. JAR包里面的配置文件参数(application.properties(YAML)
  16. @Configuration配置文件上 @PropertySource 注解加载的参数;
  17. 默认参数(通过 SpringApplication.setDefaultProperties 指定);

数字小的优先级越高,即数字小的会覆盖数字大的参数值

6、 SpringBoot 有哪几种读取配置的方式?

  • @PropertySource
  • @Value
  • @Environment,
  • @ConfigurationProperties

7、 SpringBoot 实现热部署有哪几种方式?

热部署就是可以不用重新运行SpringBoot项目可以实现操作后台代码自动更新到以运行的项目中,主要有两种方式:
  • Spring Loaded
  • Spring-boot-devtools
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId>
</dependency>

8、 如何重新加载SpringBoot上的更改,而无需重新启动服务器?

这可以使用DEV工具来实现,通过这种依赖关系,嵌入式tomcat将重新启动。

org.springframework.boot
spring-boot-devtools
true

9、SpringBoot事物的使用

首先使用注解EnableTransactionManagement开启事物之后,然后在Service方法上添加注解Transactional便可。

10、什么是 JavaConfig?

Spring JavaConfig 是 Spring 社区的产品,Spring 3.0引入了他,它提供了配置 Spring IOC 容器的纯Java 方法,因此它有助于避免使用 XML 配置。

使用 JavaConfig 的优点在于:

  • 面向对象的配置:由于配置被定义为 JavaConfig 中的类,因此用户可以充分利用 Java 中的面向对象功能,一个配置类可以继承另一个,重写它的@Bean 方法等。
  • 减少或消除 XML 配置
  • 类型安全和重构友好:JavaConfig 提供了一种类型安全的方法来配置 Spring容器,可以按类型而不是按名称检索 bean,不需要任何强制转换或基于字符串的查找。

常用的Java config:

@Configuration:
在类上打上写下此注解,表示这个类是配置类
@ComponentScan:
该注解默认会扫描该类所在的包下所有的配置类,相当于之前的
< context:component-scan >
@Bean:
bean的注入:相当于以前的< bean id=“objectMapper” class=“org.codehaus.jackson.map.ObjectMapper” />
@EnableWebMvc:
相当于xml的< mvc:annotation-driven >
@ImportResource:
相当于xml的 < import resource=“applicationContextcache.xml” >

11、SpringBoot 中如何实现定时任务 ?

在 Spring Boot 中使用定时任务主要有两种不同的方式,一个就是使用 Spring 中的 @Scheduled 注解,另一个则是使用第三方框架 Quartz。
使用 Spring 中的 @Scheduled 的方式主要通过 @Scheduled 注解来实现。
使用 Quartz ,则按照 Quartz 的方式,定义 Job 和 Trigger 即可。

12、 有哪些spring-boot-maven-plugin命令?

spring-boot-maven-plugin 提供了一些像 jar 一样打包或者运行应用程序的命令。

  • spring-boot:run
    运行 SpringBooty 应用程序。
  • spring-boot:repackage
    重新打包 jar 包或者是 war 包使其可执行
  • spring-boot:start 和 spring-boot:stop
    管理 Spring Boot 应用程序的生命周期(也可以说是为了集成测试)。
  • spring-boot:build-info
    生成执行器可以使用的构造信息。

13、SpringBoot的自动配置原理是什么?

主要是SpringBoot的启动类上的核心注解SpringBootApplication注解主配置类,有了这个主配置类启动时就会为SpringBoot开启一个@EnableAutoConfiguration注解自动配置功能。
有了这个EnableAutoConfiguration的话就会从配置文件META_INF/Spring.factories加载可能用到的自动配置类。
去重,并将exclude和excludeName属性携带的类排除
过滤,将满足条件(@Conditional)的自动配置类返回

14、 什么是YAML? 它有哪些优点?

YAML是一种人类可读的数据序列化语言。
它通常用于配置文件,与属性文件相比,如果我们想要在配置文件中添加复杂的属性,YAML文件就更加结构化,而且更少混淆。
可以看出YAML具有分层配置数据。

优点:

  • 配置有序,在一些特殊的场景下,配置有序很关键
  • 简洁明了,他还支持数组,数组中的元素可以是基本数据类型也可以是对象

14、SpringBoot多数据源拆分的思路

先在properties配置文件中配置两个数据源,创建分包mapper,使用@ConfigurationProperties读取properties中的配置,使用@MapperScan注册到对应的mapper包中。

15、SpringBoot多数据源事务如何管理?

  • 是在service层的@TransactionManager中使用transactionManager指定DataSourceConfig中配置的事务。
  • 是使用jta-atomikos实现分布式事务管理。

16、SpringBoot 打成的 jar 和普通的 jar 有什么区别 ?

  • SpringBoot 项目最终打包成的 jar 是可执行 jar ,这种 jar 可以直接通过 java -jar xxx.jar 命令来运行,这种 jar 不可以作为普通的 jar 被其他项目依赖,即使依赖了也无法使用其中的类。
  • SpringBoot 的 jar 无法被其他项目依赖,主要还是他和普通 jar 的结构不同,普通的 jar 包,解压后直接就是包名,包里就是我们的代码,而 Spring Boot 打包成的可执行 jar 解压后,在 \BOOTINF\classes 目录下才是我们的代码,因此无法被直接引用。如果非要引用,可以在 pom.xml文件中增加配置,将 Spring Boot 项目打包成两个 jar ,一个可执行,一个可引用。

Java校招面试汇总相关推荐

  1. 【Java校招面试】实战算法(五)——分数相加(虎牙)

    目录 前言 一.题目描述 二.解题思路 三.代码实现 后记 前言 "实战算法"是本专栏的第三个部分,本篇博文是第五篇博文,主要讲解虎牙笔试题--分数相加--的解法,如有需要,可: ...

  2. 关于java校招面试技术心得

    写在前面 写作动机 这篇文章不能带给你什么 我的面试经历 春招找实习,投了三家(阿里,腾讯,美团),全部拿到实习 Offer,去了阿里. 秋招为了稳妥地转正,拿一个好的评级,在专心准备转正面试,错过了 ...

  3. Java校招面试经验汇总,最终入职阿里

    一面 自我介绍 项目中的监控:那个监控指标常见的有哪些? 微服务涉及到的技术以及需要注意的问题有哪些? 注册中心你了解了哪些? consul 的可靠性你了解吗? consul 的机制你有没有具体深入过 ...

  4. Java校招面试经验汇总,看完直接跪服

    美团技术一面20分钟 晚7点,因为想到下周一才面试,我刚准备出去打个羽毛球,北京的电话就来了.面试官各种抱歉,说开会拖延了. 1.自我介绍 说了很多遍了,很流畅捡重点介绍完. 2.问我数据结构算法好不 ...

  5. Java的面试汇总,有这些还担心不通过?

    面试官提问的内容通常会由易到难,如果前面容易的内容都答不上来,往往就被刷了,如果是前面提问的都答上来了,他会问一些有深度的问题,这时候就算你回答不上来也不要慌张,他只是看看你的薪资水平而已 接下来我就 ...

  6. Java校招面试,项目怎么聊

    大家好,我是一个双非院校计算机专业的硕士研究生.在秋招期间,我获得了阿里.字节.携程.科大讯飞.用友的Java开发offer.在面试技巧方面,也是经历了从什么都不懂,面试官问啥我说啥到逐渐熟练,摸出门 ...

  7. 华为java校招面试流程_华为面试流程是什么?

    本人今年9月17号刚参加完华为校招的笔试,接着就较为顺利的接到了华为发过来的在线职业风格的测试,然后就是收到去一面的短信通知.可惜一面之后就无缘二面了,虽然说有点遗憾和可惜,但是也不失为一次宝贵的面试 ...

  8. Java技术面试汇总

    1.servlet执行流程 客户端发出http请求,web服务器将请求转发到servlet容器,servlet容器解析url并根据web.xml找到相对应的servlet,并将request.resp ...

  9. 【2021年最新版Java校招面试题目合集】

    1.什么是Java虚拟机?为什么Java被称作是"平台无关的编程语言"? Java虚拟机是一个可以执行Java字节码的虚拟机进程.Java源文件被编译成能被Java虚拟机执行的字节 ...

最新文章

  1. 奇异值分解 SVD 的数学解释
  2. ctypes python3_聊聊Python ctypes 模块
  3. C#编程总结--总目录
  4. AndoridSQLite数据库开发基础教程(8)
  5. Transformer Family
  6. html5——DOM扩展
  7. Qt学习笔记-web图片爬取器(webKit)Qt5.6以下的版本才有
  8. dubbo调用超时回滚_如何处理Dubbo调用超时?
  9. postgresql 相关杂记
  10. 块存储性能--阿里云ECS磁盘
  11. 130242014060-郑佳敏-第3次实验
  12. cenyos7安装 yum不可用_小区阳台隐形网横管不可用扁管,要改再去安装,还有问题...
  13. [IT新应用]无线投影技术
  14. JVM虚拟机详解(一)JVM与JAVA体系统结构
  15. 腾讯魔镜壁纸所在位置
  16. 关于浏览器主页篡改修复
  17. UML图详解(九)包图
  18. Widows Server 2012上无法安装.net framework 3.5
  19. Harbor可视化docker私有仓库
  20. 乐视max70老款_这货是电视?超大尺寸乐视TV Max70试玩

热门文章

  1. MDK5安装破解以及安装stm32与C51支持包(附安装包)
  2. 基于Android的校园考研论坛管理系统
  3. 基于采蜜机制的正弦余弦算法
  4. Vue之用户登录功能(六)使用axios解决‘Access-Control-Allow-Origin’跨域
  5. 闪电动画模拟(Dielectric Breakdown Model)附源码
  6. photosho 等距复制或旋转复制
  7. 使用计算机粘贴板的步骤,如何打开电脑剪贴板图文教程
  8. Apache2.4常用配置
  9. python openpyxl合并单元格_python使用openpyxl excel 合并拆分单元格
  10. 废旧机械硬盘用于笔记本扩容!!!