Java面试题—基础(二)

  1. 日期和时间

    • 如何取得年、月、日、时、分、秒、毫秒?
    • 如何取得从1970年1月1日0时0分0秒到现在的毫秒数?
    • 如何取得某月的后一天?
    • 如何获取昨天的当前时刻?
    • 如何格式化日期?

    • 取得年、月、日、时、分、秒、毫秒
       Calendar cal = Calendar.getInstance();         
       System.out.println(cal.get(Calendar.YEAR));         
       System.out.println(cal.get(Calendar.MONTH));    // 0 - 11
       System.out.println(cal.get(Calendar.DATE));         
       System.out.println(cal.get(Calendar.HOUR_OF_DAY));        
       System.out.println(cal.get(Calendar.MINUTE));         
       System.out.println(cal.get(Calendar.SECOND)); 
      
       // Java 8         
       LocalDateTime dt = LocalDateTime.now();
       System.out.println(dt.getYear());
       System.out.println(dt.getMonthValue());// 1 - 12         
       System.out.println(dt.getDayOfMonth());      
       System.out.println(dt.getHour());       
       System.out.println(dt.getMinute());       
       System.out.println(dt.getSecond());   
      
    • 如何取得从1970年1月1日0时0分0秒到现在的毫秒数?
      Calendar.getInstance().getTimeInMillis();
      System.currentTimeMillis();
      Clock.systemDefaultZone().millis(); // Java 8 
      
    • 如何取得某月的后一天?
      Calendar time = Calendar.getInstance(); 
      time.getActualMaximum(Calendar.DAY_OF_MONTH); 
      //Java 8
      LocalDate date = LocalDate.now();
      date.with(TemporalAdjusters.lastDayOfMonth());
      
    • 如何获取昨天的当前时刻?
      Calendar cal = Calendar.getInstance();
      cal.add(Calendar.DATE, -1); 
      //Java 8
      LocalDateTime today = LocalDateTime.now(); 
      LocalDateTime yesterday = today.minusDays(1); 		
      
    • 如何格式化日期?
      SimpleDateFormat oldFormatter = new SimpleDateFormat("yyyy/MM/dd");
      Date date1 = new Date();
      System.out.println(oldFormatter.format(date1)); 
      
      // Java 8         
      DateTimeFormatter newFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
      LocalDate date2 = LocalDate.now();
      System.out.println(date2.format(newFormatter)); 
      
  2. 比较一下Java和JavaScript
    Java与JavaScript是两个公司开发的不同的两个产品。Java 是原Sun Micro Systems公司推出的面向对象的程序设计语言,特别适合于互联网应用程序开发;而JavaScript是 Netscape公司的产品,为了扩展Netscape浏览器的功能而开发的一种可以嵌入Web页面中运行的基于对象和事件驱动的解释性语言。JavaScript的前身是LiveScript;而Java的前身是 Oak语言。
    两种语言间的异同如下比较:

    • 基于对象和面向对象:Java是一种真正的面向对象的语言,即使是开发简单的程序,必须设计对象;JavaScript是种脚本语言,它可以用来制作与网络无关的,与用户交互作用的复杂软件。它是一种基于对象(Object-Based)和事件驱动(Event-Driven)的编程语言,因而它本身提供了非常丰富的内部对象供设计人员使用。
    • 解释和编译:Java的源代码在执行之前,必须经过编译。JavaScript是一种解释性编程语言,其源代码不需经过编译,由浏览器解释执行。(目前的浏览器几乎都使用了JIT(即时编 译)技术来提升JavaScript的运行效率)
    • 强类型变量和类型弱变量:Java采用强类型变量检查,即所有变量在编译之前必须作声明;JavaScript中变量是弱类型的,甚至在使用变量前可以不作声明,JavaScript的解释器在运行 时检查推断其数据类型。

    Java和JavaScript重要的区别是一个是静态语言,一个是动态语言。目前的编程语言的发展趋势是函数式语言和动态语 言。在Java中类(class)是一等公民,而JavaScript中函数(function)是一等公民,因此JavaScript支持函数式编程,可以使用Lambda函数和闭包(closure),当然Java 8也开始支 持函数式编程,提供了对Lambda表达式以及函数式接口的支持。

  3. Error和Exception有什么区别?
    Error表示系统级的错误和程序不必处理的异常,是能恢复但很困难的情况下的一种严重问题;比如内存溢出,不可能指望程序能处理这样的情况;Exception表示需要捕捉或者需要程序进行处理的异常,是一种设计或实现问题,也就是说,它表示如果程序运行正常,从不会发生的情况。
    常见的Error:

    • OutOfMemoryError:内存溢出,内存不够用
    • StackOverFlowError:栈溢出,栈空间不够用或者方法调用深度超过最大值(递归不合理造成)
  4. try里有一个return语句,对应结构finally里的代码会不会被执行,什么时候被执行,在return前还是后?
    会执行。

    finally中的语句会在try和catch里控制流传语句前执行和语句结束后执行,控制流传语句分四种情况:break、continue、返回return和抛出异常throw,语句结束就是除了控制流转语句之外后面没有语句了(除了System.exit(n))。
    try里的语句对返回值的变量的引用的修改不会影响返回的引用,但是对返回值的变量属性的修改会影响返回值的属性,因为操作的同一对象;finally里的语句同样在catch里的return或者throw语句结束之前执行,finally里的return和throw会覆盖返回值和抛出异常,一般不建议这么做,这违背了try(-catch)-finally的设计初衷,这里的catch是可选的,没有catch编译自动添加catch并重抛异常。
    try(-catch)-finally的实现方式:(《深入理解Java虚拟机》)

    在JDK1.4.2之前的Javac编译器采用了jsr和ret指令实现finally语句,但1.4.2之后已经改为编译器自动在每段可能的分支路径之后都将finally语句块的内容冗余生成一遍来实现finally语义。在JDK 1.7中,已经完全禁止Class文件中出现jsr和ret指令,如果遇到这两条指令,虚拟机会在类加载的字节码校验阶段抛出异常。

  5. Java语言如何进行异常处理,关键字:throws、throw、try、catch、finally分别如何使用?
    Java通过面向对象的方法进行异常处理,把各种不同的异常进行分类,并提供了良好的接口。在Java中,每个异常都是一个对象,它是Throwable类或其子类的实例。当一个方法出 现异常后便抛出一个异常对象,该对象中包含有异常信息,调用这个对象的方法可以捕获到这个异常并可以对其进行处理。
    Java的异常处理是通过5个关键词来实现的:try、catch、 throw、throws和finally。一般情况下是用try来执行一段程序,如果系统会抛出(throw)一个异常对象,可以通过它的类型来捕获(catch)它,或通过总是执行代码块(finally)来处理;try用来指定一块预防所有异常的程序;catch子句紧跟在try块后面,用来指定你想要捕获的异常的类型;throw语句用来明确地抛出一个异常;throws用来声明一个方法可能抛出 的各种异常(当然声明异常时允许无病呻吟);finally为确保一段代码不管发生什么异常状况都要被执行;try语句可以嵌套,每当遇到一个try语句,异常的结构就会被放入异常栈中, 直到所有的try语句都完成。如果下一级的try语句没有对某种异常进行处理,异常栈就会执行出栈操作,直到遇到有处理这种异常的try语句或者终将异常抛给JVM。

  6. 运行时异常与受检异常有何异同?
    异常表示程序运行过程中可能出现的非正常状态。
    运行时异常(RuntimeException)表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误,只要程序设计得没有问题通常就不会发生。
    受检异常(Exception)跟程序运行的上下文环境有关,即使程序设计无误,仍然可能因使用的问题而引发。Java编译器要求方法必须声明抛出可能发生的受检异常,但是并不要求必须声明抛出未被捕获的运行 时异常。
    异常和继承一样,是面向对象程序设计中经常被滥用的东西,在Effective Java中对异常的使用给出了以下指导原则:

    • 不要将异常处理用于正常的控制流(设计良好的API不应该强迫它的调用者为了正常的控制流而使用异常)
    • 对可以恢复的情况使用受检异常,对编程错误使用运行时异常
    • 避免不必要的使用受检异常(可以通过一些状态检测手段来避免异常的发生)
    • 优先使用标准的异常 - 每个方法抛出的异常都要有文档 - 保持异常的原子性 - 不要在catch中忽略掉捕获到的异常。

    基本异常继承关系。(图片来源:https://blog.csdn.net/a15089415104/article/details/83350277)
    异常基继承关系
    凡是继承RuntimeException的类都是运行时异常,其他只要不是Error都是受检异常。
    运行时异常和Error不要去try检查,其他异常都需要try检查。
    try中的语句所调用的方法如果未能抛出某个Exception子类匹配的受检异常,那么catch中不能捕捉此类型的异常,其他均不受限制。

    常见的运行时异常:ArithmeticException(算术异常) 、ClassCastException (类转换异常) 、IllegalArgumentException (非法参数异常)、 IndexOutOfBoundsException (下标越界异常) 、NullPointerException (空指针异常) 、SecurityException (安全异常)

    常见的受检异常:IOException(IO异常) 、FileNotFoundException (文件未找到异常) 、CloneNotSupportedException(不支持clone异常)、ReflectiveOperationException(反射操作异常)、ClassNotFoundException(类未找到异常)、SQLException(SQL异常)

  7. 阐述final、finally、finalize的区别。

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

    try {
           throw new ExampleB("b")
       } catch(ExampleA e){
           System.out.println("ExampleA");
       } catch(Exception e){
           System.out.println("Exception");
    }
    

    请问执行此段代码的输出是什么?
    ExampleA。(根据里氏代换原则[能使用父类型的地方一定能使用子类型],抓取ExampleA类型异常的catch块能够抓住try块中抛出的ExampleB类型的异常) 。

    class Human {
         public static void main(String[] args) throws Exception {
             try {
                 try {
                     throw new Sneeze();
                 } catch (Annoyance a) {
                     System.out.println("Caught Annoyance");
                     throw a;
                 }
             } catch (Sneeze s) {
                 System.out.println("Caught Sneeze");
             } finally {
                 System.out.println("Hello World!");
             }
         }
    }
    

    执行结果

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

  10. 阐述ArrayList、Vector、LinkedList的存储性能和特性
    ArrayList 和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动和数组扩容等内存操作,所以索引数据快而插入数据慢,Vector中的方法由于添加了synchronized修饰,因此Vector是线程安全的容器,但性能上较ArrayList差,因此已经是Java中的遗留容器。 LinkedList使用双向链表实现存储(将内存中零散的内存单元通过附加的引用关联起来,形成一个可以按序号索引的线性结构,这种链式存储方式与数组的连续存储方式相比,内存的利 用率更高),按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。Vector属于遗留容器(Java早期的版本中提供的容器,除此 之外,Hashtable、Dictionary、Stack、Properties都是遗留容器)已经不推荐使用。

    由于ArrayList和LinkedList都是非线程安全的,如果遇到多个线程操作同一个容器的场景,则可以通过工具类Collections中的synchronizedList方法将其转换成线程安全的容器后再使用(这是对装饰模式的应用,将已有对象传入另一个类的构造器中创建新的对象来 增强实现)。

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

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

  13. TreeMap和TreeSet在排序时如何比较元素?Collections工具类中的sort()方法如何比较元素?
    TreeMap和TreeSet通过在构造时指定一个排序器或要求存放的对象所属的类必须实现Comparable接口(该接口提供了比较元素的compareTo()方法)当插入元素时会回调该方法比较元素的大小;TreeSet内部持有一个TreeMap的,忽略了Value。

    Collections工具类的sort方法有两种重载的形式,第一种要求传入的待排序容器中存放的对象比较实现Comparable 接口以实现元素的比较;第二种不强制性的要求容器中的元素必须可比较,但是要求传入第二个参数,参数是Comparator接口的子类型(需要重写compare方法实现元素的比较),相当于一个临时定义的排序规则,其实就是通过接口注入比较元素大小的算法,也是对回调模式的应用(Java8中对函数式编程的支持)。

  14. 集合与数组区别和特性
    数组(可以存储基本数据类型)是用来存现对象的一种容器,但是数组的长度固定,不适合在对象数量未知的情况下使用。
    集合(只能存储对象,对象类型可以不一样)的长度可变,可在多数情况下使用。
    集合框架层级图,如图所示:图中,实线边框的是实现类,折线边框的是抽象类,而点线边框的是接口
    集合框架层级图
    Collection接口是集合类的根接口,Java中没有提供这个接口的直接的实现类。但是却让其被继承产生了两个接口,就是Set和List。Set中不能包含重复的元素。List是一个有序的集合,可以包含重复的元素,提供了按索引访问的方式。
    Map是Java.util包中的另一个接口,它和Collection接口没有关系,是相互独立的,但是都属于集合类的一部分。Map包含了key-value对。Map不能包含重复的key,但是可以包含相同的value。
    Iterator,所有的集合类,都实现了Iterator接口,这是一个用于遍历集合中元素的接口,主要包含以下三种方法:

    1. hasNext()是否还有下一个元素。
    2. next()返回下一个元素。
    3. remove()删除当前元素
  15. Enumeration和Iterator接口的区别?
    Enumeration的速度是Iterator的两倍,也使用更少的内存。Enumeration是非常基础的,也满足了基础的需要。但是,与Enumeration相比,Iterator更加安全,因为当一个集合正在被遍历的时候,它会阻止其它线程去修改集合。

  16. Java集合框架常见的Map有哪些实现类,他们的区别是什么
    HashMap、Hashtable、ConcurrentHashMap、LinkedHashMap和TreeMap。

    • HashMap:它根据键的HashCode值决定键值对存储位置的,根据键可以直接获取它的值,具有很快的访问速度,遍历时,取得数据的顺序是完全随机的。因为键对象不可以重复,所以HashMap最多只允许一条记录的键为Null,允许多条记录的值为Null,是非同步的。HashMap解决冲突的方式是“拉链法”,java8中当拉链上的元素个数超过8(含)个时采用红黑树,以便快速访问。思考与HashSet的关系。
    • Hashtable:Hashtable与HashMap类似,是HashMap的线程安全版,它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了Hashtale在写入时会比较慢,它继承自Dictionary类,不同的是它不允许记录的键或者值为null,同时效率较低。
    • ConcurrentHashMap:线程安全,并且锁分离。Java7之前,ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个小的hash table,它们有自己的锁,只要多个修改操作发生在不同的段上,它们就可以并发进行。Java8之后放弃使用分段锁机制,采用CAS(乐观锁)方式保证线程安全。
    • LinkedHashMap:LinkedHashMap维护了迭代顺序,既可以是插入顺序,也可以时访问顺序,在构造时可以传入这个标志,有HashMap的全部特性。其实现方式继承HashMap,然后其Entry继承了HashMap.Entry,添加了before和after属性,维护了Entry节点的先后关系,使节点之间形成了了一个双向链表,在插入或者访问时把对应节点调整到链表的最后,但是Hash结构性依然保留。思考与LinkedHashSet的关系。
    • TreeMap:TreeMap实现SortMap接口,能够把它保存的记录根据键排序,默认是按键值(此种情况Key需要实现Comparable接口,否则会异常)的升序排序,也可以指定排序的比较器。当用Iterator遍历TreeMap时,得到的记录是排过序的。不允许key值为空,非同步的;思考与TreeSet的关系。

    思考:Set与Map有个共性:不允许元素/key重复,那么Set的实现可以借助Map,用Key来做元素,忽略value,只关注Key就可以,Java的set实现基本上是这么做的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值