整理的面试问题大合集
1、数据库
1.1、范式
- 首先要明确的是:满足着第三范式,那么就一定满足第二范式、满足着第二范式就一定满足第一范式
- 第一范式:字段是最小的的单元不可再分
- 学生信息组成学生信息表,有年龄、性别、学号等信息组成。这些字段都不可再分,所以它是满足第一范式的
- 第二范式:满足第一范式,表中的字段必须完全依赖于全部主键而非部分主键。
- 其他字段组成的这行记录和主键表示的是同一个东西,而主键是唯一的,它们只需要依赖于主键,也就成了唯一的
- 学号为1024的同学,姓名为Java3y,年龄是22岁。姓名和年龄字段都依赖着学号主键。
- 第三范式:满足第二范式,非主键外的所有字段必须互不依赖
- 就是数据只在一个地方存储,不重复出现在多张表中,可以认为就是消除传递依赖
- 比如,我们大学分了很多系(中文系、英语系、计算机系……),这个系别管理表信息有以下字段组成:系编号,系主任,系简介,系架构。那我们能不能在学生信息表添加系编号,系主任,系简介,系架构字段呢?不行的,因为这样就冗余了,非主键外的
1.2、索引
- 是一种快速查询表中内容的机制,类似于新华字典的目录
- 底层实现是B+和Hash
MySQL 的索引有两种分类方式:逻辑分类和物理分类。 按照逻辑分类,索引可分为:
- 主键索引:一张表只能有一个主键索引,不允许重复、不允许为 NULL;
- 唯一索引:数据列不允许重复,允许为 NULL 值,一张表可有多个唯一索引,但是一个唯一索引只能包含一列,比如身份证号码、卡号等都可以作为唯一索引;
- 普通索引:一张表可以创建多个普通索引,一个普通索引可以包含多个字段,允许数据重复,允许 NULL 值插入;
- 全文索引:让搜索关键词更高效的一种索引。
按照物理分类,索引可分为:
- 聚集索引:一般是表中的主键索引,如果表中没有显示指定主键,则会选择表中的第一个不允许为 NULL 的唯一索引,如果还是没有的话,就采用 Innodb 存储引擎为每行数据内置的 6 字节 ROWID 作为聚集索引。每张表只有一个聚集索引,因为聚集索引的键值的逻辑顺序决定了表中相应行的物理顺序。聚集索引在精确查找和范围查找方面有良好的性能表现(相比于普通索引和全表扫描),聚集索引就显得弥足珍贵,聚集索引选择还是要慎重的(一般不会让没有语义的自增 id 充当聚集索引);
-
非聚集索引:该索引中索引的逻辑顺序与磁盘上行的物理存储顺序不同(非主键的那一列),一个表中可以拥有多个非聚集索引。
-
什么时候【要】创建索引
- (1)表经常进行 SELECT 操作
- (2)表很大(记录超多),记录内容分布范围很广
- (3)列名经常在 WHERE 子句或连接条件中出现
什么时候【不要】创建索引
- (1)表经常进行 INSERT/UPDATE/DELETE 操作
- (2)表很小(记录超少)
- (3)列名不经常作为连接条件或出现在 WHERE 子句中
1.3、连接
内联结
也就是在两张表的相同属性上做的等值连接即自然连接,下面用简明的图例进行说明
上面是我们用到两张表,可以看到在两张表中的相同属性为TNO 即为教师的职工号,那么此时如果我们想要对李诚老师的课程进行查询就涉及到了内联结,内联结的两种写法是:
-- 第一种写法
select cname from table1 inner join table2 on table1.tno=table2.tno;
-- 第二种写法
select cname from table1 and table2where table1.tno=table2.tno;
第一种写法,是标SQL的写法, from 语句中将需要进行等值连接的表 写在 inner join两侧,on 语句表示进行等值连接的字段
第二种写法,是比较常见的写法,但是当将内联结和where语句进行条件选择的时候,可能会分不清哪个是连接字段,那可是作为选择的字段。
外连结
外连接包括 左连接、右连接、全连接 (left|right | full outer join ……on),其中outer可以省略
1.左连接
select student.* ,Score.* from student left join Score on student.id=Score.sid
查询结果如下:
关系如下图
总结:left join 以左表为准,查询出左表的所有数据,右表中有对应的则显示出来,没有对应的则显示为null.
注:A left join B on 与 A,B where 有相同效果,如下:
select student.* ,Score.* from student inner join Score on student.id=Score.sid
select student.* ,Score.* from student,Score where student.id=Score.sid
2.右连接
select student.* ,Score.* from student right join Score on student.id=Score.sid
关系如下图:
总结:right join 以右表为准,查询出右表的所有数据,左表中有对应的则显示出来,没有对应的则显示为null.
3.全连接
select student.* ,Score.* from student full join Score on student.id=Score.sid
总结:full join 是为left和right的集合,某表中某一行在另一表中无匹配行,则相应列的内容为NULL。
交叉连接
select student.* ,Score.* from student cross join Score
叉联接也称作笛卡尔积。相当于两个表中的所有行进行排列组合。
若表a有X行,表b有Y行,则将返回XY行记录。
1.4、事务
- 一个数据库事务通常包含对数据库进行读或写的一个操作序列。
事务四个特性
原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。
一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束。
隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行。
持久性(Durability):一个事务一旦提交,他对数据库的修改应该永久保存在数据库中。
- 事务隔离级别
- 如果不考虑事务的隔离性,会发生的问题
- 脏读:A事务执行过程中,B事务读取了A事务的修改。但是由于某些原因,A事务可能没有完成提交,发生RollBack了操作,则B事务所读取的数据就会是不正确的。
- 不可重复度:B事务读取了两次数据,在这两次的读取过程中A事务修改了数据,B事务的这两次读取出来的数据不一样。
- 不可重复读有一种特殊情况,两个事务更新同一条数据资源,后完成的事务会造成先完成的事务更新丢失。这种情况就是大名鼎鼎的第二类丢失更新
- 幻读:B事务读取了两次数据,在这两次的读取过程中A事务添加了数据,B事务的这两次读取出来的集合不一样。
- 幻读强调的集合的增减,而不是单独一条数据的修改
- 数据库定义了4个隔离级别:
- Serializable 可串行化【可避免脏读,不可重复读,虚读】
- Repeatable read 可重复读【可避免脏读,不可重复读】
- Read committed 读已提交【可避免脏读】
- Read uncommitted 读未提交【级别最低,什么都避免不了】
1.5、视图
视图是一种基于数据表的一种虚表
- (1)视图是一种虚表
- (2)视图建立在已有表的基础上, 视图赖以建立的这些表称为基表
- (3)向视图提供数据内容的语句为 SELECT 语句,可以将视图理解为存储起来的 SELECT 语句
- (4)视图向用户提供基表数据的另一种表现形式
- (5)视图没有存储真正的数据,真正的数据还是存储在基表中
- (6)程序员虽然操作的是视图,但最终视图还会转成操作基表
- (7)一个基表可以有0个或多个视图
我们应该做到:他们想看到什么样的数据,我们就给他们什么样的数据...一方面就能够让他们只关注自己的数据,另一方面,我们也保证数据表一些保密的数据不会泄露出来...
1.6、键
- 主键: 数据库表中对储存数据对象予以唯一和完整标识的数据列或属性的组合。
- 外键:在一个表中存在的另一个表的主键
- 超键:在关系中能唯一标识元组的属性集称为关系模式的超键。超键包含候选键和主键。
- 候选键:是最小超键,即没有冗余元素的超键。
1.7、约束
- NOT NULL: 用于控制字段的内容一定不能为空(NULL)。
- UNIQUE: 控件字段内容不能重复,一个表允许有多个 Unique 约束。
- PRIMARY KEY: 也是用于控件字段内容不能重复,但它在一个表只允许出现一个。
- FOREIGN KEY: 用于预防破坏表之间连接的动作,也能防止非法数据插入外键列,因为它必须是它指向的那个表中的值之一。
- CHECK: 用于控制字段的值范围。
1.8、存储引擎
- Innodb引擎,Innodb引擎提供了对数据库ACID事务的支持。并且还提供了行级锁和外键的约束。它的设计的目标就是处理大数据容量的数据库系统。
- MyIASM引擎(原本Mysql的默认引擎),不提供事务的支持,也不支持行级锁和外键。
- MEMORY引擎:所有的数据都在内存中,数据的处理速度快,但是安全性不高。
varchar和char的区别
Char是一种固定长度的类型,varchar是一种可变长度的类型
1.9、模糊查询
- % :表示任意0个或多个字符。可匹配任意类型和长度的字符,有些情况下若是中文,请使用两个百分号(%%)表示。
- _ : 表示任意单个字符。匹配单个任意字符,它常用来限制表达式的字符长度语句:
- 【】:表示括号内所列字符中的一个(类似正则表达式)。指定一个字符、字符串或范围,要求所匹配对象为它们中的任一个。
- [^ ] :表示不在括号所列之内的单个字符。其取值和 [] 相同,但它要求所匹配对象为指定字符以外的任一个字符。
1.10where groupBy having的区别
执行顺序:
where>groupBy>聚合函数(sum,min,max,avg,count)>having
- having只能用于group by(分组统计语句中)
- where 是用于在初始表中筛选查询,having用于在where和group by 结果分组中查询
- having 子句中的每一个元素也必须出现在select列表中
- having语句可以使用聚合函数,而where不使用。
1.11、SQL优化
- 只返回所需要的数据
- 尽量不写
select*
的语句 - 合理写 where 子句,不要写没有 where 的语句
- 适当建立索引,但一下几点会进行全表扫描
- 左模糊查询
%···
- 使用了不等操作符
- Or 使用不当,or 两边必须都存在索引
- Where 对字段进行表达式操作
- 尽量不写
- 使用 join 代替子查询
- 使用 union 代替手动创建临时表
2、java基础
2.1、字符串问题==与equals
equals检测内容是否相等
==检测地址是否相等
String a = "helloworld";
这句代码在字符串常量池中创建了一个值为helloworld的对象。
String s = new String(“HelloWorld”)
这句代码创建了两个对象,一个在常量池中,一个在堆中
String str1 = "hello";
String str2 = "he" + new String("llo");
System.err.println(str1 == str2);
这题中str1是在字符串常量区创建了hello
str中的new是在堆中,创建的str2也是在堆中
2.2、重写与重载
- 重写:重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
- 重载:重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
2.3、static与final
final表示这个东西是无需改变的常量
- 被final修饰的类不可以被继承
- 被final修饰的方法不可以被重写
- 被final修饰的变量不可以被改变.如果修饰引用,那么表示引用不可变,引用指向的内容可变.
- 被final修饰的方法,JVM会尝试将其内联,以提高运行效率
- 被final修饰的常量,在编译阶段会存入常量池中.
static属性属于这个类所有,即由该类创建的所有对象共享同一个static属性。可以对象创建后通过对象名.属性名和 类名.属性名两种方式来访问。也可以在没有创建任何对象之前通过类名.属性名的方式来访问。
结论:可以重载,不能重写,由于属于同一个类的同一块区域故可以继承
2.4、线程
什么是线程
线程是操作系统能够调度的最小单位,可以理解为一个大任务(进程)中,细分的小任务
QQ,播放器,游戏,Ide等等属于进程
播放器里的声音,弹幕,视频,就是线程
线程的状态
为什么会有线程不安全
多个线程同一时刻对同一个全局变量(同一份资源)做写操作(读操作不会涉及线程安全)时
如何使线程安全
- 同步代码块
- 同步方法
- Lock锁机制
创建线程的方式
扩展Thread类
实现Runnable接口
2.5、集合
Map接口和Collection接口是所有集合框架的父接口:
- Collection接口的子接口包括:Set接口和List接口
- Map接口的实现类主要有:HashMap、TreeMap、Hashtable、ConcurrentHashMap以及Properties等
- Set接口的实现类主要有:HashSet、TreeSet、LinkedHashSet等
- List接口的实现类主要有:ArrayList、LinkedList、Stack以及Vector等
Set集合不能有重复元素,无序,list可以有重复元素,有序
List
唯一吗?
是:Set
排序吗?
是:TreeSet或LinkedHashSet
否:HashSet
如果你知道是Set,但是不知道是哪个Set,就用HashSet。否:List
要安全吗?
是:Vector
否:ArrayList或者LinkedList查询多:ArrayList
增删多:LinkedList
如果你知道是List,但是不知道是哪个List,就用ArrayList。
SET
HashSet
底层数据结构是哈希表。
如何保证元素唯一性的呢?
依赖两个方法:hashCode()和equals()
开发中自动生成这两个方法即可即重写LinkedHashSet
底层数据结构是链表和哈希表
由链表保证元素有序
由哈希表保证元素唯一TreeSet
底层数据结构是红黑树。
如何保证元素排序的呢?
自然排序,实现Comparable接口,并且重写compreTo方法。
比较器排序 自定义比较,实现Comparator接口,重写compare方法。
如何保证元素唯一性的呢?
根据比较的返回值是否是0来决定
Map
- HashMap,Map不能包含相同的键,每个键只能映射一个值。键还决定了储存对象在映射中的储存位置。
- 单一,无序
- 线程不安全
- HashTable
- 线程安全
- LinkedHashMap
- 用法跟HashMap基本一致,它是基于链表和哈希表结构的所以具有存取有序,键不重复的特性,存的顺序和遍历出来的顺序是一致的。
- 单一、有序
- TreeMap
- TreeMap与TreeSet类似,都需要重写比较器(外部比较器+内部比较器)
- 单一,有序
2.6、JVM
JVM内存区域
程序计数器
Java虚拟机栈
Java堆
方法区
运行时常量池
类加载
- 加载:
- 加载是类加载过程中的一个阶段,这个阶段会在内存中生成一个代表这个类的java.lang.Class对 象,作为方法区这个类的各种数据的入口。
- 注意这里不一定非得要从一个Class文件获取,这里既 可以从ZIP包中读取(比如从jar包和war包中读取),也可以在运行时计算生成(动态代理), 也可以由其它文件生成(比如将JSP文件转换成对应的Class类)。
- java方法区生成全部静态数据,并在堆中生成一个Class类的对象,代表这是一个类
- 具体解释可看中 第二点【运行过程】
- 验证:
- 这一阶段的主要目的是为了确保Class文件的字节流中包含的信息是否符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
- 准备:
- 准备阶段是正式为类变量(static)分配内存并设置类变量的默认初始值阶段(也就是0,而不是我们给的值,为其赋值是在类初始化【client】方法中),即在方法区中分配这些变量所使用的内存空间
- 解析:
- 解析阶段是指虚拟机将常量池中的符号引用替换为直接引用的过程。符号引用就是class文件中 的: 1. CONSTANT_Class_info 2. CONSTANT_Field_info 3. CONSTANT_Method_info 等类型的常量。
- 符号引用与虚拟机实现的布局无关,引用的目标并不一定要已经加载到内存中。各种虚拟 机实现的内存布局可以各不相同,但是它们能接受的符号引用必须是一致的,因为符号引 用的字面量形式明确定义在Java虚拟机规范的Class文件格式中。
- 直接引用可以是指向目标的指针,相对偏移量或是一个能间接定位到目标的句柄。如果有 了直接引用,那引用的目标必定已经在内存中存在。
- 初始化:
- 初始化阶段是类加载后一个阶段,前面的类加载阶段之后,除了在加载阶段可以自定义类加载 器以外,其它操作都由JVM主导。到了初始阶段,才开始真正执行类中定义的Java程序代码
- 初始化阶段是执行类构造器方法的过程
- 方法是由编译器自动收集类中的类变 量的赋值操作和静态语句块中的语句合并而成的
- 如果一个类中没有对静态变量赋值也没有静态语句块,那么编译 器可以不为这个类生成()方法
类主动使用的六种情况 |
---|
创建类的实例,也就是new的方式 |
访问某个类或接口的静态变量,或者对该静态变量赋值(凡是被final修饰不不不其实更准确的说是在编译器把结果放入常量池的静态字段除外) |
调用类的静态方法 |
反射(如 Class.forName(“com.gx.yichun”)) |
初始化某个类的子类,则其父类也会被初始化 |
Java虚拟机启动时被标明为启动类的类( JavaTest ),还有就是Main方法的类会首先被初始化 |
2.7、GC垃圾回收
两种思想
- 分代算法
将物理空间划分为:新生代、老年代、永久代(元空间)
-
分区算法
G1算法将堆划分为若干个区域(Region),它仍然属于分代收集器。不过,这些区域的一部分包含新生代,新生代的垃圾收集依然采用暂停所有应用线程的方式,将存活对象拷贝到老年代或者Survivor空间。
算法
-
复制算法
- Eden区和FromSurvivor区在上一轮GC后还存活的对象苟延残喘到TO Survivor区中(如果有对象的年龄达到了老年的标准,则赋值到老年代区)同时把这些对象的年龄+1(如果 ServicorTo 不 够位置了就放到老年区)
- 清空Eden,From Survivor区
- 将From和To区互换(经受新的一轮制裁吧哈哈哈哈哈哈)
- 标记清除
- 标记阶段:首先通过根节点,标记所有从根节点开始的可达对象。未被标记的对象就是未被引用的垃圾对象
- 清除阶段:清除所有未被标记的对象。
- 标记整理
- 标记阶段:先通过根节点,标记所有从根节点开始的可达对象,未被标记的为垃圾对象
-
整理阶段:将所有的存活对象压缩到内存的一段,之后清理边界外所有的空间
最起码咱整理了不是
GC过程
-
新生代有一个Eden区和两个survivor区
- 首先将对象放入Eden区
- 如果空间不足就向其中的一个survivor区上放
- 如果仍然放不下就会引发一次发生在新生代的minor GC
- 将存活的对象放入另一个survivor区中
- 然后清空Eden和之前的那个survivor区的内存
- 在某次GC过程中,如果发现仍然又放不下的对象,就将这些对象放入老年代内存里去。
- 大对象以及长期存活的对象直接进入老年区。
-
当每次执行minor GC的时候应该对要晋升到老年代的对象进行分析,如果这些马上要到老年区的老年对象的大小超过了老年区的剩余大小,那么执行一次Full GC以尽可能地获得老年区的空间。
2.8、基本数据类型
Java语言提供了八种基本类型:
- 六种数字类型(四个整数型,两个浮点型)
字节型byte 8位 | 短整型short 16位 | 整型int 32位 |
---|---|---|
长整型long 64位 | 单精度float 32位 | 双精度double 64位 |
- 一种字符类型:字符型char 8位
- 还有一种布尔型:布尔型:boolean 8位 可存储"True"和"false"。
- String本身就是一个对象而不是基本数据类型,String的变量名是对String类的引用。
2.9、抽象类与接口
- 接口的意义用三个词就可以概括:规范,扩展,回调.
-
接口默认是public,不能使用其他修饰符
接口重写方法时访问权限问题
子类重写父类方法时,方法的访问权限不能小于原访问权限,在接口中,方法的默认权限就是public,所以子类重写后只能是public
abstract为什么不可以用static final或private 修饰
- private :因为一个abstract方法需要被重写,所以不能修饰为private;
- final:因为一个abstract方法需要被重写。被final修饰的方法是不能被重写的,所以不能同final共存;
- static:因为一个abstract方法没有方法体。静态方法需要对方法体执行内容分配空间,所以不能同static共存;
抽象类的意义
- 为其他子类提供一个公共的类型
- 封装子类中重复定义的内容
- 定义抽象方法,子类虽然有不同的实现,但是定义时一致的
2.10、三大特性
- 封装
-
继承
-
多态
继承,重写,上转型
例子:在不同页面下按下f1键会有不同的操作。
2.11、异常
抛出异常:throw,throws
捕获异常:try,catch,finally
2.12、反射
是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;
3、算法
3.1、冒泡
重复地走访过要排序的数列,一次比较两个元素
3.2、插入排序
将数组中的所有元素依次跟前面已经排好的元素相比较,如果选择的元素比已排序的元素小,则交换
3.3、选择排序
在未排序序列中找到最小(大)元素,存放到未排序序列的起始位置
3.4、快速排序
①. 从数列中挑出一个元素,称为”基准”(pivot)。
②. 重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(相同的数可以到任一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
③. 递归地(recursively)把小于基准值元素的子数列和大于基准值元素的子数列排序。
3.5、归并
将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。
4、WEB
项目
超市管理系统,无框架,前端使用JS,Html,CSS,JSON,后端使用WebSerrvlet,数据库使用JDBC对接Mysql
遇到的问题
因为是第一次部署这种比较大的项目,所以遇到了许多可能比较蠢得问题,比如一开始
- 关于驱动的导入Class.forname,了解了jvm类加载机制
- 登陆功能中理解了传递的参数为什么要传递,而不是新建
- basedao是不做任何新东西的创建的,因为要把创建的东西关闭,以此类推:最底层的东西是不会新建的,都是传过来的参数,在传过来的的地方做关闭处理
- 因为要考虑资源释放,baseDao的closeResource方法需要关闭开着的公共资源,比如connection,resultSet等,而且只关闭当前调用的资源,如果在basedao的getconnection现创建,就必须在本方法内关闭资源
- xml与jsp中跳转页面不一致,导致的失败问题
- 动态sql问题
- basedao
- 静态代码块
- properties读取driver等信息
- 获取数据库连接:Class.forname
- 公共查询方法:连接,结果集,预指令,sql,参数
- 公共增删改方法:无结果集
- 资源关闭方法:关闭reslult,预指令,连接
- limit -1问题
- junit不能进行测试问题:scope配置了Junit可用的位置,test表示只能在src下的test文件夹下面才可以使用
- 创建普通MAVEN项目发布空指针异常问题:因为使用了普通maven项目,缺少了插件
- 关于使用@WebServlet(“/xxx”)后出现404的问题:在Servlet3.X以上的web.xml中可以设置metadata-complete属性,如果设置metadata-complete="true",会在启动时不扫描注解(annotation)
- 前端页面下拉框数据回显问题
- 权限选择下拉框的地方,其中在option标签里做了一个role.id == queryUserRole的判断
- 在选择好权限后显示的页面如果有分页,在点击下一页后就会改变
- 权限又回到了默认状态,相当于查看的是全体权限
实现的功能
- 登陆,退出
- 登陆:从前端得到用户名密码,之后将用户名传到dao,从数据库取到userCode,遍历查询得到用户,返回具体的User给service层,service做密码判断,如果正确,返回User给Servlet,serVlet跳转页面
- 退出:退出只需要移除session中的用户,不需要操作数据库也就不同写dao和service,只需要在servlet中进行就好
- 密码修改
- 需要前端得到登陆时存储的User,通过user中的新密码,id传递给dao,来对数据库做修改
- 验证新旧密码一致:在jsp页面插入js文件,当焦点在旧密码上时,从servlet接收一个方法传来的值,js根据这个值来进行不同的前端显示
- session失效了
- session没失效
- 输入的旧密码为空
- 输入的旧密码不为空
- 密码与session用户里密码不相等
- 密码与session用户里密码相等
- 用户增删改查
- 获取用户数量【动态拼接sql】
- 这里用到了很原始的动态拼接sql,用的是StringBuffer,append方法,如果前端选择了用户权限(比如经理,员工这种),就会在这加一个if判断,然后拼接role.id,如果是填写了姓名,这里就拼接姓名,同时把参数加入list,并转换为param参数数组传给basedao
- 获取用户列表【分页】
- 这里使用了limit来分页,根据当前页号和每页显示的数量计算出应该显示的号码,用;limit来实现分页
前后端如何交互
Session
5、自我介绍
按照岗位叙述
Comments | NOTHING