目录
1.面向对象与面向过程
面向过程
优点:对程序性能要求比较高,而类调用时开销比较大,比较消耗资源,面向过程开发避免了这一弊端,性能较好
缺点:难维护,难服用,难扩展
面向对象
优点:面向对象有封装、继承、多态特性,可以设计出低耦合的系统,使系统更加灵活易于维护
缺点:性能低于面向过程
2.java语言特点
简单易学
面向对象(封装、继承、多态)
平台无关性(java编译的字节码可以跑在不同平台上的jvm里)
可靠性
安全性(内存自动回收机制防止内存泄漏;异常处理可以中断程序运行;没有指针保护内存数据)
支持多线程
支持网络编程
编译与解释并存(
.java
文件 -> 编译 ->.class
字节码文件 -> 放到jvm虚拟机里解释执行)
3.封装、继承、多态
封装
把一个对象私有化,同时提供一些可以被外界访问的属性的方法。
继承
子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问的,只是拥有。(内存分析:在一个子类被创建的时候,首先会在内存中创建一个父类对象,然后在父类对象外部放上子类独有的属性,两者合起来形成一个子类的对象。)
子类可以拥有自己的属性和方法,即功能扩展。
子类可以用自己的方式实现父类的方法。
多态
程序中定义的引用变量所指向的具体类型和通过该引用变量出发的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象。该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。
在java中有两种形式可以实现多态:继承(多个子类对同一方法的重写)和接口(实现接口并覆盖接口中同一方法)。
4.接口和抽象类的区别
接口中所有方法在接口中不能有实现(注:从java8开始接口方法可以有默认实现),而抽象类可以有非抽象的方法。
接口中除了
static
、final
变量,不能有其他变量,而抽象类中则不一定。一个类可以有多个接口,但只能实现一个抽象类。接口自己本身可以通过
extends
关键字扩展多个接口。接口方法默认修饰符是
public
,抽象方法可以有public
、protected
和default
这些修饰符(抽象方法就是为了被重写所以不能使用private关键字修饰)。从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。
注:在jdk8中,接口也可以定义静态方法,可以直接用接口名调用,实现类和实现是不可以调用的。如果同时出现两个接口,接口中定义了一样的默认方法,则必须重写,不然会报错。
5.成员变量和局部变量区别
从语法形式上看:成员变量是属于类的,而局部变量是在方法中定义的变量或是方法的参数;成员变量可以被
public
、private
、static
等修饰符所修饰,而局部变量不能被访问控制修饰符及static
所修饰;但是,成员变量和局部变量都能被final所修饰。从变量在内存中的存储方式来看:如果成员变量是使用
static
修饰的,那么这个成员变量是属于类的,如果没有使用static
修饰,这个成员变量是属于实例的。而对象存在与对内存,局部变量则存在于栈内存。从变量在内存中的生命周期来看:成员变量是对象的一部分,它随着对象的创建而存在,而局部变量随着方法的调用而自动消失。
成员变量如果没有被赋初值,则会自动以类型的默认值而赋值(例外:被
final
修饰的成员变量必须显式地赋值),而局部变量则不会自动赋值。
6.hashCode与equals
hashCode介绍
hashCode()
的作用是获取哈希码,也成为散列码,它实际上是一个int
整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode()
定义在JDK的Object
对象中,这就意味着java中所有类的对象都包含该函数。
散列表存储的键值对(key-value),特点是:能根据“key”快速检索出对应的“value”。这其中就利用到了散列码。
为什么要有hashCode
当你把对象加入HashSet
时,HashSet
会先计算对象的hashcode
值来判断对象加入的位置,同时也会与其他已经加入的对象的hashcode
值作比较,如果没有相符的hashcode
,HashSet
会假设对象没有重复出现。但是如果发现有相同hashcode
值的对象,这时会调用equals()
方法来检查hashcode
相等的对象是否真的相同。如果两者相同,HashSet
就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。这样我们就大大减少了equals
的次数,相应就大大提高了执行速度。
通过我们可以看出:hashCode()
的作用就是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode()
在散列表中才有用,在其它情况下没用。在散列表中hashCode()
的作用是获取对象的散列码,进而确定该对象在散列表中的位置。
hashCode与equals相关约定
如果两个对象相等,则
hashCode
一定也是相同的两个对象相等,对两个对象分别调用
equals
方法都返回true
两个对象有相同的
hashCode
值,它们也不一定是相等的因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖
hashCode()
的默认行为是对堆上的对象产生独特值。如果没有重写hashCode()
,则该类的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)
7.线程的基本状态
状态名称 | 说明 |
---|---|
NEW | 初始状态,线程被构建,但是还没有调用start()方法 |
RUNNABLE | 运行状态,Java县城将操作系统中的就绪和运行两种状态笼统地称作“运行中” |
BLOCKED | 阻塞状态,表示线程阻塞于锁 |
WAITING | 等待状态,表示线程进入等待状态,进入该状态表示但钱县城需要等待其他线程做出一些特定动作(通知或中断) |
TIME_WAITING | 超时等待状态,该状态不同于WAITING,它是可以在指定的时间自行返回 |
TERMINATED | 终止状态,表示当前县城已经执行完毕 |
8.Java中的异常处理
Java异常类层次结构图
在 Java 中,所有的异常都有一个共同的祖先java.lang包中的 Throwable类。Throwable: 有两个重要的子类:Exception(异常) 和 Error(错误) ,二者都是 Java 异常处理的重要子类,各自都包含大量子类。
Error(错误):是程序无法处理的错误,表示运行应用程序中较严重问题。大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。例如,Java虚拟机运行错误(Virtual MachineError),当 JVM 不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。
这些错误表示故障发生于虚拟机自身、或者发生在虚拟机试图执行应用时,如Java虚拟机运行错误(Virtual MachineError)、类定义错误(NoClassDefFoundError)等。这些错误是不可查的,因为它们在应用程序的控制和处理能力之 外,而且绝大多数是程序运行时不允许出现的状况。对于设计合理的应用程序来说,即使确实发生了错误,本质上也不应该试图去处理它所引起的异常状况。在 Java中,错误通过Error的子类描述。
Exception(异常):是程序本身可以处理的异常。Exception 类有一个重要的子类RuntimeException。RuntimeException 异常由Java虚拟机抛出。NullPointerException(要访问的变量没有引用任何对象时,抛出该异常)、ArithmeticException(算术运算异常,一个整数除以0时,抛出该异常)和 ArrayIndexOutOfBoundsException(下标越界异常)。
注意:异常和错误的区别:异常能被程序本身可以处理,错误是无法处理。
Throwable类常用方法
public string getMessage():返回一场发生时的详细信息
public string toString():返回一场发生时的简要描述
public string getLocalizedMessage():返回一场对象的本地化信息。使用Throwable的子类覆盖这个方法,可以声称本地化信息。如果子类没有覆盖该方法,则该方法返回的信息与getMessage()返回的结果相同
public void printStackTrace():在控制台上打印Throwable对象封装的异常信息
异常处理总结
try 块:用于捕获一场。其后可接零个或多个catch块,如果没有catch块,则必须跟一个finally块。
catch 块:用于处理try 捕获到的异常。
finally 块:无论是否捕获或处理异常,finally 块里的语句都会被执行。当在try 块或catch 块中遇到return 语句时,finally 语句块将在方法返回之前被执行。
在一下4种特殊情况下,finally 块不会被执行
在finally 语句块第一行发生了异常,因为在其他行,finally 块还是会得到执行
在前面的代码中用了
System.exit(int)
已退出程序。exit
是带参函数;若该语句在异常语句之后,finally 会执行程序所在的线程死亡
关闭CPU
关于返回值
如果try 语句里有return,返回的是try 语句块中变量值。详细执行过程如下:
如果有返回值,就把返回值保存到局部变量中;
执行jsr指令跳到finally 语句里执行;
执行完finally 语句后,返回之前保存在局部变量表里的值;
如果try,finally 语句里均有return,忽略try 的return,而使用finally的return。
9.Java中IO流
java中IO流分为几种?
按照流的流向分,可以分为输入流和输出流
按照操作单元划分,可以划分为字节流和字符流
按照刘的角色划分,可以划分为节点流和处理流
Java IO流共涉及40多个类,这些类看上去很杂乱,但实际上很有规则,而且彼此之间存在非常紧密的联系,Java IO流的40多个类都是从如下4个抽象类基类中派生出来的。
InputStream/Reader:所有的输入流的基类,前者是字节输入流,后者是字符输入流。
OutputStream/Writer:所有输出流的基类,前者是字节输出流,后者是字符输出流。
按操作方式分类结构图:
按操作对象分类结构图:
BIO,NIO,AIO有什么区别?
BIO (Blocking I/O): 同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。在活动连接数不是特别高(小于单机1000)的情况下,这种模型是比较不错的,可以让每一个连接专注于自己的 I/O 并且编程模型简单,也不用过多考虑系统的过载、限流等问题。线程池本身就是一个天然的漏斗,可以缓冲一些系统处理不了的连接或请求。但是,当面对十万甚至百万级连接的时候,传统的 BIO 模型是无能为力的。因此,我们需要一种更高效的 I/O 处理模型来应对更高的并发量。
NIO (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操作本身是同步的。