分类

类型:
不限 游戏开发 计算机程序开发 Android开发 网站开发 笔记总结 其他
评分:
不限 10 9 8 7 6 5 4 3 2 1
原创:
不限
年份:
不限 2018 2019 2020 2021

技术文章列表

  • Java总结笔记

    面试笔试题
    jvmjvm的结构:类加载器(启动类加载器、扩展、应用)、运行时数据区、执行引擎、本地库接口
    运行时数据区:
    程序计数器、虚拟机栈、方法区、堆、本地方法栈
    类的加载过程:

    加载:根据查找路径找到相应的 class 文件然后导入;检查:检查加载的 class 文件的正确性;准备:给类中的静态变量分配内存空间;解析:虚拟机将常量池中的符号引用替换成直接引用的过程。符号引用就理解为一个标示,而在直接引用直接指向内存中的地址;初始化:对静态变量和静态代码块执行初始化工作。
    三种类加载器共同协作,使用双亲委托的方式把一个类加载到内存中,
    JVM是如何识别到该调用哪个方法的:
    调用方法的时候,jvm会把在内存中的方法区获取到这个要调用的方法的代码,并且把它们加载到栈中(入栈),在栈区中一行行的运行这些代码

    JVM是根据 类名+方法名+方法描述符(形参+返回类型) 来识别到底该调用哪一个方法的。
    我们再从JVM层面分析下,JVM里面是通过哪里指令来实现方法的调用的:

    invokestatic:调用静态方法invokeinterface:调用接口方法(多态)invokespecial:调用非静态私有方法、构造方法(包括super)invokevirtual:调用非静态非私有方法(多态)invokedynamic:动态调用(Java7引入的,第一次用却是在Java8中,用在了Lambda表达式和默认方法中,它允许调用任意类中的同名方法,注意是任意类,和重载重写不同)(动态 ≠ 多态)

    那么这些指令又是怎么来调用方法的呢?(invokedynamic和这些有点不一样,稍后单独解释下)
    在编译的过程中,JVM并不知道目标方法的具体内存地址,此时编译器会用”符号引用”来表示该方法(加载阶段)。当JVM进行到“解析”阶段的时候,这些引用会被替换为直接引用,这个时候就知道需要去哪里调用到方法了!
    对于静态绑定的方法,直接引用就是直接指向方法的指针,而对于动态绑定的方法,直接引用其实指向方法表中的一个索引。
    方法表是一个数组,每个数组元素指向一个当前类及其父类中非private的实例方法,样子如下所示:

    由于动态绑定相比于静态绑定,在寻找方法时要出多好一个内存解析的动作,例如获取调用者类型,获取方法表,获取方法表的索引值等等,还是有点开销的,虽然这些开销是必须的。所以JVM中引入了一些优化的技术: 内存缓联+方法内联。
    动态绑定调用优化技术:
    内存缓联:
    说白了就是缓存,缓存的调用者的类型已经改类型所对应的目标方法,如果以后执行时,直接拿寻找缓存中对应的方法,不然就要去方法表中查询了(类似与操作系统的pagecache)。(非动态绑定的算法是不需要缓存的)
    方法内联:
    任何一个方法调用,除非它被内联,否则都会有固定开销。这些开销主要是保存程序在改方法中的执行位置,以及新建、压入和弹出新方法所使用中的栈帧。对于一些非常简单的方法例如getter/setter,这部分开销耗费的时间可能超过方法本身。
    方法内联就是将调用函数的表达式直接用函数的函数体来直接替换,这样就减少了寻址开销,虽然可能会增加目标程序的代码量(增加空间开销),这是一个很重要的优化方法,由JVM来实现。

    独特的Invokedynamic:
    前面四种指令是吧符号引用替换为直接引用,直接指到内存中的地址,也就是说他们需要指导方法所在类名,方法名以及方法描述符。但是像Lambda表达式这种,尤其是scala,只要方法描述符(又叫方法签名)对上了,可以使用引用到其他类中的方法(任意Function类中的apply)。
    也就是说需要有这么一个指令,可以允许程序将调用点连接到任意符合条件的方法上,这就需要用到反射机制了,但是反射调用有反复权限检查的开销,所以JVM在底层引入了轻量级的方法句柄(MethodHandle)的概念,并由JVM对它做一些优化(如方法内联)。
    可以这么理解Reflection API设计失误诶Java语言服务的,而MethodHandle则为所有运行在JVM之上的语言服务。
    GC是什么? 为什么要有GC?GC是垃圾收集的意思(Gabage Collection),内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的显示操作方法。
    GC算法:标记清楚法、标记整理法、复制算法、
    JDK 和 JRE 有什么区别

    JDK:Java Development Kit 的简称,java 开发工具包,提供了 java 的开发环境和运行环境。JRE:Java Runtime Environment 的简称,java 运行环境,为 java 的运行提供了所需环境。
    能否控制垃圾回收器去回收内存垃圾:
    不行,可以调用API中的相应方法去建议垃圾回收器去回收,但不一定回真的回收。
    java.lang.System.gc();java.lang.Runtime.gc();
    面向对象创建对象有几种方法:

    new:反射:序列化和反序列化:clone
    “==”和“equals”的区别:

    ”==“

    基本类型:比较的是值是否相同;引用类型:比较的是引用是否相同;
    “equals”

    引用类型比较:对象的地址值,String 和 Integer重写了 equals方法,变成了值得比较

    面向对象的特征:

    分装:避免重复性使用代码。定义方法,可以视为对一段同样作用代码的封装,来降低代码重复性定义一个类,接口等等,也可以视为对具有相同特性的代码块的封装。同样关键字enum,也可视为对Enum类的封装。
    继承:继承主要实现重用代码,节省开发时间。无论继承类还是实现接口,都是因为父类(接口)中有一段重复性代码(属性或功能)同样适用于子(实现)类,为避免重复封装这段代码,所以才有了继承的体现。
    多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。
    重载(overload)和重写(override)是实现多态的两种主要方式。


    接口多态性。继承多态性。通过抽象类实现的多态性。


    抽象类和接口的区别:

    默认方法实现:抽象类可以有默认的方法实现;接口不能有默认的方法实现。
    实现:抽象类的子类使用 extends 来继承;接口必须使用 implements 来实现接口。接口不可以实现接口,可以继承一个或多个接口,
    构造函数:抽象类可以有构造函数;接口不能有。main 方法:抽象类可以有 main 方法,并且我们能运行它;接口不能有 main 方法。实现数量:类可以实现很多个接口;但是只能继承一个抽象类。访问修饰符:接口中的方法默认使用 public 修饰;抽象类中的方法可以是任意访问修饰符。
    普通类和抽象类的区别:

    普通类不能包含抽象方法,抽象类可以包含抽象方法。抽象类不能直接实例化,普通类可以直接实例化。
    final 和 finally 、finalize 的区别:

    final 修饰的类叫最终类,该类不能被继承。final 修饰的方法不能被重写。final 修饰的变量叫常量,常量必须初始化,初始化之后值就不能被修改。如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。finally后边的代码块,不管try-catch会不会报错,都会执行finalize: 是 Object 类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法。
    重写与重载:

    重载:

    发生在同一个类中相同的方法名参数列表不同不看返回值,如果出现了只有返回值不同的“重载”,是错的。

    重写:

    发生在子类与父类中相同的方法名相同的参数列表返回值相同 或者 子类方法的返回值是父类方法返回值类型的子类访问修饰符相同 或者 子类方法的修饰符范围 大于 父类抛出的异常相同 或者 子类方法抛出的异常 小于父类
    this 和 super:

    this 调用的是子类的构造器、属性、方法super 调用的是父类的构造器、属性、方法
    基本数据类型:int short long double char boolean float byte
    String 是引用类型
    java的类型转换:

    java的类型转换:

    小的类型自动转化为大的类型

    强制类型转换:

    java 中操作字符串都有哪些类:
    String、StringBuffer、StringBuilderString 和 StringBuffer、StringBuilder 的区别在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象,而 StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。StringBuffer 和 StringBuilder 最大的区别在于,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。字符串反转:使用 StringBuilder 或者 stringBuffer 的 reverse() 方法
    &和&&的区别&是位运算符,表示按位与运算,&&是逻辑运算符,表示逻辑与(and)
    &既可以作为二进制数字的位运算符,也可以作为布尔表达式中的逻辑运算符,但是作为逻辑运算符的时候,&并没有&&符合的那种短路的功能。
    请你解释什么是值传递和引用传递?
    值传递是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量.引用传递一般是对于对象型变量而言的,传递的是该对象地址的一个副本, 并不是原对象本身 。 所以对引用对象进行操作会同时改变原对象.一般认为,java内的传递都是值传递.
    集合Java容器有哪些
    ​ Collection map
    List set map
    Arraylist HashSet HashMap
    linkedList LinkedHashSet TreeMap(二叉树排序)
    Vector TreeSet(可排序) Hashtable
    ​ ConcurrentHashMap
    Collection 和 Collections 有什么区别?

    Collection 是一个集合接口,它提供了对集合对象进行基本操作的通用接口方法,所有集合都是它的子类,比如 List、Set 等。Collections 是一个包装类,包含了很多静态方法,不能被实例化,就像一个工具类,比如提供的排序方法: Collections. sort(list)。
    List、Set、Map 之间的区别是什么?

    List 有序可重复Set 无序不可重复(使用hashcode和equals方法判断当前对象和集合中的对象是否重合)Map 通过键值对存放数据、k值必须唯一,value值可以重复
    Java 中的 HashMap 的工作原理是什么?
    Java 中的 HashMap 是以键值对(key-value)的形式存储元素的。HashMap 需要一个 hash 函数,它使用 hashCode()和 equals()方法来向集合添加和检索元素。当调用 put()方法的时候,HashMap 会计算 key 的 hash 值,然后把键值对存储在集合中合适的索引上。如果 key已经存在了,value 会被更新成新值。HashMap 的一些重要的特性是它的容量(capacity),负载因子(load factor)和扩容极限(threshold resizing)。
    HashMap 和 Hashtable 有什么区别?

    存储:HashMap 允许 key 和 value 为 null,而 Hashtable 不允许。线程安全:Hashtable 是线程安全的,而 HashMap 是非线程安全的。推荐使用:在 Hashtable 的类注释可以看到,Hashtable 是保留类不建议使用,推荐在单线程环境下使用 HashMap 替代,如果需要多线程使用则用 ConcurrentHashMap 替代。
    如何决定使用 HashMap 还是 TreeMap?
    对于在 Map 中插入、删除、定位一个元素这类操作,HashMap 是最好的选择,因为相对而言 HashMap 的插入会更快,但如果你要对一个 key 集合进行有序的遍历,那 TreeMap 是更好的选择。
    实现数组和 List 之间的转换?

    数组转 List:使用 Arrays. asList(array) 进行转换。List 转数组:使用 List 自带的 toArray() 方法。
    数组(Array)和列表(ArrayList)有什么区别?什么时候应该使用Array而不是ArrayList?
    Array 可以包含基本类型和对象类型,ArrayList 只能包含对象类型。
    Array 大小是固定的,ArrayList 的大小是动态变化的。
    ArrayList 提供了更多的方法和特性,比如:addAll(),removeAll(),iterator()等等。
    Array只有属性没有方法可以调用。
    对于基本类型数据,集合使用自动装箱来减少编码工作量。但是,当处理固定大小的基本数据类型的时候,这种方式相对比较慢。
    ArrayList 和 LinkedList 的区别是什么?

    ArrayList内部使用数组来实现List集合的特点,所以在使用访问元素的时候可以使用数组自带的下标来访问元素,效果比高。但是在ArrayList中插入元素的话,其他元素就会要移动位置,这种操作效率就会低一些
    LinkedList内部使用链表来实现List集合的特点,所以在LinkList中进行元素插入的时候,效率比较高。但是如果要使用下标来访问元素的时候,LinkedList的效率就会比较低,因为链表中本来没有下标,它的这个下标是模拟出来的。(Arraylist中的下标是数组自带的)

    ArrayList数组实现,通过下标访问效率高,在集合的中间位置插入数据效率低。
    LinkedList链表实现,通过下标访问效率低,在集合的中间位置插入数据效率高。
    Vector 线程安全的集合。


    HashSet 和 TreeSet 有什么区别?
    HashSet 是由一个 hash 表来实现的,因此,它的元素是无序的。add(),remove(),contains()方法的时间复杂度是 O(1)。
    另一方面,TreeSet 是由一个树形的结构来实现的,它里面的元素是有序的。因此,add(),remove(),contains()方法的时间复杂度是 O(logn)。
    Iterator 和 ListIterator 的区别是什么?
    Iterator 可用来遍历 Set 和 List 集合,但是 ListIterator 只能用来遍历 List。
    Iterator 对集合只能是前向遍历,ListIterator 既可以前向也可以后向。
    ListIterator 实现了 Iterator 接口,并包含其他的功能,比如:增加元素,替换元素,获取前一个和后一个元素的索引,等等
    集合遍历方式

    List集合:迭代器、for循环、forEach(jdk1.5以上)
    set集合:迭代器、增强的for循环、forEach
    map集合:

    增强的for循环迭代器 (遍历过程中删除元素推荐使用)entrySet遍历jdk1.8之后map集合可以用forEach加Lambda表达式的方式遍历

    keySet方法 取出所有key值
    values方法 取出所有value值
    entrySet方法 取出所有的键值对


    Set里的元素是不能重复的,那么用什么方法来区分重复与否呢?当你把对象加入HashSet时,HashSet会先计算对象的hashcode值来和其他已经加入的对象的hashcode值作比较,如果没有相符的hashcode,HashSet会假设对象没有重复出现。但是如果发现有相同hashcode值的对象,这时会调用equals()方法来检查hashcode相等的对象是否真的相同。如果两者相同,HashSet就不会让其加入操作成功。
    如何往set集合中添加重复的对象:
    重写equals方法,让equals永远等于true
    TreeMap底层实现:
    TreeMap 的实现就是红黑树数据结构,也就说是一棵自平衡的排序二叉树,这样就可以保证当需要快速检索指定节点。
    io流 Java 中 IO 流分为几种

    按功能来分:输入流(input)、输出流(output)。
    按类型来分:字节流和字符流。

    字节流:继承自InputStream和OutputStream
    字符流:继承自InputStreamReader和OutputStreamWriter

    管道流:有四种管道流,PipedInputStream,PipedOutputStream,PipedReader和PipedWriter。在多个线程或进程中传递数据的时候管道流非常有用。

    字节流和字符流的区别是:

    字节流操作的最基本的单元是字节,字符流操作的基本单位是字符字节流默认不使用缓冲区,字符流使用缓冲区。在硬盘上的所有文件都是以字节形式存在的(图片,声音,视频),而字符只在内存中才会形成。字节流按 8 位传输以字节为单位输入输出数据,字符流按 16 位传输以字符为单位输入输出数据。
    序列化、反序列化:

    序列化:对象转换为字节序列的过程。反序列化:字节序列恢复为对象的过程。
    使用可序列化、实现序列化
    把一个对象写入数据源或者从一个数据源读出来,使用可序列化,需要实现Serializable接口
    常见方法:

    flush,字符流中刷新缓存区的方法。close,关闭io流。read,读字节,write,写字节。
    OutputStream里面的write()是什么意思?write(byte b[], int off, int len)这个方法里面的三个参数分别是什么意思

    write将指定字节传入数据源Byte b[ ]是byte数组,b[off]是传入的第一个字符,b[off+len-1]是传入的最后的一个字符 ,len是实际长度
    转换处理流OutputStreamWriter 可以将字节流转为字符流
    打印出不同类型的数据到数据源

    Printwriter 可以打印各种数据类型
    BufferedReader主要是用来做什么的,它里面有那些经典的方法

    将读取的内容存在内存里面
    readLine()方法读取一行文本
    这种处理流的构造器需要传入字点流。
    BufferedReader bufferedReader = newBufferedReader(new InputStreamReader(inStream,"UTF-8"));

    拷贝
    缓冲流
    关闭流:

    一般放在finally语句块中(finally 语句一定会执行)执行。多个流互相调用只关闭最外层的流即可。
    把对象写入磁盘中,需要实现什么接口:
    ObjectInputStream,需要实现Serializable接口
    把我们控制台的输出改成输出到一个文件里面
    SetOut(printWriter,printStream)重定向
    线程线程:

    前台线程:执行线程,比如main线程
    后台线程:守护线程,比如gc(垃圾回收器)

    cpu时间片:

    操作系统为了方便管理和调度cpu的共享使用,就把cpu的使用时间划分成了一个个很短的时间段

    并行和并发有什么区别?

    并行:多个处理器或多核处理器同时处理多个任务。并发:多个任务在同一个 CPU 核上,按细分的时间片轮流(交替)执行,从逻辑上来看那些任务是同时执行。
    线程和进程的区别?

    ​ 进程是执行着的应用程序,而线程是进程内部的一个执行序列。一个进程可以有多个线程。线程又叫做轻量级进程。
    创建线程有哪几种方式?

    继承 Thread 重新 run 方法;实现 Runnable 接口;(可以处理同一资源,实现资源共享)
    线程有哪些状态?
    isAlive:判断线程是否是否存活
    stop:终止线程(过时方法,存在安全隐患)
    interrupt:打断一个线程阻塞状态
    join:t.join()方法会使所有线程(除同一时刻正在运行的其他线程)都暂停并等待t的执行完毕
    wait:交出cpu使用权
    notify:
    notifyAll:
    yield:线程让步,让出cpu执行权,不释放锁。


    新建:没有调用start方法的时候
    可运行:调用start方法的之后
    运行:抢到cpu执行权
    阻塞

    等待锁(等待其他对象释放锁之后)无线等待(对象调用wait() 方法,等待去对象调用notify()或者interrupt方法打断、其他对象调用join方法)有限等待(sleep方法、join方法被其他线程独享调用)
    死亡

    sleep() 和 wait() 有什么区别?
    类的不同:sleep() 来自 Thread,wait() 来自 Object。释放锁:sleep() 不释放锁;wait() 释放锁。用法不同:sleep() 时间到会自动恢复;wait() 可以使用 notify()/notifyAll()直接唤醒。
    notify()和 notifyAll()有什么区别?notifyAll()会唤醒所有的线程,notify()之后唤醒一个线程。notifyAll() 调用后,会将全部线程由等待池移到锁池,然后参与锁的竞争,竞争成功则继续执行,如果不成功则留在锁池等待锁被释放后再次参与竞争。而 notify()只会唤醒一个线程,具体唤醒哪一个线程由虚拟机控制
    线程同步的方法
    wait():使一个线程处于等待状态,并且释放所持有的对象的lock。sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。notityAll():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。
    线程同步的方法:

    多线程并发访问(同时争夺执行权去执行代码),然后变成了一个一个线程按照顺序依次访问

    synchronized可以代码块进行加锁,锁的是这个代码中的代码,加锁之后,这个代码块中的代码只能让当前线程来执行,其他线程是不允许进来执行的,除非当前线程把这个代码块给执行完了或者把锁给释放了,那么其他线程才有机会进来执行。
    锁:

    静态方法:当前类对象非静态方法:this代码块:手动指定一个对象
    线程池:可以最大程度的利用线程,节省资源消耗,它通过利用已有的线程多次循环执行多个任务从而提高系统的处理能力。
    通过java.util.concurrent.ThreadPoolExecutor类来创建线程池,一个任务通过 execute(Runnable)方法被添加到线程池,任务就是一个 Runnable类型的对象,任务的执行方法就是Runnable类型对象的run()方法。
    死锁
    线程t1拿着t2线程需要的锁不释放,线程t2拿着t1线程需要的锁不释放。

    ​ 两个进程都在等待对方执行完毕才能继续往下执行的时候就发生了死锁。结果就是两个进程都陷入了无限的等待中。
    预防死锁
    设置加锁顺序、设置加锁时限、死锁检测、设置加锁顺序
    Synchronized和locksynchronized是Java的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。JDK1.5以后引入了自旋锁、锁粗化、轻量级锁,偏向锁来有优化关键字的性能。
    Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。
    synchronized 底层实现原理synchronized 是由一对 monitorenter/monitorexit 指令实现的,monitor 对象是同步的基本实现单元。在 Java 6 之前,monitor 的实现完全是依靠操作系统内部的互斥锁,因为需要进行用户态到内核态的切换,所以同步操作是一个无差别的重量级操作,性能也很低。但在 Java 6 的时候,Java 虚拟机 对此进行了大刀阔斧地改进,提供了三种不同的 monitor 实现,也就是常说的三种不同的锁:偏向锁(Biased Locking)、轻量级锁和重量级锁,大大改进了其性能。
    单例模式:单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例
    饿汉模式:(线程安全)

    该模式在类被加载时就会实例化一个对象

    ublic class Person { //饿汉式单例 private static Person person = new Person(); private Person(){} public static Person getInstance(){ return person; }}
    懒汉模式:(线程不安全)

    在需要对象时才会生成单例对象(比如调用getInstance方法)

    public class User { //懒汉式单例,只有在调用getInstance时才会实例化一个单例对象 public static User user; private User(){ } public static User getInstance(){ if(user==null){ //step 1. user = new User(); //step 2 } return user; }}
    异常error和exception有什么区别?

    error 表是一种比较严重的错误情况,这种情况发生之后,程序自身无法处理不能恢复运行,那么就会停止运行。比如说内存溢出。exception 是我们已经提前知道的可能会发生的异常情况,这种情况发生之后,如果程序中对异常进行了处理(try-catch),那么程序还可以恢复到正常情况继续往下运行。
    throw 和 throws 的区别

    throw:是真实抛出一个异常。throws:是声明可能会抛出一个异常。
    try-catch-finally 中哪个部分可以省略?try-catch-finally 其中 catch 和 finally 都可以被省略,但是不能同时省略,也就是说有 try 的时候,必须后面跟一个 catch 或者 finally。
    常见的异常类有哪些?
    运行时异常
    ArithmeticException 算数运算异常,由于除数为0引起的异常; ClassCastException 类型转换异常,不存在子父类关系;ArrayStoreException 由于数组存储空间不够引起的异常; NullPointerException 空指针异常;IndexOutOfBoundsExcention 数组下标越界;ConcurrentModificationException 并发修改异常;NoSuchElementException 找不到元素异常;
    编译时异常:
    OException 输入输出流异常 FileNotFoundException 文件找不到的异常 ClassNotFoundException 类找不到异常 DataFormatException 数据格式化异常 NoSuchFieldException 没有匹配的属性异常 NoSuchMethodException 没有匹配的方法异常 SQLException 数据库操作异常 TimeoutException 执行超时异常

    网络:HTTP状态码
    HTTP状态码2xx,3xx,4xx,5xx分别是什么意思?这个是最基本的了,这个得熟练掌握,如果这个状态码都分不清,基本功就很弱了,印象分会大打折扣!

    200 请求已成功,请求所希望的响应头或数据体将随此响应返回。201 请求已经被实现,而且有一个新的资源已经依据请求的需要而建立,且其 URI 已经随Location 头信息返回202 服务器已接受请求,但尚未处理301 (永久移动) 请求的网页已永久移动到新位置。 服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。302 (临时移动) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。303 (查看其他位置) 请求者应当对不同的位置使用单独的 GET 请求来检索响应时,服务器返回此代码。304 (未修改) 自从上次请求后,请求的网页未修改过。 服务器返回此响应时,不会返回网页内容。305 (使用代理) 请求者只能使用代理访问请求的网页。 如果服务器返回此响应,还表示请求者应使用代理。307 (临时重定向) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。401 当前请求需要用户验证。如果当前请求已经包含了 Authorization 证书,那么401响应代表着服务器验证已经拒绝了那些证书403 服务器已经理解请求,但是拒绝执行它。与401响应不同的是,身份验证并不能提供任何帮助,而且这个请求也不应该被重复提交404 请求失败,请求所希望得到的资源未被在服务器上发现500 服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。一般来说,这个问题都会在服务器的程序码出错时出现。501 服务器不支持当前请求所需要的某个功能。当服务器无法识别请求的方法,并且无法支持其对任何资源的请求。502 作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应。503 由于临时的服务器维护或者过载,服务器当前无法处理请求。这个状况是临时的,并且将在一段时间以后恢复。
    请求方式:

    get:请求指定的页面信息,并返回实体主体post:向指定资源提交数据进行处理请求,数据包含在请求体中put:从客户端向服务器端传送的数据取代指定的文档的内容delete:请求服务器删除指定的页面head:类似于get请求,只不过返回的响应中没有具体的内容,用于报头。connect:http/1.1中预留给能够将连接改为管道方式的代理服务器。options:允许客户端查看服务器的性能trace:回显服务器收到的请求,主要用于测试或诊断patch:是对put方法的补充,用来对一直资源进行局部更新
    http、tcp协议:

    HTTP协议即超文本传送协议、 HTTP连接最显著的特点是客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接,从建立连接到关闭连接的过程称为“一次连接”。
    在HTTP 1.1中则可以在一次连接中处理多个请求,并且多个请求可以重叠进行,不需要等待一个请求结束后再发送下一个请求。
    TCP协议是一个面向连接的、可靠的协议。 建立起一个TCP连接需要经过“三次握手”(请求,确认,建立连接)。

    TPC/IP协议是传输层协议,主要解决数据如何在网络中传输,而HTTP是应用层协议,主要解决如何包装数据。所以,HTTP协议是建立在TCP/IP之上的一个协议。
    三次握手:
    ​ (1)第一次握手:Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认。​ (2)第二次握手:Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。​ (3)第三次握手:Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了。
    反射Java反射机制的作用?
    (1)在运行时判断任意一个对象所属的类。
    (2)在运行时判断任意一个类所具有的成员变量和方法。
    (3)在运行时任意调用一个对象的方法。
    (4)在运行时构造任意一个类的对象
    什么是反射?
    反射就是动态加载对象,并对对象进行剖析。在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法,这种动态获取信息以及动态调用对象方法的功能成为Java反射机制。
    什么是 java 序列化?什么情况下需要序列化?
    简单说就是为了保存在内存中的各种对象的状态(也就是实例变量,不是方法),并且可以把保存的对象状态再读出来。
    什么情况下需要序列化:a)当你想把的内存中的对象状态保存到一个文件中或者数据库中时候;b)当你想用套接字在网络上传送对象的时候;c)当你想通过RMI传输对象的时候;
    对象拷贝为什么要使用克隆?
    克隆的对象可能包含一些已经修改过的属性,而 new 出来的对象的属性都还是初始化时候的值,所以当需要一个新的对象来保存当前对象的“状态”就靠克隆方法了。
    如何实现对象克隆?

    实现 Cloneable 接口并重写 Object 类中的 clone() 方法。实现 Serializable 接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆。
    深拷贝和浅拷贝区别是什么?

    浅克隆:当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。深克隆:除了对象本身被复制外,对象所包含的所有成员变量也将复制
    算法:将数组进行从小到大排序 {8,2,12,6,5,16}选择排序:
    int len = arr.length; //最小值当前的位置 int now_index; //最小值应用到什么位置 int should_index; for (int i=0;i<len-1;i++) { should_index = i; now_index = i; for (int j=i+1;j<len;j++) { if (arr[j]<arr[now_index]) { now_index = j; } } if (now_index != should_index) { int temp = arr[now_index]; arr[now_index] = arr[should_index]; arr[should_index] = temp; } } for (int num : arr) { System.out.println(num); }
    冒泡排序:
    int[] arr = {5,9,8,4,6}; for (int i=0;i<arr.length;i++) { for (int j = 0; j < arr.length-i-1; j++) { if (arr[j]>arr[j+1]) { int temp=arr[j]; arr[j]=arr[j+1]; arr[j+1]=temp; } } } for (int num : arr) { System.out.println(num); }
    插入排序:
    public void test1(){ int[] arr = {2,1,4,9,8,5,3}; System.out.println(Arrays.toString(arr)); //排序操作 //每轮操作完核心目标: // 1 拿到右边当前要操作的数据 // 2 找出左边一个合适的插入位置 // 3 最后把右边当前操作的数据,插入到左边合适的位置 // 当前在右边拿到的第一个要进行操作的数据 int currentValue; // 需要把数据在左边插入的位置 int insertPosition; //外层循环,控制比较的轮数 //同时,变量i的值,还是每一轮我们要操作的右边第一个数字的下标 for(int i=1;i<arr.length;i++){ //提前保存好我们当前要操作的值 currentValue = arr[i]; //假设当前变量i的值就是要插入的位置,因为这个数据有可能是原位置不动的。 insertPosition = i; //内存循环,控制每轮比较的次数,以及比较的顺序 //同时,变量j的值,还是左边数据中从大到小的下标值 //例如:1 2 4 8 9 | 5 3 这个时候 //我们拿着数字5 要依次和左边的9 8 4 2 1 比较 //9 8 4 2 1的下标顺序就是 3 2 1 0 ,这就是j的值的变化规律 for(int j=i-1;j>=0;j--){ //每次比较,如果发现arr[j]比当前要操作的数字大 if(arr[j]>currentValue){ //就把这个大的数字往后移动一个位置,就是往后赋值 arr[j+1] = arr[j]; //然后记录一下这个位置,因为这个位置很可能是要插入的位置,到底是不是这个位置,需要和下一个数字比较后才知道 insertPosition = j; }else{ //如果发现一个比currentValue值还小的值,那么这个值的上一个比较的位置就是我们要找的插入的位置,结束当前循环 break; } } //内层循环结束后,我们把当前要操作的值currentValue,插入到指定位置insertPosition,如果insertPosition和当前i值相等,说明当前操作的这个值currentValue是不需要移动的。 if(insertPosition != i){ //进行值的插入 //把当前右边第一个值,插入到左边合适的位置 arr[insertPosition] = currentValue; } System.out.println("\t"+Arrays.toString(arr)); } System.out.println(Arrays.toString(arr)); }
    素数:
    金字塔:
    九九乘法表:
    #{}:表示一个占位符号,通过#{}可以实现preparedStatement向占位符中设置值,自动进行java类型和jdbc类型转换,#{}可以有效防止sql注入。 #{}可以接收简单类型值或pojo属性值。 如果parameterType传输单个简单类型值,#{}括号中可以是value或其它名称。
    “%”#{name}”%”
    ${}:表示拼接sql串,通过${}可以将parameterType 传入的内容拼接在sql中且不进行jdbc类型转换, ${}可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,${}括号中只能是value。
    0 留言 2021-02-24 19:47:58 奖励50点积分
  • C++利器:可以动态联编的虚拟函数

    一、虚函数的介绍与使用对于C++语言来说,程序在执行类成员函数时(跳转到相应的函数地址),有两种方式,一种是静态联编(static binding),意思就是程序在编译时就知道该函数的地址;另一种是动态联编(dynamic binding),意思是就算代码在编译后,程序还是不知道相应的函数地址,得到程序执行到这个地方,程序才能得到正确的函数地址。那么为什么需要动态联编呢?考虑这样的情况:比方说有一种类叫做宠物,狗、猫、兔子、金鱼、乌龟等都是它的子类,那么当我们定义宠物类的时候,应该定义一个吃饭的功能,因为宠物都应该吃饭。这样的话,我们把程序当成小明,小明要领养什么宠物,只有他才知道(程序执行时才能确定),宠物或许是狗,或许是兔子。可是我们不能给狗喂萝卜,兔子也不吃骨头。所以我们在定义宠物类的时候,就应该把吃饭这个类成员函数,声明称一个虚函数。
    先用代码来做一个简单的例子:
    #include <iostream>using namespace std;class Pet//这个是我们的基类:宠物类{public: char * name; int weight; int age; Pet() {}; //下面这个就是我们的虚函数 virtual void eat() { cout << "错误:我们还不能确定宠物的品种,无法喂食!" << endl; }; ~Pet() {};};class Dog : public Pet{public: Dog(char * s, int w, int a) { name = s; weight = w; age = a; } virtual void eat() //重新定义这个虚函数 { cout << "到点了,请准备好狗粮," << name << "该吃饭了。" << endl; } ~Dog() {};};class Rabbit : public Pet{public: Rabbit(char * s, int w, int a) { name = s; weight = w; age = a; } void eat() { cout << "到点了,请准备好萝卜," << name << "该吃饭了。" << endl; } ~Rabbit() {};};int main(){ int a, b, c; char s[20]; cout << "请输入1或2来创建一个生物(1是狗2是兔);然后分别输" << "入该生物的姓名、重量和年龄,用回车键分割" << endl; cin >> a; cin >> s; cin >> b; cin >> c; Pet * p; if (1 == a) { p = new Dog(s, b, c); cout << "您创建的生物是狗狗,它的名字叫做" << s << ",它今年" << c << "岁了,重大" << b << "公斤!" << "它现在被您收养了。" << endl; } else { p = new Rabbit(s, b, c); cout << "您创建的生物是兔子,它的名字叫做" << s << ",它今年" << c << "岁了,重大" << b << "公斤!" << "它现在被您收养了。" << endl; } cout << "接下来是您与宠物的日常:" << endl; for (int i = 0; i < 10; i++) { if (i < 9) cout << "您与宠物愉快的玩耍" << endl; else p->eat();//到了这里就会调用相应的虚函数,如果子类是狗类,那么调用狗类的吃饭函数,否则是兔类吃饭函数 } return 0;}
    二、虚函数的使用实境虚函数一般用在这种情况下:在菱形或更为复杂的继承体系下,由于同层子类不能互相引用或指针指向,所以一般都是使用基类的指针或引用指向派生类对象,而无需做任何强制类型转换。就像我们的上个梨子一样:由于我们不知道“小明”要养的是什么动物,所以无法确定应该调用怎样的“喂食”函数;然而我们可以创建一个基类的“宠物”指针,然后用它来指向实际中的实例对象,这样“喂食”时,就会调用对应的函数了。
    三、虚函数的工作原理虚函数的原理并不复杂,通常都是这样的:编译器在编译每一种类之后,会搜集出该类的所有函数地址,然后在某块内存(或许是代码区,或许是数据区)中,开辟出一个数组,数组中的每个元素,保存的都是该类的一个函数地址,而这个数组的头结点,就是该类的所谓虚函数表指针。然后在程序实例化该类的一个实际对象时,会偷偷的给这个对象增加一个数据成员,而这个数据成员,是一个指针,保存的就是前面说的数组地址的头结点。还是以我们上面的程序为例:编译器首先为基类宠物类创建一个地址表(虚函数地址数组),然后增添一个隐藏成员“虚函数表指针”,该成员指向这个地址表;然后对派生类比如“狗”类而言,将做同样的事:创建一个新的地址表,里面保存的是“狗类”的所有虚函数地址,然后用一个数据成员指向这个“狗”类的函数表地址;这样当程序使用这个类对象的虚函数时,会根据该虚函数在地址表中的偏移量来调用对应的函数。(但如果我们的派生类没有定义那个喂食函数,那么派生类数组地址中,对应的保存的是基类中的那个喂食函数,读者朋友如果有心,可以自己试一下)
    虚函数的使用注意事项:

    构造函数不能声明为虚函数。这是因为实例一个派生类对象时,先调用该类的构造函数,执行完毕后,再调用基类的某个构造函数。所以派生类不应该重新定义基类的构造函数
    友元函数不能声明为虚函数,这是友元函数不是类成员,而虚函数必须是类成员。如果这种规则会导致您编程时的困惑,可以试着在友元函数中调用虚函数来解决这个问题
    对于一个在继承体系下是基类或中间类的类而言,应该将它的析构函数定位为虚函数。考虑这样一种情况:A是基类,B是继承类,其中B比A多出了一个在堆中new出的字符串内容,假如有以下代码【A * p = b;】,其中那个b是B类的一个实例对象。当我们delete这个p指向的对象时,如果A不是虚析构函数,那么这将会导致内存泄漏,因为在B中的析构函数才会释放字符串占用的内存,所以说应该将A这种的类的析构函数定位为虚函数。【另外还有一种新手常见的错误,考虑这样一种继承体系,B继承自A,B内有虚函数,A内没有,新手犯得错误就是,先实例化一个B类对象,然后再做出一个A类指针指向这个B实例对象,最后delete这个指针时,程序崩溃了。。。其实原因很简单,因为B中有一个虚函数表指针,而A没有,所以当把B的对象“变成”A类型时,不应该包含这个虚函数表指针!如果我们的编译器是把虚函数表指针放在开头部分的话,那么当A指针指向B对象时,会漫过去这个虚表指针,最后当delete这个指针时,不崩溃才怪,因为malloc的返回值和free的传入值不一样嘛。所以如果子类中有虚函数时,我们应该在父类中也弄虚函数,如果确实没啥虚函数好弄的,我们可以把析构函数做成虚函数啊,总结归纳就是:把父类的析构函数声明为虚函数,是一种良好的编程习惯!】
    就像我们前面提过的一样:如果一个基类或中间类定义了某个虚函数,而在下一链中的派生类没有重新定义虚函数,那么对于这个派生类而言,它的实例对象中使用这个虚函数时,将会使用上一链的那个最新版本的虚函数
    所有虚函数的参数表应该是相同的,假如不这样,如下所示:
    class A{...virtual void fun(int i){...};}class B : public A{...virtual void fun(){...};
    那么下面的代码就会是:
    A * p = new B();p->fun(); //合法的代码p->fun(9);//非法的代码
    这是因为在B中,fun被定义为一个不接受任何参数的函数,而且隐藏了A中那个接受int参数的fun版本,而不是将fun生成具有两个函数特征标的重载版本。但是,如果返回值是基类的引用或指针,那么我们可以在重新定义下一链的派生类函数时,将返回值改为该派生类的引用或指针,这称为“返回类型协变”,英文叫做“covariance of return type”,她允许返回类型随类类型的变化而变化。
    如果某个基类或中间类的虚函数有很多重载版本,那么在下一链中的类虚函数定义中,应该要重新定义所有的重载版本。如果只定义一个或很少的虚函数,那么其它那些没有被定义的虚函数都会被隐藏掉,该派生类对象无法使用这些被隐藏掉的版本
    0 留言 2021-02-20 19:49:18 奖励36点积分
  • 获取指定进程的加载基址

    背景之前,自己写过一个进程内存分析的小程序,其中,就有一个功能是获取进程在内存中的加载基址。由于现在Windows系统引入了ASLR (Address Space Layout Randomization)机制,加载程序时候不再使用固定的基址加载。VS默认是开启基址随机化的,我们也可以设置它使用固定加载基址。至于什么是ASLR?或者ASLR有什么作用?本文就不深入探讨了,感兴趣的,可以自己私下了解了解。
    本文就是开发这样的一个小程序,使用两种方法来获取获取指定进程的加载基址。一种是使用进程模块快照方式,然后遍历加载模块并获取基址,另一种是直接调用WIN32 API函数EnumProcessModules,遍历加载模块基址。这两种方法本质上都是一样的。现在,我就把分析过程和实现方式写成文档,分享给大家。
    函数介绍CreateToolhelp32Snapshot 函数
    可以通过获取进程信息为指定的进程、进程使用的堆[HEAP]、模块[MODULE]、线程建立一个快照。
    函数声明
    HANDLE WINAPI CreateToolhelp32Snapshot( DWORD dwFlags, DWORD th32ProcessID );
    参数

    dwFlags指定快照中包含的系统内容,这个参数能够使用下列数值(常量)中的一个或多个:



    VALUE
    MEANING




    TH32CS_INHERIT
    声明快照句柄是可继承的


    TH32CS_SNAPALL
    在快照中包含系统中所有的进程和线程


    TH32CS_SNAPHEAPLIST
    在快照中包含在th32ProcessID中指定的进程的所有的堆


    TH32CS_SNAPMODULE
    在快照中包含在th32ProcessID中指定的进程的所有的模块


    TH32CS_SNAPPROCESS
    在快照中包含系统中所有的进程


    TH32CS_SNAPTHREAD
    在快照中包含系统中所有的线程




    th32ProcessID指定将要快照的进程ID。如果该参数为0表示快照当前进程。该参数只有在设置了TH32CS_SNAPHEAPLIST或者TH32CS_SNAPMODULE后才有效,在其他情况下该参数被忽略,所有的进程都会被快照。
    返回值

    调用成功,返回快照的句柄;调用失败,返回INVALID_HANDLE_VALUE 。

    Module32First 和 Module32Next 函数当我们利用函数CreateToolhelp32Snapshot()获得指定进程的快照后,我们可以利用Module32First函数来获得进程第一个模块的句柄,Module32Next函数来获得进程下一个模块的句柄。
    OpenProcess 函数
    打开一个已存在的进程对象,并返回进程的句柄。
    函数声明
    HANDLE OpenProcess( DWORD dwDesiredAccess, //渴望得到的访问权限(标志) BOOL bInheritHandle, // 是否继承句柄 DWORD dwProcessId// 进程标示符);
    参数

    dwDesiredAccess [in]访问进程对象。 此访问权限将针对进程的安全描述符进行检查。 此参数可以是一个或多个进程访问权限。如果调用者启用了SeDebugPrivilege权限,则无论安全描述符的内容如何,都会授予所请求的访问权限。bInheritHandle [in]如果此值为TRUE,则此进程创建的进程将继承该句柄。 否则,进程不会继承此句柄。dwProcessId [in]要打开的本地进程的标识符。
    返回值

    如果函数成功,则返回值是指定进程的打开句柄。如果函数失败,返回值为NULL。要获取扩展错误信息,请调用GetLastError。

    EnumProcessModules 函数
    在指定的进程中检索每个模块的句柄。要控制64位应用程序是否枚举32位模块,64位模块或两种类型的模块,请使用EnumProcessModulesEx函数。
    函数声明
    BOOL WINAPI EnumProcessModules( _In_ HANDLE hProcess, _Out_ HMODULE *lphModule, _In_ DWORD cb, _Out_ LPDWORD lpcbNeeded);
    参数

    hProcess [in]过程的句柄。lphModule [out]接收模块句柄列表的数组。cb [in]lphModule数组的大小,以字节为单位。lpcbNeeded [out]将所有模块句柄存储在lphModule数组中所需的字节数。
    返回值

    如果函数成功,返回值不为零。如果函数失败,返回值为零。 要获取扩展错误信息,请调用GetLastError。

    实现思路该程序实现进程基址的主要原理是,遍历进程里的所有加载的模块,那么,第一个加载模块的加载基址就是该进程的加载基址。
    那么,对于进程模块加载基址的遍历就有两种两种方法。一种是根据进程模块快照获取,另一种市直接调用EnumProcessModules函数获取。现在,对这两种方法的原理分别进行介绍。
    使用进程模块快照的方式获取模块基址的原理是:

    首先,使用CreateToolhelp32Snapshot 函数获取指定进程的所有模块快照。然后,根据模块快照,使用Module32First 和 Module32Next 函数进行遍历快照,并获取快照信息。其中,就包括有模块的加载基址信息。第一个模块的加载基址便是该进程的加载基址。最后,关闭上面获取的快照的句柄。
    使用EnumProcessModules函数获取模块基址的原理:

    首先,我们需要使用OpenProcess函数打开指定进程并获取进程的句柄
    然后,根据进程句柄调用EnumProcessModules函数获取进程加载的所有模块的加载基址,并保存在数组中。那么,第一个模块的加载基址便是该进程的加载基址
    关闭打开的进程句柄

    编程实现获取指定进程模块快照的方式遍历进程模块PVOID GetProcessImageBase1(DWORD dwProcessId){ PVOID pProcessImageBase = NULL; MODULEENTRY32 me32 = { 0 }; me32.dwSize = sizeof(MODULEENTRY32); // 获取指定进程全部模块的快照 HANDLE hModuleSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId); if (INVALID_HANDLE_VALUE == hModuleSnap) { ShowError("CreateToolhelp32Snapshot"); return pProcessImageBase; } // 获取快照中第一条信息 BOOL bRet = ::Module32First(hModuleSnap, &me32); if (bRet) { // 获取加载基址 pProcessImageBase = (PVOID)me32.modBaseAddr; } // 关闭句柄 ::CloseHandle(hModuleSnap); return pProcessImageBase;}
    直接使用EnumProcessModules函数获取进程模块基址PVOID GetProcessImageBase2(DWORD dwProcessId){ PVOID pProcessImageBase = NULL; //打开进程, 获取进程句柄 HANDLE hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId); if (NULL == hProcess) { ShowError("OpenProcess"); return pProcessImageBase; } // 遍历进程模块, HMODULE hModule[100] = {0}; DWORD dwRet = 0; BOOL bRet = ::EnumProcessModules(hProcess, (HMODULE *)(hModule), sizeof(hModule), &dwRet); if (FALSE == bRet) { ::CloseHandle(hProcess); ShowError("EnumProcessModules"); return pProcessImageBase; } // 获取第一个模块加载基址 pProcessImageBase = hModule[0]; // 关闭句柄 ::CloseHandle(hProcess); return pProcessImageBase;}
    程序测试我们在 main 函数中调用上述封装的函数进行测试,main 函数为:
    int _tmain(int argc, _TCHAR* argv[]){ PVOID pProcessImageBase1 = NULL; PVOID pProcessImageBase2 = NULL; pProcessImageBase1 = GetProcessImageBase1(4500); pProcessImageBase2 = GetProcessImageBase2(4500); printf("pProcessImageBase1=0x%p\npProcessImageBase2=0x%p\n", pProcessImageBase1, pProcessImageBase2); system("pause"); return 0;}
    测试结果
    我们运行程序,程序执行成功,并显示两种方法获取的进程加载基址,而且获取结果都相同。

    然后,我们使用 Process Explorer 软件查看PID为 4500 的进程的加载基址,程序基址获取正确,程序测试成功。

    总结这两种方式,本质上都是一样的,只是遍历进程加载模块所使用的方法不相同。那么,进程加载的第一个模块的加载基址,便是进程的加载基址。
    参考参考自《Windows黑客编程技术详解》一书
    3 留言 2018-12-20 12:37:03 奖励5点积分
  • Docker(二):Dockerfile 使用介绍


    很早之前就听说过Docker了,但是一直用不到,所以也没有去认真了解过是什么技术,怎么应用。最近正在研发一套系统平台,由于人手不足,只能自己硬着头皮上了,亲自设计系统架构、实现等。其中,为了系统后期方便迁移和部署,想到了Docker。于是,在网上找了一些资料学习学习。我把其中一些个人认为比较优质的博文转载保存,方便查阅。原文链接:Docker(二):Dockerfile 使用介绍

    上一篇文章Docker(一):Docker入门教程介绍了 Docker 基本概念,其中镜像、容器和 Dockerfile 。我们使用 Dockerfile 定义镜像,依赖镜像来运行容器,因此 Dockerfile 是镜像和容器的关键,Dockerfile 可以非常容易的定义镜像内容,同时在我们后期的微服务实践中,Dockerfile 也是重点关注的内容,今天我们就来一起学习它。
    首先通过一张图来了解 Docker 镜像、容器和 Dockerfile 三者之间的关系。

    通过上图可以看出使用 Dockerfile 定义镜像,运行镜像启动容器。
    Dockerfile 概念Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。
    镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile。
    Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。有了 Dockerfile,当我们需要定制自己额外的需求时,只需在 Dockerfile 上添加或者修改指令,重新生成 image 即可,省去了敲命令的麻烦。
    Dockerfile 文件格式Dockerfile文件格式如下:
    ## Dockerfile文件格式# This dockerfile uses the ubuntu image# VERSION 2 - EDITION 1# Author: docker_user# Command format: Instruction [arguments / command] ..# 1、第一行必须指定 基础镜像信息FROM ubuntu# 2、维护者信息MAINTAINER docker_user docker_user@email.com# 3、镜像操作指令RUN echo "deb http://archive.ubuntu.com/ubuntu/ raring main universe" >> /etc/apt/sources.listRUN apt-get update && apt-get install -y nginxRUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf# 4、容器启动执行指令CMD /usr/sbin/nginx
    Dockerfile 分为四部分:基础镜像信息、维护者信息、镜像操作指令、容器启动执行指令。一开始必须要指明所基于的镜像名称,接下来一般会说明维护者信息;后面则是镜像操作指令,例如 RUN 指令。每执行一条RUN 指令,镜像添加新的一层,并提交;最后是 CMD 指令,来指明运行容器时的操作命令。
    构建镜像docker build 命令会根据 Dockerfile 文件及上下文构建新 Docker 镜像。构建上下文是指 Dockerfile 所在的本地路径或一个URL(Git仓库地址)。构建上下文环境会被递归处理,所以构建所指定的路径还包括了子目录,而URL还包括了其中指定的子模块。
    将当前目录做为构建上下文时,可以像下面这样使用docker build命令构建镜像:
    docker build .Sending build context to Docker daemon 6.51 MB...
    说明:构建会在 Docker 后台守护进程(daemon)中执行,而不是CLI中。构建前,构建进程会将全部内容(递归)发送到守护进程。大多情况下,应该将一个空目录作为构建上下文环境,并将 Dockerfile 文件放在该目录下。
    在构建上下文中使用的 Dockerfile 文件,是一个构建指令文件。为了提高构建性能,可以通过.dockerignore文件排除上下文目录下不需要的文件和目录。
    在 Docker 构建镜像的第一步,docker CLI 会先在上下文目录中寻找.dockerignore文件,根据.dockerignore 文件排除上下文目录中的部分文件和目录,然后把剩下的文件和目录传递给 Docker 服务。
    Dockerfile 一般位于构建上下文的根目录下,也可以通过-f指定该文件的位置:
    docker build -f /path/to/a/Dockerfile .
    构建时,还可以通过-t参数指定构建成镜像的仓库、标签。
    镜像标签docker build -t nginx/v3 .
    如果存在多个仓库下,或使用多个镜像标签,就可以使用多个-t参数:
    docker build -t nginx/v3:1.0.2 -t nginx/v3:latest .
    在 Docker 守护进程执行 Dockerfile 中的指令前,首先会对 Dockerfile 进行语法检查,有语法错误时会返回:
    docker build -t nginx/v3 .Sending build context to Docker daemon 2.048 kBError response from daemon: Unknown instruction: RUNCMD
    缓存Docker 守护进程会一条一条的执行 Dockerfile 中的指令,而且会在每一步提交并生成一个新镜像,最后会输出最终镜像的ID。生成完成后,Docker 守护进程会自动清理你发送的上下文。 Dockerfile文件中的每条指令会被独立执行,并会创建一个新镜像,RUN cd /tmp等命令不会对下条指令产生影响。 Docker 会重用已生成的中间镜像,以加速docker build的构建速度。以下是一个使用了缓存镜像的执行过程:
    $ docker build -t svendowideit/ambassador .Sending build context to Docker daemon 15.36 kBStep 1/4 : FROM alpine:3.2 ---> 31f630c65071Step 2/4 : MAINTAINER SvenDowideit@home.org.au ---> Using cache ---> 2a1c91448f5fStep 3/4 : RUN apk update && apk add socat && rm -r /var/cache/ ---> Using cache ---> 21ed6e7fbb73Step 4/4 : CMD env | grep _TCP= | (sed 's/.*_PORT_\([0-9]*\)_TCP=tcp:\/\/\(.*\):\(.*\)/socat -t 100000000 TCP4-LISTEN:\1,fork,reuseaddr TCP4:\2:\3 \&/' && echo wait) | sh ---> Using cache ---> 7ea8aef582ccSuccessfully built 7ea8aef582cc
    构建缓存仅会使用本地父生成链上的镜像,如果不想使用本地缓存的镜像,也可以通过--cache-from指定缓存。指定后将不再使用本地生成的镜像链,而是从镜像仓库中下载。
    寻找缓存的逻辑Docker 寻找缓存的逻辑其实就是树型结构根据 Dockerfile 指令遍历子节点的过程。下图可以说明这个逻辑。
    FROM base_image:version Dockerfile: +----------+ FROM base_image:version |base image| RUN cmd1 --> use cache because we found base image +-----X----+ RUN cmd11 --> use cache because we found cmd1 / \ / \ RUN cmd1 RUN cmd2 Dockerfile: +------+ +------+ FROM base_image:version |image1| |image2| RUN cmd2 --> use cache because we found base image +---X--+ +------+ RUN cmd21 --> not use cache because there's no child node / \ running cmd21, so we build a new image here / \RUN cmd11 RUN cmd12+-------+ +-------+|image11| |image12|+-------+ +-------+
    大部分指令可以根据上述逻辑去寻找缓存,除了 ADD 和 COPY 。这两个指令会复制文件内容到镜像内,除了指令相同以外,Docker 还会检查每个文件内容校验(不包括最后修改时间和最后访问时间),如果校验不一致,则不会使用缓存。
    除了这两个命令,Docker 并不会去检查容器内的文件内容,比如 RUN apt-get -y update,每次执行时文件可能都不一样,但是 Docker 认为命令一致,会继续使用缓存。这样一来,以后构建时都不会再重新运行apt-get -y update。
    如果 Docker 没有找到当前指令的缓存,则会构建一个新的镜像,并且之后的所有指令都不会再去寻找缓存。
    简单示例接下来用一个简单的示例来感受一下 Dockerfile 是如何用来构建镜像启动容器。我们以定制 nginx 镜像为例,在一个空白目录中,建立一个文本文件,并命名为 Dockerfile:
    mkdir mynginxcd mynginxvi Dockerfile
    构建一个 Dockerfile 文件内容为:
    FROM nginxRUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.htmlvi Dockerfile
    这个 Dockerfile 很简单,一共就两行涉及到了两条指令:FROM 和 RUN,FROM 表示获取指定基础镜像,RUN 执行命令,在执行的过程中重写了 nginx 的默认页面信息,将信息替换为:Hello, Docker!。
    在 Dockerfile 文件所在目录执行:
    docker build -t nginx:v1 .
    命令最后有一个. 表示当前目录
    构建完成之后,使用 docker images 命令查看所有镜像,如果存在 REPOSITORY 为 nginx 和 TAG 是 v1 的信息,就表示构建成功。
    docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEnginx v1 8c92471de2cc 6 minutes ago 108.6 MB
    接下来使用 docker run 命令来启动容器
    docker run --name docker_nginx_v1 -d -p 80:80 nginx:v1
    这条命令会用 nginx 镜像启动一个容器,命名为docker_nginx_v1,并且映射了 80 端口,这样我们可以用浏览器去访问这个 nginx 服务器:http://192.168.0.54/,页面返回信息:

    这样一个简单使用 Dockerfile 构建镜像,运行容器的示例就完成了!
    修改容器内容容器启动后,需要对容器内的文件进行进一步的完善,可以使用docker exec -it xx bash命令再次进行修改,以上面的示例为基础,修改 nginx 启动页面内容:
    docker exec -it docker_nginx_v1 bashroot@3729b97e8226:/# echo '<h1>Hello, Docker neo!</h1>' > /usr/share/nginx/html/index.htmlroot@3729b97e8226:/# exitexit
    以交互式终端方式进入 docker_nginx_v1 容器,并执行了 bash 命令,也就是获得一个可操作的 Shell。然后,我们用<h1>Hello, Docker neo!</h1>覆盖了 /usr/share/nginx/html/index.html 的内容。
    再次刷新浏览器,会发现内容被改变。

    修改了容器的文件,也就是改动了容器的存储层,可以通过 docker diff 命令看到具体的改动。
    docker diff docker_nginx_v1 ...
    这样 Dockerfile 使用方式就为大家介绍完了,下期为大家介绍 Dockerfile 命令的详细使用。
    参考Dockerfile reference使用Dockerfile构建Docker镜像Docker镜像构建文件Dockerfile及相关命令介绍深入Dockerfile(一): 语法指南Docker — 从入门到实践
    0 留言 2021-02-24 15:24:57 奖励34点积分
  • Docker(一):Docker入门教程


    很早之前就听说过Docker了,但是一直用不到,所以也没有去认真了解过是什么技术,怎么应用。最近正在研发一套系统平台,由于人手不足,只能自己硬着头皮上了,亲自设计系统架构、实现等。其中,为了系统后期方便迁移和部署,想到了Docker。于是,在网上找了一些资料学习学习。我把其中一些个人认为比较优质的博文转载保存,方便查阅。原文链接:Docker(一):Docker入门教程

    如今Docker的使用已经非常普遍,特别在一线互联网公司。使用Docker技术可以帮助企业快速水平扩展服务,从而到达弹性部署业务的能力。在云服务概念兴起之后,Docker的使用场景和范围进一步发展,如今在微服务架构越来越流行的情况下,微服务+Docker的完美组合,更加方便微服务架构运维部署落地。
    本文详细解释介绍Docker入门相关内容,后期重点关注Docker在微服务体系中的使用。在了解Docker之前我们先考虑几个问题:1、Docker是什么?2、为什么要使用Docker,它有什么优势?带着这些问题我们来看看下面的内容。
    什么是Docker?Docker 是世界领先的软件容器平台。开发人员利用 Docker 可以消除协作编码时“在我的机器上可正常工作”的问题。运维人员利用 Docker 可以在隔离容器中并行运行和管理应用,获得更好的计算密度。企业利用 Docker 可以构建敏捷的软件交付管道,以更快的速度、更高的安全性和可靠的信誉为 Linux 和 Windows Server 应用发布新功能。
    Docker 属于 Linux 容器的一种封装,提供简单易用的容器使用接口。它是目前最流行的 Linux 容器解决方案。Docker 将应用程序与该程序的依赖,打包在一个文件里面。运行这个文件,就会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样。有了 Docker,就不用担心环境问题。
    总体来说,Docker 的接口相当简单,用户可以方便地创建和使用容器,把自己的应用放入容器。容器还可以进行版本管理、复制、分享、修改,就像管理普通的代码一样。
    Docker 最初是 dotCloud 公司创始人 Solomon Hykes 在法国期间发起的一个公司内部项目,它是基于 dotCloud 公司多年云服务技术的一次革新,并于 2013 年 3 月以 Apache 2.0 授权协议开源,主要项目代码在 GitHub 上进行维护。Docker 项目后来还加入了 Linux 基金会,并成立推动 开放容器联盟(OCI)。
    Docker 自开源后受到广泛的关注和讨论,至今其 GitHub 项目已经超过 4 万 6 千个星标和一万多个 fork。甚至由于 Docker 项目的火爆,在 2013 年底,dotCloud 公司决定改名为 Docker。Docker 最初是在 Ubuntu 12.04 上开发实现的;Red Hat 则从 RHEL 6.5 开始对 Docker 进行支持;Google 也在其 PaaS 产品中广泛应用 Docker。
    为什么要使用Docker容器除了运行其中应用外,基本不消耗额外的系统资源,使得应用的性能很高,同时系统的开销尽量小。传统虚拟机方式运行 10 个不同的应用就要起 10 个虚拟机,而Docker 只需要启动 10 个隔离的应用即可。
    具体说来,Docker 在如下几个方面具有较大的优势。
    1、更快速的交付和部署
    对开发和运维(devop)人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行。
    开发者可以使用一个标准的镜像来构建一套开发容器,开发完成之后,运维人员可以直接使用这个容器来部署代码。 Docker 可以快速创建容器,快速迭代应用程序,并让整个过程全程可见,使团队中的其他成员更容易理解应用程序是如何创建和工作的。 Docker 容器很轻很快!容器的启动时间是秒级的,大量地节约开发、测试、部署的时间。
    2、更高效的虚拟化
    Docker 容器的运行不需要额外的 hypervisor 支持,它是内核级的虚拟化,因此可以实现更高的性能和效率。
    3、更轻松的迁移和扩展
    Docker 容器几乎可以在任意的平台上运行,包括物理机、虚拟机、公有云、私有云、个人电脑、服务器等。 这种兼容性可以让用户把一个应用程序从一个平台直接迁移到另外一个。
    4、更简单的管理
    使用 Docker,只需要小小的修改,就可以替代以往大量的更新工作。所有的修改都以增量的方式被分发和更新,从而实现自动化并且高效的管理。
    Docker vs VM从下图可以看出,VM是一个运行在宿主机之上的完整的操作系统,VM运行自身操作系统会占用较多的CPU、内存、硬盘资源。Docker不同于VM,只包含应用程序以及依赖库,基于libcontainer运行在宿主机上,并处于一个隔离的环境中,这使得Docker更加轻量高效,启动容器只需几秒钟之内完成。由于Docker轻量、资源占用少,使得Docker可以轻易的应用到构建标准化的应用中。但Docker目前还不够完善,比如隔离效果不如VM,共享宿主机操作系统的一些基础库等;网络配置功能相对简单,主要以桥接方式为主;查看日志也不够方便灵活。

    Docker 在容器的基础上,进行了进一步的封装,从文件系统、网络互联到进程隔离等等,极大的简化了容器的创建和维护。使得 Docker 技术比虚拟机技术更为轻便、快捷。
    作为一种新兴的虚拟化方式,Docker 跟传统的虚拟化方式相比具有众多的优势。Docker 容器的启动可以在秒级实现,这相比传统的虚拟机方式要快得多;Docker 对系统资源的利用率很高,一台主机上可以同时运行数千个 Docker 容器。
    相关概念Docker是CS架构,主要有两个概念:

    Docker daemon: 运行在宿主机上,Docker守护进程,用户通过Docker client(Docker命令)与Docker daemon交互Docker client: Docker 命令行工具,是用户使用Docker的主要方式,Docker client与Docker daemon通信并将结果返回给用户,Docker client也可以通过socket或者RESTful api访问远程的Docker daemon

    了解了Docker的组成,再来了解一下Docker的三个主要概念:

    Docker image:镜像是只读的,镜像中包含有需要运行的文件。镜像用来创建container,一个镜像可以运行多个container;镜像可以通过Dockerfile创建,也可以从Docker hub/registry上下载。Docker container:容器是Docker的运行组件,启动一个镜像就是一个容器,容器是一个隔离环境,多个容器之间不会相互影响,保证容器中的程序运行在一个相对安全的环境中。Docker hub/registry: 共享和管理Docker镜像,用户可以上传或者下载上面的镜像,官方地址为https://registry.hub.docker.com/,也可以搭建自己私有的Docker registry。
    镜像就相当于打包好的版本,镜像启动之后运行在容器中,仓库就是装存储镜像的地方。
    Docker安装建议在linux环境下安装Docker,window环境搭建比较复杂且容易出错,使用Centos7+yum来安装Docker环境很方便。
    Docker 软件包已经包括在默认的 CentOS-Extras 软件源里。因此想要安装 docker,只需要运行下面的 yum 命令:
    yum install docker
    安装完成后,使用下面的命令来启动 docker 服务,并将其设置为开机启动:
    service docker startchkconfig docker on

    LCTT 译注:此处采用了旧式的 sysv 语法,如采用CentOS 7中支持的新式 systemd 语法,如下:

    systemctl start docker.servicesystemctl enable docker.service
    测试
    docker version
    输入上述命令,返回docker的版本相关信息,证明docker安装成功。
    Hello World下面,我们通过最简单的 image 文件”hello world”,感受一下 Docker。
    因为国内连接 Docker 的官方仓库很慢,因此我们在日常使用中会使用Docker 中国加速器。通过 Docker 官方镜像加速,中国区用户能够快速访问最流行的 Docker 镜像。该镜像托管于中国大陆,本地用户现在将会享受到更快的下载速度和更强的稳定性,从而能够更敏捷地开发和交付 Docker 化应用。
    Docker 中国官方镜像加速可通过registry.docker-cn.com访问。该镜像库只包含流行的公有镜像,私有镜像仍需要从美国镜像库中拉取。
    修改系统中docker对应的配置文件即可,如下:
    vi /etc/docker/daemon.json#添加后{ "registry-mirrors": ["https://registry.docker-cn.com"], "live-restore": true}
    运行下面的命令,将 image 文件从仓库抓取到本地。
    docker pull library/hello-world
    上面代码中,docker image pull是抓取 image 文件的命令。library/hello-world是 image 文件在仓库里面的位置,其中library是 image 文件所在的组,hello-world是 image 文件的名字。
    抓取成功以后,就可以在本机看到这个 image 文件了。
    docker images#显示结果REPOSITORY TAG IMAGE ID CREATED SIZEdocker.io/hello-world latest f2a91732366c 3 months ago 1.848 kB
    现在,运行这个 image 文件。
    docker run hello-world#显示结果Hello from Docker!This message shows that your installation appears to be working correctly....
    输出这段提示以后,hello world就会停止运行,容器自动终止。有些容器不会自动终止,因为提供的是服务,比如Mysql镜像等。
    常用命令除过以上我们使用的Docker命令外,Docker还有一些其它常用的命令
    拉取docker镜像
    docker pull image_name
    查看宿主机上的镜像,Docker镜像保存在/var/lib/docker目录下:
    docker images
    删除镜像
    docker rmi docker.io/tomcat:7.0.77-jre7 或者 docker rmi b39c68b7af30
    查看当前有哪些容器正在运行
    docker ps
    查看所有容器
    docker ps -a
    启动、停止、重启容器命令:
    docker start container_name/container_iddocker stop container_name/container_iddocker restart container_name/container_id
    后台启动一个容器后,如果想进入到这个容器,可以使用attach命令:
    docker attach container_name/container_id
    删除容器的命令:
    docker rm container_name/container_id
    删除所有停止的容器:
    docker rm $(docker ps -a -q)
    查看当前系统Docker信息
    docker info
    从Docker hub上下载某个镜像:
    docker pull centos:latestdocker pull centos:latest
    查找Docker Hub上的nginx镜像
    docker search nginx
    执行docker pull centos会将Centos这个仓库下面的所有镜像下载到本地repository。
    参考Docker — 从入门到实践Docker系列之一:入门介绍Docker 入门教程
    0 留言 2021-02-23 08:53:13
  • 基于Servlet和Ajax实现搜索框智能提示


    前段时间自己项目需要实现智能提示功能,于是在网上搜索了相关技术,发现这一篇博文介绍得较为详细,故自己排了下版面,转载到此处,方便自己今后查阅。

    一、简介搜索框相信大家都不陌生,几乎每天都会在各类网站进行着搜索。有没有注意到,很多的搜索功能,当输入内容时,下面会出现提示。这类提示就叫做搜索框的智能提示,本次就为大家介绍如何使用Servlet和Ajax来实现。主要介绍实现原理和代码的前后台实现过程。
    二、项目分析
    实现语言:java
    实现方式:Ajax异步传输
    案例:比如百度的搜索框智能提示


    理论分析:

    在搜索框输入关键字
    浏览器将关键字异步发送给服务器
    服务器经过处理,将相应的数据以JSon(xml)格式返回给客户端
    客户端接收到服务器的响应数据,解析之后使用JS操作DOM显示数据

    演示流程图分析:

    重点内容:

    数据交互采用ajax方式
    JavaScript解析数据动态展示

    三、页面开发3.1 HTML部分body内容
    <body> <div id="mydiv"> <!--输入框--> <input type="text" size="50" id="keyword" onkeyup="getMoreContents()" onblur="keywordBlur()" onfocus="getMoreContents()"/> <input type="button" value="百度一下" width="50px"> <!--内容展示区域--> <div id="popdiv"> <table id="content_table" bgcolor="#FFFAFA" border="0" cellspacing="0" cellpadding="0"> <tbody id="content_table_body"> <%--动态查询出来的数据,显示在此--%> </tbody> </table> </div> </div></body>
    3.2 CSS部分样式代码
    <style type="text/css"> #mydiv { position: absolute; left: 50%; top: 50%; margin-left: -200px; margin-top: -50px; } .mouseOver { background: #708090; color: #FFFAFA; } .mouseOut { background: #FFFAFA; color: #000000; }</style>
    3.3 JavaScript部分获取用户输入内容的关联信息的函数
    function getMoreContents() { //首先获取用户的输入 var content = document.getElementById("keyword"); if (content.value == "") { clearContent(); return; } //然后给服务器发送用户输入的内容,因为采用ajax异步发送数据,所以使用XmlHttp对象 xmlHttp = creatXMLHttp(); //给服务器发送数据 var url = "search?keyword=" + escape(content.value); xmlHttp.open("GET", url, true); //xmlHttp绑定回调方法,这个回调方法会在xmlHttp状态改变的时候被调用 //xmlHttp 状态0-4,我们只关心4(complete),完成后再调用回调方法才有意义 xmlHttp.onreadystatechange = callback; xmlHttp.send(null);}
    获取XmlHttp对象
    function creatXMLHttp() { //对于大多数浏览器都适用 var xmlHttp; if (window.XMLHttpRequest) { xmlHttp = new XMLHttpRequest(); } //要考虑浏览器的兼容性 if (window.ActiveXObject) { xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); if (!xmlHttp) { xmlHttp = new ActiveXObject("Msxml2.XMLHTTP"); } } return xmlHttp;}
    回调函数
    function callback() { //4代表成功 if (xmlHttp.readyState == 4) { //200代表服务器响应成功 if (xmlHttp.status == 200) { //交互成功 获得响应的数据 是文本格式 var result = xmlHttp.responseText; //解析数据 var json = eval("(" + result + ")"); //获取数据后动态显示 展示输入框下面 setContent(json); } }}
    设置关联数据展示
    function setContent(contents) { clearContent(); setLocation(); //获取关联数据的长度,以此来确定生成的<tr> var size = contents.length; //设置内容 for (var i = 0; i < size; i++) { var nextNode = contents[i];//代表的是JSon数据的第i个元素 var tr = document.createElement("tr"); var td = document.createElement("td"); td.setAttribute("border", "0"); td.setAttribute("bgcolor", "#FFFAFA"); td.onmouseover = function () { this.className = 'mouseOver'; }; td.onmouseout = function () { this.className = 'mouseOut'; }; td.onmousedown=function(){ //当鼠标点击一个关联数据的时候,被选中的数据 自动填充到输入框里面 document.getElementById("keyword").value=this.innerHTML; //清除div边框 document.getElementById("popDiv").style.border="none"; }; var text = document.createTextNode(nextNode); td.appendChild(text); tr.appendChild(td); document.getElementById("content_table_body").appendChild(tr); }}
    3.4 前后台程序联调清空之前的数据
    function clearContent() { var contentTableBody = document.getElementById("content_table_body"); var size = contentTableBody.childNodes.length; for (var i = size - 1; i >= 0; i--) { contentTableBody.removeChild(contentTableBody.childNodes[i]); } document.getElementById("popdiv").style.border = "none";}
    输入框失去焦点 清空
    function keywordBlur() { clearContent();}
    设置显示关联信息
    function setLocation() { //关联信息的显示位置 var content = document.getElementById("keyword"); var width = content.offsetWidth;//输入框的宽度 var left = content["offsetLeft"];//距离左边框的距离 var top = content["offsetTop"] + content.offsetHeight;//距离顶部 //获取显示数据div var popdiv = document.getElementById("popdiv"); popdiv.style.border = "black 1px solid"; popdiv.style.left = left + "px"; popdiv.style.top = top + "px"; popdiv.style.width = width + "px"; document.getElementById("content_table").style.width = width + "px";}
    3.5 编写SearchServlet.javaimport net.sf.json.JSONArray;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.util.*;public class SearchServlet extends HttpServlet { static List<String> datas = new ArrayList<String>(); //模拟数据 static { datas.add("ajax"); datas.add("ajax post"); datas.add("becky"); datas.add("bill"); datas.add("james"); datas.add("jerry"); datas.add("hao"); } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,IOException{ request.setCharacterEncoding("utf-8"); response.setCharacterEncoding("utf-8"); System.out.print("123"); //获取客户端数据 String keyword = request.getParameter("keyword"); //获取关键字 List<String> listData = getData(keyword); //返回json格式 response.getWriter().write(JSONArray.fromObject(listData).toString()); } public List<String> getData(String keyword){ List<String> list = new ArrayList<String>(); for (String data:datas) { if(data.contains(keyword)){ list.add(data); } } return list; }}
    四、最终效果图输入搜索信息下面会智能提示,点击提示信息会自动输入到搜索框中:

    五、项目地址geekerstar/AjaxSearch


    版权声明:本文(除特殊标注外)为原创文章,版权归 Geekerstar 所有。
    本文链接:http://www.geekerstar.com/project/620.html
    除了有特殊标注文章外欢迎转载,但请务必标明出处,格式如上,谢谢合作。
    本作品采用 知识共享署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议 进行许可。
    0 留言 2021-02-22 09:01:01
  • 使用模拟退火算法与局部搜索算法解决CFLP问题

    一、概述利用局部搜索方法以及模拟退火算法来求解带容量设备选址问题,对比两种方式的优劣程度,及其讲述使用的技巧。对于CFLP的71个测例来进行测试,写出实验结果的数据。实验中采用多种邻域操作的局部搜索local search策略尝试解决相同规模的CFLP问题,并与相同局部搜索的模拟退火算法进行对比。从算法结果来看,模拟退火算法求得的最优解要比局部搜索得出的最优解要好。通过该实验得出结论,局部搜索容易陷入局部最优解,而模拟退火利用其一定概率接受差解的特性跳出局部最优,从而能逐渐收敛到全局最优。
    二、题目介绍给定n个工厂和m个顾客,开工厂需要一定的费用,一个工厂有一定的容量限制,每个顾客也有一定的需求,而每个顾客要选取某个工厂也需要一定的分配费用,现在要求找出一个分配方案,把顾客分配给不同的工厂,然后在可以满足所有顾客需求的前提下让所有的花费(开工厂的花费和分配的花费)最小。
    这显然是一个NP-hard问题,因为情况数非常的多,而且也没有什么固定的好的策略,所以现在我们就用贪心算法和模拟退火法两种方法来解决这个问题。
    三、算法介绍3.1 局部搜索局部搜索(Local Search Algorithm)是指能够无穷接近最优解的能力,而全局收敛能力是指找到全局最优解所在大致位置的能力。
    局部搜索是解决最优化问题的一种启发式算法。对于某些计算起来非常复杂的最优化问题,比如各种NP完全问题,要找到最优解需要的时间随问题规模呈指数增长,因此诞生了各种启发式算法来退而求其次寻找次优解,是一种近似算法(Approximate algorithms),以时间换精度的思想。局部搜索就是其中的一种方法。
    算法的关键问题是:

    局部最优问题
    步长问题
    起始点问题

    3.2 模拟退火算法模拟退火算法(Simulated Annealing Algorithm)是元启发式搜索算法的其中一种。相比起效率低下的随即搜索算法,元启发式搜索算法借助了演化思想和集群智能思想,对解的分布有规律的复杂问题有良好的效果。
    模拟退火法(Simulated Annealing)是克服爬山法缺点的有效方法,所谓退火是冶金专家为了达到某些特种晶体结构重复将金属加热或冷却的过程,该过程的控制参数为温度T。模拟退火法的基本思想是,在系统朝着能量减小的趋势这样一个变化过程中,偶尔允许系统跳到能量较高的状态,以避开局部极小点,最终稳定到全局最小点。
    算法的关键问题是:

    初温与接受温度的设置
    局部搜索的方法
    退火系数
    某一温度下的内循环次数

    四、算法实现4.1 算法流程
    随机挑选出一单元,并给他一个随机的位移k,求出系统因此而产生的能量变化∆Ek
    若∆Ek小于等于0,该位移可采纳,而变化后的系统状态可作为下次变化的起点
    若∆Ek大于0,位移后的状态可采纳的概率如下公式所示:式中T为温度,然后从(0,1)区间均匀分布的随机数中挑选一个数R。若R小于Pk,则将变化后的状态作为下次的起点;否则,将变化前的状态作为下次的起点



    转第(1)步继续执行,直至达到平衡状态为止
    4.1.1 读取文件,储存数据根据文件中的设备数量,用户数量读取数据,并将其放在vector中,这里我利用了5个vector。
    vector<int> facilityResult; // 安排哪些工厂开放(01表示)vector<int> customerResult; // 安排每个顾客去哪个设备vector<int> facilitySequence; // 设备开放的顺序vector<int> customerSequence; // 客户加入的顺序vector<spare> spareCapacity; // 设备的剩余空间
    除此以外,为了搜索操作的需要,要对于剩余容量进行排序,或者对于用户花费进行排序,所以这里需要一些结构体。结构体根据value或cost进行排序,id保持不变,指示唯一的用户或设备。
    // 用户对于不同设备的花费struct customer_cost{ float value; int id;};// 剩余空间struct spare{ int id; int capacity;};// 顾客结构体struct customer{ float demand; std::vector<customer_cost> cost; };// 设备结构体struct facility{ int capacity; int cost;};
    一些参数,前四个用于保存文件中的数据,只会在读取文件的时候改变,后面为只读。
    后四个参数为模拟退火算法使用的参数,通过调整这些参数能获取更好的解。
    int nFacility; // 工厂数量int nCustomer; // 顾客数量std::vector<customer> customerVector; // 顾客的信息std::vector<facility> facilityVector; // 设备的信息const double coolSpeed = 0.99; // 退火速度const double acceptT = 0.00001; // 接受温度const double initialT = 2000; // 初始温度const int turn = 1000; // 内循环次数
    4.1.2 进行局部搜索找最优解统计一个解的长度
    评价函数是寻找最优解的一个关键,CFLP问题可以利用解的总花费代价作为评价该解的优秀程度。
    计算花费代价,包括两部分,其一是开启设备的花费,其二是用户选择该设备的花费,两者求和即是该解的总花费
    // 统计一个解的总花费void CalculateTotalCost(){ totalCost = 0; // 设备花费 for (int i = 0; i < nFacility; ++i) { if (facilityResult[i]) { totalCost += facilityVector[i].cost; } } // 用户花费 for (int i = 0; i < nCustomer; ++i) { for (int j = 0; j < nFacility; ++j) { if (customerResult[i] == customerVector[i].cost[j].id) { totalCost += customerVector[i].cost[j].value; break; } } }}
    判断一个解的合法性
    一个解是否合法需要检查,每一个设备的容量是否小于被选中该设备的用户需求之和,否则认为该解是不合法的,超出设备的容量。
    // 检查该解的合法性bool CheckSolution(){ int facilityCapacity[50]; for(int i = 0; i < nFacility; i++){ facilityCapacity[i] = 0; } // 检查容量 for (int i = 0; i < nCustomer; ++i) { facilityCapacity[customerResult[i]] += customerVector[i].demand; } for (int i = 0; i < nFacility; ++i) { if (facilityCapacity[i] > facilityVector[i].capacity) { return false; } } return true;}
    随机生成初始解
    这里先随机生成设备开放顺序以及顾客加入顺序
    然后根据这两个顺序来遍历用户与设备,这里填充的法则是容量First Fit
    当该设备可以装下时,继续放这个设备;直到放不下才放到下一个设备。
    // 随机生成一个解,根据开放的顺序,顺序填充顾客void RandomGenerate(){ srand(time(0)); int nChange1 = rand() % nFacility; int nChange2 = rand() % nCustomer; for (int i = 0; i < nFacility; ++i) { facilitySequence[i] = i; } // 随机设备开放顺序 for (int i = 0; i < nChange1; ++i) { int pos1 = rand() % nFacility; int pos2 = rand() % nFacility; int temp = facilitySequence[pos1]; facilitySequence[pos1] = facilitySequence[pos2]; facilitySequence[pos2] = temp; } // 随机顾客加入顺序 for (int i = 0; i < nCustomer; ++i){ customerSequence[i] = i; } for (int i = 0; i < nChange2; ++i) { int pos1 = rand() % nCustomer; int pos2 = rand() % nCustomer; int temp = customerSequence[pos1]; customerSequence[pos1] = customerSequence[pos2]; customerSequence[pos2] = temp; } // 按照前面的顺序来填充一个解 int j = 0; // 统计设备的序号 facilityResult[facilitySequence[0]] = 1; for (int i = 0; i < nCustomer; ++i) { // 当当前设备装不下用户的需求,查找一个容量能装下的设备 while(customerVector[customerSequence[i]].demand > spareCapacity[facilitySequence[j]].capacity){ j++; j = j % 10; // 新的 if (facilityResult[facilitySequence[j]] == 0) { facilityResult[facilitySequence[j]] = 1; spareCapacity[facilitySequence[j]].capacity = facilityVector[facilitySequence[j]].capacity; } } // 将用户装入设备序号j spareCapacity[facilitySequence[j]].capacity -= customerVector[i].demand; customerResult[i] = facilitySequence[j]; }}
    四种局部搜索的策略
    这里,我们设置了四种局部搜索的策略来进行找邻域中的解。
    (1) 交换两设备的开关情况
    随机找到两个设备,要求这两个设备是一开一关的,将两个设备的开关情况交换。
    需要的操作:

    将a设备的用户提取出来
    将a设备的容量还原
    将提取出来的用户放入设备b中,当b放不下后,再往后遍历
    更改设备b的容量
    重新统计该解的花费

    count = 40;while(count > 0){ int i = rand() % nFacility; int j = rand() % nFacility; if (facilityResult[i] == 1 && facilityResult[j] == 0) { facilityResult[i] = 0; facilityResult[j] = 1; // 需要改变的用户 std::vector<int> changeCustomer; for (int k = 0; k < nCustomer; ++k) { if (customerResult[k] == i) { changeCustomer.push_back(k); } } // 返还空间给设备 for (int x = 0; x < nFacility; ++x) { if(spareCapacity[x].id == i){ spareCapacity[x].capacity = facilityVector[x].capacity; break; } } for (int k = 0; k < changeCustomer.size(); ++k) { // 采用容量best fit,根据容量的大小来安排 sort(spareCapacity.begin(),spareCapacity.end()); for(int x = 0; x < nFacility; x++){ if(customerVector[changeCustomer[k]].demand < spareCapacity[x].capacity){ spareCapacity[x].capacity -= customerVector[changeCustomer[k]].demand; customerResult[changeCustomer[k]] = spareCapacity[x].id; facilityResult[spareCapacity[x].id] = 1; break; } } } break; } count--;
    (2) 提取两个设备的所有用户出来,重新分配
    随机找到两个设备,要求这两个设备都具有用户的,将提取出来的用户重新分配。
    需要的操作:

    将a, b设备的用户提取出来
    将a, b设备的容量还原
    将提取出来的用户按照容量Best Fit重新放置
    更改设备的容量
    重新统计该解的花费

    int i1 = rand() % nFacility;int i2 = rand() % nFacility;while(i1 == i2) i2 = rand() % nFacility;// 随机搜索一些用户,拿出来重新放置 int count = nCustomer;// 需要改变的用户std::vector<int> changeCustomer;for (int k = 0; k < count; ++k){ if (customerResult[k] == i1) { changeCustomer.push_back(k); // 返还空间给设备 for (int x = 0; x < nFacility; ++x) { if(spareCapacity[x].id == i1){ spareCapacity[x].capacity += customerVector[k].demand; break; } } }}for (int k = 0; k < count; ++k){ if (customerResult[k] == i2) { changeCustomer.push_back(k); // 返还空间给设备 for (int x = 0; x < nFacility; ++x) { if(spareCapacity[x].id == i2){ spareCapacity[x].capacity += customerVector[k].demand; break; } } }}facilityResult[i1] = 0;facilityResult[i2] = 0;// 从下一个开始装填int j = (i1+i2) % nFacility;for (int k = 0; k < changeCustomer.size(); ++k){ // 采用best fit sort(spareCapacity.begin(),spareCapacity.end()); for(int x = 0; x < nFacility; x++){ if(customerVector[changeCustomer[k]].demand < spareCapacity[x].capacity){ spareCapacity[x].capacity -= customerVector[changeCustomer[k]].demand; customerResult[changeCustomer[k]] = spareCapacity[x].id; //cout << "spareCapacity[x].id " << spareCapacity[x].id << endl; facilityResult[spareCapacity[x].id] = 1; break; } }}break;
    (3) 提取所有设备的最后两个用户出来,重新分配
    要求用户数大于等于2的设备,提取出来的用户重新分配。
    需要的操作:

    将所有设备的用户提取出来
    将所有设备的容量填补
    将提取出来的用户按照价格Best Fit重新放置
    更改设备的容量
    重新统计该解的花费

    // 将每个设备的后面4个客户拿出来,重新分配// 统计每个设备的顾客vector<vector<int> > v(nFacility);// 需要改变的用户std::vector<int> changeCustomer;for (int i = 0; i < nCustomer; ++i){ v[customerResult[i]].push_back(i);}for (int i = 0; i < v.size(); ++i){ if(v[i].size() > 2){ int temp1 = v[i].back(); v[i].pop_back(); // 返还空间给设备 for (int x = 0; x < nFacility; ++x) { if(spareCapacity[x].id == i){ spareCapacity[x].capacity += customerVector[temp1].demand; break; } } int temp2 = v[i].back(); v[i].pop_back(); // 返还空间给设备 for (int x = 0; x < nFacility; ++x) { if(spareCapacity[x].id == i){ spareCapacity[x].capacity += customerVector[temp2].demand; break; } } changeCustomer.push_back(temp1); changeCustomer.push_back(temp2); }}for (int k = 0; k < changeCustomer.size(); ++k){ // 采用价格best fit std::vector<customer_cost> vc; vc = customerVector[k].cost; sort(vc.begin(),vc.end()); for(int x = 0; x < nFacility; x++){ int fa_subscript = 0; // 找下标 for(int q = 0; q < nFacility; q++){ if(vc[x].id == spareCapacity[q].id){ fa_subscript = q; break; } } // 减容量 if(spareCapacity[fa_subscript].capacity > customerVector[k].demand){ spareCapacity[fa_subscript].capacity -= customerVector[k].demand; customerResult[changeCustomer[k]] = vc[x].id; facilityResult[vc[x].id] = 1; break; } }}break;
    设置循环次数,局部搜索
    局部搜索仅接受好解,当遇到差解时,保留旧解。循环多次直到解不再改变,这时说明局部搜索进入了局部最优解,而且无法跳出来。
    Solution SA() { //之后用到很多随机数,设定种子 srand(time(0)); int i = 0; Solution s; // 查看初解 Solution best = s; //设定一个固定的循环次数 for (i = 0; i < turn; i++) { Solution next = s; getNewSolution(next);//获取邻域解 if (Accept(next, s, t)) { s = next;//接受新解为当前最优 if(next < best){//记录全局最优 best = next; } } } return best;}
    其中Accept函数仅介绍比当前解要好的解,而getNewSolution就是通过之前定义的领域查找操作获取新解。
    4.1.3 添加模拟退火算法在局部搜索的基础上,不改变局部搜索的策略,仅仅添加上温度的控制,以此来让算法在一定的概率下接受差解。
    参数设定
    定义初始温度为20000,接受温度为0.0001,退火系数为0.99(即$T_{i+1} = 0.99 * T_i$)
    每个温度内循环为1000次
    将局部搜索加入到每一个温度下的循环中,并且找到一个新解时候,判断其与旧解的差值。利用这个插值以及温度来设定概率,用这个概率判断是否接受这个差解,当然好解仍然是全部接受。
    执行完一次温度的1000次内循环,进行降温操作。
    为了提高精度,在降温到一定程度,我还使用了二次升温的操作,能继续跳出局部最优。
    Solution best = s;int addT = 0;while (t > acceptT) { //设定一个固定的循环次数 for (i = 0; i < turn; i++) { Solution next = s; getNewSolution(next);//获取邻域解 //cout << next.CheckSolution() << " " << i << endl; if (Accept(next, s, t)) { s = next;//接受新解为当前最优 if(next < best){//记录全局最优 best = next; } } } t *= r;//降温 //升温 if(addT < 3 && t < 20){ t *= 20; addT++; }}
    五、样例测试结果实验环境: Windows10
    5.1 局部搜索算法与模拟退火算法的结果



    Result(SA)
    Time(s)(SA)
    Result(LS)
    Time(s)(LS)




    p1
    15054
    3.599
    18436
    1.320


    p2
    15730
    4.235
    19856
    1.217


    p3
    18795
    4.689
    24013
    1.356


    p4
    22208
    4.761
    29014
    1.157


    p5
    18231
    4.16
    20165
    1.323


    p6
    19997
    4.404
    34103
    1.029


    p7
    21155
    4.075
    25612
    1.594


    p8
    20788
    4.259
    24658
    1.358


    p9
    18834
    4.019
    26874
    1.364


    p10
    15319
    4.707
    21684
    1.277


    p11
    19323
    3.407
    25674
    1.471


    p12
    19152
    3.876
    26453
    1.351


    p13
    16688
    4.881
    24351
    0.987


    p14
    18437
    5.584
    21368
    1.375


    p15
    15328
    4.741
    19874
    1.312


    p16
    16377
    5.135
    18795
    1.489


    p17
    14281
    5.038
    19634
    1.687


    p18
    18375
    4.976
    19874
    1.547


    p19
    18699
    4.794
    20120
    1.214


    p20
    17147
    5.239
    24310
    1.200


    p21
    15569
    5.272
    21587
    1.219


    p22
    14727
    5.402
    19574
    1.354


    p23
    18181
    4.823
    20148
    1.114


    p24
    16539
    5.032
    24687
    1.365


    p25
    54957
    7.822
    84123
    1.987


    p26
    66687
    8.808
    84712
    2.359


    p27
    62994
    7.691
    78998
    2.126


    p28
    83972
    8.832
    98564
    1.784


    p29
    71849
    8.144
    95678
    1.859


    p30
    42215
    7.794
    65987
    1.879


    p31
    61148
    7.893
    66874
    2.003


    p32
    55206
    7.586
    65239
    2.410


    p33
    47642
    8.265
    75462
    1.983


    p34
    88701
    8.368
    95611
    2.153


    p35
    52371
    7.268
    66782
    1.984


    p36
    55141
    7.025
    79621
    2.264


    p37
    69995
    8.635
    98561
    2.361


    p38
    57220
    8.546
    77856
    2.223


    p39
    62602
    8.587
    85463
    1.925


    p40
    92181
    8.286
    98711
    2.314


    p41
    12846
    4.085
    15687
    1.256


    p42
    12260
    6.124
    15655
    1.597


    p43
    11387
    6.221
    13200
    1.548


    p4
    14150
    3.99
    14895
    1.268


    p45
    12393
    5.669
    13547
    1.344


    p46
    11054
    5.971
    15314
    1.562


    p47
    12982
    3.371
    14355
    1.435


    p48
    11069
    6.018
    16423
    1.998


    p49
    10414
    7.082
    13251
    2.013


    p50
    15268
    3.790
    15468
    1.246


    p51
    18369
    7.856
    20114
    1.985


    p52
    18539
    4.471
    26478
    2.014


    p53
    15378
    7.315
    20431
    2.687


    p54
    21268
    3.911
    25143
    1.654


    p55
    17370
    6.965
    19562
    1.664


    p56
    44390
    12.102
    56899
    3.012


    p57
    54296
    11.502
    98543
    2.987


    p58
    48486
    12.382
    56214
    2.887


    p59
    59698
    10.41
    85356
    3.124


    p60
    48793
    11.909
    63512
    3.210


    p61
    46821
    12.259
    59823
    3.214


    p62
    52271
    11.585
    64985
    2.963


    p63
    43909
    11.592
    47351
    2.799


    p64
    44307
    12.712
    53684
    3.569


    p65
    48465
    11.768
    58164
    2.689


    p66
    47556
    12.533
    60012
    3.045


    p67
    47062
    12.556
    56841
    3.512


    p68
    46467
    12.680
    46295
    3.246


    p69
    43157
    12.321
    48652
    2.869


    p70
    53930
    11.740
    85612
    2.694


    p71
    49072
    11.782
    62548
    3.001



    六、结论使用单纯的局部搜索方法来解决CFLP问题,非常容易陷入局部最优,这样就会导致解的准确度很差,但是我们可以先利用局部搜索的解来作为模拟退火的初解,这样会加快模拟退火的准确度,所以局部搜索在对于一些局部峰值较少的问题还是很好的,因为其收敛速度很快,其解的性质也不会太差。
    对于这个CFLP问题的领域搜索:

    对于容量

    FIRST FIT:每次放置第一个找到能容纳需求的容量的设备BEST FIT:每次放置容量恰好大于客户需求的设备
    对于花费

    FIRST FIT:找到第一个花费的设备BEST FIT:找到花费最小的设备,若设备已满,则放置在第二小,以此类推(贪心的思想)

    经过我设置搜索策略的观察,往往对于花费的BEST FIT操作能找到更好的解,但是如果仅仅使用花费的贪心搜索,这跟贪心算法没什么区别,且达不到最优解,因为会收到容量的限制。故我在搜索策略中加入对于容量的FIRST FIT以及BEST FIT,然而这与花费的结果是有矛盾冲突的,所以也需要再三权衡。
    关于局部搜索与模拟退火的对比:局部搜索收敛速度较快,但易陷入局部最优,对于初值不太敏感;而模拟退火算法收敛速度较慢,算法的准确度对于参数的设置以及初解的好坏敏感。对于不同的测试例子也有不同的反馈结果,需要进行调参。
    0 留言 2021-02-20 08:16:45 奖励32点积分
  • 【翻译】基于python-docx实现的解析docx文件,顺序保存文字、表格、图片

    前些天由于程序的需要,要从docx文档中,按文档的顺序获取文字、图片、表格信息,按自己想要的排版方式,转换成markdown格式。但是,卡在了按顺序获取文字、图片、表格上。查找了很多资料,很多都是介绍怎么使用python-docx添加文字、图片、表格的。后来经过多方查找,终于找到一份国外的源码(源码地址附在文后),已经实现了按顺序获取文字、图片、表格的功能。为了节省大家搜索时间,所以把他的英文文档翻译成中文,方便大家查阅。原文翻译如下:
    python-docx是用于创建和处理Microsoft Word(.docx)文件的Python库。Python-docx包无法按文档顺序完全读取段落,表格和图像。按照文档顺序,它只能一次获取所有段落,或者一次获取所有表格,或者一次获取所有图像。在这里,我提供了一种方法,可以将docx文件中的段落、表格和图像按文档顺序读取到python的数据框中。
    该代码位于“Para_table_image_extraction.py”的文件内。在以任何docx文件作为输入运行此代码时,此代码将生成3个数据帧,即“combined_df”、“table_list”和“image_df”:

    docx文件的所有内容(包括段落,表和图像)都按顺序存储在名为“combined_df”的python数据帧中
    如果在文档的段落之后出现图像,则对该图像的引用将存储在“combined_df”数据框中,而不是实际图像。您将必须从“combined_df”引用图像索引,并从名为“image_df”的单独数据帧中检索图像数据,该数据帧存储image_index和每个图像的相应base64编码图像数据
    类似地,如果在文档中遇到表,则将填充“combined_df”数据框中的“table_id”列。而且,您将不得不从“table_list”中检索与“ table_id”相关的对应表

    下图是示例 word 文档(docx):

    下图是根据上述docx文件生成的“combined_df”数据帧:

    在上述数据帧中:

    para_text:表示文档的实际段落内容(每一行代表文档中的每个段落)
    table_id:表示表格的表ID(如果在该文档的该位置存在表)。如果没有表格,则其值表示为“ Novalue”
    style:代表相应段落的段落样式

    注意,图像和表格不会这样存储在combined_df数据框中。对这些对象的引用仅存储在combined_df中。这意味着对于在Combined_df中存在的每个图像ID或表格ID,您将必须从combined_df中检索图像ID或表格ID,并从image_df和table_list中根据这些ID来获取图像数据或表格数据。
    图像文件以以下符号表示:
    combined_df中的 Document_Imagefile/image1.png/rId7/0这表示该引用实际上是一个图像文件,其中“image1.png”作为图像名称,“rId7”作为图像的唯一ID或标识符,“0”图片索引将在image_df中引用。

    表格对象表示为“<docx.table.0x1020f1160处的表对象>”,它表示在该位置存在一个带有相应table_id的表。您可以在“table_list”索引中根据table_id来获取获取表格数据。table_list包含所有表格的信息。

    我还没有包含使用来自combined_df的唯一标识符从image_df和table_list提取图像和表格的代码。我之所以没有这样做,是因为用例可能因情况而异。您可以用自己的方式提取它们,然后执行您希望执行的任何后续操作。
    原文链接:Python-docx-Reading-paragraphs-tables-and-images-in-document-order
    0 留言 2021-02-13 10:13:48 奖励38点积分
  • 基于SSM的超市订单管理系统

    1 系统需求分析超市订单管理系统是一个专为连锁店、超市等商业场所提供订单管理平台的系统。该系统的目标是建立一个订单管理平台,为需要合理规划超市供应链、供应商以及工作人员提供的便捷的平台。该系统的主要业务需求包括记录并维护某超市的供应商信息,以及该超市与供应商之间的交易订单信息,包括三种角色,系统管理员经理,普通员工。
    1.1 系统功能分析本系统主要的功能是实现超市订单管理功能,以便为超市、连锁店提供以及其他负责人提供订单详情、联系方式等,系统的主要功能有以下五个方面:

    登录/注销:管理员可以在网站上登录浏览,离开时注销并退出
    订单管理:管理员可以浏览所有订单信息,并且通过点击查看了解订单详情信息
    供应商管理:管理员可以在网站浏览所有供应商信息,并在在与其他供应商达成合作之后,添加相关供应商信息,并且通过点击查看了解他们的联系方式等
    用户管理:管理员可以管理所有超市员工用户,对用户进行增删改查,对于离职或其他原因的未工作用户给予注销管理
    密码修改:管理员可对自己的账号密码进行修改,填写对应之前的正确密码以及新密码之后,即完成相关修改密码操作
    搜索功能:在以上管理界面中,均允许了管理员根据关键字进行搜索,要求搜索框中输入的字段必须完全包含在物品名称中,否则无法查询


    1.2 系统功能需求根据系统功能要求,该超市订单管理系统以管理员为中心的用户角色,可以将系统分解成几个模块来分别设计应用程序界面,如图 1.1所示。

    1.3 系统性能需求超市订单管理系统的开发是在Window10平台上,以SSM为架构,采用MySQL 作为数据库管理系统管理后台数据库。本系统是超市信息管理建设中必不可少的一部分,它实现了现代管理信息系统的大部分功能需要。使用本系统可以使超市管理更加方便快捷,合理的页面设计也使得这个用户充分享受到基于Internet管理信息系统的优越。本系统开发说明:
    1.3.1 功能完备在开发初期,查看了大量关于电子商务,管理信息系统,J2EE等方面的资料,同时借鉴了很多其他电子商务网站和管理信息的流程。经过总结,确定了满足需求分析的基本模块。系统总体设计上实现了整个系统模块的划分,系统主要包含5大模块,分别是:订单管理信息,供应商管理,用户管理,修改密码,登陆退出系统,基本上实现了综合管理系统的所有功能。 
    1.3.2 界面友好系统用户登陆到管理页面后,每页有导航和引领的作用。系统具有自适应的能力,同时导航条方便快捷的引导用户进行各种合理的操作。
    1.3.3 管理科学本系统一开始就从管理学的角度做出了详细细致的考虑,后来有参考了电子商务管理等,最后才做出了系统总体设计,因此可以讲该系统是较为科学的。
    系统的性能需求主要表现在数据库中的各个表需要频繁地被插入、删除以及更新。对于用户来说,系统地响应时间不宜太长,否则会降低用户体验。为此要求我们建立良好的表结构,加上足够的存储空间以及硬件性能。
    2 可行性分析2.1 研究前提随着我国经济情况的日新月异,飞速发展,涌现出许许多多的超市和便利店。越来越多的人喜欢到超市购物,超市里销售的商品也呈现出多种多样的变化趋势。我们开发一个超市订单管理系统,它可以对仓储各环节实施全过程控制管理,对整个进货、退货、盘点等各个环节的规范化作业,控制整个过程的正常运行。去掉了手工书写票据和送到机房输入的步骤,解决库房信息陈旧滞后的弊病,方便了仓库管理人员对物品的放置和调配,提高了工作效率。
    该系统容易被接受,具有简单易学性,便于管理等功能,是对超市订单管理的一种有效工具。
    2.2 设计要求2.2.1 安全性超市订单管理增强对产品规范的审计,重点确定该项目中需要审计的产品。买家只能针对卖家允许公开的信息进行查阅。买家只享受对自己账号内数据的查阅权,与定后处理权,订货支付权,申请退货权,不允许偷窥其他人。卖家只能针对买家允许公开的信息进行查阅。卖家只享受对自己账号内数据的查阅权,发货权,退款相应处理权,不允许偷窥其他人。
    2.2.2 系统性能管理员登录查看超市供应商与超市员工用户管理,可以进行增、删、改、查等操作。超市订单系统可以使超市的管理趋于正规化、现代化和系统化。本项目的产品可以达到以下目标:

    提高工作效率,减少返工
    业务流程的流水线化
    符合相关标准和规则
    与目前的应用产品相比较,提高了可用性或减少了失效程度

    2.2.3 可扩展性所有信息呈现,操作完全由打开的网页呈现并完成。本系统所占有的是超市市场,它追求的是简单、易学、易用,能够更好地解决管理人员的负担,能够辅助超市有效的管理物品。对于订单管理系统的用户,可满足对订单管理的需求,且此种需求被接受并且满足,其系统便可以推广。

    3 数据库设计3.1 数据库需求分析经过对超市管理系统的调查分析,得出用户的需求大致如下:

    管理员可以在系统中对订单、供应商以及用户进行增、删、改、查的处理
    管理员需要输入账号密码登录,并且可以增添新的管理员

    如下是利用数据流图方法对数据库做需求分析:
    第一步:由用户的需求,可以得到顶层数据流图如图3.1.1所示。

    第二步:超市订单管理系统的第1层数据流图如图3.1.2所示。

    第三步:超市订单管理系统的第2层数据库流图——订单管理的细化数据流图如图3.1.3所示。

    第四步:超市订单管理系统的第2层数据流库——供应商管理的细化数据流图如图3.1.4所示。

    第五步:超市订单管理系统的第2层数据流库——用户管理的细化数据流图如图3.1.5所示。

    根据如上的数据流程图,可以列出以下记录超市订单管理所需的数据项和数据结构:

    管理员:管理员ID、管理员姓名、管理员密码、管理员性别、管理员角色、管理员出生日期、管理员电话、管理员住址
    订单:订单编码、商品名称、供应商名称、订单金额、是否付款
    供应商:供应商编码、供应商名称、联系人、联系电话、微信

    3.2 数据库概念结构设计本系统一共有用户、供应商、订单、角色、地址这五个基本实体。
    管理员可以对应多个订单,而一个订单只能对应于一个管理员。管理员可以管理多个供应商,而一个供应商只能对应于一个管理员。一个供应商可以对应多条订单,但一条订单只能对应于一个供应商。此外,有一个用户对应一个角色,一个角色对应多个用户;一个地址对应多个订单,一个订单对应一个地址。数据库表之间的关系如下:


    用户:主键ID、用户编码、用户名称、用户密码、性别、出生日期、手机、地址、用户角色、创建者、创建时间、更新者、更新时间、用户头像、工作照
    账单:订单编号、订单编码、商品名称、商品描述、商品单位、商品数量、商品总额、是否支付、创建者、创建时间、更新者、更新时间、供应商ID
    供应商:供应商ID、供货商编码、供货商名称、供应商详细描述、供应商联系人、联系电话、地址、微信、创建者、创建时间、更新时间、更新者、营业执照、组织机构代码证
    地址:主键ID、联系人姓名、收货地址明细、邮编、联系人电话、创建者、创建日期、修改者、修改时间、用户ID
    角色:角色编号、角色编码、角色名称、创建者、创建时间、修改者、修改时间

    3.3 数据库逻辑结构设计将概念结构设计中的各个模型转化为DBMS支持的表结构,同时保持不会出现插入异常、删除异常和修改异常,表结构应该做到符合3NF。根据系统 E-R 图,需要设计4个数据表来存放信息。在本系统中,一共有五个实体,实体转化为数据库模型为如下所示:
    一对多联系转化为一个关系模式:

    用户—订单(用户编号,订单编号)
    供货商—订单(供货商编号,订单编号)
    用户—身份(用户编号,身份编号)
    用户—地址(用户编号)

    利用以上关系模式得到的所有数据表如下所示:
    用户表(smbms_user)

    数据项:主键ID、用户编码、用户名称、用户密码、性别、出生日期、手机、地址、用户角色、创建者、创建时间、更新者、更新时间、用户头像、工作照
    说明:用户ID是唯一的用户标识,使此表的主键。如表3.3.1所示。




    列名
    数据类型
    数据长度
    可否为空
    备注




    Id
    bigint
    20
    Not null
    主键ID


    userCode
    varchar
    15
    Not null
    用户编码


    userName
    varchar
    15
    Not null
    用户名称


    userPassword
    varchar
    15
    Not null
    用户密码


    gender
    int
    10

    性别


    birthday
    date


    出生日期


    phone
    varchar
    15

    手机


    address
    varchar
    30

    地址


    userRole
    int
    10

    用户角色


    createdBy
    bigint
    20

    创建者


    creationDate
    datetime


    创建时间


    modifyBy
    bigint
    20

    更新者


    modifyDate
    datetime


    更新时间


    idPicPath
    varchar
    300

    用户头像


    workPicPath
    varchar
    300

    工作照



    供应商表(smbms_provider)

    数据项:供应商ID、供货商编码、供货商名称、供应商详细描述、供应商联系人、联系电话、地址、微信、创建者、创建时间、更新时间、更新者、营业执照、组织机构代码证
    说明:这张表标识的是超市管理信息系统中商品供应商的信息列表,供应商ID是该表的主键
    编号方法:商品供应商ID采用自动生成方式,如表3.3.2所示。




    列名
    数据类型
    数据长度
    可否为空
    备注




    Id
    Bigint
    20
    Not null
    供货商ID(主键)


    proCode
    Varchar
    20
    Not null
    供货商编码


    proName
    varchar
    20
    Not null
    供货商名称


    ProDesc
    varchar
    50

    供应商详细描述


    proContact
    varchar
    20
    Not null
    供货商联系人


    proPhone
    Varchar
    20
    Not null
    联系电话


    ProAddress
    Varchar
    50
    Not null
    供货商地址


    proFax
    varchar
    20

    微信


    CreateBy
    bigint
    20

    创建者


    CreatationDate
    datetime


    创建时间


    modifyDate
    datetime


    更新时间


    modifyBy
    bigint
    20

    更新者


    companyLicPicPath
    varchar
    300

    营业执照


    orgCodePicPath
    varchar
    300

    组织机构代码证



    订单表(smbms_bill)

    数据项:订单编号、订单编码、商品名称、商品描述、商品单位、商品数量、商品总额、是否支付、创建者、创建时间、更新者、更新时间、供应商ID
    说明:这张表标识的是超市管理信息系统订单信息列表,订单ID是该表的主键
    编号方法:订单ID采用自动生成方式,供应商ID与供应商表中供应商ID一一对应,如表3.3.3所示。




    列名
    数据类型
    数据长度
    可否为空
    备注




    Id
    bigint
    20
    Not null
    订单ID(主键)


    billCode
    varchar
    20
    Not null
    订单编码


    ProductName
    Varchar
    20
    Not null
    商品名称


    ProductDescent
    Varchar
    50
    Not null
    商品描述


    ProductUnit
    Varchar
    10
    Not null
    商品单位


    ProductCount
    Decimal
    20,2
    Not null
    商品数量


    totalPrice
    Decimal
    20,2
    Not null
    商品总额


    isPayment
    int
    10
    Not null
    是否支付


    createdBy
    bigint
    20

    创建者


    creationDate
    Datetime


    创建时间


    modifyBy
    bigint
    20

    更新者


    modifyDate
    datetime


    更新时间


    providerID
    Int
    20

    供应商ID



    身份表(smbms_role)

    数据项:角色编号、角色编码、角色名称、创建者、创建时间、修改者、修改时间
    说明:这张表标识的是超市订单管理信息系统中用户身份列表,身份编号是该表的主键
    编号方法:用户身份编号与用户表中的员工身份编号一一对应,如表3.3.4所示。




    列名
    数据类型
    数据长度
    可否为空
    备注




    Id
    bigint
    20
    Not null
    角色ID(主键)


    RoleCode
    varchar
    15
    Not null
    角色编码


    roleName
    Varchar
    15
    Not null
    角色名称


    createdBy
    bigint
    20

    创建者


    creationDate
    datetime


    创建时间


    modifyBy
    bigint
    20

    修改者


    modifyDate
    datetime


    修改时间



    地址表(smbms_address)

    数据项:主键ID、联系人姓名、收货地址明细、邮编、联系人电话、创建者、创建日期、修改者、修改时间、用户ID
    编号方法:用户ID与用户表中的用户ID一一对应,如表3.3.5所示。




    列名
    数据类型
    数据长度
    可否为空
    备注




    Id
    bigint
    20
    Not null
    主键ID(主键)


    Contact
    varchar
    15
    Not null
    联系人姓名


    addressDesce
    Varchar
    50
    Not null
    收货地址明细


    postcode
    Varchar
    15

    邮编


    Tel
    Varchar
    20
    Not null
    联系人电话


    createdBy
    bigint
    20

    创建者


    creationDate
    Datetime


    创建时间


    modifyBy
    bigint
    20

    修改者


    modifyDate
    datetime


    修改时间


    userID
    Bigint
    20

    用户ID



    数据库连接利用了SSM框架的底层的MyBatis,建立了实体类与MySQL之间映射关系,从而实现数据持久化、封装数据库连接等操作。
    3.4 数据库物理结构设计3.4.1 选择关系模式的存取方式对数据库逻辑结构设计中建立的表结构,供应商表的供应商编号属性唯一决定每一个供应商元组,所以对供应商表建立以供应商编号为主关键字的索引。同理,对管理员关系模式、订单关系模式也采用类似的索引存取方法。
    3.4.2 数据表存储结构设计本系统的所有数据表均存放在物理磁盘中。用户表、供应商表和订单表的结构是相对稳定的,表中的已有记录是要长期保存的,在此基础上系统会相应用户的操作对数据表进行增、删、改、查等操作。

    3.5 数据库的建立3.5.1 数据库的建立创建数据库
    create database smbms;USE smbms;
    创建表smbms_address
    DROP TABLE IF EXISTS `smbms_address`;CREATE TABLE `smbms_address` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `contact` varchar(15) COLLATE utf8_unicode_ci NOT NULL COMMENT '联系人姓名', `addressDesc` varchar(50) COLLATE utf8_unicode_ci NOT NULL COMMENT '收货地址明细', `postCode` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '邮编', `tel` varchar(20) COLLATE utf8_unicode_ci NOT NULL COMMENT '联系人电话', `createdBy` bigint(20) DEFAULT NULL COMMENT '创建者', `creationDate` datetime DEFAULT NULL COMMENT '创建时间', `modifyBy` bigint(20) DEFAULT NULL COMMENT '修改者', `modifyDate` datetime DEFAULT NULL COMMENT '修改时间', `userId` bigint(20) DEFAULT NULL COMMENT '用户ID', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    创建表smbms_bill
    DROP TABLE IF EXISTS `smbms_bill`;CREATE TABLE `smbms_bill` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `billCode` varchar(20) COLLATE utf8_unicode_ci NOT NULL COMMENT '账单编码', `productName` varchar(20) COLLATE utf8_unicode_ci NOT NULL COMMENT '商品名称', `productDesc` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '商品描述', `productUnit` varchar(10) COLLATE utf8_unicode_ci NOT NULL COMMENT '商品单位', `productCount` decimal(20,2) NOT NULL COMMENT '商品数量', `totalPrice` decimal(20,2) NOT NULL COMMENT '商品总额', `isPayment` int(10) NOT NULL COMMENT '是否支付(1:未支付 2:已支付)', `createdBy` bigint(20) DEFAULT NULL COMMENT '创建者(userId)', `creationDate` datetime DEFAULT NULL COMMENT '创建时间', `modifyBy` bigint(20) DEFAULT NULL COMMENT '更新者(userId)', `modifyDate` datetime DEFAULT NULL COMMENT '更新时间', `providerId` int(20) DEFAULT NULL COMMENT '供应商ID', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    创建表smbms_provider
    DROP TABLE IF EXISTS `smbms_provider`;CREATE TABLE `smbms_provider` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `proCode` varchar(20) COLLATE utf8_unicode_ci NOT NULL COMMENT '供应商编码', `proName` varchar(20) COLLATE utf8_unicode_ci NOT NULL COMMENT '供应商名称', `proDesc` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '供应商详细描述', `proContact` varchar(20) COLLATE utf8_unicode_ci NOT NULL COMMENT '供应商联系人', `proPhone` varchar(20) COLLATE utf8_unicode_ci NOT NULL COMMENT '联系电话', `proAddress` varchar(50) COLLATE utf8_unicode_ci NOT NULL COMMENT '地址', `proFax` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '微信', `createdBy` bigint(20) DEFAULT NULL COMMENT '创建者(userId)', `creationDate` datetime DEFAULT NULL COMMENT '创建时间', `modifyDate` datetime DEFAULT NULL COMMENT '更新时间', `modifyBy` bigint(20) DEFAULT NULL COMMENT '更新者(userId)', `companyLicPicPath` varchar(300) DEFAULT NULL COMMENT '营业执照', `orgCodePicPath` varchar(300) DEFAULT NULL COMMENT '组织机构代码证', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    创建表smbms_role
    DROP TABLE IF EXISTS `smbms_role`;CREATE TABLE `smbms_role` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `roleCode` varchar(15) COLLATE utf8_unicode_ci NOT NULL COMMENT '角色编码', `roleName` varchar(15) COLLATE utf8_unicode_ci NOT NULL COMMENT '角色名称', `createdBy` bigint(20) DEFAULT NULL COMMENT '创建者', `creationDate` datetime DEFAULT NULL COMMENT '创建时间', `modifyBy` bigint(20) DEFAULT NULL COMMENT '修改者', `modifyDate` datetime DEFAULT NULL COMMENT '修改时间', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    创建表smbms_user
    DROP TABLE IF EXISTS `smbms_user`;CREATE TABLE `smbms_user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `userCode` varchar(15) COLLATE utf8_unicode_ci NOT NULL COMMENT '用户编码', `userName` varchar(15) COLLATE utf8_unicode_ci NOT NULL COMMENT '用户名称', `userPassword` varchar(15) COLLATE utf8_unicode_ci NOT NULL COMMENT '用户密码', `gender` int(10) DEFAULT 2 COMMENT '性别(1:女、 2:男)', `birthday` date DEFAULT NULL COMMENT '出生日期', `phone` varchar(15) COLLATE utf8_unicode_ci NOT NULL COMMENT '手机', `address` varchar(30) COLLATE utf8_unicode_ci NOT NULL COMMENT '地址', `userRole` int(10) DEFAULT NULL COMMENT '用户角色(取自角色表-角色id)', `createdBy` bigint(20) DEFAULT NULL COMMENT '创建者(userId)', `creationDate` datetime DEFAULT NULL COMMENT '创建时间', `modifyBy` bigint(20) DEFAULT NULL COMMENT '更新者(userId)', `modifyDate` datetime DEFAULT NULL COMMENT '更新时间', `idPicPath` varchar(300) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '用户头像', `workPicPath` varchar(300) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '工作照', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    3.5.2 初始数据的输入数据表创建成功后,数据库中还没有实际的数据。为了保证外部键能使用,数据需要提前输入,如用户编码、用户姓名、订单名称和供应商等等。具体插入语句如下:
    向smbms_address表插入数据
    insert into `smbms_address`(`id`,`contact`,`addressDesc`,`postCode`,`tel`,`createdBy`,`creationDate`,`modifyBy`,`modifyDate`,`userId`) values (1,'王丽','北京市东城区东交民巷44号','100010','13678789999',1,'2020-04-13 00:00:00',NULL,NULL,1),(2,'张红丽','北京市海淀区丹棱街3号','100000','18567672312',1,'2020-04-13 00:00:00',NULL,NULL,1),(3,'任志强','北京市东城区美术馆后街23号','100021','13387906742',1,'2020-04-13 00:00:00',NULL,NULL,1),(4,'曹颖','北京市朝阳区朝阳门南大街14号','100053','13568902323',1,'2020-04-13 00:00:00',NULL,NULL,2),(5,'李慧','北京市西城区三里河路南三巷3号','100032','18032356666',1,'2020-04-13 00:00:00',NULL,NULL,3),(6,'王国强','北京市顺义区高丽营镇金马工业区18号','100061','13787882222',1,'2020-04-13 00:00:00',NULL,NULL,3);
    向smbms_bill表插入数据
    insert into `smbms_bill`(`id`,`billCode`,`productName`,`productDesc`,`productUnit`,`productCount`,`totalPrice`,`isPayment`,`createdBy`,`creationDate`,`modifyBy`,`modifyDate`,`providerId`) values (1,'BILL2016_001','洗发水、护发素','日用品-洗发、护发','瓶','500.00','25000.00',2,1,'2020-06-14 13:02:03',NULL,NULL,13),(2,'BILL2016_002','香皂、肥皂、药皂','日用品-皂类','块','1000.00','10000.00',2,1,'2020-03-23 04:20:40',NULL,NULL,13),(3,'BILL2016_003','大豆油','食品-食用油','斤','300.00','5890.00',2,1,'2020-05-14 13:02:03',NULL,NULL,6),(4,'BILL2016_004','橄榄油','食品-进口食用油','斤','200.00','9800.00',2,1,'2020-04-10 03:12:13',NULL,NULL,7),(5,'BILL2016_005','洗洁精','日用品-厨房清洁','瓶','500.00','7000.00',2,1,'2020-05-14 13:02:03',NULL,NULL,9),(6,'BILL2016_006','美国大杏仁','食品-坚果','袋','300.00','5000.00',2,1,'2020-04-14 06:08:09',NULL,NULL,4),(7,'BILL2016_007','沐浴液、精油','日用品-沐浴类','瓶','500.00','23000.00',1,1,'2020-07-01 10:10:22',NULL,NULL,14),(8,'BILL2016_008','不锈钢盘碗','日用品-厨房用具','个','600.00','6000.00',2,1,'2020-04-14 05:12:13',NULL,NULL,14),(9,'BILL2016_009','塑料杯','日用品-杯子','个','350.00','1750.00',2,1,'2020-02-04 11:40:20',NULL,NULL,14),(10,'BILL2016_010','豆瓣酱','食品-调料','瓶','200.00','2000.00',2,1,'2020-01-29 05:07:03',NULL,NULL,8),(11,'BILL2016_011','海之蓝','饮料-国酒','瓶','50.00','10000.00',1,1,'2020-04-14 16:16:00',NULL,NULL,1),(12,'BILL2016_012','芝华士','饮料-洋酒','瓶','20.00','6000.00',1,1,'2020-06-09 17:00:00',NULL,NULL,1),(13,'BILL2016_013','长城红葡萄酒','饮料-红酒','瓶','60.00','800.00',2,1,'2020-04-14 15:23:00',NULL,NULL,1),(14,'BILL2016_014','泰国香米','食品-大米','斤','400.00','5000.00',2,1,'2020-05-09 15:20:00',NULL,NULL,3),(15,'BILL2016_015','东北大米','食品-大米','斤','600.00','4000.00',2,1,'2020-05-14 14:00:00',NULL,NULL,3),(16,'BILL2016_016','可口可乐','饮料','瓶','2000.00','6000.00',2,1,'2020-03-27 13:03:01',NULL,NULL,2),(17,'BILL2016_017','脉动','饮料','瓶','1500.00','4500.00',2,1,'2020-05-10 12:00:00',NULL,NULL,2),(18,'BILL2016_018','哇哈哈','饮料','瓶','2000.00','4000.00',2,1,'2020-06-24 15:12:03',NULL,NULL,2);
    向smbms_provider表插入数据
    insert into `smbms_provider`(`id`,`proCode`,`proName`,`proDesc`,`proContact`,`proPhone`,`proAddress`,`proFax`,`createdBy`,`creationDate`,`modifyDate`,`modifyBy`) values(1,'BJ_GYS001','北京三木堂商贸有限公司','长期合作伙伴,主营产品:茅台、五粮液、郎酒、酒鬼酒、泸州老窖、赖茅酒、法国红酒等','张国强','13566667777','北京市丰台区育芳园北路','010-58858787',1,'2020-03-21 16:52:07',NULL,NULL),(2,'HB_GYS001','石家庄帅益食品贸易有限公司','长期合作伙伴,主营产品:饮料、水饮料、植物蛋白饮料、休闲食品、果汁饮料、功能饮料等','王军','13309094212','河北省石家庄新华区','0311-67738876',1,'2020-04-13 04:20:40',NULL,NULL),(3,'GZ_GYS001','深圳市泰香米业有限公司','初次合作伙伴,主营产品:良记金轮米,龙轮香米等','郑程瀚','13402013312','广东省深圳市福田区深南大道6006华丰大厦','0755-67776212',1,'2020-03-21 16:56:07',NULL,NULL),(4,'GZ_GYS002','深圳市喜来客商贸有限公司','长期合作伙伴,主营产品:坚果炒货.果脯蜜饯.天然花茶.营养豆豆.特色美食.进口食品.海味零食.肉脯肉','林妮','18599897645','广东省深圳市福龙工业区B2栋3楼西','0755-67772341',1,'2020-03-22 16:52:07',NULL,NULL),(5,'JS_GYS001','兴化佳美调味品厂','长期合作伙伴,主营产品:天然香辛料、鸡精、复合调味料','徐国洋','13754444221','江苏省兴化市林湖工业区','0523-21299098',1,'2020-02-22 16:52:07',NULL,NULL),(6,'BJ_GYS002','北京纳福尔食用油有限公司','长期合作伙伴,主营产品:山茶油、大豆油、花生油、橄榄油等','马莺','13422235678','北京市朝阳区珠江帝景1号楼','010-588634233',1,'2020-03-21 17:52:07',NULL,NULL),(7,'BJ_GYS003','北京国粮食用油有限公司','初次合作伙伴,主营产品:花生油、大豆油、小磨油等','王驰','13344441135','北京大兴青云店开发区','010-588134111',1,'2020-04-13 00:00:00',NULL,NULL),(8,'ZJ_GYS001','慈溪市广和绿色食品厂','长期合作伙伴,主营产品:豆瓣酱、黄豆酱、甜面酱,辣椒,大蒜等农产品','薛圣丹','18099953223','浙江省宁波市慈溪周巷小安村','0574-34449090',1,'2020-01-21 06:02:07',NULL,NULL),(9,'GX_GYS001','优百商贸有限公司','长期合作伙伴,主营产品:日化产品','李立国','13323566543','广西南宁市秀厢大道42-1号','0771-98861134',1,'2020-03-21 19:52:07',NULL,NULL),(10,'JS_GYS002','南京火头军信息技术有限公司','长期合作伙伴,主营产品:不锈钢厨具等','陈女士','13098992113','江苏省南京市浦口区浦口大道1号新城总部大厦A座903室','025-86223345',1,'2020-03-25 16:52:07',NULL,NULL),(11,'GZ_GYS003','广州市白云区美星五金制品厂','长期合作伙伴,主营产品:海绵床垫、坐垫、靠垫、海绵枕头、头枕等','梁天','13562276775','广州市白云区钟落潭镇福龙路20号','020-85542231',1,'2020-01-21 06:12:17',NULL,NULL),(12,'BJ_GYS004','北京隆盛日化科技','长期合作伙伴,主营产品:日化环保清洗剂,家居洗涤专卖、洗涤用品网、墙体除霉剂、墙面霉菌清除剂等','孙欣','13689865678','北京市大兴区旧宫','010-35576786',1,'2020-01-21 12:51:11',NULL,NULL),(13,'SD_GYS001','山东豪克华光联合发展有限公司','长期合作伙伴,主营产品:洗衣皂、洗衣粉、洗衣液、洗洁精、消杀类、香皂等','吴洪转','13245468787','山东济阳济北工业区仁和街21号','0531-53362445',1,'2020-01-28 10:52:07',NULL,NULL),(14,'JS_GYS003','无锡喜源坤商行','长期合作伙伴,主营产品:日化品批销','周一清','18567674532','江苏无锡盛岸西路','0510-32274422',1,'2020-04-23 11:11:11',NULL,NULL),(15,'ZJ_GYS002','乐摆日用品厂','长期合作伙伴,主营产品:各种中、高档塑料杯,塑料乐扣水杯(密封杯)、保鲜杯(保鲜盒)、广告杯、礼品杯','王世杰','13212331567','浙江省金华市义乌市义东路','0579-34452321',1,'2020-06-22 10:01:30',NULL,NULL);
    向smbms_role表插入数据
    insert into `smbms_role`(`id`,`roleCode`,`roleName`,`createdBy`,`creationDate`,`modifyBy`,`modifyDate`) values (1,'SMBMS_ADMIN','系统管理员',1,'2020-01-01 00:00:00',NULL,NULL),(2,'SMBMS_MANAGER','经理',1,'2020-02-02 00:01:00',NULL,NULL),(3,'SMBMS_EMPLOYEE','普通员工',1,'2020-02-03 00:00:00',NULL,NULL);
    向smbms_user表插入数据
    insert into `smbms_user`(`id`,`userCode`,`userName`,`userPassword`,`gender`,`birthday`,`phone`,`address`,`userRole`,`createdBy`,`creationDate`,`modifyBy`,`modifyDate`) values (1,'admin','系统管理员','1234567',1,'1983-10-10','13688889999','山东省日照市东港区成府路207号',1,1,'2020-03-21 16:52:07',NULL,NULL),(2,'liming','李明','0000000',2,'1983-12-10','13688884457','山东省日照市东港区前门东大街9号',2,1,'2020-03-01 00:00:00',NULL,NULL),(5,'hanlubiao','韩路彪','0000000',2,'2001-06-05','18567542321','山东省日照市东港区北辰中心12号',2,1,'2020-02-11 19:52:09',NULL,NULL),(6,'zhanghua','张华','0000000',1,'1980-06-15','13544561111','山东省日照市东港区学院路61号',3,1,'2020-02-11 10:51:17',NULL,NULL),(7,'wangyang','王洋','0000000',2,'2001-12-31','13444561124','山东省青岛市三二二区西二旗辉煌国际16层',3,1,'2020-06-11 19:09:07',NULL,NULL),(8,'zhaoyan','赵燕','0000000',1,'1999-03-07','18098764545','山东省青岛市东科区回龙观小区10号楼',3,1,'2020-04-21 13:54:07',NULL,NULL),(10,'sunlei','孙磊','0000000',2,'1998-01-04','13387676765','山东省日照市朝阳区管庄新月小区12楼',3,1,'2020-05-06 10:52:07',NULL,NULL),(11,'sunxing','孙兴','0000000',2,'1997-03-12','13367890900','北京市朝阳区建国门南大街10号',3,1,'2020-01-09 16:51:17',NULL,NULL),(12,'zhangchen','张晨','0000000',1,'1986-03-28','18098765434','朝阳区管庄路口北柏林爱乐三期13号楼',3,1,'2019-06-09 05:52:37',1,'2020-04-14 14:15:36'),(13,'dengchao','邓超','0000000',2,'1981-11-04','13689674534','北京市海淀区北航家属院10号楼',3,1,'2020-07-01 08:02:47',NULL,NULL),(14,'yangguo','杨过','0000000',2,'1989-01-01','13388886623','北京市朝阳区北苑家园茉莉园20号楼',3,1,'2020-02-01 03:52:07',NULL,NULL),(15,'zhaomin','赵敏','0000000',1,'1989-12-04','18099897657','山东省临沂市昌平区天通苑3区12号楼',2,1,'2020-01-12 12:02:12',NULL,NULL);
    此外,本系统中所用到的用户性别和用户身份代码如表3.5.1至表3.5.2所示。
    用户性别代码表



    代码
    说明




    1



    2




    用户身份代码



    代码
    说明




    1
    系统管理员


    2
    经理


    3
    普通员工



    4 各功能模块的设计与实现4.1 系统开发条件4.1.1 开发语言系统使用的开发语言是Java。Java具有简单性、面向对象、分布式、健壮性、安全性、平台独立与可移植性、多线程等特点。Java可以编写桌面应用程序、Web应用程序、分布式系统应用程序等。正是因为Java语言拥有如此诸多的优秀特性,所以我们选择了它作为开发超市订单管理系统,使得整个开发、调试过程更加高效。
    4.1.2 开发框架超市订单管理系统以SSM架构作为支撑,分为表现层、业务层和持久层三层,实现后台数据更新。该架构由Spring MVC、Spring和MyBatis三个开源框架整合而成,用于开发结构合理,性能优越,代码健壮的应用程序。

    4.1.3 前端框架由于本系统是Web应用,所以使用了HTML5+CSS3+JavaScript的方式实现前端页面。实现过程中参考了Bootstrap前端开发框架。Bootstrap是Twitter退出的一个用于前端开发的开源工具包。在设计前端页面时,参考了Bootstrap的相关开源代码。
    4.1.4 集成开发环境编程所使用的集成开发环境是Eclipse,是著名的跨平台的自由集成开发环境(IDE)。Eclipse 是一个开放源代码的、基于Java的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。本次系统便选用了Eclipse作为开发平台。
    4.1.5 Web应用服务器Tomcat由Apache、Sun和其他一些公司及个人共同开发而成。由于有了Sun的参与和支持,最新的Servlet和JSP规范可以在Tomcat中得到体现。因为Tomcat技术先进、性能稳定,因而成为目前比较流行的Web应用服务器。本次系统选用的便是Tomcat作为应用服务器。
    4.1.6 数据库管理系统本系统使用的数据库管理系统是MySQL Community。MySQL是一个关系型数据库管理系统,由瑞典MySQL AB公司开发。在WEB应用方面,MySQL是最好的RDBMS (Relational Database Management System,关系数据库管理系统)应用软件。
    系统中的数据库以及数据库中的所有关系模式都使用MySQL进行处理。
    4.2 用户界面设计完成数据库创建和功能说明以后,我们进行下一步工作,即设计用户界面,完成了系统要求的 5 项主要功能。
    我们把超市订单管理系统的窗体分成5个主要部分,如下图所示。
    4.2.1 订单管理
    4.2.2 供应商管理
    4.2.3 用户管理
    4.2.4 修改密码
    4.2.5 登录注销
    4.3 功能模块说明5.3.1 订单信息添加、查询、修改与删除订单信息查看:为了对订单浏览信息,能够实现浏览的功能是十分必要的。管理员输入需要搜索的相应信息,点击查看按钮后系统将寻找到的数据展示到网页中。

    订单信息添加:作为超市订单管理系统,订单信息的管理是很重要的。每当采购部门增加新的订单时,订单信息就要增加。超市也可能因为其它原因增加订单信息,订单添加模块都可以做出快捷的解决方案。管理员输入相应的信息,点击提交后系统将数据保存到数据库中。

    订单信息修改:根据订单编号可以查询订单详细信息,然后修改订单的所有信息。系统从数据库中读取数据并显示到页面上,管理员修改数据后,点击修改按钮,系统将更新表中的数据。

    订单信息删除:根据订单编号可以删除该订单的信息。管理员选择需要删除订单名称并点击删除按钮,系统将从数据库中删除相应数据。
    订单信息查询:在成千上万种商品种,如果人为寻找某一个商品肯定是不可能的,只有通过商品信息查询模块才能为用户或管理人员解决这个难题。根据订单名称可以查询该订单的信息。管理员输入订单名称并点击查询按钮,系统将从数据库中查询相应的数据并显示到页面上。

    5.3.2 供应商信息添加、查询、修改与删除供应商查询界面:供应商查询界面提供了供应商的信息,可根据供应商名称的关键字进行筛选查询,并提供了添加供应商、查看供应商详细信息、修改供应商信息、删除供应商的功能。

    供应商查看详情界面:在供应商查询界面点击具体供应商操作列表的查看按钮,可以查看供应商的具体信息,包括:供货商编码、供货商名称、详细描述、联系人、联系电话、地址、微信。

    供应商修改页面:若供应商信息变动,管理员可通过供应商信息修改功能对供应商信息进行更新,更新后的数据将保存到数据库中。

    商品供应商信息删除:企业倒闭或者经营策略的改变,当它对超市商品的供应没有作用时,商品供应商厂家信息的删除是正常的。管理员输入供应商名称查询数据表中的数据并显示到页面上,点击删除后系统将表中的相应数据删除。
    供应商添加界面:与供应商达成交易后,管理员在供应商添加页面填写供应商具体信息,填写完毕点击提交,添加后的数据将保存到数据库中。

    5.3.3 用户信息添加、查询、修改与删除用户管理页面:通过输入用户名和身份查询用户。当不记得用户名的具体名字时,只输入用户名的其中一个字,会检索出所有带这个字的用户,方便管理员查询管理。点击右边链接添加用户,会连接到相关网页添加用户信息。点击操作里的查看、修改等可以进行相应的改、删、查操作。

    用户信息删除:当企业员工离职时,或者经过一段时间后,会发现用户表中一些信息时无用的,用户删除模块可以解决这样的问题。
    添加用户信息:填写用户相关信息,下面有两个按钮,可以选择重置或者提交。

    5.3.4 修改密码为了系统的安全,用户的应该只有用户个人才能修改,这不仅保证了整个公司的利益也保护了个人隐私。用户在输入相应的用户编号,填写旧密码以及新密码后,点击提交,重置密码成功。发现输入错误时,可以手动删除或者点击重置按钮,重新填写。

    修改用户密码成功后,会弹出修改用户密码成功页面,如图4.3.14所示。

    5.3.5 登录/注销输入用户名以及用户密码登录进入超市订单管理界面,可以查看管理信息。管理员可以对相关数据进行增、改、查等操作,也可以注销退出系统。
    5 实训总结5.1 所遇困难在实现本系统时遇到的困难主要体现在两个方面,一是系统的前端页面的设计,二是怎样Web与数据库实现交互。
    系统前端页面的设计困难的解决是通过参考著名的前端框架Bootstrap实现的。Bootstrap框架提供了许多精美的组建、布局,还开放了源代码供参考。在此基础上我们还加入了一些利用JavaScript代码实现的美化效果,使得前端设计更加美观。
    实体Web与数据库交互的解决得益于SSM框架的三层Spring MVC、Spring和MyBatis,能够分离处理数据库与Web层的视图,从而达到交互的目的。
    此外,在编写后端的时候,变量的大小写、系统配置也是困难重重。好在,在反复编写之后,迅速熟悉的技巧,能够让页面自由切换。系统配置更是反复在网上求证,得以解决。
    5.2 实验心得这一次合作开发超市订单管理系统,从开始选择课题的困惑到最终完成了一个我们还算满意的作品,使我学到了很多东西。从设计数据库到编写后台代码,链接数据库,在网页上显示,令人印象深刻。反复查阅资料,启动Tomcat到凌晨0点,都是藏着对这次项目的努力。其实,从一开始选择哪个题目是否用SSM框架来开发我一直也犹豫过,像国内势头正旺的ThinkPHP,易学易用,完善的中文开发文档,遇到问题或者bug可以非常容易的在中文社区得到解答。但是我最后选择了SSM框架,不仅仅因为它广泛,而是我希望能够挑战自己。经过这一个周的磨练,我最大的收获除了学到了真正可以应用的知识外,更重要的是学会了项目合作开发的经验。
    7 留言 2020-08-05 15:32:05 奖励46点积分
  • 使用AlphaBlend函数实现位图半透明绘制

    背景自己使用VC和VS写过很多小游戏,而且不是用现成的游戏引擎,纯粹是使用GDI函数来进行绘图。所以,积累了一些绘图的经验。
    那么,对于位图半透明的绘制,在小游戏中使用也比较多。例如烟雾、光等之类的绘制。在没有了解 AlphaBlend 函数之前,绘制半透明位图都是获取两张图片的RGB数据,然后按指定透明度计算出混合后的RGB的值,再显示出来。这样,需要自己计算的过程,算是麻烦。而现在,AlphaBlend 函数直接封装了这步操作,提供了方便使用的接口。
    现在,我们就来介绍使用 AlphaBlend 函数绘制半透明位图,写成文档,分享给大家。
    函数介绍AlphaBlend 函数
    该函数用来显示具有指定透明度的图像。
    函数声明
    AlphaBlend( HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int hHeightDest, HDC hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc, BLENDFUNCTION blendFunction );
    参数

    hdcDest:指向目标设备环境的句柄。
    nXOriginDest:指定目标矩形区域左上角的X轴坐标,按逻辑单位。
    nYOriginDest:指定目标矩形区域左上角的Y轴坐标,按逻辑单位。
    nWidthDest:指定目标矩形区域的宽度,按逻辑单位。
    hHeightdest:指向目标矩形区域的高度,按逻辑单位。
    hdcSrc:指向源设备环境的句柄。
    nXOriginSrc:指定源矩形区域左上角的X轴坐标,按逻辑单位。
    nYOriginSrc:指定源矩形区域左上角的Y轴坐标,按逻辑单位。
    nWidthSrc:指定源矩形区域的宽度,按逻辑单位。
    nHeightSrc:指定源矩形区域的高度,按逻辑单位。
    blendFunction:指定用于源位图和目标位图使用的alpha混合功能,用于整个源位图的全局alpha值和格式信息。最后一个参数blendFunction是一个BLENDFUNCTION结构。
    BLENDFUNCTION结构介绍如下:
    typedef struct _BLENDFUNCTION {​ BYTE BlendOp;​ BYTE BlendFlags;​ BYTE SourceConstantAlpha;​ BYTE AlphaFormat;}BLENDFUNCTION, PBLENDFUNCTION, LPBLENDFUNCTION;
    BlendOp: 这个参数必须也只能为AC_SRC_OVER(0x00),意思就是把源图片覆盖到目标之上。
    BlendFlags: 必须为0 。
    SourceConstantAlpha: 简写为SCA,指定源图片的透明度,这个值是会和源图片的Alpha通道值合并计算的;即设置透明度,0为完全透明,255为完全不透明 。
    AlphaFormat: 可以填两种,一种是0x00,一种是AC_SRC_ALPHA (0x01);0表示常量alpha值,AC_SRC_ALPHA表示每个像素有各自的alpha通道。

    返回值

    如果函数执行成功,那么返回值为TRUE;如果函数执行失败,那么返回值为FALSE。

    透明度绘制介绍下面,我们举例说明,什么是透明度绘制。现在,有两张位图,一张是背景图:

    另一张是其它图片:

    那么,半透明绘制,就是要将两张图片绘制在一起,实现下面的效果:

    实现原理半透明位图绘制的功能实现,主要是靠 AlphaBlend 函数完成的。AlphaBlend 函数的最后一个参数 BLENDFUNCTION 结构指定用于源位图和目标位图使用的alpha混合功能。
    其中,BLENDFUNCTION 结构的 SourceConstantAlpha 变量,指定了源图片的透明度,这个值是会和源图片的 Alpha 通道值合并计算的;即设置透明度,0为完全透明,255为完全不透明 。也就是说,只要我们控制这个变量的值,就控制了位图绘制的透明度。
    编码实现导入库文件#include <WinGDI.h>#pragma comment(lib, "Msimg32.lib")
    绘制背景位图BOOL PaintBmp(HWND hWnd){ // 获取窗口的客户区域的显示设备上下文环境的句柄 HDC hDC = ::GetDC(hWnd); // 创建一个与hDC兼容的内存设备上下文环境 HDC hBuf = ::CreateCompatibleDC(hDC); // 加载位图, 获取位图句柄 HBITMAP hBmp = (HBITMAP)::LoadImage(NULL, "image\\bg.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); // 选择位图句柄到hBuf中, 并获取返回的原来位图句柄 HBITMAP hOldBmp = (HBITMAP)::SelectObject(hBuf, hBmp); // 绘图 ::BitBlt(hDC, 0, 0, 764, 397, hBuf, 0, 0, SRCCOPY); // 还原位图对象 ::SelectObject(hBuf, hOldBmp); // 释放位图 ::DeleteObject(hBmp); // 释放兼容的内存设备上下文环境 ::DeleteDC(hBuf); // 释放设备上下文环境 ::ReleaseDC(hWnd, hDC); return TRUE;}
    指定透明度绘制BOOL PaintAlphaBlendBmp(HWND hWnd){ // 获取窗口的客户区域的显示设备上下文环境的句柄 HDC hDC = ::GetDC(hWnd); // 创建一个与hDC兼容的内存设备上下文环境 HDC hBuf = ::CreateCompatibleDC(hDC); // 加载位图, 获取位图句柄 HBITMAP hBmp = (HBITMAP)::LoadImage(NULL, "image\\1.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); // 选择位图句柄到hBuf中, 并获取返回的原来位图句柄 HBITMAP hOldBmp = (HBITMAP)::SelectObject(hBuf, hBmp); // 设置透明度参数 BLENDFUNCTION ftn = { 0 }; ftn.BlendOp = 0; ftn.BlendFlags = 0; ftn.SourceConstantAlpha = 200; // 透明度指定 ftn.AlphaFormat = 0; // 指定透明度绘制 ::AlphaBlend(hDC, 0, 0, 650, 350, hBuf, 0, 0, 650, 350, ftn); // 还原位图对象 ::SelectObject(hBuf, hOldBmp); // 释放位图 ::DeleteObject(hBmp); // 释放兼容的内存设备上下文环境 ::DeleteDC(hBuf); // 释放设备上下文环境 ::ReleaseDC(hWnd, hDC); return TRUE;}
    程序测试调用上述封装好的函数进行测试,两幅位图成功按照指定的透明度混合显示:

    总结这个小程序重点是要理解 AlphaBlend 函数的使用方式,可以自己更改透明度的值尝试几次,加深理解。这个函数同样支持对位图伸缩绘制,使用起来比较灵活。
    4 留言 2018-12-20 12:36:17 奖励5点积分
  • 编程学习笔记之vector学习心得

    一、vector介绍在c++中,vector是一个可以存储各种类型对象的集合容器,可以把它看成一种能够动态【吃胖】或【减肥】的数组。vector是一个类模板,使用【vector<T>】格式可以生成一个模板类,考虑到vector容器所应具备的一些特征,vector应该设计成不管传递给vector的模板参数是什么数据类型或类类型,一个vector生成的模板类,其内存大小应该是不变的。为验证我的想法,我在我的机器上测验了一下,使用【sizeof(A)】获得的内存大小为16,其中A为我的一个自定义类类型,不管我把A制造成多大,得到的结果总是 =16,然后我用高大上的开发工具vs13智能显示了一下vector模板类里面的数据成员,发现一共有4个,它们分别是:_Myfirst、_Myend、_Mylast和忘了怎么拼写的“myblabla。。。”,如果我没猜错的话,以上四种类型应该是三个指针和一个整型,它们所代表的意思分别是起始位置、终点位置、当前位置以及当前vector类所占据的内存大小。
    值得一提的是,vector是一种线性顺序式集合容器,虽然它可以动态分配存储空间,但它所占用的内存,是一块连续存储的内存。看到这里也许您有点迷惑,既然是动态分配了,为何还会像静态变量那样使用连续存储内存呢?让我先从设计初衷解释一下,首先前辈们在制造vector时,希望它具有数组一样的高效率,当我们通过下标访问vector中的某个元素时,可以直接用指针偏移量的方式寻址访问,而不要像链表那样弄个循环找到那个位置。其次再从内存分配机制这个角度阐述一下,为求容易理解,我使用例子吧:比如我们用vector生成一个int类容器A,那么机器首先为这个A在堆中开辟了一个大小为100的连续内存区域【其中100是我随便举得例子,A本身的内存是16,A相当于一个标签,贴在一个容器上面,这个容器现在的大小是100】,并把这块区域当成是A的对应容器,然后程序运行到某个位置时,需要往容器里面添加一个元素,但程序发现A容器已经饱和,一旦添加了这个元素,A现在对应的容器会容纳不下,怎么办呢?程序会重新向堆中申请一块更大的连续内存,比如200的大小。如果有,则把原先的100连续内存中的元素逐个拷贝到新内存区域中去,最后在拷贝完成之后,销毁原先的100内存。这个过程是比较麻烦点,但好歹达到了目的——访问方便。
    vector支持很多操作,向尾部添加元素可以用【push_back(T);】,其中T为模板参数的引用;在尾部销毁元素可以用【pop_back()】;在某个位置插入元素可以用【insert(n,T)】,其中n代表位置,T是模板参数的引用;判断容器是否为空可以用【empty()】,如果容器为空,此表达式会返回true,否则返回false;清空容器用clear()等。除此之外,vector还定义了很多操作符重载,比如【!=】、【==】等。
    二、vector的使用要在程序中使用vector,首先我们要包含它所在的库【#include <vector>】,并且还要做好相应的using说明【using std::vector;】,但为了写代码方便,我的说明一向都写成这样【using namespace std;】。做好以上工作后,我们就可以在自己的代码中使用vector生成模板类了。
    接下来说说vector生成模板类最常见的四种初始化情况,比如下面4个重载构造函数:
    // 创建一个int类型的vector容器,但这个容器是空的vector<int> vi; // 创建一个int类型的vector容器,该容器暂有10个int元素,每个元素被赋初始值0vector<int> vi2(10); // 创建一个int类型的vector容器,该容器暂有5个int元素,每个元素被赋值99vector<int> vi3(5,99); // 创建一个int类型vector容器,该容器拷贝了vi2,既该容器具有10个值为0的元素vector<int> vi4(vi2);
    说了这么多,下面让我们写一个简单小程序来巩固一下,该程序使用vector生成一个int型容器,然后由我们自己随便给容器添加若干个元素,接着程序负责求出这些元素的平均值以及找出最大值,最后程序把求出的平均值插入到容器中间,再对容器内所有的元素按照递增顺序排列,代码如下:
    #include <iostream>#include <vector>#include <algorithm> //调用sort排序函数必须包含的头文件using namespace std;int main(){ int value = 0, average = 0, max = 0, num = 0; vector<int>vi; while (cin >> value) vi.push_back(value); //利用循环向容器插入数值元素 cout << "现在容器内所有元素是:"; for (vector<int>::size_type vinum = 0; vinum != vi.size(); vinum++) //使用size_type声明一个变量,并用这个变量当作vi的下标 cout << vi[vinum]<<" "; value = 0; for (vector<int>::const_iterator i = vi.begin(); i != vi.end(); i++) //这次换个方式遍历,i是vector容器的指针 { value += *i; num++; *i > max ? (max = *i) : 0; } cout << value << endl; average = value / num; cout << "容器内平均值是" << average << "\n最大值是" << max << endl; for (num = 0; num != vi.size(); num++); num /= 2; vi.insert(vi.begin() + num, 1, average); //在这里我们把平均值插入到容器中间 sort(vi.begin(), vi.end()); //在这里我们对容器内所有元素进行排序 cout << "经过处理,容器内元素被处理成如下排列:"; for (vector<int>::const_iterator i = vi.begin(); i != vi.end(); i++) //最后我们输出经过处理的容器内所有元素 cout << " " << *i; cout << endl; return 0;}
    0 留言 2021-01-20 09:20:55 奖励37点积分
  • 基于java swing和MySql实现的药品销存管理系统

    此项目可以说是半原创,因为是根据一个已有项目进行变造而来,拥有的功能也比原项目更多
    一.药品销存管理管理系统的设计与实现打开系统后为登录界面,用户输入账号密码后进入系统,系统分为管理员、药店销售员、药品采购员三类用户,不同类型用户显示界面不同。
    管理员功能:

    库存管理:实现药品的增删改查,包括药品名称、规格、进货单价、销售单价、药品数量、厂商
    销售管理:销售记录的增删改查,包括药品名称、销售价、销售时间、销售数量,如果是会员,自动积分
    员工信息管理:员工信息增删改查,包括员工账号、账号类型、密码等
    个人账号设置:包括修改密码等

    销售员功能:

    销售管理:销售记录的增删改查,包括药品名称、销售价、销售时间、销售数量,如果是会员,自动积分
    销售记录管理:销售记录的增删查改
    个人账号设置:包括修改密码等

    药品采购员功能:

    进货管理:药品增加,包括新进药品的名称、规格、进货单价、进货数量、厂商,如果新进药品信息与库存药品信息不同,则需分开保存
    个人账号设置:修改密码等

    二、MySQL表2.1 药品信息表结构设计药品信息主要方便存储每个药品的参数,比如药品名,库存,进价,售价,生产厂商。表的具体结果如下:

    2.2 药品销售记录表该表的设计主要是方便对药品销售记录的查看,表的具体结构设计如下:

    2.3 员工级别表结构设计该表主要用于管理员对员工分配职位,使员工得到相应的权限

    2.4 员工信息表结构设计该表主要用于储存员工信息,包括员工的用户名、密码、姓名、员工级别。

    三、本系统的结构图如下:
    四、各模块功能4.1 系统管理员模块功能该模块主要包括员工记录设置、库存记录设置和销售记录设置、账号设置等。

    员工记录设置:该功能选项用于系统管理员对员工用户名、密码、姓名、所属角色查看操作,方便管理员更加直观的了解员工信息
    库存记录设置:该子模块主要是系统管理员对药品库存信息的查看
    销售记录设置:该子模块主要是系统管理员对药品销售情况的查看
    账号设置设置:该子模块主要是用于管理员对员工信息的增、删、改的操作,还有员工职位的分配

    4.2 系统售货员模块功能该模块主要包括销售记录设置、销售管理设置和个人账号管理设置、添加销售设置。

    销售记录设置:该子模块主要是系统销售员对药品销售记录信息的查看
    销售管理设置:该子模块是系统销售员对销售记录信息进行修改和删除操作
    个人账号管理设置:该子模块是系统销售员对个人账户密码进行更改操作
    添加销售设置:该子模块是系统销售员添加销售信息操作

    4.3 系统进货员模块功能该模块主要包括进货管理设置、库存查看设置和个人账号管理设置、库存管理设置。

    库存查看设置:该子模块主要是系统进货员对药品库存记录信息的查看
    进货管理设置:该子模块是系统进货员对药品信息进行添加操作
    个人账号管理设置:该子模块是系统销售员对个人账户密码进行更改操作
    库存管理设置:该子模块是系统进货员对药品库存信息进行修改、删除操作

    五、用户登录注册模块程序设计本模块主要是用户通过药品销存系统的首页登录进入该系统。用户输入正确的用户名和密码,系统会根据用户的身份进行相应权限划分;如果登录信息有错误,则系统提示登入错误的信息,并且禁止系统用户进行任何操作。
    药品销存管理系统的登录主页面如图所示。

    六、系统功能模块的实现6.1 管理员模块本模块中最主要的是对员工记录、库存记录、销售记录的查看操作,还有对员工信息进行添加,修改,删除等操作。
    其中系统管理员模块功能的操作页面如下图所示:

    6.2 售货员模块本模块中最主要的是销售记录、销售管理、账号管理、添加销售操作,对数据库record表进行添加,修改,删除、查找等操作。
    其中系统售货员模块功能的操作页面如下图所示:

    6.3 进货员模块此模块主要用来进货管理、库存查看、库存管理、账号管理操作,对数据库medic表进行添加,修改,删除、查找等操作。
    其中系统管理员对订单进行管理的操作页面如下图所示:
    1 留言 2021-01-18 09:00:49 奖励56点积分
  • 基于Android实现的小型在线订餐APP饿了么

    1 绪论1.1 研究背景进入到移动互联时代,人们会愈加频繁地使用手机,安卓系统占据了智能手机界很大市场份额,安卓开发,在今后的一段时间之内,依旧会是热门之一。为实现人们网上在线点外卖的需求,需要开发一个安卓外卖app,实现包括商品的查看,下单,支付,用户信息修改等功能。
    网上在线订餐作为手机购物的一种表现形式,采用将图片和文字内容相结合的方式将商品信息展现给用户,这个方式使用户可以查看不同来源的商品信息以及不同层次的信息,以最大程度上了解商品的具体信息,同时可以搜索距离较近的商品信息。
    在线订餐客户端市场已经成为移动媒体竞争的焦点,互联网各大门户网站纷纷使出浑身解数希望占领尽可能多的移动在线订餐客户端市场份额。根据数据显示,在中国,在线订餐客户端市场排名前三的客户端分别为美团,饿了么和口碑,其中,这些在线订餐客户端在功能上过于繁重,对Android的设备系统版本有一定的要求,没有很好地做到向下兼容低版本,在较低版本的手机无法成功安装这些在线订餐客户端。
    1.2 研究现状目前市场上我们所见的个性化在线订餐APP 就是以“饿了么”为代表的,这些个性化在线订餐 APP 大多通过大数据进行用户行为分析,了解到受众的需求,然后利用算法技术,将受众感兴趣的美食商品信息推送给用户的 APP.在线订餐 APP 拥有强大的技术基础,满足了大部分受众的“使用与满足”需求,因而能在传统新闻资讯诸APP 中脱颖而出并长期占据领先地位。
    饿了么通过用户行为分析和精准定位给用户个性化进行个性化资讯推荐,这一切都依赖于饿了么的算法技术,依靠算法技术对大数据进行分析,而这算法技术的核心就是网络爬虫和矩阵筛选。
    在信息大爆炸的今天,用户身上贴满了各式各样的标签,性别、年龄、职业等都是受众的标签,根据这些标签将用户进行简单的分类。但是饿了么利用算法数据对受众的个性化特征进行更加精确的分析,通过受众登录客户端的第三方账户的社交情况或者直接注册登录客户端的浏览信息情况进行算法分析,从而得出受众的使用需求。
    随着互联网和移动终端的发展,用户对信息获取的要求越来越高,从以前单一的接受信息到现在想要获取自己感兴趣的资讯。饿了么涉及各种外卖信息,可以快速搜素到商品品信息,也可以推荐用户关心的内容。
    除了首页的个性化推荐商品之外,饿了么还有药品、便利卖场等多个频道,用户可以选择设置自己感兴趣的频道进行内容浏览,在用户浏览这些内容时产生的数据也会被今日头条记录下来形成更加精确的图谱,随着使用次数的不断深入,用户的使用图谱也会更加精确,越来越符合用户的使用需求。
    1.3 研究目的与意义本文分析了小型餐饮店管理与发展的现状和面临的问题,发现传统的服务模式已经不能适应市场发展的需求。随着网络技术的发展和普及,方便、快捷、个性化的网.上订餐服务正在进入人们的生活。针对这类问题,结合当代科技发展的最新成果,我们开发的这款饿了么app,目的是为了便于使用者方便地在线订餐,并且,支持个人信息的个性化设置,以便于其获取喜欢的商品信息。
    此外,Android 平台在线订餐客户端软件的开发可以进一步扩大各个商家的商品信息的覆盖面, 让广大公众能够随时随地方便快捷地获取商品信息,了解喜好的商家和商品。
    2 需求分析本系统的需求分析部分,主要是通过对系统用例图、系统整体的业务需求、系统功能需求、系统性能需求以及对系统的可行性分析等的分析与总结,对系统的需求有一个整体的了解,再根据这些需求确定系统的功能,同时确保系统的可扩展性。
    2.1 系统用例图
    2.2 业务需求分析伴随着我国市场经济的高度发展,企业越来越依赖于市场和客户,这种依赖关系已经开始逐步提升到关乎企业生存的高度。如何获得更多客流量,如何通过小的代价而使商家获得更大的知名度,这是使企业得以生存、发展、壮大的关键。年销售过亿的众多电子商务案例让人们看到了更多的商机。与应用场景相当有限的PC (个人电脑)相比,生而具备便携属性的手机给了电商们更大的想象空间。移动电子商务由此产生了。移动电子商务就是利用手机、PDA及掌上电脑等无线终端进行的B2B、B2C或C2C的电子商务。它将因特网、移动通信技术、短距离通信技术及其它信息处理技术完美的结合,使人们可以在任何时间、任何地点进行各种商贸活动,实现随时随地、线上线下的购物与交易、在线电子支付以及各种交易活动、商务活动、金融活动和相关的综合服务活动等。
    而Android操作系统凭借着自己开放的平台允许任何移动终端厂商加入到Android联盟中,从而使得越来越多的用户倾向于选择操作系统为Android的手机品牌。选择设计基于Android平台的软件,毫无疑问能覆盖更多的受众。
    随着移动互联网的快速发展,在线订餐APP也成为了大多用户的一部分,只要有智能手机,就能随时随地点餐,而且能在指定时间内送达指定地点,完全给了用户足不出门的理由,当之无愧的偷懒神器!本课题研究的是制作一个小型的在线订餐APP——“饿了吗?”,主要是为人们提供一个在线平台,了解附近餐厅的具体情况,在线购买,方便快捷!
    2.3 功能需求分析根据上文中对业务需求的调研,本系统的具体功能需求如下:

    登录:点击登录按钮,根据注册的手机号和密码进行登录
    注册:用户根据系统提示,填写信息,完成注册
    修改信息:已登录APP的用户点击头像,可以进入修改信息界面,按照提示修改信息
    进入首页:已登录的用户在“饿了吗”首页可以选择店家,也可以在搜索栏搜索店家和菜品
    管理订单:已登录的用户可以点击订单,可以查看历史订单,已配送和未配送订单,对于交易成功的订单可以对该店家进行评价
    进入店家:选择心仪菜品加入购物车,并在购物车下单



    2.4 性能需求分析
    在线支持用户并发业务数据的综合处理时间不超过2秒
    系统的最大并发用户数为100
    系统运行稳定,故障率较低,并且可维护性高
    系统具有可扩展性,添加功能模块不需要对系统进行重构,只需将新开发的功能接口接入即可

    2.5 系统可行性分析本章节主要对本课题的系统可行性进行分析,系统可行性分析主要包括技术可行性分析和经济可行性分析两方面的内容。技术可行性分析是对开发本系统所采用的技术进行简单的介绍,并且对其进行分析;经济可行性分析则是从系统的成本到用户使用的经济效应进行分析。
    实现“饿了吗?”在线订餐系统需要使用Windows系统和Android Studio工具进行开发,后端云服务器BMOB为整个应用提供数据支持,Android端采用传统的MVC三层架构进行开发。
    3 系统总体设计本系统的总体设计部分主要是对本系统的设计方案进行论证,并对系统的总体进行设计,最后对系统功能的设计思路以及系统的业务流程进行说明,其中包括数据库的选择,开发框架的选择等。
    3.1 系统总体设计
    3.2 功能模块设计3.2.1 首页模块功能设计首页模块的功能有店家显示和搜索界面。

    3.2.2 个人信息模块功能设计用户登录后,可以处理登录信息和修改个人信息。可修改的内容包括性别,昵称,生日和地区。

    3.2.3 登录模块功能设计登录功能是保障系统安全的屏障。本系统采用手机号密码登录这一种登录方式。

    3.2.4 注册模块功能设计用户可以通过输入手机号和密码来进行注册,并确认密码保证正确。

    3.2.5 店家详情模块功能设计用户可以点击进入店家详情查看店家菜品,加入购物车以及下单。

    3.2.6 订单详情模块功能设计用户点击订单后,订单详情展示,用户可以评论该订单。

    3.3 数据库设计本节将对本系统的数据库设计进行叙述,包括数据库的E-R图设计,以及数据库表的设计。表设计部分以每张表为单位对表的字段、字段类型、字段作用等进行叙述并以表格的形式进行详细展示。
    3.3.1 数据库E-R图用户表

    商家表

    商品表

    订单表

    评价表

    3.3.2 系统数据库表设计本系统数据库表共有5张,下面将展示每张表的设计以及详细说明。
    (1)用户表(User)用户表主要用于保存用户的登录信息,包括唯一标识number,密码,性别,头像,生日,昵称,地址。这张表尤为重要,是系统能否进入的关键表。



    Number
    Password
    Sex
    Image
    Birth
    Name
    Address




    17600006117
    123456

    Tx1jpg
    1.24
    赵先生
    南京市秦淮区


    15600007996
    123456

    Tx2.jpg
    4.30
    何先生
    盐城市阜宁县


    15600003302
    123456

    Tx3.jpg
    12.13
    陈先生
    南京市江宁区


    ……
    ……
    ……
    ……
    ……
    ……
    ……



    (2)商家表(Store)商家表主要用于保存商家的信息,包括唯一标识id,商家名,商家图片,评分,类型,销售量,起送费。其中type类型为0表示美食,为1表示水果,为3表示饮品。



    Id
    Name
    Image
    Score
    Type
    Sales
    Qisong




    1
    香儿麦
    Tx1.jpg
    4.4
    0
    223
    12


    2
    何记花甲
    Tx2.jpg
    4.6
    0
    301
    13


    3
    桥头排骨
    Tx3.jpg
    4.8
    0
    449
    15


    4
    水果先知
    Sgtx1.jpg
    4.7
    1
    324
    13


    5
    百果园
    Sgtx2.jpg
    4.2
    1
    199
    14


    6
    果小露
    Sgtx3.jpg
    4.1
    1
    179
    15


    7
    CoCo
    Yptx1.jpg
    4.3
    2
    219
    17


    8
    一点点
    Yptx2.jpg
    4.5
    2
    327
    16


    9
    五十岚
    Yptx3.jpg
    4.7
    2
    408
    12


    ……
    ……
    ……
    ……
    ……
    ……



    (3)商品表商品表主要用于保存商品信息,包括唯一标识id,商家id,iamge,价格,内容,销售量,好评率。其中通过商家id,可以定位到该商品是哪个商家的。



    Id
    Sid
    Image
    Price
    Content
    Sales
    Like




    1
    1
    Hb.jpg
    10
    香辣鸡腿堡
    198
    93%


    2
    1
    Jk.jpg
    7
    黑椒鸡块
    7
    80%


    3
    1
    Kb.jpg
    8
    奥尔良烤堡
    46
    100%


    4
    2
    Jzghj.jpg
    11
    金针菇花甲
    214
    98%


    5
    2
    Kchj.jpg
    13
    烤肠花甲
    152
    95%


    6
    2
    Xghj.jpg
    12
    香菇花甲
    124
    98%


    7
    5
    Hlg.jpg
    3.5
    海南火龙果
    101
    100%


    8
    5
    Jxmj.jpg
    7.8
    江西蜜桔
    176
    97%


    9
    5
    Fjbl.jpg
    5.8
    福建白梨
    89
    95%


    ……
    ……

    ……
    ……
    ……
    ……



    (4)订单表(Order)订单表主要用于保存订单的信息,包括唯一标识id,商品id,用户id,地址。



    Id
    Goods id
    Uid
    Address




    1
    1
    17600006117
    南京市秦淮区


    2
    9
    17600006117
    南京市秦淮区


    3
    2
    15600007996
    盐城市阜宁县


    4
    7
    15600003302
    南京市江宁区


    ……
    ……
    ……
    ……



    (5)评论表(Evaluate)评论表主要用于保存评论的信息,包括唯一标识id,商家id,用户id,时间,点赞,内容。



    Id
    Sid
    Uid
    Time
    Like
    Contents




    1
    1
    17600006117
    2019-12-08
    1
    好吃


    2
    7
    17600006117
    2019-12-08
    3
    还不错


    3
    13
    15600007996
    2019-12-08
    5
    口味nice


    4
    9
    15600003302
    2019-12-08
    6
    Very good!


    ……
    ……
    ……
    ……
    ……
    ……



    4 系统详细设计系统详细设计部分将对本系统的每个模块的详细设计进行叙述,为本系统的开发提供更为全面的指导。
    4.1 登录模块详细设计用户进入个人中心页面,点击“用户名”栏,跳出登录页面,若已注册用户,则输入账号和密码进行登录。

    4.2 注册模块详细设计用户进入个人中心页面,点击“立即注册”,根据提示信息完成注册操作,然后返回登录界面,通过账号密码登录APP。

    4.3 点餐模块详细设计用户进入APP首页,可以查看店家,点击某店家可以进入该店家浏览菜品,勾选菜品点击加入购物车,进入购物车,勾选其中菜品点击下单以购买美食。

    4.4 个人信息模块详细设计成功登录APP的用户进入个人页面,点击用户名一栏,跳转至修改信息页面,用户可以根据自己的需求修改自己的个人信息,然后保存返回;若用户不想做任何修改,则直接返回退出。

    4.5 搜索模块详细设计用户进入“饿了吗?”APP首页,点击最上方的搜索框,输入关键词,即可查找想要了解的菜品和店家。

    5 系统实现系统实现部分对已开发好的系统功能的实现进行详细的描述,对每一个主要功能通过语言描述和页面截图对本系统进行全面的展示。
    5.1 店面界面import java.io.Serializable;public class store implements Serializable { private int id; private String name; private int score; private int image; private int type; private int sales; private int qisong; public store(int id, String name, int score, int image, int type, int sales, int qisong) { this.id = id; this.name = name; this.score = score; this.image = image; this.type = type; this.sales = sales; this.qisong = qisong; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getScore() { return score; } public void setScore(int score) { this.score = score; } public int getImage() { return image; } public void setImage(int image) { this.image = image; } public int getType() { return type; } public void setType(int type) { this.type = type; } public int getSales() { return sales; } public void setSales(int sales) { this.sales = sales; } public int getQisong() { return qisong; } public void setQisong(int qisong) { this.qisong = qisong; }}
    5.2 storeActivity.java代码package com.example.a11469.food;import android.content.ContentValues;import android.content.DialogInterface;import android.content.Intent;import android.support.v7.app.AlertDialog;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.AdapterView;import android.widget.ImageView;import android.widget.ListView;import android.widget.TextView;import java.util.ArrayList;import java.util.List;import java.util.Vector;public class StoreActivity extends AppCompatActivity { int flag=0; GoodsAdapter goodsAdapter; List<Goods> goodsList; ListView listView; List<Goods> myList; List<Evaluate> evaluateList; List<Evaluate> evList; Intent intent; Bundle bundle; int id; store s; String sname; ImageView im; TextView tv_name; TextView tv_want; TextView tv_pj; int image; EvaluateDBHelper evaluateDBHelper; EvaluateAdapter evaluateAdapter; GoodsDBHelper goodsDBHelper =new GoodsDBHelper(this,"goods.db",null,1); public static String name; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_store); intent = getIntent(); name=intent.getStringExtra("zhanghao"); bundle = intent.getExtras(); s = (store)bundle.get("Storer"); evaluateDBHelper= new EvaluateDBHelper(this,"evaluate.db",null,1); id = s.getId(); sname = s.getName(); image = s.getImage(); im=findViewById(R.id.Store_image); tv_name=(TextView) findViewById(R.id.Store_name); tv_want=findViewById(R.id.Store_wantfood); tv_pj=findViewById(R.id.Store_pingjia); im.setImageResource(image); tv_name.setText(sname); setTitle("店家界面"); listView=findViewById(R.id.Store_list); myList = new ArrayList<>(); goodsList = new ArrayList<>(); evList = new ArrayList<>(); evaluateList = new ArrayList<>(); Goods goods1 = new Goods(1,1,"34",R.drawable.jirou,"鸡胸肉+精致米饭",62,57,"鸡胸肉能量餐"); Goods goods2 = new Goods(2,1,"25",R.drawable.niu,"牛肉+精致米饭",50,44,"牛排能量餐"); Goods goods3 = new Goods(3,1,"42",R.drawable.xia,"牛油果+虾仁",14,12,"牛油果大虾沙拉"); Goods goods4 = new Goods(4,1,"36",R.drawable.kao,"圣女果+橙子+沙拉",15,14,"经典考伯沙拉"); Goods goods5 = new Goods(5,2,"30",R.drawable.ji,"鸡+精致米饭",87,72,"三杯鸡便当"); Goods goods6 = new Goods(6,2,"30",R.drawable.zhu,"猪肉+梅干菜+精致米饭",27,24,"梅干菜扣肉饭"); Goods goods7 = new Goods(7,2,"25",R.drawable.rou,"烤肉+精致米饭",24,23,"烤肉饭"); Goods goods8 = new Goods(8,2,"24",R.drawable.jipai,"奥尔良鸡排+精致米饭",24,12,"奥尔良鸡排饭"); Goods goods9 = new Goods(9,3,"20",R.drawable.sanxian,"鸡蛋+火腿+精致米饭",27,18,"解馋三鲜炒饭"); Goods goods10 = new Goods(10,3,"20",R.drawable.zhutourou,"猪头肉+精致米饭",45,40,"猪头肉炒饭"); Goods goods11 = new Goods(11,3,"18",R.drawable.huo,"火腿+精致米饭",39,26,"火腿炒饭"); Goods goods12 = new Goods(12,3,"15",R.drawable.jidan,"鸡蛋+精致米饭",78,74,"鸡蛋炒饭"); Goods goods13 = new Goods(13,4,"10",R.drawable.ydd,"奶+绿茶",35,35,"奶绿"); Goods goods14 = new Goods(14,4,"10",R.drawable.ydd,"奶+红茶",65,64,"奶茶"); Goods goods15 = new Goods(15,4,"15",R.drawable.ydd,"奶+乌龙茶",41,35,"乌龙奶茶"); Goods goods16 = new Goods(16,4,"10",R.drawable.ydd,"奶+四季春茶",78,34,"四季奶绿"); Goods goods17 = new Goods(17,5,"9",R.drawable.yeguo,"椰果+奶茶",44,42,"椰果奶茶"); Goods goods18 = new Goods(18,5,"11",R.drawable.xianyu,"鲜芋+奶茶",98,89,"鲜芋奶茶"); Goods goods19 = new Goods(19,5,"11",R.drawable.hongdou,"红豆+奶茶",36,35,"红豆奶茶"); Goods goods20 = new Goods(20,5,"10",R.drawable.zhengzhu,"珍珠+奶茶",68,66,"珍珠+奶茶"); Goods goods21 = new Goods(21,6,"13",R.drawable.nai,"奶霜+不知春茶",87,82,"奶霜不知春"); Goods goods22 = new Goods(22,6,"18",R.drawable.jin,"芒果+酸奶+西米+芦荟粒",97,92,"金玉甘露"); Goods goods23 = new Goods(23,6,"18",R.drawable.qing,"柠檬+绿茶",89,79,"青柠眷贵妃"); Goods goods24 = new Goods(24,6,"18",R.drawable.cao,"草莓+酸奶",65,64,"草莓小确幸"); Goods goods25 = new Goods(25,7,"9",R.drawable.xigua,"西瓜",69,67,"西瓜现切"); Goods goods26 = new Goods(26,7,"9",R.drawable.qian,"千禧",36,35,"千禧"); Goods goods27 = new Goods(27,7,"9",R.drawable.aaa,"波罗蜜",97,95,"波罗蜜"); Goods goods28 = new Goods(28,7,"9",R.drawable.cheng,"手剥橙",31,20,"手剥橙"); Goods goods29 = new Goods(29,8,"6",R.drawable.lizi,"砀山梨",85,75,"砀山梨"); Goods goods30 = new Goods(30,8,"8",R.drawable.tao,"猕猴桃",69,65,"猕猴桃两只"); Goods goods31 = new Goods(31,8,"8",R.drawable.pingguo,"苹果",69,59,"冰糖心苹果250g"); Goods goods32 = new Goods(32,7,"8",R.drawable.tizi,"红提",26,24,"红提250g"); Goods goods33 = new Goods(33,9,"11",R.drawable.liqie,"冰糖梨",90,88,"冰糖梨切"); Goods goods34 = new Goods(34,9,"12",R.drawable.migua,"蜜瓜",28,24,"沙漠蜜瓜"); Goods goods35 = new Goods(35,9,"14",R.drawable.yezi,"椰青",22,21,"泰国椰青"); Goods goods36 = new Goods(36,9,"16",R.drawable.guozi,"火龙果",27,26,"进口火龙果"); goodsList.add(goods1); goodsList.add(goods2); goodsList.add(goods3); goodsList.add(goods4); goodsList.add(goods5); goodsList.add(goods6); goodsList.add(goods7); goodsList.add(goods8); goodsList.add(goods9); goodsList.add(goods10); goodsList.add(goods11); goodsList.add(goods12); goodsList.add(goods13); goodsList.add(goods14); goodsList.add(goods15); goodsList.add(goods16); goodsList.add(goods17); goodsList.add(goods18); goodsList.add(goods19); goodsList.add(goods20); goodsList.add(goods21); goodsList.add(goods22); goodsList.add(goods23); goodsList.add(goods24); goodsList.add(goods25); goodsList.add(goods26); goodsList.add(goods27); goodsList.add(goods28); goodsList.add(goods29); goodsList.add(goods30); goodsList.add(goods31); goodsList.add(goods32); goodsList.add(goods33); goodsList.add(goods34); goodsList.add(goods35); goodsList.add(goods36); for(Goods g:goodsList) { if (g.getSid()==id) { myList.add(g); } } for(Goods g: goodsList){ ContentValues contentValues =new ContentValues(); contentValues.put("id",g.getId()); contentValues.put("sid",g.getSid()); contentValues.put("image",g.getImage()); contentValues.put("goodsName",g.getGoodName()+""); contentValues.put("sales",g.getLike1()); contentValues.put("price",g.getPrice()+""); contentValues.put("like1",g.getLike1()); contentValues.put("content",g.getContent()+""); goodsDBHelper.insert(contentValues); } evaluateList = evaluateDBHelper.select(); for(Evaluate e:evaluateList){ if(e.getSid()==id){ evList.add(e); } } evaluateAdapter = new EvaluateAdapter(this,evList); goodsAdapter = new GoodsAdapter(this,myList); listView.setAdapter(goodsAdapter); listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) { Intent intent = new Intent(StoreActivity.this,GoodsinformationActivity.class); Bundle bundle1= new Bundle(); bundle1.putSerializable("Goods_infor",myList.get(i)); intent.putExtras(bundle1); startActivity(intent); return true; } }); tv_want.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { listView.setAdapter(goodsAdapter); flag=1; } }); tv_pj.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { listView.setAdapter(evaluateAdapter); flag=2; } }); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, final int i, long l) { if(flag==1){ AlertDialog.Builder builder=new AlertDialog.Builder( StoreActivity.this); builder.setMessage("确定加入购物车嘛?") .setTitle("提示"); builder.setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); Intent intent = new Intent(StoreActivity.this,ShopcarActivity.class); intent.putExtra("zhanghao",name); intent.putExtra("storename",s.getName()); intent.putExtra("storeid",s.getId()); Bundle bundle1= new Bundle(); bundle1.putSerializable("Shopcar",myList.get(i)); intent.putExtras(bundle1); startActivity(intent); } }); builder.setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss();}}); builder.create().show();} else if (flag==2){ } } }); }}
    5.3 storeAdapter代码package com.example.a11469.food;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.ImageView;import android.widget.TextView;import java.util.List;public class StoreAdapter extends BaseAdapter { private Context mContext; List<store> storeList; public StoreAdapter(Context context,List<store> list){ this.mContext = context; this.storeList = list; } public int getCount() { return storeList.size(); } @Override public Object getItem(int i) { return null; } @Override public long getItemId(int i) { return 0; } @Override public View getView(int i, View view, ViewGroup viewGroup) { view = LayoutInflater.from(mContext).inflate(R.layout.storeitem,null); ImageView store_image = view.findViewById(R.id.image); TextView store_name = view.findViewById(R.id.Sname); TextView store_xingji = view.findViewById(R.id.xingji); TextView store_sales = view.findViewById(R.id.sales); TextView store_score = view.findViewById(R.id.qisong); store_image.setImageDrawable(mContext.getResources().getDrawable(storeList.get(i).getImage())); store_name.setText(storeList.get(i).getName()); store_xingji.setText(storeList.get(i).getScore()+""); store_sales.setText(storeList.get(i).getSales()+""); store_score.setText(storeList.get(i).getQisong()+""); return view; }}
    5.4 店家数据库package com.example.a11469.food;import android.content.ContentValues;import android.content.Context;import android.database.Cursor;import android.database.sqlite.SQLiteDatabase;import android.database.sqlite.SQLiteOpenHelper;import java.util.ArrayList;import java.util.List;public class storeDBHelper extends SQLiteOpenHelper { private static final String DB_NAME = "store.db"; private static final String TBL_NAME = "store"; private SQLiteDatabase db; public storeDBHelper(Context context, String dbname, SQLiteDatabase.CursorFactory factory, int version) { super(context, dbname, factory, version); } @Override public void onCreate(SQLiteDatabase sqLiteDatabase) { this.db = sqLiteDatabase; String sql = "create table store (id integer primary key autoincrement,name text,image integer,score integer,type integer,sales integer,qisong integer)"; sqLiteDatabase.execSQL(sql); } @Override public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) { } public void insert(ContentValues contentValues) { SQLiteDatabase sqLiteDatabase = getWritableDatabase(); sqLiteDatabase.insert(TBL_NAME, null, contentValues); sqLiteDatabase.close(); } public List<store> query(){ List<store> storeList = new ArrayList<>(); SQLiteDatabase sqLiteDatabase = getReadableDatabase(); Cursor cursor = sqLiteDatabase.query(TBL_NAME, null, null, null, null, null, null); while (cursor.moveToNext()){ int id = cursor.getInt(cursor.getColumnIndex("id")); String name = cursor.getString(cursor.getColumnIndex("name")); int image = cursor.getInt(cursor.getColumnIndex("image")); int score = cursor.getInt(cursor.getColumnIndex("score")); int type = cursor.getInt(cursor.getColumnIndex("score")); int sales = cursor.getInt(cursor.getColumnIndex("sales")); int qisong = cursor.getInt(cursor.getColumnIndex("qisong")); store store=new store(1,"赵家外卖",20,R.drawable.zhao,1,1,20); store.setId(id); store.setName(name); store.setImage(image); store.setScore(score); store.setType(type); store.setSales(sales); store.setQisong(qisong); storeList.add(store); } return storeList; } public void delete(int id) { if (db == null) db = getWritableDatabase(); db.delete(TBL_NAME, "id=?", new String[]{String.valueOf(id)}); } public void update(ContentValues values, int id) { db = getWritableDatabase(); db.update(TBL_NAME, values, "id=?", new String[]{String.valueOf(id)}); db.close(); }}
    5.5 商品实体代码package com.example.a11469.food;import java.io.Serializable;public class Goods implements Serializable { private int id; private int sid; private String price; private String goodName; private int image; private String content; private int sales; private int like1; public int getId() { return id; } public void setId(int id) { this.id = id; } public int getSid() { return sid; } public void setSid(int sid) { this.sid = sid; } public String getPrice() { return price; } public void setPrice(String price) { this.price = price; } public String getGoodName() { return goodName; } public void setGoodName(String goodName) { this.goodName = goodName; } public int getImage() { return image; } public void setImage(int image) { this.image = image; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public int getSales() { return sales; } public void setSales(int sales) { this.sales = sales; } public int getLike1() { return like1; } public void setLike1(int like1) { this.like1 = like1; } public Goods(int id, int sid, String price, int image, String content, int sales, int like1, String goodName) { this.id = id; this.sid = sid; this.goodName=goodName; this.price = price; this.image = image; this.content = content; this.sales = sales; this.like1 = like1; }}
    5.6 商品数据库package com.example.a11469.food;import android.content.ContentValues;import android.content.Context;import android.database.Cursor;import android.database.sqlite.SQLiteDatabase;import android.database.sqlite.SQLiteOpenHelper;import java.util.ArrayList;import java.util.List;public class GoodsDBHelper extends SQLiteOpenHelper { private SQLiteDatabase db; private static final String DB_NAME="goods.db"; private static final String TBL_NAME="goodsTab"; public GoodsDBHelper(Context context, String daname, SQLiteDatabase.CursorFactory factory,int version){ super(context,daname,factory,version); } @Override public void onCreate(SQLiteDatabase db) { this.db=db; String sql ="create table goodsTab(id integer primary key autoincrement, sid integer,price text,image integer,content text,sales integer,like1 integer,goodsName text)"; db.execSQL(sql); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } public void insert(ContentValues contentValues){ SQLiteDatabase sqLiteDatabase =getWritableDatabase(); sqLiteDatabase.insert("goodsTab",null,contentValues); sqLiteDatabase.close(); } public List<Goods> query(){ List<Goods> personList = new ArrayList<>(); SQLiteDatabase sqLiteDatabase = getReadableDatabase(); Cursor cursor = sqLiteDatabase.query(TBL_NAME,null,null,null,null,null,null); while (cursor.moveToNext()){ Goods goods = new Goods(1,1,"1",1,"c0",1,1,"ccxax"); goods.setId(cursor.getInt(cursor.getColumnIndex("id"))); goods.setSid(cursor.getInt(cursor.getColumnIndex("sid"))); goods.setPrice(cursor.getString(cursor.getColumnIndex("price"))); goods.setImage(cursor.getInt(cursor.getColumnIndex("image"))); goods.setContent(cursor.getString(cursor.getColumnIndex("content"))); goods.setSales(cursor.getInt(cursor.getColumnIndex("sales"))); goods.setLike1(cursor.getInt(cursor.getColumnIndex("like1"))); goods.setGoodName(cursor.getString(cursor.getColumnIndex("goodsName"))); personList.add(goods); } return personList; } public void delete(int id){ if(db==null) db = getWritableDatabase(); db.delete(TBL_NAME,"id=?",new String[]{String.valueOf(id)}); } public void update(ContentValues contentValues,int id){ SQLiteDatabase sqLiteDatabase = getWritableDatabase(); sqLiteDatabase.update("goodsTab",contentValues,"id=?",new String[]{id+""}); sqLiteDatabase.close(); }}
    5.7 商品适配器package com.example.a11469.food;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.ImageView;import android.widget.TextView;import java.util.List;public class GoodsAdapter extends BaseAdapter { private Context mContext; private List<Goods> mList; public GoodsAdapter(Context mContext, List<Goods> mList) { this.mContext = mContext; this.mList = mList; } @Override public int getCount() { return mList.size(); } @Override public Object getItem(int position) { return null; } @Override public long getItemId(int position) { return 0; } public View getView(int i, View view, ViewGroup viewGroup) { view = LayoutInflater.from(mContext).inflate(R.layout.goodsitem,null); ImageView goods_image = view.findViewById(R.id.goods_image); TextView goods_name = view.findViewById(R.id.goods_name); TextView goods_like = view.findViewById(R.id.good_like); TextView goods_price = view.findViewById(R.id.goods_price); TextView goods_sales = view.findViewById(R.id.goods_sales); goods_image.setImageDrawable(mContext.getResources().getDrawable(mList.get(i).getImage())); goods_name.setText(mList.get(i).getGoodName()); goods_like.setText(mList.get(i).getLike1()+""); goods_price.setText(mList.get(i).getPrice()+""); goods_sales.setText(mList.get(i).getSales()+""); return view; }}
    5.8 商品详细信息package com.example.a11469.food;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.ImageView;import android.widget.TextView;import java.util.List;public class GoodsAdapter extends BaseAdapter { private Context mContext; private List<Goods> mList; public GoodsAdapter(Context mContext, List<Goods> mList) { this.mContext = mContext; this.mList = mList; } @Override public int getCount() { return mList.size(); } @Override public Object getItem(int position) { return null; } @Override public long getItemId(int position) { return 0; } public View getView(int i, View view, ViewGroup viewGroup) { view = LayoutInflater.from(mContext).inflate(R.layout.goodsitem,null); ImageView goods_image = view.findViewById(R.id.goods_image); TextView goods_name = view.findViewById(R.id.goods_name); TextView goods_like = view.findViewById(R.id.good_like); TextView goods_price = view.findViewById(R.id.goods_price); TextView goods_sales = view.findViewById(R.id.goods_sales); goods_image.setImageDrawable(mContext.getResources().getDrawable(mList.get(i).getImage())); goods_name.setText(mList.get(i).getGoodName()); goods_like.setText(mList.get(i).getLike1()+""); goods_price.setText(mList.get(i).getPrice()+""); goods_sales.setText(mList.get(i).getSales()+""); return view; }}
    5.9 订单实体package com.example.a11469.food;import java.io.Serializable;public class Order implements Serializable { private int id; private int image; private String sname; private String goods_name; private String price; private int sid; private String uname; public Order(int id, int image, String sname, String goods_name, String price, int sid,String uname){ this.image =image; this.sname = sname; this.goods_name= goods_name; this.price = price; this.sid = sid; this.id = id; this.uname = uname; } public String getUname() { return uname; } public void setUname(String uname) { this.uname = uname; } public int getId() { return id; } public void setId(int id) { this.id = id; } public int getSid() { return sid; } public void setSid(int sid) { this.sid = sid; } public int getImage() { return image; } public void setImage(int image) { this.image = image; } public String getSname() { return sname; } public void setSname(String sname) { this.sname = sname; } public String getGoods_name() { return goods_name; } public void setGoods_name(String goods_name) { this.goods_name = goods_name; } public String getPrice() { return price; } public void setPrice(String price) { this.price = price; }}
    5.10 orderActivity代码package com.example.a11469.food;import android.content.DialogInterface;import android.content.Intent;import android.support.v7.app.AlertDialog;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.AdapterView;import android.widget.Button;import android.widget.ListView;import android.widget.TextView;import java.util.ArrayList;import java.util.List;public class OrderActivity extends AppCompatActivity { TextView mine; TextView waimai; ListView listView1; OrderAdapter orderAdapter; List<User> userList; List<Order> OrderList; List<Order> OrderList1; Button bt_evaluate; Button bt_again; public static String name; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_form); OrderList = new ArrayList<>(); OrderList1 = new ArrayList<>(); mine = findViewById(R.id.form_mine); OrderDBHelper dbHelper = new OrderDBHelper(OrderActivity.this, "order.db", null, 1); waimai = findViewById(R.id.form_shop); bt_evaluate = findViewById(R.id.item_btnjudge); bt_again = findViewById(R.id.item_nextone); name = getIntent().getStringExtra("zhanghao"); OrderList = dbHelper.query(); for(Order o:OrderList){ if(name!=null){ if(name.equals(o.getUname())){ OrderList1.add(o); }} else{ OrderList1.clear(); } } listView1 = findViewById(R.id.form_list); orderAdapter = new OrderAdapter(this, OrderList1, OrderActivity.this); listView1.setAdapter(orderAdapter); mine.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(OrderActivity.this, MineActivity.class); intent.putExtra("zhanghao", name); startActivity(intent); } }); waimai.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(OrderActivity.this, ShouyeActivity.class); intent.putExtra("zhanghao", name); startActivity(intent); } }); listView1.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, final View view, int i, long l) { final int s = i; AlertDialog.Builder builder = new AlertDialog.Builder(view.getContext()); builder.setMessage("确定删除吗?") .setTitle("提示"); builder.setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int which) { dialogInterface.dismiss(); OrderDBHelper dbHelper2 = new OrderDBHelper(OrderActivity.this, "order.db", null, 1); dbHelper2.delete(OrderList1.get(s).getId()); OrderList1.clear(); OrderList1.addAll(dbHelper2.query()); orderAdapter.notifyDataSetInvalidated(); } }) .setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { dialogInterface.dismiss(); } }); AlertDialog dialog = builder.create(); dialog.show(); } }); listView1.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) { Intent intent = new Intent(OrderActivity.this, OrderMoreActivity.class); intent.putExtra("zhanghao", name); Bundle bundle = new Bundle(); bundle.putSerializable("Orderer", OrderList1.get(i)); intent.putExtras(bundle); startActivity(intent); return true; } }); }}
    5.11 订单适配器package com.example.a11469.food;import android.content.Context;import android.content.Intent;import android.os.Bundle;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.Button;import android.widget.ImageView;import android.widget.TextView;import java.util.ArrayList;import java.util.List;public class OrderAdapter extends BaseAdapter { private Context mContext; OrderActivity orderActivity; List<Order> orderList; Order o; public OrderAdapter(Context context,List<Order> list,OrderActivity orderActivity){ this.orderActivity = orderActivity; this.mContext = context; this.orderList = list; } public int getCount() { return orderList.size(); } @Override public Object getItem(int i) { return null; } @Override public long getItemId(int i) { return 0; } @Override public View getView(final int i, View view, ViewGroup viewGroup) { view = LayoutInflater.from(mContext).inflate(R.layout.form_item_layout,null); ImageView image =view.findViewById(R.id.order_image); TextView shop_name = view.findViewById(R.id.item_shopname); TextView goods_name = view.findViewById(R.id.item_productname); View fenge = view.findViewById(R.id.fenge); TextView money = view.findViewById(R.id.item_money); Button judge = view.findViewById(R.id.item_btnjudge); Button nextone = view.findViewById(R.id.item_nextone); image.setImageDrawable(mContext.getResources().getDrawable(orderList.get(i).getImage())); shop_name.setText(orderList.get(i).getSname()); goods_name.setText(orderList.get(i).getGoods_name()); money.setText(orderList.get(i).getPrice()); judge.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(mContext,EvaluateActivity.class); Bundle bundle = new Bundle(); bundle.putSerializable("Evaluate",orderList.get(i)); intent.putExtras(bundle); mContext.startActivity(intent); } }); nextone.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(mContext,ShopcarActivity.class); Bundle bundle= new Bundle(); bundle.putSerializable("Order_Shopcar",orderList.get(i)); intent.putExtra("zhanghao",OrderActivity.name); intent.putExtras(bundle); mContext.startActivity(intent); } }); return view; }}
    5.12 订单数据库package com.example.a11469.food;import android.content.ContentValues;import android.content.Context;import android.database.Cursor;import android.database.sqlite.SQLiteDatabase;import android.database.sqlite.SQLiteOpenHelper;import java.util.ArrayList;import java.util.List;public class OrderDBHelper extends SQLiteOpenHelper { private SQLiteDatabase db; private static final String DB_NAME="order.db"; private static final String TBL_NAME="orderTab"; public OrderDBHelper(Context context, String daname, SQLiteDatabase.CursorFactory factory,int version){ super(context,daname,factory,version); } @Override public void onCreate(SQLiteDatabase db) { this.db=db; String sql ="create table orderTab(id integer primary key autoincrement, image integer,sname text,good_name text,price text,sid integer,uname text)"; db.execSQL(sql); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } public void insert(ContentValues contentValues){ SQLiteDatabase sqLiteDatabase =getWritableDatabase(); sqLiteDatabase.insert("orderTab",null,contentValues); sqLiteDatabase.close(); } public List<Order> query(){ List<Order> personList = new ArrayList<>(); SQLiteDatabase sqLiteDatabase = getReadableDatabase(); Cursor cursor = sqLiteDatabase.query("orderTab",null,null,null,null,null,null); while (cursor.moveToNext()){ Order order = new Order(1,1,"12","23","ef",11,"1"); order.setId(cursor.getInt(cursor.getColumnIndex("id"))); order.setImage(cursor.getInt(cursor.getColumnIndex("image"))); order.setSname(cursor.getString(cursor.getColumnIndex("sname"))); order.setGoods_name(cursor.getString(cursor.getColumnIndex("good_name"))); order.setPrice(cursor.getString(cursor.getColumnIndex("price"))); order.setSid(cursor.getInt(cursor.getColumnIndex("sid"))); order.setUname(cursor.getString(cursor.getColumnIndex("uname"))); personList.add(order); } return personList; } public void delete(int id){ if(db==null) db = getWritableDatabase(); db.delete(TBL_NAME,"id=?",new String[]{String.valueOf(id)}); } public void update(ContentValues contentValues,int id){ SQLiteDatabase sqLiteDatabase = getWritableDatabase(); sqLiteDatabase.update("orderTab",contentValues,"id=?",new String[]{id+""}); sqLiteDatabase.close(); }}
    5.13 ordermoreActivity代码package com.example.a11469.food;import android.content.Intent;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.ImageView;import android.widget.TextView;import java.util.ArrayList;import java.util.List;public class OrderMoreActivity extends AppCompatActivity { Button turnback; TextView storename; List<User> userList; Order o; int tupian; ImageView imageView; Intent intent; Bundle bundle; public static String loginname; TextView goodsname; TextView goodprice; TextView useraddress; TextView orderid; TextView person; int id; String goodsname1; String goodprice2; String storename2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.order_more); userList=new ArrayList<>(); intent=getIntent(); imageView=findViewById(R.id.order_more_img); bundle = intent.getExtras(); o=(Order)bundle.get("Orderer"); loginname=intent.getStringExtra("zhanghao"); storename=findViewById(R.id.order_more_storename); goodsname=findViewById(R.id.order_more_goodsname); goodprice=findViewById(R.id.order_more_price); useraddress=findViewById(R.id.order_more_address); person=findViewById(R.id.order_more_person); orderid=findViewById(R.id.order_more_orderid); final String name=getIntent().getStringExtra("zhanghao"); turnback=findViewById(R.id.order_more_turnback); person.setText(name); id=o.getId(); tupian=o.getImage(); storename2=o.getSname(); goodsname1=o.getGoods_name(); goodprice2=o.getPrice(); orderid.setText(String.valueOf(id)); goodprice.setText(goodprice2); goodsname.setText(goodsname1); storename.setText(storename2); imageView.setImageResource(tupian); UserDBHelper dbHelper = new UserDBHelper(OrderMoreActivity.this, "user2.db", null, 1); userList=dbHelper.selcet(); for(User u:userList){ if(u.getNumber().equals(name)){ useraddress.setText(u.getAddress()); break;} } turnback.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent=new Intent(OrderMoreActivity.this,OrderActivity.class); intent.putExtra("zhanghao",loginname); startActivity(intent); } }); }}
    6 系统测试与结果系统测试与结果部分主要对本系统已实现的所有功能进行测试,比较测试结果与预期结果,并做出分析,从而找出系统的bug,以便及时对系统进行修复。本部分还包括对测试思路和环境搭建以及测试的结果分析。
    6.1 测试思路和环境搭建本节将对本系统的测试思路以及环境搭建进行叙述。包括对系统测试的大致方向进行叙述,以及对本系统搭建所需要的硬件环境和软件环境的叙述。
    6.1.1 测试思路为了对系统做出更为全面的测试,本次测试将对已完成的系统功能进行测试,并对结果进行分析。具体的测试方案如下:

    功能测试:主要测试的功能有用户登录、用户注册、新闻搜索功能、个人信息修改功能
    安全性测试:主要测试系统的安全性,包括使用不同角色的用户登录,测试用户登录后角色所对应的功能是否正确

    6.1.2 环境搭建
    硬件环境

    硬盘:1TB内存:16GCPU:Inter(R)Core(TM) i7-6700HQ CPU @2.60GHz
    软件环境

    在本地(windows10系统)下搭建Java环境:安装JDK1.8,配置环境变量在本地(windows10系统)下搭建android 开发环境,安装android studio ,下载android开发Sdk。下载各种支持插件,运行库注册Bmob云服务器账号,在云端创建应用,输入应用包名,得到连接应用的开发密匙,并设计云端数据库,用作存储本地数据在Android Studio中创建应用,并搭建项目的整体框架,开始实施编码逻輯。并尝试连接Bmob云数据库,实现数据操作

    6.2 测试与结果分析根据上节中的测试思路对本系统进行黑盒测试,将所有可能的错误情况测试一遍,记录预期结果以及实际情况,并对测试的结果进行分析
    6.2.1 加入购物车功能测试测试内容:进入系统后,进行搜索新闻。









    点击菜品加入购物车
    菜品加入购物车



    6.2.2 搜索功能测试测试内容:进入修改信息界面,修改用户信息。









    在搜索框内输入空
    在搜索框内输入肯德基






    在搜索框内输入沙拉拉轻食
    沙拉拉轻食店家界面



    6.2.3 判断登录与否功能测试测试内容:进入修改信息界面,修改用户信息。









    不登陆直接点单
    提示登录






    登陆了注销以后再次点单
    提示登录






    登录以后查看订单
    未登录查看订单



    6 实习总结在实验的最初我们花了大量的时间来做需求分析。需求就像我们人生的航标一样,有了它,我们的开发才有方向,不然都是无用的。每天我们制定开发计划,一小步一小步的走,一行一行慢慢的写。看着一个个小小功能的实现与完善,我们都对他竖起大拇指。
    Android的UI布局是很重要的一环。布局模式以相对模式为主,线线布局模式可以在比较简单的include进行完成,最重要的一点就是:我们可以自己通过重写方法或者通过实现View或者Layout等类进行扩充项目需要的布局(或者控件),在学习界面中,我发现Android为我们提供了很好的类似反射机制,通过Layout文件夹下的配置文件,可以快速的形成界面,在配置文件可以设置属性或者样式都是很快捷方便。对比较特殊的界面也可以通过处理嵌入到指定的界面,同样你可以通过java代码直接创建View进行添加,不过这种方式比较复杂。对一些点击、选中、按键等处理的事件,界面之间的跳转Intent管理,通过Bundle对数据在界面之间进行传输
    在Android编程过程中巩固熟悉了Java的编程。由于Android应用程序的开发离不开Java的支持,所以基础的 Java知识是必须的。Android 系统是基于Linux的手机操作系统平台,要深入系统的学习Android,不仅仅是有 Java和Android应用开发,必须要具备Linux, C/C++高级编程才能深入的涉及Androi dFramework和Android内核开发。成为Android开发的高素质人才。所以,在后续对Android的学习中可能会看一-些较底层的书籍。由于这次实习时间较短,对于Android应用程序的高级编程讲的很少,是这次实习中的不足。
    当一个团队合作时,并不是遇到的每个团队,团队成员都令你满意、每个团队都可以融洽相处。这个团队之所以形成,主要目的为了成功完成这个项目。进入团队先要明白进入团队的目的,这个团队成立的目的。明白了这两点,许多在团队中遇到的问题都可以避开或很好的解决。所有成员都围绕如何能更好更完善的完成项目为主。当然在遇到必然可能影响到项目开发进度的问题时,还需及时解决。
    5 留言 2020-06-14 17:07:26 奖励55点积分
  • 利用网页打开本地exe文件

    首次接到“利用网页打开本地exe文件”这个任务时,还真有点摸不着头脑,后来细想,在淘宝上点击卖家的旺旺能开启本地的旺旺,这不就是利用的网页打开本地exe文件吗?
    了解这种实实在在存在的合理需求后,开始调查。经过网上搜索查询,主要归纳为两种实现方式,方式一:利用JS打开本地exe文件。一般浏览器,由于安全问题,都会禁止掉这个特性,这就导致部分浏览器不支持该种方式。方式二:利用浏览器外部协议(URL Procotol)打开本地exe文件。用这种方式实现,任何浏览器都兼容。在实际开发中,当然首选方式二。
    一、利用注册表文件将外部协议写入注册表[HKEY_CLASSES_ROOT\PCTV]@="PCTVProtocol""URL Protocol"="\"C:\\Program Files (x86)\\PCTV双模软终端_64位\\PCTV.exe\""[HKEY_CLASSES_ROOT\PCTV\DefaultIcon]@="\"C:\\Program Files (x86)\\PCTV双模软终端_64位\\PCTV.exe,1\"" [HKEY_CLASSES_ROOT\PCTV\shell] [HKEY_CLASSES_ROOT\PCTV\shell\open] [HKEY_CLASSES_ROOT\PCTV\shell\open\command]@="\"C:\\Program Files (x86)\\PCTV双模软终端_64位\\PCTV.exe\" \"%1\""将以上代码存入reg文件中,双击文件执行即可。在浏览器中输入 “pctv://” 或 “pctv://param1,param2” ,执行后即可打开对应路径下的exe文件。
    二、 在安装exe文件时将外部协议写入注册表在实际部署中,不会让客户安装完程序再手动单击注册表文件将安装路径写入注册表,最容易让人接受的方式就是在安装exe文件时将安装路径写入注册表。利用Inno Setup打包exe文件时,在脚本中加入如下代码即可:
    [Registry] Root:HKCR;Subkey:"PCTV";ValueType:string;ValueName:"URL Protocol";ValueData:"{app}\{#MyAppExeName}";Flags:createvalueifdoesntexist uninsdeletekeyRoot:HKCR;Subkey:"PCTV\DefaultIcon";ValueType:string;ValueData:"{app}\{#MyAppExeName}";Flags:createvalueifdoesntexist uninsdeletekey Root:HKCR;Subkey:"PCTV\shell";Flags:createvalueifdoesntexist uninsdeletekeyRoot:HKCR;Subkey:"PCTV\shell\open";Flags:createvalueifdoesntexist uninsdeletekeyRoot:HKCR;Subkey:"PCTV\shell\open\command";ValueType:string;ValueData:"{app}\{#MyAppExeName} ""%1""";Flags:createvalueifdoesntexist uninsdeletekey这样,在浏览器中输入 “pctv://” 或 “pctv://param1,param2” ,执行后即可打开对应路径下的exe文件。
    以上两种写入注册的方式,允许在外部协议中带参数。
    三、遇到的问题在利用外部协议打开本地exe文件时,通过查看日志记录,看到会出现路径不对的问题。通过查看代码在程序中用Environment.CurrentDirectory获取可执行文件的路径,但是通过浏览器打开exe文件时,Environment.CurrentDirectory获取的是浏览器exe文件的路径,这样在程序中就会报错。解决方法是将Environment.CurrentDirectory修改为Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory)即可。
    1 留言 2019-12-06 17:05:22 奖励15点积分
  • 上传资源,获取积分 精华

    上传资源,获取积分“WRITE-BUG技术共享平台”是一个专注于校园计算机技术交流共享的平台,面向的主要目标群体是我们计算机相关专业的大学生。在平台上,大家既可以交流学校课内学习的心得体会,也可以分享自己课外积累的技术经验。
    为了充实平台的资源库,更好地服务于各位同学,平台决定推出“众源计划”,有偿征集同学们自己计算机专业的作业、课程设计或是毕业设计等资源。“众源计划”的主要目的是创建一个具有一定规模的“技术资源库”,资源库里的每一份资源,都必须有详细的开发文档和可编译的源代码。
    作业、课程设计或是毕业设计等资源是同学们自己辛苦付出的成果,也是自己技术进步的见证。这部分资源通常都有详细的开发文档和完整的程序源代码,能够帮助其他初学者更好地消化和吸收将要学习的技术,降低学习门槛。所以,平台决定积分奖励征集这些资源。
    具体要求奖励方式
    资源上传并审核通过后,根据资源质量,奖励每贴 10 - 100 点积分
    上传流程
    会员登录自己的账号上传资源
    资源上传后,管理员会在 24 小时之内审核资源
    审核通过后,管理员会立即发放奖励积分至所对应账户

    审核重点
    重点审核资源是否具有详细的文档和完整的源代码
    审查资源是否原创,切勿重复提交

    资源要求“众源计划”仅对两类资源进行积分奖励征集,分别是“课内资源”和“课外资源”,各类资源具体要求如下所示。

    课内资源

    内容范围:计算机相关专业课内的毕业设计、课程设计、小学期、大作业等课程内开发的程序,程序包括游戏、PC程序、APP、网站或者其他软件形式
    内容要求:资源必须要包括完整的程序源代码和详细的开发文档或报告

    课外资源

    内容范围:计算机相关专业的课外自己主导研究游戏、项目、竞赛、个人研究等,区别于课程设计和毕业设计等课内资源
    内容要求:资源必须要包括完整的程序源代码和详细的开发文档或报告


    使用须知** 在上传资源前,请花两分钟阅读 “使用须知” 部分内容 **
    41 留言 2019-01-24 09:26:15 奖励100点积分
显示 0 到 15 ,共 15 条
eject