上图是我整理的Java面试题合集,包揽了基本所有技术栈:完整版Java面试题合集附答案,高清PDF下载
字节流用于操作包含ASCII字符的文件。JAVA也支持其他的字符如Unicode,为了读取包含Unicode字符的文件,JAVA语言引入了字符流。ASCII作为Unicode的子集,对于英语字符的文件,可以使用字节流也可以使用字符流。
1、 LineNumberInputStream
:给目标文件增加行号。
2、 DataInputStream
:有些特殊的方法如:readInt()、readDouble()和readLine()等可以一次性的读取一个int, double和一个string类型的数据。
3、 BufferedInputStream
:增加性能。
4、 PushbackInputStream
:推送要求的字节到系统中。
引用数据类型分3种:类,接口,数组;
简单来说,只要不是基本数据类型.都是引用数据类型。 那他们有什么不同呢?
1、从概念方面来说
1,基本数据类型:变量名指向具体的数值
2,引用数据类型:变量名不是指向具体的数值,而是指向存数据的内存地址,.也及时hash值
2、从内存的构建方面来说(内存中,有堆内存和栈内存两者)
1,基本数据类型:被创建时,在栈内存中会被划分出一定的内存,并将数值存储在该内存中.
2,引用数据类型:被创建时,首先会在栈内存中分配一块空间,然后在堆内存中也会分配一块具体的空间用来存储数据的具体信息,即hash值,然后由栈中引用指向堆中的对象地址.
举个例子
//基本数据类型作为方法参数被调用 public class Main{ public static void main(String[] args){ //基本数据类型 int i = 1; int j = 1; double d = 1.2; //引用数据类型 String str = "Hello"; String str1= "Hello"; } }
由上图可知,基本数据类型中会存在两个相同的1,而引用型类型就不会存在相同的数据。
假如"hello"的引用地址是xxxxx1,声明str变量并其赋值"hello"实际上就是让str变量引用了"hello"的内存地址,这个内存地址就存储在堆内存中,是不会改变的,当再次声明变量str1也是赋值为"hello"时,此时就会在堆内存中查询是否有"hello"这个地址,如果堆内存中已经存在这个地址了,就不会再次创建了,而是让str1变量也指向xxxxx1这个地址,如果没有的话,就会重新创建一个地址给str1变量。
从使用方面来说
1,基本数据类型:判断数据是否相等,用和!=判断。
2,引用数据类型:判断数据是否相等,用equals()方法,和!=是比较数值的。而equals()方法是比较内存地址的。
补充:数据类型选择的原则
关于Java中的IO,网络通讯模型主要分三种:IO、NIO和AIO。
适用场景:
BIO, 适用于连接较少,对服务器资源消耗很大,但是编程简单。是同步阻塞的。
举例:你到餐馆点餐,然后在那儿等着,什么也做不了,只要饭还没有好,就要必须等着
NIO, 使用于连接数量比较多且连接时间比较短的架构,比如聊天服务器,编程比较复杂。是同步非阻塞的
举例:你到餐馆点完餐,然后就可以去玩儿了,玩一会儿就回饭馆问一声,饭好了没。
AIO, 适用于连接数量多而且连接时间长的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂。是异步非阻塞的。
举例:饭馆打电话给你说,我们知道你的位置,待会儿给您送来,你安心的玩儿就可以了。类似于外卖。
1、IO(同步阻塞)
传统的网络通讯模型,就是BIO,同步阻塞IO, 其实就是服务端创建一个ServerSocket, 然后就是客户端用一个Socket去连接服务端的那个ServerSocket, ServerSocket接收到了一个的连接请求就创建一个Socket和一个线程去跟那个Socket进行通讯。接着客户端和服务端就进行阻塞式的通信,客户端发送一个请求,服务端Socket进行处理后返回响应,在响应返回前,客户端那边就阻塞等待,上门事情也做不了。 这种方式的缺点, 每次一个客户端接入,都需要在服务端创建一个线程来服务这个客户端,这样大量客户端来的时候,就会造成服务端的线程数量可能达到了几千甚至几万,这样就可能会造成服务端过载过高,最后崩溃死掉。
2、NIO(同步非阻塞,JDK1.4)
NIO是一种同步非阻塞IO, 基于Reactor模型来实现的。其实相当于就是一个线程处理大量的客户端的请求,通过一个线程轮询大量的channel,每次就获取一批有事件的channel,然后对每个请求启动一个线程处理即可。这里的核心就是非阻塞,就那个selector一个线程就可以不停轮询channel,所有客户端请求都不会阻塞,直接就会进来,大不了就是等待一下排着队而已。这里面优化BIO的核心就是,一个客户端并不是时时刻刻都有数据进行交互,没有必要死耗着一个线程不放,所以客户端选择了让线程歇一歇,只有客户端有相应的操作的时候才发起通知,创建一个线程来处理请求。
3、AIO(NIO2,异步非阻塞,JDK1.7)
对于NIO来说,我们的业务线程是在IO操作准备好时,得到通知,接着就由这个线程自行进行IO操作,IO操作本身是同步的。但是对AIO来说,则更加进了一步,它不是在IO准备好时再通知线程,而是在IO操作已经完成后,再给线程发出通知。因此AIO是不会阻塞的,此时我们的业务逻辑将变成一个回调函数,等待IO操作完成后,由系统自动触发。
与NIO不同,当进行读写操作时,只须直接调用API的read或write方法即可。这两种方法均为异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统主动通知应用程序。 即可以理解为,read/write方法都是异步的,完成后会主动调用回调函数。 在JDK1.7中,这部分内容被称作NIO.2,主要在Java.nio.channels包下增加了下面四个异步通道:
AsynchronousSocketChannel
AsynchronousServerSocketChannel
AsynchronousFileChannel
AsynchronousDatagramChannel
AIO是异步非阻塞IO,基于Proactor模型实现。 每个连接发送过来的请求,都会绑定一个Buffer,然后通知操作系统去完成异步的读,这个时间你就可以去做其他的事情,等到操作系统完成读之后,就会调用你的接口,给你操作系统异步读完的数据。这个时候你就可以拿到数据进行处理,将数据往回写,在往回写的过程,同样是给操作系统一个Buffer,让操作系统去完成写,写完了来通知你。这俩个过程都有buffer存在,数据都是通过buffer来完成读写。,
我们知道finally{}中的语句是一定会执行的,那么这个可能正常脱口而出就是return之前,return之后可能就出了这个方法了,鬼知道跑哪里去了,但更准确的应该是在return中间执行,请看下面程序代码的运行结果:
public classTest { public static void main(String[]args) { System.out.println(newTest().test());; } static int test() { intx = 1; try { return x; } finally { ++x; } } }
执行结果如下:
1
运行结果是1,为什么呢?主函数调用子函数并得到结果的过程,好比主函数准备一个空罐子,当子函数要返回结果时,先把结果放在罐子里,然后再将程序逻辑返回到主函数。所谓返回,就是子函数说,我不运行了,你主函数继续运行吧,这没什么结果可言,结果是在说这话之前放进罐子里的。
重点是用到两个设计模式:装饰模式和适配器模式。
装饰模式: 在由InputStream、OutputStream、Reader和Writer代表的等级结构内部,有一些流处理器可以对另一些流处理器起到装饰作用,形成新的、具有改善了的功能的流处理器。
适配器模式: 在由InputStream、OutputStream、Reader和Writer代表的等级结构内部,有一些流处理器是对其他类型的流处理器的适配,这就是适配器的应用。
println是PrintStream的一个方法。out是一个静态PrintStream类型的成员变量,System是一个java.lang包中的类,用于和底层的操作系统进行交互。
用于修饰类、属性和方法
&运算符有两种用法:(1)按位与;(2)逻辑与。
&&运算符是短路与运算。逻辑与跟短路与的差别是非常巨大的,虽然二者都要求运算符左右两端的布尔值都是true 整个表达式的值才是 true。&&之所以称为短路运算,是因为如果&&左边的表达式的值是 false,右边的表达式会被直接短路掉,不会进行运算。
注意:逻辑或运算符(|)和短路或运算符(||)的差别也是如此。
Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程
抽象类是用来捕捉子类的通用特性的。接口是抽象方法的集合。
从设计层面来说,抽象类是对类的抽象,是一种模板设计,接口是行为的抽象,是一种行为的规范。
相同点
不同点
参数 | 抽象类 | 接口 |
---|---|---|
声明 | 抽象类使用abstract关键字声明 | 接口使用interface关键字声明 |
实现 | 子类使用extends关键字来继承抽象类。如果子类不是抽象类的话,它需要提供抽象类中所有声明的方法的实现 | 子类使用implements关键字来实现接口。它需要提供接口中所有声明的方法的实现 |
构造器 | 抽象类可以有构造器 | 接口不能有构造器 |
访问修饰符 | 抽象类中的方法可以是任意访问修饰符 | 接口方法默认修饰符是public。并且不允许定义为 private 或者 protected |
多继承 | 一个类最多只能继承一个抽象类 | 一个类可以实现多个接口 |
字段声明 | 抽象类的字段声明可以是任意的 | 接口的字段默认都是 static 和 final 的 |
备注:Java8中接口中引入默认方法和静态方法,以此来减少抽象类和接口之间的差异。
现在,我们可以为接口提供默认实现的方法了,并且不用强制子类来实现它。