Java面试锦囊(一)

本文深入讲解Java中的核心技术,包括基本数据类型、String类与StringBuilder、StringBuffer的区别、HashMap的工作原理等,同时探讨了面向对象的基本特性,集合框架的使用场景以及多线程编程中的关键概念。
摘要由CSDN通过智能技术生成

基础篇

目录

基础篇

Java中基本数据类型有哪些?

Integer 和 int的区别

String和StringBuilder和StringBuffer区别

String a = "A" 和 String a = new String("A") 创建字符串的区别

== 和 equals 的区别是什么

final 和 finally 和 finalize 的区别

JDK 和 JRE 有什么区别?

面向对象四大特性

方法覆盖和重载

普通类和抽象类

接口和抽象类

ArrayList和LinkedList区别?

HashMap在什么情况下扩容?如何扩容?

HashMap是如何Put一个元素的?

HashMap是如何Get一个元素的?

什么是Hash冲突?

HashMap是如何解决Hash冲突的?

创建线程是几种方式

说一下Java中的集合体系,以及他们的特点

Synchronized 和 lock的区别

线程的几种状态

sleep 和 wait的区别

Synchronized和Lock的区别


Java中基本数据类型有哪些?

byte:8位,最大存储数据量是255,存放的数据范围是-128~127之间

short:16位,最[大数据]存储量是65536,数据范围是-32768~32767之间

int:32位,最大数据存储容量是2的32次方减1,数据范围是负的2的31次方到正的2的31次方减1

long:64位,最大数据存储容量是2的64次方减1,数据范围为负的2的63次方到正的2的63次方减1

float:32位,数据范围在3.4e-45~1.4e38,直接赋值时必须在数字后加上f或F

double:64位,数据范围在4.9e-324~1.8e308,赋值时可以加d或D也可以不加

boolean:只有true和false两个取值

char:16位,存储Unicode码,用单引号赋值

Integer 和 int的区别

int是基本数据类型,变量中直接存放数值,变量初始化时值是0

Integer是引用数据类型,变量中存放的是该对象的引用,变量初始化时值时null

Integer是int类型的包装类,将int封装成Integer,符合java面向对象的特性,可以使用各种方法比如和其他数据类型间的转换

Integer和int的深入对比:

  1. 两个通过new生成的Integer对象,由于在堆中地址不同,所以永远不相等

  2. int和Integer比较时,只要数值相等,结果就相等,因为包装类和基本数据类型比较时,会自动拆箱,将Integer转化为int

  3. 通过new生成的Integer对象和非通过new生成的Integer对象相比较时,由于前者存放在堆中,后者存放在Java常量池中,所以永远不相等

  4. 两个非通过new生成的Integer对象比较时,如果两个变量的数值相等且在-128到127之间,结果就相等。这是因为给Integer对象赋一个int值,java在编译时,会自动调用静态方法valueOf(),根据java api中对Integer类型的valueOf的定义,对于-128到127之间的整数,会进行缓存,如果下次再赋相同的值会直接从缓存中取,即享元模式

String和StringBuilder和StringBuffer区别

三者底层都是char[]存储数据,JDK1.9之后使用的是byte[] ,因为往往我们存储都是短字符串,使用byte[]这样更节约空间。

由于String底层的char[]有final修饰,因此每次对String的操作都会在内存中开辟空间,生成新的对象,所以String不可变

StringBuilder和StringBuffer是可变字符串,没有final修饰,适合字符串拼接,另外StringBuffer是线程安全的,方法有synchronized修饰,但是性能较低,StringBuilder是线程不安全的,方法没有synchronized修饰,性能较高

String a = "A" 和 String a = new String("A") 创建字符串的区别

String c = "A" 首先去常量池找 “A”,如果有,会把a指向这个对象的地址 ,如果没有则在栈中创建三个char型的值'A',堆中创建一个String对象object,值为"A",接着object会被存放进字符串常量池中,最后将a指向这个对象的的地址

new String("A") : 如果常量池中么有“A”就会走上面相同的流程先创建“A”,然后在堆中创建一个String对象,它的值共享栈中已有的char值“A”。

以下代码创建了几个对象?

  • String s = "a" +"b" + "c" + "d";这条语句创建了几个对象?

创建了一个对象,因为相对于字符串常量相加的表达式,编译器会在编译期间进行优化,直接将其编译成常量相加的结果。

  • String s; 创建几个对象? 没有创建对象。

  • String a = "abc"; String b = "abc"; 创建了几个对象

    创建了一个对象,只是在第一条语句中创建了一个对象,a和b都指向相同的对象"abc",引用不是对象

== 和 equals 的区别是什么

==比较对象比较的是地址,对于Object对象中的equals 方法使用的也是 == ,比较的是对象的地址,默认情况下使用对象的equals比较Object中的equals方法,也就是比较地址,如果要实现自己的比较方式需要复写equals 方法。

对于包装类比如:Integer都是复写过equals方法,比较的是int 值。

final 和 finally 和 finalize 的区别

当用final修饰类的时,表明该类不能被其他类所继承。当我们需要让一个类永远不被继承,此时就可以用final修饰

finally作为异常处理的一部分,它只能用在try/catch语句中,并且附带一个语句块,表示这段语句最终一定会被执行(不管有没有抛出异常),经常被用在需要释放资源的情况下

finalize()是在java.lang.Object里定义的,也就是说每一个对象都有这么个方法。这个方法在gc启动,该对象被回收的时候被调用。其实gc可以回收大部分的对象(凡是new出来的对象,gc都能搞定,一般情况下我们又不会用new以外的方式去创建对象),所以一般是不需要程序员去实现finalize的。

JDK 和 JRE 有什么区别?

JRE(Java Runtime Enviroment) :是Java的运行环境,JRE是运行Java程序所必须环境的集合,包含JVM标准实现及 Java核心类库

JDK(Java Development Kit) :是Java开发工具包,它提供了Java的开发环境(提供了编译器javac等工具,用于将java文件编译为class文件)和运行环境(提 供了JVM和Runtime辅助包,用于解析class文件使其得到运行)。JDK是整个Java的核心,包括了Java运行环境(JRE),一堆Java工具tools.jar和Java标准类库 (rt.jar)。

面向对象四大特性

抽象 : 是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面,抽象只关注对象的哪些属性和行为,并不关注这此行为的细节是什么 - 举例:定义一个persion类,了就是对这种事物的抽象

封装:对数据的访问只能通过已定义的接口,封装就是隐藏一切可隐藏的东西,只向外界提供最简单的编程接口,比如在Java中,把不需要暴露的内容和实现细节隐藏起来,或者private修饰,然后提供专门的访问方法,如JavaBean。 - 生活举例:电脑主机就是把主板等封装到机壳,提供USB接口,网卡接口,电源接口等。 JavaBean就是一种封装。

继承:新类(子类,派生类)继承了原始类的特性,子类可以从它的父类哪里继承方法和实例变量,并且类可以修改或增加新的方法使之更适合特殊的需要。

多态:多态是指允许不同类的对象对同一消息做出响应。对象的多种形态,当编译时类型和运行时类型不一样,就是多态,意义在于屏蔽子类差异

方法覆盖和重载

方法的覆盖是子类和父类之间的关系,方法的重载是同一个类中方法之间的关系。 覆盖只能由一个方法,或只能由一对方法产生关系;方法的重载是多个方法之间的关系。 覆盖要求参数列表相同;重载要求参数列表不同。

普通类和抽象类

抽象类不能被实例化, 需要通过子类实例化 抽象类可以有构造函数,被继承时子类必须继承父类一个构造方法,抽象方法不能被声明为静态。 抽象方法只需申明,而无需实现,抽象类中可以允许普通方法有主体 含有抽象方法的类必须申明为抽象类 抽象的子类必须实现抽象类中所有抽象方法,否则这个子类也是抽象类

接口和抽象类

定义接口使用interface,定义抽象类使用abstract class

接口由全局常量,抽象方法,(java8后:静态方法,默认方法)

抽象类由构造方法,抽象方法,普通方法

接口和类是实现关系,抽象类和类是继承关系

ArrayList和LinkedList区别?

ArrayList是基于数组实现的,根据索引随机访问元素性能高,但是插入和删除元素性能差,因为这会涉及到移位操作

LinkedList是基于双链表实现的,不支持索引,随机访问元素需要从头查找,因此性能差,但是添加删除性能高因为不涉及移位操作,但是LinkedList容易造成内存的碎片化,增加内存管理难度。

根据实际需要,如果项目中使用查找较多,使用ArrayList,如果使用增删较多,请使用LinkedList

HashMap在什么情况下扩容?如何扩容?

HashMap的数组初始容量是16,负载因子是0.75,也就是说当数组中的元素个数大于12个,会成倍扩容

HashMap是如何Put一个元素的?

首先,将key进行hash运算,将这个hash值与上当前数组长度减1的值,计算出索引。此时判断该索引位置是否已经有元素了,如果没有,就直接放到这个位置

如果这个位置已经有元素了,也就是产生了哈希碰撞,那么判断旧元素的key和新元素的key的hash值是否相同,并且将他们进行equals比较,如果相同证明是同一个key,就覆盖旧数据,并将旧数据返回,如果不相同的话

再判断当前桶是链表还是红黑树,如果是红黑树,就按红黑树的方式,写入该数据,

如果是链表,就依次遍历并比较当前节点的key和新元素的key是否相同,如果相同就覆盖,如果不同就接着往下找,直到找到空节点并把数据封装成新节点挂到链表尾部。然后需要判断,当前链表的长度是否大于转化红黑树的阈值,如果大于就转化红黑树,最后判断数组长度是否需要扩容。

HashMap是如何Get一个元素的?

首先将key进行哈希运算,计算出数组中的索引位置,判断该索引位置是否有元素,如果没有,就返回null

如果有值,判断该数据的key是否为查询的key,如果是就返回当前值的value

如果第一个元素的key不匹配,判断是红黑树还是链表

如果是红黑树,就就按照红黑树的查询方式查找元素并返回

如果是链表,就遍历并匹配key,让后返回value值

什么是Hash冲突?

哈希冲突,也叫哈希碰撞,指的是两个不同的值,计算出了相同的hash,也就是两个不同的数据计算出同一个下标,通常解决方案有:

拉链法,把哈希碰撞的元素指向一个链表

解决方式:

开放寻址法,把产生冲突的哈希值作为值,再进行哈希运算,直到不冲突

再散列法,就是换一种哈希算法重来一次

建立公共溢出区,把哈希表分为基本表和溢出表,将产生哈希冲突的元素移到溢出表

HashMap是如何解决Hash冲突的?

采用拉链法,将哈希碰撞的元素,转化为链表

从JDK1.8开始,增加了红黑树,当链表的长度大于8,就将链表转化为红黑树,来优化查询性能

创建线程是几种方式

方式一:继承Thread类,覆写run方法,创建实例对象,调用该对象的start方法启动线程 方式二:创建Runnable接口的实现类,类中覆写run方法,再将实例作为此参数传递给Thread类有参构造创建线程对象,调用start方法启动

方式三:创建Callable接口的实现类,类中覆写call方法,创建实例对象,将其作为参数传递给FutureTask类有参构造创建FutureTask对象,再将FutureTask对象传递给Thread类的有参构造创建线程对象,调用start方法启动

说一下Java中的集合体系,以及他们的特点

按照两种接口来分类:

Collection接口

List:

ArrayList:底层数据结构是数组,查询性能高,增删性能低

Vector:底层数据结构是数组,查询性能高,增删性能低

LinkedList:底层数据结构是双向链表,查询性能低,增删性能高

Set:

HashSet:无序不重复的,底层数据结构是数组+链表+红黑树,判断重复依据是hashCode()和equals()

TreeSet:有序不重复的,底层数据结构是数组+链表+红黑树,排序方式分为自然排序,比较器排序

Map接口

HashMap:key的值没有顺序,线程不安全

TreeMap:key的值可以自然排序,线程不安全

HashTable:它的key和value都不允许为null,线程安全

Properties:它的key和value都是String类型的,线程安全

Synchronized 和 lock的区别

他们都是用来解决并发编程中的线程安全问题的,不同的是

synchronized是一个关键字,依靠Jvm内置语言实现,底层是依靠指令码来实现;Lock是一个接口,它基于CAS乐观锁来实现的

synchronized在线程发生异常时,会自动释放锁,不会发生异常死锁,Lock在异常时不会自动释放锁,我们需要在finally中释放锁

synchronized是可重入,不可判断,非公平锁,Lock是可重入,可判断的,可手动指定公平锁或者非公平锁

线程的几种状态

新建状态:线程刚创建,还没有调用start方法之前

就绪状态:也叫临时阻塞状态,当调用了start方法后,具备cpu的执行资格,等待cpu调度器轮询的状态

运行状态:就绪状态的线程,获得了cpu的时间片,真正运行的状态

冻结状态:也叫阻塞状态,指的是该线程因某种原因放弃了cpu的执行资格,暂时停止运行的状态,比如调用了wait,sleep方法

死亡状态:线程执行结束了,比如调用了stop方法

sleep 和 wait的区别

第一,sleep方法是Thread类的静态方法,wait方法是Object类的方法

第二:sleep方法不会释放对象锁,wait方法会释放对象锁

第三:sleep方法必须捕获异常,wait方法不需要捕获异常

Synchronized和Lock的区别

他们都是用来解决并发编程中的线程安全问题的,不同的是

synchronized是一个关键字,依靠Jvm内置语言实现,底层是依靠指令码来实现;Lock是一个接口,它基于CAS乐观锁来实现的

synchronized在线程发生异常时,会自动释放锁,不会发生异常死锁,Lock在异常时不会自动释放锁,我们需要在finally中释放锁

synchronized是可重入,不可判断,非公平锁,Lock是可重入,可判断的,可手动指定公平锁或者非公平锁

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值