首页 文章 精选 留言 我的

精选列表

搜索[学习],共10000篇文章
优秀的个人博客,低调大师

一篇文章带你学习分布式事务

一. 事务 1.1 什么是事务 数据库事务(简称:事务,Transaction)是指数据库执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。 事务拥有以下四个特性,习惯上被称为ACID特性: 原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。 一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态是指数据库中的数据应满足完整性约束。除此之外,一致性还有另外一层语义,就是事务的中间状态不能被观察到(这层语义也有说应该属于原子性)。 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行,如同只有这一个操作在被数据库所执行一样。 持久性(Durability):已被提交的事务对数据库的修改应该永久保存在

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

JavaScript高级程序设计学习(四)之引用类型(续)

一、Date类型 其实引用类型和相关的操作方法,远远不止昨天的所说的那些,还有一部分今天继续补充。 在java中日期Date,它所属的包有sql包,也有util包。我个人比较喜欢用util包的。理由,顺手习惯,个人也觉得比较好用。sql的Date类用的不算多。也用过。 下面就进入正题吧,不单单在java,在js中也存在日期函数,或换言之,日期对象。 var now = new Date(); 通过now.getYear(),now.getMonth(),now.getMonth(),简单的翻译,可以理解为获得当前的年月日。 now翻译过来表示现在,之所以采取这样的命名,还是那就话,规范起见。 js获得的日期,通常情况要么通过字符串拼接,要么通过格式转换,达到自己想要的那样。 常用的格式转换方法如下:  toDateString()——以特定于实现的格式显示星期几、月、日和年;  toTimeString()——以特定于实现的格式显示时、分、秒和时区;  toLocaleDateString()——以特定于地区的格式显示星期几、月、日和年;  toLocaleTimeString()——以特定于实现的格式显示时、分、秒;  toUTCString()——以特定于实现的格式完整的 UTC日期。 与 toLocaleString()和 toString()方法一样,以上这些字符串格式方法的输出也是因浏览器 而异的,因此没有哪一个方法能够用来在用户界面中显示一致的日期信息; 日期和时间组件方法如下(大家可以参考): getTime() 返回表示日期的毫秒数;与valueOf()方法返回的值相同 setTime(毫秒) 以毫秒数设置日期,会改变整个日期 getFullYear() 取得4位数的年份(如2007而非仅07) getUTCFullYear() 返回UTC日期的4位数年份 setFullYear(年) 设置日期的年份。传入的年份值必须是4位数字(如2007而非仅07) setUTCFullYear(年) 设置UTC日期的年份。传入的年份值必须是4位数字(如2007而非仅07) getMonth() 返回日期中的月份,其中0表示一月,11表示十二月 getUTCMonth() 返回UTC日期中的月份,其中0表示一月,11表示十二月 setMonth(月) 设置日期的月份。传入的月份值必须大于0,超过11则增加年份 setUTCMonth(月) 设置UTC日期的月份。传入的月份值必须大于0,超过11则增加年份 getDate() 返回日期月份中的天数(1到31) getUTCDate() 返回UTC日期月份中的天数(1到31) setDate(日) 设置日期月份中的天数。如果传入的值超过了该月中应有的天数,则增加月份 setUTCDate(日) 设置UTC日期月份中的天数。如果传入的值超过了该月中应有的天数,则增加月份 getDay() 返回日期中星期的星期几(其中0表示星期日,6表示星期六) getUTCDay() 返回UTC日期中星期的星期几(其中0表示星期日,6表示星期六) getHours() 返回日期中的小时数(0到23) getUTCHours() 返回UTC日期中的小时数(0到23) setHours(时) 设置日期中的小时数。传入的值超过了23则增加月份中的天数 setUTCHours(时) 设置UTC日期中的小时数。传入的值超过了23则增加月份中的天数 getMinutes() 返回日期中的分钟数(0到59) getUTCMinutes() 返回UTC日期中的分钟数(0到59) setMinutes(分) 设置日期中的分钟数。传入的值超过59则增加小时数 setUTCMinutes(分) 设置UTC日期中的分钟数。传入的值超过59则增加小时数 getSeconds() 返回日期中的秒数(0到59) getUTCSeconds() 返回UTC日期中的秒数(0到59) setSeconds(秒) 设置日期中的秒数。传入的值超过了59会增加分钟数 setUTCSeconds(秒) 设置UTC日期中的秒数。传入的值超过了59会增加分钟数 getMilliseconds() 返回日期中的毫秒数 getUTCMilliseconds() 返回UTC日期中的毫秒数 setMilliseconds(毫秒) 设置日期中的毫秒数 二、RegExp 类型 RegExp,简单的说,就是正则表达式,正则表达式,在开发中用的也很多,比如表单校验,手机号码,邮箱,网关,昵称,qq号,身份证等,当然在java中也有相关的方法和工具类可以完成验证,但是那样的话,全部请求就会走向服务器,那么对于服务器负载也是很大的。所以这也是js的一大亮点,减少服务器压力,同时也给用户比较好的体验。试问,如果走服务器的话,在过去网络这玩意,是个新鲜货,人们那时填写表单点击提交,基本都是跑服务器,但是有的时候不小心点错了或者大意疏忽了,好不容易填完十几个表单输入框,最后告知你,有几个必填项或者几个不符合要求的项,要求你重新填写,在那个时候,人们不得不接受,但是在这个用户量即市场的互联网时代,可以说用户就是上帝,如果用户不满意相当于自取灭亡。 正则表达式在此就可以发挥作用了,当用户填写不合法,直接提示相关文字信息,当必填项没填写直接表单被红框标记,现在的html5表单校验就是基于正则和一些相关原生js方法,这个我曾经在我开发的博客中用过。挺好用的。 当然,正则表达式的作用远远不止这么点,它可以根据字符匹配,这样就有个小小的匹配功能,也可以说搜索功能,当然这个搜索肯定是不能和百度那样的公司比的。这个搜索只不过是字符匹配。 (1)RegExp实例属性  global:布尔值,表示是否设置了 g 标志。  ignoreCase:布尔值,表示是否设置了 i 标志。  lastIndex:整数,表示开始搜索下一个匹配项的字符位置,从 0算起。  multiline:布尔值,表示是否设置了 m 标志。  source:正则表达式的字符串表示,按照字面量形式而非传入构造函数中的字符串模式返回。 (2)RegExp实例方法 RegExp 对象的主要方法是 exec(),该方法是专门为捕获组而设计的。exec()接受一个参数,即 要应用模式的字符串,然后返回包含第一个匹配项信息的数组;或者在没有匹配项的情况下返回 null。 返回的数组虽然是 Array 的实例,但包含两个额外的属性:index 和 input。其中,index 表示匹配 项在字符串中的位置,而 input 表示应用正则表达式的字符串。在数组中,第一项是与整个模式匹配 的字符串,其他项是与模式中的捕获组匹配的字符串(如果模式中没有捕获组,则该数组只包含一项)。 var text = "mom and dad and baby"; var pattern = /mom( and dad( and baby)?)?/gi; var matches = pattern.exec(text); alert(matches.index); // 0 alert(matches.input); // "mom and dad andbaby" alert(matches[0]); // "mom and dad and baby" alert(matches[1]); // " and dad and baby" alert(matches[2]); // " and baby" var text = "this has been a short summer"; var pattern = /(.)hort/g; /* * 注意:Opera 不支持 input、lastMatch、lastParen 和 multiline 属性 * Internet Explorer 不支持 multiline 属性 */ if (pattern.test(text)){ alert(RegExp.input); // this has been a short summer alert(RegExp.leftContext); // this has been a alert(RegExp.rightContext); // summer alert(RegExp.lastMatch); // short alert(RegExp.lastParen); // s alert(RegExp.multiline); // false } 以上代码创建了一个模式,匹配任何一个字符后跟 hort,而且把第一个字符放在了一个捕获组中。 RegExp 构造函数的各个属性返回了下列值:  input 属性返回了原始字符串;  leftContext 属性返回了单词 short 之前的字符串,而 rightContext 属性则返回了 short 之后的字符串;  lastMatch 属性返回近一次与整个正则表达式匹配的字符串,即 short;  lastParen 属性返回近一次匹配的捕获组,即例子中的 s。 (3)模式的局限性 尽管 ECMAScript中的正则表达式功能还是比较完备的,但仍然缺少某些语言(特别是 Perl)所支 持的高级正则表达式特性。下面列出了 ECMAScript正则表达式不支持的特性。  匹配字符串开始和结尾的\A 和\Z 锚①  向后查找(lookbehind)②  并集和交集类  原子组(atomic grouping)  Unicode支持(单个字符除外,如\uFFFF)  命名的捕获组③  s(single,单行)和 x(free-spacing,无间隔)匹配模式  条件匹配  正则表达式注释 三、Function 说起来 ECMAScript中什么有意思,我想那莫过于函数了——而有意思的根源,则在于函数实际 上是对象。每个函数都是 Function 类型的实例,而且都与其他引用类型一样具有属性和方法。由于函 数是对象,因此函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。函数通常是使用函 数声明语法定义的。 function sum (num1, num2) { return num1 + num2; } 与下面这个并没有什么差异 var sum = function(num1, num2){ return num1 + num2; }; 以上代码定义了变量 sum 并将其初始化为一个函数。有读者可能会注意到,function 关键字后面 没有函数名。这是因为在使用函数表达式定义函数时,没有必要使用函数名——通过变量 sum 即可以引 用函数。另外,还要注意函数末尾有一个分号,就像声明其他变量时一样。 下面示例不推荐使用: var sum = new Function("num1", "num2", "return num1 + num2"); // 不推荐 function sum(num1, num2){ return num1 + num2; } alert(sum(10,10)); //20 var anotherSum = sum; alert(anotherSum(10,10)); //20 sum = null; alert(anotherSum(10,10)); //20 以上代码首先定义了一个名为 sum()的函数,用于求两个值的和。然后,又声明了变量 anotherSum, 并将其设置为与 sum 相等(将 sum 的值赋给 anotherSum)。注意,使用不带圆括号的函数名是访问函 数指针,而非调用函数。此时,anotherSum 和 sum 就都指向了同一个函数,因此 anotherSum()也 可以被调用并返回结果。即使将 sum 设置为 null,让它与函数“断绝关系”,但仍然可以正常调用 anotherSum()。 var addSomeNumber = function (num){ return num + 100; }; addSomeNumber = function (num) { return num + 200; }; var result = addSomeNumber(100); //300 通过观察重写之后的代码,很容易看清楚到底是怎么回事儿——在创建第二个函数时,实际上覆盖 了引用第一个函数的变量 addSomeNumber。 (1)函数声明和函数表达式 我们一直没有对函数声明和函数表达式加以区别。而实际上,解析器在向执行环 境中加载数据时,对函数声明和函数表达式并非一视同仁。解析器会率先读取函数声明,并使其在执行 任何代码之前可用(可以访问);至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真 正被解释执行。 示例: alert(sum(10,10)); function sum(num1, num2){ return num1 + num2; } 因为在代码开始执行之前,解析器就已经通过一个名为函数声明提升 (function declaration hoisting)的过程,读取并将函数声明添加到执行环境中。对代码求值时,JavaScript 引擎在第一遍会声明函数并将它们放到源代码树的顶部。所以,即使声明函数的代码在调用它的代码后 面,JavaScript 引擎也能把函数声明提升到顶部。 如果像下面例子所示的,把上面的函数声明改为等价 的函数表达式,就会在执行期间导致错误。 示例: alert(sum(10,10)); var sum = function(num1, num2){ return num1 + num2; }; 这个就会导致常见js错误,函数未定义错误。 (2)作为值函数 因为 ECMAScript中的函数名本身就是变量,所以函数也可以作为值来使用。也就是说,不仅可以 像传递参数一样把一个函数传递给另一个函数,而且可以将一个函数作为另一个函数的结果返回。 function callSomeFunction(someFunction, someArgument){ return someFunction(someArgument); } 这个函数接受两个参数。第一个参数应该是一个函数,第二个参数应该是要传递给该函数的一个值。 然后,就可以像下面的例子一样传递函数了。 function add10(num){ return num + 10; } var result1 = callSomeFunction(add10, 10); alert(result1); //20 function getGreeting(name){ return "Hello, " + name; } var result2 = callSomeFunction(getGreeting, "Nicholas"); alert(result2); //"Hello, Nicholas" 这里的 callSomeFunction()函数是通用的,即无论第一个参数中传递进来的是什么函数,它都 会返回执行第一个参数后的结果。还记得吧,要访问函数的指针而不执行函数的话,必须去掉函数名后 面的那对圆括号。因此上面例子中传递给 callSomeFunction()的是 add10 和 getGreeting,而不 是执行它们之后的结果。 当然,可以从一个函数中返回另一个函数,而且这也是极为有用的一种技术。例如,假设有一个 对象数组,我们想要根据某个对象属性对数组进行排序。而传递给数组 sort()方法的比较函数要接收 两个参数,即要比较的值。可是,我们需要一种方式来指明按照哪个属性来排序。要解决这个问题, 可以定义一个函数,它接收一个属性名,然后根据这个属性名来创建一个比较函数,下面就是这个函 数的定义。 function createComparisonFunction(propertyName) { return function(object1, object2){ var value1 = object1[propertyName]; var value2 = object2[propertyName]; if (value1 < value2){ return -1; } else if (value1 > value2){ return 1; } else { return 0; } }; } 这个函数定义看起来有点复杂,但实际上无非就是在一个函数中嵌套了另一个函数,而且内部函数 前面加了一个 return 操作符。在内部函数接收到 propertyName 参数后,它会使用方括号表示法来 取得给定属性的值。取得了想要的属性值之后,定义比较函数就非常简单了。 var data = [{name: "Zachary", age: 28}, {name: "Nicholas", age: 29}]; data.sort(createComparisonFunction("name")); alert(data[0].name); //Nicholas data.sort(createComparisonFunction("age")); alert(data[0].name); //Zachary 这里,我们创建了一个包含两个对象的数组 data。其中,每个对象都包含一个 name 属性和一个 age 属性。在默认情况下,sort()方法会调用每个对象的 toString()方法以确定它们的次序;但得 到的结果往往并不符合人类的思维习惯。因此,我们调用 createComparisonFunction("name")方 法创建了一个比较函数,以便按照每个对象的 name 属性值进行排序。而结果排在前面的第一项是 name 为"Nicholas",age 是 29的对象。然后,我们又使用了 createComparisonFunction("age")返回 的比较函数,这次是按照对象的 age 属性排序。得到的结果是 name 值为"Zachary",age 值是 28的 对象排在了第一位。 (3)函数内部属性 在函数内部,有两个特殊的对象:arguments 和 this。其中,arguments 在第 3章曾经介绍过, 它是一个类数组对象,包含着传入函数中的所有参数。虽然 arguments 的主要用途是保存函数参数, 但这个对象还有一个名叫 callee 的属性,该属性是一个指针,指向拥有这个 arguments 对象的函数。 请看下面这个非常经典的阶乘函数: function factorial(num){ if (num <=1) { return 1; } else { return num * factorial(num-1) } } IE、Firefox、Chrome和 Safari的所有版本以及 Opera 9.6都支持 caller 属性。 当函数在严格模式下运行时,访问 arguments.callee 会导致错误。ECMAScript 5 还定义了 arguments.caller 属性,但在严格模式下访问它也会导致错误,而在非严格模式下这个属性始终是 undefined。定义这个属性是为了分清 arguments.caller 和函数的 caller 属性。以上变化都是为 了加强这门语言的安全性,这样第三方代码就不能在相同的环境里窥视其他代码了。 严格模式还有一个限制:不能为函数的 caller 属性赋值,否则会导致错误。 (4)函数属性和方法 前面曾经提到过,ECMAScript 中的函数是对象,因此函数也有属性和方法。每个函数都包含两个 属性:length 和 prototype。其中,length 属性表示函数希望接收的命名参数的个数。 如下所示: function sayName(name){ alert(name); } function sum(num1, num2){ return num1 + num2; } function sayHi(){ alert("hi"); } alert(sayName.length); //1 alert(sum.length); //2 alert(sayHi.length); //0 在 ECMAScript 核心所定义的全部属性中,耐人寻味的就要数 prototype 属性了。对于 ECMAScript 中的引用类型而言,prototype 是保存它们所有实例方法的真正所在。换句话说,诸如 toString()和 valueOf()等方法实际上都保存在 prototype 名下,只不过是通过各自对象的实例访 问罢了。在创建自定义引用类型以及实现继承时,prototype 属性的作用是极为重要的。在 ECMAScript 5中,prototype 属性是不可枚举的,因此使用 for-in 无法发现。 每个函数都包含两个非继承而来的方法:apply()和 call()。这两个方法的用途都是在特定的作 用域中调用函数,实际上等于设置函数体内 this 对象的值。首先,apply()方法接收两个参数:一个 是在其中运行函数的作用域,另一个是参数数组。其中,第二个参数可以是 Array 的实例,也可以是 arguments 对象。 function sum(num1, num2){ return num1 + num2; } function callSum1(num1, num2){ return sum.apply(this, arguments); // 传入 arguments 对象 } function callSum2(num1, num2){ return sum.apply(this, [num1, num2]); // 传入数组 } alert(callSum1(10,10)); //20 alert(callSum2(10,10)); //20 注意: 在严格模式下,未指定环境对象而调用函数,则 this 值不会转型为 window。 除非明确把函数添加到某个对象或者调用 apply()或 call(),否则 this 值将是 undefined。 四、基本包装类型 说到包装类型,在Java中不得不提基本数据类型的包装类。 在此温习下 Java基本数据类型及其对应的包装类 int - Integer byte - Byte long - Long short - Short char - Char float - Float double - Double boolean - Boolean 在js中包装类型属于引用类型,Java同样如此。 ECMAScript提供了三个包装类,String,Boolean,Number,分别对应的类型,字符类型,布尔类型,数字类型。 对于包装类型不做太多讲解,学过Java的人容易上手,没有学过Java的人理解也不存在困难。 这里讲解是包装类有什么用,最直接直白的作用就是,类型转换非常简单。不需要什么parseInt,toString,parseFloat等等进行调用转换方法。直接var b = new String(“1”),即可将其转为字符串类型。 其他也同理。 String,Boolean,Number我就在此不提太多了。 有一个要提Global,这个要提的原因主要是设计URL编码和解码问题,这个在实际应用中非常广,比如邮件验证,url加密。 编码和解码,可理解为加密和解密的过程,代码如下: <html> <meta charset="utf-8"> <head> <script> var uri = "http://www.baidu.com/illegal?userId=1"; alert(encodeURI(uri)); alert(encodeURIComponent(uri)); </script> </head> <body> </body> </html> 使用 encodeURI()编码后的结果是除了空格之外的其他字符都原封不动,只有空格被替换成了 %20。而 encodeURIComponent()方法则会使用对应的编码替换所有非字母数字字符。这也正是可以 对整个URI使用encodeURI(),而只能对附加在现有URI后面的字符串使用encodeURIComponent() 的原因所在。 与 encodeURI()和 encodeURIComponent()方法对应的两个方法分别是 decodeURI()和 decodeURIComponent()。其中,decodeURI()只能对使用 encodeURI()替换的字符进行解码。例如, 它可将%20 替换成一个空格,但不会对%23 作任何处理,因为%23 表示井字号(#),而井字号不是使用 encodeURI()替换的。同样地,decodeURIComponent()能够解码使用 encodeURIComponent()编码,的所有字符,即它可以解码任何特殊字符的编码。 示例如下所示: <html> <meta charset="utf-8"> <head> <script> var uri = "http%3A%2F%2Fwww.baidu.com%2Fillegal%20value.htm%23start"; alert(decodeURI(uri)); alert(decodeURIComponent(uri)); </script> </head> <body> </body> </html> 这里,变量 uri 包含着一个由 encodeURIComponent()编码的字符串。在第一次调用 decodeURI() 输出的结果中,只有%20 被替换成了空格。而在第二次调用 decodeURIComponent()输出的结果中, 所有特殊字符的编码都被替换成了原来的字符,得到了一个未经转义的字符串(但这个字符串并不是一 个有效的 URI)。 注意: URI方法 encodeURI()、encodeURIComponent()、decodeURI()和 decode- URIComponent()用于替代已经被ECMA-262第3版废弃的escape()和unescape() 方法。URI方法能够编码所有 Unicode字符,而原来的方法只能正确地编码 ASCII字符。 因此在开发实践中,特别是在产品级的代码中,一定要使用URI方法,不要使用 escape() 和unescape()方法。 eval这个方法我常用,主要是将json格式数据解析出来。 另外再提一个Math对象,该对象如其名"数学",主要涉及数学相关的 同时还有min和max方法,最小值和最大值 直接var num = Math.max(3,2,1,0) var num2 = Math.min(1,2,3,4) 就可以得出最大值和最小值。 加入购物车进行计算涉及该方法  Math.ceil()执行向上舍入,即它总是将数值向上舍入为接近的整数;  Math.floor()执行向下舍入,即它总是将数值向下舍入为接近的整数; 对象在 JavaScript 中被称为引用类型的值,而且有一些内置的引用类型可以用来创建特定的对象, 现简要总结如下:  引用类型与传统面向对象程序设计中的类相似,但实现不同;  Object 是一个基础类型,其他所有类型都从 Object 继承了基本的行为;  Array 类型是一组值的有序列表,同时还提供了操作和转换这些值的功能;  Date 类型提供了有关日期和时间的信息,包括当前日期和时间以及相关的计算功能;  RegExp 类型是 ECMAScript支持正则表达式的一个接口,提供了基本的和一些高级的正则表 达式功能。 函数实际上是 Function 类型的实例,因此函数也是对象;而这一点正是 JavaScript有特色的地 方。由于函数是对象,所以函数也拥有方法,可以用来增强其行为。 因为有了基本包装类型,所以 JavaScript 中的基本类型值可以被当作对象来访问。三种基本包装类 型分别是:Boolean、Number 和 String。以下是它们共同的特征:  每个包装类型都映射到同名的基本类型;  在读取模式下访问基本类型值时,就会创建对应的基本包装类型的一个对象,从而方便了数据 操作;  操作基本类型值的语句一经执行完毕,就会立即销毁新创建的包装对象。 在所有代码执行之前,作用域中就已经存在两个内置对象:Global 和 Math。在大多数ECMAScript 实现中都不能直接访问 Global 对象;不过,Web 浏览器实现了承担该角色的 window 对象。全局变 量和函数都是 Globa  Math.round()执行标准舍入,即它总是将数值四舍五入为接近的整数(这也是我们在数学课 上学到的舍入规则)。 继续昨天没总结完了,该篇是昨天和今天的内容补充。

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

JavaScript高级程序设计学习(二)之基本概念

任何语言的核心都必然会描述这门语言基本的工作原理。而描述的内容通常都要涉及这门语 言的语法、操作符、数据类型、内置功能等用于构建复杂解决方案的基本概念。如前所述, ECMA-262通过叫做 ECMAScript的“伪语言”为我们描述了 JavaScript的所有这些基本概念 本篇文章主要讲语法,类型,操作符,语句和函数。 任何编程,都是从基础的语法开始,例如java,java也有基础语法,操作符,数据类型,控制语句等。c++,php,python也同理。 js与java在变量上有其共同点,这也是很多编程语言也有的共性: 一、语法 变量命名: 1.区分大小写,例如在js中给变量命名可以是var,也可以是let, var t和vat T是两个变量,而并不是一个变量; 2.标识符,不能以数字开头,第一个字符必须是一个字母,下划线或$符号,建议命名驼峰命名法,首字母小写,剩下的第二个单词字母大写; 3.不得以关键字和保留字进行命名,例如True命名可以,true则不行,因为true是js的基本数据类型boolean类型; 关键字: 保留字: 当然上述列出的只是一部分,还有其他,其他可以去w3school参考:http://www.w3school.com.cn/js/ 变量有这么几种类型? 1.number 2.string 3.boolean 4.undefined 5.Object 上述js数据类型分别对应数字,字符串,布尔,未定义,对象等类型 typeof操作符可以得到该变量的类型 说到基本数据类型,不得不提一个类型转换,类型转换报错是很常见的: 类型转换应用: 比如字符串1,2,3,或者3.24,true等,我如何将其转换为数字类型,可使用parseInt(),parseFloat(),parseDouble()等方法可以进行转换 数字如何转字符串,toString()方法即可解决 undefined,有的时候我们添加数据时,有那么几个非必填项,但在列表展示时,常常展示为undefined,为什么会出现undefined,因为没有初始值,在java中int类型可默认初始值为0,声明尚未实现可以这样,js就不能这样,所以我们通常就要写if判断,当等于这个undefined时,我们给它赋个空串。 操作符也是开发中常用的,一元操作符,运算符,位操作符,布尔操作符,关系操作符,条件操作符,在这里我只说关系和条件,因为这两个用的比较多,当然还有布尔操作符,一元操作符,运算符等,位运算符,用的少,几乎没用过。 一元操作符类似于我们的if(condition){}else{},运算符,加减乘除,特别是关于购物车钱的计算及其日期的计算,例如下图示例: //本周 var date = new Date(); // 本周一的日期 date.setDate(date.getDate() - date.getDay() + 1); var begin = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate() + " 00:00:00"; // 本周日的日期 date.setDate(date.getDate() + 6); var end = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate() + " 23:59:59"; //当天 var year = now.getFullYear(); //年 var month = now.getMonth() + 1; //月 var day = now.getDate(); //日 var current=year+"-"+month+"-"+day; //最近一个月 function timeForMat (count) { // 拼接时间 let time1 = new Date() time1.setTime(time1.getTime() - (24 * 60 * 60 * 1000)) let Y1 = time1.getFullYear() let M1 = ((time1.getMonth() + 1) > 10 ? (time1.getMonth() + 1) : '0' + (time1.getMonth() + 1)) let D1 = (time1.getDate() > 10 ? time1.getDate() : '0' + time1.getDate()) let timer1 = Y1 + '-' + M1 + '-' + D1 // 当前时间 let time2 = new Date() time2.setTime(time2.getTime() - (24 * 60 * 60 * 1000 * count)) let Y2 = time2.getFullYear() let M2 = ((time2.getMonth() + 1) > 10 ? (time2.getMonth() + 1) : '0' + (time2.getMonth() + 1)) let D2 = (time2.getDate() > 10 ? time2.getDate() : '0' + time2.getDate()) let timer2 = Y2 + '-' + M2 + '-' + D2 // 之前的7天或者30天 return { t1: timer1, t2: timer2 } } var timer = timeForMat(30) alert(timer.t1) 布尔操作符,也比较常用,与(&),或(||),非(!) 关系操作符小于(<)、大于(>)、小于等于(<=)和大于等于(>=)这几个关系操作符用于对两个值进行比 较,比较的规则与我们在数学课上所学的一样。 条件操作符类似我们的if..else (1)var max = (num1 > num2) ? num1 : num2; (2)if(num1>num2){ alert(num1) }else{ alert(num2) } (1)和(2)实际上是一样的,没多大区别。不过注意的是写if时,连else也写了吧,这样逻辑清晰,还有if(){},这个{}可以省略,但建议不要省略。 二、语句 1.if语句 这个常用,就不多说了,总而言之,写if,要写就写全套,不要省略else if的话表单验证用的挺多的,我开发用的js验证全部用if 例如: 常见的密码一致性问题 if(password==null||password==""){ alert("密码不能为空"); return false; }else if(password2==null||password2==""){ alert("确认密码不能为空"); return false; }else if(password!=password2){ alert("两次输入密码不一致"); return false; }else{ //ajax请求,这里省略 } 2.do..while(condition) 注意:do..while和java中的一样,至少执行一次 不会出现当条件不满足时,不执行。先执行再判断,用这句话形容do..while非常合适 开发中的话,我个人不常用,当然应用场景的话,可以用作于数据初始化 3.while(conditioini) 先判断再执行,有可能一次都不执行 while的话,用的不多,应用场景当满足条件时,进行函数调用。不过注意,如果代码存在逻辑问题又或者其他原因,可能导致死循环或者浏览器崩溃。 4.for循环 这个玩意好啊,用的最多,不过用之不慎的话,也会导致死循环。记得某个幽默的大牛对我说,十年前,初次做开发,别人问他,你会什么,他回答说,我就会for循环。 for循环,我应用的地方是: // 数据展示 var loadData3 = function(list) { $("#s10_tbody tr[value != '']").remove(); var rows = ""; var comuStatus = ""; // 数据遍历 for (var i = 0; i < list.length; i++) { var comuStatus = list[i].comuStatus if (comuStatus == 1) { comuStatus = "正常"; } else { comuStatus = "异常"; } rows = rows + '<tr>'; rows = rows + ' <td>' + list[i].id + '</td>'; rows = rows + ' <td>' + comuStatus + '</td>'; rows = rows + ' <td>' + list[i].time + '</td>'; rows = rows + '</tr>'; } $("#s10_tbody").append(rows); } 5.for in 对于这个玩意,我应用的场景如图所示:通常for in是针对数组或集合 //按条件查找锁 function searchLock(index,size) { $(".list [value != '']").remove(); //用户ID var userId=$("#userId").val(); //门锁编码 var lockNo = $("#lock_no_search").val(); //房间编码 var roomCode = $("#room_no_search").val(); //房源编码 var houseCode = $("#house_bm_search").val(); //省市区 var province = $("#province").find("option:selected").text(); var city = $("#city").find("option:selected").text(); var district = $("#district").find("option:selected").text(); //安装地址 var region = province+"_"+city+"_"+district; //地址 var address = $("#address").val(); $.ajax({ type : "POST", url : "lock/list", data : { userId : userId, lockNo:lockNo, roomCode:roomCode, houseCode:houseCode, region:region, address:address, pageIndex:index, pageSize:size }, dataType : 'json', success : function(data) { var json = eval("("+data+")"); //总数 lockCount=json.lines; $("#locknum").html(lockCount); if(lockCount!=0){ //将后台集合装载入list var list = json.lockList; for (i in list){ var lockNo = list[i].lock_no; var houseCode = list[i].houseCode; var roomCode = list[i].roomCode; var type = list[i].type; var doorsensor = list[i].doorsensor; var comuStatus = list[i].comuStatus; var power = list[i].power; var region = list[i].region; var address = list[i].address; var installTime = list[i].installTime; var name = list[i].name; var nodeNo = list[i].nodeNo; if(type==1){ type="二代433门锁"; } if(doorsensor==1){ doorsensor="是"; }else{ doorsensor="否"; } if(comuStatus==1){ comuStatus="强"; } var rows="" +"<span class='fl lockinfo lockcheck' style='width:7%;'> <a class='checks' href='javascript:;' data-id='162235c895b065' data-node-comu='00' data-node-type='3' checks='false'></a> </span>" +" <span class='fl ip-p lockinfo'><a target='_blank' href='lockPassword.html?lockNo="+lockNo+"' class='listss-l'>"+lockNo+"</a></span>" +"<span class='fl code-p lockinfo'>"+houseCode+"</span>" +"<span class='fl room-p lockinfo'>"+roomCode+"</span>" +"<span class='fl lock-p lockinfo'>"+type+"</span>" +"<span class='fl magnetic-p lockinfo'>"+doorsensor+"</span>" +"<div class='fl commit-p lockinfo'><span class='ico ico-cmt-s4'></span><p class='green'>"+comuStatus+"</div>" +"<span class='fl qua-p lockinfo'><div class='iconfont icon-icon-test20 green'></div><div>"+power+"%</div></span>" +"<span class='fl add address lockinfo'><span style='display:none'>"+region+"</span>"+address+"</span>" +"<span class='fl time-p lockinfo'>"+installTime+"</span>" +"<span class='fl ope-p lockinfo'>"+name+"</span>" +"<span class='fl gate-p lockinfo'>"+nodeNo+"</span>" +"<span class='fl lockinfo lockope'><a class='opebtn opechange' href='lockPassword.html?lockNo="+lockNo+"' data-node='3' style='display: inline;'>编辑</a></span>"; $(".list").append(rows); } var page='<div id="userPage" align="center" ><font size="2">共' (lockCount+pageSize-1)/pageSize+'页</font> <font size="2">第' +(pageIndex+1)+'页</font> <a href="javascript:void" onclick="GoToFirstPage()" id="aFirstPage" >首页</a> ' +'<a href="javascript:void" onclick="GoToPrePage()" id="aPrePage" >上一页</a> ' +'<a href="javascript:void" onclick="GoToNextPage()" id="aNextPage" >下一页</a> ' +'<a href="javascript:void" onclick="GoToEndPage()" id="aEndPage" >尾页</a> '; page+='</div>'; $("#lockPage").html(page); // document.getElementById("dltitle").innerHTML = "查找结果如下"; } },error:function(){ alert("有异常"); } }); } 6.switch 这个switch的话,我觉得也很有用,可以针对不同的条件和参数调用不同的函数,挺有用的,不过我用的不多,可能是因为没有领会它的真谛吧,说到底就是用的太少不熟练。 应用场景的话,前后端分离,用的展示层就是html,如何进行权限控制呢?就通过switch,通过全局js函数获得用户角色,根据该角色,进行访问资源判断。例如, 不过这里的角色是有限的,基本定义为普通人员,安装人员,运维,admin或者root等四到五个角色,如果想crm或erp之类的,角色很多,万不可通过这种形式来定义对资源的访问。 三、函数 这个不能不提啊,现在我写的最多的就是js函数 突然让我想起了Linux,linux中的shell,shell脚本的基础就是linux的命令的集合体,记得在上家公司一位同事写监控服务器脚本就是用的函数,函数方便复用和模块化,什么是模块化呢?我个人的理解是,比如我写的一个用户管理界面,引用的js文件主要是用户管理相关js,该js文件专心于用户管理界面上的一切操作。

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

Java学习笔记--线程和多线程线程池(简单理解)

线程: 单核的cpu在一个时间片中只能执行一个应用程序 各个程序其实在做cpu的资源真多战而已 cpu做了快速的切换动作 疑问 :线程负责了代码 的执行,我们之前没有学过线程,为什么代码可以执行呢? 运行任何一个java程序,jvm在运行的时候都会创建一个main线程执行main方法中所有代码。 一个java应用程序至少有几个线程? 至少有两个线程, 一个是主线程负责main方法代码的执行,一个是垃圾回收器线程,负责了回收垃圾。 面试题 1)线程和进程有什么区别? 一个进程是一个独立(self contained)的运行环境,它可以被看作一个程序或者一个应用。而线程是在进程中执行的一个任务。线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间。别把它和栈内存搞混,每个线程都拥有单独的栈内存用来存储本地数据。 2)如何在Java中实现线程? 创建线程有两种方式: 一、继承 Thread 类,扩展线程。 二、实现 Runnable 接口。 3)Thread 类中的 start() 和 run() 方法有什么区别? 调用 start() 方法才会启动新线程;如果直接调用 Thread 的 run() 方法,它的行为就会和普通的方法一样;为了在新的线程中执行我们的代码,必须使用 Thread.start() 方法。 多线程的好处: 1. 解决了一个进程能同时执行多个任务的问题。 2. 提高了资源的利用率。 多线程 的弊端: 1. 增加cpu的负担。 2. 降低了一个进程中线程的执行概率。 3. 引发了线程安全 问题。 4. 出现了死锁现象。 如何创建多线程: 创建线程的方式: 方式一: 1. 自定义一个类继承Thread类。 2. 重写Thread类的run方法 , 把自定义线程的任务代码写在run方法中 疑问: 重写run方法的目的是什么? 每个线程都有自己的任务代码,jvm创建的主线程的任务代码就是main方法中的所有代码, 自定义线程的任务代码就写在run方法中,自定义线程负责了run方法中代码。 3. 创建Thread的子类对象,并且调用start方法开启线程。 注意: 一个线程一旦开启,那么线程就会执行run方法中的代码,run方法千万不能直接调用, 直接调用run方法就相当调用了一个普通的方法而已 并没有开启新的线程。 /* 需求: 模拟QQ视频与聊天同时在执行。 */ class TalkThread extends Thread{ @Override public void run() { while(true){ System.out.println("hi,你好!开视频呗..."); } } } class VideoThread extends Thread{ @Override public void run() { while(true){ System.out.println("视频视频...."); } } } public class Demo2 { public static void main(String[] args) { TalkThread talkThread = new TalkThread(); talkThread.start(); VideoThread videoThread = new VideoThread(); videoThread.start(); } } 还可以使用 匿名内部类创建多线程: public class Demo4_Thread { /** * @param args */ public static void main(String[] args) { new Thread() { //1,继承Thread类 public void run() { //2,重写run方法 for(int i = 0; i < 1000; i++) { //3,将要执行的代码写在run方法中 System.out.println("aaaaaaaaaaaaaa"); } } }.start(); //4,开启线程 new Thread(new Runnable() { //1,将Runnable的子类对象传递给Thread的构造方法 public void run() { //2,重写run方法 for(int i = 0; i < 1000; i++) { //3,将要执行的代码写在run方法中 System.out.println("bb"); } } }).start(); //4,开启线程 } } 线程常用的方法: Thread(String name) 初始化线程的名字 setName(String name) 设置线程对象名 getName() 返回线程的名字 重要 sleep() 线程睡眠指定的毫秒数。 静态的方法, 哪个线程执行了sleep方法代码那么就是哪个线程睡眠。 重要 currentThread() 返回当前的线程对象,该方法是一个静态的方法, 注意: 那个线程执行了currentThread()代码就返回那个线程 的对象。 一般是Thread.currentThread()取得当前的线程 getPriority() 返回当前线程对象的优先级 默认线程的优先级是5 s**etPriority(int newPriority)** 设置线程的优先级 虽然设置了线程的优先级,但是具体的实现取决于底层的操作系统的实现(最大的优先级是10 ,最小的1 , 默认是5)。 */ 多线程加锁: 这里只介绍synchronized 还有其他锁 可以参考这个博主的 https://juejin.im/post/5a43ad786fb9a0450909cb5f package cn.itcast.thread; /* 需求: 模拟3个窗口同时在售50张 票 。 问题1 :为什么50张票被卖出了150次? 出现 的原因: 因为num是非静态的,非静态的成员变量数据是在每个对象中都会维护一份数据的,三个线程对象就会有三份。 解决方案:把num票数共享出来给三个线程对象使用。使用static修饰。 问题2: 出现了线程安全问题 ? 线程 安全问题的解决方案:sun提供了线程同步机制让我们解决这类问题的。 java线程同步机制的方式: 方式一:同步代码块 同步代码块的格式: synchronized(锁对象){ 需要被同步的代码... } 同步代码块要注意事项: 1. 任意的一个对象都可以做为锁对象。 2. 在同步代码块中调用了sleep方法并不是释放锁对象的。 3. 只有真正存在线程安全问题的时候才使用同步代码块,否则会降低效率的。 4. 多线程操作的锁 对象必须 是唯一共享 的。否则无效。 需求: 一个银行账户5000块,两夫妻一个拿着 存折,一个拿着卡,开始取钱比赛,每次只能取一千块,要求不准出现线程安全问题。 方式二:同步函数 出现线程安全问题的根本原因: 1. 存在两个或者两个以上 的线程对象,而且线程之间共享着一个资源。 2. 有多个语句操作了共享资源。 实现Runnable接口比继承Thread类所具有的优势: 1):适合多个相同的程序代码的线程去处理同一个资源 2):可以避免java中的单继承的限制 3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立 4):线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类 提醒一下大家:main方法其实也是一个线程。在java中所以的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。 在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个jVM实际在就是在操作系统中启动了一个进程。 */ class SaleTicket extends Thread{ static int num = 50;//票数 非静态的成员变量,非静态的成员变量数据是在每个对象中都会维护一份数据的。 static Object o = new Object(); public SaleTicket(String name) { super(name); } @Override public void run() { while(true){ //同步代码块 synchronized ("锁") { if(num>0){ System.out.println(Thread.currentThread().getName()+"售出了第"+num+"号票"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } num--; }else{ System.out.println("售罄了.."); break; } } } } } public class Demo4 { public static void main(String[] args) { //创建三个线程对象,模拟三个窗口 SaleTicket thread1 = new SaleTicket("窗口1"); SaleTicket thread2 = new SaleTicket("窗口2"); SaleTicket thread3 = new SaleTicket("窗口3"); //开启线程售票 thread1.start(); thread2.start(); thread3.start(); } } 线程的创建还有一个方法除了 继承线程 然后代码放入run之外 还可以继承Runable class zz implements Runnable{ @Override public void run() { for (int i = 0; i <10; i++) { System.out.println("nei"); } } } zz qq=new zz(); Thread aa=new Thread(qq); aa.start(); 锁的话也可以有两种 一种是使用同步代码块 格式 在run里面 synchronized(锁对象){需要同步的代码} 同步函数 使用synchronized来修饰一个函数 同步函数要注意的事项 : 1. 如果是一个非静态的同步函数的锁 对象是this对象,如果是静态的同步函数的锁 对象是当前函数所属的类的字节码文件(class对象)。 2. 同步函数的锁对象是固定的,不能由你来指定 的。 推荐使用: 同步代码块。 原因: 1. 同步代码块的锁对象可以由我们随意指定,方便控制。同步函数的锁对象是固定 的,不能由我们来指定。 2. 同步代码块可以很方便控制需要被同步代码的范围,同步函数必须是整个函数 的所有代码都被同步了。 //静态的函数---->函数所属 的类的字节码文件对象--->BankThread.class 唯一的。 public static synchronized void getMoney(){ } 当class被加载倒内存的时候 会创建一个class对象 把内容都存到这个对象里面去 唯一 举个例子 用继承接口还有 同步函数来实现多线程 package day13; class zz implements Runnable{ @Override public void run() { zz(); } public synchronized void zz() { for (int i = 0; i <10; i++) { System.out.println("nei"+Thread.currentThread().getName()); } } } public class new1 { public static void main(String[] args) throws InterruptedException { zz qq1=new zz(); zz qq2=new zz(); Thread aa=new Thread(qq1); Thread bb=new Thread(qq2); aa.start(); bb.start(); } } 方式二: 1. 自定义一个类实现Runnable接口。 2. 实现Runnable接口 的run方法,把自定义线程的任务定义在run方法上。 3. 创建Runnable实现类对象。 4. 创建Thread类 的对象,并且把Runnable实现类的对象作为实参传递。 5. 调用Thread对象 的start方法开启一个线程。 问题1: 请问Runnable实现类的对象是线程对象吗? Runnable实现类的对象并 不是一个线程对象,只不过是实现了Runnable接口 的对象而已。 只有是Thread或者是Thread的子类才是线程 对象。 问题2: 为什么要把Runnable实现类的对象作为实参传递给Thread对象呢?作用是什么? 作用就是把Runnable实现类的对象的run方法作为了线程的任务代码去执行了。 推荐使用: 第二种。 实现Runable接口的。 原因: 因为java单继承 ,多实现的。 /* Thread类 的run方法 * @Override public void run() { if (target != null) { target.run(); //就相当于Runnable实现类的对象的run方法作为了Thread对象的任务代码了。 } } */ Thread.currentThread()获取当前正在执行的线程 System.out.println(Thread.currentThread().getName() + “…bb”); 礼让线程: class MyThread extends Thread { public void run() { for(int i = 1; i <= 1000; i++) { if(i % 10 == 0) { Thread.yield(); //让出CPU } System.out.println(getName() + "..." + i); } } } 死锁问题; 相当于 一个人拿空调板 一个人拿电池 只有同时拿到时才可以进行下一步 举例: package cn.itcast.thread; /* java中同步机制解决了线程安全问题,但是也同时引发死锁现象。 死锁现象: 死锁现象出现 的根本原因: 1. 存在两个或者两个以上的线程。 2. 存在两个或者两个以上的共享资源。 死锁现象的解决方案: 没有方案。只能尽量避免发生而已。 */ class DeadLock extends Thread{ public DeadLock(String name){ super(name); } public void run() { if("张三".equals(Thread.currentThread().getName())){ synchronized ("遥控器") { System.out.println("张三拿到了遥控器,准备 去拿电池!!"); synchronized ("电池") { System.out.println("张三拿到了遥控器与电池了,开着空调爽歪歪的吹着..."); } } }else if("狗娃".equals(Thread.currentThread().getName())){ synchronized ("电池") { System.out.println("狗娃拿到了电池,准备去拿遥控器!!"); synchronized ("遥控器") { System.out.println("狗娃拿到了遥控器与电池了,开着空调爽歪歪的吹着..."); } } } } } public class Demo2 { public static void main(String[] args) { DeadLock thread1 = new DeadLock("张三"); DeadLock thread2 = new DeadLock("狗娃"); //开启线程 thread1.start(); thread2.start(); } } 如何避免死锁 举个例子: public void run() { // TODO Auto-generated method stub synchronized("zzz") { synchronized("空调版") { System.out.println(name+"拿到空调版"); synchronized("xx") { System.out.println(name+"我拿到xx"); } } } } } 避免死锁就是保证这两个东西满足的前提是必须先满足其他一种情况 多线程总结 用接口Runnable来实现多线程 因为这样还可以继承其他类 用同步代码块来实现锁 尽量避免死锁 当前线程用Thread.currentThread()来表示 注意 public void run() { /*System.out.println("this:"+ this); System.out.println("当前线程:"+ Thread.currentThread());*/ for(int i = 0 ; i < 100 ; i++){ System.out.println(Thread.currentThread().getName()+":"+i); } } 如果是Runnable来实现的话 this指的是这个接口实现类 而 Thread.currentThread指的是Thread那个类 还是贴一下 lock锁的代码吧 回顾的时候可以一眼知道 package com.heima.thread2; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; public class Demo3_ReentrantLock { /** * @param args */ public static void main(String[] args) { final Printer3 p = new Printer3(); new Thread() { public void run() { while(true) { try { p.print1(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); new Thread() { public void run() { while(true) { try { p.print2(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); new Thread() { public void run() { while(true) { try { p.print3(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } } class Printer3 { private ReentrantLock r = new ReentrantLock(); private Condition c1 = r.newCondition(); private Condition c2 = r.newCondition(); private Condition c3 = r.newCondition(); private int flag = 1; public void print1() throws InterruptedException { r.lock(); //获取锁 if(flag != 1) { c1.await(); } System.out.print("我"); System.out.print("是"); System.out.print("线程"); System.out.print("1"); System.out.print("\r\n"); flag = 2; //this.notify(); //随机唤醒单个等待的线程 c2.signal(); r.unlock(); //释放锁 } public void print2() throws InterruptedException { r.lock(); if(flag != 2) { c2.await(); } System.out.print("我"); System.out.print("是"); System.out.print("线程"); System.out.print("2"); System.out.print("\r\n"); flag = 3; //this.notify(); c3.signal(); r.unlock(); } public void print3() throws InterruptedException { r.lock(); if(flag != 3) { c3.await(); } System.out.print("我"); System.out.print("是"); System.out.print("线程"); System.out.print("3"); System.out.print("\r\n"); flag = 1; c1.signal(); r.unlock(); } } 回过头来又来看一遍ReentrantLock锁: 理解更多了点: 第一:r.lock();和r.unlock(); 还是拿上面这个栗子来说 一个类里有三个方法 三个线程分别调用三个方法,都是抢占式的 那会不会当一个线程在执行的时候 另一个线程会抢断这个线程呢? 答案:不会的 因为这个这个方法加了锁lock 我原来以为多个线程调用同一段代码的时候这个锁才有用 后来知道了 范围还可以更广 因为 它是用代码中 同一个 r (ReentrantLock锁)来锁定这段代码的 注意是同一个 虽然r锁定的代码不一样 但是 他们都是由一个锁锁住的 一个锁只能锁一个 所以 当线程A执行的时候 是不会被线程B抢断的 第二:c1.await(); 意思就是睡眠 睡眠什么呢? 我理解的 睡眠这个线程的 当前位置 await()下面代码不会被执行 同时释放锁 如果被唤醒了 还是从之前被锁定的位置开始 ———————————————————————————————————————————————————— 线程通信: package cn.itcast.thread; /* 线程通讯: 一个线程完成了自己的任务时,要通知另外一个线程去完成另外一个任务. 生产者与消费者 wait(): 等待 如果线程执行了wait方法,那么该线程会进入等待的状态,等待状态下的线程必须要被其他线程调用notify方法才能唤醒。 notify(): 唤醒 唤醒线程池等待线程其中的一个。 notifyAll() : 唤醒线程池所有等待 线程。 wait与notify方法要注意的事项: 1. wait方法与notify方法是属于Object对象 的。 2. wait方法与notify方法必须要在同步代码块或者是同步函数中才能 使用。 3. wait方法与notify方法必需要由锁对象调用。 如果不加锁 会出现了线程安全问题。 价格错乱了… */ //产品类 class Product{ String name; //名字 double price; //价格 boolean flag = false; //产品是否生产完毕的标识,默认情况是没有生产完成。 } //生产者 class Producer extends Thread{ Product p ; //产品 public Producer(Product p) { this.p = p ; } @Override public void run() { int i = 0 ; while(true){ synchronized (p) { if(p.flag==false){ if(i%2==0){ p.name = "苹果"; p.price = 6.5; }else{ p.name="香蕉"; p.price = 2.0; } System.out.println("生产者生产出了:"+ p.name+" 价格是:"+ p.price); p.flag = true; i++; p.notifyAll(); //唤醒消费者去消费 }else{ //已经生产 完毕,等待消费者先去消费 try { p.wait(); //生产者等待 } catch (InterruptedException e) { e.printStackTrace(); } } } } } } //消费者 class Customer extends Thread{ Product p; public Customer(Product p) { this.p = p; } @Override public void run() { while(true){ synchronized (p) { if(p.flag==true){ //产品已经生产完毕 System.out.println("消费者消费了"+p.name+" 价格:"+ p.price); p.flag = false; p.notifyAll(); // 唤醒生产者去生产 }else{ //产品还没有生产,应该 等待生产者先生产。 try { p.wait(); //消费者也等待了... } catch (InterruptedException e) { e.printStackTrace(); } } } } } } public class Demo5 { public static void main(String[] args) { Product p = new Product(); //产品 //创建生产对象 Producer producer = new Producer(p); //创建消费者 Customer customer = new Customer(p); //调用start方法开启线程 producer.start(); customer.start(); } } 注意 锁是synchronized 等待是wait 唤醒是notify 对象得是锁的那个对象 wait:告诉当前线程放弃执行权,并放弃监视器(锁)并进入阻塞状态,直到其他线程持有获得执行权,并持有了相同的监视器(锁)并调用notify为止。 notify:唤醒持有同一个监视器(锁)中调用wait的第一个线程,例如,餐馆有空位置后,等候就餐最久的顾客最先入座。注意:被唤醒的线程是进入了可运行状态。等待cpu执行权。 notifyAll:唤醒持有同一监视器中调用wait的所有的线程。 如何解决生产者和消费者的问题? 可以通过设置一个标记,表示数据的(存储空间的状态)例如,当消费者读取了(消费了一次)一次数据之后可以将标记改为false,当生产者生产了一个数据,将标记改为true。 ,也就是只有标记为true的时候,消费者才能取走数据,标记为false时候生产者才生产数据。 线程间通信其实就是多个线程在操作同一个资源,但操作动作不同,wait,notify(),notifyAll()都使用在同步中,因为要对持有监视器(锁)的线程操作,所以要使用在同步中,因为只有同步才具有锁。 为什么这些方法定义在Object类中 因为这些方法在操作线程时,都必须要标识他们所操作线程持有的锁,只有同一个锁上的被等待线程,可以被统一锁上notify唤醒,不可以对不同锁中的线程进行唤醒,就是等待和唤醒必须是同一个锁。而锁由于可以使任意对象,所以可以被任意对象调用的方法定义在Object类中 wait() 和 sleep()有什么区别? wait():释放资源,释放锁。是Object的方法 sleep():释放资源,不释放锁。是Thread的方法 定义了notify为什么还要定义notifyAll,因为只用notify容易出现只唤醒本方线程情况,导致程序中的所有线程都在等待。 线程的停止: 1. 停止一个线程 我们一般都会通过一个变量去控制的。 2. 如果需要停止一个处于等待状态下的线程,那么我们需要通过变量配合notify方法或者interrupt()来使用。notify需要锁对象 interrupt()不需要而且可以指定线程但是会抛出异常 调用线程对象的interrupt()时,sleep的线程会抛出InterruptedException异常,从而中断循环,终止线程。 但是如果是IO如输入这些阻塞,中断的方法又不起作用了,还有就是对于没有阻塞的线程,调用interrupt()是达不到终止线程的效果的。 对于IO阻塞,可以关闭IO通道 interrupt()的话会在中断位置抛出异常 抛出异常是为了线程从阻塞状态醒过来,并在结束线程前让程序员有足够的时间来处理中断请求。 Thread.interrupt()方法不会中断一个正在运行的线程。这一方法实际上完成的是,在线程受到阻塞时抛出一个中断信号,这样线程就得以退出阻塞的状态。更确切的说,如果线程被Object.wait, Thread.join和Thread.sleep三种方法之一阻塞,那么,它将接收到一个中断异常(InterruptedException),从而提早地终结被阻塞状态。 因此,如果线程被上述几种方法阻塞,正确的停止线程方式是设置共享变量,并调用interrupt()(注意变量应该先设置)。 如果线程没有被阻塞,这时调用interrupt()将不起作用;否则,线程就将得到异常(该线程必须事先预备好处理此状况), 接着逃离阻塞状态。 A. 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。 B. 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。 C. 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。 如: public synchronized void run() {}那么锁对象就是这个对象 public synchronized static void xx(){}这个时候锁对象是这个class文件 举个例子: package day13; class xx implements Runnable{ public synchronized void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName()); } } } public class new1 { public static void main(String[] args) throws InterruptedException { xx zz=new xx(); for (int i = 0; i < 4; i++) { Thread aa=new Thread(zz); aa.setName("name"+i); aa.start(); } } } 锁是zz这个对象 所以不会发生混乱 wait要放在synchronized就是锁的里面 wait要对象才行 一般配合着都是wait锁 wait是把当前线程放入锁线程池中 notify是唤醒锁中的任意一个线程 一、sleep和wait的主要区别: 1、这两个方法来自不同的类分别是Thread和Object 2、最主要是sleep方法没有释放锁,而 wait 方法释放了锁,使得其他线程可以使用同步控制块或者方法。 3、wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用(使用范围) 4、sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常 5、sleep(milliseconds)可以用时间指定来使他自动醒过来,如果时间不到你只能调用interreput()来强行打断;wait()可以用notify()直接唤起. 线程中断 /* 线程的停止: 1. 停止一个线程 我们一般都会通过一个变量去控制的。 2. 如果需要停止一个处于等待状态下的线程,那么我们需要通过变量配合notify方法或者interrupt()来使用。 */ package day13; class zx implements Runnable{ boolean flag=true; int i=0; public synchronized void run() { while(flag) { /*try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); }*/ System.out.println("内"+i); } } } public class new1 { public static void main(String[] args) throws InterruptedException { zx xx=new zx(); Thread aa=new Thread(xx); aa.start(); for (int i = 0; i < 100; i++) { System.out.println("main"+i); if(i==30) { xx.flag=false; 设置了变量控制 aa.interrupt();//把线程的等待状态强制清除,被清除状态的线程会接收到一个 InterruptedException。 } } } } 但是取消了对象中try的注释 那么系统就无法停止 因为线程是wait状态 要取消有两种方法 一种是 /*synchronized (xx) { xx.notify(); }*/ 这样子把该对象线程池中线程唤醒出来 第二种是: xx.flag = false; aa.interrupt(); //把线程的等待状态强制清除,被清除状态的线程会接收到一个InterruptedException。 可以指定唤醒哪个线程 而notify不行 注意:使用了 wait后就释放了锁其他线程可以进来 放入对应的锁线程池中等待唤醒或者强制打断 interrupt并不是直接打断线程 而是告诉线程你该被取消了 给线程加上了标识符true 如果这个时候有wait或者sleep等阻塞的时候那么就会强制打断线程(取消wait状态) 标识符再改为false 如果没有wait等阻塞那么只是给线程加上true而已 不会打断线程 但是注意: i0之类的阻塞不能用interrupt 而且一旦中断就会抛出异常 记住 wait是object 一般都是这个同步锁 interrupt是指定清除哪个线程的等待状态 而notify是随机一个 守护线程 : 后台线程:就是隐藏起来一直在默默运行的线程,直到进程结束。 实现: setDaemon(boolean on) 特点: 当所有的非后台线程结束时,程序也就终止了同时还会杀死进程中的所有后台线程,也就是说,只要有非后台线程还在运行,程序就不会终止,执行main方法的主线程就是一个非后台线程。 必须在启动线程之前(调用start方法之前)调用setDaemon(true)方法,才可以把该线程设置为后台线程。 一旦main()执行完毕,那么程序就会终止,JVM也就退出了。 可以使用isDaemon() 测试该线程是否为后台线程(守护线程)。 package cn.itcast.thread; /* 守护线程(后台线程):在一个进程中如果只剩下 了守护线程,那么守护线程也会死亡。 需求: 模拟QQ下载更新包。 一个线程默认都不是守护线程。 */ public class Demo7 extends Thread { public Demo7(String name){ super(name); } @Override public void run() { for(int i = 1 ; i<=100 ; i++){ System.out.println("更新包目前下载"+i+"%"); if(i==100){ System.out.println("更新包下载完毕,准备安装.."); } try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { Demo7 d = new Demo7("后台线程"); d.setDaemon(true); //setDaemon() 设置线程是否为守护线程,true为守护线程, false为非守护线程。 // System.out.println("是守护线程吗?"+ d.isDaemon()); //判断线程是否为守护线程。 d.start(); for(int i = 1 ; i<=100 ; i++){ System.out.println(Thread.currentThread().getName()+":"+i); } } } Timer: 线程安排在后台执行的任务 可执行一次或者多次重复 就比如手机闹铃 可定期重复响铃 public static void main(String[] args) throws InterruptedException { Timer t = new Timer(); //在指定时间安排指定任务 //第一个参数,是安排的任务,第二个参数是执行的时间,第三个参数是过多长时间再重复执行 t.schedule(new MyTimerTask(), new Date(188, 6, 1, 14, 22, 50),3000); while(true) { Thread.sleep(1000); System.out.println(new Date()); } } } class MyTimerTask extends TimerTask { @Override public void run() { System.out.println("起床背英语单词"); } 总结: TimerTask是继承runnable 写个类继承TimerTask即可 简单线程池创建: public static void main(String[] args) { ExecutorService pool = Executors.newFixedThreadPool(2);//创建线程池 pool.submit(new MyRunnable()); //将线程放进池子里并执行 pool.submit(new MyRunnable()); pool.shutdown(); //关闭线程池 } 多线程还有一种创建方法 :了解就行 可以有返回值 可以抛出异常 class MyCallable implements Callable<Integer> { private int num; public MyCallable(int num) { this.num = num; } @Override public Integer call() throws Exception { int sum = 0; for(int i = 1; i <= num; i++) { sum += i; } return sum; } } public class One { public static void main(String args[]) throws InterruptedException, ExecutionException { ExecutorService pool = Executors.newFixedThreadPool(2);//创建线程池 Future<Integer> f1 = pool.submit(new MyCallable(100)); //将线程放进池子里并执行 Future<Integer> f2 = pool.submit(new MyCallable(50)); System.out.println(f1.get()); System.out.println(f2.get()); } } join: Thread的join方法 当A线程执行到了B线程Join方法时A就会等待,等B线程都执行完A才会执行,Join可以用来临时加入线程执行 /* join方法。 加入 */ //老妈 class Mon extends Thread{ public void run() { System.out.println("妈妈洗菜"); System.out.println("妈妈切菜"); System.out.println("妈妈准备炒菜,发现没有酱油了.."); //叫儿子去打酱油 Son s= new Son(); s.start(); try { s.join(); //加入。 一个线程如果执行join语句,那么就有新的线程加入, 执行该语句的线程必须要让步给新加入的线程先完成任务,然后才能继续执行。 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("妈妈继续炒菜"); System.out.println("全家一起吃饭.."); } } class Son extends Thread{ @Override public void run() { System.out.println("儿子下楼.."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("儿子一直往前走"); System.out.println("儿子打完酱油了"); System.out.println("上楼,把酱油给老妈"); } } public class Demo8 { public static void main(String[] args) { Mon m = new Mon(); m.start(); } } 线程组: class MyRunnable implements Runnable { @Override public void run() { for(int i = 0; i < 1000; i++) { System.out.println(Thread.currentThread().getName() + "...." + i); } } } public static void main(String[] args) { //demo1(); ThreadGroup tg = new ThreadGroup("我是一个新的线程组"); //创建新的线程组 MyRunnable mr = new MyRunnable(); //创建Runnable的子类对象 Thread t1 = new Thread(tg, mr, "张三"); //将线程t1放在组中 Thread t2 = new Thread(tg, mr, "李四"); //将线程t2放在组中 System.out.println(t1.getThreadGroup().getName()); //获取组名 System.out.println(t2.getThreadGroup().getName()); tg.setDaemon(true); } 参考: 简书1 简书2 守护线程 1)wait()、notify()和notifyAll()方法是本地方法,并且为final方法,无法被重写。 2)调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的monitor(即锁) 3)调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程; 4)调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程; 有朋友可能会有疑问:为何这三个不是Thread类声明中的方法,而是Object类中声明的方法(当然由于Thread类继承了Object类,所以Thread也可以调用者三个方法)?其实这个问题很简单,由于每个对象都拥有monitor(即锁),所以让当前线程等待某个对象的锁,当然应该通过这个对象来操作了。而不是用当前线程来操作,因为当前线程可能会等待多个线程的锁,如果通过线程来操作,就非常复杂了。 上面已经提到,如果调用某个对象的wait()方法,当前线程必须拥有这个对象的monitor(即锁),因此调用wait()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。 调用某个对象的wait()方法,相当于让当前线程交出此对象的monitor,然后进入等待状态,等待后续再次获得此对象的锁(Thread类中的sleep方法使当前线程暂停执行一段时间,从而让其他线程有机会继续执行,但它并不释放对象锁); notify()方法能够唤醒一个正在等待该对象的monitor的线程,当有多个线程都在等待该对象的monitor的话,则只能唤醒其中一个线程,具体唤醒哪个线程则不得而知。 同样地,调用某个对象的notify()方法,当前线程也必须拥有这个对象的monitor,因此调用notify()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。 nofityAll()方法能够唤醒所有正在等待该对象的monitor的线程,这一点与notify()方法是不同的。 这里要注意一点:notify()和notifyAll()方法只是唤醒等待该对象的monitor的线程,并不决定哪个线程能够获取到monitor。 举个简单的例子:假如有三个线程Thread1、Thread2和Thread3都在等待对象objectA的monitor,此时Thread4拥有对象objectA的monitor,当在Thread4中调用objectA.notify()方法之后,Thread1、Thread2和Thread3只有一个能被唤醒。注意,被唤醒不等于立刻就获取了objectA的monitor。假若在Thread4中调用objectA.notifyAll()方法,则Thread1、Thread2和Thread3三个线程都会被唤醒,至于哪个线程接下来能够获取到objectA的monitor就具体依赖于操作系统的调度了。 上面尤其要注意一点,一个线程被唤醒不代表立即获取了对象的monitor,只有等调用完notify()或者notifyAll()并退出synchronized块,释放对象锁后,其余线程才可获得锁执行。 package fuxi; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.HashMap; import java.util.Map; import java.util.Scanner; import javax.swing.JButton; public class One { public static Object object = new Object(); public static void main(String[] args) { Thread1 thread1 = new Thread1(); Thread2 thread2 = new Thread2(); thread1.start(); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } thread2.start(); } static class Thread1 extends Thread{ @Override public void run() { synchronized (object) { try { System.out.println("1"); object.wait(); } catch (InterruptedException e) { } System.out.println("3"); System.out.println("线程"+Thread.currentThread().getName()+"获取到了锁"); } } } static class Thread2 extends Thread{ @Override public void run() { synchronized (object) { System.out.println("2"); object.notify(); System.out.println("4"); System.out.println("线程"+Thread.currentThread().getName()+"调用了object.notify()"); } System.out.println("线程"+Thread.currentThread().getName()+"释放了锁"); } } } 运行结果是: 1 2 4 线程Thread-1调用了object.notify() 3 线程Thread-1释放了锁 线程Thread-0获取到了锁 得到的结论是 如果一个线程A wait必须在锁内 而且唤醒的话 线程B也要在锁内 而且是等这个锁内代码执行完之后才开始再次竞争 不一定被线程A抢到锁 可能还是B抢到锁 还有 如果A抢到锁了 那么就会继续上次的执行 这个就是线程抢占CPU 一样 被抢占的那个记录自己运行位置 抢回来后 继续那个位置执行 参考自: http://www.cnblogs.com/dolphin0520/p/3920385.html

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

区块链培训学习资料(特别是以太坊)

一个适合区块链新手的以太坊DApp开发教程: http://xc.hubwiz.com/course/5a952991adb3847553d205d1 一个用区块链、星际文件系统(IPFS)、Node.js和MongoDB来构建电商平台: http://xc.hubwiz.com/course/5abbb7acc02e6b6a59171dd6 收集整理了一些免费区块链、以太坊技术开发相关的文件,有需要的可以下载,文件链接: web3.js API官方文档中文版:https://pan.baidu.com/s/1hOV9hEzi7hFxJCL4LTvC6g 以太坊官方文档中文版 :https://pan.baidu.com/s/1ktODJKLMBmkOsi8MPrpIJA 以太坊白皮书中文版 :https://pan.baidu.com/s/1bzAFnzJ35hlQxJ2J4Oj-Ow Solidity的官方文档中文版 :https://pan.baidu.com/s/18yp9XjEqAHpiFm2ZSCygHw Truffle的官方文档中文版 :https://pan.baidu.com/s/1y6SVd7lSLUHK21YF5FzIUQ C#区块链编程指南 :https://pan.baidu.com/s/1sJPLqp1eQqkG7jmxqwn3EA 区块链技术指南: :https://pan.baidu.com/s/13cJxAa80I6iMCczA04CZhg 精通比特币中文版: :https://pan.baidu.com/s/1lz6te3wcQuNJm28rFvBfxg Node.js区块链开发 :https://pan.baidu.com/s/1Ldpn0DvJ5LgLqwix6eWgyg geth使用指南文档中文版 :https://pan.baidu.com/s/1M0WxhmumF_fRqzt_cegnag 以太坊DApp开发环境搭建-Ubuntu : https://pan.baidu.com/s/10qL4q-uKooMehv9X2R1qSA 以太坊DApp开发环境搭建-windows :https://pan.baidu.com/s/1cyYkhIJIFuI2oyxM9Ut0eA 以太坊DApp开发私链搭建-Ubuntu : https://pan.baidu.com/s/1aBOFZT2bCjD2o0EILBWs-g 以太坊DApp开发私链搭建-windows :https://pan.baidu.com/s/10Y6F1cqUltZNN99aJv9kAA 以太坊ganache CLI命令行参数详解:https://pan.baidu.com/s/1lnknFkwenacaeM4asOcBdg 使用truflle和infura部署以太坊合约:https://pan.baidu.com/s/1PTxSVff2vHSVUihYczRRqw IPFS安装部署与开发环境搭建-windows:https://pan.baidu.com/s/1bnhDvqCoOgAqEBZXMtVbRg

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

java学习笔记--String字符串,StringBuffer和StringBuilder总结

String 先来介绍一下字符串的存储 字符串相加是通过StringBuffer类的append()和toString()实现的, 而toString()返回的字符串是通过构造函数创建的 强调: 两个字符串常量相加, 在编译的时候之间改为一个字符串常量. 而字符串引用相加则是调用StringBuffer类的append()和toString() 这个时候就会在堆中创建一个新的对象。 比较对象的时候==和euqals比较的都是对象的内存地址 对于其他对象类来说 hascode默认代表是内存地址 equals判断的是地址 但是String类重写了equals和hascode 只要值一样 那么hascode就一样 比较的是两个字符串对象内容是否相一致 所以一般都是equals来比较字符串是否相等 注意:了解一下String继承了Comparable重写了compareTo(compareTo)主要是放在集合里判断大小用 编程习惯 常量调用equals方法 比如: “sdf”.equals(str);因为 如果 调用者是空的话会抛异常 String name1="hello"; String name2=new String(“hello”); 创建字符串的时候 jvm会检查字符串常量池是否存在“hello”的字符串 如果存在就不会在里面创建字符串对象了 如果是第一个那么就会返回的是字符串常量池的地址 如果是第二个 都会拷贝一份字符串常量池的对象内容到堆内存中 字符串常量池只能有一个对象 而堆内存中可以有多个对象而且地址不一样 字符串常量池在方法区中 name1等引用在栈内存中 对象内存中new的话 例子: String str1 = "hello"; String str2 = "hello"; String str3 = new String("hello"); String str4 = new String("hello"); System.out.println("str1==str2?"+(str1==str2)); // true System.out.println("str2==str3?"+(str2==str3)); //false System.out.println("str3==str4?"+(str3==str4)); // false System.out.println("str3.equals(str2)?"+(str3.equals(str4))); //true 字符串判断是否内容相等 public static void test(String str){ if("中国".equals(str)) { System.out.println("回答正确"); }else{ System.out.println("回答错误"); } } 字符串的构造 public static void main(String[] args) { String name=new String(); //空内容 byte[] buf= {97,98,99}; name=new String(buf,0,2); //0是索引 2是个数 输出ab char[] arr= {'我','爱','你'}; name=new String(arr,0,2); //同理byte 输出我爱 int[] buf2= {97,98,99}; name=new String(buf,0,2); //同类byte 输出ab name=new String("wocainima00"); //输出wocainima00 name="xxx"; //输出xxx System.out.print(name); } 字符串获取长度,位置或者获取字串: int length() 获取字符串的长度 char charAt(int index) 获取特定位置的字符 (角标越界) int indexOf(String str) 查找子串第一次出现的索引值,如果子串没有出现 在字符串中,那么则返回-1表示。 int lastIndexOf(String str) 查找子串最后一次出现的索引值 , 如果子串没有出现 在字符串中,那么则返回-1表示 字符串的判断: 字节数组与字符数组、字符串他们三者之间是可以互相转换。 public static void main(String[] args) { String str = "Demo.java"; System.out.println("是否以指定 的字符串结束:"+ str.endsWith("ja")); //str = ""; System.out.println("判断字符串是否为空内容:"+str.isEmpty()); System.out.println("判断字符串是否包含指定的内容:"+ str.contains("Demo")); System.out.println("判断两个 字符串的内容是否一致:"+ "DEMO.JAVA".equals(str)); System.out.println("判断两个字符串的内容是否一致(忽略大小写比较):"+ "DEMO.JAVA".equalsIgnoreCase(str)); //转换的方法 char[] buf = str.toCharArray(); //把字符串转换字符数组 System.out.println("字符数组:"+ Arrays.toString(buf)); byte[] buf2 = str.getBytes(); //把字符串转字节数组 System.out.println("字节数组:"+ Arrays.toString(buf2)); } 字符串操作 String str = "今天晚上不考试"; System.out.println("指定新内容替换旧 的内容:"+ str.replace("不", "要好好")); str = "大家-下-午-好"; String[] arr = str.split("-"); //根据指定的字符进行切割 。 System.out.println("字符串数组的内容:"+ Arrays.toString(arr)); str = "广州传智播客"; System.out.println("指定开始的索引值截取子串:"+ str.substring(2)); System.out.println("指定开始的索引值截取子串:"+ str.substring(2,6)); //包头不包尾 注意:截取的内容是包括开始的索引值,不包括结束的索引值, 截取的位置是结束的索引值-1. str = "abC中国"; System.out.println("转大写:" + str.toUpperCase()); str = "AbdfSDD"; System.out.println("转小写:"+ str.toLowerCase()); str = " 大家最近 都非常努力 "; System.out.println("去除字符串首尾的空格:"+ str.trim()); 字符串的运用: package cn.itcsat.string; /* 需求1:自己实现trim的方法。 需求2: 获取上传文件名 "D:\\20120512\\day12\\Demo1.java"。 需求3: 将字符串对象中存储的字符反序。 新中国好 -----> 好国中新 需求4: 求一个子串在整串中出现的次数 。 public class Demo6 { public static void main(String[] args) { String str =" 大家 好嘛 "; System.out.println(myTrim(str)); str = "D:\\20120512\\day12\\Demo1.java"; getFileName(str); str = "新中国好"; System.out.println("翻转后的字符串:"+ reverse(str)); str = "abcjavaabcjavaphpjava"; //java getCount(str, "java"); } // 需求1:自己实现trim的方法。 public static String myTrim(String str){ //先转换成字符 数组 char[] arr = str.toCharArray(); //定义两个 变量记录开始与结束 的索引值 int startIndex = 0 ; int endIndex = arr.length -1; //确定开始 的索引值 while(true){ if(arr[startIndex]==' '){ startIndex++; }else{ break; } } //确定结束 的索引值: while(true){ if(arr[endIndex]==' '){ endIndex--; }else{ break; } } //截取子串返回 return str.substring(startIndex,endIndex+1); } } //需求2: 获取上传文件名 "D:\\20120512\\day12\\Demo1.java"。 public static void getFileName(String path){ int index = path.lastIndexOf("\\"); String fileName = path.substring(index+1); System.out.println("文件名:"+ fileName); } //反转 public static String reverse(String str){ char[] arr = str.toCharArray(); for(int startIndex = 0 , endIndex=arr.length-1 ; startIndex<endIndex; startIndex++,endIndex--){ char temp = arr[startIndex]; arr[startIndex] = arr[endIndex]; arr[endIndex] = temp; } //使用字符数组构建一个字符串。 return new String(arr); } //统计子串出现 的次数 public static void getCount(String str,String target){ int count = 0 ; //用于记录出现的次数 int fromIndex = 0; // 记录从那个索引值开始寻找目标子串 while((fromIndex = str.indexOf(target, fromIndex))!=-1){ //如果indexof方法返回 的不是-1,那么就是已经找到了目标 元素。 count++; fromIndex = fromIndex+target.length(); } System.out.println("出现的次数:"+ count); } 字符串还有个StringBuffer 如果需要频繁修改字符串 的内容,建议使用字符串缓冲 类(StringBuffer)。 笔试题目:使用Stringbuffer无 参的构造函数创建 一个对象时,默认的初始容量是多少? 如果长度不够使用了,自动增长多少倍? StringBuffer 底层是依赖了一个字符数组才能存储字符数据 的,该字符串数组默认 的初始容量是16, 如果字符数组的长度不够使用 死,自动增长1倍。 容器的具备 的行为:增加 删除 修改 查看 判断 //先使用StringBuffer无参的构造函数创建一个字符串缓冲类。 StringBuffer sb = new StringBuffer(); sb.append("java"); sb.append("java"); sb.append("java"); sb.append("java"); sb.append("java"); StringBuffer对象和String对象之间的互转的代码如下: String s = “abc”; StringBuffer sb1 = new StringBuffer(“123”); StringBuffer sb2 = new StringBuffer(s); //String转换为StringBuffer String s1 = sb1.toString(); //StringBuffer转换为String 单行用加号拼接字符串是没有性能损失的,java编译器会隐式的替换成stringbuilder, 但在有循环的情况下,编译器没法做到足够智能的替换,仍然会有不必要的性能损耗, 因此,用循环拼接字符串的时候,还是老老实实的用stringbuilder吧。 String,StringBuffer与StringBuilder的区别?? 注意: 1、String类型的字符串对象是不可变的,一旦String对象创建后,包含在这个对象中的字符系列是不可以改变的,直到这个对象被销毁。 2、StringBuilder和StringBuffer类型的字符串是可变的,不同的是StringBuffer类型的是线程安全的,而StringBuilder不是线程安全的 3、如果是多线程环境下涉及到共享变量的插入和删除操作,StringBuffer则是首选。如果是非多线程操作并且有大量的字符串拼接,插入,删除操作则StringBuilder是首选。毕竟String类是通过创建临时变量来实现字符串拼接的,耗内存还效率不高,怎么说StringBuilder是通过JNI方式实现终极操作的。 4、StringBuilder和StringBuffer的“可变”特性总结如下: (1)append,insert,delete方法最根本上都是调用System.arraycopy()这个方法来达到目的 (2)substring(int, int)方法是通过重新new String(value, start, end - start)的方式来达到目的。因此,在执行substring操作时,StringBuilder和String基本上没什么区别。 单线程的时候基本上用StringBuild就对了主要有append和insert等方法 StringBuffer的方法 //先使用StringBuffer无参的构造函数创建一个字符串缓冲类。 StringBuffer sb = new StringBuffer(); sb.append(“abcjavaabc”); /* 添加 sb.append(true); sb.append(3.14f); 插入 sb.insert(2, "小明"); */ /* 删除 sb.delete(2, 4); // 删除的时候也是包头不包尾 sb.deleteCharAt(3); //根据指定 的索引值删除一个字符 修改 sb.replace(2, 4, "陈小狗"); sb.reverse(); // 翻转字符串的内容 sb.setCharAt(3, '红'); String subString = sb.substring(2, 4); System.out.println("子串的内容:"+ subString); 查看 int index = sb.indexOf("abc", 3); System.out.println("索引值为:"+index); sb.append("javajava"); System.out.println("查看字符数组的长度:"+ sb.capacity()); */ System.out.println("存储的字符个数:"+sb.length()); System.out.println("索引指定的索引值查找字符:"+sb.charAt(2) ); System.out.println("字符串缓冲类的内容:"+ sb); String content = sb.toString(); test(content); String s1 = "abc"; String s2 = "a"; String s3 = "bc"; String s4 = s2 + s3; System.out.println(s1 == s4); 结果为: false 原理是: 字符串相加是通过StringBuffer类的append()和toString()实现的, 而toString()返回的字符串是通过构造函数创建的. toString()是转换为String类型 如果是引用对象那么就值就是完整类名@地址 如果是String就是输出结果 而且 System.out.println 会自动调用toString() StringBuilder 可以实现字符串添加 没有创建新的字符串 toString返回的是字符串 是new出来的 在堆中

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

Java学习笔记--异常处理(传智播客的总结)

背景: 我们的java程序也是会存在某些不正常 的情况的,这些不正常的 情况我们就统称异常。(还有一种是IO流的异常 要包装成运行时异常) 异常体系: ———-| Throwable 所以异常或者错误类的超类 ————–|Error 错误 错误一般是用于jvm或者是硬件引发的问题,所以我们一般不会通过代码去处理错误的。 ————–|Exception 异常 是需要通过代码去处理的。 如何区分错误与异常呢: 如果程序出现了不正常的信息,如果不正常的信息的类名是以Error结尾的,那么肯定是一个错误。 如果是以Exception结尾的,那么肯定就是一个异常。 Throwable常用的方法: toString() 返回当前异常对象的完整类名+病态信息。 包名+类名 = 完整类名 getMessage() 返回的是创建Throwable传入的字符串信息。 printStackTrace() 打印异常的栈信息 比如:虚拟机能分配的内存是50多mb如果超过了就是Error类型的 疑问: 下面的信息是通过printStackTrace方法打印出来,那么异常对象从何而来呢? Exception in thread “main” java.lang.ArithmeticException: / by zero at Demo10.div(Demo10.java:10) at Demo10.main(Demo10.java:5) jvm运行到a/b这个语句的时候,发现b为0,除数为0在我们现实生活中是属于 不正常的情况,jvm一旦发现了这种不正常的情况时候,那么jvm就会马上创建 一个对应的异常对象,并且会调用这个异常对象 的printStackTrace的方法来处理。 异常的处理: 方式一:捕获处理 捕获处理的格式: try{ 可能发生异常的代码; }catch(捕获的异常类型 变量名){ 处理异常的代码.... } 捕获处理要注意的细节: 如果try块中代码出了异常经过了处理之后,那么try-catch块外面的代码可以正常执行。 如果try块中出了异常的代码,那么在try块中出现异常代码后面的代码是不会执行了。 一个try块后面是可以跟有多个catch块的,也就是一个try块可以捕获多种异常的类型。 一个try块可以捕获多种异常的类型,但是捕获的异常类型必须从小到大进行捕获,否则编译报错。 疑问一 : 异常的处理感觉没有多大作用,因为都是输出一个话而已? 异常处理非常有用,只不过是由于我们目前所接触的知识点太过于局限而已。 疑问二: 以后捕获处理 的时候是否就是捕获Exception即可? 错的,因为我们在现实开发中遇到不同的异常类型的时候,我往往会有不同 的处理方式。 所以要分开不同的异常类型处理。 注意 如果抛出了一个异常 那么接下来的代码就不会执行 */ 举例: try catch方法 class A{ public static void main(String[] args){ div(1,0); } public static void div(int a,int b){ int c=0; try{ c=a/b; }catch(ArithmeticException e){ System.out.println("1"); }catch(NullPointerException e){ System.out.println("2"); }catch(Exception e){ System.out.println("3"); } } } /* 异常的处理方式----抛出处理 抛出处理(throw throws) 抛出处理要注意的细节: 1. 如果一个方法的内部抛出了一个异常 对象,那么必须要在方法上声明抛出。 2. 如果调用了一个声明抛出异常 的方法,那么调用者必须要处理异常。 3. 如果一个方法内部抛出了一个异常对象,那么throw语句后面的代码都不会再执行了(一个方法遇到了throw关键字,该方法也会马上停止执行的)。 4. 在一种情况下,只能抛出一种类型异常对象。 throw 与throws两个关键字: 1. throw关键字是用于方法内部的,throws是用于方法声声明上的。 2. throw关键字是用于方法内部抛出一个异常对象的,throws关键字是用于在方法声明上声明抛出异常类型的。 3. throw关键字后面只能有一个异常对象,throws后面一次可以声明抛出多种类型的 异常。 疑问:何时使用抛出处理?何时捕获处理?原则是如何? 如果你需要通知到调用者,你代码出了问题,那么这时候就使用抛出处理. 如果代码是直接与用户打交道遇到了异常千万不要再抛,再抛的话,就给了用户了。 这时候就应该使用捕获处理。 */ 举例:throw 抛出 class Demo2{ public static void main(String[] args)throws Exception{ try{ div(0,1,null); }catch(Exception e){ System.out.print("1"); throw new Exception(); } } public static void div(int i,int j,int[] arr) throws Exception{ if(i==0){ throw new Exception(); }else if(arr==null){ throw new NullPointerException(); } int c=i/j; System.out.println(c); } } 如果 main函数也抛出异常 那么到时候出错的时候直接打印printStackTrace 自定义异常类: /* sun提供了很多的异常类给我们用于描述程序中各种的不正常情况,但是sun 给我 提供异常类还不足以描述我们现实生活中所有不正常情况,那么这时候我们就需要 自定义异常类。 需求: 模拟feiQ上线的时候,如果没有插上网线,那么就抛出一个没有插上网线的异常, 如果已经插上了网上,那么就正常显示好友列表。 自定义异常类的步骤: 自定义一个类继承Exception即可。 class NullIpException extends Exception{ public NullIpException(String x){ super(x); } } class Demo2{ public static void main(String[] args){ String ip="sdf"; ip=null; try{ xx(ip); }catch(Exception z){ z.printStackTrace(); } } public static void xx(String ip)throws Exception{ if(ip==null){ throw new NullIpException("noip"); } System.out.println("sdfsadfasdfas"); } } —————| 运行时异常: 如果一个方法内部抛出了一个运行时异常,那么方法上 可以声明也可以不 声明,调用者可以以处理也可以不处理。 ——————| 编译时异常(非运行时异常、受检异常): 如果一个方法内部抛出了一个编译时异常对象,那么方法上就必须要声明,而且调用者也必须要处理。 运行时异常: RuntimeException以及RuntimeException子类 都是属于运行时异常。 编译时异常: 除了运行时异常就是编译异常。 疑问: 为什么java编译器会如此严格要求编译时异常,对运行时异常如此宽松? 运行时异常都是可以通过程序员良好的编程习惯去避免,所以java编译器就没有严格要求处理运行时异常。 ———————————————————————————————————— */ finally块的 使用前提是必须要存在try块才能使用。 finally块的代码在任何情况下都会执行的,除了j vm退出的情况。 finally非常适合做资源释放的工作,这样子可以保证资源文件在任何情况下都 会被释放。 finally: 结论: 1、不管有木有出现异常,finally块中代码都会执行; 2、当try和catch中有return时,finally仍然会执行; 3、finally是在return后面的表达式运算后执行的(此时并没有返回运算后的值,而是先把要返回的值保存起来,管finally中的代码怎么样,返回的值都不会改变,任然是之前保存的值),所以函数返回值是在finally执行前确定的; 4、finally中最好不要包含return,否则程序会提前退出,返回值不是try或catch中保存的返回值。 System.exit(0);//退出jvm 那么finally是不会执行的 举例: class Demo2{ public static void main(String[] args){ try{ div(1); }catch(Exception e){ e.printStackTrace(); System.out.println("xipanzi"); } } public static void div(int n) throws Exception{ try{ System.exit(0); }catch(Exception e){ System.out.print("sdf"); }finally{ System.out.print("finally"); } } } 如果try里面是return 那么也会执行finally 但是不会影响return的返回值 因为finally是在return之后才执行的 fianlly释放资源的代码 import java.io.*; class Demo6 { public static void main(String[] args) throws Exception { FileReader fileReader = null; try{ //找到目标文件 File file = new File("f:\\a.txt"); //建立程序与文件的数据通道 fileReader = new FileReader(file); //读取文件 char[] buf = new char[1024]; int length = 0; length = fileReader.read(buf); System.out.println("读取到的内容:"+ new String(buf,0,length)); }catch(IOException e){ System.out.println("读取资源文件失败...."); }finally{ try{ //关闭资源 fileReader.close(); System.out.println("释放资源文件成功...."); }catch(IOException e){ System.out.println("释放资源文件失败...."); } } } } 总结: 1:子类覆盖父类方法是,父类方法抛出异常,子类的覆盖方法可以不抛 出异常,或者抛出父类方法的异常,或者该父类方法异常的子类。 2:父类方法抛出了多个异常,子类覆盖方法时,只能抛出父类异常的子 集 3:父类没有抛出异常子类不可抛出异常 1:子类发生非运行时异常,需要进行try{}catch的(){}处理,不能 抛出。 4:子类不能比父类抛出更多的异常

资源下载

更多资源
腾讯云软件源

腾讯云软件源

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

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应用均可从中受益。

Sublime Text

Sublime Text

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

用户登录
用户注册