三月,想必各位程序员GG 此刻想要的,莫过于一篇面试宝典。鄙人不才,也从未系统的刷过面试题,以鸿洋大哥一篇面试题为引,略加整理,希望能帮到各位不知道看什么小伙伴。(大部分知识都是有答案的).
原文链接: [https://mp.weixin.qq.com/s/p3l9wr4DX976Lr62-dYe8w(只有题)]
PS:其实在推送2天后就已经全部 总结出来了,只是新的优快云 编辑器排版不会用,也没时间来整理,终于有时间了,把格式排版下。(另外有些题我没去搜索,是因为我觉得那些问的比较少,而且我对那些问题从未听过,之后再进阶的时候在回来看看,并在更新,现在的这些我觉得足够复习了。)
主要分为以下几部分:
(1)java面试题
(2)Android面试题
(3)高端技术面试题
(4)非技术性问题&HR问题汇总
1.java面试题
熟练掌握java是很关键的,大公司不仅仅要求你会使用几个api,更多的是要你熟悉源码实现原理,甚至要你知道有哪些不足,怎么改进,还有一些java有关的一些算法,设计模式等等。
(一) java基础面试知识点
- java中==和equals和hashCode的区别
基本数据类型(int,char,long,boolean等)使用 = =,引用字符类型使用 equals,hashmap中,key.hashCode 出值先进行值对比,在进行equals 对比,然后在确定是否加入HashMap - int、char、long各占多少字节数
Int -4,char -2,float: 4个字节 double: 8个字节 long: 8个字节,short-2 - int与integer的区别
1、Integer是int的包装类,int则是java的一种基本数据类型
2、Integer变量必须实例化后才能使用,而int变量不需要
3、Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值
4、Integer的默认值是null,int的默认值是0 - 探探对java多态的理解
面向对象的三大基本特征:封装、继承、多态
1.(封装)这个封装其实就是面向对象语言的精髓,在这里一些都是对象,我们通过封装,只为用户提供接口,而隐藏了内部的具体实现,比如插班。不用考虑内部,我们只需要插统一的一个插口,就是调用这个接口。
2.(继承)
①提高了代码的复用性。
②类与类之间产生了关系,关系的出现
③对父类功能进行重定义.
3.(多态)同一消息可以根据发送对象的不同而采用多种不同的行为方式,父类引用指向子类对象,在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。用我的话就是智能判断你需要调用的方法! - String、StringBuffer、StringBuilder区别
String 字符串常量
StringBuffer 字符串变量(线程安全)
StringBuilder 字符串变量(非线程安全)
1.String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象, 因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,所以经常改变内容的字符串最好不要用 String,因为每次生成对象都会对系统性能产生影响.而如果是使用 StringBuffer 类则结果就不一样了,每次结果都会对 StringBuffer 对象本身进行操作,而不是生成新的对象,再改变对象引用
2.有时候你会很惊讶的发现,生成 String S1 对象的速度简直太快了,而这个时候 StringBuffer 居然速度上根本一点都不占优势。其实这是 JVM 的一个把戏,在 JVM 眼里,这个
String S1 = “This is only a” + “ simple” + “test”; 其实就是:
String S1 = “This is only a simple test”; 所以当然不需要太多的时间了
3.在大部分情况下 StringBuilder > StringBuffer ,不保证同步, - 什么是内部类?内部类的作用
-
1.内部类可以直接访问外部类中的成员(包括私有成员),
而外部类要访问内部类中的成员必须要建立内部类的对象
2.内部类可分为静态内部类,动态内部类,匿名类
静态内部类:性质上和外部类没什么区别,只是形式上是写在另外一个类里面,通过另外一个类名访问。所以一个类应不应该被写为静态内部类纯看个人喜好。对我来说,一个类满足以下条件时我就把它写为静态内部类:
① 类的代码量不多,可能只是定义一个结构体,一个接口,或者一丁点逻辑代码
② 类只起到辅助作用,一般不单独拿出来用
③. 和它所在的类关系非常紧密,以至于它的修改一般都是由它所在类引起动态内部类:
动态内部类实例化之后会保留它所在类的实例,所以内部类可以访问它所在类的动态属性。动态内部类可以模拟多继承。
匿名内部类其实也是一种动态内部类,只是没有类名。 - 抽象类和接口区别
抽象类的意义
利于代码的维护和重用,我目前使用最频繁的就是BaseActivity 基类。(初始化调用状态栏的方法)以及网络请求中的错误类型判断。 - 抽象类与接口的应用场景
①interface的应用场合
A. 类与类之前需要特定的接口进行协调,而不在乎其如何实现。(比如jni中的调用)
B. 作为能够实现特定功能的标识存在,也可以是什么接口方法都没有的纯粹标识。
C. 需要将一组类视为单一的类,而调用者只通过接口来与这组类发生联系。
D. 需要实现特定的多项功能,而这些功能之间可能完全没有任何联系。
②abstract class的应用场合
一句话,在既需要统一的接口,又需要实例变量或缺省的方法的情况下,就可以使用它。最常见的有:
A. 定义了一组接口,但又不想强迫每个实现类都必须实现所有的接口。可以用abstract class定义一组方法体,甚至可以是空方法体,然后由子类选择自己所感兴趣的方法来覆盖。
B. 某些场合下,只靠纯粹的接口不能满足类与类之间的协调,还必需类中表示状态的变量来区别不同的关系。abstract的中介作用可以很好地满足这一点。
C. 规范了一组相互协调的方法,其中一些方法是共同的,与状态无关的,可以共享的,无需子类分别实现;而另一些方法却需要各个子类根据自己特定的状态来实现特定的功能 - 抽象类是否可以没有方法和属性?
?????? (作者偷懒没去总结) - 接口的意义
功能的抽象,通过接口可以实现不相关类的相同行为,而不需要了解对象所对应的类。
通过接口可以指明多个类需要实现的方法。 - 父类的静态方法能否被子类重写
来说一下我的观点,父类的静态方法不能被子类继承,更谈不上重写,就算是子类中有一个和父类一模一样的静态方法,那也是子类本身的,和父类的那个静态方法不是一回事。方法加静态后就属于类不属于对象了。 - 进程和线程的区别
1)进程是资源的分配和调度的一个独立单元,而线程是CPU调度的基本单元
2)同一个进程中可以包括多个线程,并且线程共享整个进程的资源(寄存器、堆栈、上下文),一个进行至少包括一个线程。
3)线程是轻量级的进程,它的创建和销毁所需要的时间比进程小很多,所有操作系统中的执行功能都是创建线程去完成的
5)线程中执行时一般都要进行同步和互斥,因为他们共享同一进程的所有资源
6)线程有自己的私有属性TCB,线程id,寄存器、硬件上下文,而进程也有自己的私有属性进程控制块PCB,这些私有属性是不被共享的,用来标示一个进程或一个线程的标志 - final,finally,finalize的区别
1)Final用于修饰类、成员变量和成员方法。final修饰的类,不能被继承(String、StringBuilder、StringBuffer、Math,不可变类),其中所有的方法都不能被重写,所以不能同时用abstract和final修饰类
2)Finally通常和try catch搭配使用,保证不管有没有发生异常,资源都能够被释放(释放连接、关闭IO流)。
3)Finalize是object类中的一个方法,子类可以重写finalize()方法实现对资源的回收。垃圾*回收只负责回收内存,并不负责资源的回收,资源回收要由程序员完成,Java虚拟机在垃圾回收之前会先调用垃圾对象的finalize方法用于使对象释放资源(如关闭连接、关闭文件),之后才进行垃圾回收。 - 序列化的方式
1)有的时候我们想要把一个Java对象变成字节流的形式传出去,有的时候我们想要从一个字节流中恢复一个Java对象。
2)我常用Serializable接口 实现序列化,在传递Bean 数据包时候 - Serializable 和Parcelable 的区别
1)Serializable的作用是为了保存对象的属性到本地文件、数据库、网络流、rmi以方便数据传输,当然这种传输可以是程序内的也可以是两个程序间的。而Android的Parcelable的设计初衷是因为Serializable效率过慢
2)Parcelable的性能比Serializable好,在内存开销方面较小,所以在内存间数据传输时推荐使用Parcelable,如activity间传输数据,而Serializable可将数据持久化方便保存,所以在需要保存或网络传输数据时选择Serializable - 静态属性和静态方法是否可以被继承?是否可以被重写?以及原因?
一旦静态,就不属于对象了,因此不存在重新和继承 - 静态内部类的设计意图
?????? (作者偷懒没去总结) - 成员内部类
成员内部类也是最普通的内部类,它是外围类的一个成员,所以它可以无限制的访问外围类的所有成员属性和方法,尽管是private的,但是外围类要访问内部类的成员属性和方法则需要通过内部类实例来访问。
在成员内部类中要注意两点:
1)成员内部类中不能存在任何static的变量和方法;
2)成员内部类是依附于外围类的,所以只有先创建了外围类才能够创建内部类。 - 静态内部类
**静态内部类与非静态内部类之间存在一个最大的区别:非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围内,但是静态内部类却没有。
没有这个引用就意味着:
它的创建是不需要依赖于外围类的。
它不能使用任何外围类的非static成员变量和方法。** - 成员内部类、静态内部类、局部内部类和匿名内部类的理解,以及项目中的应用
?????? (作者偷懒没去总结) - 谈谈对kotlin的理解
2017年谷歌I/O大会的最后,谷歌宣布将Kotlin语言作为安卓开发的一级编程语言
Kotlin程序可以使用所有现有的Java框架和库,也就是说所有的现有程序不需要更改就可以直接被调用。
Kotlin可以轻松学习,平易近人。它的规则及其简单,语法规则少,易于学习。
Kotlin是开放源码,没有收费。虽然java也是开源语言,但是相比于其他的非开源的还是有一定优势的。
Kotlin的空安全性很好,使用Kotlin,你可以用更少的代码获得更多的功能。 而你写的代码越少,你犯的错误就越少。 - 闭包和局部内部类的区别
1)因为Java不支持多继承,支持实现多个接口。但有时候会存在一些使用接口很难解决的问题,这个时候我们可以利用内部类提供的、可以继承多个具体的或者抽象的类的能力来解决这些程序设计问题。可以这样说,接口只是解决了部分问题,而内部类使得多重继承的解决方案变得更加完整。
2)局部内部类是嵌套在方法和作用域内的,对于这个类的使用主要是应用与解决比较复杂的问题,想创建一个类来辅助我们的解决方案,到那时又不希望这个类是公共可用的,所以就产生了局部内部类,局部内部类和成员内部类一样被编译,只是它的作用域发生了改变,它只能在该方法和属性中被使用,出了该方法和属性就会失效。 - string 转换成 integer的方式及原理
1)静态方法Integer.toString(intvalues),
2)成员方法 a.toString();
3.)String.valueof(a)
4)String转integer 需要先进行非空判断,Integer.valueOf(str);
(二) java深入源码级的面试题(有难度)
- 哪些情况下的对象会被垃圾回收机制处理掉?
1) 没有被引用的
2)当内存占用过多,弱引用
3)垃圾回收他是在虚拟机空闲的时候或者内存紧张的时候执行的,什么时候回收不是由程序员来控制的,需要被回收的时候并不会马上被回收,而是将其放入到一个准备回收的队列,去执行finalize方法。这也就是java比较耗内存的原因之一。 - 讲一下常见编码方式?
在studio的右下角 有一个编码选择,通常有GBK,utf-8,utf-16,ASCII码
utf-8编码中的中文占几个字节;int型几个字节?
UTF-8不是固定字长编码的,而是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。
我在Notepad++ 测试 长度 为 3字节, - 静态代理和动态代理的区别,什么场景使用
1)为某个对象提供一个代理,以控制对这个对象的访问。代理类负责请求的预处理、过滤、将请求分派给委托类处理、以及委托类执行完请求后的后续处理。
2)所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。 - Java的异常体系
1) Thorwable类所有异常和错误的超类,有两个子类Error和Exception,分别表示错误和异常。
其中异常类Exception又分为运行时异常(RuntimeException)和非运行时异常,
2) Error是程序无法处理的错误,比如OutOfMemoryError、 Exception是程序本身可以处理的异常,这种异常分两大类运行时异常和非运行时异常。
3) 运行时异常都是RuntimeException类及其子类异常,如NullPointerException、IndexOutOfBoundsException等, 这些异常是不检查异常,非运行时异常是RuntimeException以外的异常,类型上都属于Exception类及其子类。IOException,JSONException 从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。 - 谈谈你对解析与分派的认识。
?????? (作者偷懒没去总结) - 修改对象A的equals方法的签名,那么使用HashMap存放这个对象实例的时候,会调用哪个equals方法
??????? (作者偷懒没去总结) - Java中实现多态的机制是什么?
多态自我理解就是多种状态!
多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。 - 如何将一个Java对象序列化到文件里?
?????? (作者偷懒没去总结) - 说说你对Java反射的理解
1)最简单的一句话来说就是:反射就是把Java类中的各种成分映射成相应的Java类。类中有什么信息,它就可以获得什么信息,不过前提是得知道类的名字,要不就没有后文了
2)JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。 - 说说你对Java注解的理解
首先先说下我接触过的2种注解,一种是xUtils框架另外一种是ButterKnife(黄油刀),黄油刀注解真的很方便,对于需要一次性初始化大量的view 尤为方便。另外也可以对监听方法进行注解,xUtils只是了解,ButterKnife也只是使用了一段时间,后来在工作中,老大告诉我但凡是注解都是基于反射机制,影响性能,还是最原始的方法最好。现在常用的注解就是 @Deprecated 用来处理废弃代码,其他没深入了解. - 说说你对依赖注入的理解
本文从开头到现在提到的一系列依赖,只要不是由内部生产(比如初始化、构造函数 __construct 中通过工厂方法、自行手动 new 的),而是由外部以参数或其他形式注入的,都属于依赖注入(DI) 。 - 说一下泛型原理,并举例说明
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。提高代码的复用 - Java中String的了解
1)String类是final类,也即意味着String类不能被继承,并且它的成员方法都默认为final方法。
2)从源码中可以看出String类其实是通过char数组来保存字符串的。
3)“String对象一旦被创建就是固定不变的了,对String对象的任何改变都不影响到原对象,相关的任何change操作都会生成新的对象”。 - String为什么要设计成不可变的?
1) 假若字符串对象允许改变,那么将会导致各种逻辑错误,比如改变一个对象会影响到另一个独立对象.
2) String被许多的Java类(库)用来当做参数,例如 网络连接地址URL,文件路径path,还有反射机制所需要的String参数等, 假若String不是固定不变的,将会引起各种安全隐患。 - Object类的equal和hashCode方法重写,为什么?
1)hashCode的意思就是散列码,也就是哈希码,散列码是没有规律的,如果x与y是两个不同的对象,那么x.hashCode()与y.hashCode()基本是不会相同的
2)因为Object对象只与自身相等,所以同一个对象的地址总是相等的,计算取得的哈希码也必然相等,对于不同的对象,由于地址不同,所获取的哈希码自然也不会相等。因此到这里我们就明白了,两个对象通过调用equals方法是相等的,那么这两个对象调用hashCode方法必须返回相同的整数。
(三) 数据结构
- 常用数据结构简介
1)数据的逻辑结构
集合,线性结构,树形结构,图形结构
2)数据的物理结构 :指数据的逻辑结构在计算机存储空间的存放形式。
有顺序、链接、索引、散列等多种,常用的有顺序存储结构和链式存储结构。顺序映像借助元素在存储器中的相对位置来表示数据元素之间的逻辑关系。非顺序映像借助指示元素存储位置的指针(pointer)来表示数据元素之间的逻辑关系 - 并发集合了解哪些?
在新增的Concurrent包中,BlockingQueue(阻塞队列)很好的解决了多线程中,如何高效安全“传输”数据的问题。通过这些高效并且线程安全的队列类,为我们快速搭建高质量的多线程程序带来极大的便利。
1) 阻塞列表,使用LinkedBlockingDeque类。
2) 用在生产者与消费者数据的阻塞列表,使用LinkedTransferQueue类。
3) 使用优先级排序元素的阻塞列表,使用PriorityBlockingQueue类。
4) 存储延迟元素的阻塞列表,使用DelayQueue类 - 列举java的集合以及集合之间的继承关系
?????? (作者偷懒没去总结) - 集合类以及集合框架
学习Java集合框架下大致可以分为如下五个部分:List列表、Set集合、Map映射、迭代器(Iterator、Enumeration)、工具类(Arrays、Collections)。
容器类介绍以及之间的区别
(容器类估计很多人没听这个词,Java容器主要可以划分为4个部分:List列表、Set集合、Map映射、工具类(Iterator迭代器、Enumeration枚举类、Arrays和Collections),具体的可以看看这篇博文 Java容器类 http://alexyyek.github.io/2015/04/06/Collection/)
List,Set,Map的区别
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;
Map
1.Map不是collection的子接口或者实现类。Map是一个接口。
2.Map 的 每个 Entry 都持有两个对象,也就是一个键一个值,Map 可能会持有相同的值对象但键对象必须是唯一的。
3.TreeMap 也通过 Comparator 或者 Comparable 维护了一个排序顺序。
4.Map 里你可以拥有随意个 null 值但最多只能有一个 null 键。
5.Map 接口最流行的几个实现类是 HashMap、LinkedHashMap、 TreeMap。
(HashMap、TreeMap最常用) - List和Map的实现方式以及存储方式
1)List(有序,可重复)
A:Map集合的数据结构仅仅针对键有效,与值无关。
B:存储的是键值对形式的元素,键唯一,值可重复。
2)HashMap也用到了哈希码的算法,以便快速查找一个键, - HashMap的实现原理
HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。
存储:当我们往HashMap中put元素的时候,先根据key的hashCode重新计算hash值,根据hash值得到这个元素在数组中的位置(即下标), 如果数组该位置上已经存放有其他元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。如果数组该位置上没有元素,就直接将该元素放到此数组中的该位置上。
读取:从HashMap中get元素时,首先计算key的hashCode,找到数组中对应位置的某一元素,然后通过key的equals方法在对应位置的链表中找到需要的元素。 - HashMap数据结构?
?????? (作者偷懒没去总结) - HashMap源码理解
?????? (作者偷懒没去总结) - HashMap如何put数据(从HashMap源码角度讲解)?
存储:当我们往HashMap中put元素的时候,先根据key的hashCode重新计算hash值,根据hash值得到这个元素在数组中的位置(即下标), 如果数组该位置上已经存放有其他元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。如果数组该位置上没有元素,就直接将该元素放到此数组中的该位置上。 - ConcurrentHashMap的实现原理
?????? (作者偷懒没去总结) - ArrayMap和HashMap的对比
HashMap内部是使用一个默认容量为16的数组来存储数据的,而数组中每一个元素却又是一个链表的头结点,所以,更准确的来说,HashMap内部存储结构是使用哈希表的拉链结构(数组+链表),这种存储数据的方法叫做拉链法
假设数据量都在千级以内的情况下:
1、数据量不大,最好在千级以内,数据结构类型为Map类型
2、如果key类型为其它的类型,则使用ArrayMap - HashTable实现原理
以淘汰 - TreeMap具体实现
TreeMap是Java内部实现比较复杂的集合类之一。与HashMap不一样,TreeMap的底层不是用哈希表实现的,而是用红黑树实现的。当需要查找的元素是排好序的,TreeMap的优势就体现出来了。<1>每个节点的颜色是红色或黑色。<2>根节点必须是黑色的。<3>每个叶节点是黑色的(叶节点是指树尾端的NULL节点)。<4>如果一个节点是红色的,那么它的子节点必须是黑色。即,不能有连续的红色节点。<5>对于任意一个节点,从它到叶节点的每条路径包含相同数量的黑色节点。 - HashMap和HashTable的区别
以淘汰 - HashMap与HashSet的区别
1)HashSet实现了Set接口,它不允许集合中有重复的值,当我们提到HashSet时,第一件事情就是在将对象存储在HashSet之前,要先确保对象重写equals()和hashCode()方法,这样才能比较对象的值是否相等,以确保set中没有储存相等的对象。如果我们没有重写这两个方法,将会使用这个方法的默认实现。
2)HashMap实现了Map接口,Map接口对键值对进行映射。Map中不允许重复的键。Map接口有两个基本的实现,HashMap和TreeMap。TreeMap保存了对象的排列次序,而HashMap则不能。HashMap允许键和值为null。HashMap是非synchronized的,但collection框架提供方法能保证HashMap synchronized,这样多个线程同时访问HashMap时,能保证只有一个线程更改Map。 - HashSet与HashMap怎么判断集合元素重复?
其实HashSet 只是实现了HashMap 的 key部分,都是无序的存储形势
1)HashSet不能添加重复的元素,当调用add(Object)方法时候,
首先会调用Object的hashCode方法判hashCode是否已经存在,如不存在则直接插入元素;
如果已存在则调用Object对象的equals方法判断是否返回true,如果为true则说明元素已经存在,如为false则插入元素。
2)HashMap中判断元素是否相同主要有两个方法,一个是判断key是否相同,一个是判断value是否相同。那就是首先得hashCode相同,然后根据key得到的 value 进行 equals 如果相同,新加入的放在链头,最先加入的放在链