JavaSe 面试题总结

JavaSe

  1. 如何理解面向对象和面向过程?

面向过程把问题分解成一个一个步骤,每个步骤使用函数实现,依次调用即可;

面向对象将问题每个步骤抽象成对象,通过不同对象间的调用,组合解决问题;

面向对象有三大特性:封装、继承、多态,以及五大基本原则:单一职责,开放封闭,里氏代换,依赖倒置和接口隔离原则

  1. 什么是多态?

多态允许父类类型的引用变量来引用子类对象,并在运行时根据实际对象的类型来确定调用哪个方法。

多态有以下特点和优势:

  • 可替换性:子类对象可以随时替代父类对象,向上转型;

  • 可扩展性:通过添加新的子类,可以扩展系统的功能;

  • 接口统一性:可以通过父类类型的引用访问子类对象的方法,统一对象的接口;

  • 代码的灵活和可维护性:通过多态,可以将代码编写成通用、松耦合,提高代码的可维护性。

动态绑定

指在编译时,Java编译器只能知道变量的声明类型,而无法确定其实际的对象类型。而在运行时JVM会通过动态绑定来解析实际对象的类型。这也就是说,编译器会推迟方法的绑定(即方法的具体调用)到运行时

虚拟方法调用

所有的非私有、非静态和非final方法都是被隐式地指定为虚拟方法,虚拟方法调用是在运行时根据实际对象类型来确定要调用的方法机制。当通过父类类型的引用变量调用被子类重写时。JVM会根据实际对象的类型来确定要调用的方法版本,而不是根据引用变量的声明类型。

实现原理:

  1. 创建父类类型的引用变量,并将其赋值给子类对象

  2. 在运行时,通过动态绑定确定引用变量所指向的实际对象类型

  3. 根据实际对象的类型,调用相应的方法版本

  1. 接口和抽象类的区别?如何选择

  1. 方法定义,在不考虑Java8引入的接口default方法情况下,接口中只有抽象的方法,没有实现的代码;

  2. 定义的关键词不同,抽象类是abstract,接口是interface

  3. 修饰符,抽象类中修饰符可以有public、protected和private、<default>这些修饰符,而接口中默认修饰符是public,不可以使用其他修饰符(接口中,如果定义了成员变量,还必须初始化)

  4. 构造器,抽象类可以有构造器,接口不能有构造器,抽象类不能直接被实例化(new),但是构造器也是有意义的,能够起到初始化共有成员变量、强制初始化操作等作用;

  5. 继承与实现:接口可以被实现,抽象类可以被继承;

  6. 单继承,多实现;

  7. 职责不同,接口主要用于指定规范,抽象类则是为了复用

  1. 重载与重写的区别?

  1. 重载是一个编译期概念,重写是运行期概念;

  2. 重载遵循“编译期绑定”,即在编译时根据参数变量的类型来判断应该调用哪个方法;

  3. 重写遵循”运行期绑定“,即在运行的时候,根据引用变量所指向的实际对象的类型来调用方法。

  1. Java中有了基本类型为什么还需要包装类?

Java是面向对象的语言,很多地方需要使用对象而不是基本数据类型。比如处理集合时,我们无法将int,double放入,集合存放的元素是Object类型。

基本类型和包装类型的区别
  1. 默认值不同,基本类型默认值为0,false等,包装类默认值为null

  2. 初始方法不同,一个需要new,一个不需要

  3. 存储位置不同,基本类型保存在栈上,包装类对象存储在堆上

如何理解自动拆装箱?
  1. 拆箱与装箱

包装类是对基本类型的包装,把基本类型转换成包装类的过程就是装箱;反之,把包装类转换为基本数据类型的过程是拆箱。

  1. 自动拆装箱

自动装箱是通过valueOf() 方法实现的。

自动拆箱是通过xxxValue()来实现的

  1. 在以下场景会发生自动拆装箱:

     

    Integer a = 1; System.out.println(a ==1?"等于":"不等于"); //自动拆箱为 System.out.println(a.intValue() == 1?"等于":"不等于");

    1. 将基本类型放入集合中

    2. 包装类型和基本类型的大小比较

    3. 包装类型的运算

    4. 三目运算符的使用

    5. 函数参数与返回值

  2. 自动拆装箱与缓存

对于Integer 数字在-128至127之间,会直接使用缓存中的对象,而不是重新创建一个对象。

  1. BigDecimal为什么不能使用equals()?

BigDecimal,是对象类,其equals方法和compareTo并不一样,equals方法会比较两部分内容,分别是值(value)和标度(scale),如果比较0.1与0.10,equals可能会返回false

compareTo方法可以只比较两个数字的值,如果相等则返回0

  1. 为什么对Java中的负数取绝对值,结果不一定为正数?

因为负数和正数的范围不一致,在取绝对值时转换为long,再进行就不会出错。

  1. String、StringBuilder、StringBuffer的区别?

String不可变,StringBuilder和StringBuffer可变,StrinBuilder是线程不安全的,StringBuffer是线程安全的

String为什么不可变?
  1. String类被声明为final,这意味着它不能被继承,那么其中的方法没办法被覆盖的

  2. 用final修饰的字符串内容char[],一旦数组被初始化,就不能再指向其他数组;

  3. String类没有提供用于修改字符串内容的公共方法。

  4. String的substring,concat等都是通过new String()方式新建了一个字符串

String的'+'是如何实现的?

使用+拼接字符串,实际上使用了StringBuiler()的append方法进行字符串拼接

Sting str = new String("abc")创建了几个对象?

1个或者2个

首先不管怎么样,每次new的过程中,都会在堆上创建一个对象;另外一个对象是常量池中的字符串常量,这个字符串其实是编译阶段就进入Class常量池的,然后在运行期,字符串常量在第一次被调用时,进行解析并在字符串池中创建对应的String实例

intern()用于将字符串放入常量池中,以及返回这个常量的引用。

String的equals()和Object的equals()有何区别?

String的equals()是被重写过的,比较的是String字符串的值是否相等。Object的equals方法是比较对象的内存地址。

  1. 成员变量和局部变量的区别?

  1. 语法形式:成员变量属于类,局部变量属于代码块或方法中定义的变量或方法的参数,成员变量可以被public,private,static等修饰符修饰,而局部变量不能被访问控制修饰符及static所修饰;但是都能被final所修饰。

  2. 存储方式:从变量在内存中的存储方式来看,如果成员变量是使用static修饰,则其属于类,否则属于实例;实例对象的成员变量存在堆内存,静态变量则存放在方法区,局部内存则存在于栈内存。

  3. 生存周期:成员变量是类对象的一部分,随着对象创建存在;局部变量随着方法的调用自动生成,方法结束而消亡;

  4. 默认值:成员变量没有被赋初值,则会自动以类型默认值赋值(被final修饰的变量,必须显示赋值);局部变量一般不会自动赋值。

  1. 静态方法为什么不能调用非静态成员?

  1. 静态方法属于类,在类加载阶段就会分配内存,可以通过类名直接访问。而非静态成员属于实例对象,只有在对象实例化之后才存在,需要通过类的实例对象去访问。

  2. 在类的非静态成员不存在时,静态方法就已存在,此时调用在内存中还不存在的非静态成员,属于非法操作。

  1. 重载与重写

  1. 重载发生在编译期,是同一个类中多个同名方法根据不同的传参来执行不同的逻辑处理

  2. 重写发生在运行期,是子类对父类的允许访问的方法的实现过程进行重新编写

    1. 方法名,参数列表必须相同,子类方法返回值类型应比父类方法返回值类型更小或相等,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类;

    2. 如果父类方法修饰符为private/fina/static,则子类不能重写该方法,但是被static修饰的方法,能够被再次声明;

    3. 构造方法无法被重写

  1. 对象和引用的区别?

对象实例存储在堆内存中,对象引用指向对象实例(存储在栈内存中)。

对象相等和引用相等的区别
  1. 对象的相等一般比较的是内存中存放的内容是否相等;

  2. 引用相等一般比较指向的地址是否相等。

  1. 深拷贝和浅拷贝区别?什么是引用拷贝?

  • 浅拷贝:在堆上创建一个新的对象,如果原对象内部属性是引用类型,浅拷贝会直接复制内部对象的引用地址,浅拷贝和原始对象共用一个内部对象;

  • 深拷贝:会完整复制整个对象,包括这个对象所包含的内部对象

  • 引用拷贝:使用两个不同的引用指向同一个对象。

  1. ==与equals的区别?

  • ==对于基本类型与引用类型的作用效果是不同的:

    • 对于基本类型,比较的是值

    • 对于引用类型,比较的是对象的内存地址

  • equals()不能用于比较基本类型,只能判断两个对象是否相等

    • 类没有重写equals()方法:通过equals()比较该类的两个对象,等价于使用“==”比较,使用的是Object类的equals()方法。

    • 类重写了equals()方法:则比较对象属性是否一致

  1. 为什么重写equals()时必须重写hashCode()方法?

因为两个相等对象的hashcode值必须相等,如果只重写equals()方法,没有重写hashCode(),可能导致equals判断相等,但hashcode值不相等

  1. 异常

Exception和Error的区别?
  • Exception,是程序可以处理的异常,通过catch来进行捕获。Exception又可以分为Checked Exception(受检查异常,必须处理)和Unchecked Exception(不受检查异常,可以不处理)。

    • Checked Exception,除RuntimeException及其子类以外,其他的Exception类及其子类都属于受检查异常;

    • Unchecked Exception,即使不处理也可以正常通过编译,如空指针异常、参数错误、字符串转换为数字格式错误、数组越界、类型转换错误、算术错误、安全错误;

  • Error,属于程序无法处理的错误,例如Java虚拟机运行错误,虚拟机内存不够错误;

Throwable类常用方法有哪些?
  • String getMessage(): 返回异常发生时的简要描述;

  • String toString(): 返回异常发生时的详细信息;

  • String getLocalozedMessage(): 返回异常对象的本地化信息;

  • Void printStackTrace(): 在控制台打印Throwable对象封装的异常信息。

try-catch-finally如何使用?

注意:

  • 不要在finally中使用return语句,当try和finally中都有return语句时,try语句块中的return语句会被忽略掉。这是因为try语句中的return返回值会被先暂存在一个本地变量中,当执行到finally语句中的return之后,本地变量的值就变为finally语句中return返回值。

  • finally中的代码不一定会执行,当执行finally之前,虚拟机就被终止运行,那么finally中的代码就不会被执行,如System.exit(1)、程序所在线程死亡、关闭CPU.

异常使用有哪些需要注意的地方?
  • 不要把异常定义为静态变量,因为这样会导致异常栈信息错乱;

  • 建议抛出具体的异常;

  • 避免重复记录日志

  1. 什么是泛型?有什么好处?

允许在定义类和接口的时候使用类型参数(type)。声明的类型参数在使用时用具体的类型来替换。

好处:

  • 方便: 可以提高代码的复用性;

  • 安全:在编译时做类型检查,可以提高程序的安全性。

泛型的使用方式:泛型类、泛型接口、泛型方法
项目中哪里用到了泛型?
  • 自定义接口通用返回结果,Result<T>通过参数T,根据返回类型动态指定返回的数据类型

  • 定义Excel处理类,用于动态指定Excel导出的数据类型。

List<?>,List<Object>,List之间的区别?
  • List<?>是一个未知类型的List,而List<Objcet>其实是任意类型的List

  • 可以把任何带参数的类型传递给原始类型List,但却不能把List<String>赋值给List<Object>,因为会产生编译错误;

  • List<?>由于不确定列表中元素的具体类型,因此只能从这种列表中读取,不能添加除null之外的任何元素。

  1. 泛型中上下界限定符extends和super有什么区别?

  • <? Extends T> 表示类型的上界,表示参数化类型的可能结果是T或者T的子类。

public <T extends Number> void processNumber<T number>{
 double value = number....; 
}
  • <? super T>表示类型下界,不鸟是参数化类型是此类型的超参数(父类型)。

 

注意:使用限定通配符时,需遵守上界生产,下界消费原则,即extend是读取不可写入,super是写入不可读取。

  1. 反射

反射指的是,在程序运行时能够获取自身的信息,只要给定类的名称,就可以通过反射机制类获取类的所有属性和方法。

反射优点:提高程序的灵活性和扩展性

缺点:代码可读性低,可维护性不高;执行效率低;反射破坏封装。

反射慢的主要原因?
  1. 反射涉及动态解析类型,因此不能执行某些Java虚拟机优化,如JIT优化等;

  2. 使用反射时,参数需要包装成Object[]类型,执行时又需要拆包成真正的类型,这些动作耗时且占内存,容易引起GC;

  3. 反射调用方法时,会从方法数组中遍历查找,并且会检查可见性,需要耗时。

  4. 除了检查方法可见性外,还会进行参数的检查。

反射常见使用场景
  1. 动态代理

  2. JDBC的class.forName

  3. BeanUtils中属性值的拷贝

  4. RPC框架

  5. ORM框架

  6. Spring的IOC/DI

  1. Java的动态代理如何实现?

  • JDK动态代理

被代理对象必须实现一个或多个接口

  • CGlib代理

静态代理和动态代理的区别?

最大区别是静态代理是编译期决定的,但动态代理却是运行期确定的。

  1. Java注解的作用?

注解用于为Java代码提供元数据,注解不直接影响你代码的执行,主要用于修饰类、方法或者变量,提供某些信息供程序在编译或运行时使用。

元注解

即定义其他注解的注解。有四个:

@Target(表示该注解可以用于什么地方,ElementType.TYPE, METHOD),

@Retention(表示在什么级别保存该注解信息),

@Documented(将此注解包含在javadoc中),

@Inherited(允许子类继承父类中的注解)

  1. ArrayList和LinkedList有什么区别?

    1. 底层实现不同,ArrayList是基于动态数组,LinkedList基于链表;

    2. 随机访问的性能不同,ArrayList优于LinkedList,因为ArrayList可以根据下标以O(1)的时间复杂度对元素进行随机访问。而LinkedList的访问时间复杂度为O(n),因为它需要遍历整个链表

    3. 插入和删除性能不同,LinkedList优于ArrayList,因为LinkedList的插入和删除操作时间复杂福为O(1),ArrayList为O(n)

  1. ArrayList和Vector有什么区别?

二者都是实现了List接口,使用动态数组实现,也拥有相同的方法。

区别:

  1. 线程安全性,Vector是线程安全的,而ArrayList不是

  2. 性能,Vector是线程安全的,所以性能偏低

  3. 初始容量的增长方式,当容量不足时ArrayList默认增加50%,而Vector会将容量翻倍。这意味着ArrayList需要频繁地进行扩容操作,而Verctor适合存储大量数据

  1. 为什么Map不能插入null(concurrentHashMap)

concurrentHashMap在插入key 为null 或者 value为null时,会抛出空指针异常。

如果允许其插入null,则会产生”二义性“问题;

对比HashMap为什么可以插入null,可以用hashMap。containsKey(key)来区分是存入的null还是不存在的null,但是concurrentHashMap是多线程的,在containsKey(key)还没返回结果时,其他线程如果插入null,则会影响本线程的结果。

Hashtable 也不允许使用null作为key或者value
  1. HashMap和HashTable有什么区别?

HashMap和HashTable都实现了Map接口,都是java中用于存储键值对的数据结构,底层数据结构都是数组加链表的形式,但也有以下的区别:

  1. 线程安全,Hashtable是线程安全的,HashMap是非线程安全的

  2. 性能,HashTable使用了synchronized给整个方法加锁,所以性能较差

  3. 存储,HashMap允许null的key和value,HashTable不允许

  1. ConcurrentHashMap和HashTable的区别?

主要区别在线程安全的实现方式上:

  1. 底层数据结构:

      JDK1.7的ConcurrentHashMap底层采用分段数组+链表实现,JDK1.8采用数组+链表/红黑二叉树。Hashtable底层采用数组+链表

  1. 实现线程安全的方式:
    1. HashTable使用synchronized修饰HashMap方法,JDK1.7中ConcurrentHashMap对整个桶数组进行分割分段(Segment,分段锁),每一把所只锁容器其中一部分数据,多线程访问容器里不同数据段就不会存在锁竞争,提高并发访问效率,Segment继承自ReentrantLock是一种可重入锁,Segment的个数一旦初始化就不能改变

    2. JDK1.8使用Node数组+链表/红黑树,Node + CAS + synchronized,锁粒度进一步缩小,synchronized只锁定当前链表或红黑树的首节点,

      不使用ReentantLock的原因,synchronized是JVM内置的语义,JVM可以做优化;其次当获取锁失败,synchronized可以通过自旋避免线程被挂起,而可重入锁汇导致线程挂起,线程挂起汇增加上下文切换的开销

    1. Hash碰撞的解决方法,JDK 1.7 采用拉链法,JDK1.8 采用拉链法结合红黑树(链表长度超过一定阈值时,将链表转换为红黑树)。

    2. 并发度,JDK1.7最大并发度是Segment的个数,默认为16,JDK1.8最大的并发数是Node的个数,并发度更大

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值