设计模式
- Java包含23种设计模式,是一套对代码设计经验的总结
JSP九大内置对象
request对象,response对象,session对象,application对象,out对象,pageContext对象,config对象,page对象,exception对象。
单例模式
- 单例模式是创建对象的一种特殊方式,程序从始至终都只创建一个对象叫单例(单实例)
工厂方法模式
- 创建对象的过程不再由当前类实例化,而是由工厂类完成,在工厂类中只需要告知对象类型即可。工厂模式中必须依赖接口
- 简单理解:一个工厂类包含了若干个小类,都集成到了工厂类中,创建任何对象都得找工厂类,工厂类生产,电脑接口分配给各个类重写电脑接口中的方法.
抽象工厂模式
- 围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。
- 对于在工厂方法的基础上,对同一个品牌的产品有不同的分类,并对分类产品创建的过程 ,一个汽车产品 会分为不同的种类(迷你汽车 ,SUV汽车 )
总结:
1、对于简单工厂,用于生产同一结构中的任意产品,对于新增产品不适用。
2、对于工厂方法,在简单工厂的基础上,生产同一等级结构中笃定产品,可以支持新增产品。
3、抽象工厂,用于生产不同种类(品牌)的相同类型(迷你,SUV),对于新增品牌可以,不支持新增类型
java和c++的区别
- java不提供指针来直接访问内存,程序内存更加安全
- Java 的类是单继承的, C++ ⽀持多重继承;虽然 Java 的类不可以多继承,但是接⼝可以多继
承。 - Java 有自动内存管理机制,不需要程序员手动释放内存
final参数
对于⼀个 final 变量,如果是基本数据类型的变量,则其数值⼀旦在初始化之后便不能更改;
如果是引⽤类型的变量,则在对其初始化之后便不能再让其指向另⼀个对象。
2. 当⽤ final 修饰⼀个类时,表明这个类不能被继承。 final 类中的所有成员⽅法都会被隐式地
指定为 final ⽅法。
3. 使⽤ final ⽅法的原因有两个。第⼀个原因是把⽅法锁定,以防任何继承类修改它的含义;第
⼆个原因是效率。在早期的 Java 实现版本中,会将 final ⽅法转为内嵌调⽤。但是如果⽅法
过于庞⼤,可能看不到内嵌调⽤带来的任何性能提升(现在的 Java 版本已经不需要使⽤ final
⽅法进⾏这些优化了)。类中所有的 private ⽅法都隐式地指定为 final。
静态方法和实例方法有何不同
1.在外部调用静态方法时,可以使"类名.方法名",也可以使用"对象名.方法名"。
实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象。
2. 静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许
访问实例成员变量和实例方法;实例方法则无此限制。
Java 中 IO 流
InputStream/Reader、OutputStream/Writer(字节流和字符流)
缓冲操作、基本数据类型操作、对象序列化操作、打印控制、文件操作、管道操作、数组操作。
既然有了字节流,为什么还要有字符流?
字符流是由 Java 虚拟机将字节转换得到的,问题就出在这个过程还算是非常耗时,并且,如果我们不知道编码类型就很容易出现乱码问题。所以, I/O 流就干脆提供了一个直接操作字符的接口,便我们平时对字符进行流操作。如果音频文件、图片等媒体文件用字节流较好,如果涉及到字符的话使用字符流较好。
Arraylist 与 LinkedList 区别
- 底层数据结构: Arraylist 底层使用的是 Object 数组; LinkedList 底层使用的是双向链表(相当于队列)
- LinkedList 不支持随机元素访问,ArrayList 支持。快速随机访问就是通过元素的序号快速获取元素对象(对应于 get(int index) 方法)。
- 内存空间占用: ArrayList的空间浪费主要体现在在list列表的结尾会预留⼀定的容量空间,LinkedList的空间花费则体现在它的每⼀个元素都需要消耗⽐ArrayList更多的空间(因为要存放直接后继和直接前驱以及数据)。
HashMap 和 Hashtable 的区别
- 线程是否安全: HashMap 是非线程安全的, HashTable 是线程安全的; HashTable 内部的方法基本都经过 synchronized 修饰。
HashMap的底层实现
- JDK1.8 之前 HashMap 底层是** 数组和链表 **结合在⼀起使⽤也就是 链表散列。 HashMap 通过 key 的
hashCode 经过扰动函数处理过后得到 hash 值,然后通过 (n - 1) & hash 判断当前元素存放的位置
(这⾥的 n 指的是数组的⻓度),如果当前位置存在元素的话,就判断该元素与要存⼊的元素的 hash
值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。 - JDK1.8之后
相比于之前的版本, JDK1.8之后在解决哈希冲突时有了较⼤的变化,当链表长度大于阈值(默认为8)
时,将链表转化为红黑树,以减少搜索时间。
多线程
进程
一个进程在其执行的过程中可以产生多个线程。与进程不同的是同类的多个线程共享进程的堆和zhan法区资源,但每个线程有程序计数器、虚拟机栈和本地法栈,所以系统在产个线程,或是在各个线程之间作切换⼯作时,负担要进程得多,也正因为如此,线程也被称为轻量级进程。
当一个线程的时间片用完的时候就会重新处于就绪状态让给其他线程使用,这个过程就属于一次上下文切换。
Linux在上下文切换上的优势
Linux 相比与其他操作系统(包括其他类 Unix 系统)有很多的优点,其中有一项就是,其上下文切换和模式切换的时间消耗非常少。
线程死锁
多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释
放。由于线程被无限期地阻塞,因此程序不可能正常终止。
- 产生死锁必须具备以下四个条件:
- 互斥条件:该资源任意一个时刻只由一个线程占用。
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
- 不剥夺条件:线程已获得的资源在末使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
为什么我们调用start() 方法时会执行run() 方法,为什么我们不能直接调用?
new 一个 Thread,线程进入了新建状态;调用start() ,会启动一个线程并使线程进入了就绪状态,当分配到时间片后就可以开始运行了。 start() 会执行线程的相应准备工作,然后执行run() 方法的内容,这是真正的多线程工作。直接执行 run() 方法,会把 run 方法当成一个 main线程下的普通方法去执行,并不会在某个线程中执行它,所以这并不是多线程⼯作。
总结: 调用 start 方法可启动线程并使线程进入就绪状态, run 只是 thread 的一个普通方法调用,还是在主线程中执行。
并发编程的三个重要特性
- 原子性 : 一个的操作或者多次操作,要么所有的操作全部都得到执行并且不会收到任何因素的干扰而中断,要么所有的操作都执行,要么都不执行。 synchronized 可以保证代码的原子性。
- 可见性 :当一个变量对共享变量进行了修改,那么另外的线程都是立即可以看到修改后的最新值。 volatile 关键字可以保证共享变量的可见性。
- 有序性 :代码在执行的过程中的先后顺序,Java 在编译器以及运行期间的优化,代码的执行顺序未必就是编写代码时候的顺序。 volatile 关键字可以禁止指令进行重排序优化。
线程池
线程池、数据库连接池、Http 连接池等等都是对这个思想的应
应。池化技术的思想主要是为了减少每次获取资源的消耗,提高对资源的利用率。
线程池的好处:
降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。