整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

现在才知道Arrays.asList()原来是这样用

现在才知道Arrays.asList()原来是这样用的

注于Java领域优质技术,欢迎关注

来自:JavaGuide , 作者 SnailClimb

简介

Arrays.asList()在平时开发中还是比较常见的,我们可以使用它将一个数组转换为一个List集合。

JDK 源码对于这个方法的说明:

《阿里巴巴Java 开发手册》对其的描述

Arrays.asList()将数组转换为集合后,底层其实还是数组,《阿里巴巴Java 开发手册》对于这个方法有如下描述:

阿里巴巴Java开发手册-Arrays.asList()方法

使用时的注意事项总结

传递的数组必须是对象数组,而不是基本类型。

Arrays.asList()是泛型方法,传入的对象必须是对象数组。

当传入一个原生数据类型数组时,Arrays.asList() 的真正得到的参数就不是数组中的元素,而是数组对象本身!此时List 的唯一元素就是这个数组,这也就解释了上面的代码。

我们使用包装类型数组就可以解决这个问题。

Integer[] myArray={ 1, 2, 3 };

使用集合的修改方法:add()、remove()、clear()会抛出异常。

Arrays.asList() 方法返回的并不是 java.util.ArrayList ,而是 java.util.Arrays 的一个内部类,这个内部类并没有实现集合的修改方法或者说并没有重写这些方法。

List myList=Arrays.asList(1, 2, 3);

System.out.println(myList.getClass());//class java.util.Arrays$ArrayList

下图是java.util.Arrays$ArrayList的简易源码,我们可以看到这个类重写的方法有哪些。

我们再看一下java.util.AbstractList的remove()方法,这样我们就明白为啥会抛出UnsupportedOperationException。

public E remove(int index) {

throw new UnsupportedOperationException();

}

如何正确的将数组转换为ArrayList?

stackoverflow:https://dwz.cn/vcBkTiTW

1. 自己动手实现(教育目的)

2. 最简便的方法(推荐)

List list=new ArrayList<>(Arrays.asList("a", "b", "c"))

3. 使用 Java8 的Stream(推荐)

4. 使用 Guava(推荐)

对于不可变集合,你可以使用ImmutableList类及其of()与copyOf()工厂方法:(参数不能为空)

对于可变集合,你可以使用Lists类及其newArrayList()工厂方法:

5. 使用 Apache Commons Collections

List<String> list=new ArrayList<String>();

CollectionUtils.addAll(list, str);


现在人工智能非常火爆,很多朋友都想学,但是一般的教程都是为博硕生准备的,太难看懂了。最近发现了一个非常适合小白入门的教程,不仅通俗易懂而且还很风趣幽默。所以忍不住分享一下给大家。点这里可以跳转到教程。

https://www.captainbed.net/suga

文由掘金@hpstream_授权发布

https://juejin.cn/post/7034796986889043999

谈到WebComponent 很多人很容易想到Vue,React中的组件。但其实H5原生也已经支持了组件的编写。
关于Web Components,MDN是如此定义其概念和使用的:

Web Components 是一套不同的技术,允许您创建可重用的定制元素(它们的功能封装在您的代码之外)并且在您的web应用中使用它们。

作为开发者,我们都知道尽可能多的重用代码是一个好主意。这对于自定义标记结构来说通常不是那么容易 — 想想复杂的HTML(以及相关的样式和脚本),有时您不得不写代码来呈现自定义UI控件,并且如果您不小心的话,多次使用它们会使您的页面变得一团糟。

Web Components旨在解决这些问题 — 它由三项主要技术组成,它们可以一起使用来创建封装功能的定制元素,可以在你喜欢的任何地方重用,不必担心代码冲突。

?

Custom elements(自定义元素):一组JavaScript API,允许您定义custom elements及其行为,然后可以在您的用户界面中按照需要使用它们。

?

?

Shadow DOM(影子DOM):一组JavaScript API,用于将封装的“影子”DOM树附加到元素(与主文档DOM分开呈现)并控制其关联的功能。通过这种方式,您可以保持元素的功能私有,这样它们就可以被脚本化和样式化,而不用担心与文档的其他部分发生冲突。

?

?

HTML templates(HTML模板):template 和 slot 元素使您可以编写不在呈现页面中显示的标记模板。然后它们可以作为自定义元素结构的基础被多次重用。

?

上面的概念难以理解,我们通过一个例子看下如何编写一个组件

案例一

「1、什么是 HTML templates(HTML模板)?」

 <template id="btn">
    <button class="hp-button">
      <slot></slot>
    </button>
  </template>

「2、Custom elements(自定义元素)」

class HpButton extends HTMLElement {
      constructor() {
        super();
        //...
        
      }
    }
// 定义了一个自定义标签 组件
window.customElements.define('hp-button', HpButton)

「3、Shadow DOM(影子DOM)」

 let shadow=this.attachShadow({
          mode: 'open'
        });
        let btnTmpl=document.getElementById('btn');
        let cloneTemplate=btnTmpl.content.cloneNode(true)
        const style=document.createElement('style');
        let type=this.getAttribute('type') || 'default';
        const btnList={
          'primary': {
            background: '#ff0000',
            color: '#fff'
          },
          'default': {
            background: '#909399',
            color: '#fff'
          }
        }
        style.textContent=`
                    .hp-button{
                        outline:none;
                        border:none;
                        border-radius:4px;
                        padding:5px 20px;
                        display:inline-flex;
                        background:${btnList[type].background};
                        color:${btnList[type].color};
                        cursor:pointer
                    }
                `
// dom操作具备移动型
  shadow.appendChild(style)
 shadow.appendChild(cloneTemplate)

一个完整简单的例子

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <style>
    :root {
      --background-color: black;
      --text-color: yellow
    }
  </style>
  <hp-button type="primary">
    <input type="text">
    按钮
  </hp-button>
  <hp-button>前端晚间课</hp-button>
  <!-- 内容是不会被渲染到视图上,不会影响页面展示,可以使用模板 -->
  <template id="btn">
    <button class="hp-button">
      <slot></slot>
    </button>
  </template>

  <script>
    class HpButton extends HTMLElement {
      constructor() {
        super();
        let shadow=this.attachShadow({
          mode: 'open'
        });
        let btnTmpl=document.getElementById('btn');
        let cloneTemplate=btnTmpl.content.cloneNode(true)
        const style=document.createElement('style');
        let type=this.getAttribute('type') || 'default';
        const btnList={
          'primary': {
            background: '#ff0000',
            color: '#fff'
          },
          'default': {
            background: '#909399',
            color: '#fff'
          }
        }
        style.textContent=`
                    .hp-button{
                        outline:none;
                        border:none;
                        border-radius:4px;
                        padding:5px 20px;
                        display:inline-flex;
                        background:${btnList[type].background};
                        color:${btnList[type].color};
                        cursor:pointer
                    }
                `
        // dom操作具备移动型
        shadow.appendChild(style)
        shadow.appendChild(cloneTemplate)
      }
    }
    // 定义了一个自定义标签 组件
    window.customElements.define('hp-button', HpButton)
  </script>
</body>

</html>

结论

原生组件与Vue,React的组件的概念是相似的,但是从写法上来看有区别。

深入学习

在custom element的构造函数中,可以指定多个不同的回调函数,它们将会在元素的不同生命时期被调用:

  • connectedCallback:当 custom element首次被插入文档DOM时,被调用。
  • disconnectedCallback:当 custom element从文档DOM中删除时,被调用。
  • adoptedCallback:当 custom element被移动到新的文档时,被调用。
  • attributeChangedCallback: 当 custom element增加、删除、修改自身属性时,被调用。

我们来看一下它们的一下用法示例。下面的代码出自life-cycle-callbacks(https://github.com/mdn/web-components-examples/tree/master/life-cycle-callbacks)示例(查看在线示例:https://mdn.github.io/web-components-examples/life-cycle-callbacks/)。这个简单示例只是生成特定大小、颜色的方块。custom element看起来像下面这样:

生命周期的代码的具体示例:

class Square extends HTMLElement {
  // Specify observed attributes so that
  // attributeChangedCallback will work
  static get observedAttributes() {
    return ['c', 'l'];
  }

  constructor() {
    // Always call super first in constructor
    super();

    const shadow=this.attachShadow({mode: 'open'});

    const div=document.createElement('div');
    const style=document.createElement('style');
    shadow.appendChild(style);
    shadow.appendChild(div);
  }

  connectedCallback() {
    console.log('Custom square element added to page.');
    updateStyle(this);
  }

  disconnectedCallback() {
    console.log('Custom square element removed from page.');
  }

  adoptedCallback() {
    console.log('Custom square element moved to new page.');
  }

  attributeChangedCallback(name, oldValue, newValue) {
    console.log('Custom square element attributes changed.');
    updateStyle(this);
  }
}

customElements.define('custom-square', Square);

事件

 document.querySelector('???').dispatchEvent(new CustomEvent('changeName', {
        detail: {
          name: 1111,
        }
      }))

折叠面板的案例

  • 完成模版部分的定义:
 <!-- 没有实际意义, 不会渲染到页面上 -->
  <template id="collapse_tmpl">
    <div class="zf-collapse">
      <slot></slot>
    </div>
  </template>
  <template id="collapse_item_tmpl">
    <div class="zf-collapse-item">
      <div class="title"></div>
      <div class="content">
        <slot></slot>
      </div>
    </div>
  </template>

  • 创建组件:
class Collapse extends HTMLElement {
  constructor() {
    super();
    const shadow=this.attachShadow({
      mode: 'open'
    });
    const tmpl=document.getElementById('collapse_tmpl');
    let cloneTemplate=tmpl.content.cloneNode(true);
    let style=document.createElement('style');
    // :host 代表的是影子的根元素
    style.textContent=`
            :host{
                display:flex;
                border:3px solid #ebebeb;
                border-radius:5px;
                width:100%;
            }
            .zf-collapse{
                width:100%;
            }
        `
    shadow.appendChild(style);
    shadow.appendChild(cloneTemplate);

    let slot=shadow.querySelector('slot'); // 监控slot变化
    slot.addEventListener('slotchange', (e)=> {
      this.slotList=e.target.assignedElements();
      this.render();
    })
  }
  static get observedAttributes() { // 监控属性的变化
    return ['active']
  }
  // update
  attributeChangedCallback(key, oldVal, newVal) {
    if (key=='active') {
      this.activeList=JSON.parse(newVal);
      this.render();
    }
  }
  render() {
    if (this.slotList && this.activeList) {
      [...this.slotList].forEach(child=> {
        child.setAttribute('active', JSON.stringify(this.activeList))
      });
    }
  }

}
export default Collapse

class CollapseItem extends HTMLElement {
  constructor() {
    super();
    let shadow=this.attachShadow({
      mode: 'open'
    });
    let tmpl=document.getElementById('collapse_item_tmpl');
    let cloneTemplate=tmpl.content.cloneNode(true);
    let style=document.createElement('style');
    this.isShow=true; // 标识自己是否需要显示

    style.textContent=`
            :host{
                width:100%;
            }
            .title{
                background:#f1f1f1;
                line-height:35px;
                height:35px;
            }
            .content{
                font-size:14px;
            }
        `

    shadow.appendChild(style)
    shadow.appendChild(cloneTemplate);
    this.titleEle=shadow.querySelector('.title');

    this.titleEle.addEventListener('click', ()=> {
      // 如果将结果传递给父亲  组件通信?
      document.querySelector('zf-collapse').dispatchEvent(new CustomEvent('changeName', {
        detail: {
          name: this.getAttribute('name'),
          isShow: this.isShow
        }
      }))
    })
  }

  static get observedAttributes() { // 监控属性的变化
    return ['active', 'title', 'name']
  }
  // update
  attributeChangedCallback(key, oldVal, newVal) {
    switch (key) {
      case 'active':
        this.activeList=JSON.parse(newVal); // 子组件接受父组件的数据
        break;
      case 'title':
        this.titleEle.innerHTML=newVal; // 接受到title属性 作为dom的title
        break;
      case 'name':
        this.name=newVal
        break;
    }
    let name=this.name;
    if (this.activeList && name) {
      this.isShow=this.activeList.includes(name);
      this.shadowRoot.querySelector('.content').style.display=this.isShow ? 'block' : 'none'
    }
  }
}
export default CollapseItem

  • 页面使用:
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <zf-collapse>
    <zf-collapse-item title="Node" name="1">
      <div>nodejs welcome</div>
    </zf-collapse-item>
    <zf-collapse-item title="react" name="2">
      <div>react welcome</div>
    </zf-collapse-item>
    <zf-collapse-item title="vue" name="3">
      <div>vue welcome</div>
    </zf-collapse-item>
  </zf-collapse>

  <!-- 没有实际意义, 不会渲染到页面上 -->
  <template id="collapse_tmpl">
    <div class="zf-collapse">
      <slot></slot>
    </div>
  </template>
  <template id="collapse_item_tmpl">
    <div class="zf-collapse-item">
      <div class="title"></div>
      <div class="content">
        <slot></slot>
      </div>
    </div>
  </template>
  <!-- vite 实现原理 就依赖于 type="module" -->
  <script src="./index1.js" type="module"></script>
</body>

</html>

更多:

案例Demo(https://github.com/mdn/web-components-examples)

之前的文章鸿蒙应用开发之运行HelloWorld 里我运行了一个穿戴设备的应用,利用JS UI实现了一个最简单的HelloWorld。

今天我打算在智慧屏设备上利用豆瓣音乐的接口数据实现一个简单的List界面。

说起来这是一个很简单的功能实现,不过其中也有不少的坑。

首先要说明的是,鸿蒙应用开发里有Java UI框架和JS UI框架,如果要谈界面实现的简单方便,当然是选用JS UI框架了。

大概的浏览了一下JS API的文档,

https://developer.harmonyos.com/cn/docs/documentation/doc-references/js-apis-overview-0000001056361791

我发现鸿蒙应用开发里面的JS限制还是比较多的,和原生的JS差别也很大,不过毕竟是在别人的地盘下开发,还是要入乡随俗。

鸿蒙应用的JS UI框架已经有了很多的组件,满足一般的开发是没问题的,也提供了自定义组件。

当然也提供了底层接口,可以获取硬件信息,地理位置等等。

在今天的开发里,我最关心的就是List组件和数据获取的方式。

https://developer.harmonyos.com/cn/docs/documentation/doc-references/js-components-container-list-0000000000611496


按照文档里提供的范例,我把index.html改成了如下代码

<!-- index.hml -->
<div class="container"> 
        <list>     
            <list-item for="{{data.items}}">  
                <text  class="item">{{$item.name}}</text>
            </list-item>   
        </list>
</div>

在index.js里我把data部分改成了如下:

data: {   
    data:{       
        items:[       
            {                name:"a"            },       
            {                name:"b"           }   
        ]
    },
},


运行以后的效果如下:


那么现在的问题就是如何获取数据了。

在官方文档里我查到了fetch方法:

https://developer.harmonyos.com/cn/docs/documentation/doc-references/js-apis-network-data-request-0000000000626077

根据文档的说法,我们首先要在index.js里导入fecth:

import fetch from '@system.fetch';

然后文档里说了这个方法需要ohos.permission.INTERNET权限,于是我就去config.json里增加了权限。

在\DevEcoStudioProjects\MyApplication1\entry\src\main\config.json里找到module,在这个属性下面增加:

"reqPermissions": [  {    "name": "ohos.permission.INTERNET" }],

这个权限声明部分其实还有很多需要增加的属性,可以参考:

https://developer.harmonyos.com/cn/docs/documentation/doc-guides/security-permissions-guidelines-0000000000029886

这个其实跟安卓开发差不多。

不过这个fetch方法跟原生JS的fetch不太一样

fetch.fetch({    
    url:"https://www.douban.com/j/app/radio/channels",    
    responseType: 'json',    
    success: function(response) {        
        console.info('response data:' + response.data);        
        console.info('response data:' + JSON.parse(response.data).channels);       
     },   
     fail: function(data, code) {        
        console.info('fail callback');  
      },
})

这个responseType据说设置成json就会返回对象,不过我测试下来并不会。

虽然你看我写出来一副了然于胸的样子,其实权限部分和这个fetch方法我一开始并不知道。

本来我以为可以直接用fetch,结果发现并不可以,后来突然想起来官方文档里号称用的是ES6语法,我就试了试import,误打误撞才发现原来需要自己导入fetch。

这时候我才去文档里找到了相关的说明,才知道居然是fetch.fetch。

一开始也还是无法返回数据,我还以为华为的远端调试不支持访问外网,再看文档才注意到权限。

设置了权限也无法访问,又会去看文档才注意到默认用https,要用http还需要另外设置。

这些都修改好了,就非常顺利地返回了数据。

接下来的问题就是如何把数据渲染到界面上。

我把index.js里的onInit做了如下修改:

onInit() {    
     let me=this;
    fetch.fetch({
        url:"https://www.douban.com/j/app/radio/channels",
        responseType: 'json',
        success: function(response) {
            console.info('response data:' + response.data);
            me.data.items=JSON.parse(response.data).channels;
        },
        fail: function(data, code) {
            console.info('fail callback');
        },
    })
}

重新运行之后(如果本地调试,是可以热更新的,远程调试需要重新运行):

所以说,直接修改data里的数据是可以导致重新渲染界面的。