最新经典面试题总结

又到了求职季,最近有些时间给大家分享一下本人近些年来面试中的一些经验和面试题,有不足之处欢迎提出宝贵意见!

先给大家来个小插曲:在社交论坛或者博客上经常看到面试中面试官常问到的自我介绍该怎么介绍自己?从哪开始介绍自己?简历上已有的姓名、学校及专业还有没有必要介绍?

答:用经验告诉你,其实面试中的自我介绍从面试官的角度讲:1.你的表达能力 2.能展现突出你个人能力才华的地方 3.在你介绍过程中空出时间浏览简历,至于你在讲什么他并不会在意;这些是面试官想要的。从面试者的角度讲:自我介绍中的姓名、学校及专业没有必要重复介绍,直接从项目开始,把自己参与的整个项目业务流程 → 自己负责的模块 →每个模块使用到的技术点 → 用这些技术的优势以及为什么要用;把这些流畅的表达清楚,一般就没什么问题。至于录不录用你那就是个人技术范畴的问题。

面试题

  1. Overload和Override的区别。Overloaded的方法是否可以改变返回值的类型?
    方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。重载对返回类型没有特殊的要求。

  2. String s = new String(“xyz”);创建了几个字符串对象?
    两个对象,一个是静态区的”xyz”,一个是用new创建在堆上的对象。

  3. int 和 Integer 有什么区别?
    Java是一个近乎纯洁的面向对象编程语言,但是为了编程的方便还是引入了基本数据类型,但是为了能够将这些基本数据类型当成对象操作,Java为每一个基本数据类型都引入了对应的包装类型(wrapper class),int的包装类就是Integer,从Java 5开始引入了自动装箱/拆箱机制,使得二者可以相互转换。 
    Java 为每个原始类型提供了包装类型: 
    - 原始类型: boolean,char,byte,short,int,long,float,double 
    - 包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double
    下面引用一个大佬的经典解释
    class AutoUnboxingTest {
    
        public static void main(String[] args) {
            Integer a = new Integer(3);
            Integer b = 3;                  // 将3自动装箱成Integer类型
            int c = 3;
            System.out.println(a == b);     // false 两个引用没有引用同一对象
            System.out.println(a == c);     // true a自动拆箱成int类型再和c比较
        }
    }
    下面也是一个面试题:
    public class Test03 {
    
        public static void main(String[] args) {
            Integer f1 = 100, f2 = 100, f3 = 150, f4 = 150;
    
            System.out.println(f1 == f2);
            System.out.println(f3 == f4);
        }
    }
    如果不明就里很容易认为两个输出要么都是true要么都是false。首先需要注意的是f1、f2、f3、f4四个变量都是Integer对象引用,所以下面的==运算比较的不是值而是引用。装箱的本质是什么呢?当我们给一个Integer对象赋一个int值的时候,会调用Integer类的静态方法valueOf,如果看看valueOf的源代码就知道发生了什么。
    public static Integer valueOf(int i) {
            if (i >= IntegerCache.low && i <= IntegerCache.high)
                return IntegerCache.cache[i + (-IntegerCache.low)];
            return new Integer(i);
        }
    IntegerCache是Integer的内部类,其代码如下所示:
    private static class IntegerCache {
            static final int low = -128;
            static final int high;
            static final Integer cache[];
    
            static {
                // high value may be configured by property
                int h = 127;
                String integerCacheHighPropValue =
                    sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
                if (integerCacheHighPropValue != null) {
                    try {
                        int i = parseInt(integerCacheHighPropValue);
                        i = Math.max(i, 127);
                        // Maximum array size is Integer.MAX_VALUE
                        h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                    } catch( NumberFormatException nfe) {
                        // If the property cannot be parsed into an int, ignore it.
                    }
                }
                high = h;
    
                cache = new Integer[(high - low) + 1];
                int j = low;
                for(int k = 0; k < cache.length; k++)
                    cache[k] = new Integer(j++);
    
                // range [-128, 127] must be interned (JLS7 5.1.7)
                assert IntegerCache.high >= 127;
            }
    
            private IntegerCache() {}
        }
    简单的说,如果整型字面量的值在-128到127之间,那么不会new新的Integer对象,而是直接引用常量池中的Integer对象,所以上面的面试题中f1==f2的结果是true,而f3==f4的结果是false。
    提醒:越是貌似简单的面试题其中的玄机就越多,需要面试者有相当深厚的功力。
    以上代码片段均为引用别处,看了比较经典所以引过来供大家参考。

  4. 说出ArrayList,Vector, LinkedList的存储性能和特性?
     ArrayList 和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,Vector中的方法由于添加了synchronized修饰,因此Vector是线程安全的容器,但性能上较ArrayList差,因此已经是Java中的遗留容器。LinkedList使用双向链表实现存储(将内存中零散的内存单元通过附加的引用关联起来,形成一个可以按序号索引的线性结构,这种链式存储方式与数组的连续存储方式相比,内存的利用率更高),按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。Vector属于遗留容器(Java早期的版本中提供的容器,除此之外,Hashtable、Dictionary、BitSet、Stack、Properties都是遗留容器),已经不推荐使用,但是由于ArrayList和LinkedListed都是非线程安全的,如果遇到多个线程操作同一个容器的场景,则可以通过工具类Collections中的synchronizedList方法将其转换成线程安全的容器后再使用(这是对装潢模式的应用,将已有对象传入另一个类的构造器中创建新的对象来增强实现)。

  5. &和&&的区别?
     &运算符有两种用法:(1)按位与;(2)逻辑与。&&运算符是短路与运算。逻辑与跟短路与的差别是非常巨大的,虽然二者都要求运算符左右两端的布尔值都是true整个表达式的值才是true。&&之所以称为短路运算是因为,如果&&左边的表达式的值是false,右边的表达式会被直接短路掉,不会进行运算。很多时候我们可能都需要用&&而不是&,例如在验证用户登录时判定用户名不是null而且不是空字符串,应当写为:username != null &&!username.equals(""),二者的顺序不能交换,更不能用&运算符,因为第一个条件如果不成立,根本不能进行字符串的equals比较,否则会产生NullPointerException异常。注意:逻辑或运算符(|)和短路或运算符(||)的差别也是如此。

  6. final, finally, finalize的区别?
    final 用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。
    finally是异常处理语句结构的一部分,表示总是执行。
    finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等。

  7. Thread类的sleep()方法和对象的wait()方法都可以让线程暂停执行,它们有什么区别? 
    答:sleep()方法(休眠)是线程类(Thread)的静态方法,调用此方法会让当前线程暂停执行指定的时间,将执行机会(CPU)让给其他线程,但是对象的锁依然保持,因此休眠时间结束后会自动恢复(线程回到就绪状态,请参考第66题中的线程状态转换图)。wait()是Object类的方法,调用对象的wait()方法导致当前线程放弃对象的锁(线程暂停执行),进入对象的等待池(wait pool),只有调用对象的notify()方法(或notifyAll()方法)时才能唤醒等待池中的线程进入等锁池(lock pool),如果线程重新获得对象的锁就可以进入就绪状态。
    补充:可能不少人对什么是进程,什么是线程还比较模糊,对于为什么需要多线程编程也不是特别理解。简单的说:进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,是操作系统进行资源分配和调度的一个独立单位;线程是进程的一个实体,是CPU调度和分派的基本单位,是比进程更小的能独立运行的基本单位。线程的划分尺度小于进程,这使得多线程程序的并发性高;进程在执行时通常拥有独立的内存单元,而线程之间可以共享内存。使用多线程的编程通常能够带来更好的性能和用户体验,但是多线程的程序对于其他程序是不友好的,因为它可能占用了更多的CPU资源。当然,也不是线程越多,程序的性能就越好,因为线程之间的调度和切换也会浪费CPU时间。时下很时髦的Node.js就采用了单线程异步I/O的工作模式。


  8. 数组有没有length()这个方法? String有没有length()这个方法?
    数组没有length()这个方法,有length的属性。String有length()这个方法(javascript获得字符串的长度是通过length属性得到)。

  9. try {}里有一个return语句,那么紧跟在这个try后的finally {}里的code会不会被执行,什么时候被执行,在return前还是后?
    会执行,在return前执行。

  10. java中实现多态的机制是什么?
    方法的重写Overriding和重载Overloading是Java多态性的不同表现。重写Overriding是父类与子类之间多态性的一种表现,重载Overloading是一个类中多态性的一种表现。

  11. 说出Servlet的生命周期,并说出Servlet和CGI的区别?
    Servlet被服务器实例化后,容器运行其init方法,请求到达时运行其service方法,service方法自动派遣运行与请求对应的doXXX方法(doGet,doPost)等,当服务器决定将实例销毁的时候调用其destroy方法。
    与cgi的区别在于servlet处于服务器进程中,它通过多线程方式运行其service方法,一个实例可以服务于多个请求,并且其实例一般不会销毁,而CGI对每个请求都产生新的进程,服务完成后就销毁,所以效率上低于servlet。

  12. 冒泡排序
      for(int i=0;i<ary.length-1;i++){
        for(int j=0;j<ary.length-i-1;j++){
           if(ary[j]>ary[j+1]){
                  int temp=ary[j];
                  ary[j]=ary[j+1];
                  ary[j+1]=temp;   
           }           
         }
      }
           return ary;

  13. String 是基本数据类型吗? 
    不是。Java中的基本数据类型只有8个:byte、short、int、long、float、double、char、boolean;除了基本类型(primitive type),剩下的都是引用类型(reference type),Java 5以后引入的枚举类型也算是一种比较特殊的引用类型。

  14. 接口是否可继承(extends)接口?抽象类是否可实现(implements)接口?抽象类是否可继承具体类(concrete class)?
    接口可以继承接口,而且支持多重继承。抽象类可以实现(implements)接口,抽象类可继承具体类也可以继承抽象类。

  15. Collection和Collections的区别?
    Collection是一个接口,它是Set、List等容器的父接口;Collections是个一个工具类,提供了一系列的静态方法来辅助容器操作,这些方法包括对容器的搜索、排序、线程安全化等等。

  16. 启动一个线程是调用run()还是start()方法?
    启动一个线程是调用start()方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM 调度并执行,这并不意味着线程就会立即运行。run()方法是线程启动后要进行回调(callback)的方法。

  17. Java支持多继承么?
    不支持,Java不支持多继承。每个类都只能继承一个类,但是可以实现多个接口。

  18. 什么是值传递和引用传递?
    对象被值传递,意味着传递了对象的一个副本。因此,就算是改变了对象副本,也不会影响源对象的值。 
    对象被引用传递,意味着传递的并不是实际的对象,而是对象的引用。因此,外部对引用对象所做的改变会反映到所有的对象上。

  19. doGet()方法和doPost()方法有什么区别?
    ①get请求用来从服务器上获得资源,而post是用来向服务器提交数据; 
    ②get将表单中数据按照name=value的形式,添加到action 所指向的URL 后面,并且两者使用"?"连接,而各个变量之间使用"&"连接;post是将表单中的数据放在HTTP协议的请求头或消息体中,传递到action所指向URL; 
    ③get传输的数据要受到URL长度限制(1024字节);而post可以传输大量的数据,上传文件通常要使用post方式; 
    ④使用get时参数会显示在地址栏上,如果这些数据不是敏感数据,那么可以使用get;对于敏感数据还是应用使用post; 
    ⑤get使用MIME类型application/x-www-form-urlencoded的URL编码(也叫百分号编码)文本的格式传递参数,保证被传送的参数由遵循规范的文本组成,例如一个空格的编码是"%20"。
  20. 访问修饰符public,private,protected,以及不写(默认)时的区别? 
    修饰符 当前类 同 包 子 类 其他包
    public
    protected√ ×
    default × ×
    private × × ×
    类的成员不写访问修饰时默认为default。默认对于同一个包中的其他类相当于公开(public),对于不是同一个包中的其他类相当于私有(private)。受保护(protected)对子类相当于公开,对不是同一包中的没有父子关系的类相当于私有。Java中,外部类的修饰符只能是public或默认,类的成员(包括内部类)的修饰符可以是以上四种。

  21. HashMap和Hashtable有什么区别?
    HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行)。HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。
    由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。

  22. float f=3.4;是否正确? 
    不正确。3.4是双精度数,将双精度型(double)赋值给浮点型(float)属于下转型(down-casting,也称为窄化)会造成精度损失,因此需要强制类型转换float f =(float)3.4; 或者写成float f =3.4F;。

  23. short s1 = 1; s1 = s1 + 1;有错吗?short s1 = 1; s1 += 1;有错吗? 
    对于short s1 = 1; s1 = s1 + 1;由于1是int类型,因此s1+1运算结果也是int 型,需要强制转换类型才能赋值给short型。而short s1 = 1; s1 += 1;可以正确编译,因为s1+= 1;相当于s1 = (short)(s1 + 1);其中有隐含的强制类型转换。

  24. Math.round(11.5) 等于多少?Math.round(-11.5)等于多少? 
    Math.round(11.5)的返回值是12,Math.round(-11.5)的返回值是-11。四舍五入的原理是在参数上加0.5然后进行下取整。

  25. 用最有效率的方法计算2乘以8? 
    2 << 3(左移3位相当于乘以2的3次方,右移3位相当于除以2的3次方)。

  26. 指出下面程序的运行结果。
    class A {
    
        static {
            System.out.print("1");
        }
    
        public A() {
            System.out.print("2");
        }
    }
    
    class B extends A{
    
        static {
            System.out.print("a");
        }
    
        public B() {
            System.out.print("b");
        }
    }
    
    public class Hello {
    
        public static void main(String[] args) {
            A ab = new B();
            ab = new B();
        }
    
    }
    执行结果:1a2b2b。创建对象时构造器的调用顺序是:先初始化静态成员,然后调用父类构造器,再初始化非静态成员,最后调用自身构造器。

  27. String和StringBuilder、StringBuffer的区别? 
    Java平台提供了两种类型的字符串:String和StringBuffer/StringBuilder,它们可以储存和操作字符串。其中String是只读字符串,也就意味着String引用的字符串内容是不能被改变的。而StringBuffer/StringBuilder类表示的字符串对象可以直接进行修改。StringBuilder是Java 5中引入的,它和StringBuffer的方法完全相同,区别在于它是在单线程环境下使用的,因为它的所有方面都没有被synchronized修饰,因此它的效率也比StringBuffer要高。

  28. 阐述final、finally、finalize的区别。  
    - final:修饰符(关键字)有三种用法:如果一个类被声明为final,意味着它不能再派生出新的子类,即不能被继承,因此它和abstract是反义词。将变量声明为final,可以保证它们在使用中不被改变,被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取不可修改。被声明为final的方法也同样只能使用,不能在子类中被重写。 
    - finally:通常放在try…catch…的后面构造总是执行代码块,这就意味着程序无论正常执行还是发生异常,这里的代码只要JVM不关闭都能执行,可以将释放外部资源的代码写在finally块中。 
    - finalize:Object类中定义的方法,Java中允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在销毁对象时调用的,通过重写finalize()方法可以整理系统资源或者执行其他清理工作。

  29. List、Set、Map是否继承自Collection接口? 
    List、Set 是,Map 不是。Map是键值对映射容器,与List和Set有明显的区别,而Set存储的零散的元素且不允许有重复元素(数学中的集合也是如此),List是线性结构的容器,适用于按数值索引访问元素的情形。

  30. List、Map、Set三个接口存取元素时,各有什么特点? 
    List以特定索引来存取元素,可以有重复元素。Set不能存放重复元素(用对象的equals()方法来区分元素是否重复)。Map保存键值对(key-value pair)映射,映射关系可以是一对一或多对一。Set和Map容器都有基于哈希存储和排序树的两种实现版本,基于哈希存储的版本理论存取时间复杂度为O(1),而基于排序树版本的实现在插入或删除元素时会按照元素或元素的键(key)构成排序树从而达到排序和去重的效果。

  31. 启动一个线程是调用run()还是start()方法? 
    启动一个线程是调用start()方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM 调度并执行,这并不意味着线程就会立即运行。run()方法是线程启动后要进行回调(callback)的方法。

  32. 用Java写一个单例类。 
    - 饿汉式单例
    public class Singleton {
        private Singleton(){}
        private static Singleton instance = new Singleton();
        public static Singleton getInstance(){
            return instance;
        }
    }
    • 懒汉式单例
      public class Singleton {
          private static Singleton instance = null;
          private Singleton() {}
          public static synchronized Singleton getInstance(){
              if (instance == null) instance = new Singleton();
              return instance;
          }
      }
      注意:实现一个单例有两点注意事项,①将构造器私有,不允许外界通过构造器创建对象;②通过公开的静态方法向外界返回类的唯一实例。
  33. 简述一下你了解的设计模式。 
    所谓设计模式,就是一套被反复使用的代码设计经验的总结(情境中一个问题经过证实的一个解决方案)。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。设计模式使人们可以更加简单方便的复用成功的设计和体系结构。将已证实的技术表述成设计模式也会使新系统开发者更加容易理解其设计思路。 
    在GoF的《Design Patterns: Elements of Reusable Object-Oriented Software》中给出了三类(创建型[对类的实例化过程的抽象化]、结构型[描述如何将类或对象结合在一起形成更大的结构]、行为型[对在不同的对象之间划分责任和算法的抽象化])共23种设计模式,包括:Abstract Factory(抽象工厂模式),Builder(建造者模式),Factory Method(工厂方法模式),Prototype(原始模型模式),Singleton(单例模式);Facade(门面模式),Adapter(适配器模式),Bridge(桥梁模式),Composite(合成模式),Decorator(装饰模式),Flyweight(享元模式),Proxy(代理模式);Command(命令模式),Interpreter(解释器模式),Visitor(访问者模式),Iterator(迭代子模式),Mediator(调停者模式),Memento(备忘录模式),Observer(观察者模式),State(状态模式),Strategy(策略模式),Template Method(模板方法模式), Chain Of Responsibility(责任链模式)。 
    面试被问到关于设计模式的知识时,可以拣最常用的作答,例如: 
    - 工厂模式:工厂类可以根据条件生成不同的子类实例,这些子类有一个公共的抽象父类并且实现了相同的方法,但是这些方法针对不同的数据进行了不同的操作(多态方法)。当得到子类的实例后,开发人员可以调用基类中的方法而不必考虑到底返回的是哪一个子类的实例。 
    - 代理模式:给一个对象提供一个代理对象,并由代理对象控制原对象的引用。实际开发中,按照使用目的的不同,代理可以分为:远程代理、虚拟代理、保护代理、Cache代理、防火墙代理、同步化代理、智能引用代理。 
    - 适配器模式:把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起使用的类能够一起工作。 
    - 模板方法模式:提供一个抽象类,将部分逻辑以具体方法或构造器的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法(多态实现),从而实现不同的业务逻辑。 
    除此之外,还可以讲讲上面提到的门面模式、桥梁模式、单例模式、装潢模式(Collections工具类和I/O系统中都使用装潢模式)等,反正基本原则就是拣自己最熟悉的、用得最多的作答,以免言多必失。

  34. 讲解JSP中的四种作用域。 
    JSP中的四种作用域包括page、request、session和application,具体来说: 
    - page代表与一个页面相关的对象和属性。 
    - request代表与Web客户机发出的一个请求相关的对象和属性。一个请求可能跨越多个页面,涉及多个Web组件;需要在页面显示的临时数据可以置于此作用域。 
    - session代表与某个用户与服务器建立的一次会话相关的对象和属性。跟某个用户相关的数据应该放在用户自己的session中。 
    - application代表与整个Web应用程序相关的对象和属性,它实质上是跨越整个Web应用程序,包括多个页面、请求和会话的一个全局作用域。

  35. 你的项目中使用过哪些JSTL标签? 
    答:项目中主要使用了JSTL的核心标签库,包括<c:if>、<c:choose>、<c: when>、<c: otherwise>、<c:forEach>等,主要用于构造循环和分支结构以控制显示逻辑。

  36. 表达式语言(EL)支持哪些运算符? 
    答:除了.和[]运算符,EL还提供了: 
    - 算术运算符:+、-、*、/或div、%或mod 
    - 关系运算符:==或eq、!=或ne、>或gt、>=或ge、<或lt、<=或le 
    - 逻辑运算符:&&或and、||或or、!或not 
    - 条件运算符:${statement? A : B}(跟Java的条件运算符类似) 
    - empty运算符:检查一个值是否为null或者空(数组长度为0或集合中没有元素也返回true)



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值