java级开发面试八股文

1、java基础知识

Q1、equals和==的区别

  • ==是判断两个变量或实例是不是指向同一个内存空间,equals是判断两个变量或实例所指向的内存空间的值是不是相同。
  • ==是对内存地址进行比较,而equals比较的是两个字符串的值是否相等。
  • ==指引用是否相同,而equals是比较值是否相同。

Q2:集合的父类是什么

List<T> extends Collection<T>          Collection<T> entends Iterable<T>

List:元素有序且可重复,主要实现类有 ArrayList,LinkedList,Vector。

Set:元素不可重复。主要实现类:HashSet,TreeSet,LinkedHashSet。

Vector类是线程安全的,因为Vector类里面的方法都是有synchronized修饰的。

ArrayList类不是线程安全的,ArrayList类里面的方法是没有用synchronized同步的。

Q3:List、Hashmap、Set区别

1、List( 输入和输出顺序一致、可重复)

ArrayList与Vector

①底层数据结构是数组,查询快,增删慢。

②前者效率高、线程不安全,后者效率低、线程安全。

LinkedList

①底层数据结构是链表,查询慢,增删快。

②线程不安全,效率高。因为是not synchronized。

2、Set

HashSet(唯一、输出元素升序降序有序)

①底层数据结构是哈希表。Hash表又叫散列表,其底层是数组,在数组中存放关键字,关键字在数据中的位置是由散列函数计算得来的。常见的散列函数有:直接定址法(线性函数)、除留余数法、数字分析法、平方取中法。在存储的时候可能会产生冲突即一个位置有多个关键字会去竞争,为了避免冲突就要去用线性探测法、平方探测法、再散列法。

②HashSet常用方法有:add、remove、isEmpty、contain等

LinkedHashSet(唯一、输入和输出顺序一致)

①底层数据结构是链表和哈希表,继承自HashSet。

②由链表保证元素有序、哈希表保证元素唯一。

TreeSet(唯一、输入和输出顺序一致)

①底层数据结构是红黑树,红黑树是一颗平衡二叉查找树。TreeSet源码里面有一个构造方法使用了Comparator比较强接口,因此在构造一个TreeSet时会调比较器构造一颗平衡二叉查找树。因此TreeSet是有序的。

  1. public TreeSet(Comparator<? super E> comparator) {

  2. this(new TreeMap<>(comparator));

  3. }

②自然排序、比较器排序。

③根据比较的返回值是否是0来决定是否唯一。

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

需要全套面试笔记【点击此处即可】免费获取

3、Map

key-value键值对,有三个比较重要的实现类,分别是HashMap、TreeMap和HashTable。

HashMap

HashMap的底层结构是红黑树,HashMap是由数组和链表构成的。hashmap会自动按照key的值从小到大排列。

HashMap的使用

  1. HashMap<String, String> hm = new HashMap<>();

  2. hm.put("a", "1");

  3. hm.put("d", "5");

  4. hm.put("c", "3");

  5. System.out.println(hm); //调用hashmap类中toString方法输出

  6. //使用Set遍历输出hashmap

  7. Set<Map.Entry<String, String>> ss = hm.entrySet();

  8. for (Map.Entry<String, String> mm : ss) {

  9. System.out.println(mm.getKey() + "---" + mm.getValue());

  10. }

使用map.put(k,v)时,首先会将k,v封装到Node节点当中,然后它的底层会调用K的hashCode()方法得出hash值,通过哈希表函数/哈希算法,将hash值转换成数组的下标,下标位置上如果没有任何元素,就把Node添加到这个位置上。如果说下标对应的位置上有链表。此时,就会拿着k和链表上每个节点的k进行equal。如果所有的equals方法返回都是false,那么这个新的节点将被添加到链表的末尾。如其中有一个equals返回了true,那么这个节点的value将会被覆盖。

使用map.get(k)时,会先调用k的hashCode()方法得出哈希值,并通过哈希算法转换成数组的下标,通过数组下标定位到某个位置,然后再从这个位置上的单链表找到相应的元素。

  1. public final int hashCode() { //HashMap类中的hashCode()方法

  2. return Objects.hashCode(key) ^ Objects.hashCode(value);

  3. }

  4. public static int hashCode(Object o) { //Object类在的hashCode()方法

  5. return o != null ? o.hashCode() : 0;

  6. }

HashMap增删和查询效率都很高,是因为增删是在链表上进行的,查询有哈希函数,hashCode()帮忙定位,所以只需扫描部分就行了。

相比 jdk1.7 的 HashMap 而言,jdk1.8最重要的就是引入了红黑树的设计,当hash表的单一链表长度超过 8 个的时候,链表结构就会转为红黑树结构。为什么要这样设计呢?好处就是避免在最极端的情况下链表变得很长很长,在查询的时候,效率会非常慢。简单的说,红黑树是一种近似平衡的二叉查找树,其主要的优点就是“平衡“,即左右子树高度几乎一致,以此来防止树退化为链表,通过这种方式来保障查找的时间复杂度为 log(n)。

HashMap的扩容因子是0.75,即当容量达到3/4的时候会进行扩容。

红黑树特性:

  1. 每个节点要么是红色,要么是黑色,但根节点永远是黑色的
  2. 每个红色节点的两个子节点一定都是黑色
  3. 红色节点不能连续(也即是,红色节点的孩子和父亲都不能是红色)
  4. 从任一节点到其子树中每个叶子节点的路径都包含相同数量的黑色节点
  5. 所有的叶节点都是是黑色的(注意这里说叶子节点其实是上图中的 NIL 节点)

在树的结构发生改变时(插入或者删除操作),往往会破坏上述条件 3 或条件 4,需要通过调整使得查找树重新满足红黑树的条件。

Hashtable的方法是同步的、synchronized线程安全的;HashMap的方法不是同步的、线程不安全。HashMap效率较高,Hashtable效率较低。

Hashtable的父类是Dictionary,HashMap的父类是AbstractMap。

HashTable的操作几乎和HashMap一致,HashTable和HashMap的底层源码非常相似,主要的区别在于HashTable为了实现多线程安全,在几乎所有的方法上都加上了synchronized锁,而加锁的结果就是HashTable操作的效率十分低下。

  1. Hashtable<String, String> ht = new Hashtable<>();

  2. ht.put("a", "1");

  3. ht.put("f", "4");

  4. ht.put("e", "6");

  5. System.out.println(ht); //调用hashtable类中toString方法输出

  6. //使用Set遍历输出hashtable

  7. Set<Map.Entry<String, String>> ss1 = ht.entrySet();

  8. for (Map.Entry<String, String> mm : ss1) {

  9. System.out.println(mm.getKey() + "---" + mm.getValue());

  10. }

如何构建一个线程安全的Hashmap?

写一个类继承HashMap,重写HashMap的方法,并在一些方法前加”synchronized”

 
  1. public class SafeMap extends HashMap {

  2. @Override

  3. public synchronized Object put(Object key, Object value) {

  4. return super.put(key, value);

  5. }

  6. }

Q4、java数据类型

java中的八种基本数据类型是:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。

java四大数据类型:

  1. 整型:byte 、short 、int 、long
  2. 浮点型:float 、 double
  3. 字符型:char
  4. 布尔型:boolean

 篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

需要全套面试笔记【点击此处即可】免费获取

Q5、javaIO流

java IO流分为字符流和字节流,字节流继承inputStream和OutputStream,字符流继承自InputSteamReader和OutputStreamWriter。输入流就是从外部文件输入到内存,输出流主要是从内存输出到文件。程序中的输入输出都是以流的形式保存的,流中保存的实际上全都是字节文件。

在实现文件下载会使用到字节流,首先会获取一个文件路径path,从这个path找到要下载的文件,File file=new File(path),再把这个文件封装成一个字节流

InputStreamResource resource = new InputStreamResource(new FileInputStream((file))); 通过http传输字节流,使用HttpHeaders中的add方法可以设置要传输的内容。然后使用ResponseEntity去响应这个http请求,最终返回一个ResponseEntity对象。ResponseEntity对象就是你下载的文件。

注:不过一般实际应用中更倾向于后端生成一个数据list,再由前端合成,具体可以参考:Vue中实现自定义excel下载

 
  1. //下载上传的预算申请表附件

  2. @RequestMapping(value = "/download_appendix", method = RequestMethod.GET)

  3. public ResponseEntity<Object> download_appendix(HttpServletRequest request,HttpServletResponse response) throws FileNotFoundException {

  4. String index_no=request.getParameter("ck_index_no");

  5. Budget_apply budget_apply=new Budget_apply();

  6. budget_apply=budget_applyService.get_by_index_no(index_no);

  7. String fileName=budget_apply.getFile_path()+budget_apply.getFile_name();

  8. File file = new File(fileName);

  9. InputStreamResource resource = new InputStreamResource(new FileInputStream((file)));

  10. HttpHeaders headers = new HttpHeaders();

  11. headers.add("Content-Disposition",String.format("attachment;filename=\"%s\"",file.getName()));

  12. headers.add("Cache-Control","no-cache,no-store,must-revalidate");

  13. headers.add("Pragma","no-cache");

  14. headers.add("Expires","0");

  15. ResponseEntity<Object> responseEntity = ResponseEntity.ok()

  16. .headers(headers)

  17. .contentLength(file.length())

  18. .contentType(MediaType.parseMediaType("application/text"))

  19. .body(resource);

  20. return responseEntity;

  21. }

字符流和字节流的区别?

  • 字节流是由字节组成的(byte)
  • 字符流是由字符组成的(char)
  • Java里字符由两个字节组成. 1字符=2字节
  • JAVA中的字节流是采用ASCII编码的,
  • 字符流是采用好似UTF编码,支持中文的

Q6、jdk1.8新特性

详细可参考:java8新特性学习笔记

  1. Lambda表达式
  2. 函数式接口
  3. 方法引用和构造器调用
  4. Stream API
  5. 接口中的默认方法和静态方法
  6. 新时间日期API

1、Lambda表达式

可以使用Lambda表达式写匿名内部类

 
  1. public class Lambda {

  2. interface AAA {

  3. void test(int a, int b);

  4. }

  5. public static void main(String[] args) {

  6. AAA aaa = new AAA() { //匿名内部类

  7. @Override

  8. public void test(int a, int b) {

  9. System.out.println(a + "," + b);

  10. }

  11. };

  12. AAA aaa1 = (a, b) -> { //匿名内部类,lambda表达式

  13. System.out.println(a + "," + b);

  14. };

  15. aaa.test(2, 3);

  16. aaa1.test(3, 4);

  17. }

  18. }

2、函数式接口

@FunctionalInterface这个注解声明该接口是一个函数式接口,简单来说,函数式接口是只包含一个方法的接口。比如Java标准库中的java.lang.Runnable和 java.util.Comparator都是典型的函数式接口。

3、方法引用和构造器调用

方法引用:当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!(实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致)!

构造器引用:与函数式接口相结合,自动与函数式接口中方法兼容。可以把构造器引用赋值给定义的方法,与构造器参数列表要与接口中抽象方法的参数列表一致!

4、Stream API

Stream 是Java8中处理集合的关键抽象概念,它可以对集合进行非常复杂的查找、过滤、筛选等操作。

可以使用StreamAPI的filter对集合进行过滤。

5、接口中的默认方法和静态方法

jdk1.8对接口进行了增强

  • 在接口中可以添加使用 default 关键字修饰的非抽象方法。即:默认方法(或扩展方法)。
  • 接口里可以声明静态方法,并且可以实现。

Java 8 允许给接口添加一个非抽象的方法实现,只需要使用 default 关键字即可,这个特征又叫做扩展方法。

接口里可以声明静态方法,并且可以实现

6、新时间日期API

  • LocalDate:包含了年月日信息。
  • LocalTime:LocalTime和LocalDate类似,区别在于LocalTime包含的是时分秒(毫秒)信息。
  • LocalDateTime:LocalDateTime是LocalDate和LocalTime的组合形式,包含了年月日时分秒信息。
  • Duration:Duration用于计算两个LocalTime或者LocalDateTime的时间差。
  • Period:Period用于计算两个LocalDate之间的时长。

 篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

需要全套面试笔记【点击此处即可】免费获取

Q7、String、StringBuffer和StringBuilder

String是final类,是不可以被继承的,是不可变对象,一旦被创建,就不能修改它的值。对于已经存在的String对象的修改都是重新创建一个新的对象,然后把新的值保存进去。String底层有用到synchronized,因此String是线程安全的。

StringBuffer是一个可变对象,当对他进行修改的时候不会像String那样重新建立对象,可以通过append()方法进行字符串追加,StringBuffer类自带reverse()方法可以实现字符串逆序。StringBuffer底层方法是用synchronized修饰的,因此StringBuffer是线程安全的。

StringBuilder和StringBuffer非常相似,都是可变字符序列,但StringBuilder不是线程安全的,不过StringBuilder的效率要比StringBuffer高。

Q8、JVM调优

JVM常用的垃圾回收算法有四种:标记-清除算法、复制算法、标记-整理算法、分代收集算法。(初级开发一般不会细问,只要能回答上四种算法应该就行了)

Q9、final、finally、finalize的区别与用法

  • final用于声明属性,方法和类,分别表示属性不可交变,方法不可覆盖,类不可继承。
  • finally是异常处理语句结构的一部分,表示总是执行。
  • finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,供垃圾收集时的其他资源回收,例如关闭文件等。

Q10、两个线程同时执行i++100次

可能的结果:最小为2,最大为200。

两个线程a,b,全局变量i。

最大为200:a执行100次i++,i=100。b再执行100次i++,i=200。

最小为2:a执行1次i=i+1,当i=1还未写入内存时,b抢过来CPU,一口气执行了99次并写入内存。此时a将i=1写入内存,替换掉了b的i=99,此时i还是等于1。b再抢得cpu执行一次i++,i变为了2但还未写入内存,此时a一口气执行99次,i=100并写入内存。然后i=2写入内存会替换掉i=100的值,因此i=2。

Q11、线程中sleep和wait的区别

  1. sleep是Thread的静态方法,wait是Object的方法,任何对象实例都能调用。
  2. sleep不会释放锁,它也不需要占用锁。wait会释放锁,但调用它的前提是当前线程占有锁(即代码要在synchronized中)。
  3. 它们都可以被interrupted方法中断。

2、锁

Q1、悲观锁和乐观锁

悲观锁,就是以悲观的态度来处理一切冲突。在数据修改前就先把数据锁住,防止其他人操作数据。在锁释放前,除了加锁者自己,其他人都不可以操作该数据。直到前面的加锁者把锁释放,然后后面的人拿到锁,并对数据加锁后才能对数据进行处理。常见的悲观锁像数据库中的行锁,表锁,读锁,写锁这些都是在做操作之前先上锁,是悲观锁。java线程同步中的synchronized也是悲观锁。

注意:悲观并发控制实际上是“先取锁再访问”的保守策略,为数据处理的安全提供了保证。但是在效率方面,处理加锁的机制会让数据库产生额外的开销,还有增加产生死锁的机会。另外还会降低并行性,一个事务如果锁定了某行数据,其他事务就必须等待该事务处理完才可以处理那行数据。

悲观锁实现形式:

1、表锁

  1. lock table users; --给表加写锁

  2. unlock tables; --释放锁

2、行锁

 
  1. --使用行锁需在建表时使用innodb引擎

  2. create table t_rowLock(id int,txt varchar(50)engine=innodb;

  3. --为了使行锁生效,需关闭自动提交

  4. set autocommit=0;

  5. --InnoDB行锁是通过给索引上的索引项加锁来实现的

  6. alter table t_rowLock add index id_index(id);

  7. alter table t_rowLock add index txt_index(txt);

乐观锁,就是以乐观的态度来处理一切冲突。在数据修改前无需加锁,只有在数据要提交时才会进行冲突检测。如果冲突了则提交失败并返回错误的信息给用户。

乐观锁是一种并发类型的锁,其本身不对数据进行加锁通而是通过业务实现锁的功能,不对数据进行加锁就意味着允许多个请求同时访问数据,同时也省掉了对数据加锁和解锁的过程,这种方式大大的提高了数据操作的性能。

乐观锁适用于读操作多的场景,这样可以提高程序的吞吐量

乐观锁实现形式:

乐观锁是通过在表中增加一个字段来控制数据的版本,如果要更新某行数据会校验版本号,版本相同则可以提交更新,版本不同则把数据视为过期。

用户A和用户B都要对uname=zhangsan进行数据更新。

  1. --首先要关闭数据库的自动提交

  2. SET autocommit=0;

  3. --用户A获取要修改数据的版本号,用户A得到数据版本号VERSION=1

  4. SELECT VERSION FROM users WHERE uname='zhangsan';

  5. --用户B获取要修改数据的版本号,用户B得到数据版本号VERSION=1

  6. SELECT VERSION FROM users WHERE uname='zhangsan';

  7. --用户A对version=1的数据进行更新,并更新版本号

  8. UPDATE users SET upwd='AAAAAA' ,VERSION=VERSION+1 WHERE uname='zhangsan' AND VERSION=1;

由于关闭了自动提交,因此此时用户A的update操作,没有提交,所以数据库表中相应的数据没有发生变化。

此时在A还没有提交的时候,B也执行了update的操作。

 篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

需要全套面试笔记【点击此处即可】免费获取

  1. --用户B对version=1的数据进行更新,并更新版本号

  2. UPDATE users SET upwd='BBBBBB' ,VERSION=VERSION+1 WHERE uname='zhangsan' AND VERSION=1;

由于A还没有提交,所以此时B的操作是处于等待状态,等待A完成提交,B的update才能执行。 

A提交之后,B的Update操作也执行成功了。并提交B的操作。

然而却发现此时数据库表中的数据只是操作A进行数据修改的结果,而操作B的数据修改则无效。

这是因为当操作A提交后此时该数据的version已经改变了,version变为2了。而操作B还在用未改变之前的version作为条件修改数据,当然就修改失败了。

这就是乐观锁!!!

Q2、公平锁和非公平锁

1、公平锁

公平锁:多个线程按照申请锁的顺序去获得锁,线程会直接进入队列去排队,永远都是队列的第一位才能得到锁。

优点:所有的线程都能得到资源,不会饿死在队列中。
缺点:吞吐量会下降很多,队列里面除了第一个线程,其他的线程都会阻塞,cpu唤醒阻塞线程的开销会很大。

2、非公平锁

非公平锁:多个线程去获取锁的时候,会直接去尝试获取,获取不到,再去进入等待队列,如果能获取到,就直接获取到锁。


优点:可以减少CPU唤醒线程的开销,整体的吞吐效率会高点,CPU也不必取唤醒所有线程,会减少唤起线程的数量。
缺点:你们可能也发现了,这样可能导致队列中间的线程一直获取不到锁或者长时间获取不到锁,导致饿死。

3、数据库知识

Q1、分页查询

1、Mysql分页查询

 
  1. //取前5条数据

  2. select * from xxx limit 0,5;

  3. //取第11条到第15条数据,共5条

  4. select * from xxx limit 10,5;

2、Oracle分页查询

rownum关键字:oracle对外提供的自动给查询结果编号的关键字

 
  1. //每页显示m条数据,查询第n页数据)

  2. select rownum,t.* from (select rownum r,e.* from 要分页的表 e where rownum<=n*m) t where r>n*m-m

3、Sqlserver分页查询

 
  1. // 1、 先查询当前页码之前的所有数据id

  2. select top ((当前页数-1)*每页数据条数) id from 表名

  3. // 2、再查询所有数据的前几条,但是id不在之前查出来的数据中

  4. select top 每页数据条数 * from 表名 where id not in ( select top ((当前页数-1)*每页数据条数) id from 表名 )

 4、DB2分页查询

row_number()(Oracle中为rownum)作为查询的辅助函数

 
  1. select * from (

  2. select row_number() over(ORDER BY date DESC) as r,e.*

  3. from emp e

  4. where e.name=’A’)

  5. where r between 1 AND 5

Q2、数据库索引

1、索引失效

  • 计算、函数、类型转换会使索引失效
  • 使用 != 或者<> 的时候无法使用索引会导致全表扫描
  • is not null 也无法使用索引,但是is null是可以使用索引的
  • like以通配符开头('%abc...')mysql索引失效会变成全表扫描的操作
  • 字符串不加单引号索引失效
  • 少用or,用它来连接时会索引失效

2、索引分类

Mysql有普通索引、唯一索引、主键索引、组合索引、全文索引。

 
  1. //普通索引

  2. create index name_no_index on t_staff(no);

  3. //唯一索引

  4. create unique index name_index on t_staff(name);

  5. //主键索引

  6. ALTER TABLE tbl_name ADD PRIMARY KEY (col_list);

  7. //组合索引

  8. create index name_no_index on t_staff(no,name);

  9. //全文索引

  10. ALTER TABLE tbl_name ADD FULLTEXT index_name (col_list);

创建全文索引可以极大的提升检索效率,解决判断字段是否包含的问题,例如: 有title字段,需要查询所有包含 "政府"的记录. 需要 like "%政府%"方式查询,查询速度慢,当查询包含"政府" OR "中国"的需要是,sql难以简单满足.全文索引就可以实现这个功能。

 
  1. //全文索引使用

  2. SELECT * FROM articles WHERE MATCH (title,body) AGAINST ('精神' IN NATURAL LANGUAGE MODE);

3、索引的优缺点

优点:大大提升查询效率

缺点:建立索引后,执行update、insert、delete操作效率会降低。insert数据时,要额外建立索引耗费存储空间。

4、索引底层

索引底层通常会采用B树实现,因为B树会自动根据两边的情况自动调节,使两端无限趋近于平衡状态。可以使性能最稳定。但B树也有弊端就是当插入/修改操作过多时,B树会不断调整平衡,消耗性能,因此索引不是越多越好。

由于索引底层的B树是三层的,因此无论查找哪个,都最多只需访问三次内存就可以搞定,B树的每一个节点都包含key和value,因此经常访问的元素可能离根节点更近,因此访问也更迅速。

 篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

需要全套面试笔记【点击此处即可】免费获取

Q3、数据库调优

如果要往一张表里插入100w条信息,如何进行性能调优

首先要根据业务需求选择合适的存储引擎,Mysql的存储引擎有MyISM和InnoDB。

MyISAM 适合于一些需要大量查询的应用,但其对于有大量写操作并不是很好。甚至你只是需要update一个字段,整个表都会被锁起来,而别的进程,就算是读进程都无法操作直到读操作完成。另外,MyISAM 对于 SELECT COUNT(*) 这类的计算是超快无比的。

InnoDB 是一个非常复杂的存储引擎,对于一些小的应用,它会比 MyISAM 还慢。他是它支持“行锁” ,于是在写操作比较多的时候,会更优秀。并且,他还支持更多的高级应用,比如:事务。

  1. 在建表的时候尽量使得表中的字段都是定长的
  2. 要建立适当的索引来加快查询速度
  3. 开启数据库缓存机制,避免重复查询浪费时间和性能
  4. 使用explain,如果发现查询缓慢则要在where条件后要有索引字段
  5. 避免select *

聚集索引和非聚集索引

聚集索引一个表只能有一个,而非聚集索引一个表可以存在多个,聚集索引存储记录是物理上连续存在,而非聚集索引是逻辑上的连续,物理存储并不连续。

Q4、mysql和sql server的区别

1、性能

  • mysql读写性能一流,即使是针对大量数据也没有问题,但前提是必须是简单查询效率快。如果是复杂查询则mysql的查询效率会下降许多。就是最好不要使用函数/join/group等方式查询。
  • sqlserver简单查询速度不如mysql,但复杂查询时,性能降低的不多,可见,sqlserver的查询优化作的可能更好。

2、对机器配置要求

  • mysql对机器配置要求比sqlserver低。

3、功能

sqlserver的功能要比mysql要多,sqlserver有一个图形化操作界面比mysql的什么Navicat要强大许多。不过Mysql比sqlserver更加容易上手。

Q5、where、having、order、group by的执行顺序

where肯定在group by 之前,即也在having之前。
where后的条件表达式里不允许使用聚合函数,而having可以。

首先是where xxx对全表数据进行筛选,返回第一个结果集。

针对第一个结果集进行group by分组,然后是having对分组后的结果进行筛选(即使用having的前提条件是分组)

最后是order对最终的一个结果进行排序。

Q6、数据库事务四大属性

ACID:原子性、一致性、隔离性和持久性

一、原子性(atomicity)

一个事务要么全部提交成功,要么全部失败回滚,不能只执行其中的一部分操作,这就是事务的原子性。

二、一致性(consistency)

事务的执行不能破坏数据库数据的完整性和一致性,一个事务在执行之前和执行之后,数据库都必须处于一致性状态。

如果数据库系统在运行过程中发生故障,有些事务尚未完成就被迫中断,这些未完成的事务对数据库所作的修改有一部分已写入物理数据库,这是数据库就处于一种不正确的状态,也就是不一致的状态。

三、隔离性(isolation)

事务的隔离性是指在并发环境中,并发的事务时相互隔离的,一个事务的执行不能不被其他事务干扰。不同的事务并发操作相同的数据时,每个事务都有各自完成的数据空间,即一个事务内部的操作及使用的数据对其他并发事务时隔离的,并发执行的各个事务之间不能相互干扰。

四、持久性(durability)

一旦事务提交,那么它对数据库中的对应数据的状态的变更就会永久保存到数据库中。--即使发生系统崩溃或机器宕机等故障,只要数据库能够重新启动,那么一定能够将其恢复到事务成功结束的状态。

4、Spring框架

Q1、Spring IOC和AOP

IOC,即控制反转,是通过依赖注入的方式去实现的。

所谓依赖注入,就是把底层类作为参数传入上层类,实现上层类对下层类的“控制”。把有依赖关系的类放到容器中,解析出这些类的实例,就是依赖注入。目的是实现类的解耦。

可参考:ssm——spring整理_金斗潼关的博客-CSDN博客

Q2、Springboot常用注解

@SpringBootApplication:Springboot项目启动类注解

@MapperScan:加在springboot启动类上,编译后会扫描相应接口的实现类(mybatis)

@Component、@Repository、@Service:用于类注解,表示将该类变成一个bean

@Component:服务层接口

@Service:服务层实现类

@Repository:持久层

@Autowired:自动装配。

@Resource:属性注入=@Autowired+@Qualifier

Q3、Springboot启动流程

入口类的要求是最顶层包下面第一个含有 main 方法的类,使用注解 @SpringBootApplication 来启用 Spring Boot 特性,使用 SpringApplication.run 方法来启动 Springboot项目。

 
  1. @SpringBootApplication

  2. public class ErpApplication extends SpringBootServletInitializer {

  3. public static void main(String[] args) {

  4. SpringApplication.run(ErpApplication.class, args);

  5. }

 
  1. public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {

  2. return run(new Class[]{primarySource}, args);

  3. }

  4. public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {

  5. return (new SpringApplication(primarySources)).run(args);

  6. }

SpringApplication类中的run方法是有两个参数,run(a,b)。

第一个参数 primarySource:加载的主要资源类。

第二个参数 args:传递给应用的应用参数。

先用主要资源类来实例化一个 SpringApplication 对象,再调用这个对象的 run 方法。

SpringApplication对象的实例化是用到了一个构造方法。

 
  1. public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {

  2. this.sources = new LinkedHashSet();

  3. this.bannerMode = Mode.CONSOLE;

  4. this.logStartupInfo = true;

  5. this.addCommandLineProperties = true;

  6. this.addConversionService = true;

  7. this.headless = true;

  8. this.registerShutdownHook = true;

  9. this.additionalProfiles = new HashSet();

  10. this.isCustomEnvironment = false;

  11. this.lazyInitialization = false;

  12. // 1、初始化资源加载器为null

  13. this.resourceLoader = resourceLoader;

  14. // 2、断言资源类不能为null,否则会报错

  15. Assert.notNull(primarySources, "PrimarySources must not be null");

  16. // 3、初始化资源类集合,并去重

  17. this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));

  18. // 4、判断当前web应用类型

  19. this.webApplicationType = WebApplicationType.deduceFromClasspath();

  20. // 5、设置应用上下文并初始化

  21. this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));

  22. // 6、设置监听器

  23. this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));

  24. // 7、找到主入口应用类

  25. this.mainApplicationClass = this.deduceMainApplicationClass();

  26. }

 篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

需要全套面试笔记【点击此处即可】免费获取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值