[Android] 面试题总结-猎趣-Java部分

前言

昨天,很意外的接到了猎趣的电话,直奔主题就开始面试了,我都不知道我投过这家公司,后来才知道,是朋友推荐的。面试官人很好,问的问题分了 Java,Android 两类,从简单到深入,一点一点来,事后,我觉得这是很难得的经历,打算记录下来,以备后用。

Java 问题

  • 简单说一说,Java 基本类型
  • 关于 String 的了解,是否可以被继承,以及 StringBuffer,StringBuilder 三者的关系
  • 说一说,对于 Collection 和 Collections 的理解
  • Map 继承自什么?以及 HashMap,HashTable 的区别
  • 接口和抽象类的区别
  • 说一说,线程同步的几种方法

下面我一个一个整理资料,为自己做一个总结。

1,Java 基本类型

这个很简单:byte,short,int,long,float,double,char,boolean;
关于这个点,没什么说的,只是要注意,回答的顺序,先整型,后浮点型,之后再特殊的,这样的好处,一是容易记,二是体现自己逻辑清晰,千万不要想起一个说一个 = =

2,String,StringBuffer,StringBuilder

我个人的使用频率 String > StringBuffer > StringBuilder,= =,还好前两天刷牛客网的题,有遇到过,简单总结一下:

  • String
    不可以被继承,原因是因为,String 类是被 final 修饰的;
public final class String
    implements java.io.Serializable, Comparable, CharSequence {
        ...
}      

String 是只读字符串,也就意味着 String 引用的字符串内容是不能被改变的,有人说:

    String a = "a";
    a = "b"; 

这不就改变了吗?其实不然,变量 a 在栈中,"a","b" 则在常量池中,以上两行代码,只是把 String a 的引用指向了 "b",而非,将"a"直接改为"b"。
另外,String a = "a" 与 String a = new String("a") 也是不一样的,主要区别就是,后者在栈中创建了对象,看下面代码一目了然:

    String str = new String("hello");
    String str_1 = "hello";
    System.out.println(str == "hello");    // false
    System.out.println(str_1 == "hello");  // true
    System.out.println(str == str_1);      // false
    System.out.println(str.equals(str_1)); // true
  • StringBuffer 与 StringBuilder
    StringBuffer 和 StringBulder 类表示的字符串对象可以直接进行修改,
    例如以下几个方法,String 类是没有的。
    StringBuffer sb = new StringBuffer("Hello");
    sb.append('a');                     // Helloa
    sb.insert(0, 'b');                  // bHelloa
    sb.deleteCharAt(sb.length() - 3);   // bHeloa
    System.out.println(sb.toString());

StringBuilder 是 JDK1.5 引入的,它和 StringBuffer 的方法完全相同,区别在于它是在单线程环境下使用的,因为它的所有方面都没有被 synchronized 修饰,因此它的效率也比 StringBuffer 略高。

3,Collection,Collections

关于 Java 的集合类,牵扯比较广,我不多做拓展,只简单说一下,这 Collection,Collections 两个东西:

  • Collection
    java.util.Collection 是一个集合接口,是为各种具体的集合提供了最大化的统一操作方式,它下面有很多实现,比如常见的 List,Set 等;
  • Collections
    java.util.Collections 是一个包装类(工具类),它包含有各种有关集合操作的静态多态方法。该类不能实例化,详情见源码:
public class Collections {
    // Suppresses default constructor, ensuring non-instantiability.
    private Collections() {
    }
    ...
}

Collections 的常见/用方法有:sort(),swap(),reverse() 等。

4,Map,HashMap,HashTable
  • 直接看源码的话,Map 是没有显式的继承类的,但在 Java 中所有的类或接口都有共同的父类,即java.lang.Object 类,所以可以说,Map接口继承了 java.lang.Object 类,但没有实现任何接口;
  • Map 是和 Collection 同级别的接口 ;
  • HashMap 与 HashTable
    • 相同点

      1. 均基于哈希表实现的,每一个元素都是一个 key-value 对;
      2. 均实现了 Serializable 接口,因此支持序列化;
        相同点,我只了解到这里,下面的都是补充 = =)
        均实现了 Cloneable 接口,能被克隆;
      3. 其内部通过单链表解决冲突问题,容量不足(超过了阈值)时,会自动增长;
      4. 二者的存储结构和解决冲突的方法都是相同的。
    • 不同点

      1. Hashtable 中 key 和 value 都不允许为 null,而 HashMap 中 key 和 value 都允许为 null;
      2. HashMap 是非线程安全的,只是用于单线程环境下,Hashtable 则是线程安全的,能用于多线程环境中;
        不同点,我只了解到这里,下面的都是补充 = =)
        HashMap 若想在多线程环境下使用,可以采用 concurrent 并发包下的 concurrentHashMap,或者使用 Collections.synchronizedMap() 方法来获取一个线程安全的集合;
      3. 继承关系的不同;
        public class HashMap 
          extends AbstractMap
          implements Map, Cloneable, Serializable {}
      
        public class Hashtable 
          extends Dictionary 
          implements Map, Cloneable, Serializable {} 
      
      1. HashTable 在不指定容量的情况下的默认容量为 11,而 HashMap 为 16,Hashtable 不要求底层数组的容量一定要为 2 的整数次幂,而 HashMap 则要求一定为2的整数次幂;
      2. 关于 HashMap 中 key 和 value 都允许为 null,有更详细的解释和描述
        (key 只能有一个为 null,而 value 则可以有多个为 null),
        但是如果在 Hashtable 中有类似 put(null,null) 的操作,编译同样可以通过,因为 key 和 value 都是 Object 类型,但运行时会抛出 NullPointerException 异常,这是 JDK 的规范规定的;
      3. Hashtable 扩容时,将容量变为原来的 2 倍加 1,而 HashMap 扩容时,将容量变为原来的 2 倍;
      4. Hashtable 和 HashMap 都重新计算了 key 的 hash 值,Hashtable 在求 hash 值对应的位置索引时,用取模运算,而 HashMap 在求位置索引时,则用与运算,且这里一般先用 hash & 0x7FFFFFFF 后,再对 length 取模,& 0x7FFFFFFF 的目的是为了将负的 hash 值转化为正值,因为 hash 值有可能为负数,而 & 0x7FFFFFFF 后,只有符号外改变,而后面的位都不变。
5,接口和抽象类
  • 相同点
    1. 抽象类和接口都不能够实例化,但可以定义抽象类和接口类型的引用。
  • 不同点
    1. 抽象类中可以定义构造器,可以有抽象方法和具体方法;而接口中不能定义构造器而且其中的方法全部都是抽象方法;
    2. 抽象类中的成员可以是 Private、默认、Protected 、Public 的,而接口中的成员全都是 public 的;
    3. 抽象类中可以定义成员变量,而接口中定义的成员变量实际上都是常量。
  • 稍微总结和多说两句
    1. 一个类如果继承了某个抽象类或者实现了某个接口,需要对其中的抽象方法全部进行实现,否则该类仍然需要被声明为抽象类;
    2. 有抽象方法的类必须被声明为抽象类,而抽象类未必要有抽象方法。
6,线程同步

这个问题,我第一反应就是,Android 里的 Handler 啊,runOnUiThread() 什么的,结果面试官强调,是 Java 中 线程同步的方法 = =
这个我了解的不多,找到一些资料,总结如下:

  • 利用 Synchronized 关键字:同步关键字,或者同步代码块,这个比较简单,基本都用到过;
  • 利用 Wait() 与 Notify(),只见过,没用过,更没有阅读过相关源码; = =
    • wait():使一个线程处于等待状态,并且释放所持有的对象的lock。
    • sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。
    • notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。
    • notifyAll():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。
  • 利用特殊域变量 (volatile) 实现线程同步, 这个更不知道,只在单例模式中见到过;
  • 利用 Lock 接口及其实现类 ReentrantLock(重入锁),实现线程同步。
  • 利用 ThreadLocal,它是一个方便解决多线程并发问题的工具类。

以上就是我整理出的,一些常见的方法,肯定不全,而且我自己都没用过几个,这几天,好好研究一下,觉得自己好菜 = = !!

后记

没想到,光是 Java 的问题,就总结了这么长,关于集合的知识点,知道的太少,看来得好好巩固了 ,不然实在是找不到工作啊 = =
额,Android 部分的估计就更长了,就此分篇,详见下一篇吧。

你可能感兴趣的:([Android] 面试题总结-猎趣-Java部分)