总结
无论是哪家公司,都很重视高并发高可用的技术,重视基础,重视JVM。面试是一个双向选择的过程,不要抱着畏惧的心态去面试,不利于自己的发挥。同时看中的应该不止薪资,还要看你是不是真的喜欢这家公司,是不是能真的得到锻炼。其实我写了这么多,只是我自己的总结,并不一定适用于所有人,相信经过一些面试,大家都会有这些感触。
最后我整理了一些面试真题资料,技术知识点剖析教程,还有和广大同仁一起交流学习共同进步,还有一些职业经验的分享。
private Singleton4() {
}
private static Singleton4 instance;
public static Singleton4 getInstance() {
if (instance == null) {
synchronized (this) {
instance = new Singleton4();
}
}
return instance;
}
}
不推荐使用,性能差
5、懒汉式(线程安全,同步方法)
//懒汉式(线程安全,同步方法)
public class Singleton5 {
private Singleton5() { }
private static Singleton5 instance;
public static synchronized Singleton5 getInstance() {
if (instance == null) {
instance = new Singleton5();
}
return instance;
}
}
不推荐使用,性能差
6、双重检查
//双重检查
public class Singleton6 {
private Singleton6() {
}
private static volatile Singleton6 instance;
public Singleton6 getInstance() {
if (instance == null) {
synchronized (this) {
if (instance == null) {
instance = new Singleton6();
}
}
}
return instance;
}
}
线程安全,延迟加载
,效率较高
7、静态内部类
//静态内部类
public class Singleton7 {
private Singleton7(){}
private static class SingletonInstance{
private static final Singleton7 INSTANCE = new Singleton7();
}
public Singleton7 getInstance(){
return SingletonInstance.INSTANCE;
}
}
优点:
- 避免了线程不安全,利用静态内部类特点实现延迟加载,效率高
8、枚举
//枚举类
public enum Singleton8 {
INSTANCE;
}
推荐使用
1、简单(静态)工厂模式
- 定义了一个
创建对象的类
,由这个类来封装实例化对象的行为
使用场景:
当我们会用到大量
的创建某种、某类或者某批对象 时,就会使用到工厂模式.
2、工厂方法模式
定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类
。
3、抽象工厂模式
-
定义了一个 interface 用于创建相关或有依赖关系的对象簇,而无需指明具体的类
-
可以将
简单工厂模式
和工厂方法模式
进行整合
。 -
将工厂抽象成两层,AbsFactory(*抽象工厂*) 和 具体实现的工厂子类。程序员可以根据创建对象类型使用对应的工厂子类。这样将单个的简单工厂类变成了
工厂簇
,更利于代码的维护和扩展。
创建对象实例时,不要直接 new 类, 而是把这个 new 类的动作放在一个工厂的方法中,并返回。有的书上说, 变量不要直接持有具体类的引用
孙大圣拔出猴毛, 变出其它孙大圣
- 用原型实例指定创建对象的种类,并且通过
拷贝克隆
这些原型,创建新的对象
代码演示:
抽象建造的过程
,让具体的实现类去实现各自的建造过程
- Product(产品角色):
一个具体的产品对象。
- Builder(抽象建造者):
创建一个 Product 对象的各个部件指定的 接口**/**抽象类。
- ConcreteBuilder(具体建造者):
实现接口,构建和装配各个部件。
- Director(指挥者):
构建一个使用 Builder 接口的对象。它主要是用于创建一个复杂的对象。它主要有两个作用,一是:隔离了客户与对象的生产过程,二是:负责控制产品对象的生产过程。
- 将一个类的接口转换成另一种接口.
让原本接口不兼容的类可以兼容
- 类适配器模式
通过类继承
- 对象适配器模式
通过成员变量聚合关系实现
- 接口适配器模式
通过一个中间抽象类来空实现,然后子类之间继承中间抽象类,对自己需要实现的方法实现
- 类适配器:以类给到,在 Adapter 里,就是将 src 当做类,
继承
- 对象适配器:以对象给到,在 Adapter 里,将 src 作为一个对象,
持有(组合、聚合)
- 接口适配器:以接口给到,在 Adapter 里,将 src 作为一个接口,
实现
- 将
实现与抽象放在两个不同的类层次中
,使两个层次可以独立改变。
//品牌接口
public interface Brand {
void open();
void close();
void call();
}
桥接类聚合了品牌接口类,然后在构造器中传入具体的品牌实现类,在具体的方法做品牌里面的方法调用
实现了抽象和实现部分的分离,从而极大的提供了系统的灵活性,让抽象部分和实现部分独立开来,这有助于系统进行分层设计,从而产生更好的结构化系统
-
套娃模式,直接new放构造器里面套把抽象类聚合到它的子类里 该子类(装饰者)构造抽象类的实现(被装饰者)
-
动态的
将新功能附加到对象上
。在对象功能扩展方面,它比继承更有弹性
,装饰者模式也体现了开闭原则**(ocp**)
代码实现:
-
它创建了对象组的树形结构,将对象组合成树状结构以表示
“整体-部分”的层次关系
-
通过
组织结构管理
,下面的例子采用了 集合管理
代码实现:
public abstract class OrganizationComponent {
private String name; // 名 字
private String des; // 说 明
protected void add(OrganizationComponent organizationComponent) {
//默认实现
throw new UnsupportedOperationException();
}
protected void remove(OrganizationComponent organizationComponent) {
//默认实现
throw new UnsupportedOperationException();
}
//构造器
public OrganizationComponent(String name, String des) {
super();
this.name = name; this.des = des;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
//方法 print, 做成抽象的, 子类都需要实现
protected abstract void print();
}
public class Client {
public static void main(String[] args) {
//从大到小创建对象 学校
OrganizationComponent university = new University(“清华大学”, " 中国顶级大学 ");
//创建 学院
OrganizationComponent computerCollege = new College(" 计 算 机 学 院 ", " 计 算 机 学 院 "); OrganizationComponent infoEngineercollege = new College(“信息工程学院”, " 信息工程学院 ");
//创建各个学院下面的系(专业)
computerCollege.add(new Department(“软件工程”, " 软件工程不错 "));
computerCollege.add(new Department(“网络工程”, " 网络工程不错 "));
computerCollege.add(new Department(“计算机科学与技术”, " 计算机科学与技术是老牌的专业 "));
infoEngineercollege.add(new Department(“通信工程”, " 通信工程不好学 "));
infoEngineercollege.add(new Department(“信息工程”, " 信息工程好学 "));
//将学院加入到 学校
university.add(computerCollege);
university.add(infoEngineercollege);
//university.print();
infoEngineercollege.print();
}
}
需要遍历组织机构,或者处理的对象具有树形结构时, 非常适合使用组合模式.
要求较高的抽象性,
如果节点和叶子有很多差异性的话,比如很多方法和属性都不一样,不适合使用组合模式
-
外观类就是
总控开关
,去管理聚合的所有类 -
解决多个复杂接口带来的使用困难,起到简化用户操作的作用
代码:
- 外观类
集合了需要使用的类,和方法,用户只需要调用这个类的某个方法即可
public class HomeTheaterFacade {
//定义各个子系统对象,聚合关系
private TheaterLight theaterLight;
private Popcorn popcorn;
private Stereo stereo;
private Projector projector;
private Screen screen;
private DVDPlayer dVDPlayer;
//构造器
public HomeTheaterFacade() {
super();
this.theaterLight = TheaterLight.getInstance();
this.popcorn = Popcorn.getInstance();
this.stereo = Stereo.getInstance();
this.projector = Projector.getInstance();
this.screen = Screen.getInstance();
this.dVDPlayer = DVDPlayer.getInstanc();
}
//操作分成 4 步
public void ready() {
popcorn.on();
popcorn.pop();
screen.down();
projector.on();
stereo.on();
dVDPlayer.on();
theaterLight.dim();
}
public void play() {
dVDPlayer.play();
}
public void pause() {
dVDPlayer.pause();
}
public void end() {
popcorn.off();
theaterLight.bright();
screen.up();
projector.off();
stereo.off();
dVDPlayer.off();
}
}
外观模式
对外屏蔽了子系统的细节
,因此外观模式降低了客户端对子系统使用的复杂性
在维护一个遗留的大型系统时,可能这个系统已经变得非常难以维护和扩展,此时可以考虑为新系统开发一个Facade 类,来提供遗留系统的比较清晰简单的接口,让新系统与 Facade 类交互,
提高复用性
-
需要的
网站结构相似度很高
,而且都不是高访问量网站,如果分成多个虚拟空间来处理,相当于一个相同网站的实例对象很多,造成服务器的资源浪费
-
运用
共享技术
有效地支持大量细粒度的对象 -
避免重新创建,如果没有我们需要的,则创建一个
内部状态 指 :对象共享出来的信息,存储在享元对象内部且不会随环境的改变而改变
外部状态 指 :对象得以依赖的一个标记,是随环境改变而改变的、不可共享的状态
。
代码:
// 网站工厂类,根据需要返回压一个网站
public class WebSiteFactory {
//集合, 充当池的作用
private HashMap<String, ConcreteWebSite> pool = new HashMap<>();
//根据网站的类型,返回一个网站, 如果没有就创建一个网站,并放入到池中,并返回
public WebSite getWebSiteCategory(String type) {
if(!pool.containsKey(type)) {
//就创建一个网站,并放入到池中
pool.put(type, new ConcreteWebSite(type));
}
return (WebSite)pool.get(type);
}
//获取网站分类的总数 (池中有多少个网站类型)
public int getWebSiteCount() {
return pool.size();
}
}
public class Client {
public static void main(String[] args) {
// 创建一个工厂类
WebSiteFactory factory = new WebSiteFactory();
// 客户要一个以新闻形式发布的网站
WebSite webSite1 = factory.getWebSiteCategory(“新闻”);
webSite1.use(new User(“tom”));
// 客户要一个以博客形式发布的网站
WebSite webSite2 = factory.getWebSiteCategory(“博客”);
webSite2.use(new User(“jack”));
// 客户要一个以博客形式发布的网站
WebSite webSite3 = factory.getWebSiteCategory(“博客”);
webSite3.use(new User(“smith”));
// 客户要一个以博客形式发布的网站
WebSite webSite4 = factory.getWebSiteCategory(“博客”);
webSite4.use(new User(“阿昌”));
System.out.println(“网站的分类共=” + factory.getWebSiteCount());
}
}
//具体网站
public class ConcreteWebSite extends WebSite {
//共享的部分,内部状态
private String type = “”; //网站发布的形式(类型)
//构造器
public ConcreteWebSite(String type) {
this.type = type;
}
@Override
public void use(User user) {
System.out.println(“网站的发布形式为:” + type + " 在使用中 … 使用者是" + user.getName());
}
}
@Data
public class User {
private String name;
public User(String name) {
super();
this.name = name;
}
}
public abstract class WebSite {
public abstract void use(User user);//抽象方法
}
享元模式
大大减少了对象的创建,降低了程序内存的占用,提高效率
享元模式提高了系统的复杂度。需要分离出内部状态和外部状态,而外部状态具有固化特性,不应该随着内部状态的改变而改变,这是我们使用享元模式需要注意的地方.
使用享元模式时,注意划分
内部状态和外部状态
,并且需要有一个工厂类加以控制。
通过替身去调用传入的被代理对象的方法,而不是直接去调用被代理对象的方法
- 为一个对象
提供一个替身
,以控制对这个对象的访问。即通过代理对象访问目标对象.这样做的好处是: 可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
1、静态代理
- 静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类
- 代码实现
public class Client {
public static void main(String[] args) {
//创建目标对象(被代理对象)
TeacherDao teacherDao = new TeacherDao();
//创建代理对象, 同时将被代理对象传递给代理对象
TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(teacherDao);
//通过代理对象,调用到被代理对象的方法
//即:执行的是代理对象的方法,代理对象再去调用目标对象的方法
teacherDaoProxy.teach();
}
}
//接口
public interface ITeacherDao {
void teach(); // 授课的方法
}
public class TeacherDao implements ITeacherDao {
@Override
public void teach() {
System.out.println(" 老师授课中 。。。。。");
}
}
//代理对象,静态代理
public class TeacherDaoProxy implements ITeacherDao{
private ITeacherDao target; //被代理对象,聚合关系
//构造器
public TeacherDaoProxy(ITeacherDao target) {
this.target = target;
}
@Override
public void teach() {
System.out.println("开始代理 完成某些操作。。。。。 ");//方法
target.teach();
System.out.println(“提交。。。。。”);//方法
}
}
代理对象与目标对象要实现相同的接口,然后通过调用相同的方法来调用目标对象的方法
优点:
- 在
不修改目标对象的功能
前提下, 能通过代理对象对目标功能扩展
缺点:
-
因为代理对象需要与目标对象
实现一样的接口
,所以会有很多代理类
-
一旦接口增加方法,目标对象与代理对象都要维护
2、动态代理
-
代理对象,
不需要实现接口
,但是目标对象要实现接口,否则不能用动态代理 -
代理对象的生成,是利用 JDK 的 API,动态的在内存中构建代理对象
public class Client {
public static void main(String[] args) {
//创建目标对象,被代理类
ITeacherDao target = new TeacherDao();
//给目标对象,创建代理对象, 可以转成 ITeacherDao,获取代理类
ITeacherDao proxyInstance = (ITeacherDao)new ProxyFactory(target).getProxyInstance();
// proxyInstance=class com.sun.proxy.$Proxy0 内存中动态生成了代理对象
System.out.println(“proxyInstance=” + proxyInstance.getClass());
//通过代理对象,调用目标对象的方法
//proxyInstance.teach();
proxyInstance.sayHello(" tom ");
}
}
//接口
public interface ITeacherDao {
void teach(); // 授课方法
void sayHello(String name);
}
public class ProxyFactory {
//维护一个目标对象 , Object
private Object target;
//构造器 , 对 target 进行初始化
public ProxyFactory(Object target) {
this.target = target;
}
//给目标对象 生成一个代理对象
public Object getProxyInstance() {
//说明
/*
- public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces, InvocationHandler h)
//1. ClassLoader loader : 指定当前目标对象使用的类加载器, 获取加载器的方法固定
//2. Class<?>[] interfaces: 目标对象实现的接口类型,使用泛型方法确认类型
//3. InvocationHandler h : 事情处理,执行目标对象的方法时,会触发事情处理器方法, 会把当前执行
的目标对象方法作为参数传入
*/
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(“JDK 代理开始~~”);
//反射机制调用目标对象的方法
Object returnVal = method.invoke(target, args);
System.out.println(“JDK 代理提交”);
return returnVal;
}
});
}
}
//被代理类
public class TeacherDao implements ITeacherDao {
@Override
public void teach() {
System.out.println(" 老师授课中… ");
}
@Override
public void sayHello(String name) {
System.out.println("hello " + name);
}
}
3、Cglib 代理
- 是在内存中构建一个子类对象从而实现对目标对象功能扩展, 有些书也将
Cglib 代理归属到动态代理。
public class Client {
public static void main(String[] args) {
//创建目标对象
TeacherDao target = new TeacherDao();
//获取到代理对象,并且将目标对象传递给代理对象
TeacherDao proxyInstance = (TeacherDao)new ProxyFactory(target).getProxyInstance();
//执行代理对象的方法,触发 intecept 方法,从而实现 对目标对象的调用
String res = proxyInstance.teach();
System.out.println(“res=” + res);
}
}
public class ProxyFactory implements MethodInterceptor {
//维护一个目标对象 ,聚合关系
private Object target;
//构造器,传入一个被代理的对象
public ProxyFactory(Object target) {
this.target = target;
}
//返回一个代理对象: 是 target 对象的被代理对象
public Object getProxyInstance() {
//1. 创建一个工具类
Enhancer enhancer = new Enhancer();
//2. 设置父类,传入被代理类的class类
enhancer.setSuperclass(target.getClass());
//3. 设置回调函数
enhancer.setCallback(this);
//4. 创建子类对象,即代理对象
return enhancer.create();
}
//重写intercept()方法,会调用目标对象的方法
@Override
public Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable {
System.out.println(“Cglib 代理模式 ~~ 开始”);
Object returnVal = method.invoke(target, args);
System.out.println(“Cglib 代理模式 ~~ 提交”);
return returnVal;
}
}
public class TeacherDao {
public String teach() {
System.out.println(" 老师授课中 , 我是 cglib 代理,不需要实现接口 "); return “hello”;
}
}
在 AOP 编程中如何选择代理模式:
-
目标对象需要实现接口,用 JDK 代理
-
目标对象不需要实现接口,用 Cglib 代理
-
在一个抽象类公开定义了执行它的方法的
模板
。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。 -
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就可以重定义该算法的某些特定步骤
- 代码演示
//抽象类,表示豆浆
public abstract class SoyaMilk {
//模板方法, make ,
//模板方法可以做成 final , 不让子类去覆盖重写
final void make() {
select();
addCondiments();
soak();
beat();
}
//选材料
void select() {
System.out.println("第一步:选择好的新鲜黄豆 ");
}
//添加不同的配料, 抽象方法, 子类具体实现
abstract void addCondiments();
//浸泡
1200页Java架构面试专题及答案
小编整理不易,对这份1200页Java架构面试专题及答案感兴趣劳烦帮忙转发/点赞
百度、字节、美团等大厂常见面试题
hod, Object[] args, MethodProxy arg3) throws Throwable {
System.out.println(“Cglib 代理模式 ~~ 开始”);
Object returnVal = method.invoke(target, args);
System.out.println(“Cglib 代理模式 ~~ 提交”);
return returnVal;
}
}
public class TeacherDao {
public String teach() {
System.out.println(" 老师授课中 , 我是 cglib 代理,不需要实现接口 "); return “hello”;
}
}
在 AOP 编程中如何选择代理模式:
-
目标对象需要实现接口,用 JDK 代理
-
目标对象不需要实现接口,用 Cglib 代理
-
在一个抽象类公开定义了执行它的方法的
模板
。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。 -
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就可以重定义该算法的某些特定步骤
- 代码演示
//抽象类,表示豆浆
public abstract class SoyaMilk {
//模板方法, make ,
//模板方法可以做成 final , 不让子类去覆盖重写
final void make() {
select();
addCondiments();
soak();
beat();
}
//选材料
void select() {
System.out.println("第一步:选择好的新鲜黄豆 ");
}
//添加不同的配料, 抽象方法, 子类具体实现
abstract void addCondiments();
//浸泡
1200页Java架构面试专题及答案
小编整理不易,对这份1200页Java架构面试专题及答案感兴趣劳烦帮忙转发/点赞
[外链图片转存中…(img-nH1TkdNU-1714850636055)]
[外链图片转存中…(img-JFoxsMTE-1714850636056)]
百度、字节、美团等大厂常见面试题
[外链图片转存中…(img-f7kghPBv-1714850636056)]