面试ing,为了查漏补缺,整理一下面试的问题,以便疏通思路和以后的面试,也为了纪下自己的生活o(╯□╰)o
前天接到天猫的电话,我本来以为我不紧张的,上次新美大给我打电话,我就好淡定的。没想到,这次会这么紧张,说话都颤抖……
面试官让我简单的进行下自我介绍,接着让我聊了下我以前的项目(感觉说的很low,都没怎么复习以前的项目,就瞎扯)。接着是让问了一下基础问题。具体如下:
大二时候学习的 Servlet,这两个函数都使用过,可惜现在都忘了。重温下这个知识点。
2.1.1 Servlet 基本知识
2.1.2 redirect 和 forward
[实现代码]
redirect 的使用方式:
response.sendRedirect(path);
forward的使用方式:
getRequestDispathcer(path).forward(request, response);
[url变化]
使用重定向:浏览器地址变成了 path 的 url 地址—->它相当于开启了一个新页面,类似与重新在客户端输入目标页面地址,重新请求。原因:这种情况是服务器发送了一个状态码(302)告诉客户端浏览器重写去请求目标 URL,客户端就会重新发起一个HTTP请求,请求地址就是目标地址 path,因此地址栏会改变。
使用跳转:浏览器地址不会变成 path 的 url 地址—->相当于将目标内容输出到原页面。原因:这种情况是服务器内部请求资源,服务器将直接访问目标 URL,得到目标 URL 的响应后,将其响应内容读取,再直接返回给浏览器。由于是服务器内部进行请求,因此浏览器并不明白这些响应内容是来自另一个 URL 的,所以它的地址还是原来的地址。
[参数传递或数据共享]
使用重定向:得到目标页面后,request 中的参数、属性值都将丢失
使用跳转: 由于它是在服务器内部请求,因此中间传递的是Servlet 容器内的 request,请求完毕后再将目标页面的内容输出到浏览器,因此不会丢失 request 中的信息
[运行效率]
redirect:高效
forward:效率较低
[使用建议]
① 若在A页面有一些内容要在B页面显示,但内容数量较大,且是暂时数据,那么不要将内容放在 Session 中,而是放在 request 范围内,用 forward 的方式跳转到 B 页面进行显示—>以节省内存
② 如果要跳转到服务器以外的url,则应该也必须使用 redirect。如跳转到百度—>防止信息泄漏
③ 用户登录后,需要根据角色转到相应的模块时,应该用 forward
④ 用户注销后,需要返回到主界面时,应该用 redirect
[小结]
重定向是客户端的行为,是客户端两次发送 http 请求;转发是服务器的行为,客户端发送一次http请求,服务器内部在进行一次http请求
2.2.1 如何实现分页?
使用 limit,limit 的语法:
① xxxx limit [offset] n;
② xxxx limit n offset m;
注:offset 是偏移量,默认为 0
实例代码:
select * from user limit 0, 3; // 返回符合条件的前 3 条记录
select * from user limit 3; // 同上
select * from user limit 3 offset 1; // 返回3 条记录,因为偏移量是 1,因此返回的记录是 1~4
select * from user limit 1, 3; // 同上
2.2.2 limit 10000,10 中,分页数据是如何返回的,会不会从前面读入然后再返回后10条记录?
这个问题的回答,就必须得知道 mysql 的分页原理。 当时问到这个的时候,我就不知道,我以为 mysql 应该会有优化,所以回答的是“应该会直接返回后 10 条记录吧”,结果现在我知道我错了…….
以 select * from user order by id desc limit 10000,10 为例, 按 mysql 分页的情况来说,这条sql语句其实做的是这样的一件事情:扫描满足条件的 10010 条记录,然后扔掉前面的 10000 条记录,再返回最后的 10 条记录。
既然知道了 limit 的分页流程,那么我们就明白了,offset 越高,效果就越低。因此使用 mysql 的分页时,需要优化效率。即:尽量减小 offset 的值。
优化原理:记录住当前页 id 的最大值和最小值,计算跳转页面和当前页相对偏移。尽量减小 offset 的值 根据上面所述,因此我们若是要获取 10000 后面的 10 条记录,优化后的语句应该是这样的。如下:(2种效果都一样)
select * from user where id > 10000 order by id asc limit 0, 10;
select * from `user` where id > 10000 order by id asc limit 10 offset 0;
这样,就会过滤掉前面的 10000 行记录,获取 10000 后的 10 条记录。而不是扫描符合条件的所有记录,即:获取10010条记录后,丢掉前 10000 行,在返回后 10 条记录。
2.2.3 说一下 Statement 和 PrepareStatement 的区别
问这个的时候,我还是记得的,这个问题貌似是最首先问的。我当时是说:使用 Statement 发送的 SQL 不能带参数,而 PrepareStatement 可以带参数。然后 Boss 又说 PrepareStatement 带参数有什么好处?我就说可以防止 SQL 注入。接着 Boss 就要我介绍什么叫 SQL 注入?我又是噼里啪啦的把自己知道的说了下,感觉还是没说好.
Q1:Statement 和 PrepareStatement 的区别
A1:
[用法]
Statement stmt = conn.CreateStatement();
PrepareStatement ptmt = conn.PreparedStatement(sql);
[携带参数与安全]
Statement用于执行静态sql语句,不能携带参数。不安全,可以进行 SQL 注入攻击
PrepareStatement是预编译的sql语句对象,sql语句被预编译并保存在对象中。其使用的 SQL 中可以使用占位符来动态设定参数值。安全,可以防止 SQL 注入
[性能]
PrepareStatement可以减少编译次数提高性能:PrepareStatement对象执行sql时,sql被数据库进行解析和编译,然后被放到命令缓冲区,每当执行同一个PrepareStatement对象时,它就会被解析一次,但不会被再次编译。在缓冲区可以发现预编译的命令,并且可以重用。
[代码可读性和维护性]
使用 PrepareStatement 操作 DB 的代码可读性更好,更易于维护
Q2:什么是 SQL 注入?
A2:通过构建特殊的输入作为参数传入Web应用程序,最终达到欺骗服务器执行恶意的SQL命令。举例:若是 sql = “select * from user where name= ‘”+varname+”’ and passwd=’”+varpasswd+”’”;那么若是攻击者将用户名 123 和 ’ or ‘1’ = ‘1 作为密码传入,即sql = “select * from user where name= ‘123’ and passwd=” or ‘1’ = ‘1’”,那么就成功实现了系统入侵。因为 ‘1’ = ‘1’ 是绝对成立的。这也是为什么要使用 PrepareStatement 而不是 Statement 来执行 SQL 语句的原因。
2.3.1 简介
又名本地线程变量和线程本地存储
多线程环境下,对于共享资源 x,若使用 ThreadLocal,它会为每个 Thread 创建一个 x 的副本,因此每个线程都可以利用 ThreadLocal 提供的 get()、set()等方法来独立操作该副本变量,而不影响其他线程所对应的副本—>从线程的角度来看,它就像是线程的本地变量
2.3.2 类方法
ThreadLocal 只有 4 个方法
1. get(): 获得当前线程所对应的线程局部变量的值。
2. set(T value): 设置当前线程的线程局部变量的值
3. remove(): 删除当前线程中线程局部变量的值
4. initialValue():返回此线程局部变量的当前线程的“初始值”。
2.3.3 ThreadLocal是如何为每个线程创建变量的副本的?
2.3.4 应用场景
最常见的ThreadLocal使用场景是用来解决 数据库连接、Session管理等
2.3.5 参考文献
http://www.cnblogs.com/dolphin0520/p/3920407.html
2.3.6 我的情况
当时问到这个的时候,我虽然听过 ThreadLocal,但因为一般情况没用到过,就没去了解。所以我就直接跟Boss说我现在正在看《Java编程思想》,刚刚看到并发这部分。然后Boss就问我看到哪了?我就说看到线程池了。接着Boss自然的就不问我ThreadLocal,而改为问我线程池。
Q1:你简单介绍下线程池
Q2:为什么要用线程池?
这两个问题,感觉我都没回答好。因此现在好好整理下
A1:线程池顾名思义就是事先创建若干个可执行的线程放入一个池(容器)中,需要的时候从池中获取线程不用自行创建,使用完毕不需要销毁线程而是放回池中,从而减少创建和销毁线程对象的开销。在Java5以后,提供了一个Executor(执行器)接口,可以利用它来创建管理线程。相对于以前传统的线程创建方式来说,利用执行器来创建管理线程的生命周期,是最为理想的。Executor有一个子类型即 ExecutorService,我们一般就是利用它来创建管理线程的。线程池有好几种,比如:CachedThreadPool、FixedThreadPool、SingleThreadExecutor。首选的是 CachedThreadPool,其次才是 FixedThreadPool。
A2:在OOP中,创建和销毁对象是一个很耗时耗费资源的操作。在创建一个对象时,JVM会经历一系列的操作(类加载检查—>分配内存—>对象初始化—>设置对象信息)。因此为了提高程序效率,我们就要尽可能减少创建和销毁对象的次数,特别是一些很耗资源的对象创建和销毁,这就是”池化资源”技术产生的原因,也就是使用线程池的原因。
刚问道这个问题时,我是蒙逼的,憋了半天说了逐行读取,然后使用冒泡排序。但是这并不是个好方案…可惜我当时也就只能想出这么个办法→_→
问题分析
文本太大,若是一次直接读文件到内存,系统资源耗费太大—->因此,一次读入的想法肯定是不现实的。现在是 1G 要是更大怎么办?因此对大文件排序的问题,主要是要考虑内存消耗的问题,绝对不能造成内存溢出。因此对于大文件的排序问题,我们可以考虑外排序。
外排序:是指能够处理极大量数据的排序算法。通常来说,外排序处理的数据不能一次装入内存,只能放在读写较慢的外存储器(通常是硬盘)上。外排序通常采用的是一种“排序-归并”的策略。在排序阶段,先读入能放在内存中的数据量,将其排序输出到一个临时文件,依此进行,将待排序数据组织为多个有序的临时文件。尔后在归并阶段将这些临时文件组合为一个大的有序文件,也即排序结果。常见的有外归并排序。
[注:① 概念来自百度百科 ② 外归并排序 != 内排序中的归并排序,二者是有区别的。外归并排序也称为多路归并排序、K路归并排序。内排序中的归并排序仅仅是是二路归并]
故而,我们考虑:将文件切割,分为多个小文件,然后排序,在归并成一个文件—>即使用外归并排序实现
昨天是网易互联的在线笔试。在收到网易给我的在线笔试通知时,我是很诧异的,因为我之前还以为我在简历一关已经被 pass 了,没想到还是过了。一直以为在线笔试的话,遇到不懂的可以去百度,可以问同学。没想到原来在线笔试,会有视频全程监考,而且不能跳出浏览器中的考试系统窗口,而且还不能打开下载工具和通讯工具,全程靠自己。
笔试的时间是从晚上7点考到9点,一共2个小时。题目不多,总题数也就25个,分别是 10 个单选,10 个多选,5 个主观题。我申请的是 Java 研发方向的职位,本以为都是考察 Java 方面的知识,比如并发啊、HashMap 的原理啊、HashMap 和 HashTable 它们的区别啊、Spring框架的知识点啊、Ioc 和 DI 是什么啊,结果一看题目就蒙逼了。基本上都是基础题目,Java题目不多,就几个,其他的都是什么操作系统(主要是Linux)、计算机网络、设计模式这类的题目,感觉就是在考研时学硕的 408 知识。蛋疼,反正我绝逼是挂了,白准备了…又得买书看书了…⊙﹏⊙!
我还记得的一些题目如下:
设计模式
第一个是考查设计模式,题目的具体描述我就不记得了,总之是说什么不同的用户有不同的级别,需要对某一东西进行访问,但这个东西需要对客户端透明,问可以使用什么模式?然后给出四个选项,分别是 A. 桥梁模式 B. 门面模式 C. 忘了 D. 适配器模式
[觉得《设计模式之禅》白看了,除了记得一些简单的模式的应用场景,其他的都不记得了。得重新看看了]
考察 JVM
这个题目是给出了一系列 JVM 的参数,-Xms1G, -Xmx2G, -Xmn500M, -XX:SurvivorRatio=3 貌似还有有个参数,根据给出的参数,求 Eden 区的大小
考察类的初始化顺序
给出一段程序,有 A 类 和 B 类,B 继承 A 类,然后 main() 方法的代码是:
public static void main(String[] args) {
System.out.println(7);
A a = new B();
}
然后选择正确的输出。
考察排序算法
在最坏的情况下,下列排序方法中时间复杂度最小的是()
A.冒泡排序
B.快速排序
C.插入排序
D.堆排序
考察 Linux中对文件进行授权,也就是 chmod 命令
给出一个文件 aaa,给出它当前的权限,貌似是 rwx rw r, 然后给出四个选项:
A. 貌似是给所有用户赋予可执行权限,给同一用户组赋予写权限的命令是不是 chmod a+x g+w aaa
B. 同上,就是变成了 chmod 764 aaa
就大概记得2个选项的情况了
考 ls -l 命令
给出 ls -l 命令的执行结果,根据结果选答案。比如该文件是不是目录?文件的拥有者是谁?有什么权限?……
考网络层中有哪些协议?
下面是网络层中的协议有:
A. HTTP
B. IP
C. ICMP
D. IPX
反正我是瞎选的,根本就不知道。而且我还以为 HTTP 是的,没想到 HTTP 是应用层的协议。
注:
① 网络层的协议:IP协议,ARP协议,RARP协议,ICMP协议
② 应用层的协议:
超文本传输协议HTTP
文件传送协议FTP
远程登录TELNET
简单邮件传送协议SMTP
DNS域名解析协议
简单文件传送协议TFTP
简单网络管理协议(SNMP)
DHCP动态主机配置协议
考察 Java 中的并发知识
考 sleep() 和 wait() 的区别,给出几个选项,选择不正确的。
第一二题是算法题:
[第一题] 对书单进行排序输出
题目大意:XXX图书馆有人借书,最大借书上限为 n,同时有 m 人借书。目前提交了一批书单,每行代表一个书单,书号之间以空格分隔,输入 n 时表示结束。要求:
1. 若书单之间没有交集,则按提交次数排序
2. 若书单之间有交集,则由交集的书单尽量靠在一起,按提交次序排序
[第二题] 判断字符串的有效性—>考察栈的运用
题目大意:字符串中只能包含 [] {} () 这几个字符,且必须成对存在。比如 “[()]” 这就是一个有效的字符串,若是 “[)” 那这就是无效的字符串。要求:对给出的字符串,判断其是否有效
这2道题,都有编辑器,可以选择你自己熟悉的语言来实现,比如 C、Java、Python,我选的是 Java。第一题写了一部分,没做出来,第二题做出来了。但是很坑,因为给出的 IDE 不能使用代码提示,只能全程手工,而且必须记得自己需要的所有的方法的使用,比如 String 类的 toCharArray() 这些都得自己写,完全木有代码提示。import 语句也得自己写,写代码的时候,真是满满的心塞,感觉就是在用记事本敲代码…→_→
第三题考递归:
这题记得最清楚,题目如下:
public static int calculate (int x) {
if (x <= 2) {
return 1;
} else {
return (calculate(x - 1) + calculate(x - 1)) * 2;
}
}
问:
1. 当 x = 5 时,输出结果是多少?
2. 求算法的时间复杂度,用大写的 O 表示
3. 如何降低该算法的时间复杂度?
x = 5 时的输出,我直接在编辑器器一步步推算的(宝宝不敢用草稿纸,怕被认为是作弊),结果是 64
时间复杂度我是最不会算的,我是写了 O(n²)
降低时间复杂度的策略,我给出的是以空间换取时间的策略,直接一次性从 x = 2 按递归表达式算出 x = n 的所有值,用数组存储,要知道 calculate(x) 时,直接到数组中取就行了
第四题考Linux
问:自旋锁和互斥锁是什么?有什么区别?
感觉 Linux 的题目好多,然而我都不会…这个题根本就不清楚,没做 o(>_<)o ~~
第五题考Java知识
问:是否可以直接修改 Set 中对象的值?为什么?
我感觉我改错了,第一次答时,我人为是不可以,因为 Set 集合是无序的,不可重复的,要修改其内某对象的值时,只能在外部修改该对象,然后添加进去,利用不可重复性覆盖掉旧值。
但后面我想起了 Java 中对象的“别名现象”,我又给改为可以,然后根据“别名现象”噼里啪啦的一阵扯…
网易的在线笔试,我记得的就差不多是这些了,若是有朋友有补充,可以跟我说下…