javaSE面试题

什么是多态?多态的实现方式?

多态是同一个行为具有不同的表现形式或形态的能力
实现方式:

  • 方法重写
  • 方法重载
  • 抽象类和接口

反射的优缺点

优点:能够在运行时动态获取类的实例,提高系统的灵活性和扩展性,如动态代理技术

缺点:性能较差;破坏了类的封装性,安全性较差;

反射为什么慢?

  1. 需要进行动态类型检查
  2. JIT优化受限
  3. 需要使用字符串名称识别类型和成员,字符串搜索不区分大小写,速度较慢
  4. 需要进行类型安全检查

为什么重写equals()就要同时重写hashcode()方法?

因为set和Map集合在判断对象是否相等时不仅依赖equals方法,还依赖于hashcode()方法返回的哈希码

泛型擦除的缺点

  1. 泛型参数不支持基本类型只支持引用类型,因为泛型会被最终擦除为具体类型,而Object不能存储基本类型的值
  2. 运行时只能对原始类型进行类型检测,无法判断带泛型的类型
  3. 不能实例化类型参数
  4. 不能实例化泛型数组

如何通过反射创建对象

  1. 使用.class语法

如果在编译时已经知道要操作的类,可以直接使用.class语法来获取Class对象。这种方式不需要类的全路径名,只需要类名即可。例如:

java 复制代码
Class clazz2 = MyClass.class;
  1. 使用Class.forName()

这种方式需要你知道类的完整路径名。通过调用Class.forName()静态方法并传入类的全路径名,可以获取到对应的Class对象。例如:

java 复制代码
Class clazz1 = Class.forName("com.example.MyClass");
  1. 使用getClass()方法

如果已经有了一个类的实例,可以通过调用该实例的getClass()方法来获取Class对象。这种方式适用于已经存在的对象实例。例如:

java 复制代码
MyClass myObject = new MyClass();
Class clazz3 = myObject.getClass();

获取到类后后使用newInstance()方法创建新的实例:

java 复制代码
Object obj = clazz3.newInstance();

权限分类?枚举类权限?


枚举类的构造器只能使用 private 权限修饰符

自定义异常实现

自定义异常类的创建涉及以下几个步骤:

  • 继承Exception类:自定义的异常类需要继承Java的Exception类。

  • 定义异常信息:可以在自定义异常类中定义一个字符串变量来存储异常信息。

  • 构造函数:自定义异常类应该包含一个构造函数,它接收一个字符串参数作为异常信息,并将其传递给父类的构造函数。

Constructor.newInstance()与Class.newInstance()的区别

  • Class.newInstance() 只能够调用无参的构造函数,即默认的构造函数; Constructor.newInstance() 可以根据传入的参数,调用任意构造构造函数。

  • Class.newInstance() 抛出所有由被调用构造函数抛出的异常。

  • Class.newInstance() 要求被调用的构造函数是可见的,也即必须是public类型的; Constructor.newInstance() 在特定的情况下,可以调用私有的构造函数。

ArrayList的扩容机制?

当添加的元素数量超过当前数组的长度时,ArrayList 会进行扩容,ArrayList 的初始容量为10 新的容量是旧容量的1.5倍左右。这是通过将旧容量右移一位(即除以2)然后加上旧容量来实现的,但这并不是严格的1.5倍,因为如果旧容量是奇数,则会丢失小数部分。

ConcurrentHashMap线程安全体现在哪些方面?

在 JDK 1.8 中,添加元素时首先会判断容器是否为空,如果为空则使用 volatile 加 CAS 来初始化。如果容器不为空则根据存储的元素计算该位置是否为空,如果为空则利用 CAS 设置该节点;如果不为空则使用 synchronize 加锁,遍历桶中的数据,替换或新增节点到桶中, 最后再判断是否需要转为红黑树,这样就能保证并发访问时的线程安全了。

我们把上述流程简化一下,我们可以简单的认为在 JDK 1.8 中,ConcurrentHashMap 是在头节点加锁来保证线程安全的,锁的粒度相比 Segment 来说更小了,发生冲突和加锁的频率降低了,并发操作的性能就提高了。而且 JDK 1.8 使用的是红黑树优化了之前的固定链表,那么当数据量比较大的时候,查询性能也得到了很大的提升,从之前的 O(n) 优化到了 O(logn) 的时间复杂度

Integer缓存?

Java的Integer类内部实现了一个静态缓存池,用于存储特定范围内的整数值对应的Integer对象。默认情况下,这个范围是-128至127。当通过Integer.valueOf(int)方法创建一个在这个范围内的整数对象时,并不会每次都生成新的对象实例,而是复用缓存中的现有对象。

何时会出现InterruptedException?

当一个线程处于阻塞状态下(例如休眠)的情况下,调用了该线程的interrupt()方法,则会出现InterruptedException。

什么是intern()方法

String.intern()是一个Native方法,它的作用是:如果字符常量池中已经包含一个等于此String对象的字符串,则返回常量池中字符串的引用,否则,将新的字符串放入常量池,并返回新字符串的引用'

BIO、NIO、AIO

项目 BIO NIO AIO
异步/阻塞 同步阻塞 同步阻塞/非阻塞 异步非阻塞
线程模型 一个连接一个线程,线程会一直阻塞,知道IO完成 一个请求一个线程,通过IO多路复用轮询连接状态 一个有效请求一个线程发起IO后立刻返回,等通知
使用场景 适用简单的连接数少的场景,维护成本高 适用于高并发,大量连接,适合长连接的应用如Netty 应用较少,但应用于对IO要求高的场景,如高性能服务器

forname和classloader的区别

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

面向对象和面向过程都是一种开发思想。

面向过程就是根据解决问题所需要的步骤,具体化的一步一步的去实现。

面向对象就是把数据及对数据的操作方法放在一起,作为一个整体,也就是对象,若干个这样的整体组成一个系统去解决实际问题。

面向过程只用函数实现,性能比较快因为不需要进行实例化,但是不容易扩展、维护,复用

面向对象通过类去实现功能模块,代码安全性高,容易扩展和复用,比较灵活且便于维护

面向对象有哪些特性?

面向对象有三大特性:封装,继承和多态

  • 封装就是将类信息隐藏在类内部,不允许外部程序直接访问,而是通过调用类的方法去访问,良好的封装能减少耦合,增加安全性。
  • 继承就是从已有的类派生出新的类,新的类继承父类的属性和方法,并扩展新的能力,极大提高了代码的复用行和易维护性。
  • 多态是指同一个行为具有多种形式的表现,具体体现在继承,重写,重载,父类引用指向子类对象等,多态体现出面向对象的灵活性。

Finally代码块中的内容一定会执行吗?

finally 块中的代码确实会执行,但在系统崩溃、无限循环、强制终止程序和守护线程等极端情况下,finally

块中的代码可能不会执行。在实际编程中,finally 块通常用于释放资源、清理状态或执行其他必要的收尾工作

String是如何实现不可变的

  • final类和final字段
    String类被声明为final,这意味着它不能被继承。此外,String类中的字段(如value数组)也被声明为final,这意味着它们在初始化后不能被修改。
  • 字符数组的私有性
    String类中的value数组是私有的,这意味着外部代码无法直接访问和修改它。这确保了String对象的值不会被外部代码修改。
  • 方法的返回值
    String类中的方法(如substring、concat等)不会修改原始String对象,而是返回一个新的String对象。这确保了原始String对象的值不会被改变。
  • 字符串常量池
    Java通过字符串常量池(String Constant Pool)机制进一步增强了String的不可变性。字符串常量池是一个特殊的内存区域,用于存储字符串字面量和字符串对象的引用。当创建一个新的字符串字面量时,Java首先检查字符串常量池中是否已经存在相同值的字符串,如果存在,则返回该字符串的引用,而不是创建一个新的字符串对象。

Error可以捕获吗

在 Java 中,Error 是继承自 Throwable 的一个子类,因此从技术上讲,它可以被 try-catch 块捕获。

链表和红黑树互相转换

一、链表长度超过 8,数组大小小于 64

如果链表长度超过了长度阈值 8。

如果数组大小等于16,小于最小树化容量64。

在这种情况下,即使链表长度超过了8,由于总容量小于64,链表不会转为红黑树,而是会进行扩容操作。
二、链表长度没有超过 8,数组大小大于 64

如果链表长度等于 5,没有超过长度阈值 8。

如果数组大小等于80,大于最小书画容量 64。

链表会在其长度达到 8 时才会转换为红黑树。因此,如果链表长度等于 5,那么它不会转为红黑树,无论数组(即HashMap的桶数组)大小是多少。
三、红黑树转换为链表

在HashMap实现中,红黑树会在一定条件下转换回链表。这主要是为了在删除元素后,保持合适的数据结构以优化性能和空间使用。红黑树转换为链表的条件如下:

树形化的红黑树节点数量小于等于 6:当红黑树节点的数量减少到6或更少时,红黑树会被转换回链表。这是因为在少量节点的情况下,链表的插入和删除操作比红黑树更高效。

最小树化容量:这是一个辅助条件,用于确保只有在HashMap的容量(桶数组大小)足够大时,才会执行链表到红黑树的转换和反转换。默认情况下,这个值是64。但是,转回链表的主要依据还是节点数量。

StringBuilder

StringBuilder的底层是一个char [ ]。

通过无参构造,append()添加元素时。数组的默认长度为16,每次扩容为数组原长度的2倍+2(value.length<<1+2)。

通过含参构造添加元素时。

①直接添加一个长度。数组的初始长度为该长度

②添加一个字符串。数组的初始长度为该字符串的长度+16(str.length+16)。每次扩容为数组原长度的2倍+2(和上述append()方法相同,因为也是通过append()添加元素的)

Java反射破坏了封装性吗?

一、用户通过反射机制获取了所使用的类中私有成员的存取权限,这是毫无意义的,因为我们上面分析过了,大多数的这些成员是依附于其它public方法而存在的,基本上都是为了服务这些public成员的。

二、紧接着上面的来说,就算用户拿到了private成员的存取权限,而且还"恶意"的修改类的私有成员,那么这么做的目的何在呢?这将大概率导致类的功能无法正常提供

HashMap和HashTable的区别

  • HashMap是非线程安全的,在多线程环境下,HashMap会产生线程安全问题;而HashTable中的大部分方法都使用synchronized关键字来确保线程同步,因此HashTable是线程安全的,不过性能要比HashMap低一些
  • HashMap的key可以使用null(但只能有一个),value可以为null,而HashTable都不允许存储key和value值为空的元素
  • HashMap继承了AbstractMap,HashTable继承了Dictionary抽象类,两者都实现了Map接口
  • HashMap的初始容量为16,- HashTable的默认容量大小为11,负载因子为0.75
  • HashMap的扩容机制为扩容两倍,而HashTable的扩容机制为两倍-1
  • HashTable不会转换为红黑树
  • HashTable采用头插法的方式迁移数组,相比较HashMap的尾插法来说效率更高
相关推荐
佚先森6 分钟前
2024ARM网络验证 支持一键云注入引流弹窗注册机 一键脱壳APP加固搭建程序源码及教程
java·html
菜鸟学Python7 分钟前
Python 数据分析核心库大全!
开发语言·python·数据挖掘·数据分析
一个小坑货14 分钟前
Cargo Rust 的包管理器
开发语言·后端·rust
bluebonnet2719 分钟前
【Rust练习】22.HashMap
开发语言·后端·rust
古月居GYH19 分钟前
在C++上实现反射用法
java·开发语言·c++
在下不上天1 小时前
Flume日志采集系统的部署,实现flume负载均衡,flume故障恢复
大数据·开发语言·python
陌小呆^O^1 小时前
Cmakelist.txt之win-c-udp-client
c语言·开发语言·udp
ifanatic1 小时前
[面试]-golang基础面试题总结
面试·职场和发展·golang