SE基础面试题
谈谈你对面向对象的理解
如果谈面向对象,首先它是一种思想,是从面向过程演变过来的
举一个例子吧
一个人要洗衣服的话
面向过程的处理
打开洗衣机》》放进衣服》》放洗衣粉》》清洗》》烘干
面向对象的话,先会对其进行分析成 人和洗衣机两个对象
人的话就是 打开洗衣机》》放进衣服》》放洗衣粉
洗衣机就是 清洗》》烘干
首先面向过程的执行速度肯定高于面向对象
但是面向对象的可扩展性,维护性,复用性都是要高于面向过程的
接下来就是面向对象的三大特征
封装
就是将属性私用化,提供对应的访问方法,外部不用了解内部的实现,直接调用即可
明确出外部可以使用的成员变量以及函数
继承
继承的话就是 实现代码的复用,以及提高可扩展性
将所有子类共有的特性抽取到父类中,子类继承扩展或重写 做出自己的改变以及扩展
多态
多态有两种
一种是方法的重载
还有的话需要两个条件继承以及重写
父类引用指向子类的实例
如果子类重写了父类的方法就会调用子类的,如果没有重写默认调用父类的方法
抽象
个人感觉 抽象能力非常的重要,一个很好地项目,肯定会抽象出很多的接口,提升项目的可扩展性
==和equals的区别
首先= =是比较栈中的值是否相等,基本类型的值是直接存在栈中的,可以直接用= =于进行比较
equals是object中的方法 里面的方法实现也是用的==
一般在开发过程中需要重写equals方法
举个例子String类型中重写的equals方法会比较char[]中的每一个元素是否相等
final 的作用
final是一个修饰符
修饰类 当前的类不能被继承
修饰方法 方法不能被重写
修饰变量 变量不能被更改
String StringBuffer StringBuilder区别以及使用场景
String 是由char[]来实现的 用到了final修饰,所有说是不可变的,每一次修改都会创建新的对象
StringBuffer StringBuilder都是在源对象进行操作的,如果需要对字符串频繁的修改可以使用
StringBuilder 是线程不安全的
StringBuffer 是线程安全的 方法上用到了synchronized修饰
在多线程的情景下共享变量优先是由StringBuffer 由于加了锁,所以性能最低
重载和重写的区别
重载
发生在同一个类中 方法名必须相同,参数列表不同,个数不同,顺序不同
重写
发生在父子类中 方法名,参数列表相同,返回值小于等于父类,抛出的异常小于等于父类,访问修饰符大于等于父类
接口和抽象类
1.抽象类中可以存放普通的函数,接口中只能存放抽象的方法
2.抽象类中可以存放各种类型的成员变量,接口中成员变量只能是static final的
3.类只能继承一个,接口可以多继承
接口主要的是定义行为约束,它只约定的有无,具体的实现不去限制
抽象类的话就是代码复用,当不同的类具有相同的行为,就可以让这里类去继承抽象类,实现代码复用,子类去扩展功能
List和Set区别
List 有序 可以重复
Set 无序 不可重复
hashcode和equals
首先hashcode是object中一个native方法 返回的是一个int值,java中对象是存储在堆中的,其实是维护了一张hash表,可以通过这个hashcode快速定位对象在堆中的位置
为什么要有hashcode
有些hash类型的存储结构用到了hashcode来计算存储位置
简单说一下HashSet是怎么做到不可重复的
通过对象的hashcode值计算出存储位置,判断位置上是否存在值,直接存储,如果发现有值得话,
会调用equals进行比较,如果返回true的话,就不允许操作成功,返回false的话,就重新散列另一个存储位置
举个例子:
如果不重写hashcode方法,两个对象返回的hashcode值是不可能相同的,如果俩个对象equals相同的话,第一个对象存储进去的话,
第二个对象存储到其他位置正好没有值,就讲两个相同的对象存储到hashset中了,破坏了存储结构
如果不重写equals方法,默认用= =进行比较,两个对象是不可能相等的,存储到hashset中,hashcode相同,在equals的时候两个对象是
永远不相等的,会将相同的对象存储到hashset中 也破坏了存储结构
因此在重写equals的时候也要重写hashcode方法
ArrayList 和 LInkedList的区别
ArrayList 底层由动态数组来实现,连续的内存存储,查询速度快,增删速度慢
LinkedList 由链表实现, 分散的内存空间, 增删速度快,查询速度慢
HashMap和HashTable 的区别
Hashtable是线程安全的 现在已经不怎么用了
HashMap是线程不安全的
HashMap 底层实现
------------恢复内容开始------------
谈谈你对面向对象的理解
如果谈面向对象,首先它是一种思想,是从面向过程演变过来的
举一个例子吧
一个人要洗衣服的话
面向过程的处理
打开洗衣机》》放进衣服》》放洗衣粉》》清洗》》烘干
面向对象的话,先会对其进行分析成 人和洗衣机两个对象
人的话就是 打开洗衣机》》放进衣服》》放洗衣粉
洗衣机就是 清洗》》烘干
首先面向过程的执行速度肯定高于面向对象
但是面向对象的可扩展性,维护性,复用性都是要高于面向过程的
接下来就是面向对象的三大特征
封装
就是将属性私用化,提供对应的访问方法,外部不用了解内部的实现,直接调用即可
明确出外部可以使用的成员变量以及函数
继承
继承的话就是 实现代码的复用,以及提高可扩展性
将所有子类共有的特性抽取到父类中,子类继承扩展或重写 做出自己的改变以及扩展
多态
多态有两种
一种是方法的重载
还有的话需要两个条件继承以及重写
父类引用指向子类的实例
如果子类重写了父类的方法就会调用子类的,如果没有重写默认调用父类的方法
抽象
个人感觉 抽象能力非常的重要,一个很好地项目,肯定会抽象出很多的接口,提升项目的可扩展性
==和equals的区别
首先= =是比较栈中的值是否相等,基本类型的值是直接存在栈中的,可以直接用= =于进行比较
equals是object中的方法 里面的方法实现也是用的==
一般在开发过程中需要重写equals方法
举个例子String类型中重写的equals方法会比较char[]中的每一个元素是否相等
final 的作用
final是一个修饰符
修饰类 当前的类不能被继承
修饰方法 方法不能被重写
修饰变量 变量不能被更改
String StringBuffer StringBuilder区别以及使用场景
String 是由char[]来实现的 用到了final修饰,所有说是不可变的,每一次修改都会创建新的对象
StringBuffer StringBuilder都是在源对象进行操作的,如果需要对字符串频繁的修改可以使用
StringBuilder 是线程不安全的
StringBuffer 是线程安全的 方法上用到了synchronized修饰
在多线程的情景下共享变量优先是由StringBuffer 由于加了锁,所以性能最低
重载和重写的区别
重载
发生在同一个类中 方法名必须相同,参数列表不同,个数不同,顺序不同
重写
发生在父子类中 方法名,参数列表相同,返回值小于等于父类,抛出的异常小于等于父类,访问修饰符大于等于父类
接口和抽象类
1.抽象类中可以存放普通的函数,接口中只能存放抽象的方法
2.抽象类中可以存放各种类型的成员变量,接口中成员变量只能是static final的
3.类只能继承一个,接口可以多继承
接口主要的是定义行为约束,它只约定的有无,具体的实现不去限制
抽象类的话就是代码复用,当不同的类具有相同的行为,就可以让这里类去继承抽象类,实现代码复用,子类去扩展功能
List和Set区别
List 有序 可以重复
Set 无序 不可重复
hashcode和equals
首先hashcode是object中一个native方法 返回的是一个int值,java中对象是存储在堆中的,其实是维护了一张hash表,可以通过这个hashcode快速定位对象在堆中的位置
为什么要有hashcode
有些hash类型的存储结构用到了hashcode来计算存储位置
简单说一下HashSet是怎么做到不可重复的
通过对象的hashcode值计算出存储位置,判断位置上是否存在值,直接存储,如果发现有值得话,
会调用equals进行比较,如果返回true的话,就不允许操作成功,返回false的话,就重新散列另一个存储位置
举个例子:
如果不重写hashcode方法,两个对象返回的hashcode值是不可能相同的,如果俩个对象equals相同的话,第一个对象存储进去的话,
第二个对象存储到其他位置正好没有值,就讲两个相同的对象存储到hashset中了,破坏了存储结构
如果不重写equals方法,默认用= =进行比较,两个对象是不可能相等的,存储到hashset中,hashcode相同,在equals的时候两个对象是
永远不相等的,会将相同的对象存储到hashset中 也破坏了存储结构
因此在重写equals的时候也要重写hashcode方法
ArrayList 和 LInkedList的区别
ArrayList 底层由动态数组来实现,连续的内存存储,查询速度快,增删速度慢
LinkedList 由链表实现, 分散的内存空间, 增删速度快,查询速度慢
HashMap和HashTable 的区别
Hashtable是线程安全的 现在已经不怎么用了
HashMap是线程不安全的
HashMap 底层实现
------------恢复内容开始------------
谈谈你对面向对象的理解
如果谈面向对象,首先它是一种思想,是从面向过程演变过来的
举一个例子吧
一个人要洗衣服的话
面向过程的处理
打开洗衣机》》放进衣服》》放洗衣粉》》清洗》》烘干
面向对象的话,先会对其进行分析成 人和洗衣机两个对象
人的话就是 打开洗衣机》》放进衣服》》放洗衣粉
洗衣机就是 清洗》》烘干
首先面向过程的执行速度肯定高于面向对象
但是面向对象的可扩展性,维护性,复用性都是要高于面向过程的
接下来就是面向对象的三大特征
封装
就是将属性私用化,提供对应的访问方法,外部不用了解内部的实现,直接调用即可
明确出外部可以使用的成员变量以及函数
继承
继承的话就是 实现代码的复用,以及提高可扩展性
将所有子类共有的特性抽取到父类中,子类继承扩展或重写 做出自己的改变以及扩展
多态
多态有两种
一种是方法的重载
还有的话需要两个条件继承以及重写
父类引用指向子类的实例
如果子类重写了父类的方法就会调用子类的,如果没有重写默认调用父类的方法
抽象
个人感觉 抽象能力非常的重要,一个很好地项目,肯定会抽象出很多的接口,提升项目的可扩展性
==和equals的区别
首先= =是比较栈中的值是否相等,基本类型的值是直接存在栈中的,可以直接用= =于进行比较
equals是object中的方法 里面的方法实现也是用的==
一般在开发过程中需要重写equals方法
举个例子String类型中重写的equals方法会比较char[]中的每一个元素是否相等
final 的作用
final是一个修饰符
修饰类 当前的类不能被继承
修饰方法 方法不能被重写
修饰变量 变量不能被更改
String StringBuffer StringBuilder区别以及使用场景
String 是由char[]来实现的 用到了final修饰,所有说是不可变的,每一次修改都会创建新的对象
StringBuffer StringBuilder都是在源对象进行操作的,如果需要对字符串频繁的修改可以使用
StringBuilder 是线程不安全的
StringBuffer 是线程安全的 方法上用到了synchronized修饰
在多线程的情景下共享变量优先是由StringBuffer 由于加了锁,所以性能最低
重载和重写的区别
重载
发生在同一个类中 方法名必须相同,参数列表不同,个数不同,顺序不同
重写
发生在父子类中 方法名,参数列表相同,返回值小于等于父类,抛出的异常小于等于父类,访问修饰符大于等于父类
接口和抽象类
1.抽象类中可以存放普通的函数,接口中只能存放抽象的方法
2.抽象类中可以存放各种类型的成员变量,接口中成员变量只能是static final的
3.类只能继承一个,接口可以多继承
接口主要的是定义行为约束,它只约定的有无,具体的实现不去限制
抽象类的话就是代码复用,当不同的类具有相同的行为,就可以让这里类去继承抽象类,实现代码复用,子类去扩展功能
List和Set区别
List 有序 可以重复
Set 无序 不可重复
hashcode和equals
首先hashcode是object中一个native方法 返回的是一个int值,java中对象是存储在堆中的,其实是维护了一张hash表,可以通过这个hashcode快速定位对象在堆中的位置
为什么要有hashcode
有些hash类型的存储结构用到了hashcode来计算存储位置
简单说一下HashSet是怎么做到不可重复的
通过对象的hashcode值计算出存储位置,判断位置上是否存在值,直接存储,如果发现有值得话,
会调用equals进行比较,如果返回true的话,就不允许操作成功,返回false的话,就重新散列另一个存储位置
举个例子:
如果不重写hashcode方法,两个对象返回的hashcode值是不可能相同的,如果俩个对象equals相同的话,第一个对象存储进去的话,
第二个对象存储到其他位置正好没有值,就讲两个相同的对象存储到hashset中了,破坏了存储结构
如果不重写equals方法,默认用= =进行比较,两个对象是不可能相等的,存储到hashset中,hashcode相同,在equals的时候两个对象是
永远不相等的,会将相同的对象存储到hashset中 也破坏了存储结构
因此在重写equals的时候也要重写hashcode方法
ArrayList 和 LInkedList的区别
ArrayList 底层由动态数组来实现,连续的内存存储,查询速度快,增删速度慢
LinkedList 由链表实现, 分散的内存空间, 增删速度快,查询速度慢
HashMap和HashTable 的区别
Hashtable是线程安全的 现在已经不怎么用了
HashMap是线程不安全的
HashMap 底层实现
jdk1.7 是由数组加链表实现的
通过hashcode & 上数组长度得到数组下标,如果没有值话直接存,如果发生hash冲突的话就用链表来解决
因为1.7中是用的头插法,在resize这个方法中的transfer方法中,多线程在操作的时候回出现循环链表的情况
在下一次get put的时候就会遍历链表,就会出现死循环的情况
jdk1.8 是由数组加链表加上红黑树来实现的
当链表的长度到8并且容量大于64 将会转换成红黑树
ConcurrentHashMap
1.7
内部维护一个segment[] 每一个segment中维护了一个hashentry,相当于一个小的hashmap,在进行加锁的时候,
直接锁定对应的segment就可以了,大大提升了性能和效率 分段锁
1.8
由于1.8对synchronize进行了优化。查找,替换都是使用的cas操作
锁的是一个链表的头结点,比1.7锁整个hashmap 更细小一些,效率肯定会更高
实现一个ioc容器
1.通过配置文件读取要扫描的路径
2.递归获取所有的.class文件
3.通过反射生成对象,交给ioc容器处理
4.对需要出入的类进行依赖注入
java类加载器有哪些
bootstrap 默认加载lib 下的jar和 class 文件
ext 默认加载ext 下的jar 和class文件
app 默认加载classpath下的jar 和class文件
双亲委派模型
为了安全性,避免自己编写的类动态替换java的核心类
java 的异常体系
java 中所有的异常都来自顶级异常throwable
他有两格子类exception 和 error
error就是错误,一旦出现程序直接停止
exception 分为两种runtime 运行时异常一般发生在程序运行时
checked 检查异常,一般语法错误
GC是怎么判断对象可被回收的
引用计数法:每一个对象都有一个计数器,每当新增一个引用就会加一,当为零的时候就可以被回收
但是java中没有采用这种方式
这种方式会出现一个弊端, 如果两个对象相互引用,他们的引用数量就都为1,就永远不会被回收
可达性分析法: 已GC root为起点开始向下搜索,当一个对象没有被引用链相连时,判断这个对象为不可
用的对象,那么虚拟机就可以进行回收
常见的GC root
虚拟机栈中引用的变量
静态属性引用的对象
常量属性引用的对象
JNI引用的对象
线程的生命周期,以及那些状态
新生状态 在调用new关键字就新建了一个线程对象
就绪状态 在调用线程的start方法,未获得cpu的使用权
运行状态 在获得cpu使用权,执行代码
阻塞状态 因为某种原因放弃cpu使用权,停止运行,进入就绪状态,等待cpu分配时间片
死亡状态 线程执行完毕或者异常退出run方法, 结束生命周期
sleep wait join yield 方法
sleep 方法是thread 中的一个静态方法,调用之后会进行阻塞,不会释放资源,如果在synchronize中使用sleep,其他线程就无法获得锁
wait 方法是object中的方法,一般用于线程通信,调用后添加到等待池并释放资源,等他其他线程调用nodifyall方法唤醒
join 方法 可以理解成一个插队的方法,可以让cpu先执行自己后在执行其他线程
yield 方法是一个礼让方法,让出cpu执行时间片,线程状态调整为就绪状态,等待cpu分配时间
说说你对线程安全的理解
这个线程安全也可以换一种说法也叫内存安全,因为堆是共享内存,可以被所有线程访问
当多个线程并发访问一个对象的时候就会发生线程安全问题
就是单线程和多线程的结果不一致
我们可以同个cas 和锁的机制来处理线程安全的问题
Thread 和 Runable的区别
其实没啥区别就是用法有一些区别
Thread是一个类 Runable是一个接口 Thread实现了Runable接口
因为java单继承 如果你继承了Thread就继承不了其他的,而接口则是多实现
说说你对守护线程的理解
为其他非守护线程提供一些服务,可以理解为保姆
经典的守护线程就是GC 他在监控所有线程中可以回收的资源,当所有线程都执行结束的话,GC也就随之销毁了
在new Thread 可以通过一个api设置成守护线程
并发 并行 串行的区别
串行的话就是 时间上不可能重叠,一个任务没有执行完,就不可能去执行下一个任务
并行 在时间上重叠,同一个时刻互不干扰同时执行
并发 交替执行
并发三大特性
原子性
就是在一个操作中cpu不能暂停然后在调度 就好比一个线程在进行自增 还没有写入工作内存,第二个线程就将值取走
进行操作,导致最后的结果就是不正确的
可见性
当多个线程访问同一个变量的时候,一个线程修改了值,其他的线程应该立刻看见修改的值
因为多线程在访问值的时候 是讲主存里的值copy一份到自己的工作空间,更改的也是自己空间的值,如果更改后没写入主存
第二个线程对第一个线程的操作就是不可见的,这就是可见性
有序性 volatile
虚拟机在编译代码的时候,不会按照我们编写代码的顺序来执行,他会先进行优化,更改我们的执行顺序,可能出现一些问题
这个问题是我在c语言基础的时候发现的,在写一个程序的时候
int i = 5
i = (++i)+(++i)+(i++) //这里应该是6+7+7 等于20 但是执行结果是21
就很奇怪,然后通过编译工具反编译成汇编语言大概看了一下 在执行期间
编译器把前面两个值都是7 进行相加的 估计是为了增加执行效率 所以最后的值是21 dev++搞得鬼
为什么用线程池,线程池的参数
池化技术
减少反复创建的线程对象对系统的开销,当线程来了可以直接执行,而不用在去创建 ,统一管理,监控
corepoolsize 核心线程的数量
maxnumpoolsize 最大线程数量
keepalivetime 表示超时核心线程的其他线程没有被调用的存活时间
workqueue 等待队列 当核心线程都被使用的时候,就会将任务放在等待队列,去创建线程
threadfactory 线程的工厂
handler 任务拒绝策略
线程池的执行流程
线程池执行任务, 判断核心线程是否已满,未满则创建线程执行任务,满了的话
判断队列是否已满,未满在放进队列,满了
判断是否达到最大线程数,未满就创建临时线程执行,满了
就执行拒绝策略
线程池线程复用的原理
线程池创建出线程后,让其反复判断是否有任务执行,是直接调用run方法进行执行的,没有调用start
如果调用start方法就在线程池中的线程中又创建出一个线程,就就是实现原理
聊聊Spring
spring是一个为了解决企业级开发难度的javaEE框架
spring核心思想就是aop ioc
ioc就是控制翻转,将创建对象的权利交给容器,以前可能我们需要自己撞见对象,维护依赖关系
交给spring这个容器后,由spring创建对象,对需要注入的进行注入,维护依赖关系
aop 面向切面
在不该变原有代码的情况下,横向切入一个模块,实现功能,常见的就是权限以及这个日志功能,个人理解就是一个增强器
将业务逻辑代码,于这个系统服务进行拆分,实现解耦 说道aop就要说道这个动态代理模式
动态代理,有jdk生成代理对象,实现增强功能 proxy invacationHandler
spring bean 的生命周期
1.通过beanfactoryreader读取配置文件
2.根据配置文件解析类得到beandefinition对象
3.执行beanfactorypostprocess 增强器,这里有一个placeholderpostprocess 这要功能是替换xml中的${}
4.实例化对象
5.填充属性
6.执行aware接口
7.调用beanpostprocess 后置增强器 ,aop就是在这里实现的
8.如果是单例就放在单例池
spring bean的作用域
singleton
prototype
session
request
application
spring中 单例bean是线程安全吗
spring中默认是单例模式的,没有对多线程安全进行封装
举个例子 一个Servers 中 有个count 属性 ,调用一次count++ 并发会出现问题

浙公网安备 33010602011771号