Offer来了:Java面试核心知识点精讲(原理篇)
第1章 JVM
- 一套字节码指令集、一组程序寄存器、一个虚拟机栈、一个虚拟机堆、一个方法区和一个垃圾回收器。
- 在一个Java进程开始运行后,虚拟机就开始实例化了,有多个进程启动就会实例化多个虚拟机实例。进程退出或者关闭,则虚拟机实例消亡,在多个虚拟机实例之间不能共享数据。
1.2 多线程
- JVM会调用操作系统的接口创建一个与之对应的原生线程
- 编译器线程:编译器线程在运行时将字节码动态编译成本地平台机器码,是JVM跨平台的具体实现。
1.3 JVM的内存区域
- JVM的内存区域分为线程私有区域(程序计数器、虚拟机栈、本地方法区)、线程共享区域(堆、方法区)和直接内存
- Java进程可以通过堆外内存技术避免在Java堆和Native堆中来回复制数据带来的资源占用和性能消耗,因此堆外内存在高并发应用场景下被广泛使用(Netty、Flink、HBase、Hadoop都有用到堆外内存)。
- 程序计数器是一块很小的内存空间,用于存储当前运行的线程所执行的字节码的行号指示器
- 虚拟机栈是描述Java方法的执行过程的内存模型,它在当前栈帧(Stack Frame)中存储了局部变量表、操作数栈、动态链接、方法出口等信息。
- 栈帧用来记录方法的执行过程,在方法被执行时虚拟机会为其创建一个与之对应的栈帧,方法的执行和返回对应栈帧在虚拟机栈中的入栈和出栈。
- 而在线程内部,每个方法的执行和返回都对应一个栈帧的入栈和出栈,每个运行中的线程当前只有一个栈帧处于活动状态。
- 本地方法栈为Native方法服务
- 现代JVM采用分代收集算法,因此Java堆从GC(Garbage Collection,垃圾回收)的角度还可以细分为:新生代、老年代和永久代。
- 方法区也被称为永久代,用于存储常量、静态变量、类信息、即时编译器编译后的机器码、运行时常量池等数据
- 永久带的内存回收主要针对常量池的回收和类的卸载,因此可回收的对象很少。
- 在即时编译后,代码的内容将在执行阶段(类加载完成后)被保存在方法区的运行时常量池中
1.4 JVM的运行时内存
- 其中新生代默认占1/3堆空间,老年代默认占2/3堆空间,永久代占非常少的堆空间
- Java新创建的对象首先会被存放在Eden区,如果新创建的对象属于大对象,则直接将其分配到老年代
- 新生代的GC过程叫作MinorGC,采用复制算法实现
- MajorGC采用标记清除算法,该算法首先会扫描所有对象并标记存活的对象,然后回收未被标记的对象,并释放内存空间。
- 元数据区并没有使用虚拟机的内存,而是直接使用操作系统的本地内存。因此,元空间的大小不受JVM内存的限制,只和操作系统的内存有关。
1.5 垃圾回收与算法
- Java采用引用计数法和可达性分析来确定对象是否应该被回收
- 引用计数法容易产生循环引用问题。循环引用指两个对象相互引用,导致它们的引用一直存在,而不能被回收
- 不可达对象要经过至少两次标记才能判定其是否可以被回收,如果在两次标记后该对象仍然是不可达的,则将被垃圾收集器回收。
- 复制算法是为了解决标记清除算法内存碎片化的问题而设计的
- 因此,针对不同的对象类型,JVM采用了不同的垃圾回收算法,该算法被称为分代收集算法。
- 大部分JVM在新生代都采用了复制算法
- 老年代主要存放生命周期较长的对象和大对象,因而每次只有少量非存活的对象被回收,因而在老年代采用标记清除算法。
- 若Servivor区的对象经过一次GC后仍然存活,则其年龄加1。在默认情况下,对象在年龄达到15时,将被移到老年代。
1.6 Java中的4种引用类型
- Java中的引用类型有4种,分别为强引用、软引用、弱引用和虚引用
- 有强引用的对象一定为可达性状态,所以不会被垃圾回收机制回收。因此,强引用是造成Java内存泄漏(Memory Link)的主要原因。
1.7 分代收集算法和分区收集算法
- 老年代主要存放长生命周期的对象和大对象,可回收的对象一般较少,因此JVM采用标记整理算法进行垃圾回收,直接释放死亡状态的对象所占用的内存空间即可。
- 分区收集算法可以根据系统可接受的停顿时间,每次都快速回收若干个小区域的内存,以缩短垃圾回收时系统停顿的时间,最后以多次并行累加的方式逐步完成整个内存区域的垃圾回收
1.8 垃圾收集器
- [插图]
- Serial垃圾收集器是Java虚拟机运行在Client模式下的新生代的默认垃圾收集器。
- ParNew垃圾收集器在垃圾收集过程中会暂停所有其他工作线程,是Java虚拟机运行在Server模式下的新生代的默认垃圾收集器。
- Serial Old垃圾收集器是JVM运行在Client模式下的老年代的默认垃圾收集器。
- 其主要目的是达到最短的垃圾回收停顿时间
- 基于标记整理算法,不产生内存碎片。◎ 可以精确地控制停顿时间,在不牺牲吞吐量的前提下实现短停顿垃圾回收。
1.9 Java网络编程模型
- 阻塞I/O模型的工作流程为:在用户线程发出I/O请求之后,内核会检查数据是否就绪,此时用户线程一直阻塞等待内存数据就绪;在内存数据就绪后,内核将数据复制到用户线程中,并返回I/O执行结果到用户线程,此时用户线程将解除阻塞状态并开始处理数据
- 非阻塞I/O模型指用户线程在发起一个I/O操作后,无须阻塞便可以马上得到内核返回的一个结果
- 在多路复用I/O模型中会有一个被称为Selector的线程不断轮询多个Socket的状态,只有在Socket有读写事件时,才会通知用户线程进行I/O读写操作。
- 多路复用I/O模型在连接数众多且消息体不大的情况下有很大的优势
- 对于多路复用I/O模型来说,在事件响应体(消息体)很大时,Selector线程就会成为性能瓶颈,导致后续的事件迟迟得不到处理,影响下一轮的事件轮询
- Java NIO的实现主要涉及三大核心内容:Selector(选择器)、Channel(通道)和Buffer(缓冲区)
- I/O是面向流的,NIO是面向缓冲区的
- 传统I/O的流操作是阻塞模式的,NIO的流操作是非阻塞模式的
- Buffer实际上是一个容器,其内部通过一个连续的字节数组存储I/O上的数据
- ByteBuffer、IntBuffer、CharBuffer、LongBuffer、DoubleBuffer、FloatBuffer、ShortBuffer。
- Selector用于检测在多个注册的Channel上是否有I/O事件发生,并对检测到的I/O事件进行相应的响应和处理
1.10 JVM的类加载机制
- JVM的类加载分为5个阶段:加载、验证、准备、解析、初始化。
- 类加载过程主要包含将Class文件读取到运行时区域的方法区内,在堆中创建java.lang.Class对象,并封装类在方法区的数据结构的过程
- 确保Class文件符合当前虚拟机的要求
- 主要工作是在方法区中为类变量分配内存空间并设置类中变量的初始值
- 静态变量value在准备阶段的初始值是0,将value设置为1000的动作是在对象初始化时完成的
- JVM会将常量池中的符号引用替换为直接引用
- 在子类引用父类的静态字段时,不会触发子类的初始化,只会触发父类的初始化
- 启动类加载器、扩展类加载器和应用程序类加载器
- JVM通过双亲委派机制对类进行加载。双亲委派机制指一个类在收到类加载请求后不会尝试自己加载这个类,而是把该类加载请求向上委派给其父类去完成,其父类在接收到该类加载请求后又会将其委派给自己的父类,以此类推,这样所有的类加载请求都被向上委派到启动类加载器中。
- 双亲委派机制的核心是保障类的唯一性和安全性
- OSGI(Open Service Gateway Initiative)是Java动态化模块化系统的一系列规范,旨在为实现Java程序的模块化编程提供基础条件
2.1 集合
- List、Queue、Set和Map
- ArrayList不适合随机插入和删除的操作,更适合随机查找和遍历的操作。
- 2.Vector:基于数组实现,增删慢,查询快,线程安全
- LinkedList还提供了在List接口中未定义的方法,用于操作链表头部和尾部的元素,因此有时可以被当作堆栈、队列或双向队列使用。
- 对象的相等性在本质上是对象的HashCode值相同,Java依据对象的内存地址计算出对象的HashCode值
- 1.HashSet:HashTable实现,无序
- 2.TreeSet:二叉树实现
- Integer和String等基础对象类型可以直接根据TreeSet的默认排序进行存储,而自定义的数据类型必须实现Comparable接口,并且覆写其中的compareTo函数才可以按照预定义的顺序存储
- 3.LinkHashSet:HashTable实现数据存储,双向链表记录顺序
- HashMap的key和value允许为null。
- 如果需要满足线程安全的条件,则可以用Collections的synchronizedMap方法使HashMap具有线程安全的能力,或者使用ConcurrentHashMap。
- capacity:当前数组的容量,默认为16,可以扩容,扩容后数组的大小为当前的两倍,因此该值始终为2n。
- 为了减少链表遍历的开销,Java 8对HashMap进行了优化,将数据结构修改为数组+链表或红黑树。在链表中的元素超过8个以后,HashMap会将链表结构转换为红黑树结构以提高查询效率,因此其时间复杂度为O(log N)。
- 2.ConcurrentHashMap:分段锁实现,线程安全
- ConcurrentHashMap由多个Segment组成(Segment的数量也是锁的并发度),每个Segment均继承自ReentrantLock并单独加锁,所以每次进行加锁操作时锁住的都是一个Segment,这样只要保证每个Segment都是线程安全的,也就实现了全局的线程安全。
- HashTable是遗留类,很多映射的常用功能都与HashMap类似,不同的是它继承自Dictionary类,并且是线程安全的,同一时刻只有一个线程能写HashTable,并发性不如ConcurrentHashMap。
- TreeMap:基于二叉树数据结构
- LinkedHashMap:基于HashTable数据结构,使用链表保存插入顺序
2.2 异常分类及处理
- 出现Error通常是因为系统的内部错误或资源耗尽,Error不能被在运行过程中被动态处理。如果程序出现Error,则系统能做的工作也只能有记录错误的成因和安全终止
- CheckedException:指在编译阶段Java编译器会检查CheckedException异常并强制程序捕获和处理此类异常,即要求程序在可能出现异常的地方通过try catch语句块捕获并处理异常
- throws、throw、系统自动抛出异常
- 位置不同:throws作用在方法上,后面跟着的是异常的类;而throw作用在方法内,后面跟着的是异常的对象。
2.3 反射机制
- 动态语言指程序在运行时可以改变其结构的语言,比如新的属性或方法的添加、删除等结构上的变化
- 反射机制指在程序运行过程中,对任意一个类都能获取其所有属性和方法,并且对任意一个对象都能调用其任意一个方法。
- Java中的对象有两种类型:编译时类型和运行时类型。编译时类型指在声明对象时所采用的类型,运行时类型指为对象赋值时所采用的类型。
- 调用Class类中的forName静态方法以获取该类对应的Class对象,这是最安全、性能也最好的方法:
- 使用Class对象的newInstance方法创建该Class对象对应类的实例,这种方法要求该Class对象对应的类有默认的空构造器。
2.5 内部类
- 可分为静态内部类、成员内部类、局部内部类和匿名内部类这4种。
- 像这种和外部类关系密切且不依赖外部类实例的类,可以使用静态内部类实现。
- 成员内部类不能定义静态方法和变量(final修饰的除外)
- 定义在方法中的类叫作局部内部类
- 匿名内部类指通过继承一个父类或者实现一个接口的方式直接定义并使用的类。
2.6 泛型
- 泛型的本质是参数化类型,泛型提供了编译时类型的安全检测机制,该机制允许程序在编译时检测非法的类型,
- 而使用泛型的好处是在编译期就能够检查类型是否安全,同时所有强制性类型转换都是自动和隐式进行的,提高了代码的安全性和重用性。
- 泛型类指在定义类时在类上定义了泛型,以便类在使用时可以根据传入的不同参数类型实例化不同的对象。
- 在编码阶段采用泛型时加上的类型参数,会被编译器在编译时去掉,这个过程就被称为类型擦除
2.7 序列化
- 对象序列化保存的是对象的状态,即它的成员变量,因此类中的静态变量不会被序列化。
- 使用Transient关键字可以阻止该变量被序列化,在被反序列化后,transient变量的值被设为对应类型的初始值,
- transient修饰的属性和static修饰的静态属性不会被序列化。
3.1 Java线程的创建方式
- 常见的Java线程的4种创建方式分别为:继承Thread类、实现Runnable接口、通过ExecutorService和Callable
实现有返回值的线程、基于线程池,如图3-1所示。 - start方法是一个native方法,通过在操作系统上启动一个新线程,并最终执行run方法来启动一个线程
- 基于Java编程语言的规范,如果子类已经继承(extends)了一个类,就无法再直接继承Thread类,此时可以通过实现Runnable接口创建线程
- 有时,我们需要在主线程中开启多个线程并发执行一个任务,然后收集各个线程执行返回的结果并将最终结果汇总起来,这时就要用到Callable接口。
3.2 线程池的工作原理
- JVM先根据用户的参数创建一定数量的可运行的线程任务,并将其放入队列中,在线程创建后启动这些任务,如果线程数量超过了最大线程数量(用户设置的线程池大小),则超出数量的线程排队等候,在有任务执行完毕后,线程池调度器会发现有可用的线程,进而再次从队列中取出任务并执行。
- 在start方法中不断循环调用传递进来的Runnable对象,程序就会不断执行run方法中的代码
- Java中的线程池是通过Executor框架实现的,在该框架中用到了Executor、Executors、ExecutorService、ThreadPoolExecutor、Callable、Future、FutureTask这几个核心类
- JDK内置的拒绝策略有AbortPolicy、CallerRunsPolicy、DiscardOldestPolicy、DiscardPolicy这4种
- AbortPolicy直接抛出异常,阻止线程正常运行
- 如果被丢弃的线程任务未关闭,则执行该线程任务
- 移除线程队列中最早的一个线程任务,并尝试提交当前任务
3.3 5种常用的线程池
- newWorkStealingPool创建持有足够线程的线程池来达到快速运算的目的,在内部通过使用多个队列来减少各个线程调度产生的竞争
3.4 线程的生命周期
- 线程的生命周期分为新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)这5种状态
3.5 线程的基本方法
- 在调用wait方法后会释放对象的锁,因此wait方法一般被用于同步方法或同步代码块中。
- 调用yield方法会使当前线程让出(释放)CPU执行时间片,与其他线程一起重新竞争CPU时间片
- join方法用于等待其他线程终止,如果在当前线程中调用一个线程的join方法,则当前线程转为阻塞状态,等到另一个线程结束,当前线程再由阻塞状态转为就绪状态,等待获取CPU的使用权
- 垃圾回收线程就是一个经典的守护线程
- sleep方法与wait方法的区别
- 在调用wait方法时,线程会放弃对象锁,进入等待此对象的等待锁池,只有针对此对象调用notify方法后,该线程才能进入对象锁池准备获取对象锁,并进入运行状态。
3.6 Java中的锁
- Java中的锁主要用于保障多并发线程情况下数据的一致性
- 锁从乐观和悲观的角度可分为乐观锁和悲观锁,从获取资源的公平性角度可分为公平锁和非公平锁,从是否共享资源的角度可分为共享锁和独占锁,从锁的状态的角度可分为偏向锁、轻量级锁和重量级锁。
- 乐观锁采用乐观的思想处理数据,在每次读取数据时都认为别人不会修改该数据,所以不会上锁,但在更新时会判断在此期间别人有没有更新该数据,通常采用在写时先读出当前版本号然后加锁的方法。
- CAS是一种原子更新操作,在对数据操作之前首先会比较当前值跟传入的值是否一样,如果一样则更新,否则不执行更新操作,直接返回失败状态
- Java中的悲观锁大部分基于AQS(Abstract Queued Synchronized,抽象的队列同步器)架构实现
- 自旋锁认为:如果持有锁的线程能在很短的时间内释放锁资源,那么那些等待竞争锁的线程就不需要做内核态和用户态之间的切换进入阻塞、挂起状态,只需等一等(也叫作自旋),在等待持有锁的线程释放锁后即可立即获取锁,这样就避免了用户线程在内核状态的切换上导致的锁时间消耗。
- 适应性自旋锁的自旋时间不再是固定值,而是由上一次在同一个锁上的自旋时间及锁的拥有者的状态来决定的,可基本认为一个线程上下文切换的时间是就一个最佳时间
- synchronized属于独占式的悲观锁,同时属于可重入锁。
- Java中的每个对象都有个monitor对象,加锁就是在竞争monitor对象
- synchronized作用于成员变量和非静态方法时,锁住的是对象的实例,即this对象。
- synchronized作用于静态同步方法,锁住的是当前类的Class对象
- 在synchronized内部包括ContentionList、EntryList、WaitSet、OnDeck、Owner、! Owner这6个区域,每个区域的数据都代表锁的不同状态。
- synchronized在收到新的锁请求时首先自旋,如果通过自旋也没有获取锁资源,则将被放入锁竞争队列ContentionList中
- 在线程进入ContentionList之前,等待的线程会先尝试以自旋的方式获取锁,如果获取不到就进入ContentionList,该做法对于已经进入队列的线程是不公平的,因此synchronized是非公平锁。
- 锁可以从偏向锁升级到轻量级锁,再升级到重量级锁。这种升级过程叫作锁膨胀
- 可重入锁指该锁能够支持一个线程对同一个资源执行多次加锁操作
- ReentrantLock之所以被称为可重入锁,是因为ReentrantLock锁可以反复进入。即允许连续两次获得同一把锁,两次释放同一把锁。
- 公平锁指锁的分配和竞争机制是公平的,即遵循先到先得原则。非公平锁指JVM遵循随机、就近原则分配锁的机制。
- 二者的底层实现不一样:synchronized是同步阻塞,采用的是悲观并发策略;Lock是同步非阻塞,采用的是乐观并发策略。
- Semaphore是一种基于计数的信号量
- 可重入锁也叫作递归锁,指在同一线程中,在外层函数获取到该锁之后,内层的递归函数仍然可以继续获取该锁
- synchronized在内部基于监视器锁(Monitor)实现,监视器锁基于底层的操作系统的Mutex Lock实现,因此synchronized属于重量级锁
- 轻量级锁适用于线程交替执行同步代码块的情况(即互斥操作),如果同一时刻有多个线程访问同一个锁,则将会导致轻量级锁膨胀为重量级锁。
- 偏向锁用于在某个线程获取某个锁之后,消除这个线程锁重入的开销,看起来似乎是这个线程得到了该锁的偏向(偏袒)。
- 偏向锁的主要目的是在同一个线程多次获取某个锁的情况下尽量减少轻量级锁的执行路径
- 轻量级锁用于提高线程交替执行同步块时的性能
- 锁的状态总共有4种:无锁、偏向锁、轻量级锁和重量级锁
- ConcurrentHashMap在内部就是使用分段锁实现的。
- 减小锁粒度最典型的案例就是ConcurrentHashMap中的分段锁。
3.7 线程上下文切换
- 任务的状态保存及再加载就叫作线程的上下文切换。
- 指线程切换时CPU寄存器和程序计数器所保存的当前线程的信息。
3.9 Java并发关键字
- CountDownLatch基于线程计数器来实现并发访问控制,主要用于主线程等待其他子线程都执行完毕后执行相关操作
- CyclicBarrier(循环屏障)是一个同步工具,可以实现让一组线程等待至某个状态之后再全部同时执行
- volatile也用于确保将变量的更新操作通知到其他线程。
- 一种是保证该变量对所有线程可见,在一个线程修改了变量的值后,新的值对于其他线程是可以立即获取的
- volatile关键字可以严格保障变量的单次读、写操作的原子性,但并不能保证像i++这种操作的原子性,因为i++在本质上是读、写两次操作
3.15 ABA问题
- ABA问题指第1个线程从内存的V位置取出A,这时第2个线程也从内存中取出A,并将V位置的数据首先修改为B,接着又将V位置的数据修改为A,这时第1个线程在进行CAS操作时会发现在内存中仍然是A,然后第1个线程操作成功。
- 部分乐观锁是通过版本号(version)来解决ABA问题的,
3.16 什么是AQS
- AQS只是一个框架,只定义了一个接口,具体资源的获取、释放都交由自定义同步器去实现
4.1 栈及其Java实现
- 栈也叫作后进先出(FILO-First In Last Out)的线性表
4.8 位图
- 位图通常是用来判断某个数据存不存在的,常用于在Bloom Filter中判断数据是否存在,还可用于无重复整数的排序等,在大数据行业中使用广泛。
第6章 网络与负载均衡
- 物理层、数据链路层、网络层、传输层、会话层、表示层和应用层
- TCP/IP由4个层次组成:网络接口层、网络层、传输层和应用层
- TCP数据在传输之前会建立连接需要进行3次沟通,一般被称为“三次握手”,在数据传输完成断开连接的时候要进行4次沟通,一般被称为“四次挥手”。
- 因为TCP连接是全双工的(即数据可在两个方向上同时传递),所以在进行关闭时对每个方向都要单独进行关闭,这种单方向的关闭叫作半关闭
- 为了数据传输的安全,HTTPS在HTTP的基础上加入了SSL协议,SSL依靠证书来验证服务器的身份,并对浏览器和服务器之间的通信进行数据加密,以保障数据传输的安全性,其端口一般是443。
6.2 负载均衡
- 四层负载均衡基于IP和端口的方式实现网络的负载均衡
- 四层负载均衡只能针对IP地址和端口上的数据做统一的分发,而七层负载均衡能根据消息的内容做更加详细的有针对性的负载均衡
- 对响应速度的获取是通过负载均衡设备定时为每台服务都发出一个探测请求(例如Ping)实现的
7.1 数据库的基本概念及原则
- MyIASM的特点是执行读取操作的速度快,且占用的内存和存储资源较少
- MyIASM的缺点是更新数据慢且不支持事务处理,优点是查询速度快。
- InnoDB的底层存储结构为B+树,B+树的每个节点都对应InnoDB的一个Page, Page大小是固定的,一般被设为16KB。其中,非叶子节点只有键值,叶子节点包含完整的数据,如图7-1所示。
- TokuDB在线添加索引,不影响读写操作,有非常高的写入性能,主要适用于要求写入速度快、访问频率不高的数据或历史数据归档。
- 范式是具有最小冗余的表结构
- 如果每列都是不可再分的最小数据单元(也叫作最小的原子单元),则满足第一范式,第一范式的目标是确保每列的原子性
- 第二范式在第一范式的基础上,规定表中的非主键列不存在对主键的部分依赖,即第二范式要求每个表只描述一件事情
- 第三范式的定义为:满足第一范式和第二范式,并且表中的列不存在对非主键列的传递依赖
7.2 数据库的并发操作和锁
- 悲观锁又可分为排它锁(写锁)和共享锁(读锁)。
- 行级锁指对某行数据加锁,是一种排他锁,防止其他事务修改此行。
- 表级锁定分为表共享读锁(共享锁)与表独占写锁(排他锁)。
- Redis实现的分布式锁以Redis setnx命令为中心实现
- 将表按照功能模块、关系密切程度划分并部署到不同的库中
7.3 数据库分布式事务
- 二阶段提交的算法思路可以概括为:参与者将操作成败通知协调者,再由协调者根据所有参与者的反馈决定各参与者是提交操作还是中止操作。
- 两阶段型、补偿型、异步确保型、最大努力通知型。
第8章 分布式缓存的原理及应用
- 缓存分进程级缓存和分布式缓存
8.3 Redis的原理及应用
- String类型的值最大能存储512MB数据。
- Redis是基于请求/响应协议的TCP服务
- 在分布式环境下,Redis的性能瓶颈主要体现在网络延迟上。
- Redis的管道技术指在服务端未响应时,客户端可以继续向服务端发送请求,并最终一次性读取所有服务端的响应。
- Redis的事务操作分为开启事务、命令入队列、执行事务三个阶段。
- 主数据库主要用于执行写操作和数据同步,从数据库主要用于执行读操作缓解系统的读压力
8.4 分布式缓存设计的核心问题
- 缓存预热一般有系统启动加载、定时加载等方式。
- 缓存降级指由于访问量剧增导致服务出现问题(如响应时间慢或不响应)时,优先保障核心业务的运行,减少或关闭非核心业务对资源的使用
9.1 设计模式简介
- 设计模式有7个原则:单一职责原则、开闭原则、里氏代换原则、依赖倒转原则、接口隔离原则、合成/聚合复用原则、迪米特法则
9.2 工厂模式的概念及Java实现
- 工厂模式在接口中定义了创建对象的方法,而将具体的创建对象的过程在子类中实现,用户只需通过接口创建需要的对象即可,不用关注对象的具体创建过程
- 工厂模式的本质就是用工厂方法代替new操作创建一种实例化对象的方式,以提供一种方便地创建有同种类型接口的产品的复杂对象。
9.3 抽象工厂模式的概念及Java实现
- 抽象工厂模式(Abstract Factory Pattern)在工厂模式上添加了一个创建不同工厂的抽象接口(抽象类或接口实现),该接口可叫作超级工厂
9.4 单例模式的概念及Java实现
- 类的静态内部类在JVM中是唯一的,这很好地保障了单例对象的唯一性
9.9 代理模式的概念及Java实现
- 代理模式指为对象提供一种通过代理的方式来访问并控制该对象行为的方法。
9.15 模板方法模式的概念及Java实现
- 模板方法(Template Method)模式定义了一个算法框架,并通过继承的方式将算法的实现延迟到子类中,使得子类可以在不改变算法框架及其流程的前提下重新定义该算法在某些特定环节的实现,是一种类行为型模式。
9.17 迭代器模式的概念及Java实现
- 迭代器(Iterator)模式提供了顺序访问集合对象中的各种元素,而不暴露该对象内部结构的方法。