高级Java开发面试常用题的答案1

一、数据结构与算法基础
· 说一下几种常见的排序算法和分别的复杂度。

· 用Java写一个冒泡排序算法

/*
现在有一个包含1000个数的数组,仅前面100个无序,后面900个都已排好序且都大于前面100个数字,那么在第一趟遍历后,最后发生交换的位置必定小于100,且这个位置之后的数据必定已经有序了,也就是这个位置以后的数据不需要再排序了,于是记录下这位置,第二次只要从数组头部遍历到这个位置就可以了。如果是对于上面的冒泡排序算法2来说,虽然也只排序100次,但是前面的100次排序每次都要对后面的900个数据进行比较,而对于现在的排序算法3,只需要有一次比较后面的900个数据,之后就会设置尾边界,保证后面的900个数据不再被排序。
/
public static void bubbleSort(int [] a, int n){
int j , k;
int flag = n ;//flag来记录最后交换的位置,也就是排序的尾边界
while (flag > 0){//排序未结束标志
k = flag; //k 来记录遍历的尾边界
flag = 0;
for(j=1; j<k; j++){
if(a[j-1] > a[j]){//前面的数字大于后面的数字就交换
//交换a[j-1]和a[j]
int temp;
temp = a[j-1];
a[j-1] = a[j];
a[j]=temp;
//表示交换过数据;
flag = j;//记录最新的尾边界.
}
}
}
}
· 描述一下链式存储结构。

它不要求逻辑上相邻的元素在物理位置上也相邻。因此它没有顺序存储结构所具有的弱点,同时也失去了顺序表可随机存取的优点。

其特点主要表现为:

1、比顺序存储结构的存储密度小;

2、插入、删除灵活,结点可以被插入到链表的任何位置,首、中、末都可以,而且不必要移动结点中的指针;

3、链表的大小可以按需伸缩,是一种动态存储结构,其实现的集合在增、删方面性能更高;

4、查找结点时的效率就相对数组较低,只能从第一个结点开始顺着链表逐个查找(这是他的缺点)。

· 如何遍历一棵二叉树?

二叉树的遍历分为三种:

前序遍历:按照“根左右”,先遍历根节点,再遍历左子树 ,再遍历右子树

中序遍历:按照“左根右“,先遍历左子树,再遍历根节点,最后遍历右子树

后续遍历:按照“左右根”,先遍历左子树,再遍历右子树,最后遍历根节点

其中前,后,中指的是每次遍历时候的根节点被遍历的顺序

package com.tree;
import java.util.ArrayList;
import java.util.List;
public class Tree {
private Node root;
private List<Node> list=new ArrayList<Node>();
public Tree(){
init();
}
//树的初始化:先从叶节点开始,由叶到根
public void init(){
Node x=new Node("X",null,null);
Node y=new Node("Y",null,null);
Node d=new Node("d",x,y);
Node e=new Node("e",null,null);
Node f=new Node("f",null,null);
Node c=new Node("c",e,f);
Node b=new Node("b",d,null);
Node a=new Node("a",b,c);
root =a;
}
//定义节点类:
private class Node{
private String data;
private Node lchid;//定义指向左子树的指针
private Node rchild;//定义指向右子树的指针
public Node(String data,Node lchild,Node rchild){
this.data=data;
this.lchid=lchild;
this.rchild=rchild;
}
}
/**

  • 对该二叉树进行前序遍历 结果存储到list中 前序遍历:ABDXYCEF
    */
    public void preOrder(Node node)
    {
    list.add(node); //先将根节点存入list
    //如果左子树不为空继续往左找,在递归调用方法的时候一直会将子树的根存入list,这就做到了先遍历根节点
    if(node.lchid != null)
    {
    preOrder(node.lchid);
    }
    //无论走到哪一层,只要当前节点左子树为空,那么就可以在右子树上遍历,保证了根左右的遍历顺序
    if(node.rchild != null)
    {
    preOrder(node.rchild);
    }
    }
    /**
  • 对该二叉树进行中序遍历 结果存储到list中
    */
    public void inOrder(Node node)
    {
    if(node.lchid!=null){
    inOrder(node.lchid);
    }
    list.add(node);
    if(node.rchild!=null){
    inOrder(node.rchild);
    }
    }
    /**
  • 对该二叉树进行后序遍历 结果存储到list中
    */
    public void postOrder(Node node)
    {
    if(node.lchid!=null){
    postOrder(node.lchid);
    }
    if(node.rchild!=null){
    postOrder(node.rchild);
    }
    list.add(node);
    }
    /**
  • 返回当前数的深度
  • 说明:
  • 1、如果一棵树只有一个结点,它的深度为1。
  • 2、如果根结点只有左子树而没有右子树,那么树的深度是其左子树的深度加1;
  • 3、如果根结点只有右子树而没有左子树,那么树的深度应该是其右子树的深度加1;
  • 4、如果既有右子树又有左子树,那该树的深度就是其左、右子树深度的较大值再加1。
  • @return
    */
    public int getTreeDepth(Node node) {
    if(node.lchid == null && node.rchild == null)
    {
    return 1;
    }
    int left=0,right = 0;
    if(node.lchid!=null)
    {
    left = getTreeDepth(node.lchid);
    }
    if(node.rchild!=null)
    {
    right = getTreeDepth(node.rchild);
    }
    return left>right?left+1:right+1;
    }
    //得到遍历结果
    public List<Node> getResult()
    {
    return list;
    }
    public static void main(String[] args) {
    Tree tree=new Tree();
    System.out.println("根节点是:"+tree.root);
    //tree.preOrder(tree.root);
    tree.postOrder(tree.root);
    for(Node node:tree.getResult()){
    System.out.println(node.data);
    }
    System.out.println("树的深度是"+tree.getTreeDepth(tree.root));
    }
    }
    二叉树与一般树的区别

一般树的子树不分次序,而二叉树的子树有左右之分.
由于二叉树也是树的一种,所以大部分的树的概念,对二叉树也适用.
二叉树的存贮:每个节点只需要两个指针域(左节点,右节点),有的为了操作方便也会 增加指向父级节点的指针,除了指针域以外,还会有一个数据域用来保存当前节点的信息
二叉树的特点:

性质1:在二叉树的第i层上至多有2^(i-1)个节点(i >= 1)
性质2:深度为k的二叉树至多有2^k-1个节点(k >=1)
性质3:对于任意一棵二叉树T而言,其叶子节点数目为N0,度为2的节点数目为N2,则有N0 = N2 + 1。
性质4:具有n个节点的完全二叉树的深度。
· 倒排一个LinkedList。

根据LinkedList的实现,LinkedList的底层是双向链表,它在get任何一个位置的数据的时候,都会把前面的数据走一遍。用迭代器或者foreach循环(foreach循环的原理就是迭代器)去遍历LinkedList即可,这种方式是直接按照地址去找数据的,将会大大提升遍历LinkedList的效率。

public static <T> LinkedList<T> reverse(LinkedList<T> linkedList)
{
if (linkedList == null) return linkedList;
LinkedList<T> temp_linkedlist = new LinkedList<T>();
for (T item: linkedList) {
temp_linkedlist.addLast(item);
}
return temp_linkedlist;
}
· 用Java写一个遍历目录下面的所有文件。

public static void foreachFileList(String filePath) throws IOException {
LinkedList<File> linkedList = new LinkedList<File>();
File file = new File(filePath);
if (file.exists()) {
linkedList.add(file);
while (true) {
file = linkedList.poll();
if (file == null) break;
File[] fileList = file.listFiles();
for (File fileItem : fileList) {
if (fileItem.isDirectory()) {
linkedList.add(fileItem);
continue;//for
}
if (fileItem.isFile())
System.out.println(fileItem.getCanonicalPath());
}
}
}
}
二、Java基础
· 接口与抽象类的区别?

一个类可以实现多个接口,但只能继承最多一个抽象类
抽象类可以包含具体的方法;接口所有的方法都是抽象的(不管是否对接口声明都是抽象的)(jdk1.7以前,jdk1.8开始新增功能接口中有default 方法,有兴趣自己研究)
抽象类可以声明和使用字段;接口则不能,但是可以创建静态的final常量
抽象类中的方法可以是public、protected、private或者默认的package;接口的方法都是public(不管是否声明,接口都是公开的)
抽象类可以定义构造函数,接口不能。
接口被声明为public,省略后,包外的类不能访问接口
· Java中的异常有哪几类?分别怎么使用?

Throwable包含了错误(Error)和异常(Excetion两类)
Exception又包含了 运行时异常(RuntimeException, 又叫非检查异常) 和 非运行时异常(又叫检查异常)(1) Error是程序无法处理了, 如果OutOfMemoryError、OutOfMemoryError等等, 这些异常发生时, java虚拟机一般会终止线程 .
-(2) 运行时异常都是RuntimeException类及其子类,如 NullPointerException、IndexOutOfBoundsException等, 这些异常是不检查的异常, 是在程序运行的时候可能会发生的, 所以程序可以捕捉, 也可以不捕捉. 这些错误一般是由程序的逻辑错误引起的, 程序应该从逻辑角度去尽量避免.
(3) 检查异常是运行时异常以外的异常, 也是Exception及其子类, 这些异常从程序的角度来说是必须经过捕捉检查处理的, 否则不能通过编译. 如IOException、SQLException等
· 常用的集合类有哪些?比如List如何排序?

常用的集合分为List(有序排放)、Map(以名和值一一对应的存放)、Set(既无序也没名).在这三者之中其中List和Set是Collection接口的子接口,而Map不是Collection接口的子接口.
List常用有:ArrayList和LinkedList,Vecotr(线程安全)
Set常用有: TreeSet, HashSet 元素不可重复,内部结构用HashMap,Key为Set的item值,value为一个固定的常量。java.util.Collections.newHashSetFromMap(),内部其实质还是通过ConcurrentHashMap实现线程安全的。
Map: TreeMap和LinkedHashMap,HashMap,HashTable(线程安全)
sort()方法排序的本质其实也是借助Comparable接口和Comparator接口的实现,一般有2种用法:
直接将需要排序的list作为参数传入,此时list中的对象必须实现了Comparable接口,然后sort会按升序的形式对元素进行排序;
传入list作为第一个参数,同时追加一个Comparator的实现类作为第二个参数,然后sort方法会根据Comparator接口的实现类的逻辑,按升序进行排序;
· ArrayList和LinkedList内部的实现大致是怎样的?他们之间的区别和优缺点?

Linkedlist集合的优势:添加元素时可以指定位置,比ArrayList集合添加元素要快很多。
Linkedlist在get很慢,LinkedList在get任何一个位置的数据的时候,都会把前面的数据走一遍。尽量不使用,而使用foreach LinkedList的方式来直接取得数据。
这两种方式各有优缺,为更好的使用可以将这两者进行联合使用,使用Linkedlist集合进行存储和添加元素,使用Arraylist集合进行get获取元素。
· 内存溢出是怎么回事?请举一个例子?

内存溢出(out of memory)通俗理解就是内存不够,在计算机程序中通俗的理解就是开辟的内存空间得不到释放。
OOM有堆溢出,栈溢出,方法区溢出(主要是动态生成class的处理过多)
· ==和equals的区别?

==号在比较基本数据类型时比较的是值,而用==号比较两个对象时比较的是两个对象的地址值
Object类中equals()方法底层依赖的是==号,那么,在所有没有重写equals()方法的类中,调用equals()方法其实和使用==号的效果一样,然而,Java提供的所有类中,绝大多数类都重写了equals()方法,重写后的equals()方法一般都是比较两个对象的值
· hashCode方法的作用?

hashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,hashCode是用来在散列存储结构中确定对象的存储地址的;
如果两个对象相同,就是适用于equals(Java.lang.Object) 方法,那么这两个对象的hashCode一定要相同;
如果对象的equals方法被重写,那么对象的hashCode也尽量重写,并且产生hashCode使用的对象,一定要和equals方法中使用的一致,否则就会违反上面提到的第2点;
两个对象的hashCode相同,并不一定表示两个对象就相同,也就是不一定适用于equals(java.lang.Object) 方法,只能够说明这两个对象在散列存储结构中,如Hashtable,他们“存放在同一个篮子里”。
· NIO是什么?适用于何种场景?

JDK引入了一种基于通道和缓冲区的 I/O 方式,它可以使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆的 DirectByteBuffer 对象作为这块内存的引用进行操作,避免了在 Java 堆和 Native 堆中来回复制数据。
NIO 是一种同步非阻塞的 IO 模型。同步是指线程不断轮询 IO 事件是否就绪,非阻塞是指线程在等待 IO 的时候,可以同时做其他任务。同步的核心就是 Selector,Selector 代替了线程本身轮询 IO 事件,避免了阻塞同时减少了不必要的线程消耗;非阻塞的核心就是通道和缓冲区,当 IO 事件就绪时,可以通过写道缓冲区,保证 IO 的成功,而无需线程阻塞式地等待。
· HashMap实现原理,如何保证HashMap的线程安全?

HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。HashMap底层就是一个数组结构,数组中的每一项又是一个链表。

=使用 java.util.Hashtable 类,此类是线程安全的。Hashtable是通过每个方法用synchronized来处理,性能不及ConcurrentHashMap
使用 java.util.concurrent.ConcurrentHashMap,此类是线程安全的。采用了分段锁实现同步。
使用 java.util.Collections.synchronizedMap() 方法包装 HashMap object,得到线程安全的Map,并在此Map上进行操作。
· JVM内存结构,为什么需要GC?

垃圾回收可以有效的防止内存泄露,有效的使用可以使用的内存,简化代码开发。

· NIO模型,select/epoll的区别,多路复用的原理

(过长,没有回答)

· Java中一个字符占多少个字节,扩展再问int, long, double占多少字节

Java char: utf-16:2个字节, int-4, long-8,double-9

· 创建一个类的实例都有哪些办法?

关键字 new。工厂模式是对这种方式的包装;
类实现克隆接口,克隆一个实例。原型模式是一个应用实例;
用该类的加载器,newinstance。java的反射,反射使用实例:Spring的依赖注入、切面编程中动态代理等取得
通过IO流反序列化读取一个类,获得实例。
· final/finally/finalize的区别?

final:如果一个类被final修饰,意味着该类不能派生出新的子类,不能作为父类被继承。因此一个类不能被声明为abstract,又被声明为final。将变量或方法声明为final。可以保证他们在使用的时候不被改变。其初始化可以在两个地方:一是其定义的地方,也就是在final变量在定义的时候就对其赋值;二是在构造函数中。这两个地方只能选其中的一个。被声明为final的方法也只能使用,不能重写。
finally:在异常处理的时候,提供finally块在成功或失败都可以执行任何的清除操作。
finalize:finalize是方法名,java技术允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。finalize()方法是在垃圾收集器删除对象之前对这个对象调用的,可以从Object.finalize()继承下来。
· Session/Cookie的区别?

cookie是客户端的会话状态的一种储存机制。一般限制4k以内。session是一种服务器端的信息管理机制。
session产生的session_id放在cookie里面,如果用户把cookie禁止掉,可以通过在url中保留session_id
· String/StringBuffer/StringBuilder的区别,扩展再问他们的实现?

StringBuilder:线程非安全的,StringBuffer:线程安全的,三者在执行速度方面的比较:StringBuilder > StringBuffer > String

· Servlet的生命周期?

Servlet 通过调用 init () 方法进行初始化。
Servlet 调用 service() 方法来处理客户端的请求。
Servlet 通过调用 destroy() 方法终止(结束)。
最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。
· 如何用Java分配一段连续的1G的内存空间?需要注意些什么?

使用ArrayList来分配,注意堆内存不足造面OOM

· Java有自己的内存回收机制,但为什么还存在内存泄漏的问题呢?

主要是没有释放对象引用造成的内存泄漏,比如下例:

class MyList{
/*

  • 此处只为掩饰效果,并没有进行封装之类的操作
  • 将List集合用关键字 static 声明,这时这个集合将不属于任MyList 对象,而是一个类成员变量
    */
    public static List<String> list = new ArrayList<String>();
    }

    class Demo{
    public static void main(String[] args) {
    MyList list = new MyList();
    list.list.add("123456");
    // 此时即便我们将 list指向null,仍然存在内存泄漏,因为MyList中的list是静态的,它属于类所有而不属于任何特定的实例
    list = null;
    }
    }
    · 什么是java序列化,如何实现java序列化?(写一个实例)?

序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。Java的序列化需要实现Serializable接口。

//序列化后生成指定文件路径
File file = new File("D:" + File.separator + "person.ser") ;
ObjectOutputStream oos = null ;
//装饰流(流)
oos = new ObjectOutputStream(new FileOutputStream(file)) ;
//实例化类
Person per = new Person("张三",30) ;
oos.writeObject(per) ;
//把类对象序列化
oos.close() ;
· String s = new String("abc");创建了几个 String Object?

两个对象,一个是“abc”对象,在常量池中创建;一个是new关键字创建的s对象指向“abc”。

”我自己是一名从事了十余年的后端的老程序员,辞职后目前在做讲师,近期我花了一个月整理了一份最适合2018年学习的JAVA干货(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)从事后端的小伙伴们都可以来了解一下的,这里是程序员秘密聚集地,各位还在架构师的道路上挣扎的小伙伴们速来。“

加QQ群:585550789(名额有限哦!)

猜你喜欢

转载自blog.51cto.com/14226273/2382793