2.27TODO

1. 基础

  1. Java语言特点:面向对象、通过JVM实现平台无关性和移植性、多线程、网络编程

  2. 基本数据类型:int(32)、char(16)、byte(8)、short(16)、long(64)、float(32)、double(64)、Boolean(1)

  3. String是不可变的,用final声明,不可继承,线程安全,字符串常量池

  4. String、StringBuffer、StringBuilder

    1. 可变性:String不可变,是通过new一个字符串来拼接。StringBuffer和StringBuilder可通过append()方法拼接字符串
    2. 线程安全:String线程安全,StringBuffer的append方法用了synchronized修饰,是线程安全,StringBuilder的append方法没有
    3. 性能:StringBuilder > StringBuffer > String
  5. 深拷贝:拷贝对象和原始对象引用不同,修改原始对象的值不会影响拷贝对象的值

  6. 浅拷贝:拷贝对象和原始对象引用相同,修改原始对象的值会影响拷贝对象的值
    例子:公会乱斗保存的公会成员,要断开引用

  7. equals和==的区别

    1. 基本数据类型,==比较的是值。引用数据类型,==比较的是内存地址
    2. 基本数据类型没有equals方法。引用数据类型,equals默认比较内存地址,如果有重写则按照重写逻辑
  8. 访问权限修饰符

    1. public:对所有类可见,使用对象:类、接口、方法、变量
    2. protected:对同一个包的类和所有子类可见,使用对象:变量、方法,不能修饰外部类
    3. private:仅同一个类可见,使用对象:变量、方法,不能修饰外部类
    4. default:同一包可见,使用对象:类、接口、变量、方法
  9. static关键字

    1. static变量:静态变量,可通过类.变量名访问。内存中只有一个副本,在类加载时被初始化(用来定义事件、协议)
    2. static方法:静态方法,在static方法内不能访问非静态的变量和非静态方法
    3. static代码块:仅在类加载时执行一次,可以将一些只进行一次的初始化操作放在static代码块执行
  10. 初始化顺序:父类静态变量和静态代码块 —— 子类静态变量和静态代码块 —— 父类实例变量和普通代码块 —— 父类构造函数 —— 子类实例变量和普通代码块 —— 子类构造函数

  11. final关键字

    1. 修饰类:不可继承
    2. 修饰方法:不可被重写
    3. 修饰变量:值不可变
  12. this关键字

    1. 调用当前类的实例变量,this.aid = aid
    2. 调用当前类的方法,this.mehtodName()
    3. 调用当前类的构造函数,this()
  13. super关键字

    1. 引用父类的实例变量,super.aid = aid
    2. 引用父类的方法,super.methodName()
    3. 引用父类的构造函数,super()
  14. 面向对象的三大特性:封装、继承、多态

    1. 封装:隐藏了对象的属性和实现细节,仅对外公开接口,通过使用访问权限修饰符来定义访问级别。比如类就是将类的变量和方法实现隐藏在类中,通过类才能访问类的变量和方法
    2. 继承:子类可以继承父类的特征和行为,也可以拓展自己的特征和行为。单继承,一个子类只能有一个父类。
    3. 多态:实现多态的三要素:继承、重写、父类引用指向子类对象和接口引用指向实现类对象
      1. 静态多态性:通过方法的重载实现,即相同的方法可以有不同的参数列表,实现根据参数不同做出不同的逻辑处理。在代码编译时就可以根据参数列表不同区分
      2. 动态多态性:子类重写父类方法或者接口实现类重写接口方法,代码运行期间通过引用对象的不同调用不同的方法。比如子类cat,父类animal,都有eat()方法。Aniaml cat = new Cat(),cat.eat()调用的是子类cat的方法
  15. 抽象类与接口:

    1. 修饰:abstract class 和 interface
    2. 实现:extends 和 implements。子类和实现类都必须提供方法实现
    3. 继承:一个类只能继承一个抽象类,但可以实现多个接口
    4. 访问修饰符:抽象方法有public、protected、default。接口方法默认是public
    5. 构造函数:抽象类可以有构造函数,接口没有构造函数
  16. 变量

    1. 类变量:方法外,static修饰
    2. 实例变量:方法外,没有static修饰
    3. 局部变量:方法中
  17. 内部类:成员内部类、局部内部类、匿名内部类、静态内部类

  18. 重写和重载

    1. 重载:在同一个类中,方法名相同但参数列表不同
    2. 重写:@Override在父子类之间,方法名、参数列表相同,子类重写的方法的访问修饰符小于等于父类
    3. 构造函数可以被重载不能被重写
  19. hashcode和equals

    1. 如果两个对象调用equals比较返回true,那么他们的hashcode一定相同
    2. 如果两个对象的hashcode相同,调用equals比较不一定为true
    3. 重写equals要重写hashcode,是为了保证equals返回true的情况下,hashcode也相同。如果重写了equals没有重写hashcode,就会出现两个对象相等但是hashcode不同的情况,这时当其中一个对象作为key保存到hashMap中,再通过另一个对象从hashMap中获取时,会拿不到
  20. 反射:在运行状态中,对于任何一个类,都能知道这个类的所有属性和方法;对于任意一个对象,都能调用它的任意一个方法和属性。(例子:spring的ioc、项目执行groovy脚本)

    1. clazz.getMethod()
    2. clazz.getDeclaredMethod()
    3. clazz.getDeclaredFields()
    4. clazz.getFields()
    5. 设置访问权限:field.setAccessible(true)、method.setAccessible(true);
    6. 反射为什么慢:https://juejin.cn/post/7330115846140051496
  21. 当编译一个类之后,会产生一个.class文件,该文件内存放着class对象。类加载相当于class对象的加载,而反射可以在运行时通过.class字节码问题件生成类并实现对象的增强

    1. Field:变量级别,get和set
    2. Method:方法级别,invoke()调用
    3. Constructor:构造函数,new instance()

2. 集合

  1. List:有序可重复
    1. ArrayList:数组,初始容量10,线程不安全

      1. 扩容:默认情况下,扩容为原来的1.5倍,然后将原数组通过Arrays.copyOf()复制到新数组中
      2. 如果在遍历arrayList的过程中移除一个元素
        使用for循环或者forEach会报错,可以使用迭代器Iterator.remove()
    2. ArrayList和LinkedList

      1. 都是线程不安全
      2. ArrayList是数组,LinkedList是双向链表
      3. ArrayList扩容为原来的1.5倍,LinkedList是指针后移
      4. 访问、插入和删除元素:
        1. ArrayList增删时间复杂度为On(找到对应的位置n),查询时间复杂度O1(通过索引定位)。适合多查询少增删
        2. LinkedList增删时间复杂度为O1(指针指向),如果是指定index的插入,时间复杂度On,查询时间复杂度On(要遍历链表)。适合多增删少查询
      5. 绝大部分情况下直接使用arraylist,linkedlist作者自己也调侃从来没使用过linkedlist
    3. ArrayList和Vector

      1. 底层都是数组
      2. Vector线程安全,使用synchronized修饰,性能不如ArrayList
      3. ArrayList扩容1.5倍,Vector扩容2倍
  1. Set:无序不可重复

    1. HashSet:底层是HashMap,初始容量16,负载因子为0.75的hashmap。hashset的值放在hashmap的key上,value为PRESENT。

      1. hashSet如何实现数据唯一
        先计算add对象的hashcode来确定对象存储的位置,如果该位置没有其他对象,则将add对象存放到该位置。如果有其他对象,则通过equals判断两个对象是否相同,相同则存储失败,不同则重新计算存储位置
    2. TreeSet:红黑树

  2. Map:kv键值对,通过key获取value,key唯一,value不唯一

    1. HashMap:初始容量16,负载因子0.75,阈值为负载因子*容量

      1. JDK1.8之前:数组+链表
      2. JDK1.8之后:数组+链表+红黑树,链表长度大于8时,将链表转换为红黑树,当红黑树节点小于6时转换为链表
    2. HashMap扩容:阈值为负载因子*容量,当元素数量大于阈值时,会扩容2倍的数组代替原数组

      1. 1.8之前:头插法,链表元素顺序发生改变,多线程情况下会出现循环链表问题
      2. 1.8之后:尾插法,链表元素顺序没有变化
    3. HashMap的put:JDK1.8之后:数组+链表+红黑树

      1. 先判断数组是否为空,如果为空,则新建数组,长度16
      2. 使用hash算法计算key的索引,如果索引处没有元素,则将kv存入table[idx]处,如果table[idx]处已经有元素,如果table[idx]是树节点,则按照红黑树结构存入kv键值对,如果是链表节点,则插入链表尾端
      3. 如果插入后链表长度大于8,则转换为红黑树
      4. 插入成功后检测是否需要扩容
    4. 哈希冲突:在计算key的hash值后,会对数组长度取余,然后得到idx位置,所以不同的key计算得到的idx是可能相同的,这就是hash冲突

      1. 拉链法:用链表的形式,将Node节点(kv键值对)挂在table[idx]
      2. 红黑树:如果数据多,hash冲突严重,导致链表长度过长,当链表长度大于8时,转换为红黑树,提高查询效率
    5. HashMap长度为什么是2的幂次方:因为hashmap存放kv时会先对key进行hash值计算,然后对得到的hash值进行数组长度取余,最终得到key存放的位置idx。而数组下标计算的方法是(length-1)&hash,长度为2的幂次方的话,等同于hash%length,提高了计算效率

    6. 为什么线程不安全

      1. JDK1.8之前,扩容后转移链表元素,采用头插法,多线程情况下可能出现循环链表问题
      2. JDK1.8之后,多线程情况下put元素,出现数据覆盖
    7. HashMap和HashTable的区别

      1. HashMap初始容量16,扩容2倍。HashTable初始容量11,扩容2倍+1
      2. HashMap线程不安全,HashTable线程安全(get和put使用synchronized)
      3. HashMap可以接受key和value为null,当key为null时,存放在table[0]的链表,HashTable的kv不能为null
    8. TreeMap:是有序的kv集合,对传入的key进行大小排序

      1. floorEntry:返回小于等于
      2. ceilingEntry:返回大于等于
    9. LinkedHashMap:每次put会将entry插入到链表的尾部,实现有序的存储,遍历时能根据元素存入的顺序遍历

  3. Queue:队列,后进前出(用作聊天记录,设置记录条数上限,add最新的一条会把最旧的一条顶掉)

  4. ConcurrentHashMap:线程安全的hashMap

    1. put

      1. 1.8之前,采用分段锁+Entry节点实现,每个分段锁维护一段数组,多个线程可以同时访问不同的分段锁,并发提高
      2. 1.8之后,Cas + synchronized + Node节点实现,锁住Node节点,锁粒度小。如果table[idx]位置为空,则通过CAS方式存入元素。如果不为空,则取出node节点,synchronized加锁,如果是链表节点则插入链表尾部,如果是红黑树节点则存入红黑树
    2. 扩容:在transfer转移方法中设置一个长度,表示一个线程处理的数组长度,最小值是16,在长度范围内只会有一个线程对其进行复制转移操作

    3. 跟HashTable的区别

      1. hashtable通过synchronized修饰,会锁住整个数组,而concurrenthashmap通过锁node节点,降低了锁的粒度,提高了性能
      2. hashtable初始容量11,扩容2倍+1。concurrenthashmap初始容量16,扩容2倍
  5. CopyOnWriteArrayList:线程安全的List

    1. add方法加锁处理(reentrantlock),保证了多线程写入是不会出现线程问题
    2. 使用Arrays.copyOf()将数据复制到新数组
    3. 内存占用,写时复制机制,在写入数据时,内存中存在两个对象的内存
    4. 不能保证数据一致性,可能会读到旧数据
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 0、总纲-数据结构分布图 # 1、基本的数据结构知识树[图片上传失败...(image-14e376-159973...
    一只狗被牵着走阅读 3,520评论 0 0
  • 1.说一下面向对象的三大特征,有什么特点? 继承、封装、多态继承:继承就是子类通过extends继承父类的方法,是...
    MrAnt阅读 3,666评论 0 0
  • Java继承关系初始化顺序 父类的静态变量-->父类的静态代码块-->子类的静态变量-->子类的静态代码快-->父...
    第六象限阅读 6,516评论 0 9
  • 一、java面试题 熟练掌握java是很关键的,大公司不仅仅要求你会使用几个api,更多的是要你熟悉源码实现原理,...
    上善若水0819阅读 8,862评论 0 1
  • Catalog 1 HashMap原理1.1 HashMap JDK1.7实现1.2 HashMap JDK1.8...
    allen锅阅读 3,352评论 0 1

友情链接更多精彩内容