您的位置:首页 > 职场人生

2016年1月15日面试某互联网公司总结(转)

2016-01-18 13:32 162 查看
本人在传统软件公司工作了三年,在大学又学习了一年多了。现在,又面临着再次找工作。之前谁说的“最好的稳定,是上一份工作失去之后,马上就能找到下一份。”曾有一段时间深以为然,可是真的要找个合适的确实不那么容易啊!本人现希望谋得一份Java中级工程师的职位,望大家能推荐一下。

面试的这个公司是同学推荐去的,因此也省了很多事情。主要谈一下技术面试,虽然他们主要开发语言是PHP,但是面试过程中倒是没怎么涉及到PHP的问题。主要还是一些基本的知识,现将主要的内容总结如下:

============================华丽分割线============================================

1.介绍cookie和session的区别,怎么获取与使用?(这个问题比较开放,可深可浅,现在将这里涉及的主要问题总计如下答案)

答:

一、cookie机制和session机制的区别

cookie机制采用的是在客户端保持状态的方式,而session机制采用的是在服务器端保持状态的方式。

同时我们也看到,由于服务器端保持状态的方案在客户端也需要保存一个标识,所以session机制可能需要借助于cookie机制来达到保存标识的目的,但实际上还有其他选择。

二、会话cookie和持久cookie的区别

如果不设置过期时间,则表示这个cookie生命周期为浏览器会话期间,只要关闭浏览器窗口,cookie就消失了。这种生命期为浏览会话期的cookie被称为会话cookie。会话cookie一般不保存在硬盘上而是保存在内存里。

  如果设置了过期时间,浏览器就会把cookie保存到硬盘上,关闭后再次打开浏览器,这些cookie依然有效直到超过设定的过期时间。

  存储在硬盘上的cookie可以在不同的浏览器进程间共享,比如两个IE窗口。而对于保存在内存的cookie,不同的浏览器有不同的处理方式。

三、如何利用实现自动登录

  当用户在某个网站注册后,就会收到一个惟一用户ID的cookie。客户后来重新连接时,这个用户ID会自动返回,服务器对它进行检查,确定它是否为注册用户且选择了自动登录,从而使用户务需给出明确的用户名和密码,就可以访问服务器上的资源。

四、如何根据用户的爱好定制站点

  网站可以使用cookie记录用户的意愿。对于简单的设置,网站可以直接将页面的设置存储在cookie中完成定制。然而对于更复杂的定制,网站只需仅将一个惟一的标识符发送给用户,由服务器端的数据库存储每个标识符对应的页面设置。

五、cookie的发送

  1.创建Cookie对象

  2.设置最大时效

  3.将Cookie放入到HTTP响应报头

  如果你创建了一个cookie,并将他发送到浏览器,默认情况下它是一个会话级别的cookie:存储在浏览器的内存中,用户退出浏览器之后被删除。如果你希望浏览器将该cookie存储在磁盘上,则需要使用maxAge,并给出一个以秒为单位的时间。将最大时效设为0则是命令浏览器删除该cookie。

发送cookie需要使用HttpServletResponse的addCookie方法,将cookie插入到一个Set-Cookie HTTP请求报头中。由于这个方法并不修改任何之前指定的Set-Cookie报头,而是创建新的报头,因此我们将这个方法称为是addCookie,而非setCookie。同样要记住响应报头必须在任何文档内容发送到客户端之前设置。

六、cookie的读取

  1.调用request.getCookie

  要获取有浏览器发送来的cookie,需要调用HttpServletRequest的getCookies方法,这个调用返回Cookie对象的数组,对应由HTTP请求中Cookie报头输入的值。

  2.对数组进行循环,调用每个cookie的getName方法,直到找到感兴趣的cookie为止

   cookie与你的主机(域)相关,而非你的servlet或JSP页面。因而,尽管你的servlet可能只发送了单个cookie,你也可能会得到许多不相关的cookie。

例如:

HttpSession session = request.getSession();

SomeMutableClass value = (SomeMutableClass)session.getAttribute(“someIdentifier”);

if(value = = null){

value = new SomeMutableClass(…);

session.setAttribute(“someIdentifier”,value);

}else{

value.updateInternalAttribute(…);      // 如果已经存在该对象则更新其属性而不需重新设置属性

}


三十、不可更改对象和可更改对象在会话数据更新时的不同处理

  不可更改对象因为一旦创建之后就不能更改,所以每次要修改会话中属性的值的时候,都需要调用setAttribute(“someIdentifier”,newValue)来代替原有的属性的值,否则属性的值不会被更新可更改对象因为其自身一般提供了修改自身属性的方法,所以每次要修改会话中属性的值的时候,只要调用该可更改对象的相关修改自身属性的方法就可以了。这意味着我们就不需要调用setAttribute方法了。

=====================================华丽分割线=============================================

2.二叉树遍历的各种变种问题。(问的是二叉树结点求和)

答:虽然数据结构用C++表述的话比较方便,之前学习的时候多用C++写成。现在,要应聘Java工程师,因此,将这些都由JAVA实现。

二叉树简单介绍:

  二叉树是树形结构的一个重要类型。许多实际问题抽象出来的数据结构往往是二叉树的形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显得特别重要。
二叉树(BinaryTree)是n(n≥0)个结点的有限集,它或者是空集(n=0),或者由一个根结点及两棵互不相交的、分别称作这个根的左子树和右子树的二叉树组成。
这个定义是递归的。由于左、右子树也是二叉树, 因此子树也可为空树。

二叉树的遍历

对于二叉树来讲最主要、最基本的运算是遍历。
遍历二叉树 是指以一定的次序访问二叉树中的每个结点。所谓 访问结点 是指对结点进行各种操作的简称。例如,查询结点数据域的内容,或输出它的值,或找出结点位置,或是执行对结点的其他操作。遍历二叉树的过程实质是把二叉树的结点进行线性排列的过程。假设遍历二叉树时访问结点的操作就是输出结点数据域的值,那么遍历的结果得到一个线性序列。

从二叉树的递归定义可知,一棵非空的二叉树由根结点及左、右子树这三个基本部分组成。因此,在任一给定结点上,可以按某种次序执行三个操作:
 (1)访问结点本身(N),
 (2)遍历该结点的左子树(L),
 (3)遍历该结点的右子树(R)。

具体实现如下:(包括数据的构造,二叉树递归遍历,非递归遍历,以及变种问题的解决方案。也可以重构之后,直接作为库函数使用)

/**
* 二叉树的递归遍历,非递归遍历,已经衍生出来的其他问题。
*/
package com.algorithm.Tree;

import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
/**
* @author Administrator
*/
public class BinaryTree {

private int data[]={1,2,3,5,9,8,5};
private static List<TreeNode> nodeList=null;
/**
* 创建B树
*/
public void createBTree(){
nodeList = new LinkedList<BinaryTree.TreeNode>();

for(int nodeIndex=0;nodeIndex<data.length;nodeIndex++){
nodeList.add(new TreeNode(data[nodeIndex]));
}
//create as the binary tree.
for(int parentIndex=0;parentIndex<data.length/2-1;parentIndex++){
nodeList.get(parentIndex).leftTree=nodeList.get(parentIndex*2+1);
nodeList.get(parentIndex).rightTree=nodeList.get(parentIndex*2+2);
}
int lastParentIndex = data.length / 2 - 1;
// 左孩子
nodeList.get(lastParentIndex).leftTree = nodeList
.get(lastParentIndex * 2 + 1);
if (data.length % 2 == 1) {
nodeList.get(lastParentIndex).rightTree = nodeList
.get(lastParentIndex * 2 + 2);
}
}
/**
* 中序遍历
* @param treeNode
*/
public void inOrderTraverse(TreeNode treeNode) {
if (treeNode == null)
return;
inOrderTraverse(treeNode.leftTree);
visit(treeNode);
inOrderTraverse(treeNode.rightTree);
}
/**
* 递归实现树的求和。
* @param treeNode
* @return
*/
public int sum(TreeNode treeNode){
int sum=0;
if(treeNode == null){
sum=0;
}else{
sum+=treeNode.getData();
sum+=sum(treeNode.leftTree);
sum+=sum(treeNode.rightTree);
}
return sum;
}
/**
* 非递归实现中序遍历
* @param treeNode
*/
public void noRecInOrder(TreeNode treeNode){
Stack<BinaryTree.TreeNode> stack=new Stack<BinaryTree.TreeNode>();
while(treeNode!=null||stack.size()>0){
while(treeNode!=null){
stack.push(treeNode);
treeNode=treeNode.leftTree;
}
if(stack.size()>0){
treeNode=stack.pop();
visit(treeNode);
treeNode=treeNode.rightTree;
}
}
}
public int noRecSum(TreeNode treeNode){
int sum=0;
Stack<BinaryTree.TreeNode> stack=new Stack<BinaryTree.TreeNode>();
while(treeNode!=null||stack.size()>0){
while(treeNode!=null){
stack.push(treeNode);
treeNode=treeNode.leftTree;
}
if(stack.size()>0){
treeNode=stack.pop();
sum+=treeNode.getData();
treeNode=treeNode.rightTree;
}
}
return sum;
}
private void visit(TreeNode treeNode){
treeNode.setVisted(true);
System.out.print(treeNode.getData()+" ");
}

public static void main(String[] args) {
BinaryTree bt=new BinaryTree();
bt.createBTree();
bt.inOrderTraverse(nodeList.get(0));
System.out.println("\n+++++++++++++");
System.out.println(bt.sum(nodeList.get(0)));
System.out.println("\n+++++++++++++");
bt.noRecInOrder(nodeList.get(0));
System.out.println("\n+++++++++++++");
System.out.println(bt.noRecSum(nodeList.get(0)));
}

/**
* 内部内,主要构造为树的节点。
*/
private static class TreeNode{
private TreeNode leftTree;
private TreeNode rightTree;
private int data;
private boolean isVisted=false;//是否已经访问标识
TreeNode(int newData){
this.leftTree=null;
this.rightTree=null;
this.data = newData;
this.setVisted(false);
}
public int getData(){
return data;
}
@SuppressWarnings("unused")
public boolean isVisted() {
return isVisted;
}
public void setVisted(boolean isVisted) {
this.isVisted = isVisted;
}
}

}

http://www.cnblogs.com/accipiter/p/5135823.html
  首先,感谢大家的浏览,还有朋友给推荐工作,由于本人目前在北京,且年后本人在北京还有部分课程需要上,因此现在不便于远行前往深圳,广州等地。还是非常感谢大家!

  今天主要是把之前的工具,做了一些设置与调整,工欲善其事,必先利其器嘛。主要涉及到Eclipse设置maven使用(之前工作都是使用自有构建工具,现在用开源,开源工具功能大而全,但是,往往也坑比较多),git工具的使用(以前工作一直使用SVN,版本管理以后稍作介绍),Mysql安装使用(之前工作一直使用oracle,关于这一部分以后做详加使用说明)。下面还是接昨天的,把面试涉及到的东西写完整。

====================================华丽分割线===========================================

1.索引的使用,联合索引的使用,是否索引越多越好?

答:[以oracle为例]索引好比是一本书前面的目录,能加快数据库表的查询速度。索引分为聚簇索引和非聚簇索引两种,聚簇索引 是按照数据存放的物理位置为顺序的,而非聚簇索引就不一样了;聚簇索引能提高多行检索的速度,而非聚簇索引对于单行的检索很快。

 用的最多也最好的是B-tree索引,它主要是避免了大量数据的排序操作。

  数据库中存储数据的基础单位就是页,数据库的页大小和操作系统类似,是指存放数据时,每一块的大小。比如一个1M的数据存放在数据库中时, 需要大概12块页来存放。如果是在操作系统上安装的数据库,最好将数据库页大小设置为操作系统页大小的倍数,才是最佳设置。数据库可以将数据从逻辑上分成页,磁盘的I/O操作就是在页级执行。

96字节大小的标头,存储统计信息,包括页码、页类型、页的可用空间以及拥有该页的对象的分配单元 ID。数据库页类型主要分如下三类:

数据页,除了大型对象的数据列之外的数据存储页,比如int,float,varchar等。

索引页,存放索引的条目。

大型对象数据类型,比如text,image,nvarchar(max)等。

一个区包含8个页,它是管理空间的单位,分为如下两类

统一区,由单个对象所有。

混合区,最多可由八个对象共享

  知道了区以及页的概念,再看下数据表和这两者之间的联系, 表包含一个或多个分区,每个分区在一个堆或一个聚集索引结构中包含数据行。

  索引中的底层节点称为叶节点。根节点与叶节点之间的任何索引级别统称为中间级。在聚集索引中,叶节点包含基础表的数据页。根节点和中间级节点包含存有索引行的索引页。每个索引行包含一个键值和一个指针,该指针指向 B -树上的某一中间级页或叶级索引中的某个数据行。每级索引中的页均被链接在双向链接列表中。

非聚集索引与聚集索引之间的显著差别在于以下两点:

基础表的数据行不按非聚集键的顺序排序和存储。

非聚集索引的叶层是由索引页而不是由数据页组成。

 索引的优点:正确的索引会大大提高数据查询,对结果进行排序、分组的操作效率。
索引的缺点:
1.创建索引需要额外的磁盘空间,索引最大一般为表大小的1.2倍左右。
2.在表数据修改时,例如增加,删除,更新,都需要维护索引表,这是需要系统开销的。
3.不合理的索引设计非但不能利于系统,反而会使系统性能下降。例如我们在一个创建有非聚集索引的列上做范围查询,此列的索引不会起到任何的优化效果,反而由于数据的修改而需要维护索引表,从而影响了对数据修改的性能。

什么字段不适合创建索引?
1.不经常使用的列,这种索引带来缺点远大于带来的优点。

2.逻辑性的字段,例如性别字段等等,匹配的记录太多,和全表扫描比起来差不多。

3.字段内容特别大的字段,例如大字段等,这会大大增大索引所占用的空间以及索引维护时的速度。

 4.涉及到计算的列,或者是需要利用数据库函数进行加工处理的列不应当创建索引。

联合索引:

1.查询条件中出现联合索引第一列,或者全部,则能利用联合索引.

2.条件列中只要条件相连在一起,无论前后,都会利用上联合索引.

3.查询条件中没有出现联合索引的第一列,而出现联合索引的第二列,或者第三列,都不会利用联合索引查询.

单一列索引:

  1.只要条件列中出现索引列,无论在什么位置,都能利用索引查询.

另外,实际使用过程中,经常遇到索引丢失的情况,这种情况一般需要重建索引,有的涉及到复杂业务查询的语句,需要优化查询,对于具体的SQL,可能由于优化的原因,有不是理想的执行计划。

======================================华丽分割线==========================================

2.实现二路归并排序(面试是要求画出图)

答:二路归并理解起来比较容易,主要是利用分治法。但是,具体代码还是要下一些功夫。

  核心思想:将待排序序列R[0...n-1]看成是n个长度为1的有序序列,将相邻的有序表成对归并,得到n/2个长度为2的有序表;将这些有序序列再次归并,得到n/4个长度为4的有序序列;如此反复进行下去,最后得到一个长度为n的有序序列。
归并排序其实要做两件事:

  1.分解——将序列每次折半划分

  2.合并——将划分后的序列段两两合并后排序

合并的规则:

  在每次合并过程中,都是对两个有序的序列段进行合并,然后排序。这两个有序序列段分别为 arr[low, mid] 和 arr[mid+1, high]。先将他们合并到一个局部的暂存数组tmparr中,待合并完成后再将tmparr复制回R中。这里称 arr[low, mid] 第一段,arr[mid+1, high] 为第二段。每次从两个段中取出一个记录进行关键字的比较,将较小者放入tmparr中。最后将各段中余下的部分直接复制到tmparr中。经过这样的过程,tmparr已经是一个有序的序列,再将其复制回arr中,一次合并排序就完成了。

  注意,若子表个数为奇数,则最后一个子表无须和其他子表归并(即本趟处理轮空):若子表个数为偶数,则要注意到最后一对子表中后一个子表区间的上限为n-1。

/**
* @FileName MergeSort.java
* @Package com.algorithm.sort
* @Description TODO[what the file to do]
* @Author ali blog:http://www.cnblogs.com/accipiter
* @Date 上午1:06:31
* @Version V1.0.1
*/
package com.algorithm.sort;
/**
* @ClassName MergeSort
* @Description TODO
* @Date 下午11:14:51
*/
public class MergeSort {
/**
* @Title Merge
* @Description TODO
* @param arr
* @param low
* @param mid
* @param high
* @Return void
* @Throws
* @user Administrator
*/
public void Merge(int[] arr, int low, int mid, int high) {
int i = low; // i是第一段序列的起始
int j = mid + 1; // j是第二段序列的起始
int k = 0; // k是临时存放合并序列的起始
int[] tmparr = new int[high - low + 1]; // tmparr是临时合并序列
while (i <= mid && j <= high) {
if (arr[i] <= arr[j]) {
tmparr[k++] = arr[i++];
} else {
tmparr[k++] = arr[j++];
}
}
while (i <= mid) {
tmparr[k++] = arr[i++];
}
while (j <= high) {
tmparr[k++] = arr[j++];
}
for (k = 0, i = low; i <= high; i++, k++) {
arr[i] = tmparr[k];
}
}
/**
* @Title MergeStep
* @Description TODO
* @param arr
* @param len
* @param length
* @Return void
* @Throws
* @user Administrator
*/
public void MergeStep(int[] arr, int len, int length) {
int i = 0;
for (i = 0; i + 2 * len - 1 < length; i = i + 2 * len) {
Merge(arr, i, i + len - 1, i + 2 * len - 1);
}
if (i + len - 1 < length) {
Merge(arr, i, i + len - 1, length - 1);
}
}
/**
* @Title sort
* @Description TODO
* @param list
* @return
* @Return int[]
* @Throws
* @user Administrator
*/
public int[] sort(int[] list) {
for (int len = 1; len < list.length; len = 2 * len) {
MergeStep(list, len, list.length);
System.out.print("len = " + len + ":\t");
this.printAll(list);
}
return list;
}
/**
* @Title printAll
* @Description TODO
* @param list
* @Return void
* @Throws
* @user Administrator
*/
public void printAll(int[] list) {
for (int value : list) {
System.out.print(value + "\t");
}
System.out.println();
}

public static void main(String[] args) {
int[] array = { 8, 1, 7, 3, 1, 2, 6, 9, 5};
MergeSort merge = new MergeSort();
System.out.print("排序前:\t\t");
merge.printAll(array);
merge.sort(array);
System.out.print("排序后:\t\t");
merge.printAll(array);
}
}

http://www.cnblogs.com/accipiter/p/5138162.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: