架构设计

2021年09月15日 阅读数:3
这篇文章主要向大家介绍架构设计,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

咱们将建立一个 Shape 接口和实现 Shape 接口的实体类。下一步是定义工厂类 ShapeFactory前端

FactoryPatternDemo,咱们的演示类使用 ShapeFactory 来获取 Shape 对象。它将向 ShapeFactory 传递信息(CIRCLE / RECTANGLE / SQUARE),以便获取它所需对象的类型。java

工厂模式

步骤 1nginx

建立一个接口。git

Shape.java面试


public interface Shape {

    void draw();

} 

步骤 2redis

建立实现接口的实体类。算法

Rectangle.java数据库


public class Rectangle implements Shape {

    @Override

    public void draw() {

        System.out.println("Inside Rectangle::draw() method.");

    }

} 

Square.java编程


public class Square implements Shape {

    @Override

    public void draw() {

        System.out.println("Inside Square::draw() method.");

    }

} 

Circle.java设计模式


public class Circle implements Shape {

    @Override

    public void draw() {

        System.out.println("Inside Circle::draw() method.");

    }

} 

步骤 3

建立一个工厂,生成基于给定信息的实体类的对象。

ShapeFactory.java


public class ShapeFactory {

    //使用 getShape 方法获取形状类型的对象

    public Shape getShape(String shapeType) {

        if (shapeType == null) {

            return null;

        }

        shapeType = shapeType.toLowerCase();

        switch (shapeType) {

            case "circle":

                return new Circle();

            case "rectangle":

                return new Rectangle();

            case "square":

                return new Square();

            default:

                return null;

        }

    }

} 

步骤 4

使用该工厂,经过传递类型信息来获取实体类的对象。

FactoryPatternDemo.java


public class FactoryPatternDemo {

    public static void main(String[] args) {

        ShapeFactory shapeFactory = new ShapeFactory();

        //获取 Circle 的对象,并调用它的 draw 方法

        Shape shape1 = shapeFactory.getShape("CIRCLE");

        //调用 Circle 的 draw 方法

        shape1.draw();

        //获取 Rectangle 的对象,并调用它的 draw 方法

        Shape shape2 = shapeFactory.getShape("RECTANGLE");

        //调用 Rectangle 的 draw 方法

        shape2.draw();

        //获取 Square 的对象,并调用它的 draw 方法

        Shape shape3 = shapeFactory.getShape("SQUARE");

        //调用 Square 的 draw 方法

        shape3.draw();

    }

} 

步骤 5

验证输出。


Inside Circle::draw() method.

Inside Rectangle::draw() method.

Inside Square::draw() method. 

[](

)观察者模式

当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。好比,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。

意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,全部依赖于它的对象都获得通知并被自动更新。

主要解决:一个对象状态改变给其余对象通知的问题,并且要考虑到易用和低耦合,保证高度的协做。

实现

观察者模式使用三个类 Subject、Observer 和 Client。Subject 对象带有绑定观察者到 Client 对象和从 Client 对象解绑观察者的方法。咱们建立 Subject 类、Observer 抽象类和扩展了抽象类 Observer 的实体类。

ObserverPatternDemo,咱们的演示类使用 Subject 和实体类对象来演示观察者模式。

观察者模式

步骤 1

建立 Subject 类。

Subject.java


public class Subject {

    private List<Observer> observers = new ArrayList<>();

    private int state;

    public int getState() {

        return state;

    }

    public void setState(int state) {

        this.state = state;

        notifyAllObservers();

    }

    public void attach(Observer observer) {

        observers.add(observer);

    }

    public void notifyAllObservers() {

        for (Observer observer : observers) {

            observer.update();

        }

    }

} 

步骤 2

建立 Observer 类。

Observer.java


public abstract class Observer {

    protected Subject subject;

    public abstract void update();

} 

步骤 3

建立实体观察者类。

BinaryObserver.java


public class BinaryObserver extends Observer {

    public BinaryObserver(Subject subject) {

        this.subject = subject;

        this.subject.attach(this);

    }

    @Override

    public void update() {

        System.out.println("Binary String: "

                + Integer.toBinaryString(subject.getState()));

    }

} 

OctalObserver.java


public class OctalObserver extends Observer {

    public OctalObserver(Subject subject){

        this.subject = subject;

        this.subject.attach(this);

    }

    @Override

    public void update() {

        System.out.println( "Octal String: "

                + Integer.toOctalString( subject.getState() ) );

    }

} 

HexaObserver.java


public class HexaObserver extends Observer {

    public HexaObserver(Subject subject){

        this.subject = subject;

        this.subject.attach(this);

    }

    @Override

    public void update() {

        System.out.println( "Hex String: "

                + Integer.toHexString( subject.getState() ).toUpperCase() );

    }

} 

步骤 4

使用 Subject 和实体观察者对象。

ObserverPatternDemo.java


public class ObserverPatternDemo {

    public static void main(String[] args) {

        Subject subject = new Subject();

        new BinaryObserver(subject);

        new HexaObserver(subject);

        new OctalObserver(subject);

        System.out.println("First state change: 15");

        subject.setState(15);

        System.out.println();

        System.out.println("Second state change: 10");

        subject.setState(10);

    }

} 

步骤 5

验证输出。


First state change: 15

Binary String: 1111

Hex String: F

Octal String: 17

Second state change: 10

Binary String: 1010

Hex String: A

Octal String: 12 

[](

)装饰器模式

装饰器模式(Decorator Pattern)容许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是做为现有的类的一个包装。

意图:动态地给一个对象添加一些额外的职责。就增长功能来讲,装饰器模式相比生成子类更为灵活。

主要解决:通常的,咱们为了扩展一个类常用继承方式实现,因为继承为类引入静态特征,而且随着扩展功能的增多,子类会很膨胀。

实现

咱们将建立一个 Shape 接口和实现了 Shape 接口的实体类。而后咱们建立一个实现了 Shape 接口的抽象装饰类 ShapeDecorator,并把 Shape 对象做为它的实例变量。

RedShapeDecorator 是实现了 ShapeDecorator 的实体类。

DecoratorPatternDemo,咱们的演示类使用 RedShapeDecorator 来装饰 Shape 对象。

装饰器模式

步骤 1

建立一个接口。

Shape.java


public interface Shape {

    void draw();

} 

步骤 2

建立实现接口的实体类。

Rectangle.java


public class Rectangle implements Shape {

    @Override

    public void draw() {

        System.out.println("Shape: Rectangle");

    }

} 

Circle.java


public class Circle implements Shape {

    @Override

    public void draw() {

        System.out.println("Shape: Circle");

    }

} 

步骤 3

建立实现了 Shape 接口的抽象装饰类。

ShapeDecorator.java


public abstract class ShapeDecorator implements Shape {

    protected Shape decoratorShape;

    public ShapeDecorator(Shape decoratorShape) {

        this.decoratorShape = decoratorShape;

    }

    @Override

    public void draw() {

        decoratorShape.draw();

    }

} 

步骤 4

建立扩展了 ShapeDecorator 类的实体装饰类。

RedShapeDecorator.java


public class RedShapeDecorator extends ShapeDecorator {

    public RedShapeDecorator(Shape decoratorShape) {

        super(decoratorShape);

    }

    @Override

    public void draw() {

        decoratorShape.draw();

        setRedBorder(decoratorShape);

    }

    private void setRedBorder(Shape decoratorShape) {

        System.out.println("Border Color: Red");

    }

} 

步骤 5

使用 RedShapeDecorator 来装饰 Shape 对象。

DecoratorPatternDemo.java


public class DecoratorPatternDemo {

    public static void main(String[] args) {

        Shape circle = new Circle();

        Shape redCircle = new RedShapeDecorator(new Circle());

        Shape redRectangle = new RedShapeDecorator(new Rectangle());

        System.out.println("Circle with normal border");

        circle.draw();

        System.out.println("\nCircle of red border");

        redCircle.draw();

        System.out.println("\nRectangle of red border");

        redRectangle.draw();

    }

} 

步骤 6

验证输出。


Circle with normal border

Shape: Circle

Circle of red border

Shape: Circle

Border Color: Red

Rectangle of red border

Shape: Rectangle

Border Color: Red 

[](

)秒杀系统设计

什么是秒杀

通俗一点讲就是网络商家为促销等目的组织的网上限时抢购活动

业务特色

  • 高并发:秒杀的特色就是这样时间极短瞬间用户量大

  • 库存量少:通常秒杀活动商品量不多,这就致使了只有极少许用户能成功购买到。

  • 业务简单:流程比较简单,通常都是下订单、扣库存、支付订单

  • 恶意请求,数据库压力大

解决方案

前端:页面资源静态化,按钮控制,使用答题校验码能够防止秒杀器的干扰,让更多用户有机会抢到

nginx:校验恶意请求,转发请求,负载均衡;动静分离,不走tomcat获取静态资源;gzip压缩,减小静态文件传输的体积,节省带宽,提升渲染速度

业务层:集群,多台机器处理,提升并发能力

redis:集群保证高可用,持久化数据;分布式锁(悲观锁);缓存热点数据(库存)

mq:削峰限流,MQ堆积订单,保护订单处理层的负载,Consumer根据本身的消费能力来取Task,实际上下游的压力就可控了。重点作好路由层和MQ的安全

数据库:读写分离,拆分事务提升并发度

秒杀系统设计小结

  • 秒杀系统就是一个“三高”系统,即高并发、高性能高可用的分布式系统

  • 秒杀设计原则:前台请求尽可能少,后台数据尽可能少,调用链路尽可能短,尽可能不要有单点

  • 秒杀高并发方法:访问拦截、分流、动静分离

  • 秒杀数据方法:减库存策略、热点、异步、限流降级

  • 访问拦截主要思路:经过CDN和缓存技术,尽可能把访问拦截在离用户更近的层,尽量地过滤掉无效请求。

  • 分流主要思路:经过分布式集群技术,多台机器处理,提升并发能力。

[](

)分布式


[](

)分布式概述

[](

)分布式

分布式(distributed)是为了解决单个物理服务器容量和性能瓶颈问题而采用的优化手段,将一个业务拆分红不一样的子业务,分布在不一样的机器上执行。服务之间经过远程调用协同工做,对外提供服务。

该领域须要解决的问题极多,在不一样的技术层面上,又包括:分布式缓存、分布式数据库、分布式计算、分布式文件系统等,一些技术如MQ、Redis、zookeeper等都跟分布式有关。

从理念上讲,分布式的实现有两种形式:

水平扩展:当一台机器扛不住流量时,就经过添加机器的方式,将流量平分到全部服务器上,全部机器均可以提供 相同的服务;

垂直拆分:前端有多种查询需求时,一台机器扛不住,能够将不一样的业务需求分发到不一样的机器上,好比A机器处理余票查询的请求,B机器处理支付的请求。

[](

)集群

集群(cluster)是指在多台不一样的服务器中部署相同应用或服务模块,构成一个集群,经过负载均衡设备对外提供服务。

两个特色

可扩展性:集群中的服务节点,能够动态的添加机器,从而增长集群的处理能力。

高可用性:若是集群某个节点发生故障,这台节点上面运行的服务,能够被其余服务节点接管,从而加强集群的高可用性。

两大能力

负载均衡:负载均衡能把任务比较均衡地分布到集群环境下的计算和网络资源。

集群容错:当咱们的系统中用到集群环境,由于各类缘由在集群调用失败时,集群容错起到关键性的做用。

[](

)微服务

微服务就是很小的服务,小到一个服务只对应一个单一的功能,只作一件事。这个服务能够单独部署运行,服务之间经过远程调用协同工做,每一个微服务都是由独立的小团队开发,测试,部署,上线,负责它的整个生命周期。

[](

)多线程

多线程(multi-thread):多线程是指程序中包含多个执行流,即在一个程序中能够同时运行多个不一样的线程来执行不一样的任务。多线程是为了提升CPU的利用率。

[](

)高并发

高并发(High Concurrency)是一种系统运行过程当中发生了一种“短期内遇到大量请求”的状况,高并发对应的是访问请求,多线程是解决高并发的方法之一,高并发还能够经过分布式,集群,算法优化,数据库优化等方法解决。

[](

)分布式系统设计理念

[](

)分布式系统的目标与要素

分布式系统的目标是提高系统的总体性能和吞吐量另外还要尽可能保证分布式系统的容错性(假如增长10台服务器才达到单机运行效果2倍左右的性能,那么这个分布式系统就根本没有存在的意义)。

即便采用了分布式系统,咱们也要尽力运用并发编程、高性能网络框架等等手段提高单机上的程序性能。

[](

)分布式系统设计两大思路:中心化和去中心化

分布式系统设计两大思路:中心化和去中心化

中心化设计

  • 两个角色: 中心化的设计思想很简单,分布式集群中的节点机器按照角色分工,大致上分为两种角色: “领导”“干活的”

  • 角色职责: “领导”一般负责分发任务并监督“干活的”,发现谁太闲了,就想发设法地给其安排新任务,确保没有一个“干活的”可以偷懒,若是“领导”发现某个“干活的”由于劳累过分而病倒了,则是不会考虑先尝试“医治”他的,而是一脚踢出去,而后把他的任务分给其余人。其中微服务架构 Kubernetes 就刚好采用了这一设计思路。

  • 中心化设计的问题

    1. 中心化的设计存在的最大问题是“领导”的安危问题,若是“领导”出了问题,则群龙无首,整个集群就奔溃了。但咱们难以同时安排两个“领导”以免单点问题。

    2. 中心化设计还存在另一个潜在的问题,既“领导”的能力问题:能够领导10我的高效工做并不意味着能够领导100我的高效工做,因此若是系统设计和实现得很差,问题就会卡在“领导”身上。
  • 领导安危问题的解决办法: 大多数中心化系统都采用了主备两个“领导”的设计方案,能够是热备或者冷备,也能够是自动切换或者手动切换,并且愈来愈多的新系统都开始具有自动选举切换“领导”的能力,以提高系统的可用性。

去中心化设计

  • 众生地位平等: 在去中心化的设计里,一般没有“领导”和“干活的”这两种角色的区分,你们的角色都是同样的,地位是平等的,全球互联网就是一个典型的去中心化的分布式系统,联网的任意节点设备宕机,都只会影响很小范围的功能。

  • “去中心化”不是不要中心,而是由节点来自由选择中心。 (集群的成员会自发的举行“会议”选举新的“领导”主持工做。最典型的案例就是ZooKeeper及Go语言实现的Etcd)

  • 去中心化设计的问题: 去中心化设计里最难解决的一个问题是 “脑裂”问题 ,这种状况的发生几率很低,但影响很大。脑裂指一个集群因为网络的故障,被分为至少两个彼此没法通讯的单独集群,此时若是两个集群都各自工做,则可能会产生严重的数据冲突和错误。通常的设计思路是,当集群判断发生了脑裂问题时,规模较小的集群就“自杀”或者拒绝服务。

[](

)分布式与集群的区别是什么?

  • 分布式: 一个业务分拆多个子业务,部署在不一样的服务器上

  • 集群: 同一个业务,部署在多个服务器上。好比以前作电商网站搭的redis集群以及solr集群都是属于将redis服务器提供的缓存服务以及solr服务器提供的搜索服务部署在多个服务器上以提升系统性能、并发量解决海量存储问题。

[](

)CAP定理

CAP定理

在理论计算机科学中,CAP定理(CAP theorem),又被称做布鲁尔定理(Brewer’s theorem),它指出对于一个分布式计算系统来讲,不可能同时知足如下三点:

| 选项 | 描述 |

| :-- | :-- |

| Consistency(一致性) | 指数据在多个副本之间可以保持一致的特性(严格的一致性) |

| Availability(可用性) | 指系统提供的服务必须一直处于可用的状态,每次请求都能获取到非错的响应(不保证获取的数据为最新数据) |

| Partition tolerance(分区容错性) | 分布式系统在遇到任何网络分区故障的时候,仍然可以对外提供知足一致性和可用性的服务,除非整个网络环境都发生了故障 |

Spring Cloud在CAP法则上主要知足的是A和P法则,Dubbo和Zookeeper在CAP法则主要知足的是C和P法则

CAP仅适用于原子读写的NOSQL场景中,并不适合数据库系统。如今的分布式系统具备更多特性好比扩展性、可用性等等,在进行系统设计和开发时,咱们不该该仅仅局限在CAP问题上。

注意:不是所谓的3选2(不要被网上大多数文章误导了)

现实生活中,大部分人解释这必定律时,经常简单的表述为:“一致性、可用性、分区容忍性三者你只能同时达到其中两个,不可能同时达到”。实际上这是一个很是具备误导性质的说法,并且在CAP理论诞生12年以后,CAP之父也在2012年重写了以前的论文。

当发生网络分区的时候,若是咱们要继续服务,那么强一致性和可用性只能2选1。也就是说当网络分区以后P是前提,决定了P以后才有C和A的选择。也就是说分区容错性(Partition tolerance)咱们是必需要实现的。

[](

)CAP定理的证实


关于CAP这三个特性咱们就介绍完了,接下来咱们试着证实一下为何CAP不能同时知足

img

为了简化证实的过程,咱们假设整个集群里只有两个N1和N2两个节点,以下图:

N1和N2当中各自有一个应用程序AB和数据库,当系统知足一致性的时候,咱们认为N1和N2数据库中的数据保持一致。在知足可用性的时候,咱们认为不管用户访问N1仍是N2,均可以得到正确的结果,在知足分区容错性的时候,咱们认为不管N1仍是N2宕机或者是二者的通讯中断,都不影响系统的运行。

咱们假设一种极端状况,假设某个时刻N1和N2之间的网络通讯忽然中断了。若是系统知足分区容错性,那么显然能够支持这种异常。问题是在此前提下,一致性和可用性是否能够作到不受影响呢?

咱们作个假象实验,以下图,忽然某一时刻N1和N2之间的关联断开:

img

有用户向N1发送了请求更改了数据,将数据库从V0更新成了V1。因为网络断开,因此N2数据库依然是V0,若是这个时候有一个请求发给了N2,可是N2并无办法能够直接给出最新的结果V1,这个时候该怎么办呢?

这个时候没法两种方法,一种是将错就错,将错误的V0数据返回给用户。第二种是阻塞等待,等待网络通讯恢复,N2中的数据更新以后再返回给用户。显然前者牺牲了一致性,后者牺牲了可用性。

这个例子虽然简单,可是说明的内容却很重要。在分布式系统当中,CAP三个特性咱们是没法同时知足的,必然要舍弃一个。三者舍弃一个,显然排列组合一共有三种可能。

[](

)BASE理论

BASE理论由eBay架构师Dan Pritchett提出,在2008年上被分表为论文,而且eBay给出了他们在实践中总结的基于BASE理论的一套新的分布式事务解决方案。

BASEBasically Available(基本可用)Soft-state(软状态)Eventually Consistent(最终一致性) 三个短语的缩写。BASE理论是对CAP中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的总结,是基于CAP定理逐步演化而来的,它大大下降了咱们对系统的要求。

[](

)BASE理论的核心思想

即便没法作到强一致性,但每一个应用均可以根据自身业务特色,采用适当的方式来使系统达到最终一致性。也就是牺牲数据的一致性来知足系统的高可用性,系统中一部分数据不可用或者不一致时,仍须要保持系统总体“主要可用”。

针对数据库领域,BASE思想的主要实现是对业务数据进行拆分,让不一样的数据分布在不一样的机器上,以提高系统的可用性,当前主要有如下两种作法:

  • 按功能划分数据库

  • 分片(如开源的Mycat、Amoeba等)。

因为拆分后会涉及分布式事务问题,因此eBay在该BASE论文中提到了如何用最终一致性的思路来实现高性能的分布式事务。

[](

)BASE理论三要素

BASE理论三要素

[](

)1. 基本可用

基本可用是指分布式系统在出现不可预知故障的时候,容许损失部分可用性。可是,这毫不等价于系统不可用。

好比:

  • 响应时间上的损失:正常状况下,一个在线搜索引擎须要在0.5秒以内返回给用户相应的查询结果,但因为出现故障,查询结果的响应时间增长了1~2秒

  • 系统功能上的损失:正常状况下,在一个电子商务网站上进行购物的时候,消费者几乎可以顺利完成每一笔订单,可是在一些节日大促购物高峰的时候,因为消费者的购物行为激增,为了保护购物系统的稳定性,部分消费者可能会被引导到一个降级页面
[](

)2. 软状态

软状态指容许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的总体可用性,即容许系统在不一样节点的数据副本之间进行数据同步的过程存在延时

[](

)3. 最终一致性

最终一致性强调的是系统中全部的数据副本,在通过一段时间的同步后,最终可以达到一个一致的状态。所以,最终一致性的本质是须要系统保证最终数据可以达到一致,而不须要实时保证系统数据的强一致性。

[](

)数据结构与算法


[](

)冒泡排序

冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,依次比较两个元素,若是它们的顺序错误就把它们交换过来。走访数列的工做是重复地进行直到没有再须要交换,也就是说该数列已经排序完成。这个算法的名字由来是由于越小的元素会经由交换慢慢“浮”到数列的顶端。

算法描述

  • 比较相邻的元素。若是第一个比第二个大,就交换它们两个;

  • 对每一对相邻元素做一样的工做,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;

  • 针对全部的元素重复以上的步骤,除了最后一个;

  • 重复步骤1~3,直到排序完成。

动图演示

冒泡排序

代码实现

下面的排序算法统一使用的测试代码以下,[源码GitHub连接](

)


public static void main(String[] args) {

    int[] array = {3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48};

    // 只须要修改为对应的方法名就能够了

    bubbleSort(array);

    System.out.println(Arrays.toString(array));

} 

/**

  • Description:冒泡排序

  • @param array 须要排序的数组

  • @author JourWon

  • @date 2019/7/11 9:54

    */

public static void bubbleSort(int[] array) {

if (array == null || array.length <= 1) {

    return;

}

int length = array.length;

// 外层循环控制比较轮数i

for (int i = 0; i < length; i++) {

    // 内层循环控制每一轮比较次数,每进行一轮排序都会找出一个较大值

    // (array.length - 1)防止索引越界,(array.length - 1 - i)减小比较次数

    for (int j = 0; j < length - 1 - i; j++) {

        // 前面的数大于后面的数就进行交换

        if (array[j] > array[j + 1]) {

            int temp = array[j + 1];

            array[j + 1] = array[j];

            array[j] = temp;

        }

    }

}

}


算法分析

**最佳状况:T(n) = O(n) 最差状况:T(n) = O(n2) 平均状况:T(n) = O(n2)**

### [](

)选择排序

表现**最稳定的排序算法之一**,由于**不管什么数据进去都是O(n2)的时间复杂度**,因此用到它的时候,数据规模越小越好。惟一的好处可能就是不占用额外的内存空间了吧。理论上讲,选择排序可能也是平时排序通常人想到的最多的排序方法了吧。

选择排序(Selection-sort)是一种简单直观的排序算法。它的工做原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,而后,再从剩余未排序元素中继续寻找最小(大)元素,而后放到已排序序列的末尾。以此类推,直到全部元素均排序完毕。

算法描述

n个记录的直接选择排序可通过n-1趟直接选择排序获得有序结果。具体算法描述以下:

*   初始状态:无序区为R\[1…n\],有序区为空;

*   第i趟排序(i=1,2,3…n-1)开始时,当前有序区和无序区分别为R\[1…i-1\]和R(i…n)。该趟排序从当前无序区中-选出关键字最小的记录 R\[k\],将它与无序区的第1个记录R交换,使R\[1…i\]和R\[i+1…n)分别变为记录个数增长1个的新有序区和记录个数减小1个的新无序区;

*   n-1趟结束,数组有序化了。

动图演示

![选择排序](https://img-blog.csdnimg.cn/20190712143023558.gif)

代码实现

下面的排序算法统一使用的测试代码以下,[源码GitHub连接](

)

public static void main(String[] args) {

int[] array = {3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48};

// 只须要修改为对应的方法名就能够了

selectionSort(array);

System.out.println(Arrays.toString(array));

}


/**

 * Description: 选择排序

 *

 * @param array

 * @return void

 * @author JourWon

 * @date 2019/7/11 23:31

 */

public static void selectionSort(int[] array) {

    if (array == null || array.length <= 1) {

        return;

    }

    int length = array.length;

    for (int i = 0; i < length - 1; i++) {

        // 保存最小数的索引

        int minIndex = i;

        for (int j = i + 1; j < length; j++) {

            // 找到最小的数

            if (array[j] < array[minIndex]) {

                minIndex = j;

            }

        }

        // 交换元素位置

        if (i != minIndex) {

            swap(array, minIndex, i);

        }

    }

}

/**

 * Description: 交换元素位置

 *

 * @param array

 * @param a

 * @param b

 * @return void

 * @author JourWon

 * @date 2019/7/11 17:57

 */

private static void swap(int[] array, int a, int b) {

    int temp = array[a];

    array[a] = array[b];

    array[b] = temp;

} 

资料分享

CodeChina开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频】

首先分享一份学习大纲,内容较多,涵盖了互联网行业全部的流行以及核心技术,以截图形式分享:

(亿级流量性能调优实战+一线大厂分布式实战+架构师筑基必备技能+设计思想开源框架解读+性能直线提高架构技术+高效存储让项目性能起飞+分布式扩展到微服务架构.........实在是太多了)

其次分享一些技术知识,以截图形式分享一部分:

Tomcat架构解析:

架构设计

算法训练+高分宝典:

架构设计

Spring Cloud+Docker微服务实战:

架构设计

最后分享一波面试资料:

切莫死记硬背,当心面试官直接让你出门右拐

1000道互联网Java面试题:

架构设计

Java高级架构面试知识整理:

架构设计