首页 文章 精选 留言 我的

精选列表

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

Javascript基础之-强制类型转换(一)

转换为字符串规则如下图代码: console.log(String(undefined)); // "undefined" console.log(String(null)); // "null" console.log(String(true)); // "true" console.log(String(+0)); // "0" console.log(String(-0)); // "0" console.log(String(-20)); // "-20" console.log(String(Infinity)); // "Infinity" console.log(String(new Number(123))) // 123 console.log(String(new Object())) // [object Object] console.log(100000000000000000000000000000); // 1e+29 大致就是普通其他基本类型转为字符串的话,就直接转为其值的字符串表达形式, 如果是基本类型的封装对象,会先拆封,然后再转为字符串, 如果是普通对象,则会调用其内部[class]的值, 如果是极大数和级小数,将会进行一些转化,具体规则请参考ecma 官方文档https://www.ecma-internationa... 请思考下面的代码 var obj = { toString() { return "toString"; } } console.log(String(obj)) // toString var obj1 = Object.create(null); obj1.valueOf = function() { return "valueOf"; } console.log(String(obj1)); // valueOf 上面这个代码似乎可以看出,实际上普通对象转为字符串的过程,似乎就是toString()的一个过程,也就是调用其内部toString()的一个过程 如果toString()没有咋办,调用valueOf,如果这个也没有呢,就直接报错了。 转换为数字的规则 普通转换为数字的规则如下: console.log(Number(true)); // 1 console.log(Number(0b1101)) // 13 console.log(Number(false)); // 0 console.log(Number("123")); //123 console.log(Number("123a")); //NaN console.log(Number(undefined)); // NaN console.log(Number(null)); // 0 不过呢,如果是对象类型的咋办呢,实际上同上的一点是,如果是基本类型封装后的对象,会先拆封,也就是转为基本类型值,然后再转为数字,比如说 console.log(Number(new String("123123"))); // 123123 console.log(Number([1])); // 1 console.log(Number([1, 3])); // NaN 可能你会疑惑,说,为啥数组只有一个数据的时候可以转换,有两个数据的时候就是NaN了呢,原因在这里,看代码 console.log([1].valueOf()); // [1] console.log([1, 3].valueOf()); // [1, 3] console.log([1].toString()); // 1 console.log([1, 3].toString()); //1, 3 可以看到,valueOf没有返回基本类型值,所以转而使用toString转为基本类型值,这俩字符串再转为数字的结果就一目了然了,和上面的是一样的 那么转为数字的过程中,valueOf和toString是哪个先执行的呢? 看代码 var a = { valueOf() { return 2; }, toString() { return 3; } } var b = { valueOf() { return [1, 3]; }, toString() { return 3; } } var c = { toString() { return 3; } } var d = Object.create(null); console.log(Number(a)); // 2 console.log(Number(b)); // 3 console.log(Number(c)); // 3 console.log(Number(d)); // Uncaught TypeError: Cannot convert object to primitive value 这个其实就说明了一个问题,就是说对象转数字的时候,会先找valueOf(),如果valueOf()没有,或者是说转的是非基本数据类型的,将会使用toString(),最后基本类型专成数字类型的,如果valueOf和toString()都没有,就直接报错了 转化为数字呢,除了以上使用Number(),还可以使用+符号,这个也可以实现从其他类型转换到字符串 console.log(+"abc"); // NaN console.log(+"1111"); //1111 console.log(+[1]); // 1 var obj = { valueOf() { return "1111"; } } console.log(+obj); // 1111 如果原数据类型是Date,那么可以用Number或者+转为以微秒为单位的unix时间戳 var d = new Date( "Mon, 18 Aug 2014 08:53:06 CDT" ); console.log(+d); // 1408369986000 console.log(Number(d)); //1408369986000 parseInt和Number()的区别:看例子 var a = "123abc"; console.log(+a); // NaN console.log(parseInt(a)); // 123 console.log(parseInt("abc123")); // NaN 实际上,parseInt会从左往右进行解析,找到非字符串的时候停止,如果一开始就不可转为数字,那么就返回NaN parseInt其他一些坑点,这里就不细说了,想看的,可以找《你不知道的Javascript》了解细节 // 0 ("0" 来自于 "0.000008") console.log(parseInt( 0.000008 )); // 8 ("8" 来自于 "8e-7") console.log(parseInt( 0.0000008 )); // 250 ("fa" 来自于 "false") console.log(parseInt( false, 16 )); // 15 ("f" 来自于 "function..") console.log(parseInt( parseInt, 16 )); console.log(parseInt( "0x10" )); // 16 console.log(parseInt( "103", 2 )); // 2 转化为布尔值 下面是假值列表,理论上说,除了以下内容以外的值都是true console.log(Boolean(undefined)); console.log(Boolean(null)); console.log(Boolean(false)); console.log(Boolean(+0)); console.log(Boolean(-0)); console.log(Boolean(NaN)); console.log(Boolean("")); 这里有一点需要说明,规范里有提到,所有的对象都是true,所以这方面尤其需要注意的就是下面的例子了 console.log(Boolean(new Boolean(false))); // true console.log(Boolean(new String(""))); // true console.log(Boolean(new Number(0))); // true 返回的都是true,虽然他是对假值得封装,但是他们是对象,是对象就返回true,所以用假值对象或者其他看起来像价值的字符串来进行if判断是不靠谱的 var bObj = new Boolean(false); var a = []; var d = {}; var e = function() {} var f = "false"; var g = "0"; var h = "''"; if (a && d && e && f && g && h) { console.log('all right'); // all right } 一般显式的吧数据转为布尔型,除了使用Boolean()以外,还可以使用!!符号,也就是连着反转两次 console.log(!!undefined); // falseconsole.log(!!new Boolean(false)); // true 好了就到这里了,这一次因为东西比较零散,准备了一阵子,希望大家有所收获 参考书籍《你不知道的Javascript中卷》 本文转载自http://www.lht.ren/article/5/

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

Java SSM框架基础面试题

一、Spring面试题 1、Spring 在ssm中起什么作用?Spring:轻量级框架 作用:Bean工厂,用来管理Bean的生命周期和框架集成。 两大核心: 1、IOC/DI(控制反转/依赖注入) :把dao依赖注入到service层,service层反转给action层,Spring顶层容器为BeanFactory。 2、AOP:面向切面编程 2、Spring的事务?编程式事务管理:编程方式管理事务,极大灵活性,难维护。 声明式事务管理:可以将业务代码和事务管理分离,用注解和xml配置来管理事务。 3、IOC 在项目中的作用?作用:Ioc解决对象之间的依赖问题,把所有Bean的依赖关系通过配置文件或注解关联起来,降低了耦合度。 4、Spring的配置文件中的内容?开启事务注解驱动 事务管理器 开启注解功能,并配置扫描包 配置数据库 配置SQL会话工厂,别名,映射文件 不用编写Dao层的实现类 5、Spring下的注解?注册:@Controller @Service @Component 注入:@Autowired @Resource 请求地址:@RequestMapping 返回具体数据类型而非跳转:@ResponseBody 6、Spring DI 的三种方式?构造器注入:通过构造方法初始化 setter方法注入:通过setter方法初始化 接口注入 7、Spring主要使用了什么模式?工厂模式:每个Bean的创建通过方法 单例模式:默认的每个Bean的作用域都是单例 代理模式:关于Aop的实现通过代理模式 8、IOC,AOP的实现原理?IOC:通过反射机制生成对象注入 AOP:动态代理 二、SpringMvc面试题 1、SpringMvc 的控制器是不是单例模式,如果是,有什么问题,怎么解决?问题:单例模式,在多线程访问时有线程安全问题 解决方法:不要用同步,在控制器里面不能写字段 2、SpringMvc 中控制器的注解?@Controller:该注解表明该类扮演控制器的角色 3、@RequestMapping 注解用在类上的作用?作用:用来映射一个URL到一个类或者一个特定的处理方法上 4、前台多个参数,这些参数都是一个对象,快速得到对象?方法:直接在方法中声明这个对象,SpringMvc就自动把属性赋值到这个对象里面 5、SpringMvc中函数的返回值?String,ModelAndView,List,Set 等 一般String,Ajax请求,返回一个List集合 6、SpringMvc中的转发和重定向?转发: return:“hello” 重定向 :return:“redirect:hello.jsp” 7、SpringMvc和Ajax之间的相互调用?通过JackSon框架把java里面对象直接转换成js可识别的json对象,具体步骤如下: 加入JackSon.jar 在配置文件中配置json的映射 在接受Ajax方法里面直接返回Object,list等,方法前面需要加上注解@ResponseBody 8、SpringMvc的工作流程图? 9、Struts2 和 SpringMvc的区别?入口不同:Struts2:filter过滤器 SpringMvc:一个Servlet即前端控制器 开发方式不同:Struts2:基于类开发,传递参数通过类的属性,只能设置为多例 SpringMvc:基于方法开发(一个url对应一个方法),请求参数传递到方法形参,可以为单例也可以为多例(建议单例) 请求方式不同:Struts2:值栈村塾请求和响应的数据,通过OGNL存取数据 SpringMvc:通过参数解析器将request请求内容解析,给方法形参赋值,将数据和视图封装成ModelAndView对象,最后又将ModelAndView中的模型数据通过request域传输到页面,jsp视图解析器默认使用的是jstl。 三、Mybatis面试题 1、Ibatis和Mybatis?Ibatis:2010年,apache的Ibatis框架停止更新,并移交给了google团队,同时更名为MyBatis。从2010年后Ibatis在没更新过,彻底变成了一个孤儿框架。一个没人维护的框架注定被mybatis拍在沙滩上。 Mybatis:Ibatis的升级版本。 2、什么是Mybatis的接口绑定,有什么好处?Mybatis实现了DAO接口与xml映射文件的绑定,自动为我们生成接口的具体实现,使用起来变得更加省事和方便。 3、什么情况用注解,什么情况用xml绑定?注解使用情况:Sql语句简单时 xml绑定使用情况:xml绑定 (@RequestMap用来绑定xml文件) 4、Mybatis在核心处理类叫什么?SqlSession 5、查询表名和返回实体Bean对象不一致,如何处理?映射键值对即可 column:数据库中表的列名 property:实体Bean中的属性名 6、Mybatis的好处?把Sql语句从Java中独立出来。 封装了底层的JDBC,API的调用,并且能够将结果集自动转换成JavaBean对象,简化了Java数据库编程的重复工作。 自己编写Sql语句,更加的灵活。 入参无需用对象封装(或者map封装),使用@Param注解 7、Mybatis配置一对多? property:属性名 column:共同列 ofType:集合中元素的类型 select:要连接的查询 8、Mybatis配置一对一? property:属性名 select:要连接的查询 column:共同列 javaType:集合中元素的类型 9 、${} 和 #{}的区别?${}:简单字符串替换,把${}直接替换成变量的值,不做任何转换,这种是取值以后再去编译SQL语句。 {}:预编译处理,sql中的#{}替换成?,补全预编译语句,有效的防止Sql语句注入,这种取值是编译好SQL语句再取值。 总结:一般用#{}来进行列的代替10、获取上一次自动生成的主键值? 11、Mybatis如何分页,分页原理?RowBounds对象分页 在Sql内直接书写,带有物理分页 12、Mybatis工作原理? 原理: 通过SqlSessionFactoryBuilder从mybatis-config.xml配置文件中构建出SqlSessionFactory。 SqlSessionFactory开启一个SqlSession,通过SqlSession实例获得Mapper对象并且运行Mapper映射的Sql语句。 完成数据库的CRUD操作和事务提交,关闭SqlSession。 原文发布时间为:2019-1-6本文作者:唐_方本文来自云栖社区合作伙伴“ Web项目聚集地 ”,了解相关信息可以关注“ web_resource”微信公众号

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

Android 基础动画之 scale 渐变缩放

小菜最近在学习 ViewPager 的小动画,说来惭愧,工作这么久了一直没有认真了解过动画这部分,今天特意学习一下 Android 的基本动画。 Android 的基本的动画包括 alpha(透明度)/ scale(缩放)/ translate(位移) / rotate(旋转)四种,小菜今天学习一下 scale 渐变缩放动画效果。 Activity 绑定动画事件: mBtn1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mV1.startAnimation(AnimationUtils.loadAnimation(AnimActivity.this, R.anim.anim_scale)); } }); layout.xml 显示动画效果 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/anim_btn1" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="40dp" android:text="开始动画" /> <View android:id="@+id/anim_v1" android:layout_width="300dp" android:layout_height="150dp" android:layout_gravity="center" android:background="@color/colorAccent" /> </LinearLayout> anim.xml 设置动画属性 <?xml version="1.0" encoding="utf-8"?> <scale xmlns:android="http://schemas.android.com/apk/res/android" android:duration="3500" android:fromXScale="0.0" android:fromYScale="0.0" android:pivotX="100%p" android:pivotY="100%p" android:toXScale="1.0" android:toYScale="1.0" /> 代码很简单,小菜接下来逐条学习一下 anim_scale 中各条属性: 1. android:duration="3500" duration 代表动画过程中持续时常; 2. android:fromXScale="0.0" fromXScale 代表初始时横向 View 比例,0.0为从没有开始动画,1.0即 View 原尺寸,2.0即 View 原尺寸两倍;建议与 toXScale 共同使用; 3. android:fromYScale="0.0" fromYScale 为初始时纵向 View 比例,与 fromXScale 使用相同; 4. android:toXScale="1.0" toXScale 代表动画过程中横向变化尺寸比例,一般与 fromXScale 共同使用; 5. android:toYScale="1.0" toYScale 代表动画过程中纵向变化尺寸比例,一般与 fromYScale 共同使用; 6. android:pivotX="100%p" android:pivotY="100%p" pivotX 和 pivotY 是小菜重点学习的地方,小菜理解为动画起点坐标,可以为整数值、百分数(或者小数)、百分数p 三种样式。 整数值:android:pivotX="100" 整数值类型是相对于自身 View 来定义,以自身 View 左上角的点为原点,水平向右为正,竖直向下为正的坐标系中计算,设置的整数值为 px,为固定值。 百分数/小数:android:pivotX="100%" 百分数/小数类型是相对于自身 View 来定义,与整数值类似,只是坐标点是以自身 View 尺寸比例来计算而非固定值。 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/anim_btn1" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="40dp" android:text="开始动画" /> <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <View android:id="@+id/anim_v1" android:layout_width="300dp" android:layout_height="150dp" android:layout_gravity="center" android:background="@color/colorAccent" /> </FrameLayout> <View android:layout_width="match_parent" android:layout_height="1dp" android:background="@android:color/background_dark" /> <View android:layout_width="1dp" android:layout_height="match_parent" android:layout_gravity="center" android:layout_marginRight="150dp" android:background="@android:color/background_dark" /> </FrameLayout> </LinearLayout> 百分数 + p: 这种方式是最特殊的,小菜理解为自身 View 与相对于某个父容器的大小,并非单纯的根据父容器大小尺寸位置。小菜为了测试方便,设置了一个固定的 400dp*400dp 的 LinearLayout,测试百分数 + p 的方式都正常,但是如果设置 View 居中或其他情况时跟小菜想的很有差距,小菜测试了很久,终于有一些了解。 百分比 + p 这种方式是相对的,既与父容器相关也与自身 View 相关,当设置 View 位置为居中或其他位置时,整个移动的坐标系也会变化,原点并非直接父容器左上角而是自身 View 左上角,整个移动布局根据 View 平移;而父容器是一个框架,动画的范围大小为父容器大小且只在父容器中进行展示。如图: 小菜这才了解到刚开始测试时并未设置 LinearLayout 的 gravity 或自身 View 的 layout_gravity 属性,默认是居左上角,此时与父容器左上角重合。 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/anim_btn1" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="40dp" android:text="开始动画" /> <FrameLayout android:layout_width="wrap_content" android:layout_height="wrap_content" > <LinearLayout android:layout_width="400dp" android:layout_height="400dp" android:background="#8099cc00" android:layout_marginLeft="50dp" android:layout_marginTop="125dp" android:gravity="center" android:orientation="horizontal" /> <LinearLayout android:layout_width="400dp" android:layout_height="400dp" android:background="#80008577" android:gravity="center" android:orientation="horizontal" > <View android:id="@+id/anim_v1" android:layout_width="300dp" android:layout_height="150dp" android:background="@color/colorAccent" android:gravity="center" android:text="Hello World!" /> </LinearLayout> <View android:layout_width="4dp" android:layout_height="4dp" android:layout_marginLeft="248dp" android:layout_marginTop="123dp" android:background="@android:color/background_dark" /> <View android:layout_width="4dp" android:layout_height="4dp" android:layout_marginLeft="48dp" android:layout_marginTop="323dp" android:background="@android:color/background_dark" /> <View android:layout_width="4dp" android:layout_height="4dp" android:layout_marginLeft="248dp" android:layout_marginTop="323dp" android:background="@android:color/background_dark" /> <View android:layout_width="4dp" android:layout_height="4dp" android:layout_marginLeft="448dp" android:layout_marginTop="523dp" android:background="@android:color/background_dark" /> </FrameLayout> </LinearLayout> 7. android:interpolator="@android:anim/accelerate_decelerate_interpolator" interpolator 代表缩放动画曲线,即动画由大变小,变换速率等,小菜目前还未学习到,后期补充。 小菜的动画部分是短板,正在从零学习,不对的请多多指正!

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

GeoMesa时空基础及应用场景

PPT下载地址:https://yq.aliyun.com/download/3266 视频回看:https://yq.aliyun.com/live/793 内容概要:GeoMesa是一款开源的基于分布式计算系统的面向海量时空数据查询与分析的工具包。本报告首先介绍了GeoMesa基于HBase系统的整体架构与部署架构,其次,分析了其时空索引原理与算法实现,最后简要介绍了GeoMesa与Spark、Kafka、Lambda等开源系统或架构的整合方式。 讲师:肖斐——阿里云数据库技术专家 直播地址HBase生态+Spark社区钉钉大群方式一:扫码入群看直播方式二:Link入群看直播https://dwz.cn/Fvqv066s 直播时间:每周二 18:00-18:30 分享;18:30- 18:45~ 问答

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

神经网络基础及Keras入门

神经网络定义 人工神经网络,简称神经网络,在机器学习和认知科学领域,是一种模仿生物神经网络(动物的中枢神经系统,特别是大脑)的结构和功能的数学模型或计算模型,用于对函数进行估计或近似。 为了描述神经网络,我们先从最简单的神经网络讲起,这个神经网络仅由一个“神经元”构成,以下即是这个“神经元”的图示: 可以看出,这个单一“神经元”的输入-输出映射关系其实就是一个逻辑回归(logistic regression)。 神经网络模型 所谓神经网络就是将许多个单一“神经元”联结在一起,这样,一个“神经元”的输出就可以是另一个“神经元”的输入。例如,下图就是一个简单的神经网络: Keras实战 使用keras实现如下网络结构, 并训练模型: 输入值(x1,x2,x3)代表人的身高体重和年龄, 输出值(y1,y2) importnumpyasnp #总人数是1000,一半是男生 n=1000 #所有的身体指标数据都是标准化数据,平均值0,标准差1 tizhong=np.random.normal(size=n) shengao=np.random.normal(size=n) nianling=np.random.normal(size=n) #性别数据,前500名学生是男生,用数字1表示 gender=np.zeros(n) gender[:500]=1 #男生的体重比较重,所以让男生的体重+1 tizhong[:500]+=1 #男生的身高比较高,所以让男生的升高+1 shengao[:500]+=1 #男生的年龄偏小,所以让男生年龄降低1 nianling[:500]-=1 创建模型 fromkerasimportSequential fromkeras.layersimportDense,Activation model=Sequential() #只有一个神经元,三个输入数值 model.add(Dense(4,input_dim=3,kernel_initializer='random_normal',name="Dense1")) #激活函数使用softmax model.add(Activation('relu',name="hidden")) #添加输出层 model.add(Dense(2,input_dim=4,kernel_initializer='random_normal',name="Dense2")) #激活函数使用softmax model.add(Activation('softmax',name="output")) 编译模型 需要指定优化器和损失函数: model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy']) 训练模型 #转换成one-hot格式 fromkerasimportutils gender_one_hot=utils.to_categorical(gender,num_classes=2) #身体指标都放入一个矩阵data data=np.array([tizhong,shengao,nianling]).T #训练模型 model.fit(data,gender_one_hot,epochs=10,batch_size=8) 输出(stream): Epoch1/10 1000/1000[==============================]-0s235us/step-loss:0.6743-acc:0.7180 Epoch2/10 1000/1000[==============================]-0s86us/step-loss:0.6162-acc:0.7310 Epoch3/10 1000/1000[==============================]-0s88us/step-loss:0.5592-acc:0.7570 Epoch4/10 1000/1000[==============================]-0s87us/step-loss:0.5162-acc:0.7680 Epoch5/10 1000/1000[==============================]-0s89us/step-loss:0.4867-acc:0.7770 Epoch6/10 1000/1000[==============================]-0s88us/step-loss:0.4663-acc:0.7830 Epoch7/10 1000/1000[==============================]-0s87us/step-loss:0.4539-acc:0.7890 Epoch8/10 1000/1000[==============================]-0s86us/step-loss:0.4469-acc:0.7920 Epoch9/10 1000/1000[==============================]-0s88us/step-loss:0.4431-acc:0.7940 Epoch10/10 1000/1000[==============================]-0s88us/step-loss:0.4407-acc:0.7900 输出(plain)://Python学习开发705673780 进行预测 test_data=np.array([[0,0,0]]) probability=model.predict(test_data) ifprobability[0,0]>0.5: print('女生') else: print('男生') ### 输出(stream): 女生 关键词解释 input_dim: 输入的维度数 kernel_initializer: 数值初始化方法, 通常是正太分布 batch_size: 一次训练中, 样本数据被分割成多个小份, 每一小份包含的样本数叫做batch_size epochs: 如果说将所有数据训练一次叫做一轮的话。epochs决定了总共进行几轮训练。 optimizer: 优化器, 可以理解为求梯度的方法 loss: 损失函数, 可以理解为用于衡量估计值和观察值之间的差距, 差距越小, loss越小 metrics: 类似loss, 只是metrics不参与梯度计算, 只是一个衡量算法准确性的指标, 分类模型就用accuracy 看完觉得有所收获的朋友可以点赞加关注哦,谢谢支持!

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

WebRTC基础实践 - 1. WebRTC简介

WebRTC 是一个开源的实时通信项目, 主要目标是对Web/原生App平台上的语音、视频、以及数据传输等实时通讯提供支持。 WebRTC 主要包括以下 JavaScript API(点击链接可查看相关demo)。 getUserMedia(): 获取用户设备的音频和视频. MediaRecorder: 录制音频和视频. RTCPeerConnection: 流式传输两个客户端之间的音频与视频. RTCDataChannel: 在两个客户端之间传输数据流. WebRTC的平台支持情况 目前, PC版和Android版的 Firefox、Opera 和 Chrome 浏览器都支持WebRTC。 此外、iOS和Android的一些原生App也支持WebRTC。 译者注: 国内使用量巨大的360浏览器、搜狗浏览器兼容性基本和Chrome一致。当然, 推荐使用最新的版本(当前时间: 2018年6月28日)。 信令(signaling) WebRTC 通过 RTCPeerConnection 在浏览器之间进行流数据传输, 但还需要一种机制, 来协调通信以及发送控制指令, 这个过程就叫做信令控制. WebRTC 没有规定具体使用的协议或方法。 在本教程中, 我们使用 Socket.IO 来传递消息, 当然也可以使用 其他实现。 STUN和TURN简介 WebRTC 是基于点对点(peer-to-peer)网络设计的, 在理想环境中, 双方通过路由器进行直连. 但在现实世界中, 两个客户端之间, 需要穿透防火墙以及 NAT 网关, 如果直连失败, 则需要回退降级。所以, 为了应对各种复杂的网络环境, WebRTC API 需要使用 STUN 服务器的帮助, 来获取双方的公网IP, 如果对等连接失败, 则需要使用 TURN 服务器作为中继服务器. 现实世界中的网络环境是什么样子的呢, 请参考 WebRTC in the real world WebRTC的安全性 WebRTC的所有组件强制加密. 相关的JavaScript API也只能在安全的域名中使用(即 HTTPS 或者 localhost). 但WebRTC标准没有指定信令机制, 所以需要开发者确保使用了安全传输协议。 更多信息和资源, 请参考: http://webrtc.org/start 相关词汇对照: capture : 获取、抓取 audio : 音频 video : 视频 stream : 流 data stream : 数据流 record : 录制、记录 signaling : 信令 Encryption : 加密 relay server : 中继服务器 peer-to-peer: 点对点网络 原文链接: https://codelabs.developers.google.com/codelabs/webrtc-web/#0 翻译人员: 铁锚 - https://blog.csdn.net/renfufei 翻译日期: 2018年06月28日

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

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.

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

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

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

多线程基础篇(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方法。 欢迎各位指出错误以及不足,谢谢

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

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 更高,速度更快,因为他不记录事务日志,会释放数据,索引占据的空间,删除的数据不可恢复。

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Mario

Mario

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

Spring

Spring

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

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

用户登录
用户注册