文章目录
- 一、面向过程和面向对象的区别
- 二、java性能低是因为面向过程的原因嘛?
- 三、构造器是否可以被override?
- 四、String, StringBuffer 和 StringBuilder的区别,为什么说String 是 不可变的?
- 五、静态方法为什么不能调用非静态方法?
- 六、接口和抽象类的区别是什么?
- 七、== 和 equals
- 八、你重写过hashCode和equals嘛,为什么重写equals的时候必须重写hashCode方法?
- 九、Java是值传递还是引用传递
- 十、java线程的状态与生命周期
- 十一、关于final的一些总结
- 十二、异常处理总结
- 十三、java 序列化忽略字段
- 十四、java中的IO流
- 十五、深拷⻉ 和 浅拷⻉
提示:以下是本篇文章正文内容,下面案例可供参考
一、面向过程和面向对象的区别
面向过程:面向过程性能会比面像过程高,因为类的调用需要实例化,开销比较大,比较耗费资源,所以当对性能要求比较高的时候,一般采用面向过程,如单片机、嵌入式开发等。
二、java性能低是因为面向过程的原因嘛?
这并不是根本原因,面向过程也需要分配内存,计算偏移量等,java性能低的主要原因是因为他是半编译语言,最终的执行代码并不是可以直接被cpu执行的二进制机械码。
三、构造器是否可以被override?
构造器不可以重写,但是可以重载
四、String, StringBuffer 和 StringBuilder的区别,为什么说String 是 不可变的?
String类中实际上是用final关键字修饰的字符数组 private final char value[],所以String对象是不可变的。在java 9之后,String 类的实现改用byte数组存储字符串
StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder没有用final修饰字符串数组,所以这两种对象都是可变的。这两个对象的构造方法都是调用父类的。
线程安全性
String 是final的所以是线程安全的。StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder没有加锁,所以是非线程安全的。
五、静态方法为什么不能调用非静态方法?
由于静态方法可以不通过类调用,因此在静态方法里不能调用其他非静态变量,因为静态方法先加载到内存。
六、接口和抽象类的区别是什么?
- 方法在接口中不能有实现(java 8允许方法可以有默认实现),而抽象类可以有非抽象的方法。
- 接口中出来static、final变量不能有其他变量,抽象类则不一定。
- 一个类可以实现多个接口,但是只能实现一个抽象类。
- 接口方法默认是public,抽象方法可以有public、protected和default这些修饰符(抽象方法是为了被重写所以不能使用private关键字修饰)。
- 从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为规范。
- 在jdk8中,接口也可以定义静态方法,可以用接口名调用,实现类和实现是不能调用的。如果同时实现了两个接口,接口中定义了一样的默认方方法,则必须重写,否则报错。
- jdk9 的接口允许定义私有接口
jdk7之前,接口中只能有常量变量和抽象方法;jdk8的时候接口可以有默认方法和静态方法功能;jdk9在接口中引入了私有方法和私有静态方法。
七、== 和 equals
- 基本数据类型 == 比的是值
- 引用数据类型 == 比的是地址
- String的特殊情况见图1
- 没覆盖equals方法则等同于 ==
八、你重写过hashCode和equals嘛,为什么重写equals的时候必须重写hashCode方法?
hashCode定义在Object中,作用是获取哈希码,这个哈希码是对象在哈希表中的索引位置,并且hashCode方法是native方法。
当我们向一个Hash结构的集合中添加某个元素,集合会首先调用hashCode方法,这样就可以直接定位它所存储的位置,若该处没有其他元素,则直接保存。若该处已经有元素存在,就调用equals方法来匹配这两个元素是否相同,相同则不存,不同则计算另一个位置储存。
调用调用hashCode的原因是因为hashCode效率更高(仅为一个int值),比较起来更快。
当equals方法被重写时,通常有必要重写hashCode方法,以维护hashCode方法的常规约定:值相同的对象必须有相同的hashCode。
由此可以得到
- object1.equals(object2)为true,hashCode也相同;
- hashCode不同时,object1.equals(object2)为false;
- hashCode相同时,object1.equals(object2)不一定为true;
那如果不重写hashcode会有什么问题呢,举个例子:
new 两个学生,学生只有一个属性,String idCart= 10,他们是两个不同的对象,所以他们默认的hash值是一定不同的,因此我们向hashSet中插入数据时,hashSet会判断hashCode码,发现hashCode码不一样,所以直接插入。此时hashSet中有两个身份证一样的人,但是显然这不是我们想要的结果,所以需要重写hashcode,手动将判断学生的idCart,如果idCart相同,则返回一样的hashcode。
九、Java是值传递还是引用传递
java是值传递,很多语言同时支持值传递和引用传递,在java中,将一个对象作为形参传到一个函数中,函数中的实参承接的是引用值的拷贝(注意实参和原参数是两个对象),如果在函数中对两个对象做swap(a,b);那么实际上对调的只是实参的引用,原来数据的引用并没有对调。所以java对象采用的不是引用调用,实际上对象引用是按值传递的。
十、java线程的状态与生命周期
java线程的几种状态
线程的生命周期
线程创建之后它将处于 NEW(新建) 状态,调⽤ start() ⽅法后开始运⾏,线程这时候处于READY(可运⾏) 状态。可运⾏状态的线程获得了 cpu 时间⽚(timeslice)后就处于RUNNING(运⾏) 状态。
操作系统隐藏 Java 虚拟机(JVM)中的 READY 和 RUNNING 状态,它只能看到RUNNABLE 状态(图源:HowToDoInJava:Java Thread Life Cycle and Thread
States),所以 Java 系统⼀般将这两个状态统称为 RUNNABLE(运⾏中) 状态 。
十一、关于final的一些总结
final主要用在三个关键字上,变量,方法,类
- final修饰变量的时候,变量的值初始化后就再也不能改变其值,修饰引用类型变量的时候,则其在初始化后便不能在指向其他对象。
- final修饰类的时候,这个类将不能在被继承。final类中的成员方法将会隐式指定为final。
- final修饰方法的原因有两个,一个是把方法锁定,防止继承类修改他的含义,另一个是效率,在早期java中会将final方法转为内嵌调用。类中的所有private方法都会隐式指定为final。
十二、异常处理总结
当在 try 块或catch 块中遇到 return 语句时, finally 语句块将在⽅法返回之前被执⾏。
在以下 3 种特殊情况下, finally 块不会被执⾏:
- 在 try 或 finally 块中⽤了 System.exit(int) 退出程序。但是,如果 System.exit(int) 在异常
语句之后, finally 还是会被执⾏ - 程序所在的线程死亡。
- 关闭 CPU。
当 try 语句和 finally 语句中都有 return 语句时,在⽅法返回之前,finally 语句的内容将被
执⾏,并且 finally 语句的返回值将会覆盖原始的返回值。
如果调⽤ f(2) ,返回值将是 0,因为 finally 语句的返回值覆盖了 try 语句块的返回值。
十三、java 序列化忽略字段
对于不想进⾏序列化的变量,使⽤ transient 关键字修饰。
transient 关键字的作⽤是:阻⽌实例中那些⽤此关键字修饰的的变量序列化;当对象被反序列化时,被 transient 修饰的变量值不会被持久化和恢复。transient 只能修饰变量,不能修饰类和⽅法
十四、java中的IO流
1. 流的基本分类
- 按照流的流的流向,可以分为输入流和输出流;
- 按照操作单元划分,可以分为字节流和字符流;
- 按照流的角色划分为节点流和处理流;
Java Io流共涉及40多个类,这些类存在紧密的联系,他们都是从如下四个抽象类基类中派生出来的。。
- InputStream/Reader: 所有的输⼊流的基类,前者是字节输⼊流,后者是字符输⼊流。
- OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流
2. 为什么要有字符流
Java中一切都是字节流,没有字符流,字符只是根据编码对字节流进行翻译的结果。 字符流 = 字节流+编码表
字符流是由 Java 虚拟机将字节转换得到的,问题就出在这个过程还算是⾮常耗时,并且,如果我们不知道编码类型就很容易出现乱码问题。所以, I/O 流就⼲脆提供了⼀个直接操作字符的接⼝,⽅便我们平时对字符进⾏流操作。
3. BIO,NIO,AIO 有什么区别?
- BIO (Blocking I/O): 同步阻塞 I/O 模式,数据的读取写⼊必须阻塞在⼀个线程内等待其完成。在活动连接数不是特别⾼(⼩于单机 1000)的情况下,这种模型是比较不错的,可以 让每⼀个连接专注于⾃⼰的 I/O并且编程模型简单,也不⽤过多考虑系统的过载、限流等问 题。线程池本身就是⼀个天然的漏⽃,可以缓冲⼀些系统处理不了的连接或请求。但是,当⾯对⼗万甚⾄百万级连接的时候,传统的 BIO 模型是⽆能为力的。因此,我们需要⼀种更⾼效的 I/O 处理模型来应对更⾼的并发量。
- NIO (Non-blocking/New I/O): NIO 是⼀种同步⾮阻塞的 I/O 模型,在 Java 1.4 中引⼊了 NIO框架,对应 java.nio 包,提供了 Channel , Selector,Buffer 等抽象。NIO 中的 N 可以 理解为Non-blocking,不单纯是 New。它⽀持⾯向缓冲的,基于通道的 I/O 操作⽅法。 NIO 提供了与传统 BIO 模型中的Socket 和 ServerSocket 相对应的 SocketChannel 和 ServerSocketChannel两种不同的套接字通道实现,两种通道都支持阻塞和非阻塞两种模式。
阻塞模式使⽤就像传统中的模式⼀样,比较简单,但是性能和可靠性都不好;
⾮阻塞模式正好与之相反。对于低负载、低并发的应⽤程序,可以使⽤同步阻塞 I/O 来提升开发速率和更好的维护性;对于⾼负载、⾼并发的(⽹络)应⽤,应使⽤ NIO 的⾮阻塞模式来开发。 - AIO (Asynchronous I/O): AIO 也就是 NIO 2。在 Java 7 中引⼊了 NIO 的改进版 NIO 2,它是异步⾮阻塞的 IO 模型。异步 IO 是基于事件和回调机制实现的,也就是应⽤操作之后会直接返回,不会堵塞在那⾥,当后台处理完成,操作系统会通知相应的线程进⾏后续的操作。 AIO 是异步 IO 的缩写,虽然 NIO在⽹络操作中,提供了⾮阻塞的⽅法,但是 NIO 的 IO ⾏ 为还是同步的。对于 NIO 来说,我们的业务线程是在 IO操作准备好时,得到通知,接着就 由这个线程⾃⾏进⾏ IO 操作,IO 操作本身是同步的。查阅⽹上相关资料,发现就⽬前来 说 AIO的应⽤还不是很⼴泛,Netty 之前也尝试使⽤过 AIO,不过⼜放弃了。
十五、深拷⻉ 和 浅拷⻉
- 浅拷⻉:对基本数据类型进⾏值传递,对引⽤数据类型进⾏引⽤传递般的拷⻉,此为浅拷⻉。
- 深拷⻉:对基本数据类型进⾏值传递,对引⽤数据类型,创建⼀个新的对象,并复制其内容,此为深拷⻉。