首页 文章 精选 留言 我的

精选列表

搜索[基础搭建],共10000篇文章
优秀的个人博客,低调大师

docker基础教程

核心概念 镜像 一个镜像可以有很多层,每一层都可以用来存储数据 容器 容器就是一个镜像加上一个读写层 仓库 用来存放镜像 安装docker 菜鸟教程的安装链接 [centos](安装docker: https://www.cnblogs.com/yufeng218/p/8370670.html) docker -v 查看版本号 注册docker账户,用来装你自己创建的镜像https://hub.docker.com/ 镜像加速 阿里云镜像:https://df4d3xrt.mirror.aliyuncs.com 在任务栏点击 Docker for mac 应用图标 -> Perferences... -> Daemon -> Registry mirrors。在列表中填写加速器地址即可。修改完成之后,点击 Apply & Restart 按钮,Docker 就会重启并应用配置的镜像地址了。请看图示 相关docker教程 第一次构建、运行、发布、获取docker镜像 Centos7上安装docker dcoker入门,使用docker部署NodeJs应用 10张图带你深入理解Docker容器和镜像 30分钟入门Docker使用默认镜像 docker run -d nginx (-d后台-it前台) docker ps -a -l 查看已启动的容器 docker ps -l 列出最新创建的容器 docker inspect acd349ac64c5(容器id)查看容器的详细情况 curl 172.17.0.2(你自己的容器ip地址) 访问服务 docker logs acd349ac64c5(容器id) 查看日志 docker exec -it acd349ac64c5 bash 今日容器 exit 退出容器 docker run -d -p 8888:80 nginx 对外暴露服务端口8888 检查容器是不是当前的容器 docker exec -it 7d1f74d7f24d bash echo "hello world" >usr/share/nginx/html/index.html 在浏览localhost:888查看页面内容是否已经变成了hello world 自己创建镜像(手工) docker pull centos:7 安装centos7 yum install epel-release 在centos安装一个软件包 yum install nginx 安装nginx 安装ifconfig yum search ifconfig 查找软件在什么地方 yum install net-tools.x86_64 -y 安装有ifconfig的包 ifconfig 拿到系统的ip(本机是172.17.0.4) curl 172.17.0.4 查看服务情况 Dockerfile vi Dockerfile 创建文件,内容如下 FROM centos:7 MAINTAINER huruqing RUN yum install epel-release -y &&\ yum install nginx -y net-tools -y EXPOSE 80 CMD ["nginx","-g","deamon off;"] docker build -t nginx:v1 构建镜像 docker images |grep nginx 查找nginx的镜像 docker run -d -p 8888:80 100bf0d80bfb 对外暴露服务端口 打tag docker tag nginx huruqing/nginx:v1 添加标志 push docker push nginx huruqing/nginx:v1 提交到仓库 docker的常用命令 #查看当前docker版本 docker -v #查看当前本地所有镜像 docker images #构造镜像,用法docker build -t 镜像名称 . docker build -t docker_demo . #用于容器与主机之间的数据拷贝。用法docker cp 主机文件地址 容器内地址。12d7f14v45cv为容器id。 docker cp /www/runoob 12d7f14v45cv:/www/ #创建一个新的容器并运行,-d为后台执行,-p 9000:3000前面为主机端口,后面是容器端口。docker_demo镜像名 docker run -d -p 9000:3000 docker_demo #启动已被停止的容器 docker start docker_demo #关闭已被启动的容器 docker stop docker_demo #关闭所有容器 docker stop $(docker ps -a -q) # 删除所有容器 docker rm $(docker ps -a -q) #重新启动容器 docker restart docker_demo #杀掉一个运行中的容器。 docker kill -s KILL docker_demo #删除一个或多少容器。-f :通过SIGKILL信号强制删除一个运行中的容器-l :移除容器间的网络连接,而非容器本身-v :-v 删除与容器关联的卷 docker rm -f docker_demo、docker_demo1 #在运行的容器中执行命令。104e28f2f072容器id sudo docker exec -it 104e28f2f072 /bin/bash #列出容器。 -a:所有容器包含没有运行的 docker ps #获取容器获取容器的日志 104e28f2f072容器id,-t:显示时间戳 docker logs -f -t 104e28f2f072 #登陆镜像仓库 docker login #获取镜像 docker pull #上传镜像 docker push #查看指定镜像的创建历史。 docker history docker_demo Docker命令大全 采坑记录 docker stop 后面只能跟容器id而非镜像名 执行exec时报错: OCI runtime exec failed: exec failed: container_linux.go:348: starting container process caused "exec: "bash": executable file not found in $PATH": unknown 解决办法 exec 报错是因为 /bin/bash 文件不存在,可以尝试 /bin/sh 等。

优秀的个人博客,低调大师

Java内存模型——基础

JMM因何产生 由于计算机的存储设备与处理器的运算能力之间有几个数量级的差距,所以现代计算机系统都不得不加入一层读写速度尽可能接近处理器运算速度的高速缓存(Cache)来作为内存与处理器之间的缓冲——将运算需要使用到的数据复制到缓存中,让运算能快速进行,当运算结束后再从缓存同步回内存之中没这样处理器就无需等待缓慢的内存读写了。 基于高速缓存的存储交互很好地解决了处理器与内存的速度矛盾,但是引入了一个新的问题——缓存一致性(Cache Coherence)。在多处理器系统中,每个处理器都有自己的高速缓存,而他们又共享同一主存,当多个处理器的运算任务都涉及同一块主内存区域时, 将可能导致各自的缓存数据不一致的情况, 而这也是并发编程必须考虑的问题,为了解决一致性的问题,Java虚拟机规范定义了Java内存模型(Java Memory Model ,JMM)来屏蔽了各种硬件和操作系统的内存访问差异,以实现让 Java程序在各种平台下都能达到一致的内存访问效果。Java内存模型的关键技术点围绕多线程的原子性、可见性和有序性建立。 主内存与工作内存 Java内存模型的主要目标是定义程序中各个变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取出变量这样的底层细节。此处的变量与Java编程中所说的变量有所区别,它包括了实例字段、静态字段和构成数组对象的元素,但不包含局部变量与方法参数,因为后者是线程私有的,不会被共享。 Java内存模型规定了所有的变量都存储在主内存(Main Memory,虚拟机内存的一部分)中,每条线程还有自己的工作内存(Working Memory,我看网上很多文章说成本地内存,我觉得工作内存更贴切一些,JMM本身就是抽象的概念,所以工作内存也是个抽象的概念而已),线程的工作内存中保存了被该线程使用到的共享变量的主内存副本拷贝,线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,不能直接读写主内存中的变量。不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成,线程、工作内存、主内存三者的交互关系如图: 内存间交互操作 一个变量如何从主内存拷贝到工作内存、如何从工作内存同步会主内存的实现细节,在Java内存模型中定义了以下8种操作来完成 lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占状态。 unlock(解锁):作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。 read(读取):作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用 load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。 use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。 assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。 store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作。 write(写入):作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中。 如果要把一个变量从主内存中复制到工作内存,就需要顺序地执行read和load操作,如果把变量从工作内存中同步回主内存中,就要按顺序地执行store和write操作。Java内存模型只要求上述操作必须按顺序执行,而没有保证必须是连续执行。也就是read和load之间,store和write之间是可以插入其他指令的,如对主内存中的变量a、b进行访问时,可能的顺序是read a,read b,load b, load a。Java内存模型还规定了在执行上述八种基本操作时,必须满足如下规则: 不允许read和load、store和write操作之一单独出现 不允许一个线程丢弃它的最近assign的操作,即变量在工作内存中改变了之后必须同步到主内存中。 不允许一个线程无原因地(没有发生过任何assign操作)把数据从工作内存同步回主内存中。 一个新的变量只能在主内存中诞生,不允许在工作内存中直接使用一个未被初始化(load或assign)的变量。即就是对一个变量实施use和store操作之前,必须先执行过了assign和load操作。 一个变量在同一时刻只允许一条线程对其进行lock操作,lock和unlock必须成对出现 如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前需要重新执行load或assign操作初始化变量的值 如果一个变量事先没有被lock操作锁定,则不允许对它执行unlock操作;也不允许去unlock一个被其他线程锁定的变量。 对一个变量执行unlock操作之前,必须先把此变量同步到主内存中(执行store和write操作)。 JVM对Java内存模型的实现 经常有人把 Java内存区分为堆内存(Heap)和栈内存(Stack),这种分法比较粗糙, Java内存区域的划分实际上远比这复杂。 下图展示了Java内存模型在JVM中的逻辑视图: 每一个运行在Java虚拟机里的线程都拥有自己的线程栈。一个线程仅能访问自己的线程栈,这个线程栈包含了当前线程执行的方法的所有本地变量信息,所有原始类型的本地变量都存放在线程栈上,因此对其它线程不可见。即使两个线程执行同样的代码,这两个线程仍然在在自己的线程栈中来创建本地变量。一个线程可以向另一个线程传递一个原始类型变量的拷贝,但是它不能共享这个原始类型变量自身。因此,每个线程拥有每个本地变量的独有版本。 堆包含Java程序中创建的所有对象信息,不管对象是哪个线程创建的。这包括原始类型的封装类。不管对象是属于一个成员变量还是方法中的本地变量,它都会被存储在堆区。 下图展示了调用栈和本地变量都存储在栈区,对象都存储在堆区: 一个本地变量如果是原始类型,那么它会被完全存储到栈区。 一个本地变量如果是一个对象的引用,那么这个本地引用会被存储到栈中,但是对象本身仍然存储在堆区。 对于一个对象的成员方法,这些方法中包含本地变量,仍需要存储在栈区,即使它们所属的对象在堆区。 对于一个对象的成员变量,不管它是原始类型还是包装类型,都会被存储到堆区。 Static类型的变量以及类本身相关信息都会随着类本身存储在堆区。堆中的对象可以被多线程共享。如果一个线程获得一个对象的引用,它便可访问这个对象的成员变量。如果两个线程同时调用了同一个对象的同一个方法,那么这两个线程便可同时访问这个对象的成员变量,但是对于本地变量,每个线程都会拷贝一份到自己的线程栈中。 下图展示了上面描述的过程: 参考: https://www.cnblogs.com/jimoer/p/9108779.html http://www.cnblogs.com/nexiyi/p/java_memory_model_and_thread.html http://ifeve.com/java-memory-model-6/ https://blog.csdn.net/hollis_chuang/article/details/80880118 http://www.infoq.com/cn/articles/java-memory-model-1 http://www.infoq.com/cn/articles/java-memory-model-2 https://www.jianshu.com/p/d3fda02d4cae https://blog.csdn.net/suifeng3051/article/details/52611310 https://baike.baidu.com/item/java%E5%86%85%E5%AD%98%E6%A8%A1%E5%9E%8B/3399303

优秀的个人博客,低调大师

Python 基础入门 (全)

MongoDB简介 1.MongoDB ①MongoDB 是一个基于分布式 文件存储的NoSQL数据库 ②由C++语言编写,运行稳定,性能高 ③旨在为 WEB 应用提供可扩展的高性能数据存储解决方案 ④官方网址:https://www.mongodb.com/ 2.MongoDB特点 ①MongoDB可以轻松地布置两个活跃集群,但关系型不容易在两台服务器上同时运行单个数据库 ②MongoDB天然缺少对事物的支持 ③MongoDB并不提供主/主复制,但它支持分片 3.Bson和Json ①Bosn版本是Json的二进制格式,但并不意味着Bson占用的空间就比Json小的多 ②继续使用Bson的原因是因为MongoDB在设计上就是要求快速,而不是节省空间,如果处理数据的速度更快,存储文档时的一点开销是完全可以接受的 ③Bson更易于浏览(即遍历),遍历索引页非常快,第二点是很容易将Bson数据快速转化为编程语言的原生数据格式 4.名词 SQL术语/概念 MongoDB术语/概念 解释/说明 database database 数据库 table collection 数据库表/集合 row document 数据记录行/文档 column field 数据字段/域 index index 索引 tablejoins 表连接,MongoDB不支持 primarykey primarykey 主键,MongoDB自动将_id字段设置为主键 5.MongoDB三要素:数据库,集合,文档 ①数据库:是一个集合的物理容器,一个数据库中可以包含多个文档,一个服务器通常有多个数据库 ②集合:类似于关系数据库中的表,储存多个文档,结构不固定,如可以存储如下文档在一个集合中 {'name':'guojing','gender':'男'} {'name':'huangrong','age':18} {'book':'shuihuzhuan','heros':'108'} ③文档:就是一个对象,由键值对组成,是Json的扩展Bson格式 {'name':'guojing','gender':'男'} MongoDB安装与管理 1.安装 ①下载MongoDB的版本注意,两点注意 根据规则:偶数为稳定版,如1.6X,奇数为开发版,如1.7X 32bit的MongoDB最大只能存放2G的数据,64bit就没有限制 ②到官网,选择合适的版本下载 ③解压 tar-zxvfmongodb-linux-x86_64-ubuntu1604-3.4.0.tgz ④移动到/usr/local/目录下 sudomv-rmongodb-linux-x86_64-ubuntu1604-3.4.0//usr/local/mongodb ⑤将可执行文件添加到PATH路径中 exportPATH=/usr/local/mongodb/bin:$PATH 2.管理 ①配置文件在/etc/mongod.conf,默认端口27017 ②启动 sudoservicemongodstart ③停止 sudoservicemongodstop ④使用终端连接:mongo,这个shell就是mongodb的客户端,同时也是一个js的编译器 ⑤ GUI:robomongo,解压后在bin目录下找到运行程序 数据库操作 1.数据库切换 ①查看当前数据库名称:db ②查看所有数据库名称:show dbs,(列出所有在物理上存在的数据库) ③切换数据库:use 数据库名称,(如果数据库不存在,则指向数据库,但不创建,直到插入数据或创建集合时数据库才被创建) ④默认的数据库为test,如果没有创建新的数据库,集合将存放在test数据库中 2.数据库删除 删除当前指向的数据库,如果数据库不存在,则什么也不做 db.dropDatabase() 集合操作 1.集合创建 语法: db.createCollection(name,option) ①name是要创建集合的名称 ②options是一个文档,用于指定集合的配置 ③选项参数是可选的,所以只需要到指定的集合名称 例1:不限制集合的大小 db.createCollection(‘stu’) 例2:限制集合大小 db.createCollection(‘sub’,{capped:true,size:10}) 参数capped:默认值为false表示不设置上限,值为true表示设置上限 参数size:当capped值为true时,需要指定此参数,表示上限大小,当文档设置上限时,会将之前的数据覆盖,单位为字节 2.查看当前数据库的集合 showcollections 3.删除集合 db.集合名称.drop() 数据类型 1. MongoDB中常用的几种数据类型: ①Object ID:文档ID ②String:字符串,最常用,必须是有效的UTF-8 ③Boolean:存储一个布尔值,true或false ④Integer:整数可以是32位或64位,这取决于服务器 ⑤Double:存储浮点值 ⑥Arrays:数组或列表,多个值存储到一个键 ⑦Object:用于嵌入式的文档,即一个值为一个文档 ⑧Null:存储Null值 ⑨Timestamp:时间戳 ⑩Date:存储当前日期或时间的UNIX时间格式 2. object id ①每个文档都有一个属性,为_id,保证每个文档的唯一性 ②可以自己去设置_id插入文档 ③如果没有提供,那么MongoDB为每个文档提供了一个独特的_id,类型为objectID ④objectID是一个12字节的十六进制数 前4个字节为当前时间戳 接下来3个字节的机器ID 接下来的2个字节中MongoDB的服务进程id 最后3个字节是简单的增量值 数据操作 1.插入 插入文档时,如果不指定_id参数,MongoDB会为文档分配一个唯一的ObjectId db.集合名称.insert(document) 2.简单查询 db.集合名称.find() 3.更新 db.集合名称.update(<query>,<update>,multi:<boolean>}) ①参数query:查询条件,类似sql语句update中where部分 ②参数update:更新操作符,类似sql语句update中set部分 ③参数multi:可选,默认是false,表示只更新找到的第一条记录,值为true表示把满足条件的文档全部更新 例3:全文档更新 db.stu.update( {name:'hr'},{name:'mnc'} ) 例4:指定属性更新,通过操作符$set db.stu.insert({name:'hr',gender:0}) db.stu.update({name:'hr'},{$set:{name:'hys'}}) 例5:修改多条匹配到的数据 db.stu.update({},{$set:{gender:0}},{multi:true}) 4.保存 如果文档的_id已经存在则修改,不存在则添加 db.集合名称.save(document) 5.删除 db.集合名称.remove(<query>,{justOne:<boolean>}) ①参数query:可选,删除的文档的条件 ②参数justOne:可选,如果设为true或1,则只删除一条,默认false,表示删除多条 例6:只删除匹配到的第一条 db.stu.remove({gender:0},{justOne:true}) 例7:全部删除 db.stu.remove({}) 数据查询 1.基本查询 ①方法find():查询 db.集合名称.find({条件文档}) ②方法findOne():查询,只返回第一个 db.集合名称.findOne({条件文档}) ③方法pretty():将结果格式化 db.集合名称.find({条件文档}).pretty() 2.比较运算符 ①等于,默认是等于判断,没有运算符 ②小于$lt ③小于或等于$lte ④大于$gt ⑤大于或等于$gte ⑥不等于$ne 例8:查询年龄大于或等于18的学生 db.stu.find({age:{$gte:18}}) 3.逻辑运算符 ①查询时可以有多个条件,多个条件之间需要通过逻辑运算符连接 ②逻辑与:默认是逻辑与的关系 例9:查询年龄大于或等于18,并且性别为1的学生 db.stu.find({age:{$gte:18},gender:1}) 逻辑或:使用$or 例10:查询年龄大于18,或性别为0的学生 db.stu.find({$or:[{age:{$gt:18}},{gender:1}]}) and和or一起使用 例11:查询年龄大于18或性别为0的学生,并且学生的姓名为gj db.stu.find({$or:[{age:{$gte:18}},{gender:1}],name:'gj'}) 4.范围运算符 使用"$in","$nin" 判断是否在某个范围内 例12:查询年龄为18、28的学生 db.stu.find({age:{$in:[18,28]}}) 5.支持正则表达式 使用//或$regex编写正则表达式 例13:查询姓黄的学生 db.stu.find({name:/^黄/})db.stu.find({name:{$regex:'^黄'}}) 6.自定义查询 使用$where后面写一个函数,返回满足条件的数据 例14:查询年龄大于30的学生 db.stu.find({$where:function(){returnthis.age>20}}) 7. Limit(方法limit():用于读取指定数量的文档) db.集合名称.find().limit(NUMBER) ①参数NUMBER表示要获取文档的条数 ②如果没有指定参数则显示集合中的所有文档 例15:查询2条学生信息 db.stu.find().limit(2) 8. skip(方法skip():用于跳过指定数量的文档) db.集合名称.find().skip(NUMBER) 参数NUMBER表示跳过的记录条数,默认值为0 例16:查询从第3条开始的学生信息 db.stu.find().skip(2) 9.Limit与Skip一起使用 方法limit()和skip()可以一起使用,不分先后顺序 查询第5至8条数据(跳取) db.stu.find().limit(4).skip(5)db.stu.find().skip(5).limit(4) 10. 投影 在查询到的返回结果中,只选择必要的字段,而不是选择一个文档的整个字段 语法:参数为字段与值,值为1表示显示,值为0不显示 db.集合名称.find({},{字段名称:1,...}) 对于需要显示的字段,设置为1即可,不设置即为不显示 特殊:对于_id列默认是显示的,如果不显示需要明确设置为0 db.stu.find({},{_id:0,name:1,gender:1}) 11.排序 方法sort(),用于对结果集进行排序 语法:db.集合名称.find().sort({字段:1,...}) ①参数1为升序排列 ②参数-1为降序排列 例18:根据性别降序,再根据年龄升序 db.stu.find().sort({gender:-1,age:1}) 12.统计个数 方法count()用于统计结果集中文档条数 语法: db.集合名称.find({条件}).count() 也可以与为 db.集合名称.count({条件}) 例19:统计男生人数 db.stu.find({gender:1}).count() 例20:统计年龄大于20的男生人数 db.stu.count({age:{$gt:20},gender:1}) 13.消除重复 方法distinct()对数据进行去重 语法: db.集合名称.distinct('去重字段',{条件}) 例21:查找年龄大于18的性别(去重) db.stu.distinct('gender',{age:{$gt:18}}) 聚合 aggregate 1.聚合(aggregate) 主要用于计算数据,类似sql中的sum()、avg() 语法: db.集合名称.aggregate([{管道:{表达式}}]) 2.管道 管道在Unix和Linux中一般用于将当前命令的输出结果作为下一个命令的输入 psajx|grepmongo 在mongodb中,管道具有同样的作用,文档处理完毕后,通过管道进行下一次处理 常用管道: $group:将集合中的文档分组,可用于统计结果 $match:过滤数据,只输出符合条件的文档 $project:修改输入文档的结构,如重命名、增加、删除字段、创建计算结果 $sort:将输入文档排序后输出 $limit:限制聚合管道返回的文档数 $skip:跳过指定数量的文档,并返回余下的文档 $unwind:将数组类型的字段进行拆分 表达式 处理输入文档并输出 语法:表达式:'$列名' $sum:计算总和,$sum:1同count表示计数 $avg:计算平均值 $min:获取最小值 $max:获取最大值 $push:在结果文档中插入值到一个数组中 $first:根据资源文档的排序获取第一个文档数据 $last:根据资源文档的排序获取最后一个文档数据 3.$group 将集合中的文档分组,可用于统计结果 _id表示分组的依据,使用某个字段的格式为'$字段' 例22:统计男生、女生的总人数 db.stu.aggregate([{$group:{_id:'$gender',counter:{$sum:1}}}]) Group by null 将集合中所有文档分为一组 例23:求学生总人数、平均年龄 db.stu.aggregate([{$group:{_id:null,counter:{$sum:1},avgAge:{$avg:'$age'}}}]) 透视数据 例24:统计学生性别及学生姓名 db.stu.aggregate([{$group:{_id:'$gender',name:{$push:'$name'}}}]) 使用$$ROOT可以将文档内容加入到结果集的数组中,代码如下 db.stu.aggregate([{$group:{_id:'$gender',name:{$push:'$$ROOT'}}}]) 4.$match 用于过滤数据,只输出符合条件的文档 使用MongoDB的标准查询操作 例25:查询年龄大于20的学生 db.stu.aggregate([{$match:{age:{$gt:20}}}]) 例26:查询年龄大于20的男生、女生人数 db.stu.aggregate([{$match:{age:{$gt:20}}},{$group:{_id:'$gender',counter:{$sum:1}}}]) 5.$project 修改输入文档的结构,如重命名、增加、删除字段、创建计算结果 例27:查询学生的姓名、年龄 db.stu.aggregate([{$project:{_id:0,name:1,age:1}}]) 例28:查询男生、女生人数,输出人数 db.stu.aggregate([{$group:{_id:'$gender',counter:{$sum:1}}},{$project:{_id:0,counter:1}}]) 6. $sort 将输入文档排序后输出 例29:查询学生信息,按年龄升序 b.stu.aggregate([{$sort:{age:1}}]) 例30:查询男生、女生人数,按人数降序 db.stu.aggregate([{$group:{_id:'$gender',counter:{$sum:1}}},{$sort:{counter:-1}}]) 7. $limit,$skip 限制聚合管道返回的文档数 例31:查询2条学生信息 db.stu.aggregate([{$limit:2}]) $skip 跳过指定数量的文档,并返回余下的文档 例32:查询从第3条开始的学生信息 db.stu.aggregate([{$skip:2}]) 例33:统计男生、女生人数,按人数升序,取第二条数据 db.stu.aggregate( [{$group:{_id:'$gender',counter:{$sum:1}}}, {$sort:{counter:1}}, {$skip:1}, {$limit:1}]) 注意顺序:先写skip,再写limit 8. $unwind 将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值 语法1:对某字段值进行拆分 db.集合名称.aggregate([{$unwind:'$字段名称'}]) 构造数据 db.t2.insert({_id:1,item:'t-shirt',size:['S','M','L']}) 查询 db.t2.aggregate([{$unwind:'$size'}]) 语法2:对某字段值进行拆分 处理空数组、非数组、无字段、null情况 db.inventory.aggregate([{$unwind:{path:'$字段名称',preserveNullAndEmptyArrays:<boolean>#防止数据丢失}}]) 构造数据 db.t3.insert( [{"_id":1,"item":"a","size":["S","M","L"]}, {"_id":2,"item":"b","size":[]}, {"_id":3,"item":"c","size":"M"}, {"_id":4,"item":"d"}, {"_id":5,"item":"e","size":null}] ) 使用语法1查询 db.t3.aggregate([{$unwind:'$size'}]) 查看查询结果,发现对于空数组、无字段、null的文档,都被丢弃了 问:如何能不丢弃呢? 答:使用语法2查询 db.t3.aggregate([{$unwind:{path:'$sizes',preserveNullAndEmptyArrays:true}}]) 安全 1.超级管理员 为了更安全的访问mongodb,需要访问者提供用户名和密码,于是需要在mongodb中创建用户 采用了角色-用户-数据库的安全管理方式 常用系统角色如下: root:只在admin数据库中可用,超级账号,超级权限 Read:允许用户读取指定数据库 readWrite:允许用户读写指定数据库 创建超级管理用户 useadmindb.createUser({ user:'admin', pwd:'123', roles:[{role:'root',db:'admin'}] }) 2.启用安全认证 修改配置文件 sudovi/etc/mongod.conf 启用身份验证 注意:keys and values之间一定要加空格, 否则解析会报错 security:authorization:enabled 重启服务 sudoservicemongodstop sudoservicemongodstart 终端连接 mongo-u'admin'-p'123'--authenticationDatabase'admin' 3.普通用户管理 使用超级管理员登录,然后进入用户管理操作 查看当前数据库的用户 usetest1showusers 创建普通用户 db.createUser({ user:'t1',pwd:'123',roles:[{role:'readWrite',db:'test1'}] }) 终端连接 mongo-ut1-p123--authenticationDatabasetest1 切换数据库,执行命令查看效果 修改用户:可以修改pwd、roles属性 db.updateUser('t1',{pwd:'456'}) 复制(副本集) 1.什么是复制 ①复制提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性,并可以保证数据的安全性 ②复制还允许从硬件故障和服务中断中恢复数据 2.为什么要复制 ①数据备份 ②数据灾难恢复 ③读写分离 ④高(24* 7)数据可用性 ⑤无宕机维护 ⑥副本集对应用程序是透明 3.复制的工作原理 ①复制至少需要两个节点A、B... ②A是主节点,负责处理客户端请求 ③其余的都是从节点,负责复制主节点上的数据 ④节点常见的搭配方式为:一主一从、一主多从 ⑤主节点记录在其上的所有操作,从节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作,从而保证从节点的数据与主节点一致 ⑥主节点与从节点进行数据交互保障数据的一致性 4.复制的特点 ①N 个节点的集群 ②任何节点可作为主节点 ③所有写入操作都在主节点上 ④自动故障转移 ⑤自动恢复 5.设置复制节点 接下来的操作需要打开多个终端窗口,而且可能会连接多台ubuntu主机,会显得有些乱,建议在xshell中实现 step1:创建数据库目录t1、t2 在Desktop目录下演示,其它目录也可以,注意权限即可 mkdirt1mkdirt2 step2:使用如下格式启动mongod,注意replSet的名称是一致的 mongod--bind_ip192.168.196.128--port27017 --dbpath~/Desktop/t1--replSet rs0mongod--bind_ip192.168.196.128--port27018 --dbpath~/Desktop/t2--replSetrs0 step3:连接主服务器,此处设置192.168.196.128:27017为主服务器 mongo--host192.168.196.128--port27017 step4:初始化 rs.initiate() step5:查看当前状态 rs.status() step6:添加复本集 rs.add('192.168.196.128:27018') step7:连接第二个mongo服务 mongo--host192.168.196.128--port27018 step8:向主服务器中插入数据 usetest1for(i=0;i<10;i++){ db.t1.insert({_id:i}) } db.t1.find() step9:在从服务器中插查询 说明:如果在从服务器上进行读操作,需要设置rs.slaveOk() rs.slaveOk()db.t1.find() 其它说明 删除从节点 rs.remove('192.168.196.128:27018') 关闭主服务器后,再重新启动,会发现原来的从服务器变为了主服务器,新启动的服务器(原来的从服务器)变为了从服务器 备份和恢复 1.备份 语法: mongodump-hdbhost-ddbname-odbdirectory -h:服务器地址,也可以指定端口号 -d:需要备份的数据库名称 -o:备份的数据存放位置,此目录中存放着备份出来的数据 例1: sudomkdirtest1bak sudomongodump-h192.168.196.128:27017-dtest1-o~/Desktop/test1bak 2.恢复 语法: mongorestore-hdbhost-ddbname--dirdbdirectory -h:服务器地址 -d:需要恢复的数据库实例 --dir:备份数据所在位置 例2: mongorestore-h192.168.196.128:27017-dtest2--dir~/Desktop/test1bak/test1 与python交互 1.点击查看官方文档:http://api.mongodb.org/python/current/tutorial.html 2.安装python包 进入虚拟环境 sudopipinstallpymongo 或源码安装 pythonsetup.py 引入包pymongo importpymongo 连接,创建客户端 client=pymongo.MongoClient("localhost",27017) 获得数据库test1 db=client.test1 获得集合stu stu=db.stu 添加文档 s1={name:'gj',age:18} s1_id=stu.insert_one(s1).inserted_id 查找一个文档 s2=stu.find_one() 查找多个文档1 forcurinstu.find(): printcur 查找多个文档2 cur=stu.find()cur.next()cur.next()cur.next() 获取文档个数 printstu.count()

优秀的个人博客,低调大师

JavaScript 基础--- (经典案例)

案例一:全选练习 ** 使用复选框上面一个属性判断是否选中 - checked属性 - checked=true:选中 - checked=false:不选中 * 创建一个页面 ** 复选框和按钮 - 四个复选框表示爱好 - 还有一个复选框操作 全选和选不选,也有一个事件 ** 三个按钮,分别有事件 - 全选 - 全不选 - 反选 * 全选操作 步骤: /* 1、获取要操作的复选框 - 使用getElementsByName() 2、返回是数组, - 属性 checked判断复选框是否选中 *** checked = true; //表示选中 *** checked = false;//表示不选中 - 遍历数组,得到的是每一个checkbox * 把每一个checkbox属性checked=true */ * 全不选操作 步骤 /* 1、获取到要操作的复选框 2、返回的是数组,遍历数组 3、得到每一个复选框 4、设置复选框的属性 checked=false */ * 反选操作 步骤 /* 1、获取到要操作的复选框 2、返回数组,遍历数组 3、得到每一个复选框 4、判断当前的复选框是选中还是不选中 - if(love1.checked == true) {} 5、如果选中,属性checked设置成false,否则,设置成true */ * 使用复选框实现全选和全不选 步骤 /* 1、得到上面那个复选框 - 通过id获取到 2、判断这个复选框是否是选中 - if条件,checked==true 3、如果是选中,下面是全选 4、如果不是选中,下面是全不选 */ <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <style type="text/css"> table{ border: 1px solid #000; width: 400px; } </style> <script type="text/javascript"> function change(){ var all = document.getElementById("checkbox_all"); var cb = document.getElementsByName("personid"); //判断全选按钮是否被选中 if(all.checked){ for(var i = 0; i < cb.length; i++){ cb[i].checked = true; } }else{ for(var i = 0; i < cb.length; i++){ cb[i].checked = false; } } } //反选 function reverse(){ //获取所有的checkbox var cb = document.getElementsByName("personid"); for(var i = 0; i < cb.length; i++){ if(cb[i].checked){ cb[i].checked = false; }else{ cb[i].checked = true; } } } </script> </head> <body> <table border="1" cellspacing="0" cellpadding="0"> <tr> <th>全选<input type="checkbox" id="checkbox_all" onclick="change()"/></th> <th>姓名</th> <th>年龄</th> <th>手机号</th> </tr> <tr> <td><input type="checkbox" name="personid" /></td> <td>张三</td> <td>20</td> <td>15033444455</td> </tr> <tr> <td><input type="checkbox" name="personid"/></td> <td>李四</td> <td>30</td> <td>15033444456</td> </tr> <tr> <td><input type="checkbox" name="personid"/></td> <td>王五</td> <td>40</td> <td>15033444457</td> </tr> <tr> <td><input type="checkbox" name="personid"/></td> <td>赵六</td> <td>50</td> <td>15033444458</td> </tr> </table> <input type="button" value="反选" onclick="reverse()"/> </body> </html> 输出: 案例二:下拉列表左右选择 * 下拉选择框 <select> <option>111</option> <option>111</option> </select> * 创建一个页面 ** 两个下拉选择框 - 设置属性 multiple属性 ** 四个按钮,有事件 * 选中添加到右边 步骤 /* 1、获取select1里面的option - getElementsByTagName()返回是数组 - 遍历数组,得到每一个option 2、判断option是否被选中 - 属性 selected,判断是否被选中 ** selected= true: 选中 ** selected= false:没有选择 3、如果是选中,把选择的添加到右边去 4、得到select2 4、添加选择的部分 - appendChild方法 */ * 全部添加到右边 步骤 /* 1、获取第一个select下面的option对象 2、返回数组,遍历数组 3、得到每一个option对象 4、得到select2 5、添加到select2下面 - appendChild方法 */ * 选中添加到左边 步骤 /* 1、获取select2里面的option对象 2、返回是数组,遍历数组 3、得到每一个option对象 4、判断option是否被选中 - if条件 属性 selected == true:选择 5、获取select1 6、添加到select1里面 - 使用appendChild方法 */ * 全部添加到左边 步骤 /* 1、获取select2里面的option对象 2、返回是数组,遍历数组 3、得到每一个option对象 4、获取到select1 5、添加到select1里面 - 使用appendChild方法 */ <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <style type="text/css"> .center{ float:left; margin-left: 100px ; } </style> </head> <body> <div class="center"> <select id="select1" multiple style="width:100px;height: 200px;" ondblclick="sel()"> <option value="选项1">选项1</option> <option value="选项2">选项2</option> <option value="选项3">选项3</option> <option value="选项4">选项4</option> <option value="选项5">选项5</option> </select> <input type="button" onclick="sel()" value="将左边选中的选项添加到右边" /> </div> <div class="center"> <select id="select2" multiple style="width:100px;height: 200px;" ondblclick="cancel_sel()"> </select> <input type="button" onclick="cancel_sel()" value="将右边选中的选项添加到左边" /> </div> </body> <script type="text/javascript"> function sel(){ var select1 = document.getElementById("select1"); var select2 = document.getElementById("select2"); var select1_options = select1.getElementsByTagName("option"); for(var i = 0; i < select1_options.length; i++){ var opt = select1_options[i]; if(opt.selected){ select2.appendChild(opt); } } } //取消选中 function cancel_sel(){ var select1 = document.getElementById("select1"); var select2 = document.getElementById("select2"); var select2_options = select2.getElementsByTagName("option"); for(var i = 0; i < select2_options.length; i++){ var opt = select2_options[i]; if(opt.selected){ select1.appendChild(opt); } } } </script> </html> 输出: 案例三:省市联动 * 创建一个页面,有两个下拉选择框 * 在第一个下拉框里面有一个事件 :改变事件 onchange事件 - 方法add1(this.value);表示当前改变的option里面的value值 * 创建一个二维数组,存储数据 * 每个数组中第一个元素是国家名称,后面的元素是国家里面的城市 * /* 1、遍历二维数组 2、得到也是一个数组(国家对应关系) 3、拿到数组中的第一个值和传递过来的值做比较 4、如果相同,获取到第一个值后面的元素 5、得到city的select 6、添加过去(使用)appendChild方法 - 创建option(三步) */ /* 由于每次都要向city里面添加option 第二次添加,会追加。 * 每次添加之前,判断一下city里面是否有option,如果有,删除 */ 方法一: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <style type="text/css"> select{ width:100px; } </style> </head> <body> 省: <select id="sheng" onchange="selectShi()"> <option value="">请选择省份</option> <option value="0">河北省</option> <option value="1">山东省</option> <option value="2">河南省</option> <option value="3">山西省</option> </select> 市: <select id="shi"> <option value="">请选择市</option> </select> </body> <script type="text/javascript"> var arrs = []; arrs[0] = ["秦皇岛","保定","石家庄","唐山"]; arrs[1] = ["济南","临沂","青岛","威海"]; arrs[2] = ["洛阳","郑州","开封","周口"]; arrs[3] = ["太原","大同","运城","忻州"]; function selectShi(){ var sheng = document.getElementById('sheng').value; var shi = document.getElementById("shi"); var shis = arrs[sheng]; //清空原来的信息 //获取市的option属性 var shi_opts = shi.getElementsByTagName("option"); //遍历删除市中的option for(var i= shi_opts.length - 1; i >= 1; i--){ var shi_opt = shi_opts[i]; shi.removeChild(shi_opt); } for(var i = 0; i < shis.length; i++){ var textNode = document.createTextNode(shis[i]); var opt = document.createElement("option"); opt.appendChild(textNode); shi.appendChild(opt); } } </script> </html> 方法二: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <style type="text/css"> select{ width:100px; } </style> </head> <body> 省: <select id="sheng" onchange="selectShi()"> <option value="">请选择省份</option> <option value="0">河北省</option> <option value="1">山东省</option> <option value="2">河南省</option> <option value="3">山西省</option> </select> 市: <select id="shi"> <option value="">请选择市</option> </select> </body> <script type="text/javascript"> var arrs = []; arrs[0] = ["秦皇岛","保定","石家庄","唐山"]; arrs[1] = ["济南","临沂","青岛","威海"]; arrs[2] = ["洛阳","郑州","开封","周口"]; arrs[3] = ["太原","大同","运城","忻州"]; function selectShi(){ var sheng = document.getElementById('sheng').value; var shi = document.getElementById("shi"); var shis = arrs[sheng]; var str = "<option value=''>请选择市</option>"; for(var i = 0; i < shis.length; i++){ str += "<option value=''>"+ shis[i] +"</option>"; } shi.innerHTML = str; } </script> </html> 输出:

优秀的个人博客,低调大师

Java 基础 之 for 循环

http://www.verejava.com/?id=16992632674123 /** for 循环 定义 : for(初始化变量;判断条件;更新循环变量){} 执行过程: 1. 初始化变量 2. 执行判断条件, 如果true 继续执行,执行完后, 更新循环变量, 再判断条件, 直到 判断条件为 false 退出循环 */ public class ForStatement { public static void main(String[] args) { //打印出0-9 /* 执行过程: 1. 初始化i=0; 2. 执行 i<10 如果为true 执行 System.out.println(i); 执行完后 i++ 再跟 i<10 判断 以此循环直到 i>=10 退出循环 */ for (int i = 0; i < 10; i++) { System.out.println(i); } } } /* while, do while, for 循环的比较 1. while 循环是先判断再循环, do while 是先执行再判断 2. for 循环一般用于下标值访问的情况 问题: 如果我们要在循环的时候中途终止,或退出需要用到 break ,continue */ http://www.verejava.com/?id=16992632674123

优秀的个人博客,低调大师

Java 基础 之 常量

http://www.verejava.com/?id=16992564786810 /* java中常量分为以下几种 1. 整型常量 2. 浮点数常量 3. 字符常量 4. 布尔型常量 5. 字符串常量 */ public class FinalVariable { public static void main(String[] args) { //常量的定义:不可以改变的变量 //1. 整型常量 final int UP=0; final int DOWN=1; final int LEFT=2; final int RIGHT=3; System.out.println(UP); System.out.println(DOWN); System.out.println(LEFT); System.out.println(RIGHT); /* 注意: 1. 常量的定义一般用大写 */ //2. 浮点数常量 final float PI=3.14f; System.out.println(PI); //3. 布尔常量 final boolean RUN=true; System.out.println(RUN); //4. 字符常量 final char A='A'; System.out.println(A); System.out.println('\r'); System.out.println('\''); System.out.println('\"'); System.out.println('\\'); //5. 字符串常量 final String CONST="情意地久天长"; System.out.println(CONST); } } /* 总结: 1. 常量的命名规则跟变量一样 2. 常量一般大写 3. 常量前面加 final 关键字 4. 常量不能改变值 */ http://www.verejava.com/?id=16992564786810

优秀的个人博客,低调大师

Java 基础 之 变量

http://www.verejava.com/?id=1699255231918 /* 注意: 1. 变量命名不允许重复 2. 变量的命名规则: 以字符,下划线,$ ,数字组成,并且开始首字母必须为 字符,下滑线或$ 不能是数字 */ public class TestVariable { public static void main(String[] args) { //变量的定义: 系统分配的一块内存区用来存储数据的地方 //String 存储数据的类型 字符串 : 以双引号" " 括起来的字符或汉字等 //bottle 变量名 //等号 = 代表赋值 也就是说将 "矿泉水" 数据存储到 名称为 bottle的内存区域中 String bottle="矿泉水"; //将变量名为bottle内存区域的数据打印输出到控制台上 System.out.println(bottle); //修改变量的值, 将 "果汁" 替换了bottle内存区域的值 bottle="果汁"; System.out.println(bottle); //清空变量, 清空bottle的内存数据 bottle=""; System.out.println(bottle); //删除变量, 赋值给null值删除变量, 这个时候垃圾回收器 GC 如果扫描到 //bottle 变量会自动回收分配的内存空间 bottle=null; System.out.println(bottle); //注意: //1.变量名是不能重复定义的, 否则编译不能通过 //String bottle="牛奶"; //System.out.println(bottle); //变量的命名规则: 以字符,下划线,$ ,数字组成,并且开始首字母必须为 字符,下滑线或$ 不能是数字 //String 1a="a"; //System.out.println(1a); String $b="b"; System.out.println($b); String _c="c"; System.out.println(_c); } } http://www.verejava.com/?id=1699255231918

优秀的个人博客,低调大师

python网络编程基础

目录 1、udp 2、tcp 3、socket函数 4、粘包问题 标签(空格分隔): 网络编程 目录 tcp和udp 1、udp tcp、udp是第四层传输层拥有的协议,用于在完成寻址功能后的数据传输。传输层将路由交换层和应用数据层划分开,主要提供数据传输控制。 udp的使用是面向无连接的,即协议自身的设计是不保证数据的有序性和重传的,这样的缺点是丢包率增加,并且无法有序的接收数据。不过这两个缺点都可以通过应用层来弥补。而udp的优点是报头小、数据传输效率高(不需要确认、协商各种流控制等等),所以udp更适用于数据需要快速传输,并对数据完整性并不太高的场景。比如:视频直播,要求实时传输,而且可以接受部分的丢帧。 udp报头非常的简单,核心只有端口+校验和 2、tcp tcp的使用是面向连接的,即数据的传输必须基于虚链路的完整建立。虚链路的建立提供了很多优秀的功能,比如确认重传以保证数据的完整性,滑动窗口以保证数据传输的高效性,各类选项字段提供不同的可选功能。tcp的缺点是报头大,因为需要包含比udp更多的功能字段,而且tcp数据的每次发送都必须得到确认否则将会重传。tcp一般应用于对数据完整性要求很高的场景。比如:ssh。 tcp的报头字段比较多,核心有:seq和ack用于处理确认重传、SYN/FIN/RST等用于提供数据包身份标记、窗口用于处理流量控制、还有各种选项。 3、socket函数 为了将路由交换和应用层分离开,socket提供了一个统一的接口供应用层直接调用而无需考虑底层路由交换的通信问题。 所有的操作系统都提供socket调用,python的socket模块也是对底层socket模块的封装,并提供了方便使用的一些函数接口。 1、建立对象 # 实例化一个socket对象,用于处理本地的socket事务,不论是服务器还是客户端都一样 # socket模块提供了很多不同类型的socket,这里选择tcp,使用ipv4 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 2、bind # socket对象可以绑定本地的ip和端口号,这个函数在服务器和客户端均可使用,不过一般不会绑定客户端 # 客户端的ip和端口一般使用随机。 # 服务器一般会固定ip和端口以供大量客户端连接 # 服务器提供的ip会通过dns发布域名 # bind的参数是一个ip+port的元组,如果没有提供ip,则会监听本机所有对外接口ip sock.bind(('127.0.0.1', 8080)) 3、listen # 此函数只应该用于服务器,因为服务器才需要监听端口等待客户主动连接 # 此函数将会告知操作系统监听socket连接 # 此函数的底层操作应该就是完成tcp三次握手 # 此函数的5表示已经完成三次握手的客户端最大数量,但是这些客户端还未与服务器交互数据 sock.listen(5) 4、connect # 此函数只应该用于客户端,因为客户端才需要连接服务器的端口 # 此函数一旦启动,则会在客户端上随机选取本地端口 # 此函数的语义是发起tcp三次握手 sock.connect(('127.0.0.1', 8080)) 5、accept # 此函数应该用于服务器,当tcp三次握手完成之后,服务器通过此函数获取此客户端socket对象和地址。 # 此函数是一个阻塞函数,即,如果服务器没有任何虚链路完成,将会无限阻塞,直到有一个虚链路通过 # listen完成,accept才会返回。 # 如果要服务器提供无限接收客户端的功能,应该循环此函数以提供链路循环 conn, addr = sock.accept() 6、recv # 此函数用于从一个socket对象(管道)中获取数据,而实际上,是从操作系统的网卡缓存中获取数据 # 可以指定需要一次获取的字节数,获取得到的数据是bytes类型,需要decode才方便阅读 # 此函数是一个阻塞函数,即,如果网卡缓存没有任何数据,则会一直阻塞到数据到达为止 msg = conn.recv() 7、send # 此函数用于将bytes类型的数据发送给socket对象(管道),而实际上,是发送给网卡缓存,后续交由 # 操作系统真正的发送数据。 # 此函数非阻塞,可以直接返回,不过要特别注意的是,msg如果为空,此函数可以正确执行,但是实际上 # 操作系统是没有发送数据给对端的。这样会产生一些socket连接的问题,所以要杜绝发空。 conn.send(msg) 8、close # 关闭虚链路 conn.close() 9、getpeername # 获取一个虚链路对端的地址二元组 print(sock.getpeername()) 4、粘包问题 根本原因:tcp面向流,无法区分消息之间的数据边界,固定recv就会导致消息粘包 解决思路:每次recv的时候动态获取,并准确的获取一个消息的长度 解决办法:每一个消息都增加固定长度的报头。每次recv的时候先获取固定长度的报头,从报头中获取本次消息的准确长度,然后再recv完整准确的信息。

优秀的个人博客,低调大师

JavaScript 基础---( window 总结)

window 概念:1.表示浏览器窗口 2.所有 JavaScript 全局对象、函数以及变量均自动成为 window 对象的成员 3.全局变量是 window 对象的属性 4.全局函数是 window 对象的方法 一、方法: 实例1(新建窗口) 运行: 实例2 (更改窗口大小) 实例3(移动窗口)(700,500 是 x 轴 和 Y 轴) 实例4 关闭窗口(点击关闭) 二、windom screen(获取浏览器的宽度和高度) 实例 三、windom location (浏览器地址栏) 实例 四、window History (记录访问该站点的所有页面地址) 实例 其中,history.go(0) 代表当前页面,history.go(-2)代表当前回到之前的之前页面(即后退 2 次)

优秀的个人博客,低调大师

JavaScript基础(四)函数

函数 函数的定义 //第一种:(函数的声明)第一种定义方法最强大,定义完毕后,在哪里使用都可以,无位置限制。 function fn1(){ console.log("我是第一种定义方法!"); } //第二种(函数表达式:匿名函数) :后两种定义方法是有局限性的。(使用函数必须在定义函数之后) var fn2 = function (){ console.log("我是第二种定义方法!"); }; // 注意分号 function (){ console.log("我是第二种定义方法!"); }(); // 第二种方式的调用方式之一:函数的自调用 //第三种 var fn3 = new Function("console.log('我是第三种定义方法!')"); 函数的调用 函数名(); 函数名 要遵循驼峰命名法。 不能同名(函数重载),否则后面的函数会覆盖前面的函数。 //打印函数名,就等于打印整个函数。 console.log(fn); //打印执行函数,就等于打印函数的返回值。 console.log(fn()); 形参和实参 形参不需要写 var. 形参的个数和实参的个数可以不一致 。 返回值 1. 如果函数没有显示的使用 return 语句 ,那么函数有默认的返回值:undefined2. 如果函数使用 return语句,但是return后面没有任何值,那么函数的返回值也是:undefined. 变量和作用域 全局变量:在 script 使用 var定义的变量和没有 var 的变量(所有的 script 共享其全局性,js里面没有块级作用域概念,只有全局作用域和局部作用域)。 隐式全局变量:在 script 没有 var 的变量。 function fn(){ var a = b = c = 1; // b和c就是隐式全局变量(等号) var a = 1; b = 2; c = 3; // b和c就是隐式全局变量(分号) var a = 1 , b = 2 , c = 3; // b和c就不是隐式全局变量(逗号) } (全局变量是不能被删除的,隐式全局变量是可以被删除的) var num1 = 10; num = 20; delete num1; delete num2; console.log(typeof num1); // number console.log(typeof num2); // undefined 局部变量:函数内部用 var 定义的变量和形参。 变量声明提升(预解析) 作用:查看语法错误。js的解析器在页面加载的时候,首先检查页面上的语法错误。把变量声明提升起来。变量声明提升和函数整体提升。 变量的提升 只提升变量名,不提升变量值。 consolas.log(aaa);// 打印的结果是 undefined ,因为只提升变量名,不提升变量值。 var aaa = 111; 在函数范围内,照样适用。 函数的提升 function 直接定义的方法:整体提升(上面说的第一种函数定义的方法). fn(); var aaa = 111; function fn(){ //变量声明提升在函数内部照样实用。 //函数的就近原则(局部变量作用域),打印的aaa不是111,而是 undefined。 console.log(aaa); // undefined var aaa = 222; } 预解析会分块: 多对的 script 标签中函数重名的话,预解析不会冲突。也就是预解析的作用域是每一个的 script 标签。 var先提升,function再提升: 示例: console.log(a); // 输出a函数体 function a() { console.log("aaaaa"); } var a = 1; console.log(a); // 输出1 打印第一个结果的时候,var会提升,之后 function 再提升,但是函数a和变量a重名,function的a在后面覆盖掉变量a,所以第一个输出 a 函数体. 第二个前面var a = 1;提升之后,这个位置就相当于只有 a = 1; 赋值,所以第二个打印1. 匿名函数 //1.直接调用 (function (){ console.log(1); })(); //2.绑定事件 document.onclick = function () { alert(1); } //3.定时器 setInterval(function () { console.log(444); },1000);

优秀的个人博客,低调大师

JavaScript基础(五)数组

数组 数组定义 通过字面量定义数组: var arr = [10,20,30]; 通过构造函数定义数组: var arr = new Array(参数); // 参数位置为一个数值时为数组长度,多个数值时为数组中的元素。如果没有参数的时候 Array后面的括号可以省略。 数组操作 数组长度: 数组名.length; 数组中存储的数据类型一定是一样的吗? 类型可以不一样。 数组的长度是不是可以改变呢? 可以改变。 数组高级API 1、判断数组和转换数组 instanceof // 是一个关键字,判断A是否是B类型。 isArray() //HTML5中新增 ,判断是不是数组 toString() //把数组转换成字符串,每一项用,分割 valueOf() //返回数组对象本身 join(变量) //根据每个字符把数组元素连起来变成字符串变量可以有可以没有。不写默认用逗号分隔,无缝连接用空字符串。 instanceof var str1 = new String("abc"); var str2 = "abc"; console.log(str1 instanceof String); // true console.log(str2 instanceof String); // false join //join是把数组元素用特殊方式链接成字符串(参数决定用什么链接,无参默认用逗号链接) var arr = ["关羽","张飞","刘备"]; var str1 = arr.join(); var str2 = arr.join(" ");//如果用空格的话,那么元素之间会有一个空格 var str3 = arr.join("");//用空字符串,链接元素,无缝连接 var str4 = arr.join("&"); console.log(str1); console.log(str2); console.log(str3); console.log(str4); arguements 只在函数中使用,代表传入实参的数组。 arguements 是伪数组:不能修改长短的数组。(可以修改元素,但是不能变长变短) fn(1,2); fn(1,2,3); fn(1,2,3,4,5); function fn(a,b){ //只在函数中使用,实参的数组。 arguments[0] = 0; // 可以修改内容 console.log(arguments); //伪数组:不能修改长短的数组。(可以修改元素,但是不能变长变短) arguments.push(1); console.log(arguments instanceof Array); // false //形参个数 console.log(fn.length); //实参个数 console.log(arguments.length); // 形参和实参个数可以不同,因为实参传入的时候可以形参的个数不一样。 // arguments.callee相当于函数名,这里打印整个函数。 console.log(arguments.callee); } 2、数组增删和换位置(原数组讲被修改) push() //在数组最后面插入项,返回数组的长度 //数组1改后的长度 = 数组1.push(元素1); pop() //取出数组中的最后一项,返回最后一项 //被删除的元素 = 数组1.pop(); unshift() //在数组最前面插入项,返回数组的长度 //数组1改后的长度 = 数组1.unshift(元素1); shift() //取出数组中的第一个元素,返回最后一项 //被删除的元素 = 数组1.shift(); reverse() //翻转数组(原数组讲呗反转,返回值也是被反转后的数组) //反转后的数组 = 数组1.reverse(); sort(); //给数组排序,返回排序后的数组。如何排序看参数。 //从小到大排序后的数组 = 数组1.sort(function(a,b){ // return a-b; //}); sort var arr2 = [7,6,15,4,13,2,1]; console.log(arr2); // 7,6,15,4,13,2,1 console.log(arr2.sort()); // 1,13,15,2,4,6,7 问题:只能通过第一位排列。(对首字母的 unicode 编码进行排序) 解决:sort方法不稳定,设计的时候就是这么设计的,可以通过回调函数进行规则设置。 console.log(arr2); console.log(arr2.sort(function (a,b) { return a-b; //升序 //b-a:降序 })); 迭代方法 代替 for 循环。 every every()他的返回值是一个 boolean 类型值。而参数是一个回调函数。 参数有三个。名字随便起,但是表示的意思还是这样顺序的。 var arr = ["关长","张飞","赵子龙","马超","黄忠"]; // function (element,index,array) // element:数组元素的值 // index:索引 // array:调用这个方法的整个数组对象(一般不用) arr.every(function(fff,www,ggg) { console.log(fff); console.log(www); console.log(ggg); return true; }); // 关长 // 0 // ["关长", "张飞", "赵龙", "马超", "黄忠"] // 张飞 // 1 // ["关长", "张飞", "赵龙", "马超", "黄忠"] // 赵龙 // 2 // ["关长", "张飞", "赵龙", "马超", "黄忠"] // 马超 // 3 // ["关长", "张飞", "赵龙", "马超", "黄忠"] // 黄忠 // 4 // ["关长", "张飞", "赵龙", "马超", "黄忠"] var arr = ["青花瓷", "一路向北", "轨迹"]; var flag = arr.every(function (ele, index) { // 只要有一个没满足条件,就返回false ele.length > 2; }); console.log(flag); // false filter filter返回值是一个新数组。return为 true 的数组。 var arr = ["关长","张飞","赵子龙","马超","黄忠"]; var arr1 = arr.filter(function (ele,index,array) { if(ele.length>2){ return true; } return false; }); console.log(arr1); // ["赵子龙"] foreach foreach遍历数组(无返回值,纯操作数组中的元素) var arr = ["关长","张飞","赵子龙","马超","黄忠"]; var str = ""; arr.forEach(function (ele,index,array) { str+=ele; }); console.log(str); // 关长张飞赵子龙马超黄忠 map map有返回值,返回什么都添加到新数组中。 var arr = ["关长","张飞","赵子龙","马超","黄忠"]; var arr2 = arr.map(function (ele,index,array) { return ele+"你好"; }) console.log(arr2); // (5) ["关长你好", "张飞你好", "赵子龙你好", "马超你好", "黄忠你好"] some some有返回值,如果函数结果有一个是true,那么some方法结果也是true。 var arr = ["关长","张飞","赵子龙","马超","黄忠"]; var flag = arr.some(function (ele,index,array) { if(ele.length>2){ return true; } return false; }) console.log(flag); // true push 向数组的末尾添加一个或更多元素,并返回新的长度。 注意: 新元素将添加在数组的末尾。 注意: 此方法改变数组的长度。 提示: 在数组起始位置添加元素请使用 unshift() 方法。 pop 删除数组的最后一个元素并返回删除的元素。 注意:此方法改变数组的长度! 提示: 移除数组第一个元素,请使用 shift() 方法。 了解方法 concat() //把参数拼接到当前数组 //新数组 = 数组1.concat(数组2); slice() //从当前数组中截取一个新的数组,不影响原来的数组,参数start从0开始,end从1开始 //新数组 = 数组1.slice(索引1,索引2); splice()//删除或替换当前数组的某些项目,参数start,deleteCount,options(要替换的项目) //新数组 = 数组1.splice(起始索引,结束索引,替换内容); indexOf()、lastIndexOf() //如果没找到返回-1 //索引值 = 数组.indexOf/lastIndexOf(数组中的元素); forEach() // 数组每个元素都执行一次回调函数。 清空数组 var array = [1,2,3,4,5,6]; // 方法一:删除数组中所有项目 array.splice(0,array.length); // 方法二:length属性可以赋值,其它语言中length是只读 array.length = 0; // 方法三: array = []; //推荐

优秀的个人博客,低调大师

python基础知识

1、print()函数print函数用于向控制台、python解释器、cmd命令行输出你想要输出的内容。print函数有输入参数,可以是字符串、数字、变量等等,也可以是它们的混合,不同对象之间可以使用,来分隔,print函数遇到,的时候会输出一个空格来作为分隔输出的效果。注意:在print函数中,如果输入的内容是可以继续计算或者合并的,那么python会先计算或者合并之后再输出。如:print(100+200)print('a'+'b')会输出数字300以及字符串'ab'2、input()函数用于从命令行、python解释器获取用户的输入,从subline text中无法输入。用户的所有输入都会被当做是str类型,即字符串类型,用户可以将input的输入内容放入变量中,即:name=input()用于保存输入的内容用作后续代码的指代。同时,为了更友好的界面,可以给input函数输入参数值,参数值可以是一个字符串,也可以是一个变量的指代,用于在提示用户输入的时候给出详细的说明,即:name=input('inpurt your name:')或者i='please input here:' , name=input(变量i)。3、组织方式python的组织方式是通过统一缩进,主旨是简洁优雅。4、数据类型和变量====整数和浮点数:整数分为正整数、负整数、0浮点数有精度问题,很大的浮点数可以用科学计数法标记,其中的10使用e来表示,即:12300000 = 1.23e7 ,或者类似2.35e-5等等====字符串:字符串,顾名思义,是一串字符序列,使用单引号或者双引号包括起来,外围的单引号或者双引号不算字符串的内容。如果字符串内容有单引号,那么外围要使用双引号包围,反之亦然,如果字符串内容中既有单引号又有双引号,则要使用\来转义其中一个引号如:' i\'am a "good" man ' ,使用\'来表示转义单引号====字符串+转义字符:在print函数中无法通过回车来体现最终打印字符串的换行,必须使用\n来表示换行,使用\t表示制表符,\单引号或者双引号,\\表示\符号注意:默认情况下,一个字符串中的\转义字符是会转义的,转义规则根据上述说明。但是如果在字符串前面加上r,则表示不对字符串内容做转义如:默认转义,print('line1\'line2')---->line1'line2强制不转义,print(r'line1\'line2')----->line1\'line2(可用于re的过滤规则中)这种情况下,不会转义:print('line1\\'+'nline2')------>line1\nline2====字符串+''''''实现简化换行:如果要输出一个段落,涉及字符串内部的多处换行,可以使用\n来实现,也可以使用print(''' ''')来实现,里面的字符串根据实际内容直接回车如:print('''这里是根据传入的页数,形成了所有的链接这里是server端这是一张新图片,准备下载''')====布尔值:True和False,用于各种判断,意味着一件事件为真或者为假,如3>2为真,2==0为假逻辑运算and,or,not,逻辑运算会与布尔值结合计算,如True and True 的结果为True注意:在python中,1可以被认为是True,0被认为是False(可以计算),其他值即不是True也不是False注意is是表示两边是同一个对象====空值:python中的空值使用None表示,注意,None不是0,对于0而言是有意义的,而None是一种特殊的属性,意味着空====变量:python支持多种数据类型,有内置原生数据类型,也有自定义扩展数据类型,任何类型的数据都可以看成一个对象,通过变量来指向它,对变量进行赋值就是让某一个变量指向某一个对象,建立他们两之间的关系。变量类似一个指针,指向某一个内存对象,python是动态语言,所以可以指向不同类型的对象,如str,int,float,list等等,java是静态语言一旦int a,那么a只能指向ini类型对象a='hello' , b=a ,意味着把a指向的对象的地址赋给b,或者说,让b指向a所指向的对象,这时候'hello'对象有两个指向自己的指针。如果此时a='world',那么print(b)还是等于'hello'====常量:约定俗成的使用全部大写变量名来表示常量,如PI对于除法/,python3中是不会取整的,python2是取整的对于地板除//,python3和2都是取整的使用%获取余数5、字符串编码问题====历史:在计算机历史上,计算机被设计成拥有处理数字的功能,计算机底层1个字节定义成8个位,1个字节可以表示的数字最大是255,如果需要表示更大的数字,就需要更多的字节,2个字节可以表示最大65535,4个字节可以表示42亿左右。根据计算机的原本设计是只能处理数字,如果要处理文本,即处理字符串,则需要把字符串与数字进行映射,即通过编码的方式记录每一个字符对应的数字。计算机是美国人发明的,所以当时只需要处理英文字符串,所有英文字符,包括字母大小写、数字、符号等等加在一起一共形成了127个字符,这127个字符通过1个字节就可以完全表示。通过1个字节对应的1个整数,映射到不同的字符,这种编码方式就是ASCII编码方式。随着计算机的发展,不同国家都在使用,都想通过计算机输入本国的语言,如中文,对于中文而言,中文的字符至少需要2个字节即至少需要65535个数字才能表示,所以中国为中文定制了GB2312编码方式,用于将中文编码到计算机的底层整数。同样的,不同国家有不同的编码方式,这就导致:同样的1个编码整数可能会在不同国家有不同的表示,而且,如果多个国家的程序放在一起,计算机就无法识别某一个字符串是属于英文还是中文还是其他国家的字符。为了解决不同国家定制不同的编码标准,出现了unicode统一字符编码标准,unicode标准常常使用2个字节表示1个字符,将世界上所有的语言都包含到这个编码标准中,这样不论计算机在哪里使用,不论计算机使用的是哪种语言,都不会出现混乱和冲突的问题。对于一些生僻的字符,unicode可能需要使用4-6个字符来表示。而如果单纯只使用unicode编码的时候,会出现空间浪费的问题,如果程序全部使用英文编写,所有英文字符默认2个unicode字节对应1个英文字符。所以使用unicode虽然可以很好的解决不同语言之间互通的问题,但是会带来空间浪费和效率不高的问题。这时候就需要使用UTF-8可变长编码方式,使用UTF-8的时候,如果是中文就是2个字节对应1个中文字符,如果是英文就是1个字节对应1个英文字符,即UTF-8的字节使用数量是根据实际使用的语言来灵活变化的,这种好处尤其体现在数据的保存、压缩、传输上。所以现在的计算机保存的数据都是UTF-8编码方式,一旦被程序调用的时候,文件中的数据则转换成unicode编码方式在内存中被处理,在处理完毕再次保存之前又被转换成高效的UTF-8编码方式。我们从网站上获取网页的时候,网页html是以源代码的形式传回给我们的浏览器,源代码中经常有:<meta charset="utf-8" /> 表示该网页使用UTF-8编码的方式来传输数据。====python字符串的编码:python3版本中,所有的字符串str类型对象都是使用unicode编码方式,即python中的所有字符串,都有对应的unicode整数,如:unicode是针对每一个字符做编码的,可以通过ord()函数对任意一个字符求出对应的unicode整数,也可以通过chr()输入unicode整数来得到对应的unicode字符当然,也可以这样写:'\uxxxx'其中要放入unicode整数的十六进制数,如,25991的十六进制数是0x6587:====编码转换:python在内存中的数据都是unicode编码,都是str类型的对象。而数据在保存或者网络传输的时候,必须转换成byte类型的字节流的形式,把unicode转换成byte类型需要依据不同的内容做不同的编码,所有byte类型的对象都是b开头,如:'abc'是str类型的对象,是unicode编码,是英文,所以转换成可传输的字节流就是'abc'.encode('ascii')---->b'abc',此时ascii编码中,1个字节对应1个英文字符'中文'是str类型的对象,是unicode编码,是中文,所以转换成可传输的字节流就是'中文'.encode('utf-8')---->b'\xe4\xb8\xad\xe6\x96\x87',此时utf-8编码中,使用3个字节表示1个中文字符------------------------------------------------------------------------------------------------------'中文'.encode('gb2312')---->b'\xd6\xd0\xce\xc4' ,此时gb2312编码中,使用2个字符表示1个中文字符总结:在计算机内存中,在python3中,所有str对象都是unicode编码,这是为了使不同国家语言可以互通,unicode编码一般是2个字节编码1个字符。而在传输、保存数据的时候,需要把unicode编码的字符转换成其他的编码方式,英文可以转换成ascii或者utf-8,中文可以转换成gb2312或者utf-8,变成可传输的字节流。使用不同的编码方式,1个字符使用的字节数量不同。一般情况下,我们都使用unicode和utf-8这两种,因为中文和英文都可以使用utf-8方式编码。即:str对象.encode('utf-8')可以变成字节流byte类型对象以b开头, byte对象.decode('utf-8')可以变成unicode字符的str类型对象用于计算机和程序的处理,所以在hello.py文件的开头都有:#coding:utf-8就是告诉python解释器:使用utf-8编码方式来decode到unicode来处理,然后将unicode的数据encode到utf-8来保存和传输====python的格式化字符串:last=72today=85rate=(today-last)/lastprint('小明的成绩从去年的%d分提升到了今年的%d分,增长百分比是:%.1f%%' %(last,today,rate))6、list和tuple====list的性质:list是python的内置数据类型,翻译成列表或者数组。list是一组数据或者一组对象或者一组元素的序列,同样的元素不同的排列是不同的列表。列表的元素数量可以通过len(a)来获得,list是可变长数组,可以随时通过append在末尾增加元素,通过pop(i)弹出位置i的元素默认是末尾,通过insert(i,xx)在位置i插入xx元素。除此之外,list还是混合类型数组,即数组内部可以允许不同类型的元素,如:a=[1,True,'abc',2.2],此时的a是一个指向一个list地址的变量(性质上是指针)。list通过下标0~len-1的范围来获取每一个元素。list可以通过-1,-2来从尾部开始获取元素,-1表示倒数第1个元素,-2表示倒数第2个元素,以此类推。list中可以放任何类型的对象,除了python内置的整数、浮点数、布尔值、字符串等,还可以是list,tuple,dict,其他自定义数据类型等等。如果list中还有一个list,则意味着内部的list是一个二维数组,如果内部list的一个对象还是一个list,则最内部list是三维数组,以此类推,通过a[1][2]来获取二维数组的元素。====tuple的性质:tuple的性质和list很相似,最大的不同在于tuple更严格、更安全、数据只能取不能增删改,即tuple一旦初始化后则无法改变内容,而list可以随时增删改元素。====注意:二义性的tuple符号:list在创建的时候使用[],tuple在创建的时候使用(),如果是创建一个空list则是a=[],如果是创建空tuple则是a=(),如果创建一个只含有1个元素的tuple,不应该写成a=(1)这会被python解释成a=1,a是一个int类型的对象因为python遇到()的时候有二义性,到底是tuple还是普通的小括号,python规定当做小括号使用,所以声明一个只有1个元素的tuple应该是:a=(1,)注意不要缺少这个逗号。====注意:“可变的”tupletuple的不可修改指的是每一个tuple中的元素所指向的对象都不发生地址变化,但是所指向的对象的内容却可以改变,如:a=('a','b',['A','B'])a[2][0]='X'a[2][1]='Y'print(a) -- > ('a','b',['X','Y'])这种修改是合法的,因为对于tuple a而言,a[2]这个元素地址并没有发生改变,即并没有指向其他的list,而这个未改变地址的list的内容发生改变并不影响tuple的规定。7、条件判断if,elif,else判断语句,用于分支判断,if可以理解成判断后面的语句是否为真,如果是真则执行if语句块,如果不为真就一定为假,则执行else语句块,使用elif可以增加多个判断条件。连用多个if和elif的判断逻辑类似于ACL访问控制列表,判断顺序也是自上而下,一条没有满足即顺延到下一条判断条件,一旦某一条判断条件是真,则不再进行后续判断,如果所有if和elif都判断为假,则用最后的else作为permit any含义。注意条件判断后面不要遗漏:冒号,条件判断还可以简写成if x: 这个x只要是非零整数、非空列表、非空tuple、非空字符串等等,都认为是True,否则认为是False。input()函数用于获取用户输入,无论用户输入的是数字、字符、还是字符串等等,都被input函数以str的类型返回。可以通过eval()函数获取一个字符串的值,如:'2'就是2,'a'会得到变量a8、循环python中的循环有两类====for循环:for循环用于遍历一个序列,将序列中的每一个元素赋值给自定义的变量以便操作序列中的所有元素值,如:注意:一般会使用list(range(10))来构造一个10个元素的列表,因为从0开始所以是0~9的10个元素。for循环的循环执行依赖于明确的范围遍历(无法依赖条件判断)====while循环:while循环的执行依赖于while关键字后面的条件判断,当为真的时候执行循环体,执行完毕后再次条件判断,为假的时候跳出循环,所以循环体内部需要有一定的限制条件或者手段来保证有限次的循环后可以跳出,即while循环的执行依赖于条件判断+循环体内部限制条件注意:跳出while循环的时候,一定是第一次不满足循环条件判断的时候====break:在循环体内部使用break可以跳出当前一层循环,用于特殊、有需要的程序执行分支流程====continue:在循环体内部使用continue可以省略剩余的循环体语句并立即开始下一次循环,如果是for就中断并提前遍历下一个元素,如果是while就中断并提前执行下一次条件判断注意:大量的、不加规划的使用break和continue的确会导致程序的逻辑、执行流程混乱,加重代码的复杂度以及后期debug的工作量。另外,就像无限迭代会导致机器崩溃一样,无限循环也会导致机器崩溃,所以编写循环首要注意的就是是否可以在必要的时候跳出循环体,尤其是while循环体中是否有足够的限制条件和手段。9、dict和set就像list和tuple非常的类似,并且都是python的内置数据类型一样。dict和set也是python的内置数据类型,也一样非常类似。====dict:dict在python中称为字典,是一种key-value对,其实类似于哈希表,即一个输入(key),有着唯一的输出(value),中间通过hash算法来完成唯一的映射。dict使用{}来表示,里面填写每一个key-value对使用逗号分隔。dict中的key就是哈希表中的输入或者关键字,dict使用哈希算法将key映射成唯一的内存地址,该内存地址上可以保存其他对象即value,所以key被要求不可变(即一旦初始化即不可再改变),如整数、字符串、元祖tuple等等,而value的值却是可变的,但若value变化多次则会以最后一次的值为准。在dict中,key是没有排序的,即无法使用dict[0]这种类似list和tuple的方式来获取第1个元素,dict只能使用d[key名称]的形式来获取这个key所对应的value。dict使用d[key名称]=value的方式来为一个dict新增键值对,也可以使用del d[key]的方式删除一个键值对,或者是d.pop(key)的方式弹出一个键值对,dict使用d[key名称]的方式来访问这个key对应的value,如果没有该key则会报错,所以访问方式还可以是:key in d 使用布尔判断是否含有此key,或者是d.get(key,-1)获取d的key的value,如果没有key则返回-1,默认返回None无法在python解释器中显示结果。遇到任何有联系的两个对象,都可以使其一成为key,另一个成为value放入dict中,在dict中的查找速度是非常快的基本等于O(1)。====set:set在python中称为集合,更确切的说,就是一些不重复的对象的集合,之所以不说是序列,是因为set是无序的,即无法使用s[0]这种序列访问方式来访问,就像dict一样,也是无序的。同时,set的构造使用{}来表示,和dict一样,但是set的对象并不是dict那样的键值对,事实上,set的对象只有key而没有value。可以使用s=set(某个序列)函数来构造一个集合,输入需要是一个序列,可以是list、tuple、range(10)等。set最重要的特性是:1、set中对象无重复2、set作为集合,可用于数学上的集合运算,如交集&,并集|交集:取s1和s2中共有的部分作为结果,即交集是s1的子集,也是s2的子集并集:将s1和s2所有元素整合并消除重复,即s1是并集的子集,s2页是并集的子集3、set可以增加或者减少对象,s.add(100), s.remove(2)如果新增多个重复的对象,set会自动过滤====注意:关于不可变对象:首先,对象不可变指的是某个对象一旦被构造并被初始化值之后,就不能再改变这个对象的地址以及这个对象的值,如:整数、字符串、tuple、dict的key等等。其次,不可变对象的一些函数如果涉及到需要改变对象的值,则一般会复制一份同值的对象并返回,而原来的不可变对象还是没有改变值,以此来保证对象的不可变性。对于可变对象,对象的函数如果涉及改变对象的值,则直接在原有对象上进行改变。如:因为str1不可变,所以replace涉及修改值的时候返回了另一个对象因为list1可变,所以直接在list1对象上直接修改值,并不返回另一个对象

优秀的个人博客,低调大师

JVM调优基础

1. JVM命令功能 jcmd help jhat: 读取内存堆转存 jmap: 提供转存和内存使用信息 jinfo:查看jvm的系统属性,也可以是设置系统属性,可用于脚本 jstack: 转存java进程信息,可用于脚本 jstat: 提供GC和类装载活动的信息,可用于脚本 jvisualvm: 可视化工具 jstat -J-Djstat.showUnspported=true -snap 5376 -- 获得监控信息 -XX:ReservedCodeCacheSize=N 标志可以设置代码缓存最大值-XX:InitialCodeCacheSize=N 代码缓存从初始大小开始分配 1.1 常用信息 1.错误信息:C: C帧, j: java帧 V: VM帧 v:VM生成的stub帧,J:其它帧,含java帧(编译) 2: TPS:每秒事物数,比如执行了dml操作,那么相应的tps会增加 RPS: 每秒请求数,并发数/平均响应时间 OPS:每秒操作次数 QPS是指每秒内查询次数,比如执行了select操作,相应的qps会增加。 PV 是指页面被浏览的次数,比如你打开一网页,那么这个网站的pv就算加了一次; 3 vmstate 1: 单个线程,CPU内存等监控, iostat -xm 5 , nicstat 5: 监控网络信息 4 JVM内部线程状态: _thread_uninitialized _thread_new _thread_in_native _thread_in_vm _thread_in_java _thread_block _<thread_state_type>_trans 5.JVM状态 not at a safepoint: 正常执行的状态 at safepoint: 所有线程阻塞,等待VM完成专门的VM操作(VM operation) synchronizing : VM接到一个专门的VM操作请求,等待VM中所有现场阻塞 2. JVM保留内存和分配内存之间的差别: 编译是基于JVM计数器: 方法调用计数器和方法中的循环回边计数器, 当方法或循环 编译的时候,就会进入编译队列,队列有一个或多个后台线程处理.也就是异步的,并不是严格先进先出,调用计数次数多的方法有更高优先级,也就是PrintCompilation输出中ID乱序的原因. OSR(on-stack Replacement): 在循环进行的时候还能编译循环,在循环代码编译结束后,JVM会替换在栈上的代码,循环的下一次就行快的多的代码 -XX:CompliThreshold=Nclient默认1500,server默认10000,一般不用修改),这个数值是回边计数器和方法调用次数的总和 -XX:PrintCompilation(默认false):每编译一个循环和方法就输出编译内容 -XX:CICompilerCount=N :JVM 处理队列的线程总数 -XX:+BackgroundCompilation(默认为true,即异步处理,也可以改为false,一个方法适合编译就一直等到他确实被编译) jstat -compiler 8895 输出这个pid的编译过的方法,有时也会出现编译失败的方法 jstat -printcompilation 8895 1000 (每秒钟输出一次编译的方法) -XX:-Inline (默认开启,关闭的话严重影响性能)-XX:PrintInlining 生成内联信息 方法是否内联取决于: 多热和它的大小,只有字节码小于325字节才会或者小于35字节-XX:MaxFreqInlineSize=N -XX:MaxInLineSize=N 逃逸分析-XX:+DoEscapeAnalysis(默认为true) 逆优化 (意味着编译器不得不撤销之前的优化): (1) 代码状态为"made not entrant"(代码被丢弃) -原因: 可能和类与接口的工作方式有关,也可能与分层编译的实现有关 (2) "made zombie"(产生僵尸代码) client编译的结果后来server编译更优化的结果 编译级别: C1编译器有3种级别,所以总有5种编译级别 0: 解释代码 1: 简单的C1编译代码 2: 受限的C1编译代码 3: 完全的C1编译代码 4: C2编译代码 目前主流的4个GC算法: 1: Serial : -XX:+UseSerialGC 2: Throughput(Parallel): 7之后默认,使用多线程收集Eden区, -XX:+UseParalleGC -XX:+UseParallelOldGC 3: Concurrent (CMS): 修改之前UseParalleGC算法收集Eden区,改用 -XX:UseParNewGC收集Eden, -XX:+UseConcMarkSweepGC 后台线程扫描Old区的垃圾对象,停顿时间少, 问题: (1)占用CPU线程资源,(2)内存碎片化严重,最后变成Serial(间隔时间长),整理好内存之后重复之前 4: G1: -XX:+UseG1GC, 属于Concurrent收集器,老年代的收集工作由后台线程完成,大多数不需要暂停应用线程,由于老年代被划分到不同的 区域,G1收集器通过将对象从一个区域复制到另外一个区域,完成对象的清理工作,这也意味着G1实现了堆的压缩整理(至少是部分整理) System.gc() 会触发Full GC -Xms4096m -Xmx4096m 设置一样大就不需要估算堆是否需要调整大小了 -XX:NewRatio=N(默认2): 新生代与老年代的空间占用比率 Initial Yong Gen size = Initial Heap Size / (1+NewRatio) --> 默认新生代占 1/3 -XX:NewSize=N 直接设置新生代初始的大小 -XX:MaxNewSize: 设置新生代最大大小 -XX:PermSize=N, -XX:MaxPermSize=N 设置永久区的大小 -XX:MetaspaceSize=N -XX:MaxMatespaceSize=N 调整 除了SerialGC之外几乎所有的GC都是使用多线程,启动线程数由 -XX:ParalleGCThreads=N控制-XX:+UseParallelGC -XX:+UseParNewGC -XX:+UseG1GC 收集新生代空间-XX:+UseParallelOldGC 收集老年代空间估算公式: ParalleGCThreads = 8 + ((N-8)*5/8), 每超出5/8个CPU启动一个新的线程(可能会略高,需适当调整) -XX:-UseAdaptiveSizePolicy(默认开启) 可以在全局范围内关闭自适应调整功能,如果堆容量最大值和最小值设置同样的值,与此同时,新生代的最大值和最小值也设置为同样的值,自适应功能将会被关闭 -XX:PrintAdaptiveSizePolicy: 一旦发生GC会在日志中包含GC不同空间调整的信息 -verbose:gc 或 -XX:+PrintGC -XX:+PrintGCDetails(默认关闭) -XX:+PrintGCTimeStamps -XX:+PirntGCDateStamps -XX:+UseGCLogfileRotation -XX:NumberOfGCLogfiles=N -XX:GCLogfileSize=N 组合使用可以控制循环输出日志 文件大小不足8kb按照8kb算 查看日志可以使用 gchisto 或者jstat -gcutil pid 1000 每秒输出日志信息 GC回收算法 1. Throughput 收集器 回收新生代的垃圾 Minor GC 回收老年代垃圾 Full GC -XX:MaxGCPauseMillis=N 设定应用可承受最大停顿时间 -XX:GCTimeRatio=N 设置你希望应用程序在垃圾回收上花费多少时间(与应用线程的运行时间相比) 公式: ThroughputGoal=1-1/(1+GCTimeRatio) 默认是99,也就是运行程序占99%,只有1%的时间消耗在垃圾回收上 2. CMS收集器 CMS会对新生代的对象进行回收 ParNew(所有线程都会被暂停) CMS会启动一个并发线程对老年代空间进行垃圾回收,(不会内存整理) 如有必要,CMS会发起 Full GC CMS收集步骤: 1: Init-mark: 找到堆中所有垃圾回收根节点对象 2: current-mark: 和应用程序并行,标记,(可能还会产生垃圾) 3: preclean-start: 预处理,与应用程序并行 4: remark(多个阶段),不是并发,STW 5: concurrent-sweep: 与应用程序并行 6: concurrent-reset: 并发重置 CMS可能会产生的问题: concurrent mode failure:新生代发生GC,同时老年代又没有足够的空间容纳晋升的对象时,CMS GC就会退化成Full GC,所有的应用线程都会暂停,老年代对象进行回收,这个操作是单线程的,所以非常耗时 老年代有足够的空间容纳晋升的对象,但是空间非常碎片化,导致晋升失败. 这时新生代暂停了所有的应用线程,专门在老年代整理碎片内存,好消息是内存碎片整理完了,但是会有很长时间停顿,这比并发模式失效停顿的时间还长.(因为并发模式失效时只需要整理堆里无效的对象) 针对并发模式失效的调优: 增大老年代空间 以更高的频率运行后台回收线程 使用更多的后台线程 注意点:1. CMS与其他回收算法的显著不同就是:除非发生Full GC,否则CMS的新生代大小不会调整.CMS的目标就是尽量避免Full GC, 可以使用: MaxPauseMllis=N和GCTimeRatio=N来确定使用多大的堆和多大的空间 2. 给后台线程更多的运行机会: -XX:CMSInitiatingOccupancyFraction=N和-XX+UseCMSInitiatingOccupanyOnly(默认70,即CMS在老年代空间占70%时启动并发收集周期调整) 调整后台线程: -XX:ConcGCThreads=N增加后台线程,ConcGCThreads=(3+ParallelGCThreads) / 4 3. CMS不会处理永久代的垃圾,如果耗尽会产生一次Full GC来回收垃圾,可以设置: -XX:+CMSPermGenSweepingEnable(默认false)标志开启和老年代同样的回收方式,后台开启一组线程并发回收永久代中的垃圾,同时配合:+XX:CMSInitiatingPermOccupancyFrantion=N指定在永久代占用达到比例值时开启回收线程. java8 CMSPermGenSweepingEnable标志默认开启 4: 增量式CMS,(java8种不推荐使用) 当CPU资源有限,后台线程会间歇性暂停,让出一部分CPU给应用线程 开启: -XX:+CMSIncrementalMode, 设置: -XX:CMSIncrementalSaftyFactor=N,-XX:CMSIncrementalDutyCycleMin=N,-XX:CMSIncrementalPacing可以控制GC线程为应用线程让出多少CPU G1收集器 分区(Region)一般分为2048个区域,专注于垃圾最多的分区,用最少的时间回收最多的垃圾 新生代进行回收时,要么被回收,要么被晋升,新生代采用分区机制的部分原因是采用预定义的分区能够便于代的大小调整 主要包括4种操作: 1.新生代垃圾收集 2.后台收集,并发周期 3.混合式垃圾回收(不仅进行新生代的GC同时也回收部分后台扫描线程标记的分区) 4.以及必要的Full GC 操作步骤: 1:initial-mark(STW) 2: root-scan(并发) 3: conc-mark 4:remark 5: conc -clean G1注意: 1: 并发模式失效:,在G1启动标记周期,但老年代在周期完成之前就被填完,G1会放弃标记周期,(GC-concurrent-mark-abort)2:晋升失败: G1完成了标记阶段,开始启动混合式垃圾回收,清理老年代分区,不过,老年代空间在垃圾回收释放出足够内存之前就会被耗尽. 一般都是混合式回收之后就是Full GC 3:疏散失败:进行新生代垃圾回收时,S区和老年代中没有足够的空间容纳所有的幸运对象.这表明堆已经几乎用完或者碎片化了.转用Full GC,性能下降,最简单的解决办法就是增加堆 4:巨型对象分配失败也可能会导致Full GC G1 调优: G1调优的主要目标就是避免发生并发模式失败或者疏散失败 -XX:MaxGCPauseMillis=N(默认200ms,这个CMS有所不同) 1:调整G1的后台线程数,可以使用ParallelGCThreads设置运行的线程数,计算方式ConcGCThreads=(ParallelGCThreads+2)/4 这个和CMS也不同 2: -XX:InitiatingHeapOccupancyPercent=N(默认45),堆占用比例,如果设置过高会陷入Full GC泥潭中,因为并发阶段没有足够的时间在剩下的堆空间被填满之前完成垃圾回收,如果设置过小又会以超过实际的节奏在后台大量处理 3:调整G1收集器混合式垃圾收集周期:混合式垃圾收集取决于三个因素(1)有多少分区被发现大部分是垃圾对象,默认超过35%就会标记为可进行垃圾回收,实验版标志: -XX:G1MixedGCLiveThresholdPercent=N (2)G1垃圾回收分区时最大混合式GC周期数,参数 -XX:G1MixedGCCountTarget=N(默认8),减少该值可以解决晋升失败的问题(代价是混合式GC周期的停顿时间会更长) (3)GC停顿可忍受的最大时常,参数-XX:MaxGCPauseMillis 4:2种情况下会被移动到老年区: (1):S区实在太小,S区被填满之后,Eden区剩下的活跃的对象直接放到Old区,(2)在S区经历的GC周期的个数上限(Tenuring Threshold) 5: S区是新生代的一部分,跟堆内存的其他区域一样,JVM可以对他动态调节,参数: -XX:InitialSurvivorRatio=N(默认8)决定, survivor_space_size=new_size / (initial_survivor_ratio+2) JVM可以增大S区空间直至最大上限, -XX:MinSurvivorRatio(默认3)设置(注意参数在分母,名字不直观),maximum_survivor_space=new_size / (min_survivor_ratio + 2), -XX:TargetSurvivorRatio=N可以在Survivor空间调整之后能保证垃圾回收之后有50%的空间是空闲的. 6: -XX:InitialTenuringThreshold=N可以设置初始晋升阈值(Throughput和G1的默认值7,CMS默认值是7),JVM最终会在1和最大阈值(-XX:MaxTenuringThreshold=N)之间选择一个合适的值, 对于Throughput和G1,最大值默认15,CMS最大默认6) 7:-XX:PrintTenringDistribution标志可以在GC中增加这些信息 8: TLAB的大小由三个因素决定: (1)应用线程的线程数(2)Eden空间的大小(3)线程分配率,默认开启,-XX:-UseTLAB可以关闭. -XX:TLABSize=N可以显示的指定TLAB的大小(默认为0,由Eden区的大小动态计算出),这个标志只能设置TLAB的初始大小,为了避免每次GC时都调整TLAB的大小可以使用 -XX:-ResizeTLAB,TLAB调整时,最小容量可以使用:-XX:MinTLABSize=N设置(默认2kb),最大容量略小于1GB 9: -XX:+PrintTLAB输出信息 G1分区大小: size=1<<log(初始堆的大小/2048), 分区最小1MB,最大不能超过32MB, 堆的大小: 堆的大小 Region Size < 4GB 1MB 4GB - 8GB 2MB 8GB - 16GB 4MB 16GB - 32GB 8MB 32GB - 64GB 16MB > 64GB 32MB G1分区的大小可以通过-XX:G1HeapRegionSize=N设置(默认0,动态计算出),选择合适的大小,使分区数量接近于: 2048个 堆内存 1.jmap -dump:live,file=/path/heap.dump.hprof pid, jmap有live选项会在堆转储之前强制执行一次Full GC, jcmd默认就会这么做 jcmd pid GC.heap_dump /path/heap_dump.hprof -XX:+HeapDumpOnOutOfMemoryError(默认false)会在抛出OutofMemoryError时转储 -XX:HeapDumpPath=/path 指定堆转储位置,默认java_pid.hprof -XX:+HeapDumpAfterFullGC:在运行一次Full GC之后生成一个堆转储 -XX:+HeapDumpBeforeullGC:会在一次Full GC之前生成一个堆转储文件 3: Exception in thread 'main' java.lang.OutOfMemoryError: GC overhead limit exceeded:原因: (1) 花在Full GC上的时间超出了-XX:GCTimeLimit=N(默认98,也就是98%的时间花在了GC上了) (2):一次Full GC回收的内存量少于-XX:GCHeapFreeLimit=N设置的值,默认值2,即意味着如果Full GC期间释放的内存不足堆的2%,(3)上面2个条件连续5次Full GC都成立(这个值无法调整) (4):-XX:+UseGCOverHeadLimit标志的值为true(默认true) 请注意:所有四个条件都满足的时候才会报错,如果连续4次Full GC都成立,作为释放内存的最后一搏,JVM中所有的软引用都会在第五次Full GC之前被释放 减少内存使用(减少对象大小,对象延迟初始化,规范化对象) 1.对普通对象,对象头字段在32位JVM上占8字节,在64位JVM上占16字节,对于数组,对象头在32位JVM以及堆小于32GB的64位JVM上占16字节,其他情况是64字节 2.不可变对象:基本的数据类型Double,Boolean,以及一些基于数值的类型,BigDeciaml,String,像这类不可变对象的单一化表示,就被称为标准化(canonical)版本,类似有String.intern 例如: 创建一个Map来保存该对象的标准化版本,为防止内存泄漏,采用WeakHashMap class ImmutableObject{ WeakHashMap<ImmutableObject,ImmutableObject> map = new WeakHashMap<>(); public ImmutableObject canonicalVersion(ImmutableObject io){ synchronized (map){ ImmutableObject cv = map.get(io); if(cv == null){ map.put(io,io); cv = io; } return cv; } } } String提供了自己的标准化方法,intern()方法.保留字符串的表是保留在原生内存中的,他是一个大小固定的HashTable,在Java 7u40之前默认是1009个桶,之后的版本中,默认改为60013个 可以通过-XX:StringTableSize=N设置,输出信息: -XX:+PrintStringTableStatistics(默认false) 对象重用的方式: 对象池和ThreadLocals,注意:被重用的对象会在堆中停留很长时间,如果有大量对象存于堆中,那用来创建新对象的空间就少了.并且会通过Eden S0 S1最终才会进入Old区,执行一次Full GC所化的时间与老年代中仍然存活的对象数量成正比,存活对象的数量甚至比堆的大小更重要. 线程池: 线程初始化成本高 JDBC池: 数据库连接初始化的成本高 EJB池: EJB初始化成本高 大数组:java要求,一个数组在分配的时候,其中的每个元素都必须初始化为某个默认初始值(null,0,false) 原生NIO缓冲区:不管缓冲区多大,分配一个直接java.nio.Buffer(即调用allocateDirector()方法返回的缓冲区) 安全相关的类: MessageDigest,Signature以及其他安全算法的实例 字符串编解码对象,大多数都是软引用 StringBuilder协助者:BigDecimal类在计算中间结果时会重用一个StringBuilder对象 随机数生成器: Random和SecureRandom类,生成这些类代价较高 从DNS查询到的名字:网络查询代价很高 ZIP编解码器:初始化开销不是特别高,但是释放成本很高,因为这些对象要依赖对象终结操作(finalization)来确保释放掉所用的原生内存. 对象池: (1)GC影响,降低GC效率 (2)同步: 对象池必然是同步 (3)限流(Throttling):对于稀缺的资源访问,线程池可以起到限流的作用.同时也意味着,超出的部分会等待资源. 线程局部变量:(1)生命周期管理 (2)基数性(Cardinality):线程局部变量通常会伴生线程数与保存的可重用对象数之间一一对应的关系.(3)不需要同步 注意: 初始化一个Random对象的开销非常大,而且持续创建这个类的实例,与再多个线程间共享一个类实例的同步瓶颈相比,性能可能更差.使用TreadLocalRandom类性能会更好. -XX:+PrintReferenceGC(默认false),可以输出调试信息 软引用的释放: (1):所引用的对象不能有其它的强引用,如果软引用是指向其所引用对象的唯一引用,而且该引用最近没有被访问过,则所引用对象会在下一次GC周期前释放. long ms = SoftRefLRUPolicyMSPerMB * AmountOfFreeMemoryInMb if(now - last_access_to_reference > ms) free the reference (2) 堆空间空闲内存的数量,例如堆空间4GB,再一次Full GC之后,堆可能被占用了50%,因此空闲堆是2GB,SoftRefLRUPolicyMSPerMB的默认值1000,意味着 过去的 2048s内没有访问到的任何软引用都会被清理. 如果4GB的堆占了75%,则过去的1024s没有访问到的软引用会被回收. log ms = 2048000;//2048*1000 if(System.currentTimeMillis()- last_access_to_reference_in_ms ms) free the reference 注意: 对于长期运行的应用,如果满足以下2个条件,可以考虑增加SoftRefLRUPolicyMSPerMB的值: (1)有很多空闲堆可用 (2)软引用会频繁访问 ,(不过情况罕见,增加软引用的策略值就是告诉JVM不到万不得已不要释放软引用) 软引用:当问题中的所引用对象会同时被几个线程使用时,考虑弱引用,否则入弱引用很可能会被GC掉,只有弱引用的对象在每个GC周期都可以回收 原生内存的使用 1.从Java8开始: -XX:NativeMemoryTracking=off|summary|detail 标志来跟踪JVM如何分配原生内存. 也可以使用-XX:+PrintNMTStatistics(默认false)启动 ,前提需要设置-XX:+UnlockDiagnosticVMOptions 2: 开启压缩指针: -XX:+UseCompressedOops(默认启用的) Ordinary object pointer tables Area Cool cold 2 is right $1800 col is 2 left $0099

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册