前言

昨天知道得力集团在某一个培训机构进行校园招聘。于是我今天就来了,听了一下宣讲内容。发现得力集团刚8月份在武汉成立了研究院,主要是做云服务,从0开始,现在的团队规模大概在20多人。一开始宣讲的是HR,后来就是技术总监,感觉技术总监给人一种很厉害的感觉。

不过薪资的确是太低了,4.5K - 5.5K,而且浮动的1k还要看学历。得力主要诱惑我的是云服务项目是从0开始,能让自己得到很大的提高。我的确心动了一下,心想先去面试一下,看自己的技术怎么样。


初面

  • 面试官是一个很漂亮的HR小姐姐。首先让我自我介绍一下,然后问了我以下的几个问题。

    • 你的职业规划是什么?
    • 你是怎么学习的?
    • 你觉得得力集团怎么样?

我回答的很干净利落,然后进入了复试。复试面试官是一个HR和技术总监,很让我意外的是技术总监问的题目把我问懵逼了,我都无法完整的答上来。

面试的最后,技术总监问我有什么想说的吗,我就咨询了加薪的标准,然后HR顿时脸黑了,很不耐烦的跟我说一堆。我从她的话知道了,涨薪极小值是10%,极大值是30%。我顿时感觉无望了,考核标准还是一年,而且实习没有薪水。最后HR还问我挂科没有,我说挂了单片机。

HR一听脸又黑了,不耐烦的噼里啪啦的说了一堆。我现在对得力集团完全没有好感了,但是技术总监难倒我的问题,我还是需要复盘分析一波,毕竟学习是自己的。


关于复试的题目

观察者模式

这个模式我很熟悉,EventBus的实现就是基于这个模式。但是还是有必要的提起这个模式。
* 当对象存在一对多关系时,则使用观察者模式。比如,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为模式。

  • 用白话说,就是观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,让它们能够自动更新自己。

  • 观察者模式的组成:

    • 抽象主题角色:把所有对观察者对象的引用保存在一个集合中,一般用ArrayList。每个抽象主题角色都可以有任意数量的观察者。抽象主题可以提供一个接口,可以增加和删除观察者。一般用一个抽象类和接口来实现。

    • 抽象观察者角色:为所有具体的观察者定义一个接口,在得到主题的通知时可以更新自己。

    • 具体主题角色: 在具体主题内部状态发生改变的时候,给所有注册过的观察者发出通知。

    • 具体观察者角色: 实现抽象观察者中的更新接口,以便使本身的状态与主题的状态相互协调。

  • 手写观察者模式Demo

    • 定义一个Subject类,也就是被观察者。
public class Subject {private List<Observer> observers = new ArrayList<Observer>();private int state;public int getState() {return state;}public void setState(int state) {this.state = state;notifyAllObservers();}public void attach(Observer observer) {observers.add(observer);}public void notifyAllObservers() {for (Observer observer : observers) {observer.update();}}
}
  • 定义一个抽象的ObServer类,也就是抽象的观察者类。
public abstract class Observer {protected Subject subject;public abstract void update();
}
  • 定义一个具体的BinaryObserver类,它继承ObServer类。
 public class BinaryObserver extends Observer {public BinaryObserver(Subject subject) {this.subject = subject;this.subject.attach(this);}@Overridepublic void update() {System.out.println("binary=" + Integer.toBinaryString(subject.getState()));}
}
  • 定义一个具体的OctalObserver类,它继承于ObServer类。
 public class OctalObserver extends Observer {public OctalObserver(Subject subject) {this.subject = subject;this.subject.attach(this);}@Overridepublic void update() {System.out.println("octal:" + Integer.toOctalString(subject.getState()));}
}
  • 编写测试用例ObserverPatternDemo,并运行。
public class ObserverPatternDemo {public static void main(String[] args) {Subject subject = new Subject();new BinaryObserver(subject);new OctalObserver(subject);subject.setState(15);subject.setState(10);}
}

  • 观察者模式的优缺点:
    优点:

    • 观察者和被观察者是抽象耦合的。
    • 建立一套触发机制。

    缺点:

    • 如果一个被观察者对象有很多的直接和间接的观察者的话, 将所有的观察者都通知到会花费很多时间。

    • 如果在观察者和被观察者之间有循环依赖的话,被观察者会触发它们之间进行循环调用,可能会导致系统崩溃。

    • 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。


POST和GET的区别

面试的时候,回答POSTGET的区别,受到了网上一些博客的误导。现在必须开始纠正了。

  • GETPOST本质上是TCP链接,并无差别。大多数浏览器通常都会限制url长度在2K个字节,而大多数服务器最多处理64K大小的url。由于HTTP的规定和浏览器/服务器的限制,导致它们在应用过程中体现出不同。

  • 对于GET方式的请求,浏览器会把http headerdata一起发送出去,服务器响应200(返回数据)。
    而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再去发送data,服务器响应200(返回数据)。所以GET产生一个TCP数据包,POST产生两个数据包。并不是所有浏览器都会在POST中发送两次包,而Firefox就只发送一次。

  • GET把参数包含在URL中,POST通过request body传递参数。

  • 幂等主要是为了处理同一个请求重复发送的情况,比如在请求响应前失去连接,如果方法是幂等的,就可以放心的重发一次请求。GETPUTDELETE都是幂等的,但是POST不是幂等,这也是浏览器再后退或者刷新时遇到POST请求会给用户提示的原因,重复请求可能会造成意想不到的结果。


什么是幂等?

  • 幂等是一个数学或计算机学概念。常用于抽象代数中。对于单目运算符来说,如果一个运算对于在范围内的所有的一个数多次进行该运算所得的结果和进行一次运算所得的结果是一样的。那么我们就称该运算是幂等的。比如绝对值运算就是一个例子。在实数集中,有abs(a) = abs(abs(a))。对于双目运算,则要求当参与运算的两个值都是等值的情况下,如果满足运算结果与参与运算的两个值相等,那么可以称这个运算为幂等。比如max(x,x) = x

  • 幂等是指同一个请求方法执行多次和仅执行一次的效果完全相同。


SpringMVC注解

  • 关于SpringMVC注解,可以看我之前的一篇文章有提到过。MyBatis-Spring官方文档 学习笔记

  • 面试官问我自动扫包的注解,但是我忘记怎么读了。<context:component-scan base-package="com.augmentum.exam" />


Java序列化

  • 序列化就是把对象转换成字节序列的过程。

  • 反序列化就是把字节序列恢复为对象的过程。·

  • ParcelableSerializable都能实现序列化。SerializableJava中的序列化接口,其使用起来简单但是开销很大,序列化和反序列化过程需要大量的I/O操作。而ParcelableAndroid中的序列化方式,因此更适合在Android平台上,它的缺点就是使用起来稍微麻烦点,但是它的效率很高,这是Android推荐的序列化方式,因此我们要首选ParcelableParcelable主要用在内存序列化上,Serializable主要用于将对象序列化到存储设备中或者将对象序列化后通过网络传输。

  • 我们需要指定serialVersionUID的值,如果反序列化时当前的类有所改变,比如增加或者删除了某些成员变量,那么系统就会重新计算当前类的hash值并把它赋值给serialVersionUID。这个时候当前类的serialVersionUID就和序列化的数据中的serialVersionUID不一致,于是反序列化失败了。

面试官问我JavaSerializable序列化性能太差,问我如何高效的序列化。当时一脸懵逼,不知所云。现在回想起来,应该回答使用第三方序列化工具,也就是fastjson

  • 替换其他所有的json库,java世界里没有其他的json库能够和fastjson可相比了。

  • 使用fastjson的序列化和反序列化替换java Serializablejava Serializable不单性能慢,而且体积大。

  • 使用fastjson替换hessian(是一个基于binary-RPC实现的远程通讯library,使用二进制传输数据),json协议和hessian协议大小差不多一样,而且fastjson性能优越,10倍于hessian

  • fastjson用于memcached(是一个高性能的分布式内存对象缓存对象系统,用于动态Web应用以减轻数据库负载)缓存对象数据。

写着写着,突然又想到了Externalizable接口。这是Java提供的另一种序列化机制,这种序列化方式完全由程序员决定存储和恢复对象数据。要实现该目标,Java类必须实现Externalizable接口。我们接下来写一个Demo

  • 定义一个Person类,实现了java.io.Externalizable接口。Person类必须去实现readExternal()writeExternal()两个方法。
public class Person implements Externalizable {private String name;private int age;public Person(String name, int age) {System.out.println("有参数的构造器");this.name = name;this.age = age;}public Person() {System.out.println("无参数的构造器");}@Overridepublic void writeExternal(ObjectOutput out) throws IOException {out.writeObject(new StringBuffer(name).reverse());out.writeInt(age);}@Overridepublic void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {this.name = ((StringBuffer) in.readObject()).reverse().toString();this.age = in.readInt();}@Overridepublic String toString() {return "Person [name=" + name + ", age=" + age + "]";}
}
  • 接下来我们写一个测试用例。
public class ExternalizableDemo {public static void main(String[] args) throws IOException {File fileName = new File("externalizable.txt");FileOutputStream fos = new FileOutputStream(fileName);FileInputStream fis = new FileInputStream(fileName);ObjectOutputStream os = new ObjectOutputStream(fos);ObjectInputStream is = new ObjectInputStream(fis);try {Person person = new Person("cmazxiaoma", 21);os.writeObject(person);os.writeObject(person);Person newPerson = (Person) is.readObject();System.out.println(newPerson);System.out.println("两个person对象引用是否相等 :" + person == newPerson + "");} catch (Exception e) {System.out.println(e.getMessage());} finally {close(is);close(os);close(fis);close(fos);}}public static void close(Closeable closeable) {try {if (closeable != null) {closeable.close();}} catch (IOException e) {e.printStackTrace();}}
}
  • 运行测试用例,看控制台输出。我们发现反序列化的时候,会读取Java对象中的数据,然后调用无参构造器给对象完成必要的初始化。我们还会发现序列化之前的Person对象和反序列之后生成的Person对象不是同一个对象。那么得出结论:反序列会重新生成一个对象。

  • 那么可以有一个假设,使用Externalizable方式反序列化会调用无参构造器。我们去掉Person类的无参构造器,再运行一下,会发生什么呢?会打印出"no valid constructor"这一行,很显然需要一个无参构造器。

关于对象序列化,还有几点需要注意。
* 对象的类名、实例变量(包括基本类型、数组、对其他对象的引用)都会被序列化;方法、类变量,transient实例变量(瞬态实列变量)都不会被序列化。

  • 实现Serializable接口的类如果需要让某一个实例变量不被序列化,则可以在该实例变量前加transient修饰符,而不是加static关键字。虽然static关键字也可以达到这种效果,但是不能这样用。

  • 反序列化对象时必须有序列化对象的class文件。

  • 当通过文件、网络来读取序列化后的对象时,必须按实际写入的顺序读取。

  • Serializable反序列化机制在恢复Java对象时无需调用构造器来初始化Java对象,而Externalizable反序列化机制就需要无参构造器。

在这里还需要说,Java序列化机制采用一种特殊的序列化算法,如下:

  • 所有保存到磁盘中的对象都有一个序列化编号。

  • 当程序试图序列化一个对象时,程序将先检查该对象是否已经被序列化过,只有该对象从未在本次虚拟机中被序列化过,系统才会将该对象转换成字节序列并输出。

  • 如果某个对象已经序列化过,程序将只输出一个序列化编号,而不是再次重新序列化该对象。


什么是NIO?

关于NIO这个概念,也是我学习Java知识所忽略的一个点吧。以前看博客的时候,零星的看过,当时没有什么在意。记得昨天技术总监问我NIO是什么? 我当时没听清他的回答,然后反问NIO是什么? 他跟我说NIO是异步IO,也就是Asynchronous IO的意思。当时一脸懵逼,不知所云。今天在掘金上面搜索了一下关于NIO的文章,也总结一波。

  • BIO(Blocking I/O):同步阻塞IO模式,数据的读取写入必须阻塞在一个线程内等待其完成。

  • NIO(New I/O):同时支持阻塞和非阻塞模式。我们以同步非阻塞IO模式来说,如果拿烧开水为说,NIO的做法是开启一个线程不断的轮询水壶的状态。

  • AIO(Asynchronous I/O):异步非阻塞IO模式。异步非阻塞和同步非阻塞的区别在于无需开启一个线程去轮询水壶的状态,当水烧开了,水壶会发生叫声,系统就会通知对应的线程来处理。

那么我们需要说同步和异步的区别了。

  • 同步:比如发送一个请求,需要等待返回,然后才能发送下一个请求,中间有等待过程。

  • 异步:指发送一个请求,不需要等待返回,随时可以再发送下一个请求,即不需要等待。

  • 场景需求: 同步可以避免读脏数据的发生。一般共享某一资源的时候用,如果每个人都有修改权限,当A删除了一个文件时,B又去访问该文件,就会出错,应该使用同步机制。比如银行的转账系统,数据库的保存操作等就需要同步了。

那么NIOIO有什么区别呢

  • IO只能实现阻塞式的网络通信,NIO能够实现非阻塞的网络通信。

  • 标准IO基于字节或者字符流进行操作,而NIO是基于Channel进行操作的。

  • 流的读写通常是单向的,要么是输入,要么输出。
    通道是双向的,既可以写数据到Channel,又可以从Channel读取数据。

区别说完了,那么开始NIO之旅了。

  • NIO使用了不同的方式来输入IONIO采用内存映射文件的方式去处理输入/输出,NIO将文件或者文件的一段区域映射到内存中,这样就可以向访问内存一样来访问文件了。

  • Channel与传统的InputStreamOutputStream最大的区别在于它提供了一个map()方法,通过该map方法可以直接将一块数据映射到内存中。如果说传统的输入/输出系统是面向流的处理,那么NIO则是面向块的处理。

  • Buffer可以理解成一个容器,它的本质是一个数组,发送到Channel中的所有对象都必须先放到Buffer中,而从Channel中读取的数据也必须先放入Buffer

  • NIO还提供了用于将Unicode字符串映射成字节序列以及逆映射操作的Charset类,也提供了非阻塞式输入/输出的Selector类。

在Buffer中有3个重要的概念: 容量(capacity),界限(limit),位置(position)

  • capacity: 缓冲区的容量标识该Buffer的最大数据容量。

  • limit:位于limit后的数据既不可被读,也不可被写。

  • position:用于指明下一个可以被读写的缓冲区位置的索引(类似于IO流中的记录指针)。

接着就来说Buffer中的flip()clear()方法。

  • Buffer装入数据结束后,调用Bufferflip()方法,该方法将limit设置为position位置,并将position设为0,这就使得Buffer的读写指针又移动了开始位置。简而言之,filp()为从Buffer中取出数据做好准备。

  • Buffer输出数据结束后,Buffer调用clear()方法,clear()方法不是清空Buffer中的数据,它仅仅将position置为0,将limit设置为capacity,这样为再次向Buffer中装入数据做好准备。

理论总结的很多,那么开始手写代码吧。

  • 我们在NIODemo中写了3种方法,都是从读取"nio_read.txt"文件的内容,然后写入"nio_write.txt"文件中。
public class NIODemo {public static void main(String[] args) throws IOException {// methodOne();// methodTwo();methodThree();}public static void methodOne() throws IOException {String rFile = "nio_read.txt";String wFile = "nio_write.txt";FileChannel rFileChannel = new FileInputStream(rFile).getChannel();FileChannel wFileChannel = new FileOutputStream(wFile).getChannel();ByteBuffer buff = ByteBuffer.allocate(1024);while (rFileChannel.read(buff) > 0) {buff.flip();wFileChannel.write(buff);buff.clear();}close(wFileChannel);close(rFileChannel);}public static void methodTwo() throws IOException {String rFile = "nio_read.txt";String wFile = "nio_write.txt";FileChannel rFileChannel = new FileInputStream(rFile).getChannel();FileChannel wFileChannel = new FileOutputStream(wFile).getChannel();rFileChannel.transferTo(0, rFileChannel.size(), wFileChannel);close(wFileChannel);close(rFileChannel);}public static void methodThree() throws IOException {String rFile = "nio_read.txt";String wFile = "nio_write.txt";RandomAccessFile raf = new RandomAccessFile(rFile, "rw");FileChannel randomChannel = raf.getChannel();FileChannel wFileChannel = new FileOutputStream(wFile).getChannel();// 将Channel中的所有数据映射成ByteChannelByteBuffer buff = randomChannel.map(FileChannel.MapMode.READ_ONLY, 0, raf.length());// 把Channel的指针移动到最后randomChannel.position(raf.length());wFileChannel.write(buff);close(wFileChannel);close(randomChannel);}public static void close(Closeable closeable) {try {if (closeable != null) {closeable.close();}} catch (IOException e) {e.printStackTrace();}}
}
  • 既然methodThree()方法中用到了RandomAccessFile。那么就顺便说一下使用注意事项:RandomAccessFile依然不能向文件的指定位置插入内容,如果直接将文件记录指针移动到中间某一个位置后开始输出,则新输出的内容会覆盖文件中原有的内容。如果需要向指定位置插入内容,程序需要先把插入点后面的位置读入到缓冲区,等把需要插入的数据写入文件中后,再把缓冲区的内容追加到文件后面。

参考文献

  • 99%的人都理解错了HTTP中GET与POST的区别

  • get和post的区别

  • 观察者模式

  • 观察者模式(订阅与发布模式)

  • 浅谈HTTP中Get与Post的区别

  • Fastjson 技术内幕

  • 解读Java的NIO

  • 这么说吧,NIO很简单,其实就是个牛逼IO

  • 以Java的视角来聊聊BIO、NIO与AIO的区别?


注意事项

  • 如果第一个参考文献链接打开提示参数错误,那么请复制链接通过QQ或者微信打开https://mp.weixin.qq.com/s?sn=71f6c214f3833d9ca20b9f7dcd9d33e4&__biz=MzI3NzIzMzg3Mw%3D%3D&mid=100000054&idx=1#rd

  • 如果最后一个参考文献链接打开提示参数错误,那么请复制链接通过QQ或者微信打开
    https://mp.weixin.qq.com/s?__biz=MzIzMzgxOTQ5NA==&mid=100000199&idx=1&sn=1e9006f2289cdfb612f22e9f6b7b44cb&chksm=68fe9dce5f8914d8ba791b26ae6de6742686dc660db0f38a67c41ed87c942cbe679f26a4b24c#rd


尾言

心之所向,素履以往。生如逆旅,一苇以航。总有一天,已百炼,遂成钢。

一个Java小白面试得力集团的收获总结相关推荐

  1. 一个Java小白面试得力集团的收获

    ###前言 昨天知道得力集团在某一个培训机构进行校园招聘.于是我今天就来了,听了一下宣讲内容.发现得力集团刚8月份在武汉成立了研究院,主要是做云服务,从0开始,现在的团队规模大概在20多人.一开始宣讲 ...

  2. 超仪电子 java面试_全靠这份阿里大佬的“Java进阶面试手册”助我收获蚂蚁金服offer!...

    都2021年了,你的工资涨了吗? 对于即将到来的金三银四跳槽涨薪季,我想很多小伙伴都已经摩拳擦掌了吧!当然,我猜也有很多小伙伴是下图的状态吧!毕竟面试一年比一年难[落泪] 不得不说现在面试确实很难,现 ...

  3. 勇敢猿猿不怕困难!为去大厂我把这个Java大厂面试真题刷了几十遍!(2021最新版)

    BAJT等互联网公司的高薪和福利吸引了很多工程师的加入,面试难度也水涨船高,一线互联网公司面试有哪些特点,如何更好的准备互联网公司的面试?本篇文章就针对一线大厂后端面试,从方法论到实战,帮你梳理一个J ...

  4. 视频教程:Java常见面试题目深度解析!

    视频教程:Java常见面试题目深度解析! Java作为目前比较火的计算机语言之一,连续几年蝉联最受程序员欢迎的计算机语言榜首,因此每年新入职Java程序员也数不胜数.很多java程序员在学成之后,会面 ...

  5. 从Java小白到收获BAT等offer,分享我这两年的经验和感悟,BAT 面试官 如何面试

    写在最前面,我总结出了很多互联网公司的面试题及答案,并整理成了文档,以及各种学习的进阶学习资料,免费分享给大家.扫码加微信好友进[程序员面试学习交流群],免费领取.也欢迎各位一起在群里探讨技术. 微信 ...

  6. 如果你是一个Java面试官,你会问哪些问题?

    作为一名年近40的大龄IT从业人员,在上市公司当经理兼创业公司当总监,从面试上来说也算是阅人无数了吧,所以谈谈个人作为Java面试官,我一般会问的一些问题,希望对你有所收获. 一.请自我介绍 我一般面 ...

  7. 从Java小白到收获BAT等offer,分享我这两年的经验和感悟

    我 常想,人生最有趣莫过于前路未知.于是我常常羡慕那些个"金梁古温"笔下随遇而安.随性而为.随缘而爱的浪子们.比如陆小凤,比如叶开. 写在最前 我写过很多篇秋招总结,这篇文章应该是 ...

  8. 一个很艰难的 Java 核心面试问题!

    一个很艰难的 Java 核心面试问题,这个 Java 问题也常被问: 什么是线程安全的单例,你怎么创建它. 好吧,在Java 5之前的版本, 使用双重检查锁定创建单例 Singleton 时,如果多个 ...

  9. java resume过时方法_面试官没想到,一个 Java 线程生命周期,我可以扯半小时

    面试官:你不是精通 Java 并发吗?从基础的 Java 线程生命周期开始讲讲吧. 好的,面试官.吧啦啦啦... 如果要说 Java 线程的生命周期的话,那我觉得就要先说说操作系统的线程生命周期 因为 ...

最新文章

  1. python操作MariaDB
  2. 解决SVN 每次操作都需要重输入用户名密码问题
  3. php中register_global,PHP安全之register_globals的on和off的区别
  4. 神奇的滚动动画,30个视差滚动网站设计
  5. matlab实现评价图像增强效果的参数——信背比(SBR)
  6. Linux 三剑客之SED行天下
  7. vscode+XDebug调试远程环境(虚拟机)上的PHP代码
  8. TPYBoard v102 DIY照相机(视频和制作流程)
  9. CentOS查看软件源提供的软件版本命令
  10. Ubuntu Server安全Webserver搭建流程
  11. SAP CRM呼叫中心end按钮的实现逻辑
  12. ASP.NET与ASP.NET Core用户验证Cookie并存解决方案
  13. sdoi2017 r1 不堪回首
  14. Java中文编码转换与字节长度判断
  15. 已知两点坐标求水平距离_根据两点经纬度计算距离
  16. mysql完全卸载教程(图文详细)
  17. AndroidStudio 自带avd模拟器WiFi网络受限无法连接
  18. HR在线揭秘:面试总被虐?这 12 个必问题请记好答案!
  19. GitHub 狂飙 30K+star 面试现场, 专为程序员面试打造, 现已开源可下载
  20. 10、VUE组件基本使用

热门文章

  1. 课程设计_左支座加工工艺和钻φ25孔夹具设计(说明书+CAD图纸+工序卡+过程卡)
  2. 150万海淘一个还会流汗的“AI老婆”:5年保修30天包退,弄疼她了还要哄
  3. mysql ora01031_ORA-01031: insufficient privileges
  4. 陶渊明劝学——勤学如春起之苗
  5. 如何使用Arduino开发板和DS1307 RTC模块制作智能提醒器
  6. 蓝牙耳机哪款音质最好?公认音质好的蓝牙耳机品牌
  7. √[ (11^4+100^4+111^4)÷2]的开根号算法
  8. 实战篇-OpenSSL之AES加密算法-CFB1模式
  9. 深圳俱乐部3月活动《IT大讲堂---思想的盛筵》
  10. 双机3D影厅收集,方便买不到IMAX的朋友,欢迎完善补充