收集以下今天的被面试官问到的一些问题,以前忙于学业没有太多接触java,最近找工作可能有点临时抱佛脚做了下复习,回答到不咋地,希望通过后面做毕设的时间巩固一下Java知识吧。
目录
自我介绍
- 来自哪里,毕业院校,时间
- 获得过哪些奖励,实习经验
- 参加过哪些比赛
- 完成过哪些项目
什么是内存溢出并且如何产生的
指程序申请内存时,没有足够的内存供申请者使用,或者说,给了你一块存储int类型数据的存储空间,但是你却存储long类型的数据,那么结果就是内存不够用,此时就会报错OOM,即所谓的内存溢出。
总结:太久没有接触java底层知识了,直接和面试官说了不了解
说一说AOP有哪些实用的场景
- 权限
- 缓存
- 错误处理
- 懒加载
- 调试
- 记录跟踪 优化 校准
- 事务
总结:只回答事物、异常处理、日志,哎其实后面想想学习spring的时候就那时候就经常提到过spring里面啥啥用到了aop。
如何保证多线程的同步
volatile、synchronized、ReentrantLock然后在说说这些锁的作用吧
AOP有哪些通知
前置通知、后置通知、环绕通知、异常通知、最终通知
常见的引用类型有哪些
强引用:最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。
public static User user = new User();
软引用:如果一个对象只具有软引用,则内存空间充足时,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。
public static SoftReference<User> user = new SoftReference<User>(new User());
弱引用:弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
虚引用:顾名思义,就是形同虚设。与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
public static WeakReference<User> = new WeakReference<User>(new User());
引用类型 | 被垃圾回收时间 | 用途 | 生存时间 |
---|---|---|---|
强引用 | 从来不会 | 对象的一般状态 | JVM停止运行时终止 |
软引用 | 当内存不足时 | 对象缓存 | 内存不足时终止 |
弱引用 | 正常垃圾回收时 | 对象缓存 | 垃圾回收后终止 |
虚引用 | 正常垃圾回收时 | 跟踪对象的垃圾回收 | 垃圾回收后终止 |
谈谈微服务的优势和劣势
优点:
- 松藕合:无论是在开发阶段或部署阶段都是独立的。
- 快速响应:局部修改容易, 一个服务出现问题不会影响整个应用。
- 集成第三方:易于和第三方应用系统集成, 支持使用不同的语言开发, 允许你利用融合最新技术。
- 更加专注:每个微服务都很小,足够内聚,足够小,代码容易理解。团队能够更关注自己的工作成果, 聚焦指定的业务功能或业务需求。
- 开发效率:一个服务可能就是专一的只干一件事, 能够被小团队单独开发,这个小团队可以是 2 到 5 人的开发人员组成。
缺点 - 部署困难:微服务架构带来过多的运维操作, 可能需要团队具备一定的 DevOps 技巧.
- 难以管理:分布式系统可能复杂难以管理。因为分布部署跟踪问题难。当服务数量增加,管理复杂性增加。
增加全局资源需求(所有服务器或主机的总内存,驱动器和网络资源)。在许多情况下,当您使用微服务方法替换单一应用程序时,新的基于微服务的应用程序所需的初始全局资源量将大于原始单片应用程序的基础架构需求。这是因为更高的粒度和分布式服务需要更多的全局资源。但是,考虑到整体资源成本低,并且与单个应用程序发展过程中的长期成本相比,能够扩展应用程序的某些区域的好处,增加资源的使用通常是一个很好的权衡期限申请。
什么是内存泄露
JAVA不同与C需要程序员自己进行释放资源,JAVA中存在一个GC的垃圾回收机制,Java中采用的是可达性分析法,从GCRoots开始向下搜索,如果搜索的到则表示这个对象被引用着,否则就会去回收。
在java中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点:
- 对象是可达的
- 对象是无用的,即程序以后不会再使用这些对象。
如果对象满足这两个条件,这些对象就可以判定为java中的内存泄漏,这些对象不会被GC所回收,然而它们却占用内存。
简单说:就是对象没有在被使用,但是GC无法进行回收。
说说微服务有哪些重要的组件
- Eureka:各个服务启动时,Eureka Client都会将服务注册到Eureka Server,并且Eureka Client还可以反过来从Eureka Server拉取注册表,从而知道其他服务在哪里
Eureka Client:负责将这个服务的信息注册到Eureka Server中
Eureka Server:注册中心,里面有一个注册表,保存了各个服务所在的机器和端口号 - Feign:基于Feign的动态代理机制,根据注解和选择的机器,拼接请求URL地址,发起请求
- Ribbon:服务间发起请求的时候,基于Ribbon做负载均衡,从一个服务的多台机器中选择一台
- Hystrix:发起请求是通过Hystrix的线程池来走的,不同的服务走不同的线程池,实现了不同服务调用的隔离,避免了服务雪崩的问题
- Zuul:微服务网关前端所有请求都往网关走,网关会根据请求中的一些特征,将请求转发给后端的各个服务。可以做统一的降级、限流、认证授权、安全,等等
这个我是真的回答不出来了,学了微服务一年多了我至今没有用到项目中,几乎忘到一干二净了
序列化和反序列化的作用
序列化:把对象转换为字节序列的过程称为对象的序列化。
反序列化:把字节序列恢复为对象的过程称为对象的反序列化。
大白话就是可以吧对象和对象数据持久化保存到起来叫序列化,反过来吧序列化的生成的文件转换回对象就是反序列化。
说一说有哪些IO流
对文件进行操作:FileInputStream(字节输入流)、FileOutputStream(字节输出流)、FileReader(字符输入流)、FileWriter(字符输出流)
对管道进行操作:PipedInputStream(字节输入流)、PipedOutStream(字节输出流)、PipedReader(字符输入流)、PipedWriter(字符输出流)
Buffered 缓冲流:BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter
字节转化成字符流:InputStreamReader、OutputStreamWriter
打印流:PrintStream、PrintWriter
对象流:ObjectInputStream、ObjectOutputStream
序列化流:SequenceInputStream
String是否可以被继承
String是被final所修饰类是无法被继承的
表中有大量数据应该如何处理
这个方法有很多种问以问个人学习情况来说说我会的几种吧。
- 读写分离:通过主从数据库服务器,主负责写,从负责读,从服务器同步主服务器。
- 分布式数据库: 将不同的表存放到不同的数据库中,然后再放到不同的服务器中。
- 拆分数据表:通过水平拆分存放到不同的数据库,通过并发查询多台服务器上的表
- 创建索引:对查询好,但是对插入删除非常的不友好
Cookie和Session的区别
- Cookie和Session都是会话技术,Cookie是运行在客户端,Session是运行在服务器端。
- Cookie有大小限制以及浏览器在存cookie的个数也有限制,Session是没有大小限制和服务器的内存大小有关。
- Cookie有安全隐患,通过拦截或本地文件找得到你的cookie后可以进行攻击。
- Session是保存在服务器端上会存在一段时间才会消失,如果session过多会增加服务器的压力。
如何设计权限表
用户表:用户ID,用户名
用户—角色表:ID,用户ID,角色ID
角色表:角色ID,角色名
角色—权限表:ID,角色ID,权限ID
权限表:权限ID,权限名
用户和角色多对多
角色和权限多对多
如何做全局异常处理
异常用的也是spring中的aop来实现的
@ControllerAdvice
public class CommonExceptionHandler {
//需要拦截的异常
@ExceptionHandler(Exception.class)
//如果返回字符就加上 如果返回视图模板加上ModelAndView
@ResponseBody
public Map<String, Object> exceptionHandler(HttpServletRequest req,HttpServletResponse resp,Exception e){
Map<String, Object> map = new HashMap<String, Object>();
map.put("respmsg", e.getStackTrace());
map.put("url", req.getRequestURL());
map.put("status", false);
return map;
}
死锁是如何产生的
- 互斥条件:如一个资源只能被一个进程进行操作,若此时其他进行访问这个资源只能在外面等待
- 不可剥夺条件: 一个资源被使用,只能自己进行释放,不能被其他进程强行夺走。
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
- 循环等待条件:比如A等待B,B等待C,C等待A他就形成了一段循环等待
如何打破死锁
(1) 打破互斥条件:允许进程同时访问某些资源。
(2) 打破不可抢占的条件:就是说允许进程强行从资源的占有者那里抢夺资源。
(3) 打破占有申请条件:可以实现资源预先分配策略,在进程运行前一次性向系统申请他所需要的全部资源。如果进程所需的资源不能满足,则不分配任何资源,进程暂时不运行。
(4) 打破循环等待条件:实行资源的有序分配策略,把资源事先分类编号,按号分配,使进程在申请,占用资源时候不能形成环路,所有进程对资源的请求必须严格按照资源号递增的顺序提出,进程占用了小号的资源,才能申请大号资源。(简单说,对象或方法安装顺序执行如先执行A在执行B)
实现多线程有哪些
- 继承Thread,重写run()方法,调用start()方法
public class TestThread extends Thread{
public void run(){
}
}
new TestThread().start();
- 实现Runable接口,重写run方法,执行线程需要丢入runnable接口的实现类,调用start方法
public class satrtThread implements Runnable{
public void run(){
}
}
new Thread(new startThread()).satrt();
- 实现Callable
- 实现Callable接口泛型作为返回值,并重写call方法
- 使用FutureTask通过构造函数传入Callable实现类
- 使用Thread创建线程并传入FutureTask
- 启动线程start
- 在你需要线程返回值的时候调用FutureTask的get方法获取值
public class CallableThreadTest implements Callable<Integer> {
@Override
public Integer call() throws Exception {
//业务代码
return 1;
}
public static void main(String[] args) {
CallableThreadTest callableThreadTest = new CallableThreadTest();
FutureTask futureTask = new FutureTask<Integer>(callableThreadTest);
Thread thread = new Thread(futureTask);
try {
System.out.println(futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
实现线程池的方式
ThreadPoolExecutor(
int corePoolSize, //该线程池中核心数最大值 可以看成正式员工
int maximumPoolSize,//最大线程数量这里包括核心线程数量超出的都是非核心线程 可以看成临时员工,当正式员工忙不过来的时候就是聘请一些临时员工
long keepAliveTime,//保持的时间
TimeUnit unit,//时间单位 时 分 天 秒 如果keepAliveTime为1 unit为天 就是创建非核心线程数量的时间保持1天
BlockingQueue<Runnable> workQueue//等待队列 聘请了临时员工还是处理不完怎么办,那就堆积到仓库中(队列中)
ThreadFactory threadFactory,//线程工厂 用来生产线程的
RejectedExecutionHandler handler//拒绝策略 如果线程用完了,并且队列也排满了就返回拒绝策略的数据过去,比如在Web应用中返回流量过大,请稍后在试 仓库都爆炸了要不要给客户点反馈了
1.newCachedThreadPool(随着任务的增加而增加线程)
优缺点:容易CPU百分百创建过多的线程,执行效率快
通过以下代码可以看出核心线程一个都没有,来任务都是使用非核心线程,非核心线程模式是不创建的,需要在核心线程不够用的时候才去创建,他有一个生命周期如果多长时间不使用就会进行销毁,所以可以得到核心线程为0,最大非核心线程时integer最大值,在60秒没有使用就销毁非核心线程。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
2.newFixedThreadPool(创建自定义核心线程池)
优缺点:容易内存OOM,队列创建的太多,执行效率偏慢。
这里你可以创建一个默认创建多少个核心线程,最大线程数量和核心线程数量是相同的所以不存在创建非核心线程。等待队列=LinkedBlockingQueue返回的是int最大值
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
3.newSingleThreadExecutor(就一个线程进行复用)
优缺点:容易内容OOM,队列创建的太多,并且执行效率慢。
就创建一个线程,等待队列=LinkedBlockingQueue返回的是int最大值
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
非技术问题
你对开源有什么看法
你开发中是否使用的代码版本管理软件
谈谈你的其中一个项目是如何实现的
你是否在开源程序中提交过你的建议或者代码贡献
你是否使用过什么团队协作软件
你参加的比赛你有哪些心的
考虑过发展Android开发吗
你在团队中如果遇到问题是主动还是被动去处理
未来有什么规划
你对前沿的技术的好奇心
我认为的前沿技术有哪些
只记得这些。