0%

【面试】Java100面试题

Java面试题

1 面向对象的理解

  • 要与面向过程对比
  • 面向过程:注重事情的每一个步骤以及顺序(直接高效)
  • 面向对象:注重事情有哪些参与者(易于复用、拓展和维护)
  • 面向对象的三大特性
    • 封住:提供给外部调用的属性和方法,内部细节无需外部知道以及修改
    • 继承:继承基类的方法,并作出自己的改变或拓展
    • 多态:父类引向子类对象父类 变量名 = new 子类(),创建的类虽然是父类,但调用的方法的逻辑,是子类的逻辑

2 JDK JRE JVM

  • JDK:Java Develpment Kit(Java开发工具)
  • JRE:Java Runtime Enviroment(Java运行时环境)
  • JVM:Java Virtual Machine(Java虚拟机)

3 ==和equals

  • 一般笔试中用于判断true / false
  • ==比较的是栈中的值,比较的是堆中内存对象的地址
    • 如果创建一个对象,用==比较,比较的是地址,而不是值
  • equals一般与==是同一比较效果
    • 但我们一般会对equals进行方法重写,用来比较类中的值,而不是地址

4 final

  • (1)final作用
    • 修饰类:表示类不可被继承
    • 修饰方法:表示方法不可被子类覆盖(重写),但可以重载
    • 修饰变量:表示变量一旦被赋值就不可以更改它的值
  • 修饰成员变量
    1
    2
    3
    public class Finalvar {
    final int b = 0; //在声明的时候就需要赋值;或者在代码块中赋值;或者构造器赋值
    }
  • 修饰局部变量
    1
    2
    3
    4
    5
    public class Finalvar {
    public static void main(String[] args) {
    final int a; //局部变量可以在声明的时候不赋值,但要在使用前进行赋值
    }
    }
  • (2)局部内部类和匿名内部类
    • 局部内部类和匿名内部类方法中的参数要添加final修饰,否者访问不到
    • 原因:
      • 内部类与外部类是同一级别,不会因为内部方法执行完后,会进行销毁
      • 实际执行流程,将局部变量赋值一份作为内部类的成员变量,当方法结束,局变量变量销毁,内部类仍然可以访问局部变量,即copy的变量
      • 复制变量的过程汇总,必须保证两个变量是一样的,所以添加一个final来修饰局部变量

5 String StringBuffer StringBuilder

  • String是final修饰的,不可变,每次操作都会产生新的String对象
  • StringBuffer和StringBuilder都是在原对象上操作
  • StringBuffer是线程安全的,StringBuilder线程不安全
  • StringBuffer方法都是synchronized修饰
  • 性能:StringBuilder > StringBuffer > String
  • 场景:经常需要改变字符串内容时使用后面两个,优先使用StringBuilder,多线程使用共享变量时使用StringBuffer

6 重载和重写

  • (太过容易简单回答,注意要详细)
  • 重载:发生在同一个类中,方法名相同,参数类型,个数,顺序不同,方法返回值和访问修饰符可以不同
  • 重写:发生在父子类中,方法名、参数列表必须相同,返回值的范围小于等于父类,抛出的异常小于等于父类,访问修饰符访问大于等于父类(如果父类为private,则子类不能重写该方法)

7 接口和抽象类的区别

  • 抽象类可以存在普通成员函数(可以有实现方法),而接口中只能存在public、abstract方法
  • 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型
  • 抽象类只能继承一个,接口可以实现多个

8 List和Set的区别

  • List:有序,按对象进入的顺序保存对象;可重复,允许多个Null元素对象,可以使用迭代器(Iterator)取出所有元素,逐一遍历,还以使用get(int index)获取指定下标的元素
  • Set:无序,不可重复,最多允许一个Null元素对象,取元素时只能用迭代器取出所有元素,再逐一遍历各个元素

9 hashCode与equals

  • 如果两个对象相等,则hashCode一定也相等
  • 两个对象相等 ,对两个对象分别调用equals方法都会返回true
  • 两个对象有相同的hashCode值,它们也不一定是相等,因此equals你发呗覆盖过,则hashCode方法也必须被覆盖
  • hashCode的默认行为是对对上的对象产生独特值,如果没有重写hashCode()方法,则该class的两个对象无论润滑都不会相等(即使这两个对象指向相同的数据)

10 ArrayList和LinkedList区别

  • ArrayList:基于动态数组,连续内存存储,适合下标访问
    • 如果ArrayList使用尾插法,就不会涉及元素的移动,就会极大提高性能,甚至会超过LinkedList
  • LinkedList:基于链表,分散在内存中,适合做数据插入以及删除操作,不适合做查询
    • 遍历LinkedList必须使用迭代器进行访问(for效率低),比较麻烦。
    • 另外不要用IndexOf来返回元素索引,当结果为空时,会遍历整个列表

11 HashMap和HashTable区别

区别

  • (1)HashMap方法没有synchronized修饰,线程非安全,HashTable线程安全
  • (2)HashMap允许key和value为null,而HashTable不允许

底层实现:(数组和链表实现)

  • JDK8开始链表高度到8,数组长度超过64,链表转为红黑树,元素以内部类Node节点存在
  • 实现方法类似HashSet,利用hashcode计算数组位置,判断该位置上是否有值?没,直接插入;有和链表上的数据比较hashCode,判断是否相同?不相同,链表上插入新数据;相同,使用equals比较,判断是否相同?相同,取消插入;不相同,将原来值修改为新插入的值

12 ConcurrentHashMap


13 如何实现一个IOC容器

  • (1)配置文件配置包扫描路径
  • (2)递归包扫描获取.class文件
  • (3)反射、确定需要交给IOC管理的类
  • (4)对需要注入的类进行依赖注入
  • 配置文件中指定需要扫描的包路径
  • 定义一些注解,分别表示访问控制层,业务服务成…
  • 从配置文件中获取需要扫描的包路径,获取到当前路径下的文件信息及文件夹信息,我们将当前路径下所有以.class结尾的文件添加到一个set集合中进行存储
  • 遍历这个set集合,获取在类上有指定注解的类,并将其交给IOC容器,定义一个安全的Map用来存储这些对象
  • 遍历这个IOC容器,获取到每一个类的实例,判断里面是否有依赖其他类的实例,然后进行递归注入

14 什么是字节码?采用字节码的好处是什么?

  • Java中的编译器和解释器
  • Java中引入了虚拟机的概念,即在机器和编译程序之间加入了一层抽象的虚拟机,这台虚拟的机器在任何平台上都提供给编译程序一个共同的接口(实现同一Java代码,能够在不同平台之间运行)
  • 编译程序只需要面向虚拟机,生成虚拟机能够理解的代码,然后由解释器来将虚拟机代码转换为特定系统机器码执行。
  • Java中,供虚拟机理解代码叫做字节码(.class文件)
  • 字节码好处
  • 一定程度上解决传统解释型语言执行效率低的问题,同时又保留解释型语言可移植的特点,无需重新边意思便可在多种不同的计算机上运行

15 Java类加载器有哪些?

  • JDK自带有三个类加载器:bootstrap ClassLoader、ExClassLoader、AppClassLoader
  • bootstrapClassLoader是ExtClassLoader的父类加载器,默认负责加载%JAVA_HOME%lib下的jar包和class文件
  • ExtClassLoader是AppClassLoader的父类加载器,负责加载%JAVA_HOME%/lib/ext文件下的jar包和class类
  • AppClassLoader是自定义类加载器的父类,负责加载classpath下的类文件。继承ClassLoader实现自定义类加载器

16 双亲委托(派)模型

双亲委派模型的好处:

  • 主要是为了安全性,避免了用户自己编写类动态替换Java的一些核心类,比如String
  • 同时也避免了类的重复加载,因为JVM中区分不同类,不仅仅是根据类名,相同的class被不同的ClassLoader加载就是不同的两个类

17 Java中的异常体系

  • Java中的所有异常都来自顶级父类Throwable
  • Throwable下有两个子类ExceptionError
  • Error是程序无法处理的错误,一旦出现这个错误,即程序将被迫停止运行
  • Exception不糊导致程序停止,又分为两个部分RunTimeException运行时异常 和 CheckedException检查异常
  • RunTimeException常常发生在程序运行过程中,会导致程序当前线程执行失败。CheckedException常常发生在程序编译过程中,会导致程序编译不通过

18 GC如何判断对象可以被回收(JVM)


19 线程的生命周期,线程有哪些状态

  • 线程的五种状态:创建,就绪,运行,阻塞,死亡
  • 阻塞的情况分为三种:
    • (1)等待阻塞:运行的线程执行了wait()方法
    • (2)同步阻塞:运行线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入”锁池”中
    • (3)其他阻塞:运行的线程执行了sleep或join方法,或发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep状态超时,join等待线程的终止或者超时。或者I/O处理完毕时,线程重新转入就绪状态。sleep是thread类的方法
  • 创建状态(new):新创建一个线程对象
  • 就绪状态(Runable):线程对象创建后,其他线程调用了该对象的start方法,该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权
  • 运行状态(Running):就绪状态的线程获取了CPU,执行程序代码
  • 阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态
  • 死亡状态(Dead):线程执行完或者因一场退出了run方法,该线程结束生命周期

20 sleep()、wait()、join()、yield()区别

  • (1)锁池:
    • 所有需要竞争同步锁的线程都会放在锁池当中,比如当前对象的锁已经被其中一个线程得到,则其他线程需要在这个锁池进行等待,当前面的线程释放同步锁后锁池中的线程去竞争同步锁,当某个线程得到后会进入就绪队列进行等待CPU资源分配
  • (2)等待池:
    • ·当我们调用wait()方法后,线程会放到等待池当中,等待池的线程是不会去竞争同步锁。只有调用notify()或notifyAll()后等待池的线程才会开始去竞争锁,notify()是随机从等待池选出一个线程放到锁池,而notifyAll()是将等待池中的所有线程方法哦锁池当中
  • sleep是Thread类的静态本地方法,wait()是Object类的本地方法
  • sleep方法不会释放lock,但是wait会释放,并加入等待列表中
  • sleep方法不依赖于同步器synchronzied,但是wait()需要依赖synchronzied关键字
  • sleep不需要被唤醒(时间到自动唤醒),但是wait()需要notify进行唤醒
  • sleep一般用于当前线程休眠,或者轮循暂停操作,wait则多用于多线程之间的通信
  • sleep会让出CPU执行时间且强制上下文切换,而wait()则不一定,wait后可能还是有机会重新竞争到锁继续执行的
  • yield()执行后线程直接进入就绪状态,马上释放了cpu的执行权,但是依然保留了cpu的执行资格,所以有可能cpu下次进行线程调度还会让出这个线程获得到执行权继续执行
  • join() 执行后线程进入阻塞状态,例如在线程B中使用线程A的join(),那线程B会进入到阻塞队列,直到线程A结束或中断线程

21 对线程安全的理解


32 Sring是什么?

  • spring是一个轻量级的开源的J2EE框架,它是一个容器框架,用来装JavaBean。中间层可以起一个连接作用,整合其他框架,可以让我们的企业开发更快、更简洁
  • Spring是一个轻量级的控制反转(IOC)和面向切面(AOP)的容器框架
    • 从大小与开销两方面而言,Spring都是轻量级的
    • 通过控制反转(IOC)技术到到松耦合的目的
    • 提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务进行内聚性的开发
    • 包含并管理应用对象(Bean)的配置和声明周期,这个意义上是一个容器
    • 将简单的组件配置,组合成为复杂的应用,这个意义上是一个框架

33 对AOP的理解

  • 系统由许多不同的组件组成,每一个组件各负责一块特定功能,除了实现自身核心功能之外,这些组件还经常承担额外的制作,例如日志,事务管理
  • 当我们需要为分散的对象映日公共行为的时候,OOP(面向对象)则显得无能为力。OOP允许你定义从上到下的关系,但不适合定义从左到右的关系。例如日志功能。
  • AOP(面向切面),将程序中的交叉业务逻辑(安全,日志,事务等),封装成一个切面,然后注入到目标对象(具体业务逻辑)中去。AOP可以对某个对象或某个对象的功能进行增强,比如对象中的方法进行增强,可以在执行某个方法之前额外的做一些事情,在某个方法执行之后外的做一些事情

34 对IOC理解

切入点:容器概念、控制反转、依赖注入

  • IOC容器:
    • 实际上市一个Map(key, value),里面存储各种对象(被注解标识的类,xml中配置的Bean)。在项目启动时,通过全限定类名使用反射创建对讲放入map里
    • map中拥有各种对象,通过@autowired / @resource等注解,或xml中bean节点内的ref属性
  • 控制反转:
    • (1)没有引入IOC容器之前,对象A依赖对象B,在对象A初始化,运行到某一点时,需要自己去创建对象B或使用已经创建的对象B,控制权都在自己的手上
    • (2)引入IOC之后,对象A与对象B失去了直接联系,当对象A运行到需要对象B时候,IOC会主动创建一个对象B注入到对象A需要的地方
    • 对象A获取对象B的过程是有主动变成被动应为,控制权颠倒,称为控制反转
  • 依赖注入:
    • 获取依赖对象的过程有自身管理变为了有IOC容器主动注入,依赖注入是实现IOC的方法,在IOC容器运行时间,动态地将某种依赖关系注入到对象之中

35 BeanFactory和ApplicationContext区别

  • ApplicationContext是BeanFactory的子接口
  • ApplicationContext提供了更完整的功能
    • (1)继承了MessageSource,因此支持国际化
    • (2)同一的资源文件访问方式
    • (3)提供在监听器中注册bean的事件
    • (4)同时加载多个配置文件
    • (5)载入多个(有继承关系)上下文,使得每一个上下文都有专注于一个特定的层次
  • BeanFactory采用延迟加载形式来注入Bean,使用使用某个Bean时(getBean()),才对该Bean进行加载实例化
  • ApplicationContext,它是在容器启动时,一次性创建所有Bean。
  • 两者相比,ApplicationContext在启动时,就能发现哪个Bean配置出现问题。而BeanFactory只有使用到相应的Bean时才知道是否出现问题。
  • 两者相比,ApplicationContext相比于BeanFactory比较占用内存
  • BeanFactory通常以编程的方式被创建,Application还能以声明的方式被创建,如使用ContextLoader
  • BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册

36 Spring Bean的生命周期

  • 扫描路径,解析类获得BeanDefinition
  • 如果有多个构造方法,则要推断构造方法
  • 确定好构造方法后,进行实例化得到一个对象
  • 对对象中的加了@autowired注解属性进行属性填充
  • 回调Aware方法,比如BeanNameAware、BeanFactoryAware
  • 调用BeanPostProcessor初始化前的方法
  • 调用初始化方法
  • 调用BeanPostProcessor的初始化后的方法,在这里会进行AOP
  • 如果当前创建的Bean是单例的则会把Bean放入单例池
  • 使用Bean
  • Spring容器关闭时调用DisposableBean中的destory()方法

37 Spring支持的Bean的作用域

  • singleton:默认,每一个容器中只有一个bean的实例,单例的模式由BeanFactory自身来维护,该对象的生命周期是与Spring IOC容器一致的(但在第一次被注入时才会创建一个新的对象)
  • prototype:为每一个Bean请求提供一个实例。在每次注入时都会创建一个新的对象
  • request:Bean被定义为在每个HTTP请求中创建一个单例对象,也就是说在单个请求中都会复用这一个单例对象
  • session:与request范围类似,确保每个session中有个bean的实例,在session过期后,bean会随之失效
  • application:Bean被定义为ServeltContext的生命周期中复用一个单例对象
  • websocker:Bean被定义在websocker的生命周期中复用一个单例对象

38 Spring框架中的单例Bean是线程安全的吗

  • 单例Bean:IOC容器中只有这么一个Bean,无论多少个线程去注入Bean,都是同一个Bean,不会额外new一个新的Bean
  • 该单例Bean并不是线程安全的,因为框架并没有对Bean进行多线程的封装处理
  • 如果Bean是有状态的(Bean里面存储数据),会出现线程安全问题,需要更改Bean的作用于,将默认的singleton更改为prototype,每次依赖注入时都会创建一个新的Bean
  • 如果Bean是无状态的,只是调用Bean里面的接口,就不会发生线程安全问题
  • 例子:DAO会操作数据库的connection,connection是带有状态的,比如数据库事务,spring事务管理器使用Threadlocal为不同线程维护了一套独立的connection副本,保证线程之间不会互相影响

39 Spring 框架使用到的设计模式

  • (1)简单工厂:有一个工厂类根据传入的参数,动态决定应该创建哪一个产品类

    1
    spring中的`BeanFactory`就是简单工厂模式的体现,根据传入一个唯一的标识来获取Bean对象,但是否是在传入参数后创建,还是传入参数前创建就根据具体的情况来决定
  • (2)工厂方法:

    1
    实现了`FactoryBean`接口的bean是一类叫做factorybean,其特点是,spring会在使用getBean()调用火哥该bean时,会自动调用该beangetObject()方法,所以返回的不是factory这个bean,而是这个bean.getObject()方法的返回值
  • (3)单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点
    1
    spring对单例的实现,spring中的单例模式完成了后半句话,即提供了全局的访问点BeanFactory,但没有从构造器级别去控制单例,这是因为spring管理的是任意的java对象
  • (4)适配器模式:
    1
    Spring定义一个适配接口,使得每一种Controller有一种对应的适配器实现类,让适配器代替Controller执行相应的方法,这样在拓展Controller时,只需要加一个适配器类就完成了SpringMVC的扩展了
  • (5)装饰器模式:动态给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活
    1
    2
    3
    4
    5
    Spring中用到的包装器模式在类名上有两种表现:一种是类名中含有wrapper,另一种是类名中含有Desorator
    ```


    + (6)动态代理:
    切面在应用运行的时刻被织入,在织入切面时,AOP容器会为目标对象动态创建一个代理对象,SpringAOP就是以这种方式织入切面
    织入:把切面应用到目标对象并创建新的代理对象的过程

40 Spring事务的实现方式和原理以及隔离级别