Java面试题学习记录

提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档


一.Java

1.面向对象的基本特质是什么?

  • 封装:
    • 调用程序中的某个方法,获取一个值,至于它执行了什么操作拿到这个值。你不需要知道。
    • 用private修饰成员变量,然后用public编写get和set方法,这就是一种封装
    • 也保证了数据的安全性和有效性,隐藏了细节。
    • 让对象和调用者进行解耦
  • 继承(extends):
    • 子类继承父类所以功能和成员变量
  • 多态:
    不同对象调用父类的同一个方法,返回的结果不同,这就是多态。
    • 继承
    • 重写 (调用的方法是子类重写后的方法)
    • 上转型 (父类引用指向子类对象)

2.Arraylist和Linkedlist的区别

  1. 底层数据结构不同,ArrayList底层基于Object数组实现的 ,LinkedList底层是基于双向链表实现的。(1.7之前是环形列表 )
  2. 由于底层数据结构不同,他的所适用场景也不同,ArrayList更适合随机查找,LinkedList更适合删除和添加。
  3. ArrayList和LinkedList都是实现List接口,但LinkedList还实现了Deque接口,可以当作队列使用。
  4. Arraylist默认添加是从尾部,此时时间复杂度为O(1),扩容策略是扩容到原来的1.5倍。
  5. Linkedlist默认添加到尾部,时间复杂度为O(1)。

3.Java8新特性(待学习)

待学习

4.==和equals

  1. ==对于基本数据类型它比较的是值,对于引用类型来说它比较的是对象内存地址
  2. 两个引用数据类型比较可以使用equals,String数据类型就重写了equals方法。
  3. equals要注意空指针异常,用常量去比较变量,用不可能为null的比较变量。

5.final的使用

  1. 如果final被放到了类前,那么这个方法不能被继承。
  2. 如果final被放到了方法前,那这个方法不能被重写,但可以被重载。
  3. 如果final放在了静态变量(static)前,有2次赋值机会,声明变量的时候给赋值,如果没有在声明变量的时候给赋值的话,那么可以在静态( static{} )块里给赋值
  4. 如果final放在了成员变量前,有3次赋值机会,声明期间、非静态代码块、构造器中
  5. 如果final放在了方法参数前,有1次赋值机会,在方法调用时给赋予参数
  6. 如果final放在了局部变量前,有2次赋值机会,初始化,和二次赋值。
  7. 如果final赋给的是基本数据类型的变量值是不能更改的,如果是引用数据类型变量,但引用值的对象其内容是可以修改的。

6.抽象类和接口的区别?

  1. 抽象类和接口都不能直接实例化。如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。
  2. 抽象类要被子类继承,接口要被类实现。
  3. 抽象类中可以做方法声明,也可以做方法实现,接口只能做方法声明。
  4. 抽象类中的变量是普通变量,接口里定义的变量只能是公共的静态的常量、public static final。
  5. 抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。
  6. 抽象方法只能声明,不能实现。
  7. 抽象类里可以没有抽象方法
  8. 如果—个类里有抽象方法,那么这个类只能是抽象类
  9. 抽象方法要被实现,所以不能是静态的,也不能是私有的、public、protected和default。
  10. 接口可以继承接口,并且可多继承接口,但类只能单—继承。
  11. 接口可以通过匿名内部类实例化。接口是对动作的抽象,抽象类是对根源的抽象。抽象类表示的是,这个对象是什么。而接口表示的是,这个对象能做什么。

7.string stringbuilder stringbuffer的区别

  1. StringBuilder和StringBuffer非常类似,均代表可变的字符序列,而且方法也一样
  2. String:不可变字符序列,效率低,但是复用率高。
    3.** StringBuffer**:可变字符序列、效率较高(增删)、线程安全
  3. StringBuilder:可变字符序列、效率最高、线程不安全

8.list和set的区别

  1. List和Set都是Collection接口从子接口。
  2. List底层由Object数组实现的,是有序可重复的,可根据索引查询,允许元素重复,可存在多个null。
  3. HashSet底层由哈希表(HashMap集合)实现的,是无序不可重复,通常不能记住元素的添加顺序,不允许元素重复不支持null,否则返回false。
  4. 可以使用TreeSet实现有序状态,它实现了Set接口,是非线程安全的,底层由TreeMap(树状结构)实现。

9.Java的异常体系

  • Java中的所有异常都来自顶级父级Throwable。
  • Throwable下有两个子类:Exception和Error。
  • Error是程序无法处理的错误,一旦出现这个错误,则这个程序被迫停止运行。
  • Exception不会导致程序停止,又分两个部分RunTimeException程序运行时异常和CheckedException检测异常。
  • RunTimeException异常会让当前线程执行失败。(接触最多,一般自定义异常继承这个类)
  • CheckedException异常会在查询编译期间报错,会导致程序编译不通过。

10.hashcode和equals

  • hashcode()通过算法结合对象的值计算出哈希值。两个相同值不同对象互相比较hashcode(),他们获取到的哈希值一定相等。
  • equals用于比较两个对象的值是否相等。
  • 在一个数组中用对象哈希值取对象长度的余来决定储存索引位置,用equals来判断 相同索引值 取余的值 和 已经存入的值 比较,判断他们的值是否相等。这样来提高存储后获取的效率。

11.Java的数据类型

  • 第一种:基本数据类型
    • 第一类:整数型
      • byte、short、int、int
    • 第二类:浮点型
      • float、double
    • 第三类:布尔型
      • boolean
    • 第四类:字符型
      • char

所占大小
一字节占8bit,也就是8个二进制位数占8位。

byte(1字节)、short(2字节)、int(4字节)、int(8字节)
float(4字节)、double(8字节)
boolean(1字节)
char(2字节)

取值范围为[ -2(1字节*8-1) ~ 2(1字节*8-1)-1 ]

默认123的这种直接值被当作int类型看
例如21474836148被当int类型看而它的大小超过了int的取值范围。
所以用21474836148L即可。

二.多线程

1.线程的生命周期

  • 新建状态:new 一个Thread之后就是新建状态
  • 就绪状态:当被调用的start方法后进入就绪状态,调用start方法之后,Java虚拟机就会为其创建线程栈和程序计数器,然后就会等待调度运行了
  • 运行状态:获取CPU时间片进入运行状态,占用CUP开始运行,时间片用完,进入就绪状态,在获取时间片。期间可能进入阻塞状态。
  • 阻塞状态
    • 等待阻塞:运行的线程执行o.wait()方法,JVM会把该线程放入等待队列中
    • 同步阻塞:运行的在获取对象的同步锁时,该锁被其他线程占用了,JVM则会吧该线程放入锁池中。
    • 其他阻塞:运行线程上执行Thread.sleep()或t.join()方法,或者发出I/O请求时,JVN会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或超时、或者I/O处理完毕时,线程重新转入可运行状态。
  • 死亡状态:当线程调用stop()或run、main方法结束或异常抛出之后,就进入死亡状态。
    请添加图片描述

2.线程的sleep、wait、join、yield如何使用?

  • sleep()方法:让当前线程在睡眠指定时间,谁调用谁睡眠,睡眠期间不会释放锁资源。
  • wait()方法:导致当前线程等待。会释放掉它所占用的锁资源,可以通过其他前程调用该对象的notify或notifyAll方法唤醒,当前线程必须有当前对象锁,否则调用wait()会抛出异常。
  • join()方法:线程之间的协程方式,在线程中调用b线程的.join()方法,则线程进入其他阻塞状态,需等待b线程执行完毕后唤醒。
  • **yield()方法 **:暂停当前正在执行的线程对象,yield()方法将当前线程重新回到就绪状态,可能刚暂停又立马被执行了。

3.守护线程的理解

  • 守护线程创建:在创建守护线程时,需要主动都要用setDaemon()方法并设置参数为true
  • 生命周期:当一个进程中只要还有一个用户进程还在运行,那么这个进程就不会结束,但是守护线程不是用户线程,所有即时守护线程还在执行,但用户线程没有了,程序就会直接结束。所有守护线程的生命周期依赖于用户线程。
  • 不建议场景:线程池或IO的任务场景,因为如果正在执行这些操作时,用户线程全部执行完毕,进程不会等待守护线程执行完直接结束,可能导致任务未完成或资源没用即时释放的问题。

三.JVM(Java虚拟机)

1.jre jdk jvm的区别

  1. JVM: Java虚拟机 是Java跨平台的关键,屏蔽了Java在不同操作系统的差异,让相同的Java程序在不同的环境运行出相同的结果
  2. JRE: Java运行时环境 运行Java已编译程序所必须的软件环境(JVM、Java标准类库)
  3. JDK: Java开发工具包包含了JRE、编译器以及调试分析的工具软件
  4. 关系:JDK包含JRE,JRE又包含了JVM(如果只想运行Java程序,只需安装JRE)

2.什么是字节码?

  • 字节码是一个Java虚拟机可执行文件的一种格式
  • 在字节码的前8个字节是cafe babe(咖啡宝贝),JVM会查看这个开头,通过这个开头判断该文件是否是.class文件

3.什么Java类加载器?

Java类加载器在JVM中,用于加载字节码文件的加载器,Java类加载器总共被分为:

  • 启动类加载器:引用类加载器,是由C/C++语言实现的。加载包名为Java、Javax、sun等开头的类。(扩展类加载器和系统类加载器都是由它加载的)
  • 扩展类加载器:从Java.ext.dirs系统属性所指定的目录中加载类库,或从JDK的安装目录的jre/lib/ext子目录下加载类库,如果用户创建的jar放在此目录下,也会主动被扩展类加载器加载。(加载核心包之外的扩展类型的包)
  • 系统类加载器:它的父类加载器是扩展类加载器,负责加载classpath或系统属性、java.class.path指定路径下的类库,我们程序中写的类默认都是用系统类加载器来完成加载的。
  • 自定义加载器

4.双亲委派机制是什么?

  • 概念
    • 双亲委派机制是在java1.2后引入的,其工作原理是,如果一个类加载器接收到了类加载请求,不会自己先加载,而是把这个请求委托给父类加载器去执行,如果父类还有父类那么会再次将请求委托给它的父类加载器,依次递归,请求最终将到达顶层的启动类加载器,如果父类可以加载则加载成功,如果加载不了则让子类去加载,这就是双亲委派机制。
  • 双亲委派机制的好处:
    • 每一个类都只会被加载一次,避免了重复加载。
    • 每一个类都会被尽可能的加载
    • 有效的避免了某些恶意类的加载

四.Spring

1.什么是Spring?

Spring是一个Java企业级应用的开源的开发框架。Spring框架目标是简化Java企业级应用开发。

Spring的优点

  • 通过控制反转和依赖注入实现 松耦合
  • 支持 面向切面 的编程,并且把应用业务逻辑和系统服务分开。
  • 通过切面和面板减少样板式代码。
  • 声明式事务的支持。可从单调繁冗的事务管理代码中解脱出来,通过声明式方式灵活地进行事务管理,提高开发效率和质量。

2.如何实现IOC容器

l0C(lnversion of Control),意思是控制反转,不是什么技术,而是一种设计思想,10C意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。

在传统的程序设计中,我们直接在对象内部通过new进行对象创建,是程序主动去创建依赖对象,而IOC是有专门的容器来进行对象的创建,即IOC容器来控制对象的创建

在传统的应用程序中,我们是在对象中主动控制去直接获取依赖对象,这个是正转,反转是由容器来帮忙创建及注入依赖对象,在这个过程过程中,由容器帮我们查找级注入依赖对象,对象只是被动的接受依赖对象.

  1. 先准备一个基本的容器对象,包含一些map结构的集合,用来方便后续过程中存储具体的对象
  2. 进行配置文件的读取工作或者注解的解析工作,将需要创建的bean对象都封装成BeanDefinition对象存储在容器中
  3. 容器将封装好的BeanDefinition对象通过反射的方式进行实例化,完成对象的实例化工作后,进行对象的初始化操作,也就是给类中的对应属性值就行设置,也就是进行依赖注入,完成整个对象的创建,变成一个完整的bean对象,存储在容器的某个map结构中建,
  4. 通过容器对象来获取对象,进行对象的获取和逻辑处理工作
  5. 提供销毁操作,当对象不用或者容器关闭的时候,将无用的对象进行销毁

3.AOP的理解?

AOP面向切面编程,它是为解耦而生,AOP在业务类的隔离上,绝对做到了解耦

任何一个系统都是由不同的组件组成的,每个组件负责一块特定的功能,当然会存在很多组件是跟业务无关的,例如日志、事务、权限等核心服务组件,这些核心服务组件经常融入到具体的业务逻辑中,如果我们为每一个具体业务逻辑操作都添加这样的代码,很明显代码几余太多
因此我们需要将这些公共的代码逻辑抽象出来变成一个切面,然后注入到目标对象(具体业务)中去
AOP正是基于这样的一个思路实现的,通过动态代理的方式,将需要注入切面的对象进行代理,在进行调用的时候,将公共的逻辑直接添加进去,而不需要修改原有业务的逻辑代码,只需要在原来的业务逻辑基础之上做一些增强功能即可。

Mybatis

1.#{}和${}的区别

相同点:都能取到变量的值。
区别:
${}会sql注入,而#{}不会,因为#{}使用预编译的方式将要插入数据的位置用’?'代替 防止sql注入

#{}可以实现预编译预编译过程中mybatis主要做了数据类型检测安全检查两部分
数据类型检测:判断数据类型,如果是数值类型,那么就不加引号,即?, 如果是字符串类型,就加上引号,即’?'。
安全检查:若变量的值带有引号,会对引号进行转义处理,这样可以防止sql注入.

应用场景
**${}**使用在sql映射文件动态拼接sql时的场景下运用,在传入多个参数进行条件查询。或传入一个pojo进行数据插入
**#{}**适用于动态插入sql语句。也可以用#{sql}代替整个查询语句。有sql注入风险。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值