最后
针对最近很多人都在面试,我这边也整理了相当多的面试专题资料,也有其他大厂的面经。希望可以帮助到大家。
上述的面试题答案都整理成文档笔记。 也还整理了一些面试资料&最新2021收集的一些大厂的面试真题(都整理成文档,小部分截图)
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
@Product(name = “B”)
public class ProductB implements IProduct {
@Override
public void doSomeThing() {
System.out.println(“ProductB…doSomeThing”);
}
}
@Product
public class ProductC implements IProduct {
@Override
public void doSomeThing() {
System.out.println(“ProductC…doSomeThing”);
}
}
public class SimpleFactory {
public static IProduct getInstance(String name) {
IProduct product = INSTANCE_MAP.get(name);
if (product == null) {
throw new UnsupportedOperationException(“not match product, name=” + name);
}
return product;
}
private static final Map<String, IProduct> INSTANCE_MAP = new HashMap<>();
static {
init();
}
private static void init() {
System.out.println(“init…”);
Set<Class<?>> classSet = ClassUtil.scanPackageByAnnotation(“com.stefan.designPattern.factory”, Product.class);
for (Class<?> clazz : classSet) {
boolean isProduct = false;
in : for (Class<?> i : clazz.getInterfaces()) {
if (IProduct.class.equals(i)) {
isProduct = true;
break in;
}
}
if (!isProduct) {
continue;
}
Product product = clazz.getAnnotation(Product.class);
String name = product.name();
// 虽然指定name default是“”,但是这里取的话会隐式new String()
// 所以必须用equals比较,不能用==
if (“”.equals(name)) {
name = clazz.getSimpleName();
}
try {
INSTANCE_MAP.put(name, (IProduct) clazz.newInstance());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
这里用到了hutool
的ClassUtil.scanPackageByAnnotation
扫描类的工具,方法使用很简单,但是过程复杂一些,包扫面首先会获取当前类加载器并调用getResources
获取指定目录下的所有类资源,如果是目录,扫描目录下的类文件或者jar文件,如果是jar包,则直接从jar包中获取类名(比较考验类加载机制和反射基本功)。
6、简单工厂在JDK中的体现
JDK中用到工厂模式的地方老多了,下面简单举几个例子:
(1)Calendar.getInstance()
,可以根据不同时区TimeZone
、地区Locale
获取不同Calendar
实例(不是单例),最终会调用到CalendarProvider
的getInstance
:
(2)DateFormat
的创建,最终会调用到DateFormatProvider
创建对象的方法:
(3)几个包装类型,如String
、Interger
、Long
、Double
等的valueOf
方法都是会创建对象或者从缓存中获取。
(4)线程工厂java.util.concurrent.ThreadFactory#newThread
。
(其他开源框架如Tomcat中也会大量用到简单工厂,暂时不再举例。)
7、简单工厂优缺点
(1)优点:简单工厂的优点就是简单,容易理解,还有就是解耦,隔离变化。
(2)缺点:简单工厂一般以静态方法为主,工厂类单一,难以扩展;当产品基数增多时,工厂类代码可能会比较臃肿,虽然可以通过缓存、反射、自定义注解等方式缓解if-else
,但是需要牺牲一些内存和性能,且有一定的使用场景和开发成本。
工厂方法,外国人起的破名字,和抽象工厂傻傻分不清楚,且看定义和类图:
工厂方法就是为了解决简单工厂难以扩展问题的,依然是生产同类产品,但是每个产品生产较为复杂且细节不尽相同,所以给每个产品分配一个专属工厂:
1、通用写法
产品类和简单工厂一样,假装创建对象的过程复杂且不同;每个产品一个工厂,为了面向接口编程,可扩展性,所以建立一个工厂接口,所有工厂实现这个接口:
public interface IProduct {
void doSomeThing();
}
public class ProductA implements IProduct {
@Override
public void doSomeThing() {
System.out.println(“ProductA…doSomeThing”);
}
}
public class ProductB implements IProduct {
@Override
public void doSomeThing() {
System.out.println(“ProductB…doSomeThing”);
}
}
public class ProductC implements IProduct {
@Override
public void doSomeThing() {
System.out.println(“ProductC…doSomeThing”);
}
}
public interface IFactory {
IProduct create();
}
public class AFactory implements IFactory {
@Override
public IProduct create() {
return new ProductA();
}
}
public class BFactory implements IFactory {
@Override
public IProduct create() {
return new ProductB();
}
}
public class CFactory implements IFactory {
@Override
public IProduct create() {
return new ProductC();
}
}
public class Test {
public static void main(String[] args) {
IFactory factoryA = new AFactory();
IProduct productA = factoryA.create();
productA.doSomeThing();
IFactory factoryB = new BFactory();
IProduct productB = factoryB.create();
productB.doSomeThing();
}
}
很明显类增加了不少,也使得整体变复杂了,再看客户端怎么使用工厂生产产品,需要先实例化对应工厂类,然后创建对应产品。如果客户端不知道使用哪个工厂那还得if-else
选择工厂,咋还回去了,越来越复杂了。。。那就再给工厂加个简单工厂,把if-else
移到一个简单工厂里,生产不同的工厂方法,真的是复杂了,达咩!注意工厂方法的使用场景!
工厂方法的出现是因为每个对象的创建过程较复杂且有自己的创建细节,所以才给每个产品对象分配了一个工厂;且客户端调用、是有不同的场景对应不同的工厂方法,不需要再在工厂方法上面加简单工厂,这是最佳适配。
如果后期新增产品,同时新增工厂,不需要改到旧代码。
2、工厂方法在slf4j+logback中的体现
在开源日志框架门面slf4j
中提供了Logger
接口和ILoggerFactory
供第三方框架扩展(如logback
)。不同的工厂负责生产不同的日志框架,基本类图如下:
可以看出slf4j+logback
既使用了工厂方法,又使用了简单工厂去管理工厂方法。
3、工厂方法优缺点
(1)优点:易扩展,新增产品同时新增工厂,隔离变化,隔离复杂,理想状态完全符合开闭原则、迪米特原则(最少知道原则)、依赖倒置原则。
(2)缺点:类太多了,增加了代码复杂度;更加抽象,不易理解;依然只能生产一种产品,种类单一。
抽象工厂,可以生产不同类型的产品(实现于不同抽象接口),这些产品之间有一定的联系,可以归纳为一个族系的产品。
抽象工厂因为可以生产多种产品,所以不会像工厂方法那样有那么多的工厂类。不过也是因为产品的族系越来越复杂,才使得抽象工厂有了用武之地。
如有两条产品系列1和2,每个系列有一个抽象工厂,每个工厂可以生产A和B。
1、通用写法
public interface IProductA {
void doSomeThing();
}
public interface IProductB {
void doSomeThing();
}
public class ProductA1 implements IProductA {
public void doSomeThing() {
System.out.println(“ProductA1…doSomeThing”);
}
}
public class ProductA2 implements IProductA {
public void doSomeThing() {
System.out.println(“ProductA2…doSomeThing”);
}
}
public class ProductB1 implements IProductB {
public void doSomeThing() {
System.out.println(“ProductB1…doSomeThing”);
}
}
public class ProductB2 implements IProductB {
public void doSomeThing() {
System.out.println(“ProductB2…doSomeThing”);
}
}
public interface IFactory {
IProductA createProductA();
IProductB createProductB();
}
public class Product1Factory implements IFactory {
@Override
public IProductA createProductA() {
return new ProductA1();
}
@Override
public IProductB createProductB() {
return new ProductB1();
}
}
public class Product2Factory implements IFactory {
@Override
public IProductA createProductA() {
return new ProductA2();
}
@Override
public IProductB createProductB() {
return new ProductB2();
}
}
public class Test {
public static void main(String[] args) {
IFactory factory1 = new Product1Factory();
IProductA productA1 = factory1.createProductA();
IProductB productB1 = factory1.createProductB();
}
}
类好多啊,好复杂,好抽象。
2、抽象工厂在JMX中的体现
抽象工厂的例子挺不好找的,有些文章说Spring IOC
里BeanFactory
及其子类是抽象工厂的体现,个人觉得不是那么典型,IOC
是一个bean
容器,是一个超级工厂,万物皆可对象,万物皆可生产。
还看到网上有说java.sql.Connection
体现了抽象工厂,真的是生靠啊,明明生成的对象都是实现了Statement
接口,算是同类产品,而且Connection
没有抽象意义上工厂的概念。
public interface Connection extends Wrapper, AutoCloseable {
Statement createStatement() throws SQLException;
PreparedStatement prepareStatement(String sql)throws SQLException;
CallableStatement prepareCall(String sql) throws SQLException;
… … 还有其他方法,如commit、rollback…
}
寻寻觅觅,终于是找到一个比较典型的代表JMX
:
MBeanIntrospector
提供了多个创建对象的抽象方法,MXBeanIntrospector
和StandardMBeanIntrospector
分别实现,创建不同的对象。
abstract class MBeanIntrospector {
abstract PerInterfaceMap getPerInterfaceMap();
abstract MBeanInfoMap getMBeanInfoMap();
abstract MBeanAnalyzer getAnalyzer(Class<?> mbeanInterface)
throws NotCompliantMBeanException;
abstract MBeanOperationInfo getMBeanOperationInfo(String operationName,
M operation);
abstract Descriptor getBasicMBeanDescriptor();
abstract Descriptor getMBeanDescriptor(Class<?> resourceClass);
}
class MXBeanIntrospector extends MBeanIntrospector {
private static final MXBeanIntrospector instance = new MXBeanIntrospector();
static MXBeanIntrospector getInstance() {
return instance;
}
@Override
Descriptor getBasicMBeanDescriptor() {
return new ImmutableDescriptor(“mxbean=true”,
“immutableInfo=true”);
}
// 其他方法省略
}
class StandardMBeanIntrospector extends MBeanIntrospector {
private static final StandardMBeanIntrospector instance =
new StandardMBeanIntrospector();
static StandardMBeanIntrospector getInstance() {
return instance;
}
@Override
结尾
这不止是一份面试清单,更是一种”被期望的责任“,因为有无数个待面试者,希望从这篇文章中,找出通往期望公司的”钥匙“,所以上面每道选题都是结合我自身的经验于千万个面试题中经过艰辛的两周,一个题一个题筛选出来再次对好答案和格式做出来的,面试的答案也是再三斟酌,深怕误人子弟是小,影响他人仕途才是大过,也希望您能把这篇文章分享给更多的朋友,让他帮助更多的人,帮助他人,快乐自己,最后,感谢您的阅读。
由于细节内容实在太多啦,在这里我花了两周的时间把这些答案整理成一份文档了,在这里只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!
class StandardMBeanIntrospector extends MBeanIntrospector {
private static final StandardMBeanIntrospector instance =
new StandardMBeanIntrospector();
static StandardMBeanIntrospector getInstance() {
return instance;
}
@Override
结尾
[外链图片转存中…(img-mUChnYtd-1714894630449)]
这不止是一份面试清单,更是一种”被期望的责任“,因为有无数个待面试者,希望从这篇文章中,找出通往期望公司的”钥匙“,所以上面每道选题都是结合我自身的经验于千万个面试题中经过艰辛的两周,一个题一个题筛选出来再次对好答案和格式做出来的,面试的答案也是再三斟酌,深怕误人子弟是小,影响他人仕途才是大过,也希望您能把这篇文章分享给更多的朋友,让他帮助更多的人,帮助他人,快乐自己,最后,感谢您的阅读。
由于细节内容实在太多啦,在这里我花了两周的时间把这些答案整理成一份文档了,在这里只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!