JAVA学习笔记—JAVA SE(五)JAVA新特性和项目

五、Java新特性和项目

1. 常用设计原则和设计模式

1.1 常用的设计原则

1.1.1 软件开发的流程

需求分析文档、概要设计文档、详细设计文档、编码和测试、安装和调试、维护和升级

1.1.2 常用的设计原则

开闭原则(Open Close Principle)
对扩展开放对修改关闭,为了使程序的扩展性好,易于维护和升级。
里氏代换原则(Liskov Substitution Principle)
任何基类可以出现的地方,子类一定可以出现,多使用多态的方式。
依赖倒转原则(Dependence Inversion Principle)
尽量多依赖于抽象类或接口而不是具体实现类,对子类具有强制性和规范性
接口隔离原则(Interface Segregation Principle)
尽量多使用小接口而不是大接口,避免接口的污染,降低类之间耦合度。
迪米特法则(最少知道原则)(Demeter Principle)
一个实体应当尽量少与其他实体之间发生相互作用,使系统功能模块相对独立。高内聚,低耦合。
合成复用原则(Composite Reuse Principle)
尽量多使用合成/聚合的方式,而不是继承的方式。

1.2 常用的设计模式

1.2.1 基本概念

设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。
设计模式就是一种用于固定场合的固定套路。

单例设计模式懒汉式代码的优化:

public class Singleton {

    // 2.声明本类类型的引用指向本类类型的对象并使用private static关键字修饰
    private static Singleton sin = null;

    // 1.私有化构造方法,使用private关键字修饰
    private Singleton() {}

    // 3.提供公有的get方法负责将上述对象返回出去,使用public static关键字修饰
    public static /*synchronized*/ Singleton getInstance() {
        /*synchronized (Singleton.class) {
            if (null == sin) {
                sin = new Singleton();
            }
            return sin;
        }*/
        if (null == sin) {
            synchronized (Singleton.class) {
                if (null == sin) {
                    sin = new Singleton();
                }
            }
        }
        return sin;
    }
}
1.2.2 基本分类

创建型模式 - 单例设计模式、工厂方法模式、抽象工厂模式、…
结构型模式 - 装饰器模式、代理模式、…
行为型模式 - 模板设计模式、…

1.3 设计模式详解

1.3.1 单例设计模式

单例设计模式主要分为:饿汉式 和 懒汉式,懒汉式需要对多线程进行同步处理。

1.3.2 普通工厂模式

(1)基本概念
普通工厂方法模式就是建立一个工厂类,对实现了同一接口的不同实现类进行实例的创建。
(2)类图结构

image-20201030125111936

(3)主要缺点
在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,并且可能出现空指针异常。

public interface Sender {
    // 自定义抽象方法来描述发送的行为
    void send();
}

public class MailSender implements Sender {
    @Override
    public void send() {
        System.out.println("正在发送邮件...");
    }
}

public class SmsSender implements Sender {
    @Override
    public void send() {
        System.out.println("正在发送短信...");
    }
}
public class SendFactory {
    // 自定义成员方法实现对象的创建
    public Sender produce(String type) {
        //System.out.println("随便加一句打印进行测试");
        if ("mail".equals(type)) {
            return new MailSender();
        }
        if ("sms".equals(type)) {
            return new SmsSender();
        }
        return null;
    }
public class SendFactoryTest {

    public static void main(String[] args) {
		 // 缺点:代码复杂,可读性略差
        // 优点:扩展性和可维护性更强!  尤其是在创建大量对象的前提下
        // 1.声明工厂类类型的引用指向工厂类类型的对象
        //SendFactory sf = new SendFactory();
        // 2.调用生产方法来实现对象的创建
        //Sender sender = sf.produce("mail");
          // 3.使用对象调用方法模拟发生的行为
        sender.send();
        System.out.println("-------------------------------------");
        // 优点:代码简单,可读性强    在创建单个对象时有优势
        // 缺点:扩展性和可维护性略差
        Sender sender1 = new MailSender();
        sender1.send();
	}
}
1.3.3 多个工厂方法模式

(1)类图结构

image-20201030125138279(2)主要缺点
在多个工厂方法模式中,为了能够正确创建对象,先需要创建工厂类的对象才能调用工厂类中的生产方法。

public class SendFactory {
    // 自定义成员方法实现对象的创建

    public Sender produceMail() {
        return new MailSender();
    }
    public Sender produceSms() {
        return new SmsSender();
    }
}

1.3.4 静态工厂方法模式

(1)类图结构

image-20201030125158899

(2)实际意义
工厂方法模式适合:凡是出现了大量的产品需要创建且具有共同的接口时,可以通过工厂方法模式进行创建。
(3)主要缺点
工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序生产新的产品,就必须对工厂类的代码进行修改,这就违背了开闭原则。

public class SendFactory {
    // 自定义成员方法实现对象的创建

    public static Sender produceMail() {
        return new MailSender();
    }
    public static Sender produceSms() {
        return new SmsSender();
    }
}

1.3.5 抽象工厂模式

(1)类图结构

image-20201030134513746

public interface Provider {
    // 自定义抽象方法描述产品的生产行为
    Sender produce();
}

public class MailSendFactory implements Provider {
    @Override
    public Sender produce() {
        return new MailSender();
    }
}

public class SmsSendFactory implements Provider {
    @Override
    public Sender produce() {
        return new SmsSender();
    }
}

public class MailSender implements Sender {
    @Override
    public void send() {
        System.out.println("正在发送邮件...");
    }
}

public class SmsSender implements Sender {
    @Override
    public void send() {
        System.out.println("正在发送短信...");
    }
}

    public class SendFactoryTest {

    public static void main(String[] args) {

        Provider provider = new MailSendFactory();
        Sender sender2 = provider.produce();
        sender2.send();

        System.out.println("-------------------------------------");
        Provider provider1 = new PacketSendFactory();
        Sender sender3 = provider1.produce();
        sender3.send();
    }
}

1.3.6 装饰器模式

(1)基本概念
装饰器模式就是给一个对象动态的增加一些新功能,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。
(2)类图结构

image-20201030134704739

(3)实际意义
可以实现一个类功能的扩展。
可以动态的增加功能,而且还能动态撤销(继承不行)。
缺点:产生过多相似的对象,不易排错。

public interface Sourceable {
    // 自定义抽象方法
    void method();
}

public class Source implements Sourceable {
    @Override
    public void method() {
        System.out.println("素颜美可以如此之美!");
    }
}

public class Decorator implements Sourceable {
    private Sourceable source;

    public Decorator(Sourceable source) {
        this.source = source;
    }

    @Override
    public void method() {
        source.method(); // 保证原有功能不变
        System.out.println("化妆之后你会更美!");
    }
}

public class SourceableTest {

    public static void main(String[] args) {

        Sourceable sourceable = new Source();
        sourceable.method();

        System.out.println("---------------------------------------------------");
        // 接下来使用装饰类实现功能
        Sourceable sourceable1 = new Decorator(sourceable);
        sourceable1.method();

     
    }
}

1.3.7 代理模式

(1)基本概念
代理模式就是找一个代理类替原对象进行一些操作。
比如我们在租房子的时候找中介,再如我们打官司需要请律师,中介和律师在这里就是我们的代理。
(2)类图结构

image-2020103012530502

(3)实际意义
如果在使用的时候需要对原有的方法进行改进,可以采用一个代理类调用原有方法,并且对产生的结果进行控制,这种方式就是代理模式。
使用代理模式,可以将功能划分的更加清晰,有助于后期维护。
(4)代理模式和装饰器模式的比较
装饰器模式通常的做法是将原始对象作为一个参数传给装饰者的构造器,而代理模式通常在一个代理类中创建一个被代理类的对象。
装饰器模式关注于在一个对象上动态的添加方法,然而代理模式关注于控制对对象的访问。

public class Proxy implements Sourceable {
    private Source source;

    public Proxy() {
        source = new Source();
    }

    @Override
    public void method() {
        source.method();
        System.out.println("我和装饰器模式其实是不一样的!");
    }
}

public class SourceableTest {

    public static void main(String[] args) {

        Sourceable sourceable = new Source();
        sourceable.method();

        System.out.println("---------------------------------------------------");
        // 接下来使用装饰类实现功能
        Sourceable sourceable1 = new Decorator(sourceable);
        sourceable1.method();
		//代理模式实现功能
 	    System.out.println("---------------------------------------------------");
        Sourceable sourceable2 = new Proxy();
        sourceable2.method();
     
    }
}

1.3.8 模板方法模式

(1)基本概念
模板方法模式主要指一个抽象类中封装了一个固定流程,流程中的具体步骤可以由不同子类进行不同的实现,通过抽象类让固定的流程产生不同的结果。
(2)类图结构

image-20201030135254936

(3)实际意义
将多个子类共有且逻辑基本相同的内容提取出来实现代码复用。
不同的子类实现不同的效果形成多态,有助于后期维护。

public abstract class AbstractCalculator {

    // 自定义成员方法实现将参数指定的表达式按照参数指定的规则进行切割并返回计算结果  1+1  +
    public int splitExpression(String exp, String op) {
        String[] sArr = exp.split(op);
        return calculate(Integer.parseInt(sArr[0]), Integer.parseInt(sArr[1]));
    }

    // 自定义抽象方法实现运算
    public abstract int calculate(int ia, int ib);
}

public class Plus extends AbstractCalculator {
    @Override
    public int calculate(int ia, int ib) {
        return ia + ib;
    }
}
public class Minus extends AbstractCalculator {
    @Override
    public int calculate(int ia, int ib) {
        return ia - ib;
    }
}
public class AbstractCalculatorTest {

    public static void main(String[] args) {

        AbstractCalculator abstractCalculator = new Plus();
        int res = abstractCalculator.splitExpression("1+1", "\\+");
        System.out.println("最终的运算结果是:" + res); // 2
    }
}

2. 新特性

2.1 Java8的新特性

2.1.1 Java8的概述

Java8是 Java 语言的一个重要版本,该版本于2014年3月发布,是自Java5以来最具革命性的版本,这个版本包含语言、编译器、库、工具和JVM等方面的十多个新特性。

2.1.2 函数式接口

函数式接口主要指只包含一个抽象方法的接口,如:java.lang.Runnable、java.util.Comparator接口等。
Java8提供@FunctionalInterface注解来定义函数式接口,若定义的接口不符合函数式的规范便会报错。

Java8中增加了java.util.function包,该包包含了常用的函数式接口,具体如下:

接口名称方法声明功能介绍
Consumervoid accept(T t)根据指定的参数执行操作
SupplierT get()得到一个返回值
Function<T,R>R apply(T t)根据指定的参数执行操作并返回
Predicateboolean test(T t)判断指定的参数是否满足条件
2.1.3 Lambda表达式

Lambda 表达式是实例化函数式接口的重要方式,使用 Lambda 表达式可以使代码变的更加简洁紧凑。
lambda表达式:参数列表、箭头符号->和方法体组成,而方法体中可以是表达式,也可以是语句块。
语法格式:(参数列表) -> { 方法体; } - 其中()、参数类型、{} 以及return关键字 可以省略。

2.1.4 方法引用

方法引用主要指通过方法的名字来指向一个方法而不需要为方法引用提供方法体,该方法的调用交给函数式接口执行。
方法引用使用一对冒号 :: 将类或对象与方法名进行连接,通常使用方式如下:
对象的非静态方法引用 ObjectName :: MethodName
类的静态方法引用 ClassName :: StaticMethodName
类的非静态方法引用 ClassName :: MethodName
构造器的引用 ClassName :: new
数组的引用 TypeName[] :: new

方法引用是在特定场景下lambda表达式的一种简化表示,可以进一步简化代码的编写使代码更加紧凑简洁,从而减少冗余代码。

public class MethodReferenceTest {

    public static void main(String[] args) {

        // 1.使用匿名内部类的方式通过函数式接口Runnable中的方法实现对Person类中show方法的调用
        Person person = new Person("zhangfei", 30);
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                person.show();
            }
        };
        runnable.run(); // 没事出来秀一下哦

        System.out.println("-------------------------------------------------------------");
        // 2.使用lambda表达式的方式实现Person类中show方法的调用
        Runnable runnable1 = () -> person.show();
        runnable1.run(); // 没事出来秀一下哦

        System.out.println("-------------------------------------------------------------");
        // 3.使用方法引用的方式实现Person类中show方法的调用
        Runnable runnable2 = person::show;
        runnable2.run();
		}
}
2.1.5 Stream接口

案例题目:
准备一个List集合并放入Person类型的对象,将集合中所有成年人过滤出来放到另外一个集合并打印出来。
(1)基本概念
java.util.stream.Stream接口是对集合功能的增强,可以对集合元素进行复杂的查找、过滤、筛选等操作。
Stream接口借助于Lambda 表达式极大的提高编程效率和程序可读性,同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势。
(2)使用步骤
创建Stream,通过一个数据源来获取一个流。
转换Stream,每次转换返回一个新的Stream对象。
对Stream进行聚合操作并产生结果。
(3)创建方式
方式一:通过调用集合的默认方法来获取流,如:default Stream stream()
方式二:通过数组工具类中的静态方法来获取流,如:static IntStream stream(int[] array)
方式三:通过Stream接口的静态方法来获取流,如:static Stream of(T… values)
方式四:通过Stream接口的静态方法来获取流,static Stream generate(Supplier<? extends T>s)
(4)中间操作
筛选与切片的常用方法如下:

方法声明功能介绍
Stream filter(Predicate<? super T> predicate)返回一个包含匹配元素的流
Stream distinct()返回不包含重复元素的流
Stream limit(long maxSize)返回不超过给定元素数量的流
Stream skip(long n)返回丢弃前n个元素后的流

映射的常用方法如下:

方法声明功能介绍
Stream map(Function<? super T,? extends R> mapper)返回每个处理过元素组成的流
Stream flatMap(Function<? super T,? extends Stream<? extends R>> mapper)返回每个被替换过元素组成的流,并 将所有流合成一个流

排序的常用方法如下:

方法声明功能介绍
Stream sorted()返回经过自然排序后元素组成的流
Stream sorted(Comparator<? super T> comparator)返回经过比较器排序后元素组成的流

(5)终止操作
匹配与查找的常用方法如下:

方法声明功能介绍
Optional findFirst()返回该流的第一个元素
boolean allMatch(Predicate<? super T> predicate)返回所有元素是否匹配
boolean noneMatch(Predicate<? super T> predicate)返回没有元素是否匹配
Optional max(Comparator<? super T> comparator)根据比较器返回最大元素
Optional min(Comparator<? super T> comparator)根据比较器返回最小元素
long count()返回元素的个数
void forEach(Consumer<? super T> action)对流中每个元素执行操作

规约的常用方法如下:

方法声明功能介绍
Optional reduce(BinaryOperator accumulator)返回结合后的元素值

收集的常用方法如下:

方法声明功能介绍
<R,A> R collect(Collector<? super T,A,R> collector)使用收集器对元素进行处理
public class ListPersonTest {

    public static void main(String[] args) {

        // 1.准备一个List集合并放入Person类型的对象后打印
        List<Person> list = new LinkedList<>();
        list.add(new Person("zhangfei", 30));
        list.add(new Person("xiaoqiao", 17));
        list.add(new Person("zhouyu", 20));
        list.add(new Person("zhangfei", 30));
        list.add(new Person("guanyu", 35));
        list.add(new Person("liubei", 40));
        for (Person tp: list) {
            System.out.println(tp);
        }

        System.out.println("-------------------------------------------------------");
        // 2.将List集合中所有成年人过滤出来并放入另外一个集合中打印
        List<Person> list1 = new LinkedList<>();
        for (Person tp : list) {
            if (tp.getAge() >= 18) {
                list1.add(tp);
            }
        }
        for (Person tp : list1) {
            System.out.println(tp);
        }

        System.out.println("-------------------------------------------------------");
        // 3.使用Stream接口实现上述功能
        list.stream().filter(new Predicate<Person>() {
            @Override
            public boolean test(Person person) {
                return person.getAge() >= 18;
            }
        }).forEach(new Consumer<Person>() {
            @Override
            public void accept(Person person) {
                System.out.println(person);
            }
        });

        System.out.println("-------------------------------------------------------");
        // 4.使用lambda表达式对上述代码进行优化
        //list.stream().filter(person -> person.getAge() >= 18).forEach(person -> System.out.println(person));
        list.stream().filter(person -> person.getAge() >= 18).forEach(System.out::println);

        System.out.println("-------------------------------------------------------");
        // 5.实现对集合中元素通过流跳过2个元素后再取3个元素后打印
        list.stream().skip(2).limit(3).forEach(System.out::println);

        System.out.println("-------------------------------------------------------");
        // 6.实现集合中所有元素中的年龄获取出来并打印
        list.stream().map(new Function<Person, Integer>() {
            @Override
            public Integer apply(Person person) {
                return person.getAge();
            }
        }).forEach(System.out::println);

        //list.stream().map(person -> person.getAge()).forEach(System.out::println);
        list.stream().map(Person::getAge).forEach(System.out::println);

        System.out.println("-------------------------------------------------------");
        // 7.实现集合中所有元素的自然排序并打印
        list.stream().sorted().forEach(System.out::println);

        System.out.println("-------------------------------------------------------");
        // 8.判断集合中是否没有元素的年龄是大于45岁的
        boolean b1 = list.stream().noneMatch(new Predicate<Person>() {
            @Override
            public boolean test(Person person) {
                return person.getAge() > 45;
            }
        });
        System.out.println("b1 = " + b1); // true

        b1 = list1.stream().noneMatch(person -> person.getAge() > 45);
        System.out.println("b1 = " + b1); // true

        System.out.println("-------------------------------------------------------");
        // 9.按照指定的比较器规则获取集合所有元素中的最大值
        Optional<Person> max = list.stream().max(new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                return o1.getAge() - o2.getAge();
            }
        });
        System.out.println("按照年龄排序后的最大值是:" + max);

        max = list.stream().max((o1, o2) -> o1.getAge() - o2.getAge());
        System.out.println("按照年龄排序后的最大值是:" + max);

        System.out.println("-------------------------------------------------------");
        // 10.实现将集合中所有元素的年龄映射出来并进行累加后打印
        Optional<Integer> reduce = list.stream().map(Person::getAge).reduce(new BinaryOperator<Integer>() {
            @Override
            public Integer apply(Integer integer, Integer integer2) {
                return integer + integer2;
            }
        });
        System.out.println("最终所有年龄的累加和是:" + reduce); // 172

        //reduce = list.stream().map(Person::getAge).reduce(((integer, integer2) -> integer + integer2));
        reduce = list.stream().map(Person::getAge).reduce((Integer::sum));
        System.out.println("最终所有年龄的累加和是:" + reduce); // 172

        System.out.println("-------------------------------------------------------");
        // 11.实现将集合中所有元素的姓名映射出来并收集到集合中打印
        list.stream().map(Person::getName).collect(Collectors.toList()).forEach(System.out::println);
    }
}
2.1.6 Optional类

案例题目
判断字符串是否为空,若不为空则打印字符串的长度,否则打印0。
(1)基本概念
java.util.Optional类可以理解为一个简单的容器,其值可能是null或者不是null,代表一个值存在
或不存在。
该类的引入很好的解决空指针异常,不用显式进行空值检测。
(2)常用的方法

方法声明功能介绍
static Optional ofNullable(T value)根据参数指定数值来得到Optional类型的对 象
Optional map(Function<? super T,? extends U> mapper)根据参数指定规则的结果来得到Optional类 型的对象
T orElse(T other)若该值存在就返回,否则返回other的数 值。
public class OptionalTest {

    public static void main(String[] args) {

        //String str1 = "hello";
        String str1 = null;
        if (null != str1) {
            System.out.println("字符串的长度是:" + str1.length()); // 5  空指针异常
        } else {
            System.out.println("字符串为空,因此长度为0!");
        }

        System.out.println("----------------------------------------------------");
        // Java8中使用Optional类实现空值的处理
        // 1.将数据str1装到Optional对象代表的容器中
        Optional<String> optional = Optional.ofNullable(str1);
        // 2.建立映射关系  使用字符串的长度与字符串建立映射关系
        /*Optional<Integer> integer = optional.map(new Function<String, Integer>() {
            @Override
            public Integer apply(String s) {
                return s.length();
            }
        });*/
        //Optional<Integer> integer = optional.map(s -> s.length());
        Optional<Integer> integer = optional.map(String::length);
        // 3.若字符串为空则打印0,否则打印字符串的数值
        System.out.println("integer = " + integer); // Optional.empty
        System.out.println(integer.orElse(0)); // 0
    }
}

2.2 Java9的新特性

2.2.1 Java9的概述

Java9发布于2017年9月发布,带来了很多新特性,其中最主要的变化是模块化系统。
模块就是代码和数据的封装体,模块的代码被组织成多个包,每个包中包含Java类和接口,模块的数据则包括资源文件和其他静态信息。

2.2.2 模块化的使用

(1)语法格式
在 module-info.java 文件中,我们可以用新的关键词module来声明一个模块,具体如下:
module 模块名称 {
}
(2)模块化的优势
减少内存的开销。
可简化各种类库和大型应用的 开发和维护。
安全性,可维护性,提高性能。

2.2.3 钻石操作符的使用升级

在Java9中允许在匿名内部类的使用中使用钻石操作符。

2.2.4 集合工厂方法

(1)基本概念
Java9的List、Set和Map集合中增加了静态工厂方法of实现不可变实例的创建。
不可变体现在无法添加、修改和删除它们的元素。
不允许添加null元素对象。
(2)实际意义
保证线程安全:在并发程序中既保证线程安全性,也大大增强了并发时的效率。
被不可信的类库使用时会很安全。
如果一个对象不需要支持修改操作,将会节省空间和时间的开销。
可以当作一个常量来对待,并且这个对象在以后也不会被改变。

2.2.5 InputStream的增强

InputStream类中提供了transferTo方法实现将数据直接传输到OutputStream中。

public class InputStreamTest {

    public static void main(String[] args) {

        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            inputStream = new FileInputStream("d:/a.txt");
            outputStream = new FileOutputStream("d:/b.txt");
            inputStream.transferTo(outputStream); // 实现数据的复制,底层是read和write方法的调用
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != outputStream) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != inputStream) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

2.3 Java10的新特性

2.3.1 Java10的概述

Java10于2018年3月发布,改进的关键点包括一个本地类型推断、一个垃圾回收的增强。

Java10计划只是一个短期版本,因此公开更新将在六个月内结束,9月份发布的Java11将是Java的长期支持(LTS)版本,LTS版本的发布每三年发布一次。

2.3.2 局部变量类型推断

(1)基本概念
Java10可以使用var作为局部变量类型推断标识符,此符号仅适用于局部变量,增强for循环的索引,以及传统for循环的本地变量。
它不能使用于方法形式参数,构造函数形式参数,方法返回类型,字段,catch形式参数或任何其他类型的变量声明。
(2)实际意义
标识符var不是关键字,只是一个保留的类型名称。这意味着var用作变量,方法名或包名的代码不会受到影响,但var不能作为类或则接口的名字。避免了信息冗余。对齐了变量名。更容易阅读。

2.4 Java11的新特性

2.4.1 Java11的概述

Java11于2018年9月正式发布,这是 Java 大版本周期变化 后的第一个长期支持版本,非常值得关注。

2.4.2 简化的编译运行操作

在Java11中可以使用java命令一次性进行编译和运行操作。
执行源文件中的第一个类必须包含主方法。
不可以使用其它源文件中自定义的类。

2.4.3 String类新增方法
方法声明功能介绍
boolean isBlank()判断字符串是否为空或只包含空白代码点
Optional map(Function<? super T,? extends U> mapper)根据参数指定规则的结果来得到Optional类 型的对象
T orElse(T other)若该值存在就返回,否则返回other的数 值。

3. 项目案例:在线考试系统

3.1 在线考试系统

3.1.1 软件开发的流程

需求分析文档、概要设计文档、详细设计文档、编码和测试、安装和调试、维护和升级

3.1.2 软件的需求分析

在线考试系统的主要功能分析如下:
(1)学员系统
用户模块:登录、修改密码、退出
考试模块:开始考试、查询成绩、导出成绩(选)
(2)管理员系统
学员管理模块:增加学员、删除学员、修改学员、查找学员
考题管理模块:增加考题、删除考题、修改考题、查找考题、导入考题(选)

image-20201030130318282

3.1.3 软件的概要设计

在线考试系统采用C(Client客户端)/S(Server服务器)架构进行设计,具体如下:
客户端(Client) - 主要用于提供字符界面供用户选择并将处理结果显示出来。
服务器(Server) - 主要用于针对字符界面的选择实现真正业务功能的处理。
数据库(Database) - 主要用于进行数据的存取。

image-20201030130340720

3.1.4 软件的详细设计

客户端和服务器之间采用基于tcp协议的编程模型进行通信。
客户端的对象输出流连接服务器的对象输入流。
服务器的对象输出流连接客户端的对象输入流。
客户端采用消息的类型作为具体业务的代表,伴随着账户信息等一并发送给服务器。
当客户端发来的消息类型为"managerCheck"时,则表示要实现管理员账户信息的校验功能。
当客户端发来的消息类型为"userCheck"时,则表示要实现学员账户信息的校验功能。
服务器采用消息的类型作为是否校验成功的标志发送给客户端。
当客户端收到的消息类型为"success"时,则表示账户信息正确。
当客户端收到的消息类型为"fail"时,则表示账户信息错误。

image-20201030130411581

3.1.5 软件的编码流程

(1)管理员登录功能
编写基于tcp协议的服务器端,也就是初始化服务器;
编写基于tcp协议的客户端,来连接服务器;
编写客户端的字符界面并提示客户进行业务的选择;
将客户的选择和输入的相关信息通过对象输出流发送给服务器;
服务器通过对象输入流接收客户端发来的消息并进行功能处理,将处理结果发送给客户端;
客户端通过对象输入流接收服务器的处理结果并给出提示;
(2)学员管理系统的功能
当项目启动时,将文件中的所有学员账户信息全部读取出来放到一个List集合中。
客户端输入要增加学员的用户名和密码信息,通过对象输出流发送给服务器。
服务器接收客户端发来的消息,判断集合中是否存在该学员账户信息并实现具体添加功能。
服务器将增加学员功能的处理结果发送给客户端,客户端给出对应的提示。
当项目退出时,将集合中的所有学员账户信息整体写入到文件中

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值