【原创】Java基础面试题①

2023-03-03 20:42:59 浏览数 (1)

Java面向对象编程的三大特性

1. 封装 封装是把一个对象的属性私有化,同时提供可以被外界访问的属性的方法。 2. 继承 继承是使用已存在的类作为基础建立新类的技术,可以在新类上定义新的属性和新功能,也可以使用父类的属性和功能,可以完成对父类功能的重写覆盖,也可以在父类的功能上进行增强。 继承的注意点:

代码语言:javascript复制
 (1)子类拥有父类非Private的属性和方法。
 (2)子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
 (3)子类可以用自己的方式实现父类的方法

3. 多态 多态是在程序编译阶段并不知道对象的引用变量所指向的具体类型,而在程序运行阶段才确定具体的引用变量指向的哪个类的实例对象。多态是发生在两个类中的关系上,两个类可以为继承关系,也可以为接口和实现关系。

String、StringBuffer和StringBuilder的区别

1. 可变性 String类中使用final关键字修饰的字符数组保存字符串(private final char value[]) StringBuilder和StringBuffer类都继自AbStraStringBuilder类,在AbStraStringBuilder中也是将字符串保存在字符数组中,但没有被final关键字修饰。 2. 线程安全性 String对象是不可变的,是线程安全的。 StringBuffer中的方法都被Synchronized关键字修饰,所以是线程安全的。 3. 性能 String类型的数据进行改变时,会生成新的String对象,然后会将引用地址指向新的String对象,占用内存空间比较多。 StringBuffer中的方法都是被Synchronized关键字修饰,所以执行效率会比较低。 StringBuilder中的方法没有被Synchronized关键字修饰,所以执行效率相对StringBuffer会有所提高,但会存在线程安全问题。 4. 使用场景 操作少量的数据使用String类型 单线程操作字符串缓冲区下操作大量数据使用StringBuilder 多线程操作字符串缓冲区下操作大量数据使用StringBuffer

接口和抽象类的区别

  1. 接口的方法默认是public,所有的方法在接口中不能有实现(Java 8开始接口的方法可以有默认实现),抽象类可以有非抽象方法。
  2. 接口中的实例变量默认final类修饰的,而抽象类中则可以不用final修饰。
  3. 一个类可以实现多个接口,但最多只能继承一个抽象类。
  4. 接口的实现类必须实现接口的所有方法,而抽象类中可以不完全实现。
  5. 接口不能被new关键字实例化。

==和equals的区别

Object类中的equals()方法本质上是用==比较,由于String和Integer类重写了equals()方法,使得String和Integer在使用equals()方法是发生了变化。

hashCode和equals

1. hashCode介绍: hashCode()作用是获取哈希码,也称散列码;实际上是返回一个int整数,哈希码用于确定对象在哈希表中的索引位置。hashCode()定义在JDK的Object类中,意味着Java中的任何了类都包含hashCode()方法。 哈希表的存储方式是键值对(key-value),它的特点是能够根据key会检索出对应的值。 2. HaseSet检查重复元素: HaseSet本质是无序不重复集合,底层是HashMap,HaseSet中的值为HashMap的key。将对象加入到HashSet时,HashSet会先计算对象的hashcode值来判断对象加入的位置,同时也会和其他哈希表中已经存在的对象的haseCode进行比较,如果hashcode不一致,则会假设对象没有重复出现。如果出现hashcode值相同的对象,则会调用equals()方法来检查对象是否相等。如果相等,则该元素无法加入HaseSet中,如果不相同,则会重新添加到其他位置。 3. HashCode和equals的规律: 如果两个对象相等,则hashcode一定是相同的,使用equals()比较返回的结果为true 如果两个对象有相同的hashcode值,他们不一定相等 equals方法被重写,则hashcode方法也必须被重写 hashcode()默认的行为是对堆上的对象产生独特值。如果没有重写hashcode(),则该class的两个对象无论如何都不会相等。

集合

1. 集合的分类: Collection和Map是集合的根接口。 Collection的子接口:Set接口,实现类HashSet,LinkedHashSet;List接口:ArrayList,LinkedList。 Map的子接口:HashMap,TreeMap。 2. List集合: ArrayList:本质是一个动态数组,当添加到集合的最后一位时,会自动进行扩容,扩容为原理长度的1.5倍。轻量级,查询快,增删慢。(线程不安全) LinkedList:本质是一个双向链表,提供了头插法和尾插法。增删快,查询慢,占用内存较小。 3. Set集合: 无序集合,不允许有重复元素,允许有空值,包含两个实现类HashSet和TreeSet。 HashSet:底层为Hash表,Hash表为Key--value的形式,因为Key不能重复,而Set集合中的数据作为Hash表中的Key,所以HashSet中的元素不允许重复。 TreeSet:有序集合,底层是TreeMap,本质是红黑树,相对于HashSet,TreeSet提供了一些额外的按排序位置访问的方法,TreeSet的排序分为两种,一种是自然排序,一种是定制排序。TreeSet的元素默认按照升序排序。 4. Map集合: HashMap:主要存储Key--value,不可重复的Key,但Value可以重复。HaseMap根据Key的HashCode值存储数据,也可以根据Key获取它。访问速度快(查询速度快)。HashMap最多允许一个键为空值,HashMap不支持线程同步,即同一时刻多个线程同时写入HashMap,可能会导致数据不一致。如果需要同步则需要使用Collections中的SynchronizedMap方法使HashMap具有同步能力。 TreeMap:是对Map集合的一种排序,底层是二叉树。 HashTable:是线程安全的,该类提供的方法都被Synchronized关键字修饰,效率低。

多线程

1. 线程的生命周期: 线程的生命周期有 新建---就绪----运行----阻塞----就绪----运行----死亡等状态。 2. 新建线程的方式: (1)继承Thread类,重写run()方法 (2)实现Runnable接口,重写run()方法,传入Thread作为实参,调用Thread的Start()方法执行。 (3)实现Callable类,重写call()方法,有返回值。 3. Synchronized、Lock和ThreadLocal解决多线程安全问题 Synchronized:是隐视锁,执行完同步的方法会自动释放锁。是一种悲观锁(悲观锁是指该线程执行时为防止其他线程进入,会拿到代码锁,等待线程执行完成后才释放锁),Synchronized作用于普通方法时,所使用的锁是对象的实例(this),Synchronized作用于静态方法时,所使用的锁是当前类的Class对象,Synhronized为同步代码块时,所使用的锁为任意对象,Synhronized的调用者会排队等待锁,不适合高并发下的线程安全的解决。 Lock:是显示锁,需要手动打开和关闭,常用的方法lock()、tryLock()获取锁(tryLock方法有返回值,获取到锁会返回true),unlock()释放锁。当发生异常后不会自动释放锁。 ThreadLocal:本地线程,多线程并发环境下,使用线程隔离完成多线程安全问题的解决。为每一个线程提供一个变量副本,保证当前线程方法中的互不干扰。事务中处理多条数据库业务需要保证每个线程链接数据库的Connection对象一致,就需要使用ThreadLocal对象实现,每个Thread对应一ThreadLocalMap对象,本质是一个Entity数组,保存数据为key-value的形式,其中key为ThreadLocal对象,Value为保存的变量副本。 4. 可重入锁 可重入锁是一个类中的A,B两个方法都获得同一把锁,当A方法被调用时,获得锁,在A方法的锁还没释放时,调用B方法,B方法也可以获得该锁,Synchronized是一种可重入锁。 5. 悲观锁和乐观锁 悲观锁:为防止其他方法修改数据,自己会进行加锁,直到自己处理完才会释放锁,Synchronized是一种悲观锁。 乐观锁:在拿到数据后不会立即进行数据的锁定,在完成数据的修改时会判断数据是否一致,才会去获取锁。乐观锁存在一个版本号机制,如每次修改数据都会拿当前修改数据的版本号和原本数据的版本号进行对比,如果当前数据的版本号大于或等于原有数据的版本号,才会去更新数据。 6. Java中的线程池 线程池通过复用线程,避免线程频繁创建和销毁。 Java的Executors工具类中,提供了4种类型线程池的创建方法,它们的特点和适用场景如下:

代码语言:javascript复制
  第1种是:固定大小线程池(newFixedThreadPool),特点是线程数固定,可以控制线程最大的并发数,超出的线程会在队列中等待。使用无界队列,适用于任务数量不均匀的场景、对内存压力不敏感,但系统负载比较敏感的场景;
  第2种是:Cached线程池(newChacheThreadPool),特点是不限制线程数,如果线程池长度超过处理需要,则回收空线程,否则创建新线程。适用于要求低延迟的短期任务场景;
  第3种是:单线程线程池(newSignleThreadExcutor),也就是一个线程的固定线程池,适用于需要异步执行但需要保证任务顺序的场景;
  第4种是:Scheduled线程池(newScheduledThreadPool),适用于定期执行任务场景,支持按固定频率定期执行和按固定延时定期执行两种方式;

反射

1. 反射的介绍: 反射是Java中的一种动态机制,在程序运行期间动态获取指定类中的属性,执行指定类中方法的一种技术。 2. 反射的几种实现方式: ①通过Class.forName(类的完全限定名实现)。 ②通过类.class实现。 ③通过对象.getClass()实现。 3. 反射的优缺点: 优点:反射可以使代码变得灵活,为各种框架提供开箱即用的功能提供了便利 缺点:让程序在运行状态下有了操作类的能力,也增加了安全问题,比如可以无视访问权限的控制,打开参数的访问权限,访问参数等。相比使用new关键字创建对象,使用对象调用方法的情况,反射执行效率相对较低。 4. 反射的使用场景: Mybatis中Mapper的Xml文件等标签中resultType属性的值为类的完全限定名,即通过反射的机制将数据封装到类中 Spring框架中的Bean标签,使用类的完全限定名实例化类的对象,也是通过反射的机制实现的。

序列化和反序列化

1. 需要序列化的原因: 如果需要持久化将Java对象保存到文件中,或在网络中传输Java对象,则需要将对象进行序列化操作。 2. 序列化和反序列化的介绍: 序列化:将数据结构转换成二进制字节流的过程。 反序列化:将在序列化过程中生成的二进制字节流转换成数据结构或者对象的过程。 3. 实现序列化的操作 ①如果一个对象需要实现序列化,则需要实现Serializable接口。 ②对象中不想被序列化的属性可以使用transient关键字修饰。

xml解析

xml使用自定义标签的方式,完成结构化数据的保存,在项目开发中主要进行项目的配置。xml是需要定义标签的规范或者约束的,保证能对xml文件正常解析。 xml文件的应用场景:在项目开发中主要进行项目的配置 xml文件的解析方式: DOM解析:jdk提供的,Java官方的解析,使用了dom解析,将xml文件一次性加载到内存中,形成一颗dom树,解析的Java代码和xml文件的结构,联系紧密,所以兼容性比较差,消耗资源大,效率低,一次性需要将整个文件加载到内存。 DOM4j解析:第三方的解析方式,对dom进行了优化,优化了兼容性和效率问题。结合sax和dom俩种解析方式,不是一次性将文件加载到内存。而是解析一个节点,加载一个节点,解析完成后,删除该节点,所以比较节省内存。

0 人点赞