[Java][华为云Java编程创造营][学习笔记][第二阶段][06_Java面向对象之枚举和包装类及设计模式]

网友投稿 489 2022-05-29

1,枚举类型

1.1,枚举类的定义

枚举是一种特殊类。枚举是有固定实例个数的类型,我们可以把枚举理解成有固定个数实例的多例模式。

定义枚举类型其实就是在定义一个类,只不过是很多细节由编译器帮补齐了,所以,某种程度上enum关键字的作用就像是class或者interface。

当使用enum定义枚举类型时,实际上所定义出来的类型是继承自java.lang.Enum类。而每个被枚举的成员其实就是定义的枚举类型的一个实例,它们都被默认为final。无法改变常数名称所设定的值,它们也是public和static的成员,这与接口中的常量限制相同。可以通过类名称直接使用它们。

1.2,枚举类的结构

1.2.1,枚举类的结构(1)

枚举像普通的类一样可以添加属性和方法,可以为它添加静态和非静态的属性或方法。

enum SeasonEnum { //注意:枚举写在最前面,否则编译出错。 SPRING, SUMMER, AUTUMN, WINTERS; private final static String position = "first"; public static SeasonEnum getSeason(String s) { if (position.equals(s)) { return SPRING; } else { return AUTUMN; } } }

1.2.2,枚举类的结构(2)

设计带构造器的枚举

enum Gender { //通过括号赋值,而且必须带有一个参构造器和一个属性跟方法,否则编译出错。 //赋值必须都赋值或都不赋值,不能一部分赋值一部分不赋值。 //如果不赋值则不能写构造器,赋值编译也出错。 MAN("MAN"), WOMEN("WOMEN"); private final String value; //构造器默认也只能是private,从而保证构造函数只能在内部使用。 Gender(String value) { this.value = value; } public String getValue() { return value; } }

1.2.3,枚举类的结构(3)

设计带抽象方法的枚举

enum OrderState { CANCEL {public String getName(){return "已取消";}}, WAITCONFIRM{public String getName(){return "待审核";}}, WAITPAYMENT{public String getName(){return "等待付款";}}; public abstract String getName(); //此枚举类的抽象方法 }

1.3,枚举的应用

枚举是一种类型,用于定义常量,以限制变量的赋值;赋值时通过"枚举名.值"取得枚举中的值。

枚举类的常用API如下:

2,包装类

2.1,包装类定义

包装类(1)

虽然Java是面向对象的编程语言,但它所包含的8种基本数据类型却不支持面向对象的编程机制(没有属性和方法)。

在Java中,很多类和方法都需要接收引用类型的对象,此时就无法将一个基本数据类型的值传入。

为了解决这样的问题,JDK中提供了一系列的包装类,通过这些包装类可以将基本数据类型的值包装为引用数据类型的对象。

包装类(2)

包装类的继承关系

[img1]

包装类(3)

2.2,自动装箱和拆箱

自动装箱:是指将基本数据类型的变量赋给对应的包装类变量。

自动拆箱:是指将包装类对象类型直接赋给一个对应的基本数据类型变量。

public class Test { public static void main(String[] args) { int i = 10; Integer integer = i;//将基本数据类型的变量赋值给相应的包装类变量,这就是自动装箱,把基本数据类型变成了引用类型。 Integer a = 5; int x = a;//将包装类的变量赋给相应的基本数据类型变量,这就是自动拆箱。 System.out.println(integer);//10 System.out.println(x);//5 } }

包装类API(1)

1,通过引用数据类型字符串String类的valueOf()方法可以将8种基本数据类型转换为相应的字符串类型。

2,通过8种包装类型的静态方法valueOf()既可以将对应的基本数据类型转换为包装类,也可以将变量内容匹配的字符串转换为对应的包装类(Character包装类除外)。

3,通过8种包装类的有参数构造方法同样既可以将对应的基本数据类型转换为包装类,也可以将变量内容匹配的字符串转换为对应的包装类(Character包装类除外)。

4,通过8种包装类型的静态方法parseXXX()可以将变量内容匹配的字符串转换为对应的基本数据类型。

5,包装类都重写了Object类中的toString()方法,以字符串的形式返回被包装的基本数据类型的值。

public class Test { public static void main(String[] args) { //1,通过引用数据类型字符串String类的valueOf()方法可以将8种基本数据类型转换为相应的字符串类型。 // long a = 66; // float d = 3.14f; // char c = '华'; // boolean bool = true; // // String string1 = String.valueOf(a);//将正数转换为String // System.out.println(string1);//66 //2,通过8种包装类型的静态方法valueOf()既可以将对应的基本数据类型转换为包装类,也可以将变量内容匹配的字符串转换为对应的包装类(Character包装类除外)。 // Integer integer=Integer.valueOf("华为Integer");//编译通过,运行时异常(Exception in thread "main" java.lang.NumberFormatException: For input string: "华为Integer") // Float f = Float.valueOf("华为Float"); // Boolean b = Boolean.valueOf("true"); //3,通过8种包装类的有参数构造方法同样既可以将对应的基本数据类型转换为包装类,也可以将变量内容匹配的字符串转换为对应的包装类(Character包装类除外)。 // Integer integer = new Integer("华为");//编译通过,运行时异常(Exception in thread "main" java.lang.NumberFormatException: For input string: "华为") //4,通过8种包装类型的静态方法parseXXX()可以将变量内容匹配的字符串转换为对应的基本数据类型。 // int i = Integer.parseInt("123华为");//将字符串转换为基本数据类型 //5,包装类都重写了Object类中的toString()方法,以字符串的形式返回被包装的基本数据类型的值。 // Boolean b = Boolean.valueOf("true"); // System.out.println(b); //true } }

包装类API(2)

除了Character外,包装类都有valueOf(String s)方法,可以根据String类型的参数创建包装类对象,但参数字符串s不能为null,而且字符串必须是可以解析为相应基本类型的数据,否则虽然编译通过,但运行时会报错。

public class Test { public static void main(String[] args) { Integer i1 = Integer.valueOf("123"); System.out.println(i1);//123 Integer i2 = Integer.valueOf("12a"); System.out.println(i2);//报错,Exception in thread "main" java.lang.NumberFormatException: For input string: "12a" } }

包装类API(3)

除了Character外,包装类都有parseXXX(String s)的静态方法,将字符串转换为对应的基本类型的数据。参数s不能为null,而且同样必须是可以解析为相应基本类型的数据,否则虽然编译通过,但运行时会报错。

public class Test { public static void main(String[] args) { int i = Integer.parseInt("123"); System.out.println(i);//123 Integer integer = Integer.parseInt("huawei"); System.out.println(integer);//Exception in thread "main" java.lang.NumberFormatException: For input string: "huawei" } }

3,设计模式

设计模式(Design Pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。

设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

什么GOF(Gang of Four)?

在1994年,由Erich Gamma,Richard Helm,Ralph Johnson和John Vlissides四人合著出版了一本名为Design Patterns - Elements of Reusable

Object-Oriented Software(中文译名:设计模式 - 可复用的面向对象软件元素)的书,该书首次提到了软件开发中设计模式的概念。

对接口编程而不是对实现编程。

优先使用对象组合而不是继承。

设计模式的类型

根据设计模式的参考书 Design Patterns - Elements of Reusable Object-Oriented Software(中文译名:设计模式 - 可复用的面向对象软件元素)中所提到的,总共有23种设计模式。

这些模式可以分为三大类:创建型模式(Creating Patterns),结构型模式(Structural Patterns),行为型模式(Behavioral Patterns)。

3.1,单例模式

单例模式(1)

单例模式(Singleton Pattern)是Java中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

1,单例类只能有一个实例

2,单例类必须自己创建自己的唯一实例

3,单例类必须给其他对象提供这一实例

单例模式(2)

创建一个SingleObject类。SingleObject类有它的私有构造函数和本身的一个静态实例。

SingleObject类提供了一个静态方法,供外界获取它的静态实例。SingletonPatternDemo类使用SingleObject类来获取SingleObject对象。

[img2]

单例模式(3)

方式一:懒汉式,线程安全

是否Lazy初始化:是

是否多线程安全:是

[Java][华为云Java编程创造营][学习笔记][第二阶段][06_Java面向对象之枚举和包装类及设计模式]

实现难度:易

描述:这种方式具备很好的lazy loading,能够在多线程中很好的工作,但是,效率很低,99%情况下不需要同步。

class Singleton { //1,单例类只能有一个实例 //2,单例类必须自己创建自己的唯一实例 //3,单例类必须给所有其他对象提供这一实例 private static Singleton singleton;//确保整个类只有一份 private Singleton()//将构造私有化,这样就不能在外部通过new+构造方法再来创建Singleton对象了 { } public static synchronized Singleton getInstance()//确保是线程安全的 { if (singleton == null) { singleton = new Singleton(); } return singleton; } } public class TestSingleton { public static void main(String[] args) { Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); Singleton s3 = Singleton.getInstance(); System.out.println(s1 == s2);//true System.out.println(s2 == s3);//true } }

单例模式(4)

优点:第一次调用才初始化,避免内存浪费。

缺点:必须加锁synchronized才能保证单例,但加锁会影响效率。不过getInstance()的性能对应用程序不是很关键(该方法使用不太频繁)。

public class Singleton { private static Singleton instance; private Singleton() { } public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }

单例模式(5)

方式二:饿汉式

是否lazy初始化:否

是否多线程安全:是

实现难度:易

描述:这种方式比较常用,但容易产生垃圾对象

public class Singleton2 { private static Singleton2 singleton2 = new Singleton2(); private Singleton2() { } public static Singleton2 getInstance() { return singleton2; } }

单例模式(6)

优点:没有加锁,执行效率会提高。

缺点:类加载时就初始化,浪费内存。

它基于classloader机制避免了多线程的同步问题,不过,instance在类装载时就实例化,虽然导致类装载的原因很多种,在单例模式中大多数都是调用了getInstance方法,但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance显然没有达到lazy

loading的效果。

public class Singleton { private static Singleton instance = new Singleton(); private Singleton() { } public static Singleton getInstance() { return instance; } }

3.2,策略模式

策略模式(1)

在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。

意图:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。

主要解决:在有多种算法相似的情况下,使用if…else所带来的复杂度和难以维护。

何时使用:一个系统有许多类,而区分它们的只是它们直接的行为。

如何解决:将这些算法封装成一个个类,任意替换。

关键代码:实现同一个接口。

1,诸葛亮的锦囊妙计,每一个锦囊就是一个策略。

2,旅行的出行方式,选择骑自行车、坐汽车,每一种旅行方式就是一个策略。

3,Java AWT中的LayoutManage。

策略模式(2)

创建一个定义活动的Strategy接口和实现了Strategy接口的实体策略类。Context是一个使用了某种策略的类。

StrategyPatternDemo,我们的演示类使用Context和策略对象来演示Context在它所配置或使用的策略改变时的行为变化。

[img3]

public interface Strategy { public int doOperation(int num1, int num2); }

public class OperationAdd implements Strategy { @Override public int doOperation(int num1, int num2) { return num1 + num2; } }

public class OperationSubtract implements Strategy { @Override public int doOperation(int num1, int num2) { return num1 - num2; } }

public class OperationMultiply implements Strategy { @Override public int doOperation(int num1, int num2) { return num1 * num2; } }

public class Context { private Strategy strategy; public Context(Strategy strategy) { this.strategy = strategy; } public int executeStrategy(int num1, int num2) { return strategy.doOperation(num1, num2); } }

public class StrategyPatternDemo { public static void main(String[] args) { //使用Context来查看当它改变策略Strategy时的行为变化 Context context = new Context(new OperationAdd()); System.out.println("1+2=" + context.executeStrategy(1, 2));//1+2=3 Context context1 = new Context(new OperationSubtract()); System.out.println("1-2=" + context1.executeStrategy(1, 2));//1-2=-1 Context context2 = new Context(new OperationMultiply()); System.out.println("1*2=" + context2.executeStrategy(1, 2));//1*2=2 } }

策略模式(3)

优点:1,算法可以自由切换。2,避免使用多重条件判断。3,拓展性良好。

缺点:1,策略类会增多。2,所有策略类都需要对外暴露。

1,如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。

2,一个系统需要动态地在几种算法中选择一种。

3,如果一个对象有很多地行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。

3.3,工厂模式

工厂模式(1)

工厂模式(Factory Pattern)是Java中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

工厂模式(2)

意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。

主要解决:主要解决接口选择的问题。

何时使用:我们明确地计划不同条件下创建不同实例时。

如何解决:让其子类实现工厂接口,返回的也是一个抽象的产品。

关键代码:创建过程在其子类执行。

1,你需要一辆汽车,可以直接从工厂里面拿货,而不用去管汽车是怎么做出来的,以及这个汽车里面的具体实现。

2,Hibernate换数据库只需要换方言和驱动即可。

工厂模式(3)

创建一个Shape接口和实现Shape接口的实体类。下一步是定义工厂类ShapeFactory。

FactoryPatternDemo类使用ShapeFactory来获取Shape对象。它将向ShapeFactory传递信息(Circle/Rectangle/Square),以便获取它所需对象的类型。

[img4]

public interface Shape { void draw(); }

//圆形 public class Circle implements Shape { @Override public void draw() { System.out.println("画圆形 Inside Circle::draw() method."); } }

//正方形 public class Square implements Shape { @Override public void draw() { System.out.println("画正方形 Inside Square::draw() method."); } }

//矩形 public class Rectangle implements Shape { @Override public void draw() { System.out.println("画长方形 Inside Rectangle::draw() method."); } }

public class ShapeFactory { //在简单工厂模式中用于被创建实例的方法通常为静态(static)方法 //因此简单工厂模式又被称为静态工厂方法(Static Factory Method) public static Shape getShape(String shapeType) { if (shapeType == null) { return null; } if (shapeType.equalsIgnoreCase("CIRCLE")) { return new Circle(); } else if (shapeType.equalsIgnoreCase("RECTANGLE")) { return new Rectangle(); } else if (shapeType.equalsIgnoreCase("SQUARE")) { return new Square(); } return null; } }

public class FactoryPatternDemo { public static void main(String[] args) { System.out.println("----------Circle----------"); //获取Circle的对象,并调用它的 draw 方法 Shape circleShape = ShapeFactory.getShape("circle"); //调用Circle的draw方法 circleShape.draw();//画圆形 Inside Circle::draw() method. System.out.println("----------Rectangle----------"); //获取Rectangle的对象,并调用它的 draw 方法 Shape rectangleShape = ShapeFactory.getShape("rectangle"); //调用Rectangle的draw方法 rectangleShape.draw();//画长方形 Inside Rectangle::draw() method. System.out.println("----------Square----------"); //获取Square的对象,并调用它的 draw 方法 Shape squareShape = ShapeFactory.getShape("square"); //调用Square的draw方法 squareShape.draw();//画正方形 Inside Square::draw() method. } }

工厂模式(4)

优点:1,一个调用者想创建一个对象,只要知道其名称就可以了。2,拓展性高,如果想增加一个产品,只要扩展一个工厂类就可以。3,屏蔽产品的具体实现,调用者只关心产品的接口。

缺点:每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增长,在一定程度上增加了系统的复杂度,同时增加系统具体类的依赖。这并不是什么好事。

1,日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。

2,数据库访问,当用户不知道最后系统采用哪一种数据库,以及数据库可能有变化时。

3,设计一个连接服务器的框架,需要三个协议,“POP3”,“IMAP”,“HTTP”,可以把这三个作为产品类,同时实现一个接口。

工厂模式(5)

作为一个创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。

有一点需要注意的是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过new就可以完成创建的对象,无需使用工厂模式。

如果使用工厂模式,就需要引入一个工厂类,会增加系统复杂度。

3.4,代理模式

代理模式(1)

在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。

在代理模式中,我们创建拥有现有对象的对象,以便向外界提供功能接口。

代理模式(2)

意图:为其他对象提供一种代理以控制这个对象的访问。

主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。

面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来麻烦,我们可以在访问此对象时加上一个对此对象的访问层。

何时使用:想在访问一个类时做一些控制。

如何解决:增加中间层。

关键代码:实现与被代理类组合。

1,买火车票不一定在火车站买,也可以去代售点。

2,一张支票或银行存单是账户中资金的代理。支票在市场交易中用来代替现金,并提供对签发人账号上资金的控制。

3,Spring中的AOP。

代理模式(3)

我们将创建一个Image接口和实现了Image接口的实体类。ProxyImage是一个代理类,减少RealImage对象加载的内存占用。

ProxyPatternDemo类使用ProxyImage来获取要加载的Image对象,并按照需求进行显示。

[img5]

public interface Image { void display(); }

public class RealImage implements Image { private String fileName; public RealImage(String fileName) { this.fileName = fileName; loadPromDisk(fileName); } @Override public void display() { System.out.println("Displaying " + fileName); } private void loadPromDisk(String fileName) { System.out.println("Loading " + fileName); } }

public class ProxyImage implements Image { private RealImage realImage;//代理类中拥有要被代理的对象 private String fileName; public ProxyImage(String fileName) { this.fileName = fileName; } @Override public void display() { if (realImage == null) { realImage = new RealImage(fileName); } realImage.display(); } }

public class ProxyPatternDemo { public static void main(String[] args) { Image image = new ProxyImage("uml_proxy.jpg");//创建代理对象 //第一次调用,图片从磁盘加载 image.display(); System.out.println("------------------"); image.display(); /* * 输出结果 * Loading uml_proxy.jpg Displaying uml_proxy.jpg ------------------ Displaying uml_proxy.jpg * */ } }

代理模式(4)

优点:1,职责清晰。2,高扩展性。3,智能化。

缺点:1,由于在客户端和真实真题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。2,实现代理模式需要额外的工作,有些代理模式的实现非常复杂。

按职责来划分,通常有以下使用场景:1,远程代理。2,虚拟代理。3,Copy-on-Write代理。4,保护(Protect or

Access)代理。5,Cache代理。6,防火墙(Firewall)代理。7,同步化(Synchronization)代理。8,智能引用(Smart Reference)代理。

3.5,门面模式

门面模式(1)

现代软件系统都比较复杂,设计师处理复杂系统的一个常见方法便是分而治之,一个系统划分成好几个小的子系统。

如果医院作为一个子系统,按照部门职能,这个系统可以划分为挂号、门诊、划价、化验、收费、取药等。

首先病人必须先挂号,然后门诊。如果医生要求化验,病人必须首先划价,然后缴费,才可以到化验部门做化验,化验后再回到门诊室。

看病的病人要与这些部门打交道,就如同一个子系统的客户端与一个子系统的各个类打交道一样,不是一件容易的事情。

[img6]

门面模式(2)

解决这种不便的方法便是引入门面模式,医院可以设置一个接待员的位置,由接待员负责代为挂号、划价、缴费、取药等。

这个接待员就是门面模式的体现,病人只接触接待员,由接待员与各个部门打交道。

[img7]

门面模式(3)

门面模式也叫外观模式(Facade Pattern)

这种类型的设计模式属于结构型模式,它向现有的系统添加一个可以访问系统的接口,来隐藏系统的复杂性。

这种模式涉及到一个单一的类,该类提供了客户端请求的简化方法和对现有系统类方法的委托调用。

门面模式(4)

我们将创建一个Shape接口和实现了Shape接口的实体类。下一步是定义一个外观类ShapeMaker。

ShapeMaker类使用实体类来代表用户对这些类的调用。FacadePatternDemo类使用ShapeMaker类来显示结果。

[img8]

public interface Shape//抽象接口,放公共代码和定义规范的 { void draw(); }

public class Rectangle implements Shape { @Override public void draw() { System.out.println("画矩形 Inside Rectangle::draw() method."); } }

public class Circle implements Shape { @Override public void draw() { System.out.println("画圆形 Inside Circle::draw() method."); } }

public class Square implements Shape { @Override public void draw() { System.out.println("画正方形 Inside Square::draw() method."); } }

//外观类 -> 门面 public class ShapeMaker { private Shape circle; private Shape rectangle; private Shape square; public ShapeMaker() { circle = new Circle(); rectangle = new Rectangle(); square = new Square(); } public void drawCircle() { circle.draw(); } public void drawRectangle() { rectangle.draw(); } public void drawSquare() { square.draw(); } }

public class FacadePatternDemo { public static void main(String[] args) { ShapeMaker shapeMaker = new ShapeMaker(); shapeMaker.drawCircle(); shapeMaker.drawCircle(); shapeMaker.drawSquare(); /* * 输出结果: 画圆形 Inside Circle::draw() method. 画圆形 Inside Circle::draw() method. 画正方形 Inside Square::draw() method. * */ } }

Java

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:MySQl 配置InnoDB持久化的优化器统计信息
下一篇:Linux进程间通信
相关文章