java基础知识点

目录

面向过程和面向对象的区别

八种基本数据类型的大小,以及他们的封装类

基本类型和包装类型的区别

equals和==的区别

String类的equals方法

为什么重写equals必须重写hashcode

hashcode的作用:

为什么要一起重写

接口和抽象类共同点和区别

String、StringBuffer、StringBuilder

反射的理解

IO流

分为哪几种

字节/字符缓冲流的作用

ArrayList和LinkedList区别

ArrayList

LinkedList

Vector 

ArrayList 和 Array(数组)的区别

HashMap

扩容机制

加载因子为什么是0.75不是1

HashMap如何避免内存泄露问题

HashMap 和 Hashtable 的区别

HashMap 和 HashSet 区别

Java常见的集合类

Collection与Collections的区别


面向过程和面向对象的区别

  • 面向过程:是分析解决问题的步骤,然后用函数把这些步骤一步一步地实现,然后在使用时调用即可。性能较高,单片机、嵌入式开发一般采用面向过程开发。
  • 面向对象:就是把构成问题的事物分解成各个对象,不关心其具体实现过程,只关心完成功能。面向对象有封装继承多态的特性,所以易维护易复用易扩展。可以设计出低耦合的系统,但是从性能上来说,要比面向过程要低。

八种基本数据类型的大小,以及他们的封装类

数据类型大小(字节)封装类
byte1Byte
short2Short
int4Integer
long8Long
float4Float
double8Double
char2Character
boolean1Boolean
  • 6 种数字类型:
    • 4 种整数型:byteshortintlong
    • 2 种浮点型:floatdouble
  • 1 种字符类型:char
  • 1 种布尔型:boolean

基本类型和包装类型的区别

用途:我们在方法参数、对象属性中很少会使用基本类型来定义变量,都是使用包装类型。并且,包装类型可用于泛型,而基本类型不可以。

默认值:成员变量包装类型不赋值就是 null ,而基本类型有默认值且不是 null。比如int默认值是0,而Integer默认值是null,所以Integer能区分出0和null的情况。

比较方式:对于基本数据类型来说,== 比较的是值。对于包装数据类型来说,== 比较的是对象的内存地址。所有整型包装类对象之间值的比较,全部使用 equals() 方法。

缓存机制:包装类型大部分都用到了缓存机制来提升性能。

4种整型包装类默认创建了数值 [-128,127] 的相应类型的缓存数据,范围内的数据就会从缓存中读取,范围外就会创建一个新的对象。虽然integer对象在[-128,127] 范围内会复用对象使用==判断也是正确的,但是这个区间之外,都会在堆上产生新的对象。因此所有整型包装类对象之间值的比较,全部使用 equals 方法比较

equals和==的区别

基本数据类型,==比较内容

引用数据类型,==比较对象的引用,也就是内存地址

对于equals方法,类没有重写equals,就和==作用一样,用的是object类的equals方法,object类equals()方法默认实现是使用==来比较的,所以都是比较内存地址。

但是类可以重写equals方法以实现比较对象内容的功能。比如String类就默认重写了object类的equals方法。

String类的equals方法

先比较地址,地址相同直接返回true,再遍历比较字符串内容,内容相同就返回true

为什么重写equals必须重写hashcode

hashcode的作用:

就是根据对象的内存地址换算出一个散列值,这个值可以确定该对象在哈希表中的索引位置。

在一些容器(比如 HashMapHashSet)中,有了 hashCode() 之后,判断元素是否在对应容器中的效率会更高。先调用这个元素的hashCode方法,如果同样的 hashCode 有多个对象,它会继续使用 equals() 来判断是否真的相同。

为什么要一起重写

因为两个相等的对象的 hashCode 值必须是相等。也就是说如果 equals 方法判断两个对象是相等的,那这两个对象的 hashCode 值也要相等。

如果重写 equals() 时没有重写 hashCode() 方法的话就可能会导致 equals 方法重写后通过内容比较判断是相等的两个对象,hashCode 值却不相等。因为不重写就会调用object类的hashcode方法,不是一个内存地址计算出来的hashcode值就不同。

比如HashSet集合中类如果重写equals方法没有重写hashcode方法,当添加两个等值的对象,集合先根据hashcode方法判断两个相等值对象的哈希值不同,最终导致HashSet集合根据哈希值认为他们不同,添加了两个等值的对象,集合中比较是先根据hashcode,hashcode值一样再根据equals判断,所以要一起重写。

另外,在Java中,String、Long、Integer等常见的数据类型已经实现了hashCode()和equals()方法,因此可以直接作为HashMap的key或者HashSet元素存储。

接口和抽象类共同点和区别


共同点
都不能被实例化,都可以有抽象方法
java8之后都可以有默认方法
区别
一个类只能继承一个抽象类,却可以实现多个接口
抽象类中成员变量可以是各种类型,但接口中成员变量只能是public static final
jdk1.8以前:抽象类中方法可以有方法体,而接口中方法不行。
1.8以后:接口里也可以有包含方法体的默认方法
抽象方法就是为了被重写,所以肯定不能用private修饰符。1.9以后,允许将接口方法定义为private

深拷贝和浅拷贝的区别是什么

  • 深拷贝:是指将一个对象复制到另一个对象,新对象与原对象不共享引用类型属性(如数组、集合、对象等),也就是说,新对象和原对象的引用类型属性指向的是不同的地址,修改其中一个对象中的引用类型属性,不会影响另一个对象中的属性值。
  • 浅拷贝:是指将一个对象复制到另一个对象,新对象与原对象共享引用类型属性,也就是说,新对象与原对象中的引用类型属性指向的是同一个地址,修改器中一个对象的引用类型属性,会影响到另一个对象的属性值,Java中的Object类提供了clone方法来实现浅拷贝

String、StringBuffer、StringBuilder


可变性
String不可变,StringBuffer和StringBuilder可变
例如给string类型s赋值“abcd”,第二次给s赋值为“abcdef”,不是在原内存地址上修改,而是重新生成一个新对象,新地址。指针指向新的string对象

每次+操作:隐式在堆上new了一个跟原字符串相同的StringBuilder对象,再调用append方法,拼接+后面的字符

String直接创建的字符串会将字符串对象的引用存储在常量池中,如果一个string对象已经创建过了,那么第二次就直接返回string对象在字符串常量池中的引用
线程安全
string不可变常量,所以线程是安全的。
StringBuffer是线程安全的,内部使用synchronized同步。
StringBuilder线程不安全。
性能
StringBuilder因为没加锁比StringBuffer性能更快,不要求线程安全的情况下,多数选StringBuilder

反射的理解


反射机制是指在运行时,对于任意一个类,通过反射都能够知道这个类的所有属性和方法,在java中,反射可以通过Class类的一些方法来获取ConstructorMethodFiled等类的信息,通过这些信息可以实现对类的实例化调用方法获取字段值等操作,这种动态获取信息的功能称为反射机制。


class类对象的获取方式
主要三种
根据类名:类名.class
根据对象:对象名.getClass()
根据全限定类名:Class.forName(全限定类名)
还有通过类加载器获取ClassLoader.loadClass()方法

Object类中的常用方法

  • Object类是Java中所有类的基类,她定义了一些常用的方法,包括
    1. equals(Object obj):判断当前对象是否与另一个对象相等,通常需要重写该方法
    2. hashCode():返回当前对象的哈希码,用于哈希表等数据结构
    3. toString():返回当前对象的字符串表示,通常需要重写该方法
    4. getClass():返回当前对象的类类型
    5. wait():使当前线程等待,直到其他线程调用该对象的notify()或notifyAll()方法
    6. notify():唤醒一个等待中的线程
    7. notifyAll():唤醒所有等待中的线程
    8. finalize():在垃圾回收器回收对象之前调用,用于释放资源等清理工作

IO流

分为哪几种

inputstream/Reader 字节输入流和字符输入流,是所有输入流的基类
Outputstream/Writer 字节输出流和字符输出流,是所有输出流的基类


字节/字符缓冲流的作用


缓冲流先将数据加载至缓冲区,再从缓冲区一次性读取或写入多个字节,从而避免频繁的IO操作,提高流的传输效率。

ArrayList和LinkedList区别

ArrayList

底层数据结构:基于动态数组实现,连续内存存储。

查找:所以适合下标查询(因为数组是连续的内存空间)时间复杂度为O(1)


插入删除:除了尾部都不适合,性能消耗很大。比如:执行add(E e)方法的时候, ArrayList 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是 O(1)。
因为如果在指定位置i插入删除时候会涉及到一个元素的移动(从插入位置开始的元素向后/向前复制一份)这种情况时间复杂度就为 O(n-i)。所以我们插入的时候使用尾插,并且指定一个合适的初始容量可以极大提升性能,甚至超过LinkedList。

另外因为数组长度是固定的,arraylist有一个扩容机制,以无参数构造方法创建 ArrayList 时,实际上初始化赋值的是一个空数组。当真正对数组进行添加元素操作时,才真正分配容量。即向数组中添加第一个元素时,数组容量扩为 10当超出长度时,他会新建一个数组(根据扩容因子),然后将老数组的数据拷贝到新的数组。

内存占用:ArrayList底层是数组,内存连续,节省内存。

LinkedList

底层数据结构: 底部基于双向链表实现,内部使用Node节点来存储元素,不是连续的。

插入删除:所以适合元素头尾部的插入删除,因为链表有很多节点,节点有指针可以指向下一个节点,只需要改变指针的指向就可以实现插入和删除 近似 O(1),但是也不适合指定位置i的插入删除,因为要先移动到指定位置再插入。时间复杂度近似为o(n)。
查找:不适合元素的查询,需要逐一的遍历来找到目标元素,时间复杂度O(n)。

内存占用:LinkedList是双向链表,需要存储数据和两个指针,更占用内存。

Vector 

Vector 是 List 的古老实现类,底层使用 Object[ ]存储,线程安全的。

ArrayList 和 Array(数组)的区别

  • ArrayList会根据实际存储的元素动态地扩容或缩容,而 Array 被创建之后就不能改变它的长度了,容量是固定的
  • ArrayList 允许你使用泛型来确保类型安全,Array 则不可以。
  • ArrayList 中只能存储对象。对于基本类型数据,需要使用其对应的包装类(如 Integer、Double 等)。Array 可以直接存储基本类型数据,也可以存储对象。
  • ArrayList 支持插入、删除、遍历等常见操作,并且提供了丰富的 API 操作方法,比如 add()remove()等。Array 只是一个固定长度的数组,只能按照下标访问其中的元素,不具备动态添加、删除元素的能力。
  • ArrayList创建时不需要指定大小,而Array创建时必须指定大小

红黑树

  • 红黑树是一种自平衡的二叉搜索树,具有以下特征
    1. 每个节点要么是黑色,要么是红色
    2. 根节点是黑色的
    3. 所有叶子结点都是黑色的空节点(NIL节点)
    4. 如果一个节点是红色的,则它的啷个子节点都是黑色
    5. 任意节点到其每个叶子结点的所有路径都包含相同数目的黑色节点
  • 这些特征保证了红黑树在插入和删除节点时能够保持平衡,从而保证了其查找、插入、删除操作的时间复杂度都是O(log n)级别的。

HashMap

底层实现:
jdk1.8之前:数组+单向链表实现。

若遇到哈希冲突,就判断该元素与要存入的元素的  key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。即数组中每一格就是一个链表,将冲突的值直接加到链表中即可。

jdk1.8之后:当链表长度大于8且只有数组长度超过64,链表会转变为红黑树,元素以内部类Node节点存在。否则,就是只是执行 resize() 方法对数组扩容。

 

put方法:

1.当table数组为null或者长度为0时进行resize扩容初始化数组
2.计算key的hash值,找到对应的数组下标,如果没有产生hash冲突,就直接插入元素存入数组
3.如果产生hash冲突有元素,就要和插入的key先进行比较,key相同就直接覆盖取代该元素,如果不同,则判断是否是红黑树节点,如果是,就放入树中,如果为链表节点,就遍历链表插入尾部。
当链表长度大于8,并且数组长度大于等于64则将链表转变为红黑树,否则就只是执行数组扩容,当长度低于6则将红黑树转回链表。
4.如果key为null,就存在数组下标为0的位置。HashMap 可以存储 null 的 key 和 value,但 null 作为键只能有一个,null 作为值可以有多个
5.并且其数组也是和Arraylist一样有数组扩容机制

get方法:

获取时,直接找到hash值对应的下标,在进一步判断key是否相同,从而找到对应值。

扩容机制

在添加元素或初始化的时候需要调用resize方法进行扩容,第一次添加数据初始化数组长度为16,以后每次每次扩容都是达到了扩容阈值(数组长度*0.75)每次扩容的时候,都是扩容之前容量的2倍;扩容之后,会新创建一个数组,需要把老数组中的数据挪动到新的数组中没有hash冲突的节点,则直接使用e.hash & (newCap - 1)计算新数组的索引位置如果是红黑树,走红黑树的添加如果是链表,则需要遍历链表,可能需要拆分链表,判断(e.hash &oldCap)是否为0,该元素的位置要么停留在原始位置,要么移动到原始位置+增加的数组大小这个位置上

加载因子为什么是0.75不是1


加载因子是控制数组存放数据的疏密程度,加载因子越接近1,数组存放的就越密,也就是会让链表长度增加,加载因子越接近0,数组存放数据也就越稀疏,hash冲突次数也就越少
加载因子太大,查询效率较低(因为hash冲突概率大,链表长度大,要一个个遍历)
加载因子太小,数组的空间利用率太低,存放的数据很分散
所以官方给出了空间和时间上的比较好的平衡点,也就是0.75

HashMap如何避免内存泄露问题


自定义对象为key的时候,一定要重写equals和hashcode保证对象key不重复创建。

HashMap 和 Hashtable 的区别

  • 线程是否安全: HashMap 是非线程安全的,Hashtable 是线程安全的,因为 Hashtable 内部的方法基本都经过synchronized 修饰。(如果你要保证线程安全的话就使用 ConcurrentHashMap 吧!);
  • 效率: 因为线程安全的问题,HashMap 要比 Hashtable 效率高一点。另外,Hashtable 基本被淘汰,不要在代码中使用它;
  • 对 Null key 和 Null value 的支持: HashMap允许key和value为null,而HashTable不允许;
  • 数据结构:Hashtable还是数组+链表。

HashMap 和 HashSet 区别

HashSet 底层就是基于 HashMap 实现

HashMapHashSet
实现了 Map 接口实现 Set 接口
存储键值对仅存储对象
调用 put()向 map 中添加元素调用 add()方法向 Set 中添加元素

Java常见的集合类

第一个是Collection属于单列集合,第二个是Map属于双列集合

在Collection中有三个子接口List、Set和Queue。在我们平常开发的过程中用的比较多像list接口中的实现类ArrarList和LinkedList。在Set接口中有实现类HashSet和TreeSet。

在map接口中有很多的实现类,平时比较常见的是HashMap、TreeMap,还有一个线程安全的map:ConcurrentHashMap。

Collection与Collections的区别

Collection是java集合类中接口,用于表示一组对象的集合。它提供了一些通用的操作,如添加、删除、遍历等。

Collections是Java中的一个工具类,它包含了一组静态方法,用于操作各种集合类型。它提供了一些常用的算法和工具方法,如排序、查找、复制等。Collections类中的方法通常是针对Collection类型的实例进行操作的。

它们是两个独立的概念。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值