`
BrokenDreams
  • 浏览: 254004 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
68ec41aa-0ce6-3f83-961b-5aa541d59e48
Java并发包源码解析
浏览量:100169
社区版块
存档分类
最新评论

Jdk1.6 Collections Framework源码解析(8)-WeakHashMap

阅读更多
        总结这个类之前,首先看一下Java引用的相关知识。Java的引用分为四种:强引用、软引用、弱引用和虚引用。

        强引用:就是常见的代码中的引用,如Object o = new Object();存在强引用的对象不会被垃圾收集器回收。所以我们会在一些代码中看到显示切断强引用,以便回收相关对象的情况。
public void clear() {
	modCount++;

	// Let gc do its work
	for (int i = 0; i < size; i++)
	    elementData[i] = null;

	size = 0;
}


        软引用:比强引用弱一些,表示对一些有用但非必须对象的引用。这些对象会在将要发生内存溢出异常之前被回收。在Java中用java.lang.ref.SoftReference来表示软引用。看一个例子,先设置下JVM参数,将堆大小设为10m,老年代新生代各5m,打印出GC日志。
JVM参数
-Xms10m -Xmx10m -Xmn5m -XX:+PrintGCDetails

	public static void main(String[] args) {
		//构造一个3M的对象
		Object _3m_1 = new byte[1024 * 1024 * 3];
		//创建软引用
		SoftReference<Object> reference = new SoftReference<Object>(_3m_1);
		//切断强引用
		_3m_1 = null;
		//再构造一个3M的对象
		Object _3m_2 = new byte[1024 * 1024 * 3];
		//触发Full GC
		System.gc();
		System.out.println("软引用关联对象:"+reference.get());
	}

        输出结果:
[GC [DefNew: 3325K->152K(4608K), 0.0023022 secs] 3325K->3224K(9728K), 0.0023380 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System) [Tenured: 3072K->3072K(5120K), 0.0057404 secs] 6378K->6296K(9728K), [Perm : 380K->380K(12288K)], 0.0057837 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] 
软引用关联对象:[B@14318bb
Heap
 def new generation   total 4608K, used 3350K [0x31fe0000, 0x324e0000, 0x324e0000)
  eden space 4096K,  81% used [0x31fe0000, 0x32325880, 0x323e0000)
  from space 512K,   0% used [0x32460000, 0x32460000, 0x324e0000)
  to   space 512K,   0% used [0x323e0000, 0x323e0000, 0x32460000)
 tenured generation   total 5120K, used 3072K [0x324e0000, 0x329e0000, 0x329e0000)
   the space 5120K,  60% used [0x324e0000, 0x327e0010, 0x327e0200, 0x329e0000)
 compacting perm gen  total 12288K, used 380K [0x329e0000, 0x335e0000, 0x369e0000)

        基本过程是这样,程序先创建了一个3M的对象_3m_1,这个对象被分配到新生代里面,然后创了一个软引用关联到_3m_1,然后切断_3m_1的强引用。接下来又创建了一个3M的对象_3m_2,_3m_2要试图分配到新生代,这时发现新生代剩余空间不足(一共4608K,已经有一个3M的对象在里面)。接下来触发了一次新生代的垃圾收集动作(Minor GC),但这次收集没有回收掉_3m_1。然后_3m_1晋升到老年代,_3m_2被分配到新生代。接下来程序显示触发了一次Full GC,但这次Full GC似乎没起什么作用,_3m_1和_3m_2都安然无恙。
        那么修改一下程序,再跑一次看看:
	public static void main(String[] args) {
		//构造一个3M的对象
		Object _3m_1 = new byte[1024 * 1024 * 3];
		//创建软引用
		SoftReference<Object> reference = new SoftReference<Object>(_3m_1);
		//切断强引用
		_3m_1 = null;
		//再构造一个3M的对象
		Object _3m_2 = new byte[1024 * 1024 * 3];
		//又构造一个3M的对象
		Object _3m_3 = new byte[1024 * 1024 * 3];
		System.out.println("软引用关联对象:"+reference.get());
	}

        输出结果:
[GC [DefNew: 3325K->152K(4608K), 0.0021553 secs] 3325K->3224K(9728K), 0.0021908 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC [DefNew: 3224K->3224K(4608K), 0.0000288 secs][Tenured: 3072K->3072K(5120K), 0.0060759 secs] 6296K->6296K(9728K), [Perm : 380K->380K(12288K)], 0.0061594 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC [Tenured: 3072K->3210K(5120K), 0.0049693 secs] 6296K->3210K(9728K), [Perm : 380K->375K(12288K)], 0.0050152 secs] [Times: user=0.01 sys=0.00, real=0.02 secs] 
软引用关联对象:null
Heap
 def new generation   total 4608K, used 3280K [0x31fe0000, 0x324e0000, 0x324e0000)
  eden space 4096K,  80% used [0x31fe0000, 0x32314050, 0x323e0000)
  from space 512K,   0% used [0x32460000, 0x32460000, 0x324e0000)
  to   space 512K,   0% used [0x323e0000, 0x323e0000, 0x32460000)
 tenured generation   total 5120K, used 3210K [0x324e0000, 0x329e0000, 0x329e0000)
   the space 5120K,  62% used [0x324e0000, 0x32802b28, 0x32802c00, 0x329e0000)

        前面的过程和上面类似,区别在创建第三个3M对象_3m_3时,首先触发了一次Minor GC,没回收什么东西,但是要把新生代里的_3m_2放到老年代,这样才能腾出地方给_3m_3。但是老年代已经有_3m_1了,剩余的空间不足以放下_3m_2,这时系统触发了Full GC(不用我们触发system Full GC了)。从日志上看似乎回收掉了3M的空间。说明在内存溢出之前,软引用关联的对象被回收了。这便是软引用的特性,如果这里是一个强引用的话,那么便会出现OOM了。

        弱引用:比软引用还弱一些,所以关联的对象在下一次垃圾收集的时候就会被回收掉。继续看例子,JVM参数同上。
	public static void main(String[] args) {
		//构造一个3M的对象
		Object _3m_1 = new byte[1024 * 1024 * 3];
		//创建软引用
		WeakReference<Object> reference = new WeakReference<Object>(_3m_1);
		//切断强引用
		_3m_1 = null;
		//触发Full GC
		System.gc();
		System.out.println("软引用关联对象:"+reference.get());
	}

        输出结果:
[Full GC (System) [Tenured: 0K->152K(5120K), 0.0086388 secs] 3325K->152K(9728K), [Perm : 380K->380K(12288K)], 0.0086922 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
软引用关联对象:null
Heap
 def new generation   total 4608K, used 208K [0x31fe0000, 0x324e0000, 0x324e0000)
  eden space 4096K,   5% used [0x31fe0000, 0x32014040, 0x323e0000)
  from space 512K,   0% used [0x323e0000, 0x323e0000, 0x32460000)
  to   space 512K,   0% used [0x32460000, 0x32460000, 0x324e0000)
 tenured generation   total 5120K, used 152K [0x324e0000, 0x329e0000, 0x329e0000)
   the space 5120K,   2% used [0x324e0000, 0x32506088, 0x32506200, 0x329e0000)


        虚引用:虚引用一般用来代替finalize方法来做一些引用对象被回收前的清理动作(由于finalize的不确定性,不建议使用)。虚引用必须配合一个ReferenceQueue一起使用,在GC决定要回收其引用对象时(引用对象没有任何强引用关联),虚引用会入栈,程序中可以在这个时机做一些清理工作。虚引用的get方法返回null:
public class PhantomReference<T> extends Reference<T> {

    /**
     * Returns this reference object's referent.  Because the referent of a
     * phantom reference is always inaccessible, this method always returns
     * <code>null</code>.
     *
     * @return  <code>null</code>
     */
    public T get() {
	return null;
    }

        这样保证了虚引用的关联对象永远不可能通过get方法再次获得强引用。但虚引用的关联对象要一直等到虚引用本身不可达或者被回收时才能够被回收,这点不同于软引用和弱引用。
  
        最后说一下java.lang.ref.ReferenceQueue。ReferenceQueue是一个引用队列,构造一个软引用、弱引用或者虚引用可以传入一个ReferenceQueue,表示这个引用注册到传入的引用队列上,简单的说,当GC决定回收引用关联对象时,会将这个引用放到引用队列里。

        大概了解了Java引用的相关内容,现在可以看下java.util.WeakHashMap的源码了。
        整体看下来,其实和HashMap差不多,所以只看一下差异的地方。
    /**
     * Reference queue for cleared WeakEntries
     */
    private final ReferenceQueue<K> queue = new ReferenceQueue<K>();

        WeakHashMap中多了一个引用队列的变量,大概能猜到是干什么的了吧。
      
        看下WeakHashMap中的Entry:
    /**
     * The entries in this hash table extend WeakReference, using its main ref
     * field as the key.
     */
    private static class Entry<K,V> extends WeakReference<K> implements Map.Entry<K,V> {
        private V value;
        private final int hash;
        private Entry<K,V> next;

        /**
         * Creates new entry.
         */
        Entry(K key, V value,
	      ReferenceQueue<K> queue,
              int hash, Entry<K,V> next) {
            super(key, queue);
            this.value = value;
            this.hash  = hash;
            this.next  = next;
        }

        public K getKey() {
            return WeakHashMap.<K>unmaskNull(get());
        }

        public V getValue() {
            return value;
        }

        public V setValue(V newValue) {
	    V oldValue = value;
            value = newValue;
            return oldValue;
        }

        public boolean equals(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry e = (Map.Entry)o;
            Object k1 = getKey();
            Object k2 = e.getKey();
            if (k1 == k2 || (k1 != null && k1.equals(k2))) {
                Object v1 = getValue();
                Object v2 = e.getValue();
                if (v1 == v2 || (v1 != null && v1.equals(v2)))
                    return true;
            }
            return false;
        }

        public int hashCode() {
            Object k = getKey();
            Object v = getValue();
            return  ((k==null ? 0 : k.hashCode()) ^
                     (v==null ? 0 : v.hashCode()));
        }

        public String toString() {
            return getKey() + "=" + getValue();
        }
    }

        java.util.WeakHashMap.Entry扩展自java.lang.ref.WeakReference,将Key(Map中的键)作为虚引用的关联对象。
        由于WeakHashMap本身允许键值为null,所以这里不同于HashMap,需要利用一些转化函数。
    /**
     * Value representing null keys inside tables.
     */
    private static final Object NULL_KEY = new Object();

    /**
     * Use NULL_KEY for key if it is null.
     */
    private static Object maskNull(Object key) {
        return (key == null ? NULL_KEY : key);
    }

    /**
     * Returns internal representation of null key back to caller as null.
     */
    private static <K> K unmaskNull(Object key) {
        return (K) (key == NULL_KEY ? null : key);
    }

        由于虚引用的性质,当WeakHashMap中的某个Key已经没有外部的强引用,那么在接下来发生的垃圾收集动作里,它将会被回收。这里要注意一下,回收的仅仅是Key,但是Entry还在呢(也可以说是虚引用本身)。
        所以在一些操作里会调用expungeStaleEntries方法,这个方法里会清理所有Key已经被回收的Entry。
    /**
     * Expunges stale entries from the table.
     */
    private void expungeStaleEntries() {
	Entry<K,V> e;
        while ( (e = (Entry<K,V>) queue.poll()) != null) {
            int h = e.hash;
            int i = indexFor(h, table.length);

            Entry<K,V> prev = table[i];
            Entry<K,V> p = prev;
            while (p != null) {
                Entry<K,V> next = p.next;
                if (p == e) {
                    if (prev == e)
                        table[i] = next;
                    else
                        prev.next = next;
                    e.next = null;  // Help GC
                    e.value = null; //  "   "
                    size--;
                    break;
                }
                prev = p;
                p = next;
            }
        }
    }

    /**
     * Returns the table after first expunging stale entries.
     */
    private Entry[] getTable() {
        expungeStaleEntries();
        return table;
    }

    /**
     * Returns the number of key-value mappings in this map.
     * This result is a snapshot, and may not reflect unprocessed
     * entries that will be removed before next attempted access
     * because they are no longer referenced.
     */
    public int size() {
        if (size == 0)
            return 0;
        expungeStaleEntries();
        return size;
    }

        基本上差异就是这些。WeakHashMap本身的特性也使它经常被应用到一些缓存的场景。
分享到:
评论

相关推荐

    【java毕业设计】新冠疫情下的校园出入系统源码(ssm+mysql+说明文档+LW).zip

    功能说明: 本系统主要包括以下功能模块:个人中心,通知公告管理,用户管理,工作人员管理,进门登记管理,出门登记管理,出入统计管理,外来登记管理等模块。 环境说明: 开发语言:Java 框架:ssm,mybatis JDK版本:JDK1.8 数据库:mysql 5.7及以上 数据库工具:Navicat11及以上 开发软件:eclipse/idea Maven包:Maven3.3及以上 服务器:tomcat7及以上

    param-1.5.1-cp34-cp34m-win_amd64.whl.rar

    PartSegCore_compiled_backend-0.12.0a0-cp36-cp36m-win_amd64.whl.rar

    yolo算法-分神驾驶数据集-8674张图像带标签-没有安全带-唤醒-昏昏欲睡-安全带-电话-打哈欠.zip

    yolo算法-分神驾驶数据集-8674张图像带标签-没有安全带-唤醒-昏昏欲睡-安全带-电话-打哈欠.zip;yolo算法-分神驾驶数据集-8674张图像带标签-没有安全带-唤醒-昏昏欲睡-安全带-电话-打哈欠.zip;yolo算法-分神驾驶数据集-8674张图像带标签-没有安全带-唤醒-昏昏欲睡-安全带-电话-打哈欠.zip;yolo算法-分神驾驶数据集-8674张图像带标签-没有安全带-唤醒-昏昏欲睡-安全带-电话-打哈欠.zip

    pgmagick-0.7.5-cp37-cp37m-win_amd64.whl.rar

    python whl离线安装包 pip安装失败可以尝试使用whl离线安装包安装 第一步 下载whl文件,注意需要与python版本配套 python版本号、32位64位、arm或amd64均有区别 第二步 使用pip install XXXXX.whl 命令安装,如果whl路径不在cmd窗口当前目录下,需要带上路径 WHL文件是以Wheel格式保存的Python安装包, Wheel是Python发行版的标准内置包格式。 在本质上是一个压缩包,WHL文件中包含了Python安装的py文件和元数据,以及经过编译的pyd文件, 这样就使得它可以在不具备编译环境的条件下,安装适合自己python版本的库文件。 如果要查看WHL文件的内容,可以把.whl后缀名改成.zip,使用解压软件(如WinRAR、WinZIP)解压打开即可查看。 为什么会用到whl文件来安装python库文件呢? 在python的使用过程中,我们免不了要经常通过pip来安装自己所需要的包, 大部分的包基本都能正常安装,但是总会遇到有那么一些包因为各种各样的问题导致安装不了的。 这时我们就可以通过尝试去Python安装包大全中(whl包下载)下载whl包来安装解决问题。

    中国省级与地级市财政转移支付数据-最新.zip

    中国省级与地级市财政转移支付数据-最新.zip

    OPENCV 常用XML 内涵17个常用XML,包括人脸检测,微笑检测,人眼检测,用于学习模型训练和使用

    OPENCV 常用XML 内涵17个常用XML,包括人脸检测,微笑检测,人眼检测,用于学习模型训练和使用

    polylearn-0.1.dev0-cp27-cp27m-win_amd64.whl.rar

    python whl离线安装包 pip安装失败可以尝试使用whl离线安装包安装 第一步 下载whl文件,注意需要与python版本配套 python版本号、32位64位、arm或amd64均有区别 第二步 使用pip install XXXXX.whl 命令安装,如果whl路径不在cmd窗口当前目录下,需要带上路径 WHL文件是以Wheel格式保存的Python安装包, Wheel是Python发行版的标准内置包格式。 在本质上是一个压缩包,WHL文件中包含了Python安装的py文件和元数据,以及经过编译的pyd文件, 这样就使得它可以在不具备编译环境的条件下,安装适合自己python版本的库文件。 如果要查看WHL文件的内容,可以把.whl后缀名改成.zip,使用解压软件(如WinRAR、WinZIP)解压打开即可查看。 为什么会用到whl文件来安装python库文件呢? 在python的使用过程中,我们免不了要经常通过pip来安装自己所需要的包, 大部分的包基本都能正常安装,但是总会遇到有那么一些包因为各种各样的问题导致安装不了的。 这时我们就可以通过尝试去Python安装包大全中(whl包下载)下载whl包来安装解决问题。

    postgresadapter-2.0.1-cp36-cp36m-win_amd64.whl.rar

    python whl离线安装包 pip安装失败可以尝试使用whl离线安装包安装 第一步 下载whl文件,注意需要与python版本配套 python版本号、32位64位、arm或amd64均有区别 第二步 使用pip install XXXXX.whl 命令安装,如果whl路径不在cmd窗口当前目录下,需要带上路径 WHL文件是以Wheel格式保存的Python安装包, Wheel是Python发行版的标准内置包格式。 在本质上是一个压缩包,WHL文件中包含了Python安装的py文件和元数据,以及经过编译的pyd文件, 这样就使得它可以在不具备编译环境的条件下,安装适合自己python版本的库文件。 如果要查看WHL文件的内容,可以把.whl后缀名改成.zip,使用解压软件(如WinRAR、WinZIP)解压打开即可查看。 为什么会用到whl文件来安装python库文件呢? 在python的使用过程中,我们免不了要经常通过pip来安装自己所需要的包, 大部分的包基本都能正常安装,但是总会遇到有那么一些包因为各种各样的问题导致安装不了的。 这时我们就可以通过尝试去Python安装包大全中(whl包下载)下载whl包来安装解决问题。

    Polygon2-2.0.8-cp27-cp27m-win_amd64.whl.rar

    python whl离线安装包 pip安装失败可以尝试使用whl离线安装包安装 第一步 下载whl文件,注意需要与python版本配套 python版本号、32位64位、arm或amd64均有区别 第二步 使用pip install XXXXX.whl 命令安装,如果whl路径不在cmd窗口当前目录下,需要带上路径 WHL文件是以Wheel格式保存的Python安装包, Wheel是Python发行版的标准内置包格式。 在本质上是一个压缩包,WHL文件中包含了Python安装的py文件和元数据,以及经过编译的pyd文件, 这样就使得它可以在不具备编译环境的条件下,安装适合自己python版本的库文件。 如果要查看WHL文件的内容,可以把.whl后缀名改成.zip,使用解压软件(如WinRAR、WinZIP)解压打开即可查看。 为什么会用到whl文件来安装python库文件呢? 在python的使用过程中,我们免不了要经常通过pip来安装自己所需要的包, 大部分的包基本都能正常安装,但是总会遇到有那么一些包因为各种各样的问题导致安装不了的。 这时我们就可以通过尝试去Python安装包大全中(whl包下载)下载whl包来安装解决问题。

    基于SSM的医院管理系统的设计与实现

    开发语言:Java 框架:SSM JDK版本:JDK1.8 服务器:tomcat8.5 数据库:mysql 5.7 数据库工具:Navicat11 开发软件:eclipse/myeclipse/idea 浏览器:谷歌浏览器

    yolo算法-扑克牌数据集-1285张图像带标签.zip

    yolo系列算法目标检测数据集,包含标签,可以直接训练模型和验证测试,数据集已经划分好,包含数据集配置文件data.yaml,适用yolov5,yolov8,yolov9,yolov7,yolov10,yolo11算法; 包含两种标签格:yolo格式(txt文件)和voc格式(xml文件),分别保存在两个文件夹中; yolo格式:<class> <x_center> <y_center> <width> <height>, 其中: <class> 是目标的类别索引(从0开始)。 <x_center> 和 <y_center> 是目标框中心点的x和y坐标,这些坐标是相对于图像宽度和高度的比例值,范围在0到1之间。 <width> 和 <height> 是目标框的宽度和高度,也是相对于图像宽度和高度的比例值

    中国各省GDP及农业主要指标数据集(1999-2019).zip

    中国各省GDP及农业主要指标数据集(1999-2019).zip

    【java毕业设计】大学生校园图书角管理系统源码(ssm+mysql+说明文档+LW).zip

    功能说明: 系统主要包括系统主页、个人中心、用户管理、图书信息管理、图书分类管理、图书购买管理、图书借阅管理、图书续借管理、图书归还管理、留言板管理、系统管理等功能模块。 环境说明: 开发语言:Java 框架:ssm,mybatis JDK版本:JDK1.8 数据库:mysql 5.7及以上 数据库工具:Navicat11及以上 开发软件:eclipse/idea Maven包:Maven3.3及以上 服务器:tomcat7及以上

    2023-04-06-项目笔记 - 第三百二十阶段 - 4.4.2.318全局变量的作用域-318 -2025.11.17

    2023-04-06-项目笔记-第三百二十阶段-课前小分享_小分享1.坚持提交gitee 小分享2.作业中提交代码 小分享3.写代码注意代码风格 4.3.1变量的使用 4.4变量的作用域与生命周期 4.4.1局部变量的作用域 4.4.2全局变量的作用域 4.4.2.1全局变量的作用域_1 4.4.2.318局变量的作用域_318- 2024-11-17

    Saturn-PCB-Toolkit-V7.00(土星PCB计算器)

    Saturn_PCB_Toolkit_V7.00_ 是一款功能强大的PCB参数计算工具,本人PCB设计15年一直使用的这款计算器,利用其给出的设计数据(如线距、线宽线厚),布出的PCB实际使用未曾出现过问题 以下是其主要功能特点: 1. **过孔特性计算**:能够计算过孔的寄生电容、寄生电感、过孔阻抗、过孔直流电阻、共振频率、阶跃响应、功耗等参数。 2. **导线载流能力计算**:可以计算不同线宽下的载流能力,根据环境温度和温升条件,提供不同条件下的载流值。 3. **串扰计算**:计算两相互耦合信号线间的串扰,这对于高速PCB设计尤为重要。 4. **波长计算**:提供波长的计算功能,这对于射频和高速数字PCB设计非常关键。 5. **导体阻抗计算**:计算导体的阻抗,这对于阻抗匹配和信号完整性至关重要。 6. **单位换算**:提供单位换算功能,方便不同单位制之间的转换。 7. **差分对计算**:针对差分信号的计算,这对于高速数据传输和降低噪声非常重要。

    yolo算法-车内乘客识别器数据集-1035张图像带标签-乘客.zip

    yolo系列算法目标检测数据集,包含标签,可以直接训练模型和验证测试,数据集已经划分好,包含数据集配置文件data.yaml,适用yolov5,yolov8,yolov9,yolov7,yolov10,yolo11算法; 包含两种标签格:yolo格式(txt文件)和voc格式(xml文件),分别保存在两个文件夹中; yolo格式:<class> <x_center> <y_center> <width> <height>, 其中: <class> 是目标的类别索引(从0开始)。 <x_center> 和 <y_center> 是目标框中心点的x和y坐标,这些坐标是相对于图像宽度和高度的比例值,范围在0到1之间。 <width> 和 <height> 是目标框的宽度和高度,也是相对于图像宽度和高度的比例值

    最新整理-中国各、省、市、县、乡镇基尼系数数据(到2023年)

    ## 一、数据介绍 数据名称:中国各、省、市、县、乡镇基尼系数数据 数据年份:1992-2023年 样本数量:92064条 数据格式:面板数据 ## 二、指标说明 共计10个指标:年份、省、省代码、市、市代码、县、县代码、乡镇、乡镇代码、夜间灯光基尼系数 ## 三、数据文件 中国各乡镇基尼系数(基于夜间灯光数据)2000-2023年.dta; 中国各区县基尼系数(基于夜间灯光数据)2000-2023年.dta; 中国各城市基尼系数(基于夜间灯光数据)2000-2023年.dta; 中国各省份基尼系数(基于夜间灯光数据)2000-2023年.dta

    【java毕业设计】学业帮扶管理系统源码(ssm+mysql+说明文档).zip

    环境说明: 开发语言:Java 框架:ssm,mybatis JDK版本:JDK1.8 数据库:mysql 5.7及以上 数据库工具:Navicat11及以上 开发软件:eclipse/idea Maven包:Maven3.3及以上 服务器:tomcat7及以上

    【java毕业设计】毕业生生活用品出售网站系统源码(ssm+mysql+说明文档+LW).zip

    功能说明: 系统主要包括管理员,商家和用户三大模块 (a) 管理员;管理员进入系统主要功能包括个人中心,用户管理,商家管理,用品分类管理,用品信息管理,系统管理,订单管理等功能并进行操作。 (b) 商家;商家进入系统主要功能包括个人中心,用品信息管理,订单管理等功能并进行操作。 (c) 用户;用户进入系统主要功能包括个人中心,我的收藏管理等功能并进行操作。 环境说明: 开发语言:Java 框架:ssm,mybatis JDK版本:JDK1.8 数据库:mysql 5.7及以上 数据库工具:Navicat11及以上 开发软件:eclipse/idea Maven包:Maven3.3及以上 服务器:tomcat7及以上

    最新行政区划、乡镇级矢量地图及2022年道路、水系、建筑轮廓数据(shp格式)-已更至最新.zip

    最新行政区划、乡镇级矢量地图及2022年道路、水系、建筑轮廓数据(shp格式)-已更至最新.zip

Global site tag (gtag.js) - Google Analytics