Java面试笔试题集
in 代码 with 9 commentsand 1063 read

Java面试笔试题集

in 代码 with 9 commentsand 1064 read

基础

String s = new String("xyz"); 创建了几个 String Object?

两个;常量池中只可能只有一个“xyz”,但是堆中可能有多个,因为你这里用了new String来强制在堆中再创建了一个对象,所以是两个哈;如果是String s= "xyz"这样的,s这个引用就会直接指向常量池的"xyz",而不会是指向你在堆中创建的“xyz”。

List和Set的区别

List:

  1. 可以允许重复的对象。
  2. 可以插入多个null元素。
  3. 是一个有序容器,保持了每个元素的插入顺序,输出的顺序就是插入的顺序。
  4. 常用的实现类有 ArrayList、LinkedList 和 Vector。ArrayList 最为流行,它提供了使用索引的随意访问,而 LinkedList 则对于经常需要从 List 中添加或删除元素的场合更为合适。

Set:

  1. 不允许重复对象
  2. 无序容器,你无法保证每个元素的存储顺序,TreeSet通过 Comparator 或者 Comparable 维护了一个排序顺序。
  3. 只允许一个 null 元素
  4. Set 接口最流行的几个实现类是 HashSet、LinkedHashSet 以及 TreeSet。最流行的是基于 HashMap 实现的 HashSet;TreeSet 还实现了 SortedSet 接口,因此 TreeSet 是一个根据其 compare() 和 compareTo() 的定义进行排序的有序容器。

HashMap和HashTable的区别

HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的分别。主要的区别有:线程安全性,同步(synchronization),以及速度。

1.HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行)。
2.HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。
3.HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。这条同样也是Enumeration和Iterator的区别。
4.由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。
5.HashMap不能保证随着时间的推移Map中的元素次序是不变的。
大佬的详细讲解

Map集合如何遍历

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class TestMap {
    public static void main(String[] args) {
        Map<Integer, String> map = new HashMap<Integer, String>();
        map.put(1, "a");
        map.put(2, "b");
        map.put(3, "ab");
        map.put(4, "ab");
        map.put(4, "ab");// 和上面相同 , 会自己筛选
        System.out.println(map.size());
        // 第一种:
        /*
         * Set<Integer> set = map.keySet(); //得到所有key的集合
         * 
         * for (Integer in : set) { String str = map.get(in);
         * System.out.println(in + "     " + str); }
         */
        System.out.println("第一种:通过Map.keySet遍历key和value:");
        for (Integer in : map.keySet()) {
            //map.keySet()返回的是所有key的值
            String str = map.get(in);//得到每个key多对用value的值
            System.out.println(in + "     " + str);
        }
        // 第二种:
        System.out.println("第二种:通过Map.entrySet使用iterator遍历key和value:");
        Iterator<Map.Entry<Integer, String>> it = map.entrySet().iterator();
        while (it.hasNext()) {
             Map.Entry<Integer, String> entry = it.next();
               System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
        }
        // 第三种:推荐,尤其是容量大时
        System.out.println("第三种:通过Map.entrySet遍历key和value");
        for (Map.Entry<Integer, String> entry : map.entrySet()) {
            //Map.entry<Integer,String> 映射项(键-值对)  有几个方法:用上面的名字entry
            //entry.getKey() ;entry.getValue(); entry.setValue();
            //map.entrySet()  返回此映射中包含的映射关系的 Set视图。
            System.out.println("key= " + entry.getKey() + " and value= "
                    + entry.getValue());
        }
        // 第四种:
        System.out.println("第四种:通过Map.values()遍历所有的value,但不能遍历key");
        for (String v : map.values()) {
            System.out.println("value= " + v);
        }
    }
}

什么场景下使用list,set,map呢?

(或者会问为什么这里要用list、或者set、map,这里回答它们的优缺点就可以了)

  1. 如果你经常会使用索引来对容器中的元素进行访问,那么 List 是你的正确的选择。如果你已经知道索引了的话,那么 List 的实现类比如 ArrayList 可以提供更快速的访问,如果经常添加删除元素的,那么肯定要选择LinkedList。
  2. 如果你想容器中的元素能够按照它们插入的次序进行有序存储,那么还是 List,因为 List 是一个有序容器,它按照插入顺序进行存储。
  3. 如果你想保证插入元素的唯一性,也就是你不想有重复值的出现,那么可以选择一个 Set 的实现类,比如 HashSet、LinkedHashSet 或者 TreeSet。所有 Set 的实现类都遵循了统一约束比如唯一性,而且还提供了额外的特性比如 TreeSet 还是一个 SortedSet,所有存储于 TreeSet 中的元素可以使用 Java 里的 Comparator 或者 Comparable 进行排序。LinkedHashSet 也按照元素的插入顺序对它们进行存储。
  4. 如果你以键和值的形式进行数据存储那么 Map 是你正确的选择。你可以根据你的后续需要从 Hashtable、HashMap、TreeMap 中进行选择。

算法

根据给定数组形成新乱序数组

public class Main {

    public static void main(String[] args) {
      int[] a ={1,2,3,4,5,6,7,8,9,10};
      int[] b = new int[a.length];

      for (int i = 0;i<a.length;i++){
          //随机生成下标范围依次减少1,又因为取出的元素会被从队尾依次往前放置,这样就能保证 不会再产生已经产生过的元素。
          int rand = (int)(Math.random()*(a.length-i));
          b[i] = a[rand];

          //将取出的元素从队尾依次往前放置
          int x = a[a.length-1-i];
          a[a.length-1-i] = a[rand];
          a[rand] = x;
      }
      //字符串输出
        System.out.println(Arrays.toString(a));
        System.out.println(Arrays.toString(b));

    }
}

单链表的逆置

递归搞法

public class Recursion {
    class Node{
    private int value;
    private Node nextNode;
    Node(int value){
        this.value = value;
    }
}
   /**
 * 逆置单链表(递归)
 * @param head 头结点
 * @return 逆置后的头结点
 */
private static Node revert(Node head) {
    if (head == null || head.nextNode == null) {
        // 到达尾结点
        return head;
    }
        //入栈
        Node revertHead = revert(head.nextNode);
        //出栈
        head.nextNode.nextNode = head;
        head.nextNode = null;
        //返回尾节点
        return revertHead;
    }
}

迭代搞法

public class iteration {
    private class Node {
        public int value;
        public Node nextNode;
        public Node(int value) {
            this.value = value;
        }
    }

    public static Node revertIter(Node head){
        Node pre = head;
        Node cur = head.nextNode;
        Node tmp;
        //头结点的nextNode应该要置空
        pre.nextNode = null;
        while (cur != null){
            //先放next节点
            tmp = cur.nextNode;
            //修改next节点指向pre
            cur.nextNode = pre;
            pre = cur;
            cur = tmp;
        }
        return pre;
    }
}

二叉树排序的基本原理

使用第一个元素作为根节点,如果之后的元素比第一个小,则放到左子树,否则放到右子树,之后按中序遍历。
下面实现一个二叉树排序的比较算法,为了操作方便,使用Integer类完成。

public final class Integer extends Number implements Comparable<Integer>

我们可以看到Integer类实现了Comparable接口,所以可用Integer实例化Comparable接口对象

public class BinaryTree {
    class Node{                     //声明一个节点类
        private Comparable data;    //节点的数据类型为Comparable
        private Node left;          //保存左子树
        private Node right;         //保存右子树
        public Node(Comparable data){   //构造函数
            this.data= data;
        }
        public void addNode(Node newNode){
            //确定是放在左子树还是右子树
            if (newNode.data.compareTo(this.data)<0){  //新节点值小于当前节点
                if (this.left == null){
                    this.left = newNode;//左子树为空的话,新节点设为左子树
                }else {
                    this.left.addNode(newNode);   //否则继续向下判断
                }
            }else{    //新节点的值大于或者等于当前节点
                if (this.right == null){
                    this.right = newNode;
                }else {
                    this.right.addNode(newNode);
                }
            }
        }

        public void printNode(){  //采用中序遍历
            if (this.left != null){  //如果不为空先输出左子树
                this.left.printNode();
            }
            System.out.print(this.data+"\t"); //输出当前根节点
            if (this.right != null){    //输出右子树
                this.right.printNode();
            }
        }
    }

    private Node root; //表示根元素

    public void add(Comparable data){   //向二叉树中插入元素
        Node newNode =new Node(data);
        if (root == null){   //没有根节点
            root = newNode;
        }else {
            root.addNode(newNode);  //判断放在左子树还是右子树
        }
    }

    public void print(){
        root.printNode();  //根据根节点输出
    }
}

多线程

给一个整数i要求建立四个线程两个加,两个减,各100次。

public class ManyThread {
    //采用Runnable接口方式创建的多条线程可以共享实例属性

    private int i;
    //同步增加方法
    private synchronized void inc(){
        i++;
        System.out.println(Thread.currentThread().getName() + "--inc--" + i);
    }

    //同步减计算
    private synchronized void dec(){
        i--;
        System.out.println(Thread.currentThread().getName() + "--dec--" + i);
    }

    //增加线程,非静态内部类
    class Inc implements Runnable{

        @Override
        public void run() {
            int i = 0;
            while (i++ <100){
                inc();
            }
        }
    }

    //减算线程 非静态内部类
    class Dec extends Thread{
        @Override
        public void run() {
            int i =0;
            while (i++ < 100){
                dec();
            }
        }
    }
     public static void main(String[] args) {

        // 由于内部类是非静态的,所以这样需要Test的实例化才能调用生成内部类实例
        ManyThread t = new ManyThread();

        // 内部类的实例化
        Inc inc = t.new Inc(); //
        // Dec dec = t. new Dec();

        Thread thread = null;
        // 创建 2 个增加线程
        for (int i = 0; i < 2; i++) {
            thread = new Thread(inc); // 实现Runnable的类的实例化,使用带参数的Thread构造方法.
            thread.start();
        }

        // 创建 2 个减少线程
        for (int i = 0; i < 2; i++) {
            thread = t.new Dec(); // 继承Thread的类可以直接实例化.
            thread.start();
        }
    }
}

数据库

Mysql的优化

大体可以分为三部分:索引的优化,sql语句的优化,表的优化
1、索引的优化
只要列中含有NULL值,就最好不要在此例设置索引,复合索引如果有NULL值,此列在使用时也不会使用索引
尽量使用短索引,如果可以,应该制定一个前缀长度对于经常在where子句使用的列,最好设置索引,这样会加快查找速度
对于有多个列where或者order by子句的,应该建立复合索引
对于like语句,以%或者‘-’开头的不会使用索引,以%结尾会使用索引
尽量不要在列上进行运算(函数操作和表达式操作)
尽量不要使用not in和<>操作
2、sql语句的优化
查询时,能不要就不用,尽量写全字段名
大部分情况连接效率远大于子查询
多使用explain和profile分析查询语句
查看慢查询日志,找出执行时间长的sql语句优化
多表连接时,尽量小表驱动大表,即小表 join 大表
在千万级分页时使用limit
对于经常使用的查询,可以开启缓存
3、表的优化
表的字段尽可能用NOT NULL
字段长度固定的表查询会更快
把数据库的大表按时间或一些标志分成小表
将表分区

购物车数据放在哪里?(三种)

目前我们使用购物车的存储方式主要有:Session方式,Cookie方式,数据库存储,我们来一一分析优缺点。
1、Session(Memcached)方式
优点:购物车信息保存在服务端,可以保存1M 信息。
缺点:对于大型网站会占有过多的服务器内存资源,造成服务器压力过大。Session保存的信息会在用户退出登录后丢失。用户下次登录,购物车中商品信息丢失,用户只能从新选择。
2、Cookie方式
优点:购物车信息存储在客户端,不占用服务器资源,基本可以到达持久化存储。
缺点:Cookie有大小的限制,不能超过4K,而且不够安全。如果是个人PC机,Cookie能很好的保存购物车信息,但如果是公共办公环境,Cookie保存的信息基本就失效了(会被其他人购物车信息覆盖)。对一个大型的电子商务网站,我们需要对用户的购买行为进行分析,需要对用户推荐用户感兴趣的商品,如果把购物车信息保存在Cookie中,则不能对用户购买行为分析统计。
3、 数据库存储
优点:持久化存储,可以分析用户购买行为。
缺点: 网站速度变慢,成本和维护增加。

面向对象

有两个篮子分别为A和B,篮子A装着鸡蛋,篮子B装着苹果,请用面向对象的思路实现两个篮子里的物品交换。

public class Basket {
    private String s;

    public Basket(){
        super();
    }

    public Basket(String s){
        super();
        this.s = s;
    }

    public String getS() {
        return s;
    }

    public  void getS(String s){
        this.s = s;
    }

    public  String toString(){
        return "Basket [s=" + s +"]";
    }
    
   public static void main(String[] args) {
        Basket A = new Basket("egg");
        Basket B = new Basket("apple");

        String gA = A.getS();
        String gB = B.getS();
        System.out.println(gA+":"+gB);

        A.getS(gB);
        B.getS(gA);

        String gA2 = A.getS();
        String gB2 = B.getS();
        System.out.println(gA2+":"+gB2);
    }

}
评论
  1. 这是高手

    回复
  2. 我来顶贴了

    回复
    1. @悲伤的橘子树

      哈哈,感谢,都是些很基础的面试题,经常查漏补缺

      回复
  3. YIR

    嗯~想想还是修图来得简单啊~

    回复
    1. @YIR

      以此为业能混口饱饭的都不容易,修图不也得把PS那些玩转么,专业不同罢了,都辛苦

      回复
  4. 我也想学学代码 想自己搞个主题 不过感觉都没有啥时间去学这个了 你的主题挺简洁的 好看!

    回复
    1. @陆三三

      这主题是大佬写的,之前也有搞主题的想法,但是审美达不到自己想要的哪种效果就放弃了,想学代码可以啊,搞网站主题就学学前端内容,PHP啥的比较适合

      回复
  5. shansan

    抓哇啊

    回复
    1. @shansan

      转抓哇,防脱发

      回复