zoukankan      html  css  js  c++  java
  • vue(版本2.xx) + 环信web sdk(版本1.8.3)实现临时聊天人员列表、历史记录的展示及收发文字、表情、图片

    实现方案:

    环信没有临时聊天人员历史记录接口,所以实现的方案是:

    1.监听消息接收;2.将受到的消息存到缓存中,同时判断此用户是否之前聊天过,如果没聊过的话,把他的用户信息也存到缓存中;3.展示的东西都是从缓存中获取;4.新消息提醒是在监听到消息后,给用户信息里设置一个状态值,当点击刚用户查看历史记录时,再改变这个状态值

    实现效果:

    1.准备工作

    npm引入sdk及 strophe.js坑:刚开始下载的最新版的sdk,但是下载下来总是少文件src,挣扎半天,换了1.8.3版本的sdk好了,可能跟我项目的哪些东西的版本有不兼容的吧

    (1)cnpm i easemob-websdk@1.8.3 --save

    (2)cnpm i strophe.js@1.2.16 --save

    (3)下载webim.config.js:   https://gitee.com/weimingye/web-im/blob/master/demo/javascript/dist/webim.config.js#

    2.修改sdk的connection.js文件(注意新增代码的位置,放在11行声明变量的后边

     代码:

    //新增代码***********start
    var Strophe = require('../../strophe.js/dist/strophe.js').Strophe;
    var meStrophe = require('../../strophe.js/dist/strophe.js');
    $iq = meStrophe.$iq;
    $build = meStrophe.$build;
    $msg = meStrophe.$msg;
    $pres = meStrophe.$pres;
    //新增代码***********end

    3.修改strophe.js

     代码:

    //新增代码***********start
      setJid: function (jid) {
        this.jid = jid;
        this.authzid = Strophe.getBareJidFromJid(this.jid);
        this.authcid = Strophe.getNodeFromJid(this.jid);
      },
    
      getJid: function () {
        return this.jid;
      },
      //新增代码***********end

    4.修改webim.config.js(首行、最后一行,把自己公司的appkey换上去)

     5.main.js新增配置

    //环信start
    require('./assets/webim.config.js')
    let WebIM = require('easemob-websdk')
    Vue.prototype.$webim = WebIM
    
    const conn = new WebIM.connection({
      isMultiLoginSessions: WebIM.config.isMultiLoginSessions,
      https: typeof WebIM.config.https === 'boolean' ? WebIM.config.https : location.protocol === 'https:',
      url: WebIM.config.xmppURL,
      heartBeatWait: WebIM.config.heartBeatWait,
      autoReconnectNumMax: WebIM.config.autoReconnectNumMax,
      autoReconnectInterval: WebIM.config.autoReconnectInterval,
      apiUrl: WebIM.config.apiURL,
      isAutoLogin: true
    })
    
    const options = {
      apiUrl: WebIM.config.apiURL,
      user: '',//用户名
      pwd: '',//密码
      appKey: WebIM.config.appkey,
      success:function (res) {
        console.log('链接服务器正常')
      },
      error:function (err) {
        alert(err)
      }
    }
    Vue.prototype.$imconn = conn
    Vue.prototype.$imoption = options
    //环信end

    6.聊天中的表情编码对应文件

     emoji.js

    module.exports = {
        path: "../../static/faces",
        obj: {
            "[):]": "ee_1.png",
            "[:D]": "ee_2.png",
            "[;)]": "ee_3.png",
            "[:-o]": "ee_4.png",
            "[:p]": "ee_5.png",
            "[(H)]": "ee_6.png",
            "[:@]": "ee_7.png",
            "[:s]": "ee_8.png",
            "[:$]": "ee_9.png",
            "[:(]": "ee_10.png",
            "[:'(]": "ee_11.png",
            "[:|]": "ee_18.png",
            "[(a)]": "ee_13.png",
            "[8o|]": "ee_14.png",
            "[8-|]": "ee_15.png",
            "[+o(]": "ee_16.png",
            "[<o)]": "ee_12.png",
            "[|-)]": "ee_17.png",
            "[*-)]": "ee_19.png",
            "[:-#]": "ee_20.png",
            "[:-*]": "ee_22.png",
            "[^o)]": "ee_21.png",
            "[8-)]": "ee_23.png",
            "[(|)]": "ee_24.png",
            "[(u)]": "ee_25.png",
            "[(S)]": "ee_26.png",
            "[(*)]": "ee_27.png",
            "[(#)]": "ee_28.png",
            "[(R)]": "ee_29.png",
            "[({)]": "ee_30.png",
            "[(})]": "ee_31.png",
            "[(k)]": "ee_32.png",
            "[(F)]": "ee_33.png",
            "[(W)]": "ee_34.png",
            "[(D)]": "ee_35.png"
        }
    };
    emoji.js

    index.js(暂时未用到)

    const _WIDTH = window.screen.availWidth > 350 ? 350 : window.screen.availWidth;
    export default {
        // whether auto check media query and dispatch by redux or not ?
        reduxMatchMedia: true,
        // map of media query breakpoints
        dimensionMap: {
            xs: "480px",
            sm: "768px",
            md: "992px",
            lg: "1200px",
            xl: "1600px"
        },
        name: "Web IM",
        logo: "",
        SIDER_COL_BREAK: "sm", // md
        SIDER_COL_WIDTH: 80,
        SIDER_WIDTH: 350,
        RIGHT_SIDER_WIDTH: _WIDTH,
        // imgType: {
        //     gif: 1,
        //     bmp: 1,
        //     jpg: 1,
        //     png: 1,
            
        // },
        PAGE_NUM: 20
    };
    index.js

    7.图片文件存放在static文件夹下,图片大致都差不多,网上应该可以查到

    8.表情组件

    <template>
      <span>
        <el-popover ref="popover5" placement="top-start" width="360" v-model="showModal">
          <img
            v-for="(v,i) in emojiList"
            :src="require(`../../../static/faces/${v}`)"
            :key="i"
            @click="selectEmoji(i)"
            class="img-style"
          />
        </el-popover>
        <i class="icon iconfont icon-face" @click="showModal = !showModal"></i>
    
      </span>
    </template>
    
    <script>
    import emoji from "../../config/emoji";
    
    export default {
      data() {
        return {
          emojiList: emoji.obj,
          currentEmoji: "",
          showModal: false
        };
      },
      methods: {
        selectEmoji(e) {
          let value = (this.inpMessage || "") + e;
          this.$data.showModal = false;
          this.$emit("selectEmoji", value);
        }
      },
      props: {
        inpMessage: String
      }
    };
    </script>
    <style scoped>
      /deep/ .el-popover.el-popper {
         360px;
        position: absolute;
        bottom: 20%;
        left: 260px;
        transform-origin: center bottom;
        z-index: 2001;
      }
    .img-style {
       22px;
      margin: 5px;
      cursor: pointer;
    }
    .img-style:hover {
      background-color: aquamarine;
    }
    </style>

    8.页面中使用

    <template>
        <div>
          <div class="headerTitle">{{title}}</div>
          <div class="chatBox">
    <!--        临时聊天用户列表-->
            <div class="chatList" ref="chatList">
              <div class="chatList_title">近期联系人</div>
              <div>
                <div v-if="chatList && chatList.length>0" v-for="(item,index) in chatList" :key="index" class="chatList_item" :class="{'active':index == currentIndex}" @click="selectFriend(item.userPhone,index)">
                  <img :src="item.userPic" :onerror="defaultImgs" style="min- 40px;"/>
                  <div>{{item.userPhone}} {{item.userName}}</div>
                  <span v-if="item.active"></span>
                </div>
              </div>
            </div>
    <!--        聊天内容-->
            <div class="content" ref="chatContent">
    <!--          聊天历史记录-->
              <div class="historyList" id="historyList">
                <div v-for="(item,index) in historyMessage" :key="index">
                  <div class="item_time">{{item.time}}</div>
                  <div class="item_message" :class="{'currentUser':item.from == username_from}">
    <!--                展示图片-->
                    <img v-if="item.type == 'img'" :src="item.message" style="max- 30%"/>
    <!--解析文本或表情-->
                    <p
                      v-if="item.type =='txt'"
                      style="user-select: text"
                      v-html="renderTxt(item.message)"
                    />
                  </div>
    
                </div>
              </div>
    
    <!--发送聊天-->
    
              <div class="chatInput">
    
                <div style="padding: 4px 10px;">
                  <!-- 表情组件 -->
                  <ChatEmoji v-on:selectEmoji="selectEmoji" :inpMessage="message" />
                  <!-- 上传图片 -->
                  <div class="upImgBox">
                    <i class="icon iconfont icon-tupian"></i>
                    <input type="file" class="sendImg" ref="sendImg" @change="sendPrivateUrlImg">
                  </div>
    
                </div>
    
    
    <!--            输入框-->
                <el-input
                  ref="txtDom"
                  type="textarea"
                  :autosize="{ minRows: 4, maxRows: 8}"
                  placeholder="请输入内容"
                  resize="none"
                  v-model="message"
                  @keyup.enter.native="sendPrivateText">
                </el-input>
                <el-button type="primary" size="small" @click="sendPrivateText" style="float: right;margin-right: 20px;">提交</el-button>
    
              </div>
            </div>
          </div>
    
    
        </div>
    </template>
    
    <script>
      import ChatEmoji from "../../components/chatEmoji/index.vue";//表情组件
      import emoji from "../../config/emoji";
    
        export default {
          name: "index",
          components: {
            ChatEmoji
          },
          data(){
              return {
                title: '互动消息',
                type: 'contact',
                currentUserpwd: '123456',
                username_from: '15263819410',//当前登录的用户(发送人手机号)
                username_to: '',//当前聊天的用户名(接收人手机号)
                message:'',//当前发送的信息
                historyMessage: [],//聊天历史记录
                showEmoji: false,//是否展示表情组件
                chatList:[],//临时聊天用户列表
                currentIndex: undefined,//左侧当前激活index
                defaultImgs: 'this.src = "' + require('../../assets/img/userLogo.png') + '"',
              }
          },
          created() {
              this.login();
              this.updateChatList();//获取临时聊天用户列表
          },
          mounted(){
              this.$refs.chatContent.style.height = this.getClientHeight() - 50 + 'px';
              this.$refs.chatList.style.height = this.getClientHeight() - 50 + 'px';
          },
          methods: {
            //登录环信账户
            login(){
              var _this = this;
              this.$imoption.user = this.username_from;
              this.$imoption.pwd = this.currentUserpwd;
              this.$imoption.success = (res)=>{
                // console.log('登录成功回调',res);
    
              }
              this.$imconn.open(this.$imoption);
              this.$imconn.listen({
                onOpened: function (message) {
                  // console.log('用户已上线');
                },
                onClosed: function (message) {
                  this.$message.info('用户已下线')
                },
                //收到表情消息
                onEmojiMessage: function (message) {
    
                  // //把聊天对象名message.from存储到storage中
                  _this.updateChatList(message);
                  //把新接收的信息更新存储到storage中
                  _this.updateStorage(message);
                  _this.selectFriend(message.from);
                },
    
                //收到图片消息
                onPictureMessage: (message) => {
                  //把聊天对象名message.from存储到storage中
                  _this.updateChatList(message);
                  //把新接收的信息更新存储到storage中
                  _this.updateStorage(message);
                  _this.selectFriend(message.from);
    
                },
                //收到文本消息
                onTextMessage: function (message) {
                  //把聊天对象名message.from存储到storage中
                  _this.updateChatList(message);
                  //把新接收的信息更新存储到storage中
                  _this.updateStorage(message);
                //  更新历史记录到页面
                  _this.selectFriend(message.from);
    
                },
                //收到音频消息
                onAudioMessage: function ( message ) {
                  var options = { url: message.url };
    
                  options.onFileDownloadComplete = function ( response ) {
                    //音频下载成功,需要将response转换成blob,使用objectURL作为audio标签的src即可播放。
                    var objectURL = WebIM.utils.parseDownloadResponse.call(Demo.conn, response);
                    // console.log('音频',objectURL)
                  };
    
                  options.onFileDownloadError = function () {
                    //音频下载失败
                  };
    
                  //通知服务器将音频转为mp3
                  options.headers = {
                    'Accept': 'audio/mp3'
                  };
    
                  WebIM.utils.download.call(conn, options);
                },
                //收到视频消息
                onVideoMessage: function (message) {
                  var node = document.getElementById('privateVideo');
                  var option = {
                    url: message.url,
                    headers: {
                      'Accept': 'audio/mp4'
                    },
                    onFileDownloadComplete: function (response) {
                      var objectURL = WebIM.utils.parseDownloadResponse.call(conn, response);
                      // console.log('视频消息',objectURL);
                      node.src = objectURL;
                    },
                    onFileDownloadError: function () {
                      // console.log('File down load error.')
                    }
                  };
                  WebIM.utils.download.call(conn, option);
                },
              })
    
            },
    
    
          //  获取好友列表(项目没用到)
          //   getFriends(){
          //     var newarry = [];
          //     this.friends = newarry;
          //     this.$imconn.getRoster({
          //       success: function (roster) {
          //         console.log('获取好友列表1',roster)
          //         roster.forEach((item,index)=>{
          //           if(item.subscription === 'both' || item.subscription === 'to'){
          //             newarry.push(item);
          //             console.log('好友列表',newarry, item.name);
          //           }
          //         });
          //       },
          //       error: function (error) {
          //         console.log('error',error)
          //       }
          //     })
          //   },
    
          //发送文本消息
            sendPrivateText(){
    
              if(this.message == ''){
                this.$message.error('不能发送空白信息');
                return false;
              }
              if(this.username_to == ''){
                this.$message.error('请选择发送的好友');
                return false;
              }
              var id = this.$imconn.getUniqueId();// 生成本地消息id
              var msg = new WebIM.message('txt',id);
              var sendTime = this.getNowTime();
              var username_from = this.username_from;
              var message = this.message;
              var _this = this;
              msg.set({
                msg: message,                  // 消息内容
                to: _this.username_to,                          // 接收消息对象(用户id)
                roomType: false,
                success: function (id, serverMsgId) {
                  _this.historyMessage.push({
                    from: username_from,
                    message: message,
                    time:sendTime,
                    type:'txt'
                  })
                  window.localStorage.setItem(_this.username_to,JSON.stringify(_this.historyMessage));
                  var lusername = window.localStorage.getItem(_this.username_to);
                  lusername = JSON.parse(lusername);
                  _this.message = '';
                  _this.setScroll();
                },
                fail: function(e){
                  // console.log("Send private text error");
                }
              });
              this.$imconn.send(msg.body);
            },
    
            // 发送图片消息
            sendPrivateUrlImg(context, payload) {
              let _this = this;
              if(_this.username_to == ''){
                _this.$message.error('请选择发送的好友');
                return false;
              }
              let file =  WebIM.utils.getFileUrl(_this.$refs.sendImg); // 将图片转化为二进制文件
              var id = this.$imconn.getUniqueId();                   // 生成本地消息id
              var msg = new WebIM.message('img', id);        // 创建图片消息
              // console.log('图片消息',msg);
              msg.set({
                apiUrl: WebIM.config.apiURL,
                file: file,
                to: this.username_to,// 接收消息对象
                roomType: false,
                chatType: 'singleChat',
                timestamp:new Date().getTime(),
                onFileUploadError: function(error){
                  // console.log("图片上传失败", error);
                },
                onFileUploadComplete: function(file){
                  //更新发送的图片到缓存中
                  _this.historyMessage.push({
                    from: _this.username_from,
                    message: `${file.uri}/${file.entities[0].uuid}`,
                    time:_this.getNowTime(),
                    type:'img'
                  })
                  window.localStorage.setItem(_this.username_to,JSON.stringify(_this.historyMessage));
                  var lusername = window.localStorage.getItem(_this.username_to);
                  lusername = JSON.parse(lusername);
                  this.setScroll();
                },
                success: function(){
                  // console.log("图片发送成功");
    
                }
              });
    
              this.$imconn.send(msg.body);
            },
    
    
            //  选择表情
            selectEmoji (code) {
              this.showEmoji = false
              this.message = code;
              this.$refs.txtDom.focus();
            },
    
    
            //返回表情
            customEmoji(value) {
              return `<img src="http://39.100.159.29/faces/${value}" style="20px"/>`;
              // return '<img src="http://localhost:9527/static/img/logo.063d13ac.png"/>' 本地图片访问不到,原因未知
            },
    
            //解析接收的消息
            renderTxt(txt = "") {
              let rnTxt = [];
              let match = null;
              const regex = /([.*?])/g;
              let start = 0;
              let index = 0;
              while ((match = regex.exec(txt))) {
                index = match.index;
                if (index > start) {
                  rnTxt.push(txt.substring(start, index));
                }
                if (match[1] in emoji.obj) {
                  const v = emoji.obj[match[1]];
                  rnTxt.push(this.customEmoji(v));
                } else {
                  rnTxt.push(match[1]);
                }
                start = index + match[1].length;
              }
              rnTxt.push(txt.substring(start, txt.length));
              return rnTxt.toString().replace(/,/g, "");
            },
    
    
            //  获取发送和接收消息的时间
            getNowTime(){
              let now = new Date();
              return now.getFullYear() + '-' + now.getMonth()+1 + '-' + now.getDate() + ' ' + (now.getHours()<10 ? '0' + now.getHours() : now.getHours()) + ':' + (now.getMinutes()<10 ? '0' + now.getMinutes() : now.getMinutes()) + ':' + (now.getSeconds()<10 ? '0' + now.getSeconds() : now.getSeconds());
            },
    
          //  选择好友发送消息
            selectFriend(item,index){
              if(index != undefined){this.currentIndex = index};
              var chatList = JSON.parse(window.localStorage.getItem('chatList'));
              this.username_to = item;
              //获取当前聊天对象历史记录
              var historyMessage = window.localStorage.getItem(item);
              historyMessage = historyMessage ? JSON.parse(historyMessage) : [];
              var emojiArr = [];
              for(var i=0;i<historyMessage.length;i++){
                var item = historyMessage[i].message;
                if(typeof(item) === 'object'){
                  if(item[0].type == 'emoji'){
                    var emojiItem = {};
                    var url = item[0].data;
                    item.data = url;
                  }
                }
              }
              this.historyMessage = historyMessage;
    
              //清除消息提示
              console.log('chatList',window.localStorage.getItem('chatList'));
    
              for(var i=0;i<chatList.length;i++){
                console.log('username',this.username_to)
                if(this.username_to == chatList[i].userPhone){
                  chatList[i].active = false;
                }
              }
    
              window.localStorage.setItem('chatList',JSON.stringify(chatList));
              this.chatList = chatList;
    
              this.setScroll();
            },
    
          //  更新聊天历史记录缓存数据
            updateStorage(message){
              let sendTimeWZ = message.time ? new Date(message.time) : new Date();
              let sendTime = sendTimeWZ.getFullYear() + '-' + sendTimeWZ.getMonth()+1 + '-' + sendTimeWZ.getDate() + ' ' + (sendTimeWZ.getHours()<10 ? '0' + sendTimeWZ.getHours() : sendTimeWZ.getHours()) + ':' + (sendTimeWZ.getMinutes()<10 ? '0' + sendTimeWZ.getMinutes() : sendTimeWZ.getMinutes()) + ':' + (sendTimeWZ.getSeconds()<10 ? '0' + sendTimeWZ.getSeconds() : sendTimeWZ.getSeconds());
    
              //判断接收的消息是什么类型
              let type = '';
              if(message.url){
                type = 'img';
              }else{
                type = 'txt';
              }
              //定义新接收到的信息
              let message_new = {
                from: message.from,
                message: message.url ? message.url :message.data,
                time: sendTime,
                type: type
              }
    
              var historyMessage = window.localStorage.getItem(message.from);
              historyMessage = historyMessage ? JSON.parse(historyMessage) : [];
              historyMessage.push(message_new);
              window.localStorage.removeItem(message.from);
              window.localStorage.setItem(message.from,JSON.stringify(historyMessage));
    
              this.setScroll();
    
            },
    
          //  判断数组对象中是否有某个属性值
            findElem(chatList,userPhone,formId){
              for(var i=0;i<chatList.length;i++){
                if(chatList[i].userPhone == formId){
                  return i
                }
              }
              return -1;
            },
            //获取可视区域高度
            getClientHeight(){
              var clientHeight=0;
              if(document.body.clientHeight&&document.documentElement.clientHeight)
              {
                var clientHeight = (document.body.clientHeight<document.documentElement.clientHeight)?document.body.clientHeight:document.documentElement.clientHeight;
              }
              else
              {
                var clientHeight = (document.body.clientHeight>document.documentElement.clientHeight)?document.body.clientHeight:document.documentElement.clientHeight;
              }
              return clientHeight;
            },
    
    
      //  更新临时聊天用户列表
            updateChatList(message){
              var chatList_str = window.localStorage.getItem('chatList');
              var chatList = chatList_str ? JSON.parse(chatList_str) : [];
    
              if(message){
                //用户信息之前不存在
                if(this.findElem(chatList,'userPhone',message.from) == (-1)){
                  var chatUser_new = {
                    userName: message.ext.userName,
                    userPic: message.ext.userPic,
                    userPhone: message.from,
                    active: false
                  }
                  chatList.push(chatUser_new);
                }
            //  新消息提示
                for(var i=0;i<chatList.length;i++){
                  if(message.from == chatList[i].userPhone){
                    chatList[i].active = true;
                  }
                }
              }
    
              window.localStorage.setItem('chatList',JSON.stringify(chatList));
              this.chatList = chatList;
            },
    
            //  设置默认滚动到页面最底部
            setScroll(){
              let msg = document.getElementById('historyList') // 获取对象
              if(msg){
                this.$nextTick(() => {
                  msg.scrollTop = msg.scrollHeight // 滚动高度
                })
              }
            }
    
    
    
          }
        }
    </script>
    
    <style scoped lang="scss">
      .headerTitle{
        color: #757373;
         100%;
        height: 50px;
        line-height: 50px;
        padding-left: 10px;
        border-bottom: 1px solid #ddd;
        position: fixed;
        top: 50px;
        left: 210px;
        z-index: 100;
        background-color: #f9fafa;
      }
      #app .hideSidebar  {
        .headerTitle {
          left: 54px;
        }
      }
      #app .mobile.hideSidebar {
        .headerTitle {
          left: 0;
        }
      }
      .chatBox {
        display: flex;
        justify-content: flex-start;
        align-items: flex-start;
      }
    
      .chatList {
         20%;
        height: 100%;
        border-right: 1px solid #ddd;
        display: inline-block;
        font-size: 14px;
        padding-top: 50px;
        .chatList_title {
           100%;
          height: 40px;
          line-height: 40px;
          padding: 0 20px;
          font-weight: 600;
          border-bottom: 1px solid #ddd;
          background-color: #f9fafa;
        }
        .chatList_item {
           100%;
          height: 60px;
          padding: 0 20px;
          border-bottom: 1px solid #ddd;
          display: flex;
          justify-content: flex-start;
          align-items: center;
          cursor: pointer;
          position: relative;
          span {
             10px;
            height: 10px;
            border-radius: 50%;
            background-color: red;
            position: absolute;
            right: 14px;
            top: 10px;
          }
          &.active {
            background-color: #0178EF;
            color: #fff;
          }
          img {
             40px;
            height: 40px;
            border-radius: 50%;
            margin-right: 10px;
          }
        }
      }
      .content {
         80%;
        display: inline-block;
        padding: 44px 0 20px;
        .historyList {
           100%;
          height: 80%;
          padding: 20px 20px 0;
          overflow-y: scroll;
          .item_time {
            text-align: center;
            margin: 10px 0;
          }
          .item_message {
            text-align: left;
            margin-bottom: 20px;
            &.currentUser {
              text-align: right;
              p {
                display: inline-block;
                background-color: #00b7ee;
                border: 1px solid #00b7ee;
                color: #fff;
              }
            }
            p {
              display: inline-block;
              border: 1px solid #ddd;
              border-radius: 4px;
              padding: 4px 8px;
            }
          }
        }
      }
    
    
    
      /*表情聊天对话框*/
      .chatInput {
        border-top: 1px solid #ddd;
        /deep/ .el-textarea__inner:hover {
          border: none;
        }
        /deep/ .el-textarea__inner {
          border: none;
        }
        /deep/ .icon {
          cursor: pointer;
        }
        /*发送图片*/
        .upImgBox {
          display: inline-block;
          position: relative;
           16px;
          height: 17px;
          margin: 0 4px;
          cursor: pointer;
          /deep/ .iconfont {
            position: absolute;
            left: 0;
            top: 0;
            cursor: pointer;
          }
          .sendImg {
            position: absolute;
            left: 0;
            top: 0;
             16px;
            height: 17px;
            opacity: 0;
          }
        }
    
      }
    
    </style>
    使用环信的页面代码
  • 相关阅读:
    重磅!阿里发布《Java开发手册(泰山版)》
    IntelliJ IDEA 快捷键终极大全,速度收藏!
    Java多态的实现机制是什么,写得非常好!
    Intellij IDEA 非常 6 的 10 个姿势!
    深入浅出Java中的clone克隆方法,写得太棒了!
    Java集合类,一张图说清楚!
    在 IntelliJ IDEA 中使用 Git,太方便了!.md
    Http(get,post)及HttpClient(get,post)的简单使用
    java servlet+jquery+json学习小例子
    JAVA中使用JSON进行数据传递
  • 原文地址:https://www.cnblogs.com/duanzhenzhen/p/13151139.html
Copyright © 2011-2022 走看看