java第二十一天Lock锁 死锁现象 线程池 定时器 设计模式 设计原则

本文主要是介绍java第二十一天Lock锁 死锁现象 线程池 定时器 设计模式 设计原则,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 

1  Lock 锁

    A:Lock锁的概述
        虽然我们可以理解同步代码块和同步方法的锁对象问题,
        但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,
        为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock
    B:Lock和ReentrantLock
        void lock()
        void unlock()

 

2  线程的状态转换图

新建 , 就绪 , 运行 , 冻结 , 死亡

       新建:线程被创建出来
       就绪:具有CPU的执行资格,但是不具有CPU的执行权
       运行:具有CPU的执行资格,也具有CPU的执行权
       阻塞:不具有CPU的执行资格,也不具有CPU的执行权
       死亡:不具有CPU的执行资格,也不具有CPU的执行权

3 线程池

线程池概述
        
程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。
        而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。
        线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。
        在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池

JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法
            public static ExecutorService newCachedThreadPool():            根据任务的数量来创建线程对应的线程个数    
            public static ExecutorService newFixedThreadPool(int nThreads):    固定初始化几个线程
            public static ExecutorService newSingleThreadExecutor():            初始化一个线程的线程池
        这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。它提供了如下方法
            Future<?> submit(Runnable task)
            <T> Future<T> submit(Callable<T> task)

4 匿名内部类的方式实现多线程程序

        new Thread(){代码…}.start();
        new Thread(new Runnable(){代码…}).start();

 

5   定时器

B:Timer和TimerTask
      
 Timer:
            public Timer()
            public void schedule(TimerTask task, long delay):    
            public void schedule(TimerTask task,long delay,long period);
            public void schedule(TimerTask task,  Date time):
            public void schedule(TimerTask task,  Date firstTime, long period):
        TimerTask
            public abstract void run()
            public boolean cancel()

6 面试题

    A:多线程有几种实现方案,分别是哪几种?    三种
    B:同步有几种方式,分别是什么?   三种,一种同步代码块,一种同步方法,一种Lock锁
    C:启动一个线程是run()还是start()?它们的区别?   start(),如果run(),仅仅是对象调用方法,start()是让jvm调用run()

7   设计模式:

设计模式概述
        设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。
        使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性以及代码的结构更加清晰.

单例模式(饿汉式)

class Student {private Student(){}private static Student student =  new Student();public static Student getStudent(){return  student;}}public class Demo1 {public static void main(String[] args) {Student s1 = Student.getStudent();Student s2 = Student.getStudent();System.out.println(s1==s2);}}

单例模式(懒汉式(线程安全版本)

class Student {private Student(){}private static Student student =  null;public synchronized static Student getStudent(){if (student == null) {student = new Student();}return  student;}}public class Demo1 {public static void main(String[] args) {Student s1 = Student.getStudent();Student s2 = Student.getStudent();System.out.println(s1==s2);}}

饿汉式与懒汉式详解

都是单例模式,即一个类中只允许有一个对象这里来进行一个比较,究竟饿汉式和懒汉式哪一个更好;饿汉式:简单来说就是空间换时间,因为上来就实例化一个对象,占用了内存,(也不管你用还是不用)懒汉式:简单的来说就是时间换空间,与饿汉式正好相反这时,会有人说,懒汉式比饿汉式更好,其实恰恰相反,这里举个例子!比如,现在有A线程和B线程,A线程刚好在这个getStudent()方法中,刚刚判断完非空(此时为null),即需要创建实例,然而,就是这么巧,B线程抢到了CPU的执行权,A线程sleep了,这时,B线程也进行了这个判断,和A一样,都需要创建实例,而这时,A也抢到了CPU,这时,B就sleep了,然后A执行了实例化后,B又抢到CPU执行权,然后B也实例化,这时,出现问题了,A和B都实例化了一个对象,这就是赤果果的两个对象呀,单例呢,唯一呢,全都没了。
--------------------- 1、线程安全:饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题,懒汉式本身是非线程安全的,为了实现线程安全有几种写法。最常见的是加synchronized关键字,2、资源加载和性能:饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成。而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。--------------------- 意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。主要解决:一个全局使用的类频繁地创建与销毁。何时使用:当您想控制实例数目,节省系统资源的时候。如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。关键代码:构造函数是私有的。应用实例: 1、一个党只能有一个主席。 2、Windows 是多进程多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须通过唯一的实例来进行。 3、一些设备管理器常常设计为单例模式,比如一个电脑有两台打印机,在输出的时候就要处理不能两台打印机打印同一个文件。优点: 1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。 2、避免对资源的多重占用(比如写文件操作)。缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。使用场景: 1、要求生产唯一序列号。 2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。 3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。注意事项:getStudent() 方法中需要使用同步锁 synchronized (Student.class) 防止多线程同时进入造成instance 被多次实例化

 

简单工厂模式静态工厂

1
public abstract class Animal {public abstract void eat();
}
2
public class Dog extends Animal {@Overridepublic void eat() {System.out.println("狗吃屎");}
}
3
public class Cat extends Animal {@Overridepublic void eat() {System.out.println("猫吃鱼");}
}4
public class AnimalFactory {public static Animal createAnimal(String s){if("Cat".equals(s)){return  new Cat();}else if("Dog".equals(s)){return new Dog();}else{return null;}}
}5
public class Demo1 {public static void main(String[] args) {Animal cat = AnimalFactory.createAnimal("Cat");Animal dog = AnimalFactory.createAnimal("Dog");cat.eat();dog.eat();}}

工厂方法模式

1
public abstract class Animal {public abstract void eat();
}
2
public class Dog extends Animal {@Overridepublic void eat() {System.out.println("狗吃屎");}
}
3
public class Cat extends Animal {@Overridepublic void eat() {System.out.println("猫吃鱼");}
}
4
public abstract interface BigAnimalFactory {public  abstract Animal createAnimal();}5
public class CatFactory implements BigAnimalFactory {@Overridepublic Animal createAnimal() {return new Cat();}}
6
public class DogFactory implements BigAnimalFactory {@Overridepublic Animal createAnimal() {return new Dog();}
}7
public class Demo1 {public static void main(String[] args) {CatFactory catFactory = new CatFactory();Animal cat = catFactory.createAnimal();DogFactory dogFactory = new DogFactory();Animal dog = dogFactory.createAnimal();cat.eat();dog.eat();}}

抽象工厂模式

//生产各种手机接口public interface PhoneProduct {public abstract void call();
}//生产各种pad接口
public interface IpadProduct {public abstract void play();
}//小米手机功能
public class xiaomiphone implements PhoneProduct {@Overridepublic void call() {System.out.println("用小米手机打电话");}
}//小米pad功能
public class xiaomiipad implements IpadProduct {@Overridepublic void play() {System.out.println("用小米pad玩");}
}//华为手机功能
public class huaweiphone implements PhoneProduct {@Overridepublic void call() {System.out.println("用华为手机打电话");}
}//华为pad功能
public class huaweiipad implements IpadProduct {@Overridepublic void play() {System.out.println("用华为pad玩");}
}--------------------------------------------------------准备//这是一个 抽象大工厂,既能生产手机,又能生产padpublic interface ProductFactory {public abstract PhoneProduct   getPhone();public abstract IpadProduct    getIpad();}//这是小米工厂,实现了这个大工厂,小米工厂既有手机又有pad
public class xiaomiFactory implements ProductFactory {@Overridepublic PhoneProduct getPhone() {return new xiaomiphone();}@Overridepublic IpadProduct getIpad() {return new xiaomiipad();}
}//这是华为工厂,实现了这个大工厂,华为工厂既有手机又有pad
public class huaweiFactory implements ProductFactory {@Overridepublic PhoneProduct getPhone() {return new huaweiphone();}@Overridepublic IpadProduct getIpad() {return new huaweiipad();}
}
---------------------------------------------------------工厂完毕
//测试类
public class Demo {public static void main(String[] args) {xiaomiFactory xiaomiFactory = new xiaomiFactory();PhoneProduct phone = xiaomiFactory.getPhone();phone.call();IpadProduct ipad = xiaomiFactory.getIpad();ipad.play();huaweiFactory huaweiFactory = new huaweiFactory();PhoneProduct phone1 = huaweiFactory.getPhone();phone1.call();IpadProduct ipad1 = huaweiFactory.getIpad();ipad1.play();}}
输出结果:用小米手机打电话
用小米pad玩
用华为手机打电话
用华为pad玩

 

简单工厂模式与工厂方法模式与抽象工厂模式详解

简单工厂中只有一个工厂,该工厂有一个创建产品的方法。根据传入参数不同,返回不同产品。举个例子:一个电视机工厂,根据客户的需要,生产海尔电视机,三星电视机。如果要新增一个产品,需要修改创建产品的方法。如果产品种类太多,代码会显得很笨重,不易维护。违背了开放-封闭原则。工厂方法模式是对简单工厂的升级,将工厂抽象出来,工厂基类定义创建产品的方法。每个工厂都需要实现该方法。每个产品对应一个工厂,一个工厂只生产对应产品。举个例子:只要是工厂都要能生产电视机。海尔工厂生产海尔电视机,三星工厂生产三星电视机。如果要新增一个产品,只需要新增对应的工厂和方法即可。遵循了开放-封闭原则。但是每新增一个产品都需要新增对应的工厂。抽象工厂模式是对工厂方法模式的升级。工厂基类除了定义创建电视机方法,还定义了创建微波炉的方法。举个例子:只要是工厂都要能生产电视机和微波炉。海尔工厂生产海尔电视机,海尔微波炉。三星工厂生产三星电视机,三星微波炉。如果要新增一个产品,也只需要新增对应的工厂和方法即可。这个层面上遵循了开放-封闭原则。但是如果需要对工厂基类进行扩展,实现生产冰箱的话。则需要对各个工厂都进行修改。三个设计模式各有利弊,需要根据场景进行使用。简单工厂用于生产少量的产品,等产品数量到了一定规模可以考虑使用工厂方法替代。工厂方法之后也可以升级为抽象工厂模式。

适配器模式

适配器模式(Adapter Pattern):将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。在适配器模式中,我们通过增加一个新的适配器类来解决接口不兼容的问题,使得原本没有任何关系的类可以协同工作。根据适配器类与适配者类的关系不同,适配器模式可分为对象适配器和类适配器两种,在对象适配器模式中,适配器与适配者之间是关联关系;在类适配器模式中,适配器与适配者之间是继承(或实现)关系。角色Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。
--------------------- 
1 类适配器。首先有一个已存在的将被适配的类public class Adaptee {public void adapteeRequest() {System.out.println("被适配者的方法");}
}2 定义一个目标接口public interface Target {void request();
}3 怎么才可以在目标接口中的 request() 调用 Adaptee 的 adapteeRequest() 方法呢?如果直接实现 Target 是不行的public class ConcreteTarget implements Target {@Overridepublic void request() {System.out.println("concreteTarget目标方法");}
}4 如果通过一个适配器类,实现 Target 接口,同时继承了 Adaptee 类,然后在实现的 request() 方法中调用父类的 adapteeRequest() 即可实现
public class Adapter extends Adaptee implements Target{@Overridepublic void request() {//...一些操作...super.adapteeRequest();//...一些操作...}
}5 我们来测试一下
public class Test {public static void main(String[] args) {Target target = new ConcreteTarget();target.request();Target adapterTarget = new Adapter();adapterTarget.request();}
}
6 输出
concreteTarget目标方法
被适配者的方法

这样我们即可在新接口 Target 中适配旧的接口或类

对象适配器

对象适配器与类适配器不同之处在于,类适配器通过继承来完成适配,对象适配器则是通过关联来完成,这里稍微修改一下 Adapter 类即可将转变为对象适配器

public class Adapter implements Target{// 适配者是对象适配器的一个属性private Adaptee adaptee = new Adaptee();@Overridepublic void request() {//...adaptee.adapteeRequest();//...}
}

注意这里的 Adapter 是将 Adaptee 作为一个成员属性,而不是继承它

 

观察者模式

观察者模式:定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖它的对象都会得到通知并自动更新

观察者模式又称为发布/订阅(Publish/Subscribe)模式

观察者设计模式涉及到两种角色:主题(Subject)和观察者(Observer)

1)Subject模块 
Subjec模块有3个主要操作addObserver():注册添加观察者(申请订阅)
deleteObserver():删除观察者(取消订阅)
notifyObserver():主题状态发生变化时通知所有的观察者对象(2)Oserver模块         //相当于消息中间件
Oserver模块有1个核心操作update(),当主题Subject状态改变时,将调用每个观察者的update()方法,更新通知。

被观察者Subject对象

public class Subject {//保存注册的观察者对象private List<Observer> mObervers = new ArrayList<>();//注册观察者对象public void attach(Observer observer) {mObervers.add(observer);Log.e("SZH", "attach an observer");}//注销观察者对象public void detach(Observer observer) {mObervers.remove(observer);Log.e("SZH", "detach an observer");}//通知所有注册的观察者对象public void notifyEveryOne(String newState) {for (Observer observer : mObervers) {observer.update(newState);}}
}

接着是一个具体的被观察者对象

public class ConcreteSubject extends Subject {private String state;public String getState() {return state;}public void change(String newState) {state = newState;Log.e("SZH", "concreteSubject state:" + newState);//状态发生改变,通知观察者notifyEveryOne(newState);}
}

观察者Observer对象       //相当于消息中间件

首先是一个接口,抽象出了一个及时更新的方法

public interface Observer {void update(String newState);
}

接着是几个观察者对象

public class ObserverA implements Observer {//观察者状态private String observerState;@Overridepublic void update(String newState) {//更新观察者状态,让它与目标状态一致observerState = newState;Log.e("SZH", "接收到消息:" + newState + ";我是A模块,快来抢吧!!");}
}public class ObserverB implements Observer {//观察者状态private String observerState;@Overridepublic void update(String newState) {//更新观察者状态,让它与目标状态一致observerState = newState;Log.e("SZH", "接收到消息:" + newState + ";我是B模块,快来抢吧!!");}
}public class ObserverC implements Observer {//观察者状态private String observerState;@Overridepublic void update(String newState) {//更新观察者状态,让它与目标状态一致observerState = newState;Log.e("SZH", "接收到消息:" + newState + ";我是C模块,快来抢吧!!");}
}

 

8  注意

volatile

CSA算法

设计的七大原则

单一职责,里氏替换,迪米特法则,依赖倒转,接口隔离,合成/聚合原则,开放-封闭 。

1.   开闭原则(Open-Closed Principle, OCP)

定义:软件实体应当对扩展开放,对修改关闭。这句话说得有点专业,更通俗一点讲,也就是:软件系统中包含的各种组件,例如模块(Modules)、类(Classes)以及功能(Functions)等等,应该在不修改现有代码的基础上,去扩展新功能。开闭原则中原有“开”,是指对于组件功能的扩展是开放的,是允许对其进行功能扩展的;开闭原则中“闭”,是指对于代码的修改是封闭的,即不应该修改原有的代码。

问题由来:凡事的产生都有缘由。我们来看看,开闭原则的产生缘由。在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误,也可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试。这就对我们的整个系统的影响特别大,这也充分展现出了系统的耦合性如果太高,会大大的增加后期的扩展,维护。为了解决这个问题,故人们总结出了开闭原则。解决开闭原则的根本其实还是在解耦合。所以,我们面向对象的开发,我们最根本的任务就是解耦合。 

解决方法:当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。 

小结:开闭原则具有理想主义的色彩,说的很抽象,它是面向对象设计的终极目标。其他几条原则,则可以看做是开闭原则的实现。我们要用抽象构建框架,用实现扩展细节。

2.    单一职责原则(Single Responsibility Principle)

定义:一个类,只有一个引起它变化的原因。即:应该只有一个职责。

每一个职责都是变化的一个轴线,如果一个类有一个以上的职责,这些职责就耦合在了一起。这会导致脆弱的设计。当一个职责发生变化时,可能会影响其它的职责。另外,多个职责耦合在一起,会影响复用性。例如:要实现逻辑和界面的分离。需要说明的一点是单一职责原则不只是面向对象编程思想所特有的,只要是模块化的程序设计,都需要遵循这一重要原则。 

问题由来:类T负责两个不同的职责:职责P1,职责P2。当由于职责P1需求发生改变而需要修改类T时,有可能会导致原本运行正常的职责P2功能发生故障。 

解决方法:分别建立两个类T1、T2,使T1完成职责P1功能,T2完成职责P2功能。这样,当修改类T1时,不会使职责P2发生故障风险;同理,当修改T2时,也不会使职责P1发生故障风险。

3.    里氏替换原则(Liskov Substitution Principle) 

定义:子类型必须能够替换掉它们的父类型。注意这里的能够两字。有人也戏称老鼠的儿子会打洞原则。

问题由来:有一功能P1,由类A完成。现需要将功能P1进行扩展,扩展后的功能为P,其中P由原有功能P1与新功能P2组成。新功能P由类A的子类B来完成,则子类B在完成新功能P2的同时,有可能会导致原有功能P1发生故障。 

解决方法:类B继承类A时,除添加新的方法完成新增功能P2外,尽量不要重写父类A的方法,也尽量不要重载父类A的方法 

小结:所有引用父类的地方必须能透明地使用其子类的对象。子类可以扩展父类的功能,但不能改变父类原有的功能,即:子类可以实现父类的抽象方法,子类也中可以增加自己特有的方法,但不能覆盖父类的非抽象方法。当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。

4.    迪米特法则(Law Of Demeter)

 定义:迪米特法则又叫最少知道原则,即:一个对象应该对其他对象保持最少的了解。如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。简单定义为只与直接的朋友通信。首先来解释一下什么是直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖、关联、组合、聚合等。其中,我们称出现成员变量、方法参数、方法返回值中的类为直接的朋友,而出现在局部变量中的类则不是直接的朋友。也就是说,陌生的类最好不要作为局部变量的形式出现在类的内部。

问题由来:类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。

最早是在1987年由美国Northeastern University的Ian Holland提出。通俗的来讲,就是一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类来说,无论逻辑多么复杂,都尽量地的将逻辑封装在类的内部,对外除了提供的public方法,不对外泄漏任何信息。迪米特法则还有一个更简单的定义:只与直接的朋友通信。 

解决方法:尽量降低类与类之间的耦合。 自从我们接触编程开始,就知道了软件编程的总的原则:低耦合,高内聚。无论是面向过程编程还是面向对象编程,只有使各个模块之间的耦合尽量的低,才能提高代码的复用率。 

迪米特法则的初衷是降低类之间的耦合,由于每个类都减少了不必要的依赖,因此的确可以降低耦合关系。但是凡事都有度,虽然可以避免与非直接的类通信,但是要通信,必然会通过一个“中介”来发生联系。故过分的使用迪米特原则,会产生大量这样的中介和传递类,导致系统复杂度变大。所以在采用迪米特法则时要反复权衡,既做到结构清晰,又要高内聚低耦合。 

5.    依赖倒置原则(Dependence Inversion Principle) 

定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。中心思想是面向接口编程 

问题由来:类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。 

解决方法:将类A修改为依赖接口I,类B和类C各自实现接口I,类A通过接口I间接与类B或者类C发生联系,则会大大降低修改类A的几率。 

在实际编程中,我们一般需要做到如下3点:

1). 低层模块尽量都要有抽象类或接口,或者两者都有。

2). 变量的声明类型尽量是抽象类或接口。

3). 使用继承时遵循里氏替换原则。 

采用依赖倒置原则尤其给多人合作开发带来了极大的便利,参与协作开发的人越多、项目越庞大,采用依赖导致原则的意义就越重大。 

小结:依赖倒置原则就是要我们面向接口编程,理解了面向接口编程,也就理解了依赖倒置。 

6.    接口隔离原则(Interface Segregation Principle)

定义:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。 

问题由来:类A通过接口I依赖类B,类C通过接口I依赖类D,如果接口I对于类A和类B来说不是最小接口,则类B和类D必须去实现他们不需要的方法 

解决方法:1、 使用委托分离接口。2、 使用多重继承分离接口。3.将臃肿的接口I拆分为独立的几个接口,类A和类C分别与他们需要的接口建立依赖关系。也就是采用接口隔离原则。 

举例说明:

下面我们来看张图,一切就一目了然了。

  

这个图的意思是:类A依赖接口I中的方法1、方法2、方法3,类B是对类A依赖的实现。类C依赖接口I中的方法1、方法4、方法5,类D是对类C依赖的实现。对于类B和类D来说,虽然他们都存在着用不到的方法(也就是图中红色字体标记的方法),但由于实现了接口I,所以也必须要实现这些用不到的方法

修改后:

 

如果接口过于臃肿,只要接口中出现的方法,不管对依赖于它的类有没有用处,实现类中都必须去实现这些方法,这显然不是好的设计。如果将这个设计修改为符合接口隔离原则,就必须对接口I进行拆分。在这里我们将原有的接口I拆分为三个接口

小结:我们在代码编写过程中,运用接口隔离原则,一定要适度,接口设计的过大或过小都不好。对接口进行细化可以提高程序设计灵活性是不挣的事实,但是如果过小,则会造成接口数量过多,使设计复杂化。所以一定要适度。设计接口的时候,只有多花些时间去思考和筹划,就能准确地实践这一原则。 

7.   合成/聚合原则(Composite/Aggregate Reuse Principle,CARP) 

定义:也有人叫做合成复用原则,及尽量使用合成/聚合,尽量不要使用类继承。换句话说,就是能用合成/聚合的地方,绝不用继承。 

为什么要尽量使用合成/聚合而不使用类继承?

1. 对象的继承关系在编译时就定义好了,所以无法在运行时改变从父类继承的子类的实现

2. 子类的实现和它的父类有非常紧密的依赖关系,以至于父类实现中的任何变化必然会导致子类发生变化

3. 当你复用子类的时候,如果继承下来的实现不适合解决新的问题,则父类必须重写或者被其它更适合的类所替换,这种依赖关系限制了灵活性,并最终限制了复用性。

总结:这些原则在设计模式中体现的淋淋尽致,设计模式就是实现了这些原则,从而达到了代码复用、增强了系统的扩展性。所以设计模式被很多人奉为经典。我们可以通过好好的研究设计模式,来慢慢的体会这些设计原则。

这篇关于java第二十一天Lock锁 死锁现象 线程池 定时器 设计模式 设计原则的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/807100

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听