首页 文章 精选 留言 我的

精选列表

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

JavaScript—Ajax基础知识梳理(29)

Ajax用一句话来说就是无须刷新页面即可从服务器取得数据。注意,虽然Ajax翻译过来叫异步JavaScript与XML,但是获得的数据不一定是XML数据,现在服务器端返回的都是JSON格式的文件。 完整的Ajax请求过程完整的Ajax请求过程 1.创建XMLHttpRequest实例2.发出HTTP请求 3.接收服务器传回的数据4.更新网页数据 下面先看一个红宝书上给出的发起Ajax请求的例子,API的用法在后面章节给出。 var xhr = new XMLHttpRequest(); // 创建XMLHttpRequest实例 xhr.onreadystatechange = function(){ if (xhr.readyState == 4){ // 判断请求响应过程阶段,4 阶段代表已接收到数据 if (xhr.status >=200 && xhr.status < 300 || xhr.status == 304) { // 校验HTTP状态码 console.log(xhr.responseText); // 输出响应的文本 } else { console.error(xhr.status, xhr.statusText); // 打印其他HTTP状态码 } } }; xhr.open('get', 'example.txt', true); // 初始化xhr实例,或者说启动请求 xhr.send(null); // 设置HTTP请求携带参数,null为不带参数 Ajax请求过程详解 创建XMLHttpRequest实例从上面的的代码可以看出,创建一个XHR实例方式为: var xhr = new XMLHttpRequest(); 发出HTTP请求实例创建好后,首先需要启动一个HTTP请求,使用XHR的open()方法,open方法接受三个参数 XMLHttpRequest.open(method, url, isAsync) // 例如 xhr.open('get', 'http://www.baidu.com', true) 第一个参数为http请求使用方法,如('get','post'等),第二是参数是请求的url, 第三个参数代表是否异步发送请求(可选)。调用open()方法后会启动一个http请求,但它不会立即发送请求,处于待命状态。需要注意的是:请求的url必须要跟请求源域(origin)同域,也就是说协议、域名、端口号要一致,跨域请求要使用别的方法。接着调用send()方法就会发出这个http请求。 xhr.open('get', 'http://www.baidu.com', true) xhr.send(null) send()方法接受一个参数,为http请求发送的数据(通常用于'post'方法),如果为null,表示不发送数据。至此,一个异步的http请求就发送到了服务器。 接收服务器传回的数据3.1 发送同步请求 如果将open方法的第三个参数设为false,即为同步请求,当收到服务器的响应后,相应的数据会自动填充到XHR对象的属性中,主要包括以下四个: responseText:作为响应主体被返回的文本。 responseXML: 响应返回的XML文档,能接收到的前提是,响应的Content-Type字段的值为text/xml或者application/xml。 status: HTTP状态码。 statusText: HTTP状态码说明。 当客户端收到以上信息后,首先要判断HTTP状态码来确认响应是否成功,状态码在200-300之间表示请求成功,同时304代表请求资源未被修改,可使用浏览器本地缓存。如果成功就可以获取响应报文主体中的数据了。 xhr.open('get', 'http://www.baidu.com', false) xhr.send(null) if (xhr.status >=200 && xhr.status < 300 || xhr.status == 304) { // 校验HTTP状态码 console.log(xhr.responseText); // 输出响应的文本 } else { console.error(xhr.status, xhr.statusText); // 打印其他HTTP状态码 } 3.2 发送异步请求如果将open方法的第三个参数设为true,即为异步请求。那么就需要一个事件来通知程序异步请求的结果是否返回。XHR对象中的readyState属性,表示请求/响应整个过程所处的阶段,它有五个值分为对应五个阶段: 0:未初始化。未调用open()方法。1:启动。已经调用open()方法,但未调用send()方法。2:发送。已调用send()方法,但未收到响应。3: 接收。已经接收到部分响应数据。4:完成。已经接受到全部响应数据。 readyState的值每变化一次,都会触发一次readStatechange事件,我们定义一个事件处理函数onreadStatechange(),并监听readyState == 4状态,就可以得知响应数据已全部收到,并进行下一步操作。那么就是文章开头给出的代码: var xhr = new XMLHttpRequest(); // 创建XMLHttpRequest实例 xhr.onreadystatechange = function(){ if (xhr.readyState == 4){ // 判断请求响应过程阶段,4 阶段代表已接收到数据 if (xhr.status >=200 && xhr.status < 300 || xhr.status == 304) { // 校验HTTP状态码 console.log(xhr.responseText); // 输出响应的文本 } else { console.error(xhr.status, xhr.statusText); // 打印其他HTTP状态码 } } }; xhr.open('get', 'example.txt', true); // 初始化xhr实例,或者说启动请求 xhr.send(null); // 设置HTTP请求携带参数,null为不带参数 补充XHR中三个有用的事件 timeout事件当超出了设置时间还未收到响应,就会触发timeout事件,进而调用ontimeout事件处理程序。同时timeout也是XHR的一个属性,用于设置这个时间阈值。下面是用法: xhr.ontimeout = function() { alert('timeout!') } xhr.open('get', 'http://www.baidu.com', true) xhr.timeout = 1000 // 时间阈值设为1秒 xhr.send(null) load事件 load事件用于简化对readState值的判断,响应数据全部接收完毕后(也就是readState == 4)会触发load事件,使用onload事件处理函数进行后续操作,onload会接收一个event对象,它的target属性等于XHR对象,当然我们在定义这个事件处理函数时也可以不传入这个参数,来看下面的用法: var xhr = new XMLHttpRequest() xhr.onload = function () { if(xhr.status >=200 && xhr.status < 300 || xhr.status == 304) { console.log(xhr.responseText); // 输出响应的文本 } else { console.error(xhr.status, xhr.statusText); // 打印其他HTTP状态码 } } xhr.open('get', 'http://www.baidu.com', true) xhr.send(null) 这样就不用去关心readyState值的变化情况了。当然如果想在特定readyState值上做一些逻辑处理,还是要用之前的方法。 progress事件这个是很有用的一个事件,progress事件会在浏览器接收数据期间周期触发,代表整个请求过程的进度,它的事件处理程序onprogress接收一个event对象,event.target是XHR对象,另外event还有三个属性: lengthComputable:Boolean值,进度信息是否可用。position:已经接收到的字节数。totalSize:总共要接收的字节数,被定义在响应报文的Content-Length字段中。 如果响应报文中有Content-Length字段,那么我们就可以计算当前时刻响应数据的加载进度了,这也是之前看到的一个面试题。看下面的代码: xhr.onprogress = function(event) { if(event.lengthComputable) { console.log(`Received: ${(event.position/event.totalSize).toFixed(4)*100}%`); } } 其他还有很多有用的API,如FormData表单序列化,overrideMimeType()重写XHR响应的MIME类型等等

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

Python基础——mysql数据库、SQLAlchemy

一、MySQL常用操作 1、创建库 create database test; 2、创建表 create table student(id int not null); 3、授权一个用户 grant all privileges on *.* to 'username'@'%' identified by 'passwd'; 其中%通配所有地址 4、查询 select * from tabel_name where 条件1 and 条件2; 5、增加数据 insert into table_name (id, name, age, sex, grander) values (1, 'jsh', 25, 'M', 99), (2, 'Tom', 45, 'F', 88); 6、删除数据 delete from table_name where 条件判断; drop table table_name; 删除表 7、改数据(更新数据) update table_name set id=10 where 条件判断; 8、联合查询 select a.id, b.name from A a join B b on a.id=b.tid; 9、创建索引 create index idx_库名_表名_列名1_列名2 或 (列名1, 列名2) 10、查看SQL是否走索引 explain select * from student where name='JSH' 二、MySQL 数据库连接 1、Python2 使用mysqldb python3 使用 pymysql 2、创建链接 和游标 在mysql连接中,尽量使用一个连接,确保mysql的并发数 conn = pymysql.connect(host='', port=, user='', passwd='', db='') # cus = conn.curse() 3、执行MySQL语句 sql = "select * from Student;" # cus.execute(sql) # cus.fetchone() 获取单个 返回值 tuple # cus.fetchall() 获取多个 返回值 list(单个元素是tuple) # cus.fetchmany(size=n) 获取多个 4、关闭游标 和 链接 注意结合try exception finally的使用 cus.close() conn.close() 三、SQLAlchemy(对象关系映射) 1、创建引擎 engine = create_engine('mysql+pymysql://root:123456@192.168.6.137/sqlalchemy',echo=True) 2、创建session DBsession = sessionmaker(bind=engine) session = DBsession() 3、创建表 a. 获得engine b. metadata = MetaData(engine) c. student = Table('表名', metadata, Colume('id', Integer, primary_key=True), Colume('name', String(50)) d. metadata.create_all() 4、增加数据 a. 先要有一个模型 Base = declarative_base(0 class Student(Base): __tablename__ = 'student' id = Column(Integer, primary_key=True) name = Column(String(100), primary_key=True) b. 导入模型类,实例化该类, sutdent1 = Student(1, 'ling') c. session.add(单实例) session.add_all([实例1, 实例2]) 5、查询 # filter和filter_by的区别 # filter:可以使用> < 等,但是列必须是: 表.列, filter的等于号是== session.query(Student).filter(Student.id>100) # filter 不支持组合查询 session.query(Student).filter(Studnet.id>100).filter(name='ling') # filter_by: 可以直接写列,不支持< > filter_by 等于是== session.query(Student).filter_by(id==10) # filter_by 可以支持组合查询 session.query(Student).filter_by(name='ling' and id='342') # 模糊查询含有ling的关键字 select * from student where name like '%JSH%'; # 模糊查询 # session.query(Student).filter(Student.name.like('%JSH%')) # 获取数据的时候有两个方法: # one() 获取到的数据为 tuple # all() 获取到的数据为 list(单个元素是tuple) # 如果在查询中不写one(), 或者all() 得到的就是sql语句 6、更新 1. 先查出来 2. 跟新一下类所对应的属性值就ok session.commit() student1 = session.query(Student).filter(Student.id=1001) student1.name = "test" session.commit() 7、删除 # 1. 先查出来 # 2. 直接调用delete()方法就可以 # 3. 提交一下 8、统计count() 分组 # 只需要在查出来以后, 把one或者all替换成count() 9、分组group_by 查出来以后,把one或者all替换成group_by(属性)

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

Bytom移动端钱包SDK开发基础

比原项目仓库: Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomBlockchain/bytom Bytom-Mobile-Wallet-SDK 是从bytom源码中抽离出的钱包层代码,并且对钱包层代码进行了改造。使用gomobile可以将代码 编译成Android和iOS平台可用的SDK,使用编译后的Android和iOS钱包SDK可以在移动端实现创建bytom密钥、账户、地址和交易签名功能。 Bytom-Mobile-Wallet-SDK源码简介 SDK源码放在项目的sdk文件夹中,android和ios文件夹是使用SDK的demo项目,bind.go 中首字母大写可以外部调用的函数会作为提供给Android和iOS调用的API。bytom创建的密钥对会存储在磁盘单独的文件中,而且对私钥进行了加密,账户地址数据是存储在go实现的leveldb中,所以Android和iOS平台也需要提供数据存储的路径。 func InitWallet(storagePath string) { hsm := pseudohsm.New(storagePath) walletDB := db.NewDB("wallet", "leveldb", storagePath) accounts := account.NewManager(walletDB) assets := asset.NewRegistry(walletDB) wallet := aWallet.NewWallet(walletDB, accounts, assets, hsm) api = aApi.API{Wallet: wallet} } Android和iOS平台调用其他钱包API的之前需要先调用InitWallet这个API,参数是磁盘上的绝对路径,InitWallet会对整个钱包进行一个初始化, 其中最重要是初始化leveldb的存储。其他的CreateKey、CreateAccount、CreateAccountReceiver是创建密钥、账户、地址等API,RestoreWallet API能够对钱包所有账户地址资产进行备份导出json格式的数据。 Bytom-Mobile-Wallet-SDK的编译 SDK代码的编译首先需要正确的安装golang和gomobile,golang需要1.7以上版本。 Android平台需要安装JDK、Android SDK、Android NDK,并且需要将Android SDK的platform-tools、ndk-bundle 添加到PATH系统环境变量中。iOS平台编译环境配置相对比较简单只需要安装Xcode就可以了。 Clone项目到本地$GOPATH/src下: git clone https://github.com/Bytom-Community/Bytom-Mobile-Wallet-SDK $GOPATH/src/github.com/bytom-community/mobile Android gomobile init -ndk ~/path/to/your/ndk cd $GOPATH/src/github.com/bytom-community/mobile gomobile bind -target=android github.com/bytom-community/mobile/sdk/ 如果需要减小SDK的体积给gomobile bind指令加上-ldflags=-s参数: gomobile bind -target=android -ldflags=-s github.com/bytom-community/mobile/sdk/ 执行指令后会在mobile文件夹生成wallet.aar和wallet-sources.jar文件。 iOS cd $GOPATH/src/github.com/bytom-community/mobile gomobile bind -target=ios github.com/bytom-community/mobile/sdk/ 如果需要减小SDK的体积给gomobile bind指令加上-ldflags=-w参数: $ gomobile bind -target=ios -ldflags=-w github.com/bytom-community/mobile/sdk/ 执行指令后会在mobile文件夹生成wallet.framework文件。 由于gomobile现在没有支持bitcode,所以生成的iOS SDK也不支持bitcode。 Bytom-Mobile-Wallet-SDK的使用 Android 拷贝wallet.aar和wallet-sources.ja到Android项目的app的libs文件夹下,并在app module中的build.gradle文件中添加: android { repositories { flatDir { dirs 'libs' } } } dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation(name: 'wallet', ext: 'aar') } sync project后可以在Android项目中对SDK的API进行调用: package io.bytom.community; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.widget.TextView; import wallet.Wallet; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView keyTextView = (TextView) findViewById(R.id.key_textview); String storagePath = getFilesDir().toString(); Log.d("storagePath", storagePath); Wallet.initWallet(storagePath); String keyResult = Wallet.createKey("Marshall", "123456"); Log.d("keyResult", keyResult); keyTextView.setText(keyResult); } } iOS 通过项目target的Linked frameworks and libraries把wallet.framework添加到项目,可以在iOS项目中对SDK的API进行调用: #import "ViewController.h" #import "Wallet/Wallet.h" // Gomobile bind generated framework @interface ViewController () @end @implementation ViewController @synthesize textLabel; - (void)loadView { [super loadView]; NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; WalletInitWallet(docPath); textLabel.text = WalletCreateKey(@"kevin",@"123456"); } @end

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

Python全栈 Web(HTML基础语法)

超链接标签: 什么是超链接标签? 能够实现从当前文件跳转到其它文件的标签 语法: <a>超链接文本</a> 属性: href: 链接地址 #表示当前页面 可以为空“ ”也表示当前但是包含了网络请求 刷新页面的效果 本地文件协议: file:/// target:设置目标文件打开方式 _self:默认当前窗口打开 _blank:新建窗口打开 锚点链接: 链接到当前文件的指定位置 语法: 设置锚点 <a name="bottom"></a> 链接锚点 <a href="#bottom">跳转</a> 表格: 语法: 表格标签: <table> </table> 属性: border:设置边框(px) width:宽度 geight:高度 align:水平对齐对齐方式 cellpadding: 设置单元格内边距(内容与边框距离)(px) cellspacing: 设置单元格外边距(单元格与单元格、表格之间的距离)(px) bgcolor: 设置表格背景颜色 取值:英文的单向 行标签: <tr> </tr> (table row) 属性: bgcolor:设置当前行背景颜色 align:设置水平对齐方式 valign:设置垂直对齐方式 top:上 middle:中 bottom:下 单元格: <td> </td> (table data) 属性: width:单元格的宽度 height:单元格高度 align:水平对齐方式 valign:垂直对齐 bgcolor:背景颜色 创建顺序: 创建表格表标签 在表格标内嵌套行标签每个tr代表一行 在标签中创建单元格用来存放数据 <table> <tr> <td>name</td> <td>age</td> <td>score</td> <td>gander</td> </tr> </table> 单元格合并: 单元格独有的属性: colspan 跨 列合并: 从当前位置开始 横向合并n个单元(包含自身) 合并后要删除当行中多余的单元格 rowspan 跨 行合并: 从当前位置开始 纵向合并n个单元格(包含自身) 合并后要删除每列中多余的单元格 行分组: 允许将表格中若干行划分为一组 便于管理 这些标签可以省略 默认所有行都会添加到tbody中 如果需要手动添加分组, 建议thead --> tfoot --> tbody顺序书写 语法: 表头行分组: <thead> <tr></tr> </thead> 表尾行分组: <tfoot> <tr></tr> </tfoot> 主体行分组: <tbody> <tr> </tr> </tbody> 表单: 表单二要素: 1.form表单元素: 收集用户信息并发送给服务器 本身是不可见的 但是不能省略 因为数据的提交功能要由form元素完成 语法: <form>表单控件</form> 属性: action:用来指定数据提交的目的地址 method:数据请求方式 GET(默认)、POST... GET请求: 通常用于向服务器端获取数据 提交的数据会以参数的方式拼接在URL后面 安全性较低 提交的数据最大为 2KB POST: 将数据提交给服务器处理 隐式提交 看不到提交的数据 安全性较高 提交数据大小无限制 2.表单控件: 提供能够跟用户交互的可视化控件 只有放在表单元素中的表单控件才允许被提交 分类: 1). 文本框: 语法: <input type="text"> 密码框; 语法: <input type="password"> *属性: name: 定义当前控件的名称 缺省的话无法提交 value: 提交给服务器的值 同时也是默认显示在控件上的值 maxlength: 限时用户输入的最大字符数 placeholder: 用户输入之前显示在输入框中的提示文本 2). 单选框: <input type="radio"> 复选框: <input type="checkbox"> *属性: name: 定义控件名称,还起到分组的作用 一组的的按钮名称必须一致 value: 设置当前控件的值 最终提交给服务器 checked: 设置预选择状态 可以省略属性值或使用“checked” 3). 隐藏域: 需要用户提交给服务器却不需要呈现给用户 语法: <input type="hidden"> 属性: name:控件名称 value:控件值 文件选择框: 选择文件上传,发送给服务器 语法: <input type="file" 属性: name:定义控件名称 4). 下拉选择框: <select name="province"> <option value="山东省">山东省</option> <option values="山西省">山西省</option> </select> 使用 GET方式请求: URL后 拼接数据为 provide 5). 文本域: 语法: 支持用户输入多行文本 <textarea> </textarea> 属性: name:控件名 cols:指定文本域默认显示的列数 一行中文显示的英文量 减半 rows:文本域能够显示的函数 文本域可以由用户调整大小 6). 按钮: 提交按钮: <input type="submit"> 将表单数据发送给服务器 重置按钮: <input type="reset"> 重置表单 将表单内容初始化 普通按钮: <input type="button" value="点击"> 绑定自定义按钮事件 value:显示按钮的显示值 按钮标签: <button>按钮提示文本</button> 可以在任何局限中使用 不局限于form 按钮标签使用在 form中默认具有提交功能 --- input submit 可以添加属性type:submit、reset、button 在 表单外作为普通按钮需要通过js动态绑定事件实现 7). label for ID 将文字和单选框之类的小按钮合并 点击文字也能选择 语法: <label for="表单控件的ID"> </label> <input type="radio" name="gender" value="male" id="male">

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

多线程基础篇(3)——初试锁

1. 锁的概念 锁是用来控制多个线程访问共享资源的方式,一般来说,一个锁能够防止多个线程同时访问共享资源(但是有些锁可以允许多个线程并发的访问共享资源,比如读写锁)。当一个资源被一个线程操作时,会对该资源加上锁,在锁未被释放期间,其他进行操作的线程都会陷入阻塞。 2. 为什么需要锁 当多个线程对同一个资源进行访问时,就会出现线程安全问题,就比如,你坐在桌子边手上拿着筷子正要去夹取最后一块食物时,突然被旁边的人夹走了食物,这时你就已经无法再进行操作,筷子就相当于CPU时间片,食物就相当于共享资源,所以此时程序就会发生一些无法预料的异常。 以下列购票程序为例,若ticket=1时,当线程t0执行到切换点时,失去CPU时间片切换到t1,但t1执行到切换点时也失去了CPU时间片,切换到t2线程顺利运行,ticket=0,t2运行完毕后,又切回t0继续运行,ticket=-1,又切到t1线程,ticket=-2,最后的结果会使得ticket数量出现负数,这显然是错误的。虽然这并不一定会发生,但一定有可能发生,所以线程安全也叫线程隐患。 public class Demo6 { //线程安全例子,以售票为例 public static void main(String[] args) { sell s=new sell(); Thread t0=new Thread(s, "线程1"); Thread t1=new Thread(s, "线程2"); Thread t2=new Thread(s, "线程3"); t0.start(); t1.start(); t2.start(); } } class sell implements Runnable{ private int ticket=100; @Override public void run() { // TODO Auto-generated method stub while(true){ if(ticket>0){ //切换点 System.out.println("当前"+Thread.currentThread().getName()+"以出售1张票,"+"剩余"+(--ticket)); }else{ break; } } } } 3.锁的实现 3.1 synchronized 代码形式为:synchronized(obj){ do work },上述购票代码则可以改成 public class Demo11 { public static void main(String[] args) { TicketSell t=new TicketSell(); Thread t1=new Thread(new Run(t),"线程1"); Thread t2=new Thread(new Run(t),"线程2"); Thread t3=new Thread(new Run(t),"线程3"); t1.start(); t2.start(); t3.start(); } } class TicketSell{ private Integer num=100; public void sell(){ num--; } public int getNum(){ return num; } } class Run implements Runnable{ private TicketSell t; //private Object obj;//也可以通过这个对象来获取锁 public Run(TicketSell t){ this.t=t; } @Override public void run() { while (true) { // synchronized (obj) synchronized (t) { if (t.getNum() > 0) { t.sell(); System.out.println(Thread.currentThread().getName() + "出售一张"); System.out.println("当前剩余" + t.getNum()); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } else { break; } } } } } synchronized(obj)中的obj可以为任意对象,但必须是一个对象而不是基本类型数据,obj被称为同步锁,用术语说应该是对象监视器。但是我们可以将共享资源封装为一个对象,然后通过该对象来获取锁,在同步代码块中通过调用方法来访问资源对象。 3)同步方法: 只需在方法返回类型前加上synchronized即可,同步方法中的锁时当前实例对象的锁,也就是this。然而对于静态同步方法,锁是当前类的Class对象的锁,若一个线程任务调用此方法则另一个线程不能调用同为该类的其他静态同步方法,静态同步方法的锁只能来自于所在类的Class对象,即只能为静态同步方法或同步代码块synchronized(类名.class){ do work }。 public class Demo12 { public static void main(String[] args) { TicketSell1 t=new TicketSell1(); Thread t1=new Thread(new Run1(t),"线程1"); Thread t2=new Thread(new Run1(t),"线程2"); Thread t3=new Thread(new Run1(t),"线程3"); t1.start(); t2.start(); t3.start(); } } class TicketSell1{ private Integer num=100; public synchronized void sell(){ if(num>0){ num--; System.out.println(Thread.currentThread().getName() + "出售一张"); System.out.println("当前剩余" + num); try { Thread.sleep(100);// 可以增大线程切换的概率 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public synchronized int getNum(){ return num; } } class Run1 implements Runnable{ private TicketSell1 t; public Run1(TicketSell1 t){ this.t=t; } @Override public void run() { while (true) { t.sell(); } } } 注意:对于需要加锁的对象,其必须是包装类型不能为基本类型,因为我们需要通过该对象来获取它所持有的锁,且应该设置为private,因为锁无法阻止线程任务直接通过访问域对象来修改值。 3.2 Lock接口显式锁 它提供了与synchronized关键字类似的同步功 能,只是在使用时需要显式地获取和释放锁。虽然它缺少了(通过synchronized块或者方法所提供的)隐式获取释放锁的便捷性,但是却拥有了锁获取与释放的可操作性、可中断的获取锁以 及超时获取锁等多种synchronized关键字所不具备的同步特性。 1)lock锁的使用方式: public class Demo13 { public static void main(String[] args) { TicketSell2 t=new TicketSell2(); Thread t1=new Thread(new Run2(t),"线程1"); Thread t2=new Thread(new Run2(t),"线程2"); Thread t3=new Thread(new Run2(t),"线程3"); t1.start(); t2.start(); t3.start(); } } class TicketSell2{ private Integer num=100; public void sell(){ num--; } public int getNum(){ return num; } } class Run2 implements Runnable{ private TicketSell2 t; private Lock lock=new ReentrantLock(); private Condition con1=lock.newCondition(); public Run2(TicketSell2 t){ this.t=t; } @Override public void run() { while (true) { lock.lock(); try { while (t.getNum()<=0) { con1.await();// 调用此方法阻塞当前线程,并且释放锁,便可以让另一个线程来对票数进行补充 } t.sell(); System.out.println(Thread.currentThread().getName() + "出售一张"); System.out.println("当前剩余" + t.getNum()); Thread.sleep(100);// 可以增大线程切换的概率 con1.signalAll();//若票数充足则唤醒所有的被阻塞线程 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { lock.unlock(); } } } } 在finally中释放锁是为了保证在获取锁以后能够释放锁,也不要将获取锁的过程写在try块中,因为如果在获取锁(自定义锁的实现)时发生了异常,异常抛出的同时,也会导致锁无故释放,必须保证return语句发生在try子句中,确保unlock()不会过早发生,将数据暴露给第二个任务。 2)Lock提供了一些synchronized所不具备的特性: 3)相关API: 4)条件对象: Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); public void conditionWait() throws InterruptedException { lock.lock(); try { condition.await(); } finally { lock.unlock(); } } public void conditionSignal() throws InterruptedException { lock.lock(); try { condition.signal(); } finally { lock.unlock(); } } 条件对象通过Lock对象的newCondition()方法获得,当线程A中的条件对象调用await()方法时,他会进入该条件的等待集,即使其他线程已经释放了锁,他仍然会处于阻塞状态,直到某个线程使用同一个条件对象进行了signalAll()操作(signal()方法也可以,但是这个方法是随机解除等待集中的某一个线程的阻塞),使得线程A脱离阻塞状态,但并不一定会立即运行,只有他再次获得锁之后才能继续从上次运行的地点继续运行。但这也可能会带来一个问题,那就是死锁,因为线程A依赖于其他线程来唤醒,如果没有线程来进行唤醒就会造成死锁。Condition的详细API 3.3 线程本地存储 1)概念:线程本地存储是一种自动化机制,它的原理是通过根除对变量的共享,为使用相同变量的每个不同线程都创建不同的存储。例如,有5个线程都要使用变量x所代表的对象,那么本地存储会生成5个用于x的不同存储块。ThreadLocal,即线程变量,是一个以ThreadLocal对象为键、任意对象为值的存储结构。这个结构被附带在线程上,也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个线程上的一个值。通过这些值我们可以看做线程的一种状态表现,他是每个线程所独享的,不受其他线程影响。 public class ThreadLocalHolder { private static final ThreadLocal<Long> time = new ThreadLocal<Long>() { protected Long initialValue() { return System.currentTimeMillis(); } }; public static final void begin() { time.set(System.currentTimeMillis()); } public static final long end() { return System.currentTimeMillis() - time.get(); } public static void main(String[] args) { ThreadRunTime t1=new ThreadRunTime(); ThreadRunTime t2=new ThreadRunTime(); t1.start(); t1.start(); } } class ThreadRunTime extends Thread{ @Override public void run() { try { ThreadLocalHolder.begin(); Thread.sleep(1000); Thread.yield(); System.out.println(getName()+ThreadLocalHolder.end()); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } 那么如果不使用线程变量会如何?如果不使用线程变量也就是直接将time变量的类型设为Long,如果在线程休眠期间或者线程切使t1线程切换到了t2线程运行,t2运行完毕再返回t1线程时,调用ThreadLocalHolder.end()所获取的就是线程t2的运行时间。因此,从另一种角度来说通过线程变量ThreadLocal也避免了对共享资源的竞争,但是这种方法却无法实现同步,所以我们可以将ThreadLocal中所包含的对象视作线程的状态。 3.4 死锁,活锁,饥饿 理解死锁与活锁只需抓住两点: 1)死锁是两个线程都持有对方锁所需要的锁且永不释放,都等待着对方释放锁,也就是互不让步。比如线程1已经持有A锁,需要B锁才能继续运行,而线程2持有B锁,需要A锁才能继续运行,然而线程1不会释放A锁,线程2不会释放B锁,导致线程陷入无限的等待,就会导致死锁。避免死锁的方法: 避免一个线程同时获取多个锁。 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源。 尝试使用定时锁,使用lock.tryLock(timeout)来替代使用内部锁机制。 对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况。 2)活锁是指线程1可以使用资源,但它很礼貌,让其他线程先使用资源,线程2也可以使用资源,但它很绅士,也让其他线程先使用资源。这样你让我,我让你,最后两个线程都无法使用资源,也就是互相让步。 3)饥饿是指线程的CPU时间片被其他线程完全抢占了所有CPU时间片而导致的无法运行,原因有 高优先级线程吞噬所有的低优先级线程的CPU时间。 线程被永久堵塞在一个等待进入同步块的状态。 线程在等待一个本身也处于永久等待完成的对象(比如调用这个对象的wait方法)。 3.5 总结 Java中的锁可以避免多线程对同一资源竞争所引起的线程安全问题,并实现同步。必须记住, Java中的锁都是来自于对象,synchronized同步代码块必须给定一个需要进行同步的对象,也就是共享资源对象,而synchronized同步方法实际上相当于synchronized(this)形式的同步代码块,Lock显示锁也类似于synchronized同步代码块,不过Lock显示锁直接通过自身来获取锁,并且比synchronized多了一些特性。

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

多线程基础篇(2)——理解中断

1.何谓线程中断 线程中断,可以理解为一个现成的标识属性,它表示一个运行中的线程是否被其他线程进行了中断操作,中断可以用来进行强制终结线程(使线程进入终止状态),即在执行run方法过程中直接退出(或者说跳过某段代码)。 线程中断的方法: 1)stop()方法:不在使用此方法,其中断线程是立即中断的,即使是在同步代码块中对数据进行操作时也会立即终止,详细不在赘述,只需知道该方法不在使用即可。 2)interrupt()方法:其他线程调用此线程的interrupt()方法对其进行中断操作时,interrupt()方法将线程的中断状态设置为true,如果该线程正处于阻塞或即将处于阻塞状态,则会发生InterruptedException并被抛出,当抛出该异常或使用interrupted()方法判断是否中断时将会重置中断状态为false。 1.1 详解interrupt()方法实现线程中断操作(阻塞线程中断) 1) 当其他线程调用某线程的interrupt()方法时,仅仅时将该线程的中断状态设置为true,并不是一定会立即将该线程中断使其进入终止状态,相当于其他线程对该线程说“兄弟,你要结束了”。如果这个线程处于阻塞或是即将进入阻塞状态时则会发生中断,也可以设置一些中断条件,通过与isInterrupted()方法进行&&操作实现中断。代码如下: public class Demo8 extends Thread{ public static void main(String[] args) { Demo8 itt = new Demo8(); itt.start(); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } itt.interrupt(); } public void run() { // 这里调用的是非清除中断标志位的isInterrupted方法 while(!Thread.currentThread().isInterrupted()) { System.out.println(Thread.currentThread().getName() + " 正在运行"); try { System.out.println(Thread.currentThread().getName() + "线程开始阻塞"); Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + " 线程阻塞结束"); } catch (InterruptedException e) { // TODO Auto-generated catch block //由于调用sleep()方法清除状态标志位 所以这里需要再次重置中断标志位 否则线程会继续运行下去 Thread.currentThread().interrupt(); e.printStackTrace(); } } System.out.println("跳出循环"); if (Thread.currentThread().isInterrupted()) { System.out.println(Thread.currentThread().getName() + "线程被中断"); } } } 2)有两种情况无法发生中断:不能中断正在获取synchronized锁或者试图执行IO操作的线程,代码如下 public class Demo9 { public static void main(String[] args) throws Exception { InputStream in=System.in;//不会被中断 Thread t1=new Thread(new IOBlocked(in)); Thread t2=new Thread(new SynchronizedBlocked()); t1.start(); t2.start(); try { Thread.sleep(2000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } t1.interrupt(); t2.interrupt(); } } class IOBlocked implements Runnable{ private InputStream in; public IOBlocked(InputStream is){ in=is; } public void run(){ try{ while(true){ System.out.println("等待进行io操作"); in.read();//试图进行IO操作 } }catch (IOException e) { if(Thread.currentThread().isInterrupted()){ System.out.println("IO thread is Interrupted"); }else{ throw new RuntimeException(e); } } System.out.println("io操作运行中"); } } class SynchronizedBlocked implements Runnable{ public synchronized void f(){ //永不释放锁 while(true){ Thread.yield(); } } public SynchronizedBlocked(){ new Thread(){ public void run(){ SynchronizedBlocked.this.f();//在此线程中获取锁,并且不进行释放 } }.start(); } @Override public void run() { // TODO Auto-generated method stub System.out.println("尝试调用f()"); f();//在此处尝试获取锁 System.out.println("f()运行中"); } } 3)对于无法中断的IO操作,我们可以通过关闭任务中发生阻塞的底层资源,如上代码中的System.in可以通过关闭流的方式来进行中断。 4)对于无法中断互斥所引起的阻塞,我们可以使用ReentrantLock类的tryLock方法或.lockInterruptibly()方法即可解决。 1.2 检查线程中断(实现非阻塞线程中断) 1)当调用某个线程的interrupt方法时,中断发生的唯一时刻是在任务即将进入到阻塞操作中时或者已经在阻塞操作内部,若在某个死循环中添加了可能会产生阻塞操作的代码,但恰巧没有发生任何阻塞的情况下,我们需要另一种方式来退出。 public class InterruptThreadTest extends Thread{ public void run() { // 这里调用的是非清除中断标志位的isInterrupted方法 while(!Thread.currentThread().isInterrupted()) { long beginTime = System.currentTimeMillis(); System.out.println(Thread.currentThread().getName() + "is running"); // 当前线程每隔一秒钟检测线程中断标志位是否被置位 while (System.currentTimeMillis() - beginTime < 1000) {} } if (Thread.currentThread().isInterrupted()) { System.out.println(Thread.currentThread().getName() + "is interrupted"); } } public static void main(String[] args) { // TODO Auto-generated method stub InterruptThreadTest itt = new InterruptThreadTest(); itt.start(); try { Thread.sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 设置线程的中断标志位 itt.interrupt(); } } 2)检查线程中断的方法: isInterrupted()方法判断当前线程是否为中断状态 interrupted()方法判断当前线程是否为中断状态,并且会重置线程中断状态为false 3)如何正确的安全的终止线程:中断状态是线程的一个标识位,而中断操作是一种简便的线程间交互 方式,而这种交互方式最适合用来取消或停止任务。除了中断以外,还可以利用一个boolean变 量来控制是否需要停止任务并终止该线程。这种通过标识位或者中断操作的方式能够使线程在终止时有机会去清理资源,而不是武断地将线程停止,因此这种终止线程的做法显得更加安全和优雅。 public class Shutdown { public static void main(String[] args) throws Exception { Runner one = new Runner(); Thread countThread = new Thread(one, "CountThread"); countThread.start(); // 睡眠1秒,main线程对CountThread进行中断,使CountThread能够感知中断而结束 Thread.sleep(1); countThread.interrupt(); Runner two = new Runner(); countThread = new Thread(two, "CountThread"); countThread.start(); // 睡眠1秒,main线程对Runner two进行取消,使CountThread能够感知on为false而结束 Thread.sleep(1); two.cancel(); } private static class Runner implements Runnable { private long i; private volatile boolean on = true; @Override public void run() { while (on && !Thread.currentThread().isInterrupted()) { i++; } System.out.println("Count i = " + i); } public void cancel() { on = false; } } }

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

(四)Java工程化--Git基础

GIT学习参考:https://git-scm.com/book/zh/v2 常见命令 git init 初始化项目 git add *.java 添加文件到git版本控制(.java后缀的全部文件) Git 有三种状态, commited(已提交),modified(已修改),staged(已暂存);已提交表示数据已经安全的保存在本地数据库中。 已修改表示修改了文件,但还没保存到数据库中。 已暂存表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。 由此引入 Git 项目的三个工作区域的概念:Git 仓库、工作目录以及暂存区域。 git status -s 当前状态,一般有进行下一步操作的提示信息 -s输出简洁信息 git commint -m '提交描述信息' 提交到本地仓库 git clone https://github.com/xuelingxiao/java-knowledge-structure knstuct 克隆远端仓库并重命名 > GitHub 有一个十分详细的针对数十种项目及语言的 .gitignore 文件列表,你可以在 https://github.com/github/gitignore 找到它. git diff 查看哪些修改了还没有暂存;查看哪些修改暂存了准备下次提交; git diff --cache 查看暂存区 git commit -a 跳过add,将跟踪文件暂存并一起提交 git rm filename.txt 将文件移除跟踪状态 git rm --cached filename 从暂存区移除文件,将保留工作区文件 git mv filefrom fileto 移动文件 git log 查看提交历史 git log -p -2 -p显示每次提交的内容差异,-2显示最近两次提交,常用的还有git log --graph,git log --pretty=oneline[short,full,fuller,format],git log -Sfunctionname,git log --grep 关键字其他的请参阅帮助 git commit --amend amend 将用来修复上次提交,例如上次提交如果忘记了某些文件,可以使用此命令修复,git将会把amend的文件与之前的文件记录为一次提交 git reset HEAD 文件名.txt 取消暂存 git checkout -- 文件名 撤销对文件的修改, 比较危险,因为本地的修改可能会被从远端来的文件覆盖 git remote -v 查看配置的远端仓库信息, -v显示git保存的简写和url git remote add knstuct https://github.com/https://github.com/xuelingxiao/java-knowledge-structure 添加远端仓库配置 git fetch knstuct 获取仓库,即镜像同步到本地 git pull knstuct 拉取远端分支到本地,并合并,fetch不会合并 git push -f remote-name branch-name 推送到远端, -f将回滚版本(强制推送) git remote show origin 查看一个远端分支的更多信息 git remote rename oldname newname 远端分支重命名 git remote rm branch-name 移除远端分支 git tag 列出标签 git tag -l 'v1.8.5*' 只列出v1.8.5系列的标签 git tag -a v1.1 -m 'v1.1版本的标签' 创建一个附注标签(git标签分两类:轻量标签和附注标签,附注标签存储了git数据库中的一个完整对象,可以被检验,包含了打标签人的信息) git tag v1.2 -lw 打轻量标签 git push origin v1.1 推送标签到远端,这样可以共享标签 git checkout -b brahchname tagname 检出标签,实际是将标签版本检出到工作区 -b只是第一次checkout使用 别名设置, 参考示例如下 $ git config --global alias.co checkout $ git config --global alias.br branch $ git config --global alias.ci commit $ git config --global alias.st status $ git config --global alias.unstage 'reset HEAD --' --上面命令运行后,下面两条语句等价 $ git unstage fileA $ git reset HEAD -- fileA --如果是外部命令, 可以在命令前加! $ git config --global alias.visual '!gitk' git merge branch master 合并分支 git rebase master 变基,掌握不住的话要少用 Git-Flow 规划团队如何使用git, 即使用git的一套规范; 可以参考google的gitflow. git hooks 可以在CI使用, 自动发布,与jenkins集成. 通过本次学习我们基本就可以应对平时的需要了.作为工程化的一部分,git就先了解这么多(后面如果有时间的话再整理下git的更多知识), 下一步将学习jenkins.

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

多线程基础篇(1)——初试线程

1.线程概念 1.1 线程与进程 一个CPU在同一时间只能处理一个进程(程序),而一个进程包含至少一个或多个线程,操作系统会对每个进程分配相应的系统资源,如cpu,内存等,而进程中的所有线程将会共享这些资源。 进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程。(进程是资源分配的最小单位) 线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。(线程是cpu调度的最小单位) 1.2 并行与并发 1.并行:真正意义上的同时运行 2.并发:只是通过CPU的时间片分配算法来循环执行所有任务,cpu不断地切换执行线程,造成同时运行 的错觉。 1.3 线程状态 1)新建状态(New):新创建了一个线程对象。 2)就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于“可运行线程池”中,变得可运行,只等待获取CPU的使用权。即在就绪状态的进程除CPU之外,其它的运行所需资源都已全部获得。 3)运行状态(Running):就绪状态的线程获取了CPU时间片,执行程序代码。 4)阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。 5)终止:线程run方法中执行到return,或发生了一个未处理(try-catch)的异常时,则会发生线程终止。 6)等待状态:表示当前线程进入等待状态,并且需要其他线程进行通知或中断。 7)超时等待状态:表示当前线程进入等待状态,并且需要其他线程进行通知或中断,也可以在一定时间后自动返回 注:等待状态,超时等待状态实际上也算阻塞状态。 2.简单线程代码实现 2.1 通过继承Thread类创建 继承Thread,重写run方法并在run方法中添加任务逻辑代码,即可创建一个线程。 public class Demo2{ public static void main(String[] args) { for(int i=0;i<=20;i++){ test t=new test(i); t.start(); System.out.println("main thread"+i); } } } class test extends Thread{ int i; public test(int i){ this.i=i; } //重写run方法 @Override public void run(){ System.out.println("new thread"+i); } } 2.2 实现Runnable接口 public class Demo3 { public static void main(String[] args) { //实现Runnable 接口 Runnable r=new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()); } }; //将Runnable对象作为参数传入Thread构造方法 Thread t=new Thread(r); t.start(); } } 2.3 实现Callable接口 这是一种特殊的方法,因为其可以获得返回值或者抛出异常,而Runnable与Thread皆不可以,但Callable线程任务对象只能交由线程池来执行。其返回值用Future对象来接收。 public class Demo5 { public static void main(String[] args) throws Exception { Callable<String> c=new Callable<String>() { @Override public String call() throws Exception { // TODO Auto-generated method stub return "callable"; } }; ExecutorService executors=Executors.newFixedThreadPool(5); Future<String> f=executors.submit(c); System.out.println(f.get()); } } 使用Runnable,Callable接口实现对比继承Thread类的优点: 1)实现了线程对象与线程任务的解耦 2)避免了类的单继承问题,获得了更好的扩展性 2.4 线程池的简单使用 与数据库连接池类似,我们通过一个线程池对象(执行器)来动态的生成管理线程对象,只需传入线程任务对象(Runnable,Callable),则线程池对象将自动的创建或者复用线程对象。 public class Demo4 { public static void main(String[] args) { //创建线程池对象,通过工厂类来创建线程池 ExecutorService executors=Executors.newFixedThreadPool(5);//创建一个可重用的固定线程数的线程池 Executors.newCachedThreadPool();//创建一个根据需要可随时创建新线程的线程池,当已经创建的线程中有可用的时便重用 Executors.newSingleThreadExecutor();//创建一个使用单个线程的线程池,并以无界队列方式运行 //调用方法提交线程任务并执行 Runnable task=new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()); } }; executors.execute(task); } } 注:Runnable与Callable对象的提交执行方式不同,Runnable对象调用execute方法,而Callable对象调用submit方法。 欢迎各位指出错误以及不足,谢谢

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

Python基础系列-判断字段是否IP

版权声明:如需转载,请注明转载地址。 https://blog.csdn.net/oJohnny123/article/details/82116511 代码是从某开源项目中找到的,忘了出处,侵删。 def is_ip(value): import sys, os, socket PY2 = sys.version_info[0] == 2 """Determine if the given string is an IP address. Python 2 on Windows doesn't provide ``inet_pton``, so this only checks IPv4 addresses in that environment. :param value: value to check :type value: str :return: True if string is an IP address :rtype: bool """ if PY2 and os.name == 'nt': try: socket.inet_aton(value) return True except socket.error: return False for family in (socket.AF_INET, socket.AF_INET6): try: socket.inet_pton(family, value) except socket.error: pass else: return True return False

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

SQL Serever学习9——基础查询语句

SQL语言概述 SQL是结构化查询语言(Structure Query Language),1974年提出,1979年被IBM实现,SQL语言已经成为关系型数据库的标准语言。 包括: DDL数据定义语言 语句有CREATE ,ALTER ,DROP,操作表,视图,触发器,存储过程 DML数据操作语言 语句有SELECT ,INSERT , UPDATE , DELETE,用于检索和操作数据 DCL数据控制语言 语句有GRANT , DENY , REVOKE,只有sysadmin,数据库创建者,拥有者,安全管理员有权利执行,用来设置或更改数据库用户或角色权限 流程控制 常用语句有BEGIN...END , IF...ELSE , WHILE , BREAK , GOTO , WAITFOR , RETURN等语 逻辑运算符 AND OR NOT ALL,所有表达式为true才为true ANY,表达式中一个为true则为true BETWEEN ,在某个范围内则为true EXISTS IN ,操作数为表达式列表中的一个则为true。 语句基本格式 SELECT * FROM 表名 WHERE 条件 GROUP BY 字段 HAVING 表达式 ORDER BY 字段 ASC| DESC 说明:GROUP BY子句后可以使用HAVING 短语,用来分组后筛选,HAVING 必须跟随ORDER BY子句使用。 默认情况,查询结构表的标题可以是表的字段名,也可以无标题,还可以使用AS 对字段标题进行修改 USE 销售管理 GO SELECT 商品名称,型号,销售价-进价 AS 差价,库存 FROM 商品表 GO 使用查询生成新表,或者临时表 USE 销售管理 GO SELECT 商品名称,型号,销售价-进价 AS 差价,库存 INTO 商品表附表 FROM 商品表 GO 结果 临时表的使用 临时表在本次服务器连接过程中有效,一旦服务器断开连接,临时表失效,并被删除。 USE 销售管理 GO SELECT 商品名称,型号,销售价-进价 AS 差价,库存 INTO #临时商品表 FROM 商品表 GO 快速生成数据表结构(空白哦) 因为WHERE 1=2不成立,所以就不会检索出符合条件的数据,生成的是一个没有数据的空白表 USE 销售管理 GO SELECT 商品名称,型号,销售价-进价 AS 差价,库存 INTO #商品表副本 FROM 商品表 WHERE 1=2 GO SELECT * FROM #商品表副本 SQL汇总查询 聚合函数 常用的聚合函数有6个: COUNT(*),统计所有记录个数 COUNT[ DISTINCT] 字段,统计字段中值的个数 SUM 字段,对指定字段(数值型)求和 AVG 字段,对指定字段(数值型)求平均值 MAX 字段,求一个字段最大值 MIN 字段,求一个字段最小值 分组查询语句 有时候统计每种商品销售总金额,需要对销售表中销售金额进行汇总,然后再进行操作,这就是分组查询。 GROUP BY子句实现, GROUP BY 字段 HAVING 分组后的筛选条件表达式 注意:BY 字段 按指定字段进行分组,字段值相同的记录放在一组,每一组汇总只有一条数据。 HAVING 的筛选是对经过分组后结果进行筛选,而不是对原始表筛选。 SELECT 子句后的字段列表,必须是聚合函数 ,或者是GROUP BY 子句中的字段。 demo.sql USE 销售管理 GO SELECT 品牌,COUNT(品牌) AS 数量 FROM 商品表 GROUP BY 品牌 --HAVING 品牌='A牌' 结果 注意这里的HAVING子句和WHERE的区别: HAVING可以有聚集函数,而WHERE子句不可以 HAVING作用于分组后的结果集,WHERE 子句作用于基本表 下面来一个小demo,用来查找某个属性的值出现最多的那个记录 原表 现在查找哪个品牌数量最多,并找出这个品牌的记录 demo USE 销售管理 GO --申明变量用来存储数量最多的品牌 DECLARE @ELE VARCHAR(20) SELECT @ELE=A.品牌 FROM (SELECT TOP 1 品牌,COUNT(品牌) AS 数量 FROM 商品表 GROUP BY 品牌 ORDER BY 数量 DESC) A --print @ele SELECT * FROM 商品表 WHERE 品牌=@ELE 结果集 汇总合计函数ROLLUP(在sqlserver2008叫做COMPUTE) 使用这个函数,需要最分组函数的最后添加with rollup,然后会在最后多一行。 分组 USE 销售管理 GO SELECT 品牌,COUNT(品牌) AS 数量 FROM 商品表 GROUP BY 品牌 使用ROLLUP汇总 USE 销售管理 GO SELECT 品牌,COUNT(品牌) AS 数量 FROM 商品表 GROUP BY 品牌 WITH ROLLUP 连接查询 就是多个表单的关联查询 INNER JOIN,内连接 LEFT JOIN,左连接,结果包含满足条件的行和左侧表的全部行,使用NULL值代替无法匹配的值 RIGHT JOIN,右连接 FULL JOIN,全连接,结果包含满足条件的行和2侧表的全部行 CROSS JOIN,交叉连接,结果包含2个表的所有行的组合,2个表的笛卡尔操作,用的不多 内连接范例 使用sqlserver语法 USE 销售管理 GO SELECT A.商品名称,A.品牌,A.销售价,B.类型名称 FROM 商品表 A,商品类型表 B WHERE A.类型=B.类型编号 使用ANSI语法 USE 销售管理 GO SELECT A.商品名称,A.品牌,A.销售价,B.类型名称 FROM 商品表 A INNER JOIN 商品类型表 B ON A.类型=B.类型编号 注意:<表名> A的意思是将某个表在这一次查询红命名为A,这样在整个查询中都可以使用A代替该表,简化操作。 子查询 子查询出现的形式: 多数情况出现在WHERE 子语句中 出现在外部查询的SELECT 子语句中 出现在外部查询的FROM 子句中,即把查询结果集看做另外一张表 使用比较运算符的子查询 /*查询一级买家信息*/ SELECT * FROM 买家表 WHERE 级别= (SELECT 级别编号 FROM 买家级别表 WHERE 级别名称='一级') 使用ALL ANY运算符的子查询 当子查询返回的是单列多值,使用ALL ANY和比较运算符构成特殊查询 >ANY,表示大于子查询结果的某个值,就是大于查询结果最小值 =ANY,等于查询结果的某个值,相当于IN <ANY,小于查询结果的最大值 >ALL,大于查询结果最大值 !=ALL,相当于NOT IN 比如查询那些台式电脑比笔记本电脑的进价还要贵 /*查询那些台式电脑比笔记本电脑的进价还要贵*/ SELECT * FROM 商品表 WHERE 商品名称='台式机' AND 进价>ANY (SELECT 进价 FROM 商品表 WHERE 商品名称='笔记本') 使用IN运算符的子查询 比如查询进价大于5000的商品销售情况 /*查询进价大于5000的商品销售情况*/ SELECT 商品编号,买家编号 FROM 销售表 WHERE 商品编号 IN (SELECT 商品编号 FROM 商品表 WHERE 进价>5000) 使用EXISTS运算符的子查询 用来判断子查询是否有结果返回,NOT EXISTS的作用刚好相反 比如查询至少有一次实际销售价比进价还低的商品信息 /*查询至少有一次实际销售价比进价还低的商品信息*/ SELECT * FROM 商品表 A WHERE EXISTS (SELECT * FROM 销售表 B WHERE A.商品编号=B.商品编号 AND B.实际销售价格<A.进价) 由于不需要子查询返回具体值,所以这种子查询的通常返回的列为*的格式 有个查询很难理解,记录如下 查询销售表每种商品(由商品编号区分)销售价格最贵的销售情况 分析:首先将商品种类分组 SELECT 商品编号,MAX(实际销售价格) FROM 销售表 GROUP BY 商品编号 这里还不能输出要求的信息,所以还要使用自连接(自己与自己的一个副本连接) 原表 经过筛选 /*查询销售表每种商品(由商品编号区分)销售价格最贵的销售情况*/ SELECT * FROM 商品表 A WHERE 销售价= (SELECT MAX(销售价) FROM 商品表 B WHERE A.品牌=B.品牌) ORDER BY 商品编号 数据库中数据的管理 插入数据INSERT 使用INSERT语句插入数据进数据表,有2种方式:插入单行数据(使用VALUES),插入多行数据(使用SELECT) 插入单行数据 /*插入单行数据*/ INSERT INTO 买家表(买家编号,买家名称,买家电话,级别) VALUES('M05','薛松','5362313','J02'); 当插入数据的数量和顺序和表中字段一一对应,可以省略字段名列表 /*插入单行数据*/ INSERT INTO 买家表 VALUES('M06','宋松','5362220','J02'); 插入多行数据 新建一张表,名为“高价销售表类”,结构与销售表相同,将销售表的实际销售价格>3000的记录插入该表。 /*插入多行数据*/ --建立一张空表 SELECT * INTO 高价销售表 FROM 销售表 WHERE 1=2 GO --插入多行数据 INSERT INTO 高价销售表(商品编号,买家编号,实际销售价格,销售日期,销售数量) SELECT 商品编号,买家编号,实际销售价格,销售日期,销售数量 FROM 销售表 WHERE 实际销售价格>3000 GO 为了建立一张空白表,查询条件WHERE 1=2永远不成立,这个是一个常用的方法。 将所有一级买家的信息存入新表“高级买家” /*插入多行数据*/ --创建新表 SELECT * INTO 高级买家 FROM 买家表 WHERE 1=2 GO --添加数据 INSERT INTO 高级买家 SELECT 买家表.* FROM 买家表,买家级别表 WHERE 买家表.级别=买家级别表.级别编号 AND 级别名称='一级' GO 由于查询过程使用了2个表了,所以SELECT 语句要声明,只要买家表的列 修改数据UPDATE 普通修改 因为与A品牌的合作有了新政策,所有A品牌商品进货价下调5% /*普通修改数据*/ UPDATE 商品表 SET 销售价=销售价*0.95 WHERE 品牌='A牌' 带子查询的修改 为了增加耗材商品的销售份额,公司决定将所有耗材商品销售价格下调5% /*子查询修改数据*/ UPDATE 商品表 SET 销售价=销售价*0.95 WHERE 商品表.类型= (SELECT 类型编号 FROM 商品类型表 WHERE 类型名称='耗材') 删除数据DELETE 删除普通数据 删除销售表4所有B牌的商品购买信息 /*删除数据*/ SELECT * INTO 销售表4 FROM 销售表 GO DELETE FROM 销售表4 WHERE 品牌='B牌' 删除子查询 删除销售表4中所有买家名称为“个人”的买家购买信息 /*删除数据*/ SELECT * INTO 销售表4 FROM 销售表 GO DELETE FROM 销售表4 WHERE 买家编号= (SELECT 买家编号 FROM 买家表 WHERE 买家名称='个人') 清空数据表 /*删除数据*/ SELECT * INTO 销售表4 FROM 销售表 GO TRUNCATE TABLE 销售表4 注意:TRUNCATE TABLE 和不带条件的DELETE最终效果都是清空表中所有数据,但是在执行上TRUNCATE TABLE 更高,速度更快,因为他不记录事务日志,会释放数据,索引占据的空间,删除的数据不可恢复。

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

(一)Java工程化--Maven基础

Maven 读作['mevən] 翻译成中文是"内行,专家" Maven是什么 包依赖的前世今生: 原始的jar包引用--> ant --> maven. 是一种项目管理工具 Maven优势: convertion over configuration 约定优于配置: 这个原则不仅适用于maven, 更是目前大多数框架遵循的原则,如mvc 简单 易于测试 构建简单 CI(持续集成) 插件丰富 下载和安装 下载: http://maven.apache.org/download.cgi 安装: 解压安装即可 环境变量配置: windows: 配置path MAVEN_HOME linux: 在 .bash_profile 文件中 运行参数: 定义环境变量MAVEN_OPS 配置settings.xml: settings.xml文件是个空模板,我们可以在这个文件进行一些自定义配置. 常用的配置如:repo存储目录 创建maven项目 项目结构: 遵循约定优于配置原则, 项目包的组织结构如下 pom.xml groupId 公司组织id artifactId 功能命名 version 版本号 packageing 打包方式,默认jar,可修改为maven-project,war dependancyManagement 最好之出现在父pom中,用于统一版本号,只做声明依赖,子模块pom中还需要引用,但不需要制定version. dependancy type 默认jar scope 指定哪个阶段适用,各阶段如下: compile 编译,打包,默认 如spring-core test 测试 如spring-test provided 编译 如servlet runtime 运行时 如JDBC驱动实现包 system 本地一些jar 依赖传递 依赖仲裁:(1)最短路径原则,根据依赖树就近取最接近的版本;(2)加载顺序原则;(3)exclusions 排除包 命令: mvn dependancy:tree Maven生命周期(三个过程) 区分术语:lifecycle/phase/goal 生命周期: clean 包含的phase: pre-clean --> clean --> post-clean default compile package install deploy ... site pre-site --> site -->post-site --> site-deploy A Build Lifecycle is Made Up of Phases 一个构建生命周期是有多个phase组成 A Build Phase is Made Up of Plugin Goals 一个构建phase是由多个插件目标goal构成

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

Python编程入门基础语法详解经典

一、基本概念 1.内置的变量类型: Python是有变量类型的,而且会强制检查变量类型。内置的变量类型有如下几种: #浮点 float_number=2.3 #复数 complex_number=1+5j #整型 integer_number=1 #list 序列 sample_list=[2,52,36,'abc'] #嵌套 sample_nest=[(2,4,6),{5:7,9:11,'key':[2,5]},6] #dictionary 字典 sample_dic={"key":value, 5:10} #tuple 只读的序列 sample_tuple=(3,9,"ab") 从上面的示例可以看出,python的变量无需声明,可直接给变量赋值。 2.字符串 python中声明一个字符串通常有三种方法,''、" "和''' ''',这三种方法在声明普通字符串时的效果是完全一样的,区别在于字符串本身中存在引号的情况,举例如下: word='good' sentence="hello world" paragraph='''good noon:"nice to meet you."''' python中单行注释采用#开头。 #!/usr/bin/python # First comment print "Hello, world!"; # second comment 上述内容输出:hello,Python!。注释可以在语句或表达式行末。 多行注释可采用三引号,比如: '''This is a comment. This is a comment, too. This is a comment, too. I said that already.''' 转义符 ''; 自然字符串, 通过在字符串前加r或R。 如 r"this is a line with " 则 会显示,并不是换行。 python允许处理unicode字符串,加前缀u或U, 如 u"this is an unicode string"。 3.Python标识符 在python里,标识符有字母、数字、下划线组成。 在python中,所有标识符可以包括英文、数字以及下划线(_),但不能以数字开头。 python中的标识符是区分大小写的。 以下划线开头的标识符是有特殊意义的。以单下划线开头(_foo)的代表不能直接访问的类属性,需通过类提供的接口进行访问,不能用"from xxx import *"而导入; 以双下划线开头的(__foo)代表类的私有成员;以双下划线开头和结尾的(__foo__)代表python里特殊方法专用的标识,如__init__()代表类的构造函数。 4.Python保留字符 下面的列表显示了在Python中的保留字。这些保留字不能用作常数或变数,或任何其他标识符名称。 所有Python的关键字只包含小写字母。 如果你在学习Python的过程中遇见了很多疑问和难题,可以加-q-u-n227 -435-450里面有软件视频资料免费 5.行和缩进 学习Python与其他语言最大的区别就是,Python的代码块不使用大括号({})来控制类,函数以及其他逻辑判断。python最具特色的就是用缩进来写模块。 缩进的空白数量是可变的,但是所有代码块语句必须包含相同的缩进空白数量,这个必须严格执行。如下所示: ifTrue: print"True" else: print"False" 上述,if与else的缩进空白数量相同。以下代码将会报错: ifTrue: print"Answer" print "True" else: print "Answer" print"False" python中分号;标识一个逻辑行的结束,但是实际中一般每个物理行只写一个逻辑行,可以避免使用分号。这样书写便于阅读。注意:不要混合使用空格和制表符来缩进,因为在跨越不同的平台时无法正常工作。 多个物理行中可以写一个逻辑行,使用行连接符,如下: s="peteris writing this article" 二、运算符与表达式 1. 运算符与其用法 2. 运算符优先级(从低到高) 三.控制流 1. 条件语句 1.1 if语句 例子, 执行if语句内的程序 a=input("a:") b=input("b:") if(a > b): print a, " > ", b if else语句: a=input("a:") b=input("b:") if(a > b): print a, " > ", b else: print a, " < ", b 1.2 if…elif…else语句 例子:根据输入的分数,输出分数等级: score=raw_input("score:") score=int(score) if(score >=90)and(score <=100): print"A" elif(score >=80)and(score < 90): print"B" elif(score >=60)and(score < 80): print "C" else: print "D" raw_input() 读取输入值。 1.3 if语句的嵌套 编写条件语句时,应该尽量避免使用嵌套语句。嵌套语句不便于阅读,而且可能会忽略一些可能性。 x=-1 y=99 if(x >=0): if(x > 0): #嵌套的if语句 y=1 else: y=0 else: y=-1 print "y =", y 1.4 实现switch语句的功能 python中没有switch特殊字,Python可以通过字典实现switch语句的功能。 实现方法分两步。首先,定义一个字典。字典是由键值对组成的集合。其次,调用字典的get()获取相应的表达式。 from__future__importdivision x=1 y=2 operator="/" result={ "+" : x+y, "-" : x-y, "*" : x*y, "/" : x/y } print result.get(operator) 输出为0.5; 另一种使用switch分支语句的方案是创建一个switch类,处理程序的流程。 a) 创建一个switch类,该类继承自Python的祖先类object。调用构造函数init( )初始化需要匹配的字符串,并需要定义两个成员变量value和fall。Value用于存放需要匹配的字符串,fall用于记录是否匹配成功,初始值为false,标识匹配不成功。如果匹配成功,程序往后执行。 b) 定义一个match( )方法,该方法用于用于匹配case子句。这里需要考虑三种情况:首先是匹配成功的情况,其次是匹配失败的默认case子句,最后是case子句中没有使用break中断的情况。 c) 重写__iter__( )方法,定义该方法后才能使switch类用于循环语句中。__iter__( )调用match( )方法进行匹配。通过yield保留字,使函数可以在循环中迭代。此外,调用StopIteration异常中断循环。 d) 编写调用代码,在for…in…循环中使用switch类。 #!/usr/bin/python # -*- coding: UTF-8 -*- classswitch(object): def__init__(self, value): # 初始化需要匹配的值value self.value=value self.fall=False # 如果匹配到的case语句中没有break,则fall为true。 def__iter__(self): yieldself.match # 调用match方法 返回一个生成器 raiseStopIteration # StopIteration 异常来判断for循环是否结束 defmatch(self,*args): # 模拟case子句的方法 ifself.fallornotargs: # 如果fall为true,则继续执行下面的case子句 # 或case子句没有匹配项,则流转到默认分支。 returnTrue elifself.valueinargs: # 匹配成功 self.fall=True returnTrue else: # 匹配失败 returnFalse operator="+" x=1 y=2 forcaseinswitch(operator): # switch只能用于for in循环中 ifcase('+'): print x+y break ifcase('-'): printx-y break ifcase('*'): printx*y break ifcase('/'): printx/y break ifcase(): # 默认分支 print"" 2.while...语句 只要在一个条件为真的情况下,while语句允许你重复执行一块语句。while语句是所谓 循环 语句的一个例子。while语句有一个可选的else从句。 whileTrue: pass else: pass #else语句可选,当while为False时,else语句被执行。 pass是空语句。 3.for 循环 foriinrange(0, 5): print i else: pass # 打印0到4 注:当for循环结束后执行else语句;range(a, b)返回一个序列,从a开始到b为止,但不包括b,range默认步长为1,可以指定步长,range(0,10,2); 四、函数 函数通过def定义。def关键字后跟函数的标识符名称,然后跟一对圆括号,括号之内可以包含一些变量名,该行以冒号结尾;接下来是一块语句,即函数体。 defsumOf(a, b): returna+b 4.1 局部变量 在函数内定义的变量与函数外具有相同名称的其他变量没有任何关系,即变量名称对于函数来说是局部的。这称为变量的作用域。global语句, 为定义在函数外的变量赋值时使用global语句。 deffunc(): globalx print "x is ", x x=1 x=3 func() printx 以上代码,输出的结果为: 3 1 4.2 默认参数 通过使用默认参数可以使函数的一些参数是‘可选的’。 defsay(msg, times=1): print msg*times say("peter") say("peter", 3) 注意:只有在形参表末尾的那些参数可以有默认参数值,即不能在声明函数形参的时候,先声明有默认值的形参而后声明没有默认值的形参,只是因为赋给形参的值是根据位置而赋值的。 4.3 关键参数 如果某个函数有很多参数,而现在只想指定其中的部分,那么可以通过命名为这些参数赋值(称为‘关键参数’)。 优点:不必担心参数的顺序,使函数变的更加简单;假设其他参数都有默认值,可以只给我们想要的那些参数赋值。 deffunc(a, b=2, c=3): print"a is %s, b is %s, c is %s"%(a, b, c) func(1) #输出a is 1, b is 2, c is 3 func(1, 5) #输出a is 1, b is 5, c is 3 func(1, c=10) #输出a is 1, b is 2, c is 10 func(c=20, a=30) #输出a is 30, b is 2, c is 20 4.3 return语句 return语句用来从一个函数返回,即跳出函数。可从函数返回一个值。 没有返回值的return语句等价于return None。None表示没有任何东西的特殊类型。

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

Java基础-Java的语法规范

1.Java中的语法大小写区分,举例:(int H;h=10)在声明变量时用大写,赋值却用的小写的他是不认的,还有关键字,大小写区分要不然不认的。 2.括号成对出现:举例写的时候将格式书写完整{}[](); 3.java中的语法符号都是半角符号:因为全角符号占2个字节 半角符号只占1个字节( ; ;)跟语法相关的都是半角符号。 4.Java跟C语言的语法很像:每句代码要有分号结束,因为Java模仿C语言; 5.命名规范;不能含有的字符很多 , 字母或下划线开头可以用数字结尾(变量名,类名,方法名,属性名)。 6.书写代码要有良好的代码书写格式手法(结构写完整,然后填充内容)会很少会发生语法上的错误。 7.备份:每天要学会备份,不备份存在隐患,尽量上传到服务器上去,上传到服务器上的好处,不存在丢除非服务区死掉。 代码越写路越窄:备份了的话可以回头看,回到以前看看,可以调整思路。 8.类的命名规范:里面如果有一个用public修饰的class那他的名称必须和文件名称一致。 一个文件文件可以申请多个class吗?:一个文件可以申请多个class文件 一个文件不能含有多个public class声明。 正确写法:a.java 文件名必须一致 public class a{ } class b{ } class c{ } class d{ } 上面编译以后会生成3个文件; a.class b.class c.class d.class 写的是写在过括号外面的。 一个文件最好声明一个class,不要写多个,多个容易出错,查找起来也麻烦,有可能会起冲突。class(有可能会重名,起冲突)

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

javascript基础修炼(3)—What's this(下)

开发者的javascript造诣取决于对【动态】和【异步】这两个词的理解水平。 这一期主要分析各种实际开发中各种复杂的this指向问题。 一. 严格模式 严格模式是ES5中添加的javascript的另一种运行模式,它可以禁止使用一些语法上不合理的部分,提高编译和运行速度,但语法要求也更为严格,使用use strict标记开启。 严格模式中this的默认指向不再为全局对象,而是默认指向undefined。这样限制的好处是在使用构造函数而忘记写new操作符时会报错,而不会把本来需要绑定在实例上的一堆属性全绑在window对象上,在许多没有正确地绑定this的场景中也会报错。 二. 函数和方法的嵌套与混用 词法定义并不影响this的指向 , 因为this是运行时确定指向的。 2.1 函数定义的嵌套 function outerFun(){ function innerFun(){ console.log('innerFun内部的this指向了:',this); } innerFun(); } outerFun(); 控制台输出的this指向全局对象。 2.2 对象属性的嵌套 当调用的函数在对象结构上的定义具有一定深度时,this指向这个方法所在的对象,而不是最外层的对象。 var IronMan = { realname:'Tony Stark', rank:'1', ability:{ total_types:100, fly:function(){ console.log('IronMan.ability.fly ,作为方法调用时this指向:',this); }, } } IronMan.ability.fly(); 控制台输出的this指向IronMan的ability属性所指向的对象,调用fly( )这个方法的对象是IronMan.ability所指向的对象,而不是IronMan所指向的对象。 this作为对象方法调用时,标识着这个方法是如何被找到的。IronMan这个标识符指向的对象信息并不能在运行时找到fly( )这个方法的位置,因为ability属性中只存了另一个对象的引用地址,而IronMan.ability对象的fly属性所记录的指向,才能让引擎在运行时找到这个匿名方法。 三. 引用转换 引用转换实际上并不会影响this的指向,因为它是词法性质的,发生在定义时,而this的指向是运行时确定的。只要遵循this指向的基本原则就不难理解。 3.1 标识符引用转换为对象方法引用 var originFun = function (){ console.log('originFun内部的this为:',this); } var ironMan = { attack:originFun }; ironMan.attack(); 这里的this指向其调用者,也就是ironMan引用的对象。 3.2 对象方法转换为标识符引用 var ironMan = { attack:function(){ console.log('对象方法中this指向了:',this); } } var originFun = ironMan.attack; originFun(); 这里的this指向全局对象,浏览器中也就是window对象。3.2中的示例被认为是javascript语言的bug,即this指向丢失的问题。同样的问题也可能在回调函数传参时发生,本文【第5章】将对这种情况进行详细说明。 四. 回调函数 javascript中的函数是可以被当做参数传递进另一个函数中的,也就有了回调函数这样一个概念。 4.1 this在回调函数中的表现 var IronMan = { attack:function(findEnemy){ findEnemy(); } } function findEnemy(){ console.log('已声明的函数被当做回调函数调用,this指向:',this); } var attackAction = { findEnemy:function(){ console.log('attackAction.findEnemy本当做回调函数调用时,this指向',this); }, isArmed:function(){ console.log('check whether the actor is Armed'); } } //1.直接传入匿名函数 IronMan.attack(function(){ console.log(this); }); //2.传入外部定义函数 IronMan.attack(findEnemy); //3.传入外部定义的对象方法 IronMan.attack(attackAction.findEnemy); 从控制台打印的结果来看,无论以哪种方式来传递回调函数,回调函数执行时的this都指向了全局变量。 4.2 原理 javascript中函数传参全部都是值传递,也就是说如果调用函数时传入一个原始类型,则会把这个值赋值给对应的形参;如果传入一个引用类型,则会把其中保存的内存指向的地址赋值给对应的形参。所以在函数内部操作一个值为引用类型的形参时,会影响到函数外部作用域,因为它们均指向内存中的同一个函数。详细可参考[深入理解javascript函数系列第二篇——函数参数]这篇博文。 理解了函数传参,就很容易理解回调函数中this为何指向全局了,回调函数对应的形参是一个引用类型的标识符,其中保存的地址直接指向这个函数在内存中的真实位置,那么通过执行这个标识符来调用函数就等同于this基本指向规则中的作为函数来调用的情况,其this指向全局对象也就不难理解了。 五. this指针丢失 在第三节和第四节中,通过原理分析就能够明白为何在一些特定的场合下this会指向全局对象,但是从语言的角度来看,却很难理解this为什么指向了全局对象,因为这个规则和语法的字面意思是有冲突的。 5.1 回调函数的字面语境 var name = 'HanMeiMei'; var liLei = { name:'liLei', introduce:function () { console.log('My name is ', this.name); } }; var liLeiSay = liLei.introduce; liLeiSay();//同第三节中的引用转换示例 setTimeout(liLei.introduce,2000);//同第四节中的回调函数示例 上面的代码从字面上看意义是很明确的,就是希望liLei立刻介绍一下自己,在2秒后再介绍一下他自己。但控制台输出的结果中,他却两次都说自己的名字是HanMeiMei。 5.2 this指针丢失 5.1中的示例,也称为this指针丢失问题,被认为是Javascript语言的设计失误,因为这种设计在字面语义上造成了混乱。 5.3 this指针修复 方式1-使用bind 为了使代码的字面语境和实际执行保持一致,需要通过显示指定this的方式对this的指向进行修复。常用的方法是使用bind( )生成一个确定了this指向的新函数,将上述示例改为如下方式即可修复this的指向: var liLeiSay = liLei.introduce.bind(liLei); setTimeout(liLei.introduce.bind(liLei),2000); bind( )的实现其实并不复杂,是闭包实现高阶函数的一个简单的实例,感兴趣的读者可以自行了解。 方式2-使用Proxy Proxy是ES6中才支持的方法。 //绑定This的函数 function fixThis (target) { const cache = new WeakMap(); //返回一个新的代理对象 return new Proxy(target, { get (target, key) { const value = Reflect.get(target, key); //如果要取的属性不是函数,则直接返回属性值 if (typeof value !== 'function') { return value; } if (!cache.has(value)) { cache.set(value, value.bind(target)); } return cache.get(value); } }); } const toggleButtonInstance = fitThis(new ToggleButton()); 两种修复this指向的思路其实很类似,第一种方式相当于为调用的方法创建了一个代理方法,第二种方式是为被访问的对象创建了一个代理对象。 六. this的透传 实际开发过程中,往往需要在更深层次的函数中获取外层this的指向。 常规的解决方案是:将外层函数的this赋值给一个局部变量,通会使用_this,that,self,_self等来作为变量名保存当前作用域中的this。由于在javascript中作用域链的存在,嵌套的内部函数可以调用外部函数的局部变量,标识符会去寻找距离作用域链末端最近的一个指向作为其值,示例如下: document.querySelector('#btn').onclick = function(){ //保存外部函数中的this var _this = this; _.each(dataSet, function(item, index){ //回调函数的this指向了全局,调用外部函数的this来操作DOM元素 _this.innerHTML += item; }); } 七. 事件监听 事件监听中this的指向情况其实是几种情况的集合,与代码如何编写有很大关系。 7.1 表现 1. 在html文件中使用事件监听相关的属性来触发方法 <button onclick="someFun()">点击按钮</button> <button onclick="someObj.someFun()">点击按钮</button> 如果以第一种方式触发,则函数中的this指向全局; 如果以第二种方式触发,则函数中的this指向someObj这个对象。 2. 在js文件中直接为属性赋值 //声明一个函数 function callFromHTML() { console.log('callFromHTML,this指向:',this); } //定义一个对象方法 var obj = { callFromObj:function () { console.log('callFromObj',this); } } //注册事件监听-方式1 document.querySelector('#btn').onclick = function (event) { console.log(this); } //注册事件监听-方式2 document.querySelector('#btn').onclick = callFromHTML; //注册事件监听-方式3 document.querySelector('#btn').onclick = obj.callFromObj; 以上三种注册的事件监听响应函数,其this均指向id="btn"的DOM元素。 3. 使用addEventListener方法注册响应函数 //低版本IE浏览器中需要使用另外的方法 document.querySelector('#btn').addEventListener('click',function(event){ console.log(this); }); //也可以将函数名或对象方法作为回调函数传入 document.querySelector('#btn').addEventListener('click',callFromHTML); document.querySelector('#btn').addEventListener('click',obj.callFromObj); 这种方式注册的响应函数,其this与场景2相同,均指向id="btn"的DOM元素。区别在于使用addEventListener方法添加的响应函数会依次执行,而采用场景2的方式时,只有最后一次赋值的函数会被调用。 7.2 基本原理 1. 通过标签属性注册 <button id="btn" onclick="callFromHTML()">点我</button> <script> function callFromHTML() { console.log(document.querySelector('#btn').onclick); } </script> 在html中绑定事件处理程序,然后当按钮点击时,在控制台打印出DOM对象的onclick属性,可以看到: 这种绑定方式其实是将监听方法包裹在另一个函数中去执行,相当于: document.querySelector('#btn').onclick = function(event){ callFromHTML(); } 这样上述的表现就不难理解了。 2. 通过元素对象属性注册 document在javascript中是一个对象,通过其暴露的查找方法返回的节点也是一个对象,那么方式二绑定的监听函数在运行时,实际上就是在执行指定节点的onclick方法,根据this指向的基本规则可知其函数体中的this应该指向调用对象,也就是onclick这个方法所在的节点对象。 3. 通过addEventListener方法注册 这种方式是在DOM2事件模型中扩展的,用于支持多个监听器绑定的场景。DOM2事件模型的描述中规定了通过这种方式添加的监听函数执行时的this指向所在的节点对象,不同内核的浏览器实现方式有区别。 7.3 使用建议 不同的使用方式实质上是伴随着DOM事件模型升级而发生改变的,现代浏览器对于以上几种模式都是支持的,只有需要兼容老版本浏览器时需要考虑对DOM事件模型的支持程度。开发中DOM2级事件模型中addEventListener()和removeEventListener()来管理事件监听函数是最为推荐的方法。 八. 异步函数 1. setTimeout( )和setInterval( ) 这里的情况相当于上文中的回调函数的情况。 2. 事件监听 详见第7章。 3. ajax请求 几乎没有遇到过。 4. Promise 这里的情况相当于上文中的回调函数的情况。 九. 箭头函数和this 箭头函数是ES6标准中支持的语法,它的诞生不仅仅是因为表达方式简洁,也是为了更好地支持函数式编程。箭头函数内部不绑定this,arguments,super,new.target,所以由于作用域链的机制,箭头函数的函数体中如果使用到this,则执行引擎会沿着作用域链去获取外层的this。 十. Nodejs中的this Nodejs是一种脱离浏览器环境的javascript运行环境,this的指向规则上与浏览器环境在全局对象的指向上存在一定差异。 1. 全局对象global Nodejs的运行环境并不是浏览器,所以程序里没有DOM和BOM对象,Nodejs中也存在全局作用域,用来定义一些不需要通过任何模块的加载即可使用的变量、函数或类,全局对象中多为一些系统级的信息或方法,例如获取当前模块的路径,操作进程,定时任务等等。 2. 文件级this指向 Nodejs是支持模块作用域的,每一个文件都是一个模块,可通过require( )的方式同步引入,通过module.exports来暴露接口供其他模块调用。在一个文件中最顶级的this指向当前这个文件模块对外暴露的接口对象,也就是module.exports指向的对象。示例: var IronMan = { name:'Tony Stark', attack: function(){ } } exports.IronMan = IronMan; console.log(this); 在控制台即可看到,this指向一个对象,对象中只有一个属性IronMan,属性值为文件中定义的IronMan这个对象。 3. 函数级this指向 this的基本规则中有一条—当作为函数调用时,函数中的this指向全局对象,这一条在nodejs中也是成立的,这里的this指向了全局对象(此处的全局对象Global对象是有别于模块级全局对象的)。 思考题— React组件中为什么要bind(this) 如果你尝试使用过React进行前端开发,一定见过下面这样的代码: //假想定义一个ToggleButton开关组件 class ToggleButton extends React.Component{ constructor(props){ super(props); this.state = {isToggleOn: true}; this.handleClick = this.handleClick.bind(this); this.handleChange = this.handleChange.bind(this); } handleClick(){ this.setState(prevState => ({ isToggleOn: !preveState.isToggleOn })); } handleChange(){ console.log(this.state.isToggleOn); } render(){ return( <button onClick={this.handleClick} onChange={this.handleChange}> {this.state.isToggleOn ? 'ON':'OFF'} </button> ) } } 思考题:构造方法中为什么要给所有的实例方法绑定this呢?(强烈建议读者先自己思考再看笔者分析) 1. 代码执行的细节 上例仅仅是一个组件类的定义,当在其他组件中调用或是使用ReactDOM.render( )方法将其渲染到界面上时会生成一个组件的实例,因为组件是可以复用的,面向对象的编程方式非常适合它的定位。根据this指向的基本规则就可以知道,这里的this最终会指向组件的实例。 组件实例生成的时候,构造器constructor会被执行,此处着重分析一下下面这行代码: this.handleClick = this.handleClick.bind(this); 此时的this指向新生成的实例,那么赋值语句右侧的表达式先查找this.handleClick( )这个方法,由对象的属性查找机制(沿原型链由近及远查找)可知此处会查找到原型方法this.handleClick( ),接着执行bind(this),此处的this指向新生成的实例,所以赋值语句右侧的表达式计算完成后,会生成一个指定了this的新方法,接着执行赋值操作,将新生成的函数赋值给实例的handleClick属性,由对象的赋值机制可知,此处的handleClick会直接作为实例属性生成。总结一下,上面的语句做了一件这样的事情: 把原型方法handleClick( )改变为实例方法handleClick( ),并且强制指定这个方法中的this指向当前的实例。 2. 绑定this的必要性 在组件上绑定事件监听器,是为了响应用户的交互动作,特定的交互动作触发事件时,监听函数中往往都需要操作组件某个状态的值,进而对用户的点击行为提供响应反馈,对开发者来说,这个函数触发的时候,就需要能够拿到这个组件专属的状态合集(例如在上面的开关组件ToggleButton例子中,它的内部状态属性state.isToggleOn的值就标记了这个按钮应该显示ON或者OFF),所以此处强制绑定监听器函数的this指向当前实例的也很容易理解。 React构造方法中的bind会将响应函数与这个组件Component进行绑定以确保在这个处理函数中使用this时可以时刻指向这一组件的实例。 3. 如果不绑定this 如果类定义中没有绑定this的指向,当用户的点击动作触发this.handleClick( )这个方法时,实际上执行的是原型方法,可这样看起来并没有什么影响,如果当前组件的构造器中初始化了state这个属性,那么原型方法执行时,this.state会直接获取实例的state属性,如果构造其中没有初始化state这个属性(比如React中的UI组件),说明组件没有自身状态,此时即使调用原型方法似乎也没什么影响。 事实上的确是这样,这里的bind(this)所希望提前规避的,就是第五章中的this指针丢失的问题。 例如使用解构赋值的方式获取某个属性方法时,就会造成引用转换丢失this的问题: const toggleButton = new ToggleButton(); import {handleClick} = toggleButton; 上例中解构赋值获取到的handleClick这个方法在执行时就会报错,Class的内部是强制运行在严格模式下的,此处的this在赋值中丢失了原有的指向,在运行时指向了undefined,而undefined是没有属性的。 另一个存在的限制,是没有绑定this的响应函数在异步运行时可能会出问题,当它作为回调函数被传入一个异步执行的方法时,同样会因为丢失了this的指向而引发错误。 如果没有强制指定组件实例方法的this,在将来的使用中就无法安心使用引用转换或作为回调函数传递这样的方式,对于后续使用和协作开发而言都是不方便的。 参考 [1]《javascript高级程序设计(第三版)》 [2]《深入理解javascript函数系列第二篇》https://www.cnblogs.com/xiaohuochai/p/5706289.html [3]《ES6-Class基本语法》https://www.cnblogs.com/ChenChunChang/p/8296350.html

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

Java 基础 之 do while 循环实例

http://www.verejava.com/?id=16992628432522 /** 连续录入学生姓名,输入“q”则系统退出 */ import java.util.Scanner; public class DoWhile2 { public static void main(String[] args) { Scanner in = new Scanner(System.in); System.out.println("请连续录入学生姓名, 直到输入q退出系统"); String name = ""; do { name = in.nextLine(); } while (!name.equals("q")); System.out.println("退出系统"); } } /* while循环和do-while循环的区别? */ http://www.verejava.com/?id=16992628432522

资源下载

更多资源
Mario

Mario

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

腾讯云软件源

腾讯云软件源

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

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

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

用户登录
用户注册