java基础

Java基础之集合类

Collection

Collection接口继承自Iterable接口,Iterable接口允许使用foreach方式遍历,并且定义了一个迭代器

list

List接口继承自Collection接口,存储一组不唯一有序(插入序)的对象。
采用线性列表存储,长度可以动态改变,可以通过索引访问

ArrayList 和LinkedList区别

ArrayList底层用数组实现,随机访问效率高(O(1)),插入删除效率低(O(n),需要移动后面元素)
LinkedList底层用链表实现,随机访问效率低(O(n)),插入删除效率高(O(1))

ArrayList 和vector的区别

Vector的所有方法都是同步方法,而ArrayList不是,如果需要用到线程安全的list可以考虑用Vector,不过即使需要用线程安全的list我们也是推荐用CopyOnWriteList而不是用Vector

ArrayList是否是线程安全的?

非线程安全,如果需要线程安全,可以使用Vector或者CopyOnWriteList

set

HashSet是如何去重的?

HashSet底层是通过HashMap实现的,HashSet中的元素放在HashMap中的K上面,我们都知道HashMap中的K是不重复的,内容相同的对象hashCode是相同的(HashCode决定元素存储在HashMap中Entry数组中的位置),并且如果equals方法返回对象是相同的话默认会覆盖Value内容,但其实这里对Hashset来讲value覆盖与否都无所谓(hashset中放入的是PRENSENT对象每次都一样),因为关注的是hashmap中的k

map

HashMap和HashTable的区别

  • HashTable是线程安全的(所有方法被sychronized修饰),HashMap不是
  • HashTable不允许有空的key和value,HashMap可以

HashMap是否是线程安全的?如果在多线程环境下并发访问会不会有问题?

HashMap不是线程安全的,jdk1.7的时候HashMap在多线程下并发增加元素扩容的时候会出现环形链表,导致死循环,jdk1.8的时候采用尾插法解决了环形链表的问题,不过还是非线程安全的

HashMap底层是怎么实现的?

HashMap在jdk1.7版本的时候是通过数组+链表的方式实现的,在jdk1.8里面引入了红黑树,在使用拉链法解决hash碰撞的时候,链中元素超过8的时候会转为红黑树

红黑树和二叉排序树、AVL树的区别(延伸话题)

二叉排序树

特点:

  • 若他的左子树不为空,则他的左子树上所有的值小于根节点的值
  • 若他的右子树不为空,则它的右子树上所有的值大于根节点的值
  • 它的左子树和右子树都是平衡二叉树
  • 中序遍历二叉排序树可以得到一个正序有序的序列
  • 通常情况下,操作时间复杂度为O(logn),极端情况下退化为O(n)

这样的数据结构实际上是一个非常典型的适合进行二分法进行查找的结构,每当需要查找元素的时候,先跟根节点比较,就可以判断它在树的哪一端,每次查询都能够缩小一半的候选集,达到时间复杂度为O(logn),但是这里面有个问题,极端情况,如果二叉排序树插入的数据比较极端,比如插入了一组正序有序的数据,使得二叉排序树向右侧单侧倾斜,这时如果查找元素其实就相当于退化成了一个单链表,查找元素的时间复杂度退化为O(n),这种情况下实际上为了提高查找效率就引入了平衡二叉树和红黑树来使得二叉排序树构建的更加平衡。

平衡二叉树(AVL树、高度平衡树)
特点:

  • 是一种平衡二叉树
  • 每一个节点的左子树和右子树的高度差的绝对值不会超过1
    平衡二叉树是在构建二叉排序树的过程中,每插入或者删除一个节点,都会先检查是否破坏了树的平衡性,如果是,则找出最小的不平衡的树,通过右旋或者左旋,使之成为新的平衡树
  • 平衡二叉树的插入、查找、删除的时间复杂度是O(logn)

平衡二叉树追求的是全局平衡,在插入和删除的时候需要调整整棵树,显然这是很费时的,所以希望在调整的时候,不是整棵树进行结构性调整,而是局部性的调整,这样也就引出了红黑树

红黑树是一种二叉查找树,并在此基础上在每个节点上增加了一个存储位来表示节点的颜色,可以是红色或者黑色,通过对任何一条从根节点到叶子结点的路径的各节点着色方式的限制,确保红黑树没有任何一条路径多于其最短路径的两倍长,这保证了红黑树是大致平衡的,而且又不像平衡二叉树那样要求全局性的平衡

红黑树由以下约束保证了红黑树没有任何一条路径多于其最短路径的两倍长
特点:

  • 节点是红色或者黑色
  • 根是黑色
  • 所有叶子节点都是黑色
  • 每个红色节点必须有两个黑色的子节点(从每个叶子到根节点的所有路径上不能有两个连续的红色节点)
  • 从任一节点到其每个叶子节点的所有简单路径都包含相同数目的黑色节点

在通过插入和删除时,会使得红黑树不再符合红黑树的性质,这时,需要少量(O(logn))的颜色变更和不超过三次的树旋转,牺牲了部分平衡性以换取插入删除时少量的旋转操作,整体性能优于AVL树

ConcurrentHashMap是怎么实现的?

Java基础之关键字

final关键字

作用
final可以用来修饰类、方法、变量

修饰类的时候表示该类不能被继承
修饰方法的时候表示该方法在子类中不能被重写
修饰变量的时候,如果修饰的是基本数据类型,一旦被赋值就不能被再次赋值,如果是引用数据类型,只能保证引用所指向的地址不改变,而引用的对象的属性是可以改变的

String为什么是不可变类(延伸问题)

序列化

泛型

接口和抽象类的区别

既然要聊接口和抽象类的区别,就得先聊一下接口和抽象类分别是什么,他们的共同点,然后再聊区别更容易理解一些。

抽象类概述:

当我们在设计一些行为和属性差不多的类的时候其实可以想到面向对象中的继承,用来抽取一个基类,既可以减少重复代码,又可以让代码变得简洁,抽象类的作用就是如此,用于抽取子类通用属性的一种类,只能用作父类,用于给子类继承并且不能够实例化,作为继承的模板,也是多态的一种表现形式
因此我们可以总 结下抽象类的特点

  • 不能被实例化,可以有构造函数
  • 可以包含具体方法,也可以包含抽象方法(必须被子类(非抽象子类)实现)
  • 可以包含成员变量和静态成员变量
  • 子类的抽象方法不可以与父类的抽象方法同名

接口概述:
接口是抽象方法的集合,如果某一个类实现了某个接口,那么他就必须实现这个接口的抽象方法,接口本身并不能做任何事情

接口的特点:

  • 接口中不能有构造方法
  • 接口中可以定义”成员变量”,但是会自动转换为 public static final,即Java中的常量,并且必须被显式初始化
  • 接口中的所有方法都是抽象方法,不能包含具体的方法,也不能包含静态的方法(jdk 8可以包含)
  • 不可以通过new来实例化接口

有了这些内容,我们就可以来回答接口和抽象类的区别了

参数 抽象类 接口
默认的方法实现 可以有 jdk 8之后可以提供,之前不允许有
关键字 子类通过extends继承抽象类 子类通过implements实现接口
访问修饰符 抽象方法可以用public protected这些修饰符 接口方法默认public,不可以用其他修饰符
添加新方法 如果需要往抽象类中添加新的方法,可以提供默认的实现方法,不需要改变现有代码 jdk8以后可以提供默认方法,jdk8之前不可以,所以之前子类必须实现所有接口中的方法
构造方法 可以有 不可以有
设计理念 is-a的关系,是一种关系的延续 like-a的关系,体现的是一种功能扩展

static关键字

作用

  • 修饰成员变量 static可以用来修饰成员变量,也叫静态变量,在内存中只有一个副本,可通过类名访问
    应用场景:
    对象间传值
  • 修饰方法 一般用来抽取工具方法,通过类名直接访问,不可以访问实例变量和实例方法。
  • 修饰代码块
  • 静态内部类
  • 静态导入 写代码的时候可以导入某个类或者某个静态方法或静态变量,用来节省代码

sychronized怎么用的,里面是怎么实现的?

Java基础之数据结构

Java基础之面向对象

面向对象的特点或者说谈谈面向对象的理解?

面向过程的特点是封装、继承、多态
聊到面向对象其实就不得不提面向过程,

面向过程是一种以事件为中心的变成思想,把解决问题的步骤分析出来,使用函数将这一个个步骤实现,使用的时候直接依次调用即可,简单的问题可以通过面向过程的思路来解决,直接有效,但是问题的规模很大时,面向过程的思想就不太够用了,慢慢的出现了面向对象的编程思想
而面向对象是一种以对象为核心的编程思想,把要解决的问题分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描述某个对象在解决问题中步骤中的属性和行为。

面向过程和面向对象的优缺点:

面向过程:
优点:

  • 流程化,编程任务明确
  • 效率高,面向过程强调代码的短小精悍,善于结合数据结构来开发高效率的程序
    缺点:
    需要深入思考,耗费精力,代码重用性差,扩展能力差,后期维护难度较大

面向对象:
优点:

  • 结构清晰,模块化结构化,符合人类思维方式
  • 易扩展,代码重用率高,可继承,可覆盖,可以设计出低耦合的系统
  • 易维护,系统低耦合的特点有利于减少后期维护工作量
    缺点:
  • 开销大,修改更改对象内部时,对象的属性不允许直接存取,所以需要增加很多无意义,只负责读写的行为,使得编程工作增加负担,增加运行开销,程序变得臃肿

Java为什么不支持多继承

多继承会产生继承的二义性问题,比如B,C同时继承于A;D又多继承于B和C,这时候如果A中定义了一个f()方法,D到底是会调用谁呢?支持多继承的语言像C++是引入了虚继承来解决这个问题的,十分的晦涩难懂,Java的设计者在这里秉持着简单易用的原则,就把类的多继承给移除了