Java 中关于 Null 的这些事儿你知道吗
对于 Java 程序员来说,null 一直是令人头疼的问题,经常会受到 NullPointerException 的蹂躏和壁咚。Java 的发明者也承认这是一个巨大的设计错误。那么关于 null ,你应该知道下面这几件事情来有效的了解 null ,从而避免很多由 null 引起的错误。首先,null 是 Java 中的关键字,像是 public、static、final。它是大小写敏感的,你不能将 null 写成 Null 或 NULL,编辑器将不能识别它们然后报错。这个问题已经几乎不会出现,因为eclipse 和 Idea编译器已经给出了编译器提示,所以你不用考虑这个问题。就像是基本类型的默认值一样,例如 int 的默认值是 0,boolean 的默认值是 false,null 是所有引用类型的默认值,Java中的任何引用变量都将null作为默认值,也就是说所有 Object 类下的引用类型默认值都是 null。这对所有的引用变量都适用。publicclass DefaultNullValue {
privatestatic Object value;
public static void printValue(){
System.out.println("value = " + value);
}
public static void main(String[] args) {
printValue();
}
}这对静态和非静态的object来说都是正确的。就像你在这里看到的这样,我将 value 定义为静态引用,还有一个静态方法,我可以在 main 函数中直接使用它。null 既不是对象也不是一种类型,它仅是一种特殊的值,你可以将它赋予任何类型,你可以将 null 转换为任何类型public static void main(String[] args) { String str = null; Integer itr = null; Double dou = null; Integer integer = (Integer) null; String string = (String)null; System.out.println("integer = " + integer); System.out.println("string = " + string);}你可以看到在编译期和运行期内,将null 转换成任何的引用类型都是可行的,并且不会抛出空指针异常null 只能赋值给引用变量,不能赋值给基本类型变量。持有 null 的包装类在进行自动拆箱的时候,不能完成转换,会抛出空指针异常,并且 null 也不能和基本数据类型进行对比public static void main(String[] args) { int i = 0; Integer itr = null; System.out.println(itr == i);}使用了带有 null 值的引用类型变量,instanceof 操作会返回falsepublic static void main(String[] args) { Integer isNull = null; // instanceof = isInstance 方法 if(isNull instanceof Integer){ System.out.println("isNull is instanceof Integer"); }else{ System.out.println("isNull is not instanceof Integer"); }}这是 instanceof 操作符一个很重要的特性,使得对类型强制转换检查很有用静态变量为 null 调用静态方法不会抛出 NullPointerException。因为静态方法使用了静态绑定(更多动态绑定和静态绑定的了解,请参考下面这篇文章 理解静态绑定与动态绑定),例如:publicclass ComplingBinding { public static void staticMehtod(){ System.out.println("静态方法"); } public void instanceMethod(){ System.out.println("非静态方法"); } public static void main(String[] args) { ComplingBinding binding = null; binding.staticMehtod(); binding.instanceMethod(); }}你应该使用 null-safe 安全的方法,java类库中有很多工具类都提供了静态方法,例如基本数据类型的包装类,Integer,Double 等。例如publicclass NullSafeMethod { privatestatic String number; public static void main(String[] args) { String s = String.valueOf(number); String string = number.toString(); System.out.println("s = " + s); System.out.println("string = " + string); }}number 没有赋值,所以默认为null,使用String.value(number) 静态方法没有抛出空指针异常,但是使用 toString()却抛出了空指针异常。所以尽量使用对象的静态方法。你可以使用 == 或者 != 操作来比较 null 值,但是不能使用其他算法或者逻辑操作,例如小于或者大于。跟SQL不一样,在Java中 null == null 将返回 true,如下所示:publicclass CompareNull { privatestatic String str1; privatestatic String str2; public static void main(String[] args) { System.out.println("str1 == str2 ? " + str1 == str2); System.out.println(null == null); }} </div>
Docker 前沿概述
什么是Docker?Docker是基于Go语言实现的开源容器项目。Docker是一个为开发者和系统管理员提供的开发,部署和运行的容器应用程序。Linux使用容器开发应用程序的这种方式称为容器化。Docker项目已加入Linux基金会,并遵循Apache2.0 协议,全部开源代码再http://github.com/docker 项目仓库进行维护。Docker的基本概念Docker中包括三个基本概念:容器(Container)、镜像(Image)、仓库(Repository)容器(Container) -- 镜像运行时的实体先来看看容器较为官方的解释:容器就是将软件打包成基本单元,用来开发、交付和部署。容器镜像是轻量的、可执行的软件包 ,包含软件运行所需的所有内容:代码、运行时环境、系统工具、系统库和设置。容器化软件是基于Linux和Windows的应用,在任何环境中都能够始终如一地运行。容器赋予了软件独立性,使其免受外在环境差异(例如,开发和预演环境的差异)的影响,从而有助于减少团队间在相同环境上运行不同软件时的冲突。镜像(Image) -- 一个特殊的文件系统Docker中的Image镜像相当于是一个文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数。镜像可以基于Dockerfile构建,Dockerfile是一个描述文件,里面包含若干条命令,每条命令都会对基础文件系统创建新的层次结构。用户可以通过编写Dockerfile创建新的镜像,也可以直接从类似github的Docker Hub上下载镜像使用。仓库(Repository) -- 集中存放镜像文件的地方如果你使用过 git 和 github 就很容易理解Docker的仓库概念。Docker仓库相当于一个 github 上的代码库。Docker 仓库是用来包含镜像的位置,Docker提供一个注册服务器(Registry)来保存多个仓库,每个仓库又可以包含多个镜像。它们的范围大小依次是 仓库 > 镜像 > 容器下面再来说说虚拟机前面说到,容器在Linux上本机运行,并与其他容器共享主机的内核。它运行一个独立的进程,不占用任何其他可执行文件的内存,使其轻量级。相比之下,虚拟机运行一个完整的客户操作系统,通过虚拟机管理程序对主机资源进行虚拟访问。通常,VM提供的环境比大多数应用程序需要的资源更多。下面这幅图很好的表述了这一点容器和虚拟机的比较这或许也就能解释 Docker 如此流行的原因Docker容器很快,启动和停止可以在秒级实现,这相比传统的虚拟机方式(数分钟)要快得多;Docker容器对系统资源需求很少,一台主机可以运行数千个Docker容器;Docker通过类似Git设计理念的操作来方便用户获取、分发和更新应用镜像,存储复用,增量更新;Docker通过Dockerfile支持灵活的自动化创新和部署机制,以提高工作效率,并标准化流程。下图是Docker容器技术与传统虚拟机技术的比较:特性容器虚拟机启动速度秒级分钟级性能接近原生较弱内存代价很小较多硬盘使用一般为MB一般为GB运行密度单机支持上千个容器一般为几十个隔离性安全隔离完全隔离迁移性优秀一般Docker 解决了什么样的问题一项技术或者软件的流行都是为了解决某种问题应用而生的,那么话说回来了,Docker解决了什么问题呢?组织有序:如果没有Docker,一台机器就可能像是一个装满垃圾的抽屉,应用程序依赖各种资源,一些应用程序依赖各种代码库,语音,图像等。这种依赖关系很像分布式各个系统的调用网一样混乱不堪,如果用一张图来表示一下,就像是下面这样但是Docker解决了这个问题,应用程序的各种依赖和环境都直接部署在Docker的容器中,起到隔离一切的目 的,就像是下图所示的这样提高可移植性:另一个软件的问题是,应用程序的依赖不仅只是资源的依赖,还有可能是系统环境的依赖,操作系统之间的移植性一直是软件用户的一个主要问题。虽然Linux和OS X之间可能会有某种兼容性,但是在Windows环境下开发的软件移植到Linux会很困难。Docker解决了这一点,因为Docker可运行在原生的Linux环境下,在OS X和Windows环境中通过单独的虚拟机也可以运行。这种新的移植性在几个方面有助于用户使用:第一,它将软件以前无法使用的地方彻底解锁。第二,它可以在任何系统上运行相同的软件。第三,软件维护人员可以集中精力在单一平台和一套依赖关系中编写他们的软件,这节省了大量的时间。保护你的机器:Docker就像是物理的牢房,容器里的任何东西只能访问它内部的东西。容器限制了一个程序对其他程序带来的影响范围、可访问的数据和系统资源的影响范围。下图说明了容器内部运行和外部运行软件的区别下面就开始你的Docker搭建之旅吧!!!准备你的docker环境我为你准备了下面四种环境的docker安装教程,你可以根据教程来实现安装Ununtu Docker安装:https://www.runoob.com/docker/ubuntu-docker-install.htmlCentOS Docker安装:https://www.runoob.com/docker/centos-docker-install.htmlWindows Docker安装:https://www.runoob.com/docker/windows-docker-install.htmlMacOS Docker安装:https://www.runoob.com/docker/macos-docker-install.html测试Docker 版本安装完成后,运行docker --version 确保你的系统已经支持了docker运行docker info(没有 - )以查看有关Docker安装的更多详细信息:等等测试Docker 安装测试你的安装工作通过运行一个简单的docker 映像,hello-world:列出来你机器上下载的hello-world 映像列出显示其消息后退出的hello-world容器(由图像生成)。如果它仍在运行,则不需要--all选项:回顾和备忘单列出Docker CLI 命令dockerdocker container --help显示docker version 和 infodocker --versiondocker versiondocker info执行docker图像docker run hello-world列出docker 映像docker image ls列出docker容器docker container lsdocker container ls --alldocker container ls -aq </div>
Spring Cloud Zuul 基础搭建
Spring Cloud Zuul API服务网关一、Zuul 介绍二、构建Spring Cloud Zuul网关构建网关请求路由传统路由方式面向服务的路由请求过滤一、Zuul 介绍通过前几篇文章的介绍,我们了解了Spring Cloud Eureka 如何搭建注册中心,Spring Cloud Ribbon 如何做负载均衡,Spring Cloud Hystrix 断路器如何保护我们的服务,以防止雪崩效应的出现,Spring Cloud Feign进行声明式服务调用都有哪些应用,相比Ribbon和Hystrix都有哪些改善。可以说,以上几个组件都是搭建一套微服务架构所必须的。通过以上思路,能够梳理出下面这种基础架构: 无服务网关的架构图在此架构中,我们的服务集群是内部ServiceA 和 ServiceB,他们都会向Eureka Server集群进行注册与订阅服务。而OpenService是一个对外的Restful API 服务,它通过F5,Nginx等网络设备或工具软件实现对各个微服务的路由与负载,公开给外部客户端调用那么上述的架构存在什么问题呢?从运维的角度来看,当客户端单机某个功能的时候往往会发出一些请求到后端,这些请求通过F5,Nginx等设施的路由和负载均衡分配后,被转发到各个不同的实例上,而为了让这些设施能够正确的路由与分发请求,运维人员需要手动维护这些实例列表,当系统规模增大的时候,这些看似简单的维护回变得越来越不可取。从开发的角度来看,为了保证服务的安全性,我们需要在调用内部接口的时候,加一层过滤的功能,比如权限的校验,用户登陆状态的校验等;同时为了防止客户端在请求时被篡改等安全方面的考虑,还会有一些签名机制的存在。正是由于上述架构存在的问题,API网关被提出,API网关更像是一个智能的应用服务器,它的定义类似于设计模式中的外观模式,它就像是一个门面的角色,结婚时候女方亲属堵门时候的角色,我去参加婚礼当伴郎的时候去村子里面见新娘,女方亲属会把鞋子藏起来,有可能藏在屋子里有可能藏在身上,这得需要你自己去寻找,找到了鞋子之后,你才能够给新娘穿上才能正式的会见家长。API网关真正实现的功能有请求路由,负载均衡,校验过滤,请求转发的熔断机制,服务的聚合等一系列功能。 Spring Cloud Zuul通过与Spring Cloud Euerka进行整合,将自身注册为Eureka服务治理下的应用,同时从Eureka中获得了所有的微服务的实例信息。者可以通过使用Zuul来创建各种校验过滤器,然后指定哪些规则的请求需要执行校验逻辑,只有通过校验的才会被路由到具体的微服务接口。下面我们就来搭建一下Spring Cloud Zuul服务网关二、构建Spring Cloud Zuul网关下面我们就来实际搭建一下Zuul网关,来体会一下网关实际的用处构建网关在实现各种API网关服务的高级功能之前,我们先来启动一下前几章搭建好的服务server-provider,feign-consumer,eureka-server,虽然之前我们一直将feign-consumer视为消费者,但是在实际情况下,每个服务既时服务消费者,也是服务提供者,之前我们访问的http://localhost:9001/feign-consumer等一系列接口就是它提供的服务。这里就来介绍一下详细的构建过程创建一个Spring Boot功能,命名为api-gateway,并在Pom.xml文件中引入如下内容<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.api.gateway</groupId>
<artifactId>api-gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>api-gateway</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Brixton.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>对于spring-cloud-starter-zuul 依赖,可以通过查看依赖配置了解到,它不仅包含了Netflix Zuul的核心依赖zuul-core,还包括了下面这些网关的重要依赖spring-cloud-starter-hystrix: 该依赖用在网关服务中实现对微服务转发时候的保护机制,通过线程隔离和断路器,防止因为微服务故障引发的雪崩效应spring-cloud-starter-ribbon: 该依赖用在实现网关服务进行负载均衡和请求重试spring-cloud-starter-actuactor: 该依赖用来提供常规的微服务管理端点。另外,Spring Cloud Zuul 中还特别提供了/routes端点来返回当前的路由规则在ApiGatewayApplication 主入口中添加@EnableZuulProxy注解开启服务网关功能@EnableZuulProxy @SpringBootApplication public class ApiGatewayApplication { public static void main(String[] args) { SpringApplication.run(ApiGatewayApplication.class, args); } }在application.properties 中配置Zuul应用的基础信息,包括应用名,端口号,具体如下spring.application.name=api-gateway server.port=5555请求路由下面,我们通过一个简单的示例来为上面构建的网关增加请求路由的功能,为了演示请求路由的功能,我们先将之前的Eureka服务注册中心和微服务应用都启动起来。观察下面的服务列表,可以看到两个微服务应用已经注册成功了传统路由方式使用Spring Cloud Zuul实现路由功能非常简单,只需要对api-gateway服务增加一些关于路由的配置规则,就能实现传统路由方式zuul.routes.api-a-url.path=/api-a-url/** # 映射具体的url路径 zuul.routes.api-a-url.url=http://localhost:8080/该配置定义了发往API网关服务的请求中,所有符合/api-a-url/** 规则的访问都将被路由转发到 http://localhost:8080 的地址上,也就是说,当我们访问http://localhost:5555/api-a-url/hello 的时候,API网关服务会将该请求路由到http://localhost:8080/hello 提供的微服务接口中。其中,配置属性zuul.routes.api-a-url.path 中的api-a-url部分为路由的名字,可以任意定义,但是一组path和url映射关系的路由名要相同面向服务的路由很显然,传统的配置方式对我们来说并不友好,他同样需要运维人员花费大量的时间维护各个路由path 和url的关系。为了解决这个问题,Spring Cloud Zuul实现了与Spring Cloud Eureka的无缝衔接,我们可以让路由的path不是映射具体的url,而是让它映射到具体的服务,而具体的url则交给Eureka的服务发现机制去自动维护为了实现与Eureka的整合,我们需要在api-gateway的pom.xml中引入spring-cloud-starter-eureka依赖<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency>在api-gateway服务中对应的application.properties文件中加入如下代码zuul.routes.api-a.path=/api-a/** zuul.routes.api-a.serviceId=server-provider zuul.routes.api-b.path=/api-b/** zuul.routes.api-b.serviceId=feign-consumer eureka.client.service-url.defaultZone=http://localhost:1111/eureka/针对我们之前准备的两个微服务应用server-provider和feign-consumer,在上面的配置中分别定义了api-a 和 api-b 的路由来映射它们。然后这个api-gateway的默认注册中心是默认注册中心地址完成上述配置后,我们可以将四个服务启动起来,分别是eureka-server, server-provider, feign-consumer, api-gateway服务,启动完毕,会在eureka-server信息面板中看到多了一个api-gateway网关服务。http://localhost:5555/api-a/hello: 这个接口符合 /api-a/的规则,由api-a 路由负责转发,该路由映射的serviceId 为 server-provider,所以最终/hello请求会被发送到server-provider服务的某个实例上去http://localhost:9001/api-b/feign-consumer: 这个接口符合 /api-b/的规则,由api-b 进行路由转发,实际的地址由Eureka负责映射,该路由的serviceId是feign-consumer, 所以最终 /feign-consumer 请求会被路由到 feign-consumer 服务上。请求过滤在实现了请求路由功能之后,我们的微服务应用提供的接口就可以通过统一的API网关入口被客户端访问到了,但是,每个客户端用户请求微服务应用提供的接口时,它们的访问权限往往都有一定限制。为了实现客户端请求的安全校验和权限控制,最简单和粗暴的方法就是为每个微服务应用都实现一套用于校验签名和鉴别权限的过滤器或拦截器。但是,这样的方法并不可取,因为同一个系统中会有很多校验逻辑相同的情况,最好的方法是将这些校验逻辑剥离出去,构成一个独立的服务。对于上面这种问题,更好的做法是通过前置的网关服务来完成非业务性质的校验。为了在API网关中实现对客户端请求的校验,我们将继续介绍Spring Cloud Zuul的另外一个核心功能:请求过滤,实现方法比较简单,我们只需要继承ZuulFilter抽象类并实现它定义的4个抽象函数即可下面的代码定义了一个简单的Zuul过滤器,它实现了在请求被路由之前检查HttpServletRequest中是否带有accessToken参数public class AccessFilter extends ZuulFilter { private static Logger log = LoggerFactory.getLogger(AccessFilter.class); /** * 过滤器的执行时序 * @return */ @Override public String filterType() { return "pre"; } /** * 过滤器的执行顺序 * @return */ @Override public int filterOrder() { return 0; } /** * 判断过滤器是否应该执行 * @return */ @Override public boolean shouldFilter() { return true; } /** * 过滤器的具体执行逻辑 * @return */ @Override public Object run() { RequestContext rc = RequestContext.getCurrentContext(); HttpServletRequest request = rc.getRequest(); log.info("send {} request to {}", request.getMethod(),request.getRequestURL().toString()); String accessToken = request.getParameter("accessToken"); if(null == accessToken){ log.warn("access token is null"); rc.setResponseStatusCode(401); rc.setSendZuulResponse(false); } log.info("access token ok"); return null; } }在上面实现的过滤器代码中,我们通过继承ZuulFilter 抽象类并重写了四个方法filterType : 过滤器类型,它决定过滤器的请求在哪个生命周期中执行,这里定义为pre,意思是在请求前执行filterOrder : 过滤器的执行顺序,当请求在一个阶段存在多个过滤器时,需要根据方法的返回值来判断过滤器的执行顺序shouldFilter: 过滤器是否需要执行,这里直接返回true,因为该过滤器对所有的请求都生效run: 过滤器的具体逻辑,这里我们通过rc.setResponseStatusCode(401)设置失效的标志,rc.setSendZuulResponse(false)令Zuul过滤该请求在实现了自定义过滤器之后,它并不会直接生效,我们还需要为其创建具体的Bean才能启动该过滤器。@EnableZuulProxy @SpringBootApplication public class ApiGatewayApplication { public static void main(String[] args) { SpringApplication.run(ApiGatewayApplication.class, args); } @Bean public AccessFilter filter(){ return new AccessFilter(); } }在对api-gateway服务完成了上面的改造之后,我们可以重新启动它,并发起下面的请求,对上面的过滤器做一个验证输入 http://localhost:5555/api-a/hello : 返回 401错误输入 http://localhost:5555/api-a/hello?accessToken=token,正确路由到server-provider的/hello 接口,并返回Hello World。到这里,对于API网关的快速入门示例就搭建完成了,通过对Spring Cloud Zuul 网关的搭建,我们能认知到网关的重要性,可以总结如下:它作为系统的统一入口, 屏蔽了系统内部各个微服务的细节。它可以与服务治理框架结合,实现自动化的服务实例维护以及负载均衡的路由转发。它可以实现接口权限校验与微服务业务逻辑的解耦。通过服务网关中的过滤器, 在各生命周期中去校验请求的内容, 将原本在对外服务层做的校验前移, 保证了微服务的无状态性, 同时降低了微服务的测试难度, 让服务本身更集中关注业务逻辑的处理。 </div>
LinkedList 基本示例及源码解析(一)(上)
LinkedList 基本示例及源码解析(一)一、JavaDoc 简介二、LinkedList 继承接口和实现类介绍三、LinkedList 基本方法介绍四、LinkedList 基本方法使用五、LinkedList 内部结构以及基本元素声明六、LinkedList 具体源码分析一、JavaDoc 简介LinkedList双向链表,实现了List的 双向队列接口,实现了所有list可选择性操作,允许存储任何元素(包括null值)所有的操作都可以表现为双向性的,遍历的时候会从首部到尾部进行遍历,直到找到最近的元素位置注意这个实现不是线程安全的, 如果多个线程并发访问链表,并且至少其中的一个线程修改了链表的结构,那么这个链表必须进行外部加锁。(结构化的操作指的是任何添加或者删除至少一个元素的操作,仅仅对已有元素的值进行修改不是结构化的操作)。List list = Collections.synchronizedList(new LinkedList(…)),可以用这种链表做同步访问,但是最好在创建的时间就这样做,避免意外的非同步对链表的访问迭代器返回的iterators 和 listIterator方法会造成fail-fast机制:如果链表在生成迭代器之后被结构化的修改了,除了使用iterator独有的remove方法外,都会抛出并发修改的异常。因此,在面对并发修改的时候,这个迭代器能够快速失败,从而避免非确定性的问题二、LinkedList 继承接口和实现类介绍java.util.LinkedList 继承了 AbstractSequentialList 并实现了List , Deque , Cloneable 接口,以及Serializable 接口public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable {}类之间的继承体系如下:下面就对继承树中的部分节点进行大致介绍:AbstractSequentialList 介绍:这个接口是List一系列子类接口的核心接口,以求最大限度的减少实现此接口的工作量,由顺序访问数据存储(例如链接链表)支持。对于随机访问的数据(像是数组),AbstractList 应该优先被使用这个接口可以说是与AbstractList类相反的,它实现了随机访问方法,提供了get(int index),set(int index,E element), add(int index,E element) and remove(int index)方法对于程序员来说:要实现一个列表,程序员只需要扩展这个类并且提供listIterator 和 size方法即可。对于不可修改的列表来说, 程序员需要实现列表迭代器的 hasNext(), next(), hasPrevious(),previous 和 index 方法AbstractList 介绍:这个接口也是List继承类层次的核心接口,以求最大限度的减少实现此接口的工作量,由顺序访问数据存储(例如链接链表)支持。对于顺序访问的数据(像是链表),AbstractSequentialList 应该优先被使用,如果需要实现不可修改的list,程序员需要扩展这个类,list需要实现get(int) 方法和List.size()方法如果需要实现可修改的list,程序员必须额外重写set(int,Object) set(int,E)方法(否则会抛出UnsupportedOperationException的异常),如果list是可变大小的,程序员必须额外重写add(int,Object) , add(int, E) and remove(int) 方法AbstractCollection 介绍:这个接口是Collection接口的一个核心实现,尽量减少实现此接口所需的工作量为了实现不可修改的collection,程序员应该继承这个类并提供呢iterator和size 方法为了实现可修改的collection,程序团需要额外重写类的add方法,iterator方法返回的Iterator迭代器也必须实现remove方法三、LinkedList 基本方法介绍上面看完了LinkedList 的继承体系之后,来看看LinkedList的基本方法说明上面图片的文字比较小,可能有些不清晰,下面我就来对上面图片做一个大致介绍:四、LinkedList 基本方法使用学以致用,熟悉了上面基本方法之后,来简单做一个demo测试一下上面的方法:/** * 此方法描述 * LinedList 集合的基本使用 */public class LinkedListTest { public static void main(String[] args) { LinkedList<String> list = new LinkedList<>(); list.add("111"); list.add("222"); list.add("333"); list.add(1,"123"); // 分别在头部和尾部添加元素 list.addFirst("top"); list.addLast("bottom"); System.out.println(list); // 数组克隆 Object listClone = list.clone(); System.out.println(listClone); // 创建一个首尾互换的迭代器 Iterator<String> it = list.descendingIterator(); while (it.hasNext()){ System.out.print(it.next() + " "); } System.out.println(); list.clear(); System.out.println("list.contains('111') ? " + list.contains("111")); Collection<String> collec = Arrays.asList("123","213","321"); list.addAll(collec); System.out.println(list); System.out.println("list.element = " + list.element()); System.out.println("list.get(2) = " + list.get(2)); System.out.println("list.getFirst() = " + list.getFirst()); System.out.println("list.getLast() = " + list.getLast()); // 检索指定元素出现的位置 System.out.println("list.indexOf(213) = " + list.indexOf("213")); list.add("123"); System.out.println("list.lastIndexOf(123) = " + list.lastIndexOf("123")); // 在首部和尾部添加元素 list.offerFirst("first"); list.offerLast("999"); System.out.println("list = " + list); list.offer("last"); // 只访问,不移除指定元素 System.out.println("list.peek() = " + list.peek()); System.out.println("list.peekFirst() = " + list.peekFirst()); System.out.println("list.peekLast() = " + list.peekLast()); // 访问并移除元素 System.out.println("list.poll() = " + list.poll()); System.out.println("list.pollFirst() = " + list.pollFirst()); System.out.println("list.pollLast() = " + list.pollLast()); System.out.println("list = " + list); // 从首部弹出元素 list.pop(); // 压入元素 list.push("123"); System.out.println("list.size() = " + list.size()); System.out.println("list = " + list); // remove操作 System.out.println(list.remove()); System.out.println(list.remove(1)); System.out.println(list.remove("999")); System.out.println(list.removeFirst()); System.out.println("list = " + list); list.addAll(collec); list.addFirst("123"); list.addLast("123"); System.out.println("list = " + list); list.removeFirstOccurrence("123"); list.removeLastOccurrence("123"); list.removeLast(); System.out.println("list = " + list); list.addFirst("top"); list.addLast("bottom"); list.set(2,"321"); System.out.println("list = " + list); System.out.println("--------------------------"); // 创建一个list的双向链表 ListIterator<String> listIterator = list.listIterator(); while(listIterator.hasNext()){ // 移到list的末端 System.out.println(listIterator.next()); } System.out.println("--------------------------"); while (listIterator.hasPrevious()){ // 移到list的首端 System.out.println(listIterator.previous()); } }}输出:-------1------- [top, 111, 123, 222, 333, bottom]-------2-------[top, 111, 123, 222, 333, bottom]bottom 333 222 123 111 top list.contains('111') ? false[123, 213, 321]list.element = 123list.get(2) = 321list.getFirst() = 123list.getLast() = 321list.indexOf(213) = 1list.lastIndexOf(123) = 3-------4------- [first, 123, 213, 321, 123, 999]list.peek() = firstlist.peekFirst() = firstlist.peekLast() = lastlist.poll() = firstlist.pollFirst() = 123list.pollLast() = last-------5------- [213, 321, 123, 999]list.size() = 4-------6------- [123, 321, 123, 999]123123true321-------7------- []-------8------- [123, 123, 213, 321, 123]list = [123, 213]-------9------- [top, 123, 321, bottom]--------------------------top123321bottom--------------------------bottom321123top </div>
@SafeVarargs 使用说明
说明:@SafeVarargs 是jdk1.7引入的适用于可变参数与泛型能够更好结合的一个注解。@SuppressWarnings 产生编译期警告的示例代码官方解释:程序员认定带有注释的主体或者构造函数不会对其执行潜在的不安全操作将此注释应用于未经检查的方法或者构造器在"不可具体化"的和未经检查的参数类型警告关于所有数组参数创建的时候除了强加使用@Target 元注解的限制之外,编译器还被用在注解类型上来实现额外的限制下面几种情况会在使用@SafeVarags 注解的时候产生编译时错误:在声明一个固定参数的方法或者构造函数的时候也就是说如果你认为你的方法或者构造方法是类型安全的,那么你也就可以使用@SafeVarargs 来跳过@SuppressWarnings("unchecked")检查。示例:publicclassSafeVarargs{
// 这其实不是一个安全的类型检查
@SafeVarargs
static void m(List<String>...lists){
// 先会存储到 array[0] 的位置
Object[] array=lists;
List<Integer>tmpList=Arrays.asList(42);
// array[0] 又保存了tmpList(Integer)进行覆盖
// tmpList是一个List对象(类型已经擦除),赋值给Object类型的对象是允许的(向上转型),
// 能够编译通过
array[0] =tmpList;
// 实际取出来的应该是 42
Strings=lists[0].get(0);
}
public static void main(String[] args) {
List<String>list1=Arrays.asList("one","two");
m(list1);
}
}Exceptioninthread"main"java.lang.ClassCastException: java.lang.Integercannotbecasttojava.lang.Stringatjava7.SafeVarargs.m(SafeVarargs.java:14)atjava7.SafeVarargs.main(SafeVarargs.java:21)Processfinishedwithexitcode1具体分析:List<String>list1=Arrays.asList("one","two");当程序执行到这一步,会创建一个list1 ,其内部存在两个固定的值 [one,two],调用m(list1)方法m(list1);接着Object[] array=lists;程序执行到这一步,会在array数组中的第0个位置上存储一个list1 对象List<Integer>tmpList=Arrays.asList(42);创建一个Integer的列表,存储一个integer类型的元素42array[0] =tmpList;重新给array[0] 进行赋值,把原来位置的lists 进行覆盖,存储新的元素tmpListStrings=lists[0].get(0);我们预期的结果应该是取的值是42,但是实际上却报出了ClassCastException 因为最后array[0] 中的值 42Integer 类型, 无法直接用String 对象进行接受,所以会报错。 </div>
Arrays.asList 解析
说明asList 是 java.util.Arrays 类的一个方法public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}官方的解释: 返回由指定数组支持的固定大小的列表,这个方法是array 和 collectionn API 之间的一个桥梁,它所返回的List 是序列化之后的,并且实现了 RandomAccess 接口就是一个能够快捷指定固定大小的列表,并进行初始化指定的元素注意:这个List 返回的不是 java.util.ArrayList,而是 java.util.Arrays的一个内部类ArrayList(E[] array) { a = Objects.requireNonNull(array);}参数接受的是一个数组,使用java.util.Objects 对象的requireNonNull 来判断是否为空如果为空则直接抛出空指针异常,此种判空方式常用于构造函数的参数判断基本用法List<String> list = Arrays.asList("Apple"); asList 是一个静态方法,能够直接被调用,如上所示,只创建了一个叫apple的元素List<String> list = Arrays.asList("Apple","Orange");也可以创建多个指定元素的列表通过以上两种方式,我们能够知道,asList方法能够创建多个指定元素的列表public class ArraysTest{ public static void main(String[] args) { String[] array={"Apple","Banana","Orange"}; List<String> myList = Arrays.asList(array); for(String list: myList){ System.out.println(list); } }}----------------------------------------------------------------------输出:------------------------------------------------------------------------------AppleBananaOrange陷阱陷阱一:虽然说对于Arrays.asList 能够方便快捷的创建一个列表,但是世界上没有完美的技术,这个方法也不例外,同样会产生一些缺陷和漏洞:public class ArraysTest{ public static void main(String[] args) { int[] array={1,2,3}; List myList=Arrays.asList(array); System.out.println(myList.size()); }}----------------------------------------------------------------------输出:------------------------------------------------------------------------------1这时你会很好奇,我明明创建了三个元素,为什么输出的长度是 1 呢?因为asList需要接收的参数是一个原始数组,所以上述代码对它创建了一个名为"array"的列表,只有一个元素所以长度为1System.out.println(myList.get(0))输出发现上面得到的并不是1 2 3 中任意的一个值,而是一个hashcode ,这就说明这个list 唯一的元素是一个array对象陷阱二:假如我创建了一个指定元素的list,那么我能否对这个list进行 元素添加等操作呢?public class ArraysTest{ public static void main(String[] args) { String[] myArray = {"Apple","Banana","Orange"}; List<String> myList = Arrays.asList(myArray); myList.add("Pear"); }}运行后程序报错,Exceptioninthread"main"java.lang.UnsupportedOperationExceptionatjava.util.AbstractList.add(AbstractList.java:148)atjava.util.AbstractList.add(AbstractList.java:108)atArrays.ArraysTest.main(ArraysTest.java:11)说是不支持操作类型异常,Arrays.asList()创建完列表之后,你就不能够再修改元素改观改观一:把私有的数组转换为List使用了 JDK 1.8的lambda 表达式和流public class ArraysTest{ public static void main(String[] args) { int[] intArray={5, 10, 21}; List<Integer> myList = Arrays.stream(intArray).boxed() .collect(Collectors.toList()); }}改观二: 把数组转换成List 来接受更多的元素如上面所述,Arrays.asList()的结果不支持添加或删除项,如果你不能接受这种行为,可以换一种方式public class ArraysTest{ public static void main(String[] args) { String[] myArray = {"Apple", "Banana", "Orange"}; List<String> myList = new ArrayList<>(Arrays.asList(myArray)); myList.add("Guava"); }}可以尝试新new 一个ArrayList 来接受 Arrays.asList产生的结果改观三: 使用自己的实现将数组转换为列表下面是将Array转换为List的简单实现public class ArraysTest{ public static void main(String[] args) { String[] myArray = {"Apple", "Banana", "Orange"}; List<String> myList = newArrayList<>(); for(String str: myArray){ myList.add(str); } System.out.println(myList.size()); }} </div>
千人互动,18 位业界大咖,2022 开放原子全球开源峰会龙蜥专区总结来了
7 月 27-29 日,由开放原子开源基金会、北京市经济和信息化局、北京经济技术开发区管理委员会联合承办的“2022 开放原子全球开源峰会”在北京圆满落幕,峰会聚集了两院院士、顶级开源基金会/开源社区负责人、开源意见领袖、开源厂商代表、权威主流媒体、优秀开源企业用户等共计 3300 多位。期间,龙蜥社区携技术委员会、运营委员会、新一届理事成员以及 18 位业界大咖重磅亮相,吸引了近 1400 位参会者参与互动。更有阿里巴巴集团 CTO 程立为龙蜥操作系统站台,表达了阿里持续投入建设龙蜥的决心。7 月 27 日龙蜥论坛聚焦社区治理、生态和下一代技术,进行了龙蜥新一届理事成员授牌仪式,分享了社区合作伙伴和用户基于龙蜥操作系统的应用实践,CentOS 迁移方案指导等技术干货,更有共商“如何协同统一生态”和“论中国操作系统实力”的两大话题满满的圆桌会议。(图/龙蜥论坛两大圆桌)(图/2022 龙蜥社区新一届理事成员授牌仪式)论坛上,龙蜥社区理事长、阿里云操作系统负责人马涛发表了《同走龙蜥特色路,共创开源新未来》的主题演讲,表示“我们认为整个龙蜥社区的发展规划和路径核心要建设以云计算为终态的开源软件创新生态,秉承开放、平等、协作、创新四个准则践行龙蜥社区使命,共创数字化发展和共建开源新基建,将龙蜥社区打造成一个数字基础设施互联互通的基石,赋能中国千行百业能够解决好面临的云计算挑战,利用好云计算所带来的红利,最终带领企业进入到一个云计算新时代。”过去一年龙蜥社区一路狂奔,马涛总结到,“龙蜥社区的成长有三个非常大的进展,分别是重量理事加盟:普华基础软件、Arm、中电科申泰的加入使得龙蜥社区在基础软硬件协同生态上更上一层楼;突破百万量级:安装量 130 万,下载量 100 万,实现更大范围更多类型的用户覆盖;社区规模扩建:当前龙腾计划吸纳超过 230 家生态合作伙伴加入,社区成立 40 多个 SIG 组,围绕芯片、内核、编译器、安全、虚拟化及云原生等操作系统核心领域进行技术创新,汇聚了 2000 多名开发者、30 万名社区用户。”(图/龙蜥社区理事长、阿里云操作系统负责人马涛)龙蜥社区副理事长、统信软件 CTO 张磊表示,“统信软件坚定携手龙蜥社区,在继承了自由开放的开源精神基础之上,结合当前操作系统软硬件产业链的现状,提出分层分类演进的开源技术路线,为下一代开源操作系统的开发奠定坚实的理论与技术基础。”(图/龙蜥社区副理事长、统信软件 CTO 张磊)期间,开放原子开源基金会秘书长孙文龙发表致辞,“龙蜥社区已经发展成为国内极具创新力的开源操作系统社区,具备较为领先的产业和技术影响力。欢迎龙蜥以及更多的项目捐献给开放原子开源基金会,一起构建操作系统的开源开放新生态。希望未来龙蜥社区的发展更加开放,成为全球开源生态的重要力量。”(图/开放原子开源基金会秘书长孙文龙)中国开源软件推进联盟副主席兼秘书长,中国科学院软件所研究员刘澎表示,“龙蜥操作系统像前轮驱动的汽车一样,是通过需求迁移在拉动着操作系统的运动,跟用户的实际应用密切相关。我们可以预见到,未来龙蜥操作系统将是中国最重要的操作系统之一。”(图/中国开源软件推进联盟副主席兼秘书长、中国科学院软件所研究员刘澎)7 月 28 日峰会开幕式主论坛上,阿里巴巴集团 CTO 程立说,“我们希望龙蜥这个云原生操作系统能够成为大家共同建设、共同享有的软件基础设施。未来,借助云+开源,我们可以孵化出一个自主演进可以带动产业发展的操作系统生态,推动产业生态完善,繁荣整个开源生态。”(图/阿里巴巴集团CTO程立)与此同时,龙蜥社区召开了“第十次运营委员会”、“7 月份技术委员会”和“第二届理事大会”,呈现出一个运营有序、技术严谨、极速抽穗拔节般成长的社区治理样貌。 龙蜥社区第十次运营委员会议顺利召开 7 月 25 日,龙蜥社区在北京中关村召开了 第十次运营委员会会议。本次会议由运营委员会副主席金美琴主持。来自 Arm、阿里云、红旗软件、Intel、联通、天翼云、龙芯、飞腾、普华基础软件、统信软件、万里红、中科方德等理事单位的 25 位运营代表参会。会上,经过全体委员表决并全票通过,阿里云技术战略总监陈绪继续担任龙蜥社区运营委员会主席。(图/现场参会委员合照) 龙蜥社区 7 月份技术委员会议顺利召开 7 月 25 日,龙蜥社区召开了 7 月技术委员会,技术委员会议由阿里云刘寅主持,首先开场对 31 位与会技术委员及嘉宾进行介绍,他们来自阿里云、统信、飞腾、中科方德、红旗、万里红、intel、Arm、龙芯、兆芯、海光、联通、天翼云、普华、浪潮、申泰等多家单位参会,此次邀请到 Linux 中国开源社区创始人王兴宇作为特邀嘉宾出席会议。(图/现场参会委员合照) 龙蜥社区第二届理事大会圆满召开 7 月 26 日,龙蜥社区于北京顺利召开第二届理事大会,来自阿里云、统信软件、联通、电信云、移动云、飞腾、龙芯、兆芯、Intel、Arm 等 17 家理事单位的理事代表出席。(图/龙蜥社区新一届全体理事合影)龙蜥社区首次邀请 4 位行业资深专家参会,并聘请他们成为「特约顾问」。他们的加入,让龙蜥社区汇聚了产学研等各方力量,融合了不同视角的思考和合作。他们分别是:中国开源软件推进联盟副主席兼秘书长/中国科学院软件所研究员刘澎、CSDN 创始人兼董事长蒋涛、极客邦科技创始人兼 CEO 霍太稳、Linux 中国创始人王兴宇。(图/龙蜥社区新聘任特约顾问依次为刘澎、王兴宇、霍太稳、蒋涛)龙蜥社区特约顾问、CSDN 创始人兼董事长蒋涛表示,“龙蜥社区合作生态建立以来,吸引来了诸多合作伙伴,一起推进龙蜥在市场上的落地。未来,作为龙蜥的特约顾问,我们将在开发者的培养、技术生产等方面深入合作,助力龙蜥操作系统的产业化和市场化。”—— 完 ——加入龙蜥社群加入微信群:添加社区助理-龙蜥社区小龙(微信:openanolis_assis),备注【龙蜥】与你同在;加入钉钉群:扫描下方钉钉群二维码。欢迎开发者/用户加入龙蜥社区(OpenAnolis)交流,共同推进龙蜥社区的发展,一起打造一个活跃的、健康的开源操作系统生态!关于龙蜥社区龙蜥社区(OpenAnolis)是由企业单位、事业单位、社会团体、个人等在共建、共治、共享的基础上组成的非营利性开源社区。龙蜥社区成立于 2020 年 9 月,旨在构建一个开放、平等、协作、创新的 Linux 上游发行版社区及创新平台。龙蜥社区成立的短期目标是开发龙蜥操作系统(Anolis OS)作为 CentOS 停服后的应对方案,构建一个兼容国际 Linux 主流厂商的社区发行版。中长期目标是探索打造一个面向未来的操作系统,建立统一的开源操作系统生态,孵化创新开源项目,繁荣开源生态。目前,Anolis OS 8.6 已发布,更多龙蜥自研特性,支持 X86_64 、RISC-V、Arm64、LoongArch 架构,完善适配 Intel、兆芯、鲲鹏、龙芯等芯片,并提供全栈国密支持。欢迎下载:https://openanolis.cn/download加入我们,一起打造面向未来的开源操作系统!https://openanolis.cn
让人惊艳的高级配色,让你的色彩灵感爆棚!
项目背景无意中在刷B站的时候,看到一些很温馨的颜色搭配,让人充满灵感。以前就很喜欢使用纯色背景,经常会用纯色背景图片作为壁纸。那么本章中,我们就尝试使用SwiftUI搭建一个纯色背景App。项目搭建首先,创建一个新的SwiftUI项目,命名为SolidColor。模型搭建首先,我们先创建一个数据模型,我们命名为Model。struct Model:Identifiable {
var id:UUID
var color:Color
var colorName:String
var colorRGBName:String
}
复制代码上述代码中,我们创建了一个Model结构体,遵循Identifiable协议。Identifiable协议可以帮助我们定位到数据源的id,当我们的示例数据存在2条或者多条一模一样的数据时,会展示多条而不是合并为一条。然后我们声明了几个变量:id作为唯一标识符,color为背景颜色,colorName为颜色名称,colorRGBName为对应颜色的RGB颜色值名称。完成后,我们来创建一些示例数据,示例:var models = [
Model(colorName: "草莓红", color: Color(red: 228/255, green: 45/255, blue: 68/255), colorRGBName: "#E42D44"),
Model(colorName: "奶油白", color: Color(red: 250/255, green: 239/255, blue: 222/255), colorRGBName: "#FAEFDE"),
Model(colorName: "泥猴桃绿", color: Color(red: 123/255, green: 173/255, blue: 95/255), colorRGBName: "#7BAD5F"),
Model(colorName: "小麦黄", color: Color(red: 229/255, green: 215/255, blue: 173/255), colorRGBName: "#E5D7AD"),
Model(colorName: "板栗灰", color: Color(red: 97/255, green: 79/255, blue: 77/255), colorRGBName: "#614F4D"),
Model(colorName: "柠檬黄", color: Color(red: 255/255, green: 216/255, blue: 0/255), colorRGBName: "#FFD800"),
Model(colorName: "葡萄紫", color: Color(red: 91/255, green: 52/255, blue: 99/255), colorRGBName: "#5B3663"),
Model(colorName: "青豆绿", color: Color(red: 188/255, green: 207/255, blue: 144/255), colorRGBName: "#BCCF90"),
Model(colorName: "水蜜桃粉", color: Color(red: 246/255, green: 160/255, blue: 154/255), colorRGBName: "#F6A09A")
]
复制代码就此,数据部分我们就准备好了。页面样式我们再来分析下交互逻辑,首先我们要实现左右拖动切换页面,这个我们可以使用到TabView组件作为我们的核心视图,示例:TabView {
//代码块
}
.tabViewStyle(PageTabViewStyle())
.edgesIgnoringSafeArea(.all)
复制代码由于要实现滚动,那么在TabView视图的基础上,还需要使用tabViewStyle修饰符进行修饰,我们使用PageTabViewStyle样式。另外为了实现整个屏幕都是纯色背景效果,我们使用edgesIgnoringSafeArea修饰符,将iPhone安全区域去掉。内容结构部分,我们可以使用ForEach循环遍历我们的示例数据数组models的数据,示例:TabView {
ForEach(models) { item in
//代码块
}
}
.tabViewStyle(PageTabViewStyle())
.edgesIgnoringSafeArea(.all)
复制代码内容显示部分,我们的背景颜色和颜色名称、颜色RGB值的排布方式是文字浮在背景上,因此这里我们可以使用ZStack视图,示例:TabView {
ForEach(models) { item in
ZStack {
item.color.edgesIgnoringSafeArea(.all)
//文字部分
}
}
}
.tabViewStyle(PageTabViewStyle())
.edgesIgnoringSafeArea(.all)
复制代码上述代码中,我们填充了一个背景颜色,我们看看效果。最后是文字部分,我们使用VStack纵向布局的方式排布颜色名称和RGB颜色值。示例:TabView {
ForEach(models) { item in
ZStack {
item.color.edgesIgnoringSafeArea(.all)
VStack(alignment: .center, spacing: 20) {
Text(item.colorName)
.font(.system(size: 24))
.fontWeight(.bold)
.foregroundColor(.white)
Text(item.colorRGBName)
.font(.system(size: 17))
.fontWeight(.bold)
.foregroundColor(.white)
}
}
}
}
.tabViewStyle(PageTabViewStyle())
.edgesIgnoringSafeArea(.all)
复制代码项目展示本章代码import SwiftUI
struct ContentView: View {
var body: some View {
TabView {
ForEach(models) { item in
ZStack {
item.color.edgesIgnoringSafeArea(.all)
VStack(alignment: .center, spacing: 20) {
Text(item.colorName)
.font(.system(size: 28))
.fontWeight(.bold)
.foregroundColor(.white)
Text(item.colorRGBName)
.font(.system(size: 17))
.fontWeight(.bold)
.foregroundColor(.white)
}
}
}
}
.tabViewStyle(PageTabViewStyle())
.edgesIgnoringSafeArea(.all)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct Model: Identifiable {
var id = UUID()
var colorName: String
var color: Color
var colorRGBName: String
}
var models = [
Model(colorName: "草莓红", color: Color(red: 228 / 255, green: 45 / 255, blue: 68 / 255), colorRGBName: "#E42D44"),
Model(colorName: "奶油白", color: Color(red: 250 / 255, green: 239 / 255, blue: 222 / 255), colorRGBName: "#FAEFDE"),
Model(colorName: "泥猴桃绿", color: Color(red: 123 / 255, green: 173 / 255, blue: 95 / 255), colorRGBName: "#7BAD5F"),
Model(colorName: "小麦黄", color: Color(red: 229 / 255, green: 215 / 255, blue: 173 / 255), colorRGBName: "#E5D7AD"),
Model(colorName: "板栗灰", color: Color(red: 97 / 255, green: 79 / 255, blue: 77 / 255), colorRGBName: "#614F4D"),
Model(colorName: "柠檬黄", color: Color(red: 255 / 255, green: 216 / 255, blue: 0 / 255), colorRGBName: "#FFD800"),
Model(colorName: "葡萄紫", color: Color(red: 91 / 255, green: 52 / 255, blue: 99 / 255), colorRGBName: "#5B3663"),
Model(colorName: "青豆绿", color: Color(red: 188 / 255, green: 207 / 255, blue: 144 / 255), colorRGBName: "#BCCF90"),
Model(colorName: "水蜜桃粉", color: Color(red: 246 / 255, green: 160 / 255, blue: 154 / 255), colorRGBName: "#F6A09A"),
]
复制代码恭喜你,完成了整个项目的全部内容!快来动手试试吧。
SwiftUI100天:使用SwiftUI搭建一个每日一句App
前言为了更加熟悉和了解SwiftUI,本系列将从实战角度出发完成100个SwiftUI项目,方便大家更好地学习和掌握SwiftUI。这同时也是对自己学习SwiftUI过程的知识整理。如有错误,以你为准。项目搭建首先,创建一个新的SwiftUI项目,命名为Sentence。逻辑分析每日一句的逻辑和随机数的逻辑类似,我们预设一堆的句子,然后从句子数组中随机抽出一个句子并展示。为了让App更加饱满,我们也可以提供刷新按钮,若呈现的句子用户感触不深,则可以刷新重新请求一次。页面样式了解完每日一句的逻辑之后,我们来完成页面样式的设计。背景颜色首先是页面的背景颜色,我们可以使用ZStack填充一个背景颜色,并用edgesIgnoringSafeArea修饰符将整个颜色拉伸,覆盖包含安全区域的所有位置。示例:ZStack {
Color(red: 67 / 255, green: 71 / 255, blue: 224 / 255)
.edgesIgnoringSafeArea(.all)
}
复制代码App标题App标题,我们使用Text文本作为标题样式,示例:// 标题
func titleView() -> some View {
HStack {
Text("每日一句")
.font(.title)
.fontWeight(.bold)
.foregroundColor(.white)
Spacer()
}
}
复制代码句子展示句子展示部分,我们先使用一个句子作为示例,我们使用Text构建句子样式,我们先声明一个变量存储句子,示例:@State var sentenceText:String = "外表干净是尊重别人,内心干净是尊重自己,言行干净是尊重灵魂。"
复制代码然后我们构建句子展示的样式,示例:// 句子展示
func sentenceView() -> some View {
Text(sentenceText)
.padding()
.foregroundColor(Color(.systemGray))
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 280)
.background(.white)
.cornerRadius(8)
}
复制代码上述代码中,我们使用Text构建了一个句子展示区域,我们使用了默认的文字,后面会换成替换的文字。我们设置了句子的颜色、背景颜色、背景尺寸和圆角,整体样式看起来还不错。刷新按钮另外,我们还需要创建一个刷新按钮,供用户获取新的句子。示例:// 刷新按钮
func refreshBtn() -> some View {
Image(systemName: "repeat.circle.fill")
.font(.system(size: 32))
.foregroundColor(.white)
.padding()
}
复制代码上述代码中,我们使用Image和Apple提供的系统图标做了一个按钮,同样我们调整了它的大小和填充颜色。整体样式布局整体样式布局,我们在body中将已经构建好的元素排布组合,示例:var body: some View {
ZStack {
Color(red: 67 / 255, green: 71 / 255, blue: 224 / 255)
.edgesIgnoringSafeArea(.all)
VStack {
titleView()
Spacer()
sentenceView()
Spacer()
refreshBtn()
}.padding()
}
}
复制代码这样,样式部分我们就完成了。数据部分数据部分,我们可以使用本地的示例数据,也可以使用网络请求返回Json数据,这里做简单点,我们使用本地创建的数组,示例:private var models = [
"你的能力是否能在全世界通用,如果不能,那么需要重新评估你的能力。",
"将自身所学回馈社会,不也是一件幸福的事么。",
"当你作为演讲者时,你要装作自己是最了不起的一个人。而当你作为倾听者时,也请一定要装作自己什么也不懂。",
"当需要你提出建议时,就应该要畅所欲言,不要将想法锁在自己脑子里。",
"一个人若没有深厚的知识积累,就无法轻易说出自己到底喜欢什么。",
"通过沉浸思考,不断积累,就能逐步踏实地将一些知识转变为自己的东西。",
"外表干净是尊重别人,内心干净是尊重自己,言行干净是尊重灵魂。",
"人的精神思想方面的优势越大,给无聊留下的空间就越小。"
]
复制代码上述代码中,我们声明了一个数组models,并创建了一些示例的数据。然后创建一个随机取数的方法来获得随机句子,示例:// 随机展示句子
func getRandomSentence() {
let index = Int(arc4random() % UInt32(models.count))
sentenceText = models[index]
}
复制代码最后,在点击刷新按钮时调用上面的方法,示例:// 刷新按钮
func refreshBtn() -> some View {
Image(systemName: "repeat.circle.fill")
.font(.system(size: 32))
.foregroundColor(.white)
.padding()
.onTapGesture {
getRandomSentence()
}
}
复制代码完成后,我们预览下项目成果。项目预览本章完整代码import SwiftUI
struct ContentView: View {
@State var sentenceText: String = "外表干净是尊重别人,内心干净是尊重自己,言行干净是尊重灵魂。"
private var models = [
"你的能力是否能在全世界通用,如果不能,那么需要重新评估你的能力。",
"将自身所学回馈社会,不也是一件幸福的事么。",
"当你作为演讲者时,你要装作自己是最了不起的一个人。而当你作为倾听者时,也请一定要装作自己什么也不懂。",
"当需要你提出建议时,就应该要畅所欲言,不要将想法锁在自己脑子里。",
"一个人若没有深厚的知识积累,就无法轻易说出自己到底喜欢什么。",
"通过沉浸思考,不断积累,就能逐步踏实地将一些知识转变为自己的东西。",
"外表干净是尊重别人,内心干净是尊重自己,言行干净是尊重灵魂。",
"人的精神思想方面的优势越大,给无聊留下的空间就越小。",
]
var body: some View {
ZStack {
Color(red: 67 / 255, green: 71 / 255, blue: 224 / 255)
.edgesIgnoringSafeArea(.all)
VStack {
titleView()
Spacer()
sentenceView()
Spacer()
refreshBtn()
}.padding()
}
}
// 标题
func titleView() -> some View {
HStack {
Text("每日一句")
.font(.title)
.fontWeight(.bold)
.foregroundColor(.white)
Spacer()
}
}
// 句子展示
func sentenceView() -> some View {
Text(sentenceText)
.padding()
.foregroundColor(Color(.systemGray))
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 280)
.background(.white)
.cornerRadius(8)
}
// 刷新按钮
func refreshBtn() -> some View {
Image(systemName: "repeat.circle.fill")
.font(.system(size: 32))
.foregroundColor(.white)
.padding()
.onTapGesture {
getRandomSentence()
}
}
// 随机展示句子
func getRandomSentence() {
let index = Int(arc4random() % UInt32(models.count))
sentenceText = models[index]
}
}
复制代码不错不错!
AI赋能视频直播,如何提升系统安全性?
作者丨Suvigya Saxena译者| 崔皓审校丨Noe你可能在生活中的很多场景都被动地使用到了人工智能 (AI),即使你对此毫无察觉。例如,很多社交媒体、搜索引擎, 都使用AI来确保用户在平台上获得无缝体验,无论是自动标记照片中的朋友还是根据历史搜索提供搜索结果,都是AI在起作用。 这些人工智能的用途相对比较简单,只涉及到部分技术——机器学习(ML)。从根本上说,机器学习正变得越来越流行,那么深度学习(DL)和狭义的人工智能呢?它们如何创造出流媒体服务的新体验就是我们今天要聊的话题。人工智能vs机器学习vs深度学习 人工智能是近年来备受关注的领域。这一广阔领域涵盖了各种主题。人工智能的总体思路是计算机执行以往需要人类智能的任务,比如视觉感知和语言处理。 机器学习就是当今人工智能解决方案最常见的应用之一。它涉及到使用大量数据训练算法并应用于新数据。例如,机器学习算法用于面部识别、垃圾邮件过滤和语言翻译等工作。 机器学习是人工智能的一个子集,即使在没有明确编程的情况下,它也能让计算机从数据中学习。机器学习是人工智能的一个分支,它专注于开发计算机程序,并让这些程序在接触到新数据的时候进行学习。通过这种方式能够在没有人工干预的情况下让计算机自行做出决定。 因此,机器学习是许多服务和产品的基础,包括搜索引擎和社交媒体平台。许多金融机构使用机器学习来监控客户账户活动是否存在欺诈或其他违规行为。人工智能解决方案为用户提供个性化的视频流 虽然人工智能技术已经被应用多年,但最近,由于大型科技公司和小型初创公司的一些发展,它又成为人们关注的焦点。一种备受关注的应用就是—个性化。 对于外行来说,人工智能是执行与人类智能相关工作的计算机程序。该术语涵盖了广泛的应用,包括语音识别和内容过滤。AI有时也被用作机器学习或深度学习的同义词。人工智能可以完成的任务包括图像识别和语言处理—分别识别照片中的对象和将文本从一种语言翻译成另一种语言。 人工智能炒作周期已经持续了几十年。但今天的技术终于赶上了炒作,这在很大程度上要归功于机器学习算法的进步——语音识别、自然语言处理、自动驾驶汽车和其他人工智能应用等技术背后的驱动力。 为什么直播需要人工智能 直播已经成为交流和娱乐的强大工具。它似乎是继电子邮件、短信、微信之后的“新沟通方式”。全球观看直播的人数正在快速增长,人工智能将在未来直播行业的发展中发挥至关重要的作用。 我们很多人都喜欢观看体育赛事、音乐会、颁奖典礼等形式多样的直播。这种直播之所以吸引我们,是因为它提供了比其他媒体更多的实时信息。此外,表演者或球员总是通过直播给我们带来令人印象深刻的惊喜。 另一方面,如今人工智能技术也在飞速发展。尤其是人工智能算法在市场营销、金融、教育、医疗等诸多领域起着重要的作用。而且,人工智能已经应用到汽车、导弹、无人机上,它为无人控制的场景提供独立决策,并成为该场景应用不可或缺的重要组成部分。 该过程涉及使用实时视频,而不是预先录制的视频或图像。直播与其他视频共享服务的不同之处在于,录制是一次性完成的。你根本不需要编辑它,你记录的就是你得到的。可以使用AI让我的直播更有效吗? 答案是肯定的。以下是一些方法: 1、人工智能可以提供实时分析以获得更好的性能——人工智能可以帮助提供观众响应直播流的数据。这可以帮助改善内容和整体直播性能。 2、借助人工智能,内容发现变得更容易——如果你使用社交媒体网站进行宣传,人工智能可以帮助你找到发布内容的最佳时间,以便更多用户能够看到它。 3、内容索引可用于改善用户体验—TikTok的母公司字节跳动找到了一种将人工智能与人类策展(人为的策划展示)相结合的方法,以增强视频内容编目,从而为用户提供更好的体验。TikTok让用户可以创建短视频,可以与朋友分享或发布在其他社交媒体平台上,在年轻人中很受欢迎。为了跟上对新视频的需求,字节跳动开发了一个系统,利用人工智能从用户偏好中学习,并为他们提供相关的内容建议。但是,仅凭这项技术无法满足不断增长的TikTok用户社区的需求。人工智能解决方案:如何保障用户隐私 人工智能无处不在,在最先进的技术中,如机器人、自动化等。所有这些都包含一个人工智能系统,以提高用户的安全性。 人工智能是设备(例如电话或电视)的安全来源。它对命令提供了更好的响应,并且可以更好地控制设备。此外,它能够从经验中学习并提升自己。并且这些功能已在某些软件中实现,例如Siri。我们可以通过向Siri发送语音命令与设备自然地进行交流,Siri会在接受命令的几秒钟内执行所需的操作。 在日常生活的方方面面存在更多人工智能的例子,人工智能的应用提高了产品的安全性和效率。它可以通过分析情况从而做出相应的决策。此外,它可以从错误中吸取教训来改进自己,保证下次每次执行会变得更好。 安全已成为人们关注的重要问题。完全避免黑客攻击显然是不可能的,但找到问题的解决方案同时也具有挑战性。有几种方法可以能够保证系统的安全。其中一种就是使用人工智能,通过软件和硬件配合完成。用于保护软件的AI 软件使用场景下,人工智能解决方案将充当系统的守护者并防止任何未经授权的访问。每次用户尝试访问系统时,人工智能软件都会按照学习模式运行。它将从过去的经验中吸取教训并进行自我修改,因此任何人都无法闯入该系统。在基于硬件的人工智能场景下—需要一个外部设备,每当有人输入错误的密码或命令时。该设备将通知并拒绝任何人的访问,直到你允许他们获得访问权限。 安全系统进入了一个新时代。在人工智能的帮助下,用户的安全和隐私得到了改善。无论是企业用户还是个人用户;基于人工智能的安全系统都是最佳选择。那么是什么让它们与传统系统有所不同呢? 传统安全系统: 依赖于签名、模式匹配、黑名单和其他已知的恶意软件技术。不幸的是,这些技术在检测未知恶意软件攻击方面效果不是很明显。 基于人工智能的安全系统: 基于人工智能的安全系统依赖于可以检测未知攻击的复杂机器学习模型。它们不依赖于黑名单,因为它们基于模式。本文转载自51CTO,本文一切观点和机器智能技术圈子无关。原文链接免费体验百种AI能力以及试用热门离线SDK:【点此跳转】