0
点赞
收藏
分享

微信扫一扫

想要拿高薪的必须要知道的面试 (一) 常见面试题

萧萧雨潇潇 2022-01-20 阅读 78
java

常见面试题

1.Java的八大基本数据类型有哪些?
        byte类型:8位,最大存储是0-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码,用单引号赋值。

2.ArrayList和LinkedList区别,你一般用哪一个,为什么

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

kedList是基于双链表实现的,不支持索引,随机访问元素需要从头查找,因此性能差,但是添加删除性能高因为不涉及移位操作

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

3.创建线程是几种方式

        方式一:继承Thread类,覆写run方法,创建实例对象,调用该对象的start方法启动线程

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

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

        方式四:线程池(有时间给大家详解四种线程池的创建方式)

        方式一有单继承的局限性

        方式二和方式三避免了单继承的局限,使用更广泛。而方式二适用于无需返回值的场景,方式三使用于有返回值的场景

4.直接调用线程的start方法和run方法有什么区别

start是开启新线程,而调用run方法是一个普通方法调用,还是在主线程里执行

5.ArrayList和Vector区别

        相同点:这两个类都实现了List接口(List接口继承了Collection接口),他们都是有序集合,即存储在这两个集合中的元素的位置都是有顺序的,相当于一种动态的数组,我们以后可以按位置索引号取出某个元素,并且其中的数据是允许重复的,这是与HashSet之类的集合的最大不同处,HashSet之类的集合不可以按索引号去检索其中的元素,也不允许有重复的元素。

      不同点:ArrayList与Vector的区别主要包括两个方面:

同步性:

 Vector是线程安全的,也就是说是它的方法之间是线程同步的,而ArrayList是线程序不安全的,它 的方法之间是线程不同步的。如果只有一个线程会访问到集合,那最好是使用ArrayList,因为它不考虑线程安全,效率会高些;如果有多个线程会访问到集合,那最好是使用Vector,因为不需要我们自己再去考虑和编写线程安全的代码。

数据增长:

ArrayList与Vector都有一个初始的容量大小,当存储进它们里面的元素的个数超过了容量时,就需 要增加ArrayList与Vector的存储空间,每次要增加存储空间时,不是只增加一个存储单元,而是增加多个存储单元,每次增加的存储单元的个数在内存空间利用与程序效率之间要取得一定的平衡。Vector默认增长为原来两倍,而ArrayList的增长策略在文档中没有明确规定(从源代码看到的是增长为原来的1.5倍)。 ArrayList与Vector都可以设置初始的空间大小,Vector还可以设置增长的空间大小,而ArrayList没有提供设置增长空间的方法。总结:ArrayList底层数组容量不足时,会自动扩容0.5倍,Vector会自动扩容1倍

7.String和StringBuilder和StringBuffer区别

        三者都是基于char[ ]字符数组的,String是不可变字符串,且有final修饰,因此每次对String的操作都会在内存中开辟空间,生成新的对象。

        StringBuilder和StringBuffer是可变字符串,修改不会生成新的对象。

        ​ StringBuffer是线程安全的,方法有synchronized修饰,性能较低。

        ​ StringBuilder是线程不安全的,方法没有synchronized修饰,性能较高。

8.面向对象的几大特征

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

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

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

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

9.Equals和==的区别

        equals()方法,适用于引用数据类型,覆写Object中的equals()方法,判断对象中的内容是否相同,==是比较运算符,适用于基本数据类型和引用数据类型,基本数据类型时比较的是值,引用数据类型比较的是地址

10.int和Integer的区别

        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之间的整数,会进行缓存,如果下次再赋相同的值会直接从缓存中取,即享元模式

11.方式重载和方法覆盖的区别

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

12.接口和抽象类

        定义接口使用interface,定义抽象类使用abstract class,接口由全局常量,抽象方法,(java8后:静态方法,默认方法,抽象类由构造方法,抽象方法,普通方法,接口和类是实现关系,抽象类和类是继承关系

13.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)。

14.JVM类加载流程

        loading加载:class文件从磁盘加载到内存中

        verification验证:校验class文件,包括字节码验证,元数据验证,符号引用验证等等

        preparation准备:静态变量赋默认值,只有final会赋初始值

        resolution解析:常量池中符号引用,转换成直接访问的地址

        initializing初始化:静态变量赋初始值

15.JVM类加载器有几种类型,分别加载什么东西,用到什么设计模式?

       1. BootStrap ClassLoader启动类加载器,加载<JAVA_HOME>\lib下的类

        2.Extenstion ClassLoader 扩展类加载器,加载<JAVA_HOME>\lib\ext下的类

        3.Application ClassLoader 应用类加载器,加载Classpath下面的类

        4.自定义加载器

       设计模式:双亲委派机制,如果一个类加载器收到类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器完成。每个类加载器都是如此,只有当父加载器在自己的搜索范围内找不到指定的类时(即ClassNotFoundException),子加载器才会尝试自己去加载。

双亲委派机制模型图:        

16.JVM的组成内存模型详解

 

堆:java内存最大的一块,所有对象实例、数组都存放在java堆,GC回收的地方,线程共享。

虚拟机栈:存放基本数据类型、对象的引用、方法出口等,线程私有。

Native方法栈:对应的是本地方法,在hotspot中虚拟机栈和本地方法栈是合为一体的

程序计数器:当前线程所执行的字节码的行号指示器,用于记录正在执行的虚拟机字节指令地址,线程私有。

方法区:存放虚拟机加载的类的信息,常量,静态变量等等,JDK1.8后,改为元空间

17.JVM内存为什么要分成新生代,老年代,持久代。新生代中为什么要分为Eden和Survivor

1)共享内存区划分

共享内存区 = 持久代+ 堆
持久带 = 方法区 + 其他
Java堆 = 老年代 + 新生代
新生代 = Eden + S0 + S1
2)一些参数的配置

默认的,新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 ,可以通过参数 –XX:NewRatio 配置。
默认的,Edem : from : to = 8 : 1 : 1 ( 可以通过参数 –XX:SurvivorRatio 来设定)
Survivor区中的对象被复制次数为15(对应虚拟机参数 -XX:+MaxTenuringThreshold)
3)为什么要分为Eden和Survivor?为什么要设置两个Survivor区?

如果没有Survivor,Eden区每进行一次Minor GC,存活的对象就会被送到老年代。老年代很快被填满,触发Major GC.老年代的内存空间远大于新生代,进行一次Full GC消耗的时间比Minor GC长得多,所以需要分为Eden和Survivor。
Survivor的存在意义,就是减少被送到老年代的对象,进而减少Full GC的发生,Survivor的预筛选保证,只有经历16次Minor GC还能在新生代中存活的对象,才会被送到老年代。
设置两个Survivor区最大的好处就是解决了碎片化,刚刚新建的对象在Eden中,经历一次Minor GC,Eden中的存活对象就会被移动到第一块survivor space S0,Eden被清空;等Eden区再满了,就再触发一次Minor GC,Eden和S0中的存活对象又会被复制送入第二块survivor space S1(这个过程非常重要,因为这种复制算法保证了S1中来自S0和Eden两部分的存活对象占用连续的内存空间,避免了碎片化的发生)


18.JVM中一次完整的GC流程是怎样的,对象如何晋升到老年代

        Java堆 = 老年代 + 新生代,新生代 = Eden + S0 + S1
        当 Eden 区的空间满了, Java虚拟机会触发一次 Minor GC,以收集新生代的垃圾,存活下来的对象,则会转移到 Survivor区。
        大对象(需要大量连续内存空间的Java对象,如那种很长的字符串)直接进入老年态;
        如果对象在Eden出生,并经过第一次Minor GC后仍然存活,并且被Survivor容纳的话,年龄设为1,每熬过一次Minor GC,年龄+1,若年龄超过一定限制(15),则被晋升到老年态。即长期存活的对象进入老年态。
        老年代满了而无法容纳更多的对象,Minor GC 之后通常就会进行Full GCFull GC 清理整个内存堆 – 包括年轻代和年老代。
Major GC 发生在老年代的GC,清理老年区,经常会伴随至少一次Minor GC,比Minor GC慢10倍以上。

19.MyBatis中${}取值和#{}取值的区别

       #{}能够防止SQL注入,因为底层使用PreparedStatement对象,预编译,性能较高,${}不能防止SQL注入,因为底层使用Statement对象,不会预编译而是拼接字符串,性能较低,能使用 #{}时尽量使用 #{},但如果直接在 SQL 语句中插入一个不改变的字符串。比如,像 ORDER BY时只能使用${}

20.MyBatis关联查询中,延迟加载和饥饿加载的区别

        延迟加载,是先从单表查询,需要使用关联数据的时候才发起关联查询,不用的时候不查询关联的数据,又叫懒加载

        饥饿加载,是在查询时将关联的数据立即查询出来加载进内存,不管用不用

21.JMyBatis一级缓存和二级缓存的区别

        缓存,是指将从数据库查询出的数据存放在缓存中,下次使用相同查询时不必再从数据库查询,而是直接从缓存中读取,从而减轻数据库查询的压力,提高性能

        mybaits中的一级缓存,是SqlSession级别,默认开启,使用同一个SqlSession发送相同的SQL时命中;它的生命周期和SqlSession一致,当调用SqlSession.close()方法时会释放缓存

        mybatis中的二级缓存,是SqlSessionFactory级别,默认不开启,使用同一个SqlSessionFactory,发送相同的SQL时命中;它的生命周期是程序结束

        当SQL中执行了update()、delete()、insert()操作,则缓存中的数据都会清空

22.MyBaits的Mapper接口没有实现类为社么可以用@Autowired直接注入

        赋值给mapper接口引用的对象其实是一个代理对象,这个代理对象是由 JDK 动态代理创建的。在解析mapper的时候,mybatis会通过java反射,获取到接口所有的方法

        当调用接口中方法时,将通过接口全限定名+方法名对应找到映射文件中namespace和id匹配的sql,然后将执行结果返回

23.对IOC的理解

        IOC,Inversion Of Control 英文首字母缩写,意为控制反转,是Spring的核心思想之一

        控制,指的是对象创建、实例化、管理、销毁等等权力

        反转,指的是将控制权交给Spring框架,IOC容器

        使用依赖注入的方式,将需要的外部资源注入到组件中,使用IOC使得对象之间的耦合度降低,资源变得容易管理,从而使得代码更加优雅

24.你对AOP的理解,AOP的使用场景是什么?实现原理是什么?

        AOP,Aspect Oriented Programming 英文首字母缩写,意为面向切面编程,是Spring的核心思想之一

        AOP是对OOP(面向对象编程)的一种补充,能够做到很多面向对象无法做到的事情,比如需要在所有方法执行前开启事务,打印日志,如果使用面向对象来编程,将会产生大量重复代码,而使用AOP,可以将那些与业务无关,却为业务模块所共同调用的逻辑封装起来,一次解决这些问题。而这些重复的代码,一般统称为横切逻辑代码

        使用AOP,在不改变原有业务逻辑的情况下,实现解耦合,避免横切逻辑代码重复

        AOP的使用场景包括日志记录,性能统计,安全控制,事务处理,异常处理等等

        它是基于动态代理实现的,分为JDK动态代理CGLIB动态代理。JDK动态代理只支持实现了接口的类 ,CGLIB支持没有实现接口的类。Spring默认使用JDK动态代理,如果被代理类没有实现接口,会选择CGLIB动态代理

25.说一下切面相关的注解,分别是什么意思

        @Aspect:定义切面

        @Pointcut:定义切点

        @Before:前置通知,在目标方法运行之前运行

        @After:后置通知,在目标方法运行结束之后运行(无论方法正常结束还是异常结束)

        @AfterReturning:返回通知,在目标方法正常返回之后运行

        @AfterThrowing:异常通知,在目标方法出现异常以后运行

        @Around:动态代理,手动推进目标方法运行

举报

相关推荐

0 条评论