Java基础面试知识点

Java基础面试知识点

一.容器(HashMap、HashSet、LinkedList、ArrayList、数组等)

1、数组
数组在内存存储方面的特点:
(1)数组初始化后,长度就确定了
(2)数组声明的类型,就决定了,进行元素初始化时的类型。
数组在存储数据方面的弊端
(1)数组初始化后,长度就不可改变了,不利于扩展
(2)数组中提供的属性和方法比较少,不便于进行添加、删除、插入等操作,且效率不高,同时无法直接获取存储元素的个数
(3)数组存储的数据是有序的、可重复的
2、Collection
Collection 接口

  • Collection 接口是 List、Set 和 Queue 接口的父接口,该接口里定义的方法
    既可用于操作 Set 集合,也可用于操作 List 和 Queue 集合。
  • JDK不提供此接口的任何直接实现,而是提供更具体的子接口(如:Set和List)
    实现。
  • 在 Java5 之前,Java 集合会丢失容器中所有对象的数据类型,把所有对象都
    当成 Object 类型处理;从 JDK 5.0 增加了 泛型以后,Java 集合可以记住容
    器中对象的数据类型

Collection 接口 :单列数据,定义了存取一组对象的方法的集合
① List:元素有序、可重复的集合
② Set: 元素无序、不可重复的集合
③Map 接口:双列数据,保存具有映射关系“key-value对”的集合

3、Iterator
使用 Iterator 接口遍历集合元素

  • Iterator对象称为迭代器(设计模式的一种),主要用于遍历 Collection 集合中的元素。
    GOF给迭代器模式的定义为:提供一种方法访问一个容器(container)对象中各个元
    素,而又不需暴露该对象的内部细节。 迭代器模式,就是为容器而生。
    类似于“公
    交车上的售票员”、“火车上的乘务员”、“空姐”。

  • Collection接口继承了java.lang.Iterable接口,该接口有一个iterator()方法,那么所
    有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了
    Iterator接口的对象。

  • Iterator 仅用于遍历集合,Iterator 本身并不提供承装对象的能力。如果需要创建
    Iterator 对象,则必须有一个被迭代的集合。

  • 集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合
    的第一个元素之前。
    注意:
    (1)Iterator可以删除集合的元素,但是是遍历过程中通过迭代器对象的remove方
    法,不是集合对象的remove方法。
    (2) 如果还未调用next()或在上一次调用 next 方法之后已经调用了 remove 方法,
    再调用remove都会报IllegalStateException。

    4、List
    (1) 鉴于Java中数组用来存储数据的局限性,我们通常使用List替代数组
    (2)List集合类中 元素有序、且可重复,集合中的每个元素都有其对应的顺序索引
    (3) List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据
    序号存取容器中的元素。
    (4) JDK API中List接口的实现类常用的有:ArrayList、LinkedList和Vector。
    ArrayList
    ArrayList:作为List接口的主要实现类;线程不安全的,效率高;底层使用Object[] elementData存储

  • ArrayList 是 List 接口的典型实现类、主要实现类

  • 本质上,ArrayList是对象引用的一个”变长”数组

    ArrayList 的JDK1.8 之前与之后的实现区别?
    JDK1.7:ArrayList像饿汉式,直接创建一个初始容量为10的数组
    JDK1.8:ArrayList像懒汉式,一开始创建一个长度为0的数组,当添加第一个元
    素时再创建一个始容量为10的数组
    ArrayList list = new ArrayList();//底层Object[] elementData初始化为{}.并没有创建长度为10的数组
    list.add(123);//第一次调用add()时,底层才创建了长度10的数组,并将数据123添加到elementData[0] (节省内存)
    如果此次的添加导致底层elementData数组容量不够,则扩容。 默认情况下,扩容为原来的容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。

  • Arrays.asList(…) 方法返回的 List 集合,既不是 ArrayList 实例,也不是
    Vector 实例。 Arrays.asList(…) 返回值是一个固定长度的 List 集合
    LinkedList
    LinkedList: 双向链表,内部没有声明数组,而是定义了Node类型的first和last,
    用于记录首末元素。同时,定义内部类Node,作为LinkedList中保存数据的基
    本结构。Node除了保存数据,还定义了两个变量:

  • prev变量记录前一个元素的位置

  • next变量记录下一个元素的位置
    Vector
    (1)Vector 是一个古老的集合,JDK1.0就有了。大多数操作与ArrayList
    相同,区别之处在于Vector是线程安全的。
    (2)在各种list中,最好把ArrayList作为缺省选择。当插入、删除频繁时,
    使用LinkedList;Vector总是比ArrayList慢,所以尽量避免使用

面试题:
请问ArrayList/LinkedList/Vector的异同?谈谈你的理解?ArrayList底层
是什么?扩容机制?Vector和ArrayList的最大区别?
ArrayList和LinkedList的异同
二者都线程不安全,相对线程安全的Vector,执行效率高。
此外,ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。对于
随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。对于新增
和删除操作add(特指插入)和remove,LinkedList比较占优势,因为ArrayList要移动数据。
ArrayList和Vector的区别
Vector和ArrayList几乎是完全相同的,唯一的区别在于Vector是同步类(synchronized),属于
强同步类。因此开销就比ArrayList要大,访问要慢。正常情况下,大多数的Java程序员使用
ArrayList而不是Vector,因为同步完全可以由程序员自己来控制。Vector每次扩容请求其大
小的2倍空间,而ArrayList是1.5倍。Vector还有一个子类Stack。
5、Set
Set接口:存储无序的、不可重复的数据 -->高中讲的“集合”

  • Set接口是Collection的子接口,set接口没有提供额外的方法
  • Set 集合不允许包含相同的元素,如果试把两个相同的元素加入同一个
    Set 集合中,则添加操作失败。
  • Set 判断两个对象是否相同不是使用 == 运算符,而是根据 equals() 方法
    HashSet
  • HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时都使用这个实现类。
  • HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存取、查找、删除
    性能。
    HashSet 具有以下特点:
  • 不能保证元素的排列顺序
  • HashSet 不是线程安全的
  • 集合元素可以是 null
  • HashSet 集合判断两个元素相等的标准:两个对象通过 hashCode() 方法比较相
    等,并且两个对象的 equals() 方法返回值也相等。
  • 对于存放在Set容器中的对象, 对应的类一定要重写equals() 和hashCode(Object
    obj) 方法,以实现对象相等规则 。即: “相等的对象必须具有相等的散列码” 。

一、Set:存储无序的、不可重复的数据
以HashSet为例说明:
1. 无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定的。
2. 不可重复性:保证添加的元素按照equals()判断时,不能返回true.即:相同的元素只能添加一个。
二、添加元素的过程:以HashSet为例:
我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,
此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置),判断数组此位置上是否已经有元素:如果此位置上没有其他元素,则元素a添加成功。 如果此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值: 如果hash值不相同,则元素a添加成功。如果hash值相同,进而需要调用元素a所在类的equals()方法:
equals()返回true,元素a添加失败
equals()返回false,则元素a添加成功。
对于添加成功的情况2和情况3而言:元素a 与已经存在指定索引位置上数据以链表的方式存储。
jdk 7 :元素a放到数组中,指向原来的元素。
jdk 8 :原来的元素在数组中,指向元素a
总结:七上八下
HashSet底层:数组+链表的结构。

LinkedHashSet

  • LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入顺序保存的。
  • LinkedHashSet插入性能略低于 HashSet,但在迭代访问 Set 里的全部元素时有很好的性能。
  • LinkedHashSet 不允许集合元素重复。
    优点:对于频繁的遍历操作,LinkedHashSet效率高于HashSet

TreeSet
TreeSet和后面要讲的TreeMap
采用红黑树的存储结构
特点:有序,查询速度比List快

1.向TreeSet中添加的数据,要求是相同类的对象。
2.两种排序方式:自然排序(实现Comparable接口) 和 定制排序(Comparator)
3.自然排序中,比较两个对象是否相同的标准为:compareTo()返回0.不再是equals().
4.定制排序中,比较两个对象是否相同的标准为:compare()返回0.不再是equals().

6、Map

  • Map与Collection并列存在。用于保存具有 映射关系的数据:key-value

  • Map 中的 key 和 value 都可以是任何引用类型的数据

  • Map 中的 key 用Set来存放, 不允许重复,即同一个 Map 对象所对应
    的类,须重写hashCode()和equals()方法

  • 常用String类作为Map的“键”

  • key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到
    唯一的、确定的 value

  • Map接口的常用实现类:HashMap、TreeMap、LinkedHashMap和
    Properties。其中,HashMap是 Map 接口使用频率最高的实现类
    HashMap
    HashMap:作为Map的主要实现类;线程不安全的,效率高;存储null的key和value

  • HashMap是 Map 接口 使用频率最高的实现类。

  • 允许使用null键和null值,与HashSet一样,不保证映射的顺序。

  • 所有的key构成的集合是Set:无序的、不可重复的。所以,key所在的类要重写:
    equals()和hashCode()

  • 所有的value构成的集合是Collection:无序的、可以重复的。所以,value所在的类
    要重写:equals()

  • 一个key-value构成一个entry

  • 所有的entry构成的集合是Set:无序的、不可重复的

  • HashMap 判断两个 key 相等的标准是:两个 key 通过 equals() 方法返回 true,
    hashCode 值也相等。

  • HashMap 判断两个 value 相等的标准是:两个 value 通过 equals() 方法返回 true。

HashMap的底层实现原理?以jdk7为例说明:
HashMap map = new HashMap():
在实例化以后,底层创建了长度是16的一维数组Entry[] table。 ...可能已经执行过多次put... map.put(key1,value1):
首先,调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法计算以后,得到在Entry数组中的存放位置。 如果此位置上的数据为空,此时的key1-value1添加成功。如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据(以链表形式存在)),比较key1和已经存在的一个或多个数据 的哈希值: 如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功。 如果key1的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同,继续比较:调用key1所在类的equals(key2)方法,比较:
如果equals()返回false:此时key1-value1添加成功。
如果equals()返回true:使用value1替换value2。
补充:关于情况2和情况3:此时key1-value1和原来的数据以链表的方式存储。
在不断的添加过程中,会涉及到扩容问题,当超出临界值(且要存放的位置非空)时,扩容。默认的扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来。

jdk8 相较于jdk7在底层实现方面的不同:

  1. new HashMap():底层没有创建一个长度为16的数组
  2. jdk 8底层的数组是:Node[],而非Entry[]
  3. 首次调用put()方法时,底层创建长度为16的数组
  4. jdk7底层结构只有:数组+链表。jdk8中底层结构:数组+链表+红黑树。
    4.1 形成链表时,七上八下(jdk7:新的元素指向旧的元素。jdk8:旧的元素指向新的元素)
    4.2 当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且当前数组的长度 > 64时,此时此索引位置上的所数据改为使用红黑树存储。
    DEFAULT_INITIAL_CAPACITY : HashMap的默认容量,16
    DEFAULT_LOAD_FACTOR:HashMap的默认加载因子:0.75
    threshold:扩容的临界值,=容量*填充因子:16 * 0.75 => 12
    TREEIFY_THRESHOLD:Bucket中链表长度大于该默认值,转化为红黑树:8
    MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量:64

LinkedHashMap

  • LinkedHashMap 是 HashMap 的子类
  • 在HashMap存储结构的基础上,使用了一对双向链表来记录添加
    元素的顺序
  • 与LinkedHashSet类似,LinkedHashMap 可以维护 Map 的迭代
    顺序:迭代顺序与 Key-Value 对的插入顺序一致
    优点:
    LinkedHashMap:保证在遍历map元素时,可以按照添加的顺序实现遍历。
    原因:
    在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素。
    对于频繁的遍历操作,此类执行效率高于HashMap。

TreeMap

  • TreeMap存储 Key-Value 对时,需要根据 key-value 对进行排序。
    TreeMap 可以保证所有的 Key-Value 对处于 有序状态。
  • TreeSet底层使用 红黑树结构存储数据
  • TreeMap 的 Key 的排序:
    1)自然排序:TreeMap 的所有的 Key 必须实现 Comparable 接口,而且所有
    的 Key 应该是同一个类的对象,否则将会抛出 ClasssCastException
    2)定制排序:创建 TreeMap 时,传入一个 Comparator 对象,该对象负责对
    TreeMap 中的所有 key 进行排序。此时不需要 Map 的 Key 实现
    Comparable 接口
  • TreeMap判断 两个key 相等的标准:两个key通过compareTo()方法或
    者compare()方法返回0。

Hashtable
Hashtable是个古老的 Map 实现类,JDK1.0就提供了。不同于HashMap,
Hashtable是线程安全的。

  • Hashtable实现原理和HashMap相同,功能相同。底层都使用哈希表结构,查询
    速度快,很多情况下可以互用。
  • 与HashMap不同,Hashtable 不允许使用 null 作为 key 和 value
  • 与HashMap一样,Hashtable 也不能保证其中 Key-Value 对的顺序
  • Hashtable判断两个key相等、两个value相等的标准,与HashMap一致

Properties

  • Properties 类是 Hashtable 的子类,该对象用于处理属性文件
  • 由于属性文件里的 key、value 都是字符串类型,所以 Properties 里的 key
    和 value 都是字符串类型
  • 存取数据时,建议使用setProperty(String key,String value)方法和
    getProperty(String key)方法

7、Collections

  • Collections 是一个操作 Set、List 和 Map 等集合的工具类
  • Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,
    还提供了对集合对象设置不可变、对集合对象实现同步控制等方法

8、SparseArray ,ArrayMap
优缺点及应用场景
SparseArray 优点:

通过它的三兄弟可以避免存取元素时的装箱和拆箱
频繁的插入删除操作效率高(延迟删除机制保证了效率)
会定期通过gc函数来清理内存,内存利用率高
放弃hash查找,使用二分查找,更轻量

SparseArray缺点:

二分查找的时间复杂度O(log n),大数据量的情况下,效率没有HashMap高
key只能是int 或者long

SparseArray应用场景:

item数量为 <1000级别的
存取的value为指定类型的,比如boolean、int、long,可以避免自动装箱和拆箱问题。

ArrayMap优点:

在数据量少时,内存利用率高,及时的空间压缩机制
迭代效率高,可以使用索引来迭代(keyAt()方法以及valueAt() 方法),相比于HashMap迭代使用迭代器模式,效率要高很多

ArrayMap缺点:

存取复杂度高,花费大
二分查找的O(log n )时间复杂度远远小于HashMap
ArrayMap没有实现Serializable,不利于在Android中借助Bundle传输。

ArrayMap应用场景:

item数量为 <1000 级别的,尤其是在查询多,插入数据和删除数据不频繁的情况
Map中包含子Map对象

如果觉得这些优缺点一大堆,还是很迷,我就再精简一下二者使用的取舍:

(1) 首先二者都是适用于数据量小的情况,但是SparseArray以及他的三兄弟们避免了自动装箱和拆箱问题,也就是说在特定场景下,比如你存储的value值全部是int类型,并且key也是int类型,那么就采用SparseArray,其它情况就采用ArrayMap。
(2) 数据量多的时候当然还是使用HashMap啦

需要了解其实现原理,还要灵活运用,如:自己实现 LinkedList、两个栈实现一个队列,数组实现栈,队列实现栈等。
HashMap、HashTable 和 CurrentHashMap 的核心区别(并发),其次内部数据结构的实现、扩容、存取操作,再深一点 哈希碰撞,哈希计算,哈希映射,为什么是头插法,扩容为什么是 2 的幂次等。

参考链接

JAVA容器-自问自答学HashMap
什么是HashMap?
从源码角度认识ArrayList,LinkedList与HashMap

面试题

Collection 和 Collections的区别

答:Collection是集合类的上级接口,继承于他的接口主要有Set 和List.
Collections是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作

Set里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用==还是equals()? 它们有何区别

答:Set里的元素是不能重复的,用equals()方法判读两个Set是否相等
equals()和==方法决定引用值是否指向同一对象equals()在类中被覆盖,为的是当两个分离的对象的内容和类型相配的话,返回真值

List, Set, Map是否继承自Collection接口

答: List,Set是,Map不是

两个对象值相同(x.equals(y) == true),但却可有不同的hash code,这句话对不对

答:不对,有相同的hash code

说出ArrayList,Vector, LinkedList的存储性能和特性

答:ArrayList和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,Vector由于使用了synchronized方法(线程安全),通常性能上较ArrayList差,而LinkedList使用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。

HashMap和Hashtable的区别

答:
1.HashMap与Hashtable都实现了Map接口。由于HashMap的非线程安全性,效率上可能高于Hashtable。Hashtable的方法是Synchronize的,而HashMap不是,在多个线程访问Hashtable时,不需要自己为它的方法实现同步,而HashMap 就必须为之提供外同步。

  1. HashMap允许将null作为一个entry的key或者value,而Hashtable不允许
    3.HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因为contains方法容易让人引起误解。
    4.Hashtable继承自Dictionary类,而HashMap是Java1.2引进的Map interface的一个实现。
    5.Hashtable和HashMap采用的hash/rehash算法都大概一样,所以性能不会有很大的差异。
ArrayList和Vector的区别

答:就ArrayList与Vector主要从二方面来说.
一.同步性:Vector是线程安全的,也就是说是同步的,而ArrayList是线程序不安全的,不是同步的
二.数据增长:当需要增长时,Vector默认增长为原来一培,而ArrayList却是原来的一半

二. 内存模型

参考链接

理解Java内存模型
你了解Java内存模型么(Java7、8、9内存模型的区别)

三. 垃圾回收算法(JVM)

JVM 类加载机制、垃圾回收算法对比、Java 虚拟机结构

当你讲到分代回收算法的时候,不免会被追问到新生对象是怎么从年轻代到老年代的,
以及可以作为 root 结点的对象有哪些两个问题。

垃圾回收算法

1)标记-清除算法:首先标记出所有需要回收的对象,然后进行统一的回收,不足之处有两个:效率低、碎片多。

2)复制算法:将可用内存划分成大小相等的两块,每次只使用一块,当一块用完了,就将还存活的对象复制到另外一块上,然后把已使用的内存空间清理掉。不足之处是将内存缩小到一半,利用率不高。

3)标记-整理算法:与标记-清除类似,但后续步骤不是直接对可回收对象进行清理,而是让所有存活对象都向一端移动,然后直接清理掉端边界以外的区域

4)分代收集算法:分代收集是目前jvm普遍采用的算法,即新生代采用复制算法,因为有大量新生对象死去,只有少量存活;老年代采用标记-整理,因为老年代中对象存活率高,没有额外的空间对它进行担保。

1、谈谈对 JVM 的理解?
JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。
作用
Java中的所有类,必须被装载到jvm中才能运行,这个装载工作是由jvm中的类装载器完成的,类装载器所做的工作实质是把类文件从硬盘读取到内存中JVM对中央处理器(CPU)所执行的一种软件操作,用于执行编译过的Java程序码(Applet与应用程序)。

2、JVM 内存区域,开线程影响哪块区域内存?


image.png

3、对 Dalvik、ART 虚拟机有什么了解?对比?

ART 的机制与 Dalvik 不同。在Dalvik下,应用每次运行的时候,字节码都需要通过即时编译器(just in time ,JIT)转换为机器码,这会拖慢应用的运行效率,而在ART 环境中,应用在第一次安装的时候,字节码就会预先编译成机器码,极大的提高了程序的运行效率,同时减少了手机的耗电量,使其成为真正的本地应用。这个过程叫做预编译(AOT,Ahead-Of-Time)。这样的话,应用的启动(首次)和执行都会变得更加快速。

优点:

  • 系统性能的显著提升。
  • 应用启动更快、运行更快、体验更流畅、触感反馈更及时。
  • 更长的电池续航能力。
  • 支持更低的硬件。

缺点:

  • 机器码占用的存储空间更大,字节码变为机器码之后,可能会增加10%-20%(不过在应用包中,可执行的代码常常只是一部分。比如最新的 Google+ APK 是 28.3 MB,但是代码只有 6.9 MB。)
  • 应用的安装时间会变长。

4、垃圾回收机制和调用 System.gc()的区别?
垃圾回收机制:jvm不定时的自动回收不可达对象。
System.gc():提醒虚拟机,程序员希望你在这回收一下对象,而并不是一定会回收,要虚拟机有时间才会回收。

参考链接

Java虚拟机(JVM)你只要看这一篇就够了!

四、 类加载过程(需要多看看,重在理解,对于热修复和插件化比较重要)

image.png

五. 反射

反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 Java 语言的反射机制。

Class.form()
1. 描述一下JVM加载class文件的原理机制?
答:JVM中类的装载是由ClassLoader和它的子类来实现的,Java ClassLoader 是一个重要的Java运行时系统组件。它负责在运行时查找和装入类文件的类。

六. 多线程和线程池

1、基本 概念 : 程序、进程、线程

  • 程序(program)是为完成特定任务、用某种语言编写的一组指令的集合。即指一
    段静态的代码,静态对象。
  • 进程(process)是程序的一次执行过程,或是正在运行的一个程序。是一个动态
    的过程:有它自身的产生、存在和消亡的过程。——生命周期

如:运行中的QQ,运行中的MP3播放器
程序是静态的,进程是动态的
进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域

  • 线程(thread),进程可进一步细化为线程,是一个程序内部的一条执行路径。

    若一个进程同一时间 并行执行多个线程,就是支持多线程的
    线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小
    一个进程中的多个线程共享相同的内存单元/内存地址空间->它们从同一堆中分配对象,可以访问相同的变量和对象。这就使得线程间通信更简便、高效。但多个线程操作共享的系统资源可能就会带来安全的隐患。

  • 单核CPU 和多核CPU 的理解
    1)单核CPU,其实是一种假的多线程,因为在一个时间单元内,也只能执行一个线程的任务。
    2)如果是多核的话,才能更好的发挥多线程的效率。(现在的服务器都是多核的)
    3)一个Java应用程序java.exe,其实至少有三个线程:main()主线程,gc()垃圾回收线程,异常处理线程。当然如果发生异常,会影响主线程。

  • 并行与并发
    并行:多个CPU同时执行多个任务。比如:多个人同时做不同的事。
    并发:一个CPU(采用时间片)同时执行多个任务。比如:秒杀、多个人做同一件事。

  • 多线程程序的优点:

  1. 提高应用程序的响应。对图形化界面更有意义,可增强用户体验。
  2. 提高计算机系统CPU的利用率
  3. 改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理解和
    修改

线程有哪些状态

图片.png

JDK 中用Thread.State 类定义了 线程的几种状态
要想实现多线程,必须在主线程中创建新的线程对象。Java语言使用Thread类
及其子类的对象来表示线程,在它的一个完整的生命周期中通常要经历如下的 五
种状态:

  • 新建: 当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态
  • 就绪:处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件,只是没分配到CPU资源
  • 运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态, run()方法定义了线程的操作和功能
  • 阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出 CPU 并临时中止自己的执行,进入阻塞状态
  • 死亡:线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束
    创建线程的方式
    1)继承Thread类
    2)实现Runnable 接口
    3)实现Callable接口
    4)使用线程池
    涉及到的方法:
  • 1、
    wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。
    notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的那个。
    notifyAll():一旦执行此方法,就会唤醒所有被wait的线程。
  • 说明:
    1)wait(),notify(),notifyAll()三个方法必须使用在同步代码块或同步方法中。
    2)wait(),notify(),notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器。 否则,会出现IllegalMonitorStateException异常
    3)wait(),notify(),notifyAll()三个方法是定义在java.lang.Object类中。
  • 2 、sleep(long millis) :(指定时间:毫秒)
    令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行,时间到后
    重排队。抛出InterruptedException异常
  • 3、yield(): 线程让步
    暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程
    若队列中没有同优先级的线程,忽略此方法
    4、join()
      join()方法会使当前线程等待调用join()方法的线程结束后才能继续执行

synchronized 的锁是什么 ?
任意对象都可以作为同步锁。所有对象都自动含有单一的锁(监视器)。
 同步方法的锁:静态方法(类名.class)、非静态方法(this)
 同步代码块:自己指定,很多时候也是指定为this或类名.class

  • 注意:
     必须确保使用同一个资源的 多个线程共用一把锁,这个非常重要,否则就
    无法保证共享资源的安全
     一个线程类中的所有静态方法共用同一把锁(类名.class),所有非静态方
    法共用同一把锁(this),同步代码块(指定需谨慎)

线程的 死锁问题

  • 死锁
    不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃
    自己需要的同步资源,就形成了线程的死锁
    出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于
    阻塞状态,无法继续
  • 解决方法
    专门的算法、原则
    尽量减少同步资源的定义
    尽量避免嵌套同步

面试题:sleep() 和 wait()的异同?

  • 1.相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态。
  • 2.不同点:
    1)两个方法声明的位置不同:Thread类中声明sleep() , Object类中声明wait()
    2)调用的要求不同:sleep()可以在任何需要的场景下调用。 wait()必须使用在同步代码块或同步方法中
    3)关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,wait()会释放锁。

synchronized 和 volatile
(1)、volatile只能作用于变量,使用范围较小。synchronized可以用在方法、类、同步代码块等,使用范围比较广。 (要说明的是,java里不能直接使用synchronized声明一个变量,而是使用synchronized去修饰一个代码块或一个方法或类。)
(2)、volatile只能保证可见性和有序性,不能保证原子性。而可见性、有序性、原子性synchronized都可以保证。
(3)、volatile不会造成线程阻塞。synchronized可能会造成线程阻塞。

synchronized 与 Lock的异同

  • 相同:二者都可以解决线程安全问题
  • 不同:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器
  • Lock需要手动的启动同步(lock()),同时结束同步也需要手动的实现(unlock())
    优先使用顺序:
    Lock -> 同步代码块(已经进入了方法体,分配了相应资源) -> 同步方法
    (在方法体之外)

synchronized 修饰实例方法和修饰静态方法有啥不一样。
synchronized修饰不加static的方法,锁是加在单个对象上,不同的对象没有竞争关系;修饰加了static的方法,锁是加载类上,这个类所有的对象竞争一把锁。

哪些锁,各种锁的区别
【1】公平锁和非公平锁。
公平锁:是指按照申请锁的顺序来获取锁,
非公平所:线程获取锁的顺序不一定按照申请锁的顺序来的。

//默认是不公平锁,传入true为公平锁,否则为非公平锁
ReentrantLock reentrantLock =  new ReetrantLock();

【2】共享锁和独享锁
独享锁:一次只能被一个线程所访问
共享锁:线程可以被多个线程所持有。
ReadWriteLock 读锁是共享锁,写锁是独享锁。
【3】乐观锁和悲观锁。
乐观锁:对于一个数据的操作并发,是不会发生修改的。在更新数据的时候,会尝试采用更新,不断重入的方式,更新数据。
悲观锁:对于同一个数据的并发操作,是一定会发生修改的。因此对于同一个数据的并发操作,悲观锁采用加锁的形式。悲观锁认为,不加锁的操作一定会出问题,
【4】分段锁
1.7及之前的concurrenthashmap。并发操作就是分段锁,其思想就是让锁的粒度变小。
【5】偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价
轻量级锁
重量级锁
【6】自旋锁
自旋锁

七. 计算机网络部分

TCP 有哪些状态

图片.png

TCP 和 和 UDP
TCP 协议:
 使用TCP协议前,须先建立TCP连接,形成传输数据通道
 传输前,采用“ 三次握手”方式,点对点通信,是可靠的
 TCP协议进行通信的两个应用进程:客户端、服务端。
 在连接中可进行大数据量的传输
 传输完毕,需释放已建立的连接,效率低
UDP 协议:
 将数据、源、目的封装成数据包,不需要建立连接
 每个数据报的大小限制在64K内
 发送不管对方是否准备好,接收方收到也不确认,故是不可靠的
 可以广播发送
 发送数据结束时无需释放资源,开销小,速度快
HTTP、HTTPS、TCP/IP、Socket通信、三次握手四次挥手过程

三次握手、四次挥手。为啥不是三次不是两次
三次握手

图片.png

我们假设A和B是通信的双方。我理解的握手实际上就是通信,发一次信息就是进行一次握手。

第一次握手: A给B打电话说,你可以听到我说话吗?
第二次握手: B收到了A的信息,然后对A说: 我可以听得到你说话啊,你能听得到我说话吗?  
第三次握手: A收到了B的信息,然后说可以的,我要给你发信息啦!

注意: HTTP是基于TCP协议的,所以每次都是客户端发送请求,服务器应答,但是TCP还可以给其他应用层提供服务,即可能A、B在建立链接之后,谁都可能先开始通信。如果两次,那么B无法确定B的信息A是否能收到,所以如果B先说话,可能后面的A都收不到,会出现问题 。如果四次,那么就造成了浪费,因为在三次结束之后,就已经可以保证A可以给B发信息,A可以收到B的信息; B可以给A发信息,B可以收到A的信息。

四次挥手

图片.png

A:“喂,我不说了 (FIN)。”A->FIN_WAIT1
B:“我知道了(ACK)。等下,上一句还没说完。Balabala…..(传输数据)”B->CLOSE_WAIT | A->FIN_WAIT2
B:”好了,说完了,我也不说了(FIN)。”B->LAST_ACK
A:”我知道了(ACK)。”A->TIME_WAIT | B->CLOSED
A等待2MSL,保证B收到了消息,否则重说一次”我知道了”,A->CLOSED
这样,通过四次挥手,可以把该说的话都说完,并且A和B都知道自己没话说了,对方也没花说了,然后就挂掉电话(断开链接)了 。
Socket
 利用套接字(Socket)开发网络应用程序早已被广泛的采用,以至于成为事实
上的标准。
 网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标
识符套接字。
 通信的两端都要有Socket,是两台机器间通信的端点。
 网络通信其实就是Socket间的通信。
 Socket允许程序把网络连接当成一个流,数据在两个Socket间通过IO传输。
 一般主动发起通信的应用程序属客户端,等待通信请求的为服务端。
Socket分类:
流套接字(stream socket):使用TCP提供可依赖的字节流服务
数据报套接字(datagram socket):使用UDP提供“尽力而为”的数据报服务

HTTPS和HTTP的区别主要如下:

1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

HTTP1.0和HTTP1.1的一些区别 HTTPS 2.0

1、缓存处理,在HTTP1.0中主要使用header里的If-Modified-Since,Expires来做为缓存判断的标准,HTTP1.1则引入了更多的缓存控制策略例如Entity tag,If-Unmodified-Since, If-Match, If-None-Match等更多可供选择的缓存头来控制缓存策略。
2、带宽优化及网络连接的使用,HTTP1.0中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP1.1则在请求头引入了range头域,它允许只请求资源的某个部分,即返回码是206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。 错误通知的管理,在HTTP1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。
3、Host头处理,在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)。
4、长连接,HTTP 1.1支持长连接(PersistentConnection)和请求的流水线(Pipelining)处理,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,在HTTP1.1中默认开启Connection: keep-alive,一定程度上弥补了HTTP1.0每次请求都要创建连接的缺点

浏览器输入一个 URL 按下回车网络传输的流程

1、浏览器向DNS服务器请求解析该URL中的域名所对应的IP地址。
2、解析出ip地址后,根据IP地址和默认端口,和服务器建立TCP连接。
3、浏览器发出读取文件(URL域名后面的部分对应的文件)的HTTP请求,该请求报文作为TCP三次握手的4、第三个报文传送给服务器。
5、服务器对浏览器请求作出响应,并把对应的html文本发送给浏览器。
6、释放TCP链接。
7、浏览器将该html文本显示内容。

5、问的深一点的可能涉及到网络架构,每层有什么协议,FTP 相关原理,例:TCP 建立连接后,发包频率是怎么样的?

八.设计模式(六大基本原则、项目中常用的设计模式、手写单例等)

1.单一职责原则
2.开闭原则
面对修改是封闭的,面对扩展是开放的。
3.里氏替换原则
只要父类能出现的地方,子类就能出现,主要体现就是 继承 和 实现
4.依赖倒置原则
高层次模块不依赖低层次模块的细节,而是依赖低层次模块的抽象

    IHttp http;//低层次模块的抽象,而不是细节。!!!
    public void setHttp(IHttp http) {
        this.http = http;
    }
    public interface IHttp {
        void get();
    }
    class OKHttp implements IHttp {
        @Override
        public void get() {
        }
    }
    class XUtils implements IHttp {
        @Override
        public void get() {
        }
    }
}

5.接口隔离原则
类间的依赖关系应该建立在最小的接口上,接口隔离原则把臃肿的接口拆分成更小更具体的接口。
但是接口不能拆分的过于细致,以免接口泛滥。
6.迪米特原则(最少知识原则)
一个对象应该对其他对象有最少的了解,通俗的说,一个类应该对自己需要耦合或调度的类 知道的最少

1、生产者模式和消费者模式的区别?
2、单例模式双重加锁,为什么这样做?

双重加锁实现本质也是一种懒汉模式,相比第2种实现方式将会有较大的性能提升。

public class Singleton {
      private volatile static Singleton instance;
      
      private Singleton() {
      }
      
      public static Singleton getInstance() {
          if (instance == null) {
              synchronized (Singleton.class) {
                 if (instance == null) {
                     instance = new Singleton();
                 }
             }
         }
         
         return instance;
     }
 }

就算在单例类被实例化时有多个线程同时通过了第8行代码 if (instance == null) 的判断,但同一时间只有一个线程获得锁后进入临界区。通过第8行判断的每个线程会依次获得锁进入临界区,所以进入临界区后还要再判断一次单例类是否已被其它线程实例化,以避免多次实例化。由于双重加锁实现仅在实例化单例类时需要加锁,所以相较于第2种实现方式会带来性能上的提升。另外需要注意的是双重加锁要对 instance 域加上 volatile 修饰符。由于 synchronized 并不是对 instance 实例进行加锁(因为现在还并没有实例),所以线程在执行完第11行修改 instance 的值后,应该将修改后的 instance 立即写入主存(main memory),而不是暂时存在寄存器或者高速缓冲区(caches)中,以保证新的值对其它线程可见。
补充:第9行可以锁住任何一个对象,要进入临界区必须获得这个对象的锁。由于并不知道其它对象的锁的用途,所以这里最好的方式是对 Singleton.class 进行加锁。

3、知道的设计模式有哪些?
4、项目中常用的设计模式有哪些?
5、手写生产者、消费者模式。
6、手写观察者模式代码。
7、适配器模式、装饰者模式、外观模式的异同?

8、谈谈对 java 状态机的理解。
9、谈谈应用更新(灰度、强制更新、分区更新?)

九.断点续传

什么是断点续传

指的是在上传/下载时,将任务(一个文件或压缩包)人为的划分为几个部分,每一个部分采用一个线程进行上传/下载,如果碰到网络故障,可以从已经上传/下载的部分开始继续上传/下载未完成的部分,而没有必要从头开始上传/下载。可以节省时间,提高速度。

Http 怎么支持断点续传的?

Http 1.1 协议中默认支持获取文件的部分内容,这其中主要是通过头部的两个参数:Range 和 Content Range 来实现的。客户端发请求时对应的是 Range ,服务器端响应时对应的是 Content-Range。

OkHttp 断点下载

断点下载思路

step 1:判断检查本地是否有下载文件,若存在,则获取已下载的文件大小 downloadLength,若不存在,那么本地已下载文件的长度为 0
step 2:获取将要下载的文件总大小(HTTP 响应头部的 content-Length)
step 3:比对已下载文件大小和将要下载的文件总大小(contentLength),判断要下载的长度
step 4:再即将发起下载请求的 HTTP 头部中添加即将下载的文件大小范围(Range: bytes = downloadLength - contentLength)

十.Java 四大引用

强引用、软引用、弱引用、虚引用的区别以及使用场景。

1、强引用(StrongReference)

强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。如下:

    Object o=new Object();   //  强引用  

当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。如果不使用时,要通过如下方式来弱化引用,如下:

    o=null;     // 帮助垃圾收集器回收此对象  

强引用置为 null,会不会被回收?
显式地设置o为null,或超出对象的生命周期范围,则gc认为该对象不存在引用,这时就可以回收这个对象。具体什么时候收集这要取决于gc的算法。

2、软引用(SoftReference)
如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。

SoftReference softRef=new SoftReference(str);     // 软引用

虚引用在实际中有重要的应用,例如浏览器的后退按钮。按后退时,这个后退时显示的网页内容是重新进行请求还是从缓存中取出呢?这就要看具体的实现策略了。

3、弱引用(WeakReference)
弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。

4、虚引用(PhantomReference)
虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。

Java引用:强引用,软引用,弱引用和虚引用

1.StrongReference强引用:
回收时机:从不回收 使用:对象的一般保存 生命周期:JVM停止的时候才会终止
2.SoftReference软引用
回收时机:当内存不足的时候;使用:SoftReference结合- ReferenceQueue构造有效期短;生命周期:内存不足时终止
3.WeakReference,弱引用
回收时机:在垃圾回收的时候;使用:同软引用; 生命周期:GC后终止
4.PhatomReference 虚引用
回收时机:在垃圾回收的时候;使用:合ReferenceQueue来跟踪对象呗垃圾回收期回收的活动; 生命周期:GC后终止

稍微问的深一些的面试官会和内存泄漏检测原理以及垃圾回收糅杂在一起。

十一. Java 的泛型, 的区别

问到泛型、泛型擦除、通配符相关的东西

通配符

通配符,即 “?”,用来表示未知类型。
通配符可用作各种情况:作为参数,字段或局部变量的类型;有时也作为返回类型;通配符从不用作泛型方法调用、泛型类实例创建或超类型的类型参数。

上限 和 下限

1.? super F 下限:F or F 所有父类 都可以,所以属于把下面的类型限限制了,下面的类型不能低于F,不能是F的子类,否则编译不通过。

2.? extends F 上限:F or F 所有子类 都可以,所有属于把上面的类型给限制了,上面的类型不能高于F,不能是F的父类,否则编译不通过。

一句话记住(? super F: F 或者 F的所有父类 都可以, ? extends F: F 或者 F的所有子类都可以)

3.可写模式<?super F> 可写,不完全可读

4.可读模式 可读,不可写

[图片上传失败...(image-f28724-1585737993942)]

十二、final,finally,finalize的区别

总结:
(1)在java中,final可以用来修饰类,方法和变量(成员变量或局部变量)。
(2)finally作为异常处理的一部分,它只能用在try/catch语句中,并且附带一个语句块,表示这段语句最终一定会被执行(不管有没有抛出异常)
(3)finalize()是在java.lang.Object里定义的,也就是说每一个对象都有这么个方法。这个方法在gc启动,该对象被回收的时候被调用。其实gc可以回收大部分的对象(凡是new出来的对象,gc都能搞定,一般情况下我们又不会用new以外的方式去创建对象),所以一般是不需要程序员去实现finalize的。

十三.抽象类和接口区别

image.png
总结

(1)抽象类是用来捕捉子类的通用特性的 。它不能被实例化,只能被用作子类的超类。抽象类是被用来创建继承层级里子类的模板
(2)接口是抽象方法的集合。如果一个类实现了某个接口,那么它就继承了这个接口的抽象方法。这就像契约模式,如果实现了这个接口,那么就必须确保使用这些方法。接口只是一种形式,接口自身不能做任何事情。
抽象类和接口的使用时机
(3)那么,什么时候该用抽象类,什么时候该用接口呢?
要解决上面的问题,我们先从弄清楚抽象类和接口之间的关系。首先,我们都知道类对事物的抽象,定义了事物的属性和行为。而抽象类是不完全的类,具有抽象方法。接口则比类的抽象层次更高。所以,我们可以这样理解它们之间的关系:类是对事物的抽象,抽象类是对类的抽象,接口是对抽象类的抽象。
从这个角度来看 java 容器类,你会发现,它的设计正体现了这种关系。不是吗?从 Iterable 接口,到 AbstractList 抽象类,再到 ArrayList 类。
现在回答前面的问题:在设计类的时候,首先考虑用接口抽象出类的特性,当你发现某些方法可以复用的时候,可以使用抽象类来复用代码。简单说,接口用于抽象事物的特性,抽象类用于代码复用。

十四、int、char、long各占多少字节数

image.png

十五、java中==和equals和hashCode的区别

总结
①equals方法用于比较对象的内容是否相等(覆盖以后)
②hashcode方法只有在集合中用到
③当覆盖了equals方法时,比较对象是否相等将通过覆盖后的equals方法进行比较(判断对象的内容是否相等)。
④将对象放入到集合中时,首先判断要放入对象的hashcode值与集合中的任意一个元素的hashcode值是否相等,如果不相等直接将该对象放入集合中。如果hashcode值相等,然后再通过equals方法判断要放入对象与集合中的任意一个对象是否相等,如果equals判断不相等,直接将该元素放入到集合中,否则不放入。

十六、int与integer的区别

总结
基本
①Integer是int的包装类,int则是java的一种基本数据类型
②Integer变量必须实例化后才能使用,而int变量不需要
③Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值
④Integer的默认值是null,int的默认值是0

十七、谈谈对java多态的理解

多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。

十八、String、StringBuffer、StringBuilder区别

从线程安全上看

(1)String 不可变,因此是线程安全的
(2)StringBuilder 不是线程安全的
(3)StringBuffer 是线程安全的,内部使用 synchronized 进行同步

从使用的角度上看

(1)如果要操作少量的数据用 String;
(2)多线程操作字符串缓冲区下操作大量数据 StringBuffer;
(3)单线程操作字符串缓冲区下操作大量数据 StringBuilder。

十九、什么是内部类?内部类的作用

什么是内部类:

将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。

内部类的作用:

作用
1.内部类可以很好的实现隐藏,一般的非内部类,是不允许有 private 与protected权限的,但内部类可以
2.内部类拥有外围类的所有元素的访问权限
3.可是实现多重继承
4.可以避免修改接口而实现同一个类中两种同名方法的调用。

(1)成员内部类
成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员)。
当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认情况下访问的是成员内部类的成员。
(2)局部内部类
局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。
(3)匿名内部类
匿名内部类就是没有名字的内部类
(4)静态内部类
指被声明为static的内部类,他可以不依赖内部类而实例,而通常的内部类需要实例化外部类,从而实例化。静态内部类不可以有与外部类有相同的类名。不能访问外部类的普通成员变量,但是可以访问静态成员变量和静态方法(包括私有类型)
一个 静态内部类去掉static 就是成员内部类,他可以自由的引用外部类的属性和方法,无论是静态还是非静态。但是不可以有静态属性和方法、

二十、抽象类是否可以没有方法和属性?

抽象类专用于派生出子类,子类必须实现抽象类所声明的抽象方法。
抽象类中可以没有抽象方法,但有抽象方法的一定是抽象类。

二十一、父类的静态方法能否被子类重写

总结:
不能,父类的静态方法能够被子类继承,但是不能够被子类重写,即使子类中的静态方法与父类中的静态方法完全一样,也是两个完全不同的方法。

二十二、Serializable 和Parcelable 的区别

Serializable
(1)Serializable 是java的序列化技术,最简单的使用方式为在需要序列化的class增加implements Serializable,并增加一个唯一个序列化id: private static final long serialVersionUID = 1L; 默认方式最好直接设置为1L,因为java sdk会自动进行hash计算,并生成唯一的UID值。手动设置serialVersionUID的好处是当前class如果改变了成员变量,比如增加或者删除之后,这个UID是不改变的,那么反序列化就不会失败;自动设置则在改变了成员变量之后就会重新计算获得新的UID,从而导致失败。不过,大多数情况下两者都可以。
(2)Seralizable相对Parcelable而言,好处就是非常简单,只需对需要序列化的类class执行就可
(3)Seralizable无法序列化静态变量,使用transient修饰的对象也无法序列化。
(4)当一个父类实现序列化,子类自动实现序列化,不需要再显示实现Serializable接口。
Parcelable
(1)Parcelable是android特有的序列化API,它的出现是为了解决Serializable在序列化的过程中消耗资源严重的问题,但是因为本身使用需要手动处理序列化和反序列化过程,会与具体的代码绑定,使用较为繁琐,一般只获取内存数据的时候使用。
(2)而Parcelable依赖于Parcel,Parcel的意思是包装,实现原理是在内存中建立一块共享数据块,序列化和反序列化均是操作这一块的数据,如此来实现。

二十三、静态属性和静态方法是否可以被继承?是否可以被重写?以及原因?

(1)父类的静态属性和方法可以被子类继承
(2)当父类的引用指向子类时,使用对象调用静态方法或者静态变量,是调用的父类中的方法或者变量。并没有被子类改写。
(3)原因
static修饰函数/变量时,其实是全局函数/变量,它只是因为java强调对象的要
挂,它与任何类都没有关系。靠这个类的好处就是这个类的成员函数调用static方法不用带类名。
注意:static关键字可以用修饰代码块.static代码块可以置于类中的任何一个位置,并可以有多个static代码块。在类初次被加载时,会按照静态代码块的顺序来执行,并且只会执行一次。

二十四、闭包和局部内部类的区别

(1)、局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的。
(2)、闭包(Closure)是一种能被调用的对象,它保存了创建它的作用域的信息。JAVA并不能显式地支持闭包,但是在JAVA中,闭包可以通过“接口+内部类”来实现。

你可能感兴趣的:(Java基础面试知识点)