• 关于

    对象识别是什么

    的搜索结果

回答

反射主要做两件事情,一个是运行时创建(dc)(最典型的,插件程序,插件在主程序后被开发出来,主程序要创建插件对象,就不能在编译的时候用new写出来,必须在运行时反射创建),一个是运行时类型识别(rtti)(典型的,myeclipse能给你某个对象有什么方法、成员的列表提示,而显然这个过程也是运行时动态获取的)。`Class clazz=Class.forName("test.User");Object obj=clazz.newInstance();`这是用反射创建对象的方法之一,,也是最常用的

蛮大人123 2019-12-02 02:19:46 0 浏览量 回答数 0

回答

必须是C语言 C++的也勉强。 其它的就不行了 比如java是不被接受的 1、既然会C++了,基本等于掌握了C。 C++已经包含了C的。可以说C++只是在C的基础上增加了对象的概念! 如果果求不用C++,只要使用C++面向对象的编程思想就可以了(也就是C了,什么cin,cout都换成C常用的输入输出就可以了)。 2、计算机专业有:计算机系统结构,计算机软件与理论,计算机应用技术,计算机科学与技术,(专业学位)计算机技术,模式识别与智能系统。 方向是由导师决定的,导师研究什么就学什么。像什么数据挖掘,信息安全,图形图像处理,经济信息处理与仿真,数据库技术及其应用,计算机网络,多媒体信息处理,企业信息化,软件工程,计算智能,信息检索与自然语言处理等很多,在报名的时候可以看到。 还有就是计算机专业研究生毕业,别人不会问是计算机什么方向的,只会在乎做了什么项目,有什么样的经验。在毕业证上也只有专业名称(如计算机应用技术)没有什么方向。

liujae 2019-12-02 01:22:24 0 浏览量 回答数 0

回答

数据挖掘和机器学习的区别和联系,周志华有一篇很好的论述《机器学习和 数据挖掘》可以帮助大家理解。 数据挖掘受到很多学科领域的影响,其中数据库、机器学习、统计学无疑影响 最大。简言之,对数据挖掘而言,数据库提供数据管理技术,机器学习和统计学 提供数据分析技术。 由于统计学往往醉心于理论的优美而忽视实际的效用,因此,统计学界提供的 很多技术通常都要在机器学习界进一步研究,变成有效的机器学习算法之后才能 再进入数据挖掘领域。从这个意义上说,统计学主要是通过机器学习来对数据挖 掘发挥影响,而机器学习和数据库则是数据挖掘的两大支撑技术。 从数据分析的角度来看,绝大多数数据挖掘技术都来自机器学习领域,但机器 学习研究往往并不把海量数据作为处理对象,因此,数据挖掘要对算法进行改造, 使得算法性能和空间占用达到实用的地步。同时,数据挖掘还有自身独特的内容, 即关联分析。 而模式识别和机器学习的关系是什么呢,传统的模式识别的方法一般分为两种: 统计方法和句法方法。句法分析一般是不可学习的,而统计分析则是发展了不少机 器学习的方法。也就是说,机器学习同样是给模式识别提供了数据分析技术。 至于,数据挖掘和模式识别,那么从其概念上来区分吧,数据挖掘重在发现知识, 模式识别重在认识事物。 机器学习的目的是建模隐藏的数据结构,然后做识别、预测、分类等。因此,机器 学习是方法,模式识别是目的。 总结一下吧。只要跟决策有关系的都能叫 AI(人工智能),所以说 PR(模式识别)、 DM(数据挖掘)、IR(信息检索) 属于 AI 的具 体应用应该没有问题。 研究的东西则 不太一样, ML(机器学习) 强调自我完善的过程。 Anyway,这些学科都是相通的。

珍宝珠 2019-12-02 03:22:18 0 浏览量 回答数 0

阿里云试用中心,为您提供0门槛上云实践机会!

0元试用32+款产品,最高免费12个月!拨打95187-1,咨询专业上云建议!

问题

Java SDK是什么?

nicenelly 2019-12-01 21:28:02 1867 浏览量 回答数 0

回答

首先你后台写的是post接口,但是前面用get请求来请求是有问题的,其次你的get请求的的地址参数好像对特殊字符识别不了,中文需要转码,而且你的参数写错地方了,你应该用out这个对象吧?后面的字符串写进流里面去传输,直接在地址后面是get请求。 postMapping只能接post请求,确认下传参对么?或者换成requestMapping试下 URLConnection对象里面设置一下requestMethod("POST") 而且返回一个null会不会报错我也不确定, HttpClienthttpClient=newHttpClient();//客户端实例化PostMethodpostMethod=newPostMethod(requestUrl); 可以换个对象去调用接口,里面封装了一些方法,也可以避免一下流相关的处理错误 老哥,你这写的有点拉风啊@postmapping定义的sendPostTest02函数为什么就sendPostTest01确拼成get请求呢

爱吃鱼的程序员 2020-06-23 14:37:51 0 浏览量 回答数 0

问题

iOS SDK是什么?

nicenelly 2019-12-01 21:28:04 1767 浏览量 回答数 0

回答

第一种:Object 构造函数创建 var Person = new Object(); Person.name = "Nike"; Person.age = 29; 这行代码创建了 Object 引用类型的一个新实例,然后把实例保存在变量 Person 中。 第二种:使用对象字面量表示法 var Person = {}; //相当于 var Person = new Object(); var Person = { name: 'Nike'; age: 29; } 对象字面量是对象定义的一种简写形式,目的在于简化创建包含大量属性的对象的过程。也就是说,第一种和第二种方式创建对象的方法其实都是一样的,只是写法上的区别不同 在介绍第三种的创建方法之前,我们应该要明白为什么还要用别的方法来创建对象,也就是第一种,第二种方法的缺点所在:它们都是用了同一个接口创建很多对象,会产生大量的重复代码,就是如果你有 100 个对象,那你要输入 100 次很多相同的代码。那我们有什么方法来避免过多的重复代码呢,就是把创建对象的过程封装在函数体内,通过函数的调用直接生成对象。 第三种:使用工厂模式创建对象 function createPerson(name, age, job) { var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function() { alert(this.name); }; return o; } var person1 = createPerson("Nike", 29, "teacher"); var person2 = createPerson("Arvin", 20, "student"); 在使用工厂模式创建对象的时候,我们都可以注意到,在 createPerson 函数中,返回的是一个对象。那么我们就无法判断返回的对象究竟是一个什么样的类型。于是就出现了第四种创建对象的模式。 第四种:使用构造函数创建对象 function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = function() { alert(this.name); }; } var person1 = new Person("Nike", 29, "teacher"); var person2 = new Person("Arvin", 20, "student"); 对比工厂模式,我们可以发现以下区别: 1.没有显示地创建对象 2.直接将属性和方法赋给了 this 对象 3.没有 return 语句 4.终于可以识别的对象的类型。对于检测对象类型,我们应该使用 instanceof 操作符,我们来进行自主检测: alert(person1 instanceof Object); //ture alert(person1 instanceof Person); //ture alert(person2 instanceof Object); //ture alert(person2 instanceof Object); //ture 同时我们也应该明白,按照惯例,构造函数始终要应该以一个大写字母开头,而非构造函数则应该以一个小写字母开头。 那么构造函数确实挺好用的,但是它也有它的缺点: 就是每个方法都要在每个实例上重新创建一遍,方法指的就是我们在对象里面定义的函数。如果方法的数量很多,就会占用很多不必要的内存。于是出现了第五种创建对象的方法 第五种:原型创建对象模式 function Person() {} Person.prototype.name = "Nike"; Person.prototype.age = 20; Person.prototype.jbo = "teacher"; Person.prototype.sayName = function() { alert(this.name); }; var person1 = new Person(); person1.sayName(); 使用原型创建对象的方式,可以让所有对象实例共享它所包含的属性和方法。 如果是使用原型创建对象模式,请看下面代码: function Person() {} Person.prototype.name = "Nike"; Person.prototype.age = 20; Person.prototype.jbo = "teacher"; Person.prototype.sayName = function() { alert(this.name); }; var person1 = new Person(); var person2 = new Person(); person1.name = "Greg"; alert(person1.name); //'Greg' --来自实例 alert(person2.name); //'Nike' --来自原型 当为对象实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名属性。 这时候我们就可以使用构造函数模式与原型模式结合的方式,构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性 第六种:组合使用构造函数模式和原型模式 function Person(name, age, job) { this.name = name; this.age = age; this.job = job; } Person.prototype = { constructor: Person, sayName: function() { alert(this.name); }; } var person1 = new Person('Nike', 20, 'teacher');

茶什i 2019-12-02 03:18:14 0 浏览量 回答数 0

问题

js不同方式引入后对关键字的识别?报错

爱吃鱼的程序员 2020-06-08 14:46:20 0 浏览量 回答数 1

问题

c++ sdk是什么?

nicenelly 2019-12-01 21:01:39 1591 浏览量 回答数 0

问题

c++ sdk是什么?

nicenelly 2019-12-01 21:28:07 1372 浏览量 回答数 0

回答

在JavaScript范围内的基本层次上,对象的属性是更为复杂的实体。您可以使用setters / getters创建具有不同可枚举性,可写性和可配置性的属性。数组中的项目无法通过这种方式进行自定义:它存在或不存在。在底层引擎级别,这可以在组织表示结构的内存方面进行更多优化。 在从对象(字典)中识别数组方面,JS引擎始终在两者之间做出明确的区分。这就是为什么有很多文章尝试创建类似半伪数组的对象的方法,这些对象的行为类似于一个但允许其他功能。甚至存在这种分离的原因是因为JS引擎本身将两者存储的方式不同。 属性可以存储在数组对象上,但这仅说明了JavaScript如何坚持将所有内容制作为对象。数组中的索引值与您决定在代表基础数组数据的数组对象上设置的所有属性的存储方式不同。 每当您使用合法的数组对象并使用处理该数组的标准方法之一时,您都将访问基础数组数据。特别是在V8中,这些基本上与C ++数组相同,因此将应用这些规则。如果由于某种原因您正在使用一个引擎无法确定其是否为数组的数组,那么您将处于更加不稳定的境地。在最新版本的V8中,还有更多的工作空间。例如,可以创建一个以Array.prototype作为原型的类,并且仍然可以有效地访问各种本机数组操作方法。但这是最近的变化。 指向最近对数组操作所做的更改的特定链接可能会派上用场: http://code.google.com/p/v8/source/detail?r=10024 http://code.google.com/p/v8/source/detail?r=9849 http://code.google.com/p/v8/source/detail?r=9747 另外,这里是直接从V8来源获取的Array Pop和Array Push,它们均在JS本身中实现: function ArrayPop() { if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) { throw MakeTypeError("called_on_null_or_undefined", ["Array.prototype.pop"]); } var n = TO_UINT32(this.length); if (n == 0) { this.length = n; return; } n--; var value = this[n]; this.length = n; delete this[n]; return value; } function ArrayPush() { if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) { throw MakeTypeError("called_on_null_or_undefined", ["Array.prototype.push"]); } var n = TO_UINT32(this.length); var m = %_ArgumentsLength(); for (var i = 0; i < m; i++) { this[i+n] = %_Arguments(i); } this.length = n + m; return this.length; }

保持可爱mmm 2020-02-08 11:02:22 0 浏览量 回答数 0

问题

什么是智能云相册

云栖大讲堂 2019-12-01 21:11:14 1106 浏览量 回答数 0

问题

C++ SDK是什么?

nicenelly 2019-12-01 21:28:02 1672 浏览量 回答数 0

回答

加载验证码图片时会设置一个cookie,后台验证时根据这个cookie判断。只需要获取一次,保存后识别,然后提交上去。别忘了用cookiejar###### 引用来自“未注册用户”的评论加载验证码图片时会设置一个cookie,后台验证时根据这个cookie判断。只需要获取一次,保存后识别,然后提交上去。别忘了用cookiejar 那我应该要如何才能获取这个验证码的图片? 是不是这样? HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); String cookie = conn.getHeaderField("set-cookie");//对应你上面说的:加载验证码图片时会设置一个cookie System.out.println("cookie : " + cookie); 但是打印出来的是:  cookie : PASSID=gvXk8V; expires=Thu, 20-Jun-2013 00:42:08 GMT; path=/; domain=passport.baidu.com; httponly 我是用百度注册测试的:https://passport.baidu.com/v2/?reg&fr=old&tpl=al&u=http://open.baidu.com/?reg=pass 还有那个cookiejar是什么用的? 我百度了一下,发觉它是python的东西,和java有什么关系?   ######没注意你用的什么语言,但是原理是一样的,你f12看下会发现有设置cookie~cookiejar是python的,一个cookie自动管理的东西吧,java里找不到差不多的东西直接手动提取到然后设置算了。######我来说说,之前做过这个。 不管你前台你用什么展示,但是后台肯定 是这个逻辑。 先用 HttpURLConnection 得到图片流,注意:将这个对象中的cookie取出来。取出对应的domain,value,过期时间。保存在数据库或是文件当中。当你前台显示图片,并且输入验证友后,你再把之前那个cookie序列成对象,和你的表单一起提交。。 懂了吗?   ######你已经离成功不远了。自己做试验。######获取验证码和对应的cookie,将验证码图片拿去处理识别,识别完后与cookie一起提交进行验证码自动识别。###### 引用来自“糖度”的评论我来说说,之前做过这个。 不管你前台你用什么展示,但是后台肯定 是这个逻辑。 先用 HttpURLConnection 得到图片流,注意:将这个对象中的cookie取出来。取出对应的domain,value,过期时间。保存在数据库或是文件当中。当你前台显示图片,并且输入验证友后,你再把之前那个cookie序列成对象,和你的表单一起提交。。 懂了吗?   您好,期待你再次关注这个问题。 我现在核心的就是: 1.不是很懂如何利用HttpURLConnection来获取那个验证码的图片流。 2.验证码的流程也不是很懂,他怎么样才会肯发那个验证码给我?   还有我返回数据的时候,把之前保存的对应的domain,value,过期时间取出来加上验证码,一起返回才行?还需要其它数据吗?? 期待你的回复。。 ###### 引用来自“寒川”的评论获取验证码和对应的cookie,将验证码图片拿去处理识别,识别完后与cookie一起提交进行验证码自动识别。 您好,现在我主要碰到的问题是,不知道怎么获取验证码? 不懂在没加载完那个网页之前获取验证码。。 期待你的回复。。 ###### 引用来自“糖度”的评论我来说说,之前做过这个。 不管你前台你用什么展示,但是后台肯定 是这个逻辑。 先用 HttpURLConnection 得到图片流,注意:将这个对象中的cookie取出来。取出对应的domain,value,过期时间。保存在数据库或是文件当中。当你前台显示图片,并且输入验证友后,你再把之前那个cookie序列成对象,和你的表单一起提交。。 懂了吗?   您好,再次打扰一下你,刚刚测试一番,我是用百度网站注册来测试的。https://passport.baidu.com/v2/?reg 发觉得到那个验证码的步骤需要两个步骤: 1.首先要在这个地址 https://passport.baidu.com/v2/api/?getapi&tpl=&apiver=v3&tt=1403600195468&class=reg&app=&callback=bd__cbs__r6os13 找到: bd__cbs__r6os13({"errInfo":{ "no": "0" }, "data": { "rememberedUserName" : "kaluosi0", "codeString" : "captchaservice3235626146356e6b35355a692b49337457354d6831414d5741356a4d5a5a4566663670724f437336694a7267374844597a61664635596c2b70636a6e4c43626a4c304634584d354c786a726f31667637756662524f636a6e5474484f4b4d376c7053666946712f4a706a502f6e47714a506f436d725531714c795746364d4b32583375372f3067756572744d742b55675076383076657261754f3835757865556a584c7558347a7542664c66394e306d5933305572744a6f764b5a4354794570464164397a795a7939426b4d44754145766c35726c33737749697838696b63383341716834523543466771354a5949415a6f6c6576767a6a487a67354a67466d31744b54504274496c4d4e316e364d393655766332716277566c6539724b6132424a47672b413469", "token" : "5e4987883b49beac2105b39cd2395c10", "cookie" : "1", "usernametype":"1", "spLogin" : "rate", "disable":"", "loginrecord":{ 'email':[ ], 'phone':[ ] } }}) 截那个 captchaservice....95c10这段代码。 2.把它并到 这里去,即 https://passport.baidu.com/cgi-bin/genimage?captchaservice316663616e4141716e4f6f58685064526538414c474730365638724634494e536579733064592b46387339314132396f76374632587742634a46414a79596f3165667a2b787371684c58334342436179736468722b697a366850682f6c41694b4359593371377a72432f504e5a78744f62576c4f65375a4b392b4853506f2f674d544b794b342b41657376673148764c336c49514178445546654854645845464947447a424e3731716e766d326f4f513032326c423834746d7a7049456e724579623469774c75627a46734a73346c6f5835567169335735563465596f5a766f2f355074582f6b734f775743553855327a69347153444b51434e344a587969415571525730414f766e4159436630417a57397a3555744a61492f2f487a6b3233674231596377   上面那个就是验证码图片的地址,不过每次刷新验证码的地址都不一样,即 https://passport.baidu.com/v2/api/?getapi&tpl=&apiver=v3&tt=1403600195468&class=reg&app=&callback=bd__cbs__r6os13 每次刷新都不一样。   但是问题是,在我获得这个验证码的图片之前还有之后我该怎么做? 是不是要请求某些网址,等待返回些信息,然后保存这样的? 流程是怎么样的? 就拿https://passport.baidu.com/v2/?reg这个网址来说。   大神,我扣扣980914629,你看到之后,希望你能加加我,我很想学好这个技术,谢谢,不会打扰你太长时间的,麻烦了。。 ###### 引用来自“糖度”的评论我来说说,之前做过这个。 不管你前台你用什么展示,但是后台肯定 是这个逻辑。 先用 HttpURLConnection 得到图片流,注意:将这个对象中的cookie取出来。取出对应的domain,value,过期时间。保存在数据库或是文件当中。当你前台显示图片,并且输入验证友后,你再把之前那个cookie序列成对象,和你的表单一起提交。。 懂了吗?   引用来自“mimixi666”的评论 您好,再次打扰一下你,刚刚测试一番,我是用百度网站注册来测试的。https://passport.baidu.com/v2/?reg 发觉得到那个验证码的步骤需要两个步骤: 1.首先要在这个地址 https://passport.baidu.com/v2/api/?getapi&tpl=&apiver=v3&tt=1403600195468&class=reg&app=&callback=bd__cbs__r6os13 找到: bd__cbs__r6os13({"errInfo":{ "no": "0" }, "data": { "rememberedUserName" : "kaluosi0", "codeString" : "captchaservice3235626146356e6b35355a692b49337457354d6831414d5741356a4d5a5a4566663670724f437336694a7267374844597a61664635596c2b70636a6e4c43626a4c304634584d354c786a726f31667637756662524f636a6e5474484f4b4d376c7053666946712f4a706a502f6e47714a506f436d725531714c795746364d4b32583375372f3067756572744d742b55675076383076657261754f3835757865556a584c7558347a7542664c66394e306d5933305572744a6f764b5a4354794570464164397a795a7939426b4d44754145766c35726c33737749697838696b63383341716834523543466771354a5949415a6f6c6576767a6a487a67354a67466d31744b54504274496c4d4e316e364d393655766332716277566c6539724b6132424a47672b413469", "token" : "5e4987883b49beac2105b39cd2395c10", "cookie" : "1", "usernametype":"1", "spLogin" : "rate", "disable":"", "loginrecord":{ 'email':[ ], 'phone':[ ] } }}) 截那个 captchaservice....95c10这段代码。 2.把它并到 这里去,即 https://passport.baidu.com/cgi-bin/genimage?captchaservice316663616e4141716e4f6f58685064526538414c474730365638724634494e536579733064592b46387339314132396f76374632587742634a46414a79596f3165667a2b787371684c58334342436179736468722b697a366850682f6c41694b4359593371377a72432f504e5a78744f62576c4f65375a4b392b4853506f2f674d544b794b342b41657376673148764c336c49514178445546654854645845464947447a424e3731716e766d326f4f513032326c423834746d7a7049456e724579623469774c75627a46734a73346c6f5835567169335735563465596f5a766f2f355074582f6b734f775743553855327a69347153444b51434e344a587969415571525730414f766e4159436630417a57397a3555744a61492f2f487a6b3233674231596377   上面那个就是验证码图片的地址,不过每次刷新验证码的地址都不一样,即 https://passport.baidu.com/v2/api/?getapi&tpl=&apiver=v3&tt=1403600195468&class=reg&app=&callback=bd__cbs__r6os13 每次刷新都不一样。   但是问题是,在我获得这个验证码的图片之前还有之后我该怎么做? 是不是要请求某些网址,等待返回些信息,然后保存这样的? 流程是怎么样的? 就拿https://passport.baidu.com/v2/?reg这个网址来说。   大神,我扣扣980914629,你看到之后,希望你能加加我,我很想学好这个技术,谢谢,不会打扰你太长时间的,麻烦了。。 我最后的成果是这样: 链接: http://pan.baidu.com/s/1i5yrpml 密码: 68mg 这是android上的java代码,大家可以参考下

kun坤 2020-06-07 14:04:08 0 浏览量 回答数 0

问题

Android SDK是什么?

nicenelly 2019-12-01 21:28:03 1794 浏览量 回答数 0

问题

C++ SDK是什么?

nicenelly 2019-12-01 21:01:22 1566 浏览量 回答数 0

问题

iOS SDK是什么?

nicenelly 2019-12-01 21:01:26 1234 浏览量 回答数 0

问题

Java技术1000问(3)【精品问答】

问问小秘 2020-06-02 14:27:10 42 浏览量 回答数 1

回答

第一:不能有两个@RequestBody 第二:@RequestParam不能接收application/json的json数据 第三:一个@RequestBody正确 ---------------分割线--------------- 补充一下,想传数组还有一种方法:用@RequestParam接收 controller 代码 public void func(@RequestParam String[] names, @ReqeustParam Integer[] ages) { // do someting } ajax代码 $.ajax( url: url, type: 'post', data: {"names[0]": "name0", "names[1]": "name1", "ages[0]": "age0", "ages[1]": "age1"}, ... )  ######回复 @小伯恩 : 不好意思,太久没上OSC了。因为每个请求就一个body体,requestBody是放在body体传的######为什么就不能有两个啊,别人传json数据######回复 @whatwhowhy : 是,用@RequestParam接收的话要是form表单提交才行######我后台ajax定义了 headers: { 'Accept': 'application/json','Content-Type':'application/json' }, 所以没成功。。。######还是报Required String[] parameter 'names' is not present 错。 springboot + framework###### 这种 在ajax中以json传递的对象,在后端SPring 都认为是个对象,而不是将对象中的数组,分为多个数组。 这个本质上是JSON转换为Object的语义造成的。######没,其实一楼说的很对,如果只有一个@RequestBody确实可以获取######回复 @whatwhowhy : 这个貌似很有意思,我一直没有这样获取过。 有实例的话可以说下######也不是这样吧,spring MVC貌似就可以这样获取###### @RequestBody会去识别参数的类型。如果只有一个数组ajax参数里面写数组就可以。如果是多个就需要在后台封装一个对象接收了。###### 传参数的时候 adminIds:JSON.stringify(row), 这样传参,你后台指定用adminIds接收,但是前台没这个参数的嘛,没那么智能######有吧,在json里面有的###### 是不是 String[]的限定有问题######不知道,第一次用springboot + framework ,所以拿出来求解惑###### 将后端参数定义前加@RequestBody 修饰######@RequestBody接收,接到的是null,一楼说了只能有一个,我如果不用对象的话有好几个

爱吃鱼的程序员 2020-06-03 20:49:34 0 浏览量 回答数 0

回答

大家都知道操作 DOM 是很慢的,为什么慢的原因已经在「浏览器渲染原理」章节中说过,这里就不再赘述了。 那么相较于 DOM 来说,操作 JS 对象会快很多,并且我们也可以通过 JS 来模拟 DOM const ul = { tag: 'ul', props: { class: 'list' }, children: { tag: 'li', children: '1' } } 上述代码对应的 DOM 就是 <ul class='list'> <li>1</li> </ul> 那么既然 DOM 可以通过 JS 对象来模拟,反之也可以通过 JS 对象来渲染出对应的 DOM。当然了,通过 JS 来模拟 DOM 并且渲染对应的 DOM 只是第一步,难点在于如何判断新旧两个 JS 对象的最小差异并且实现局部更新 DOM。 首先 DOM 是一个多叉树的结构,如果需要完整的对比两颗树的差异,那么需要的时间复杂度会是 O(n ^ 3),这个复杂度肯定是不能接受的。于是 React 团队优化了算法,实现了 O(n) 的复杂度来对比差异。 实现 O(n) 复杂度的关键就是只对比同层的节点,而不是跨层对比,这也是考虑到在实际业务中很少会去跨层的移动 DOM 元素。 所以判断差异的算法就分为了两步 首先从上至下,从左往右遍历对象,也就是树的深度遍历,这一步中会给每个节点添加索引,便于最后渲染差异一旦节点有子元素,就去判断子元素是否有不同 在第一步算法中我们需要判断新旧节点的 tagName 是否相同,如果不相同的话就代表节点被替换了。如果没有更改 tagName 的话,就需要判断是否有子元素,有的话就进行第二步算法。 在第二步算法中,我们需要判断原本的列表中是否有节点被移除,在新的列表中需要判断是否有新的节点加入,还需要判断节点是否有移动。 举个例子来说,假设页面中只有一个列表,我们对列表中的元素进行了变更 // 假设这里模拟一个 ul,其中包含了 5 个 li [1, 2, 3, 4, 5] // 这里替换上面的 li [1, 2, 5, 4] 从上述例子中,我们一眼就可以看出先前的 ul 中的第三个 li 被移除了,四五替换了位置。 那么在实际的算法中,我们如何去识别改动的是哪个节点呢?这就引入了 key 这个属性,想必大家在 Vue 或者 React 的列表中都用过这个属性。这个属性是用来给每一个节点打标志的,用于判断是否是同一个节点。 当然在判断以上差异的过程中,我们还需要判断节点的属性是否有变化等等。 当我们判断出以上的差异后,就可以把这些差异记录下来。当对比完两棵树以后,就可以通过差异去局部更新 DOM,实现性能的最优化。 另外再来回答「为什么 Virtual DOM 比原生 DOM 快」这个问题。首先这个问题得分场景来说,如果无脑替换所有的 DOM 这种场景来说,Virtual DOM 的局部更新肯定要来的快。但是如果你可以人肉也同样去局部替换 DOM,那么 Virtual DOM 必然没有你直接操作 DOM 来的快,毕竟还有一层 diff 算法的损耗。 当然了 Virtual DOM 提高性能是其中一个优势,其实最大的优势还是在于: 将 Virtual DOM 作为一个兼容层,让我们还能对接非 Web 端的系统,实现跨端开发。同样的,通过 Virtual DOM 我们可以渲染到其他的平台,比如实现 SSR、同构渲染等等。实现组件的高度抽象化

前端问答 2019-12-24 12:32:58 0 浏览量 回答数 0

问题

Android SDK是什么?

nicenelly 2019-12-01 21:01:23 1531 浏览量 回答数 0

问题

anycmd所理解的rbac:报错

kun坤 2020-06-07 13:54:38 0 浏览量 回答数 1

回答

什么是机器学习? 如果人类能够训练机器从过去的数据中学习呢?嗯,这被称为机器学习,但它不仅仅是学习,它还涉及理解和推理,所以今天我们将学习机器学习的基础知识。 插一段《Python3入门机器学习经典算法与应用》这门课程中的解释: 人类是怎么学习的?通过给大脑输入一定的资料,经过学习总结得到知识和经验,有当类似的任务时可以根据已有的经验做出决定或行动。 机器学习(Machine Learning)的过程与人类学习的过程是很相似的。机器学习算法本质上就是获得一个 f(x) 函数表示的模型,如果输入一个样本 x 给 f(x) 得到的结果是一个类别,解决的就是一个分类问题,如果得到的是一个具体的数值那么解决的就是回归问题。 机器学习与人类学习的整体机制是一致的,有一点区别是人类的大脑只需要非常少的一些资料就可以归纳总结出适用性非常强的知识或者经验,例如我们只要见过几只猫或几只狗就能正确的分辨出猫和狗,但对于机器来说我们需要大量的学习资料,但机器能做到的是智能化不需要人类参与。 简单的示例 保罗听新歌,他根据歌曲的节奏、强度和声音的性别来决定喜欢还是不喜欢。 为了简单起见,我们只使用速度和强度。所以在这里,速度是在 x 轴上,从缓慢到快速,而强度是在 y 轴上,从轻到重。我们看到保罗喜欢快节奏和高亢的歌曲,而他不喜欢慢节奏和轻柔的歌曲。 现在我们知道了保罗的选择,让我们看看保罗听一首新歌,让我们给它命名这首歌 A,歌曲 A 速度快,强度飙升,所以它就在这里的某个地方。看看数据,你能猜出球在哪里会喜欢这首歌? ![7.jpg](https://ucc.alicdn.com/pic/d eveloper-ecology/a61a1dd9937f4aa4bba873397609969b.jpg) 对,保罗喜欢这首歌。 通过回顾保罗过去的选择,我们能够很容易地对未知的歌曲进行分类。假设现在保罗听了一首新歌,让我们把它贴上 B 的标签,B 这首歌就在这里的某个地方,节奏中等,强度中等,既不放松也不快速, 既不轻缓也不飞扬。 现在你能猜出保罗喜欢还是不喜欢它吗?不能猜出保罗会喜欢或不喜欢它,其他选择还不清楚。没错,我们可以很容易地对歌曲 A 进行分类,但是当选择变得复杂时,就像歌曲B 一样。机器学习可以帮你解决这个问题。 让我们看看如何。在歌曲 B 的同一个例子中,如果我们在歌曲 B 周围画一个圆圈,我们会看到有四个绿色圆点表示喜欢,而一个红色圆点不喜欢。 如果我们选择占大多数比例的绿色圆点,我们可以说保罗肯定会喜欢这首歌,这就是一个基本的机器学习算法,它被称为 K 近邻算法, 这只是众多机器学习算法之一中的一个小例子。 但是当选择变得复杂时会发生什么?就像歌曲 B 的例子一样,当机器学习进入时,它会学习数据,建立预测模型,当新的数据点进来时,它可以很容易地预测它。数据越多,模型越好,精度越高。 机器学习的分类 机器学习的方式有很多,它可以是监督学习、无监督学习或强化学习。 监督学习 让我们首先快速了解监督学习。假设你的朋友给你 100 万个三种不同货币的硬币,比如说一个是 1 欧元,一个是 1 欧尔,每个硬币有不同的重量,例如,一枚 1 卢比的硬币重 3 克, 一欧元重 7 克,一欧尔重 4 克,你的模型将预测硬币的货币。在这里,体重成为硬币的特征,而货币成为标签,当你将这些数据输入机器学习模型时,它会学习哪个特征与哪个结果相关联。 例如,它将了解到,如果一枚硬币是三克,它将是一枚卢比硬币。根据新硬币的重量,你的模型将预测货币。因此,监督学习使用标签数据来训练模型。在这里,机器知道对象的特征以及与这些特征相关的标签。 无监督学习 在这一点上,让我们看看与无监督学习的区别。假设你有不同球员的板球数据集。当您将此数据集送给机器时,机器会识别玩家性能的模式,因此它会在 x 轴上使用各自的 Achatz 对这些数据进行处理,同时在 y 轴上运行 在查看数据时,你会清楚地看到有两个集群,一个集群是得分高,分较少的球员,而另一个集群是得分较少但得分较多的球员,所以在这里我们将这两个集群解释为击球手和投球手。 需要注意的重要一点是,这里没有击球手、投球手的标签,因此 使用无标签数据的学习是无监督学习。因此,我们了解了数据被标记的监督学习和数据未标记的无监督学习。 强化学习 然后是强化学习,这是一种基于奖励的学习,或者我们可以说它的工作原理是反馈。 在这里,假设你向系统提供了一只狗的图像,并要求它识别它。系统将它识别为一只猫,所以你给机器一个负面反馈,说它是狗的形象,机器会从反馈中学习。最后,如果它遇到任何其他狗的图像,它将能够正确分类,那就是强化学习。 让我们看一个流程图,输入给机器学习模型,然后根据应用的算法给出输出。如果是正确的,我们将输出作为最终结果,否则我们会向火车模型提供反馈,并要求它预测,直到它学 机器学习的应用 你有时不知道在当今时代,机器学习是如何成为可能的,那是因为今天我们有大量可用的数据,每个人都在线,要么进行交易,要么上网,每分钟都会产生大量数据,数据是分析的关键。 此外,计算机的内存处理能力也在很大程度上增加,这有助于他们毫不拖延地处理手头如此大量的数据。 是的,计算机现在拥有强大的计算能力,所以有很多机器学习的应用。 仅举几例,机器学习用于医疗保健,在医疗保健中,医生可以预测诊断,情绪分析。 科技巨头在社交媒体上所做的推荐是另一个有趣的应用。金融部门的机器学习欺诈检测,并预测电子商务部门的客户流失。 小测验 我希望你已经理解了监督和无监督学习,所以让我们做一个快速测验,确定给定的场景是使用监督还是非监督学习。 场景 1:  Facebook 从一张标签照片相册中识别出你的朋友场景 2: Netflix 根据某人过去的电影选择推荐新电影场景 3: 分析可疑交易的银行数据并标记欺诈交易 场景 1: Facebook 在一张标签照片相册中的照片中识别你的朋友解释: 这是监督学习。在这里,Facebook 正在使用标记的照片来识别这个人。因此,标记的照片成为图片的标签,我们知道当机器从标记的数据中学习时,它是监督学习。 场景 2: 根据某人过去的音乐选择推荐新歌解释: 这是监督学习。该模型是在预先存在的标签 (歌曲流派) 上训练分类器。这是 Netflix,Pandora 和 Spotify 一直在做的事情,他们收集您已经喜欢的歌曲/电影,根据您的喜好评估功能,然后根据类似功能推荐新电影/歌曲。 场景 3: 分析可疑交易的银行数据并标记欺诈交易解释: 这是无监督学习。在这种情况下,可疑交易没有定义,因此没有 “欺诈” 和 “非欺诈” 的标签。该模型试图通过查看异常交易来识别异常值,并将其标记为 “欺诈”。

剑曼红尘 2020-04-15 19:05:53 0 浏览量 回答数 0

回答

最好一个表,要不然,会员和其他表的关联会出问题,比如会员买东西,会员发表评论,你总不能把评论表也搞成两个,购买记录也搞成两个表吧?后患无穷######搞个字段用来区分会员类型不就OK了?######一个表   用一个字段区分  线下,线上    你线上也需要注册的? 也就是说你的表的会员都有  帐号,密码字段    你在加个 初始密码是否使用 的字段就完了 至于你说的初始密码的逻辑 //登录 if(线下){ if(!初始密码是否使用){ 初始密码是否使用 = true; } } ...执行登录 ######回复 @ZhangKevin2 : 判断不只是js判断,服务端也要判断 js是可以绕过的,除了问题就大条了######那我就登录的时候 判断 写个正则,凡是DOWN_开头的 帐户名ID都是线下的,否则就执行普通线上注册的用户登录? 然后线上普通注册的 在注册的时候我在写个JS 限制不能以DOWN_开头注册就行了? 那线下的帐户名和初始密码 是用EXCEL直接导入数据库会员表里?###### 引用来自“NikoG”的答案 搞个字段用来区分会员类型不就OK了? 是啊,这个不就解决了吗?然后在付款的时候判断一下不就得了吗?反正那些行为线下会员优惠点的话,直接判断一下不就得了吗? ######楼主考虑的复杂了吧,你这个情况就如上面提到的,用一个字段识别一下即可。另外,你最后说的VIP和普通会员,与你现在的情况其实是一个性质的。 就和论坛中有不同的用户组一样,不同的组不同的权限,而且也强烈建议你使用权限来区分各用户组所能进行的操作,这样也有利于你日后的扩展。给你一个参考的基础库结构: ID:int(索引) UserID:nvarchar(用户名) Password:nvarchar(密码) Int:int(boolean,是否完成初始化) Group:int(用户组)(另建用户组表,定义各组的权限) Rebate:int(折扣,也可在用户组中定义折扣率,此处的折扣率可用做针对单个用户的私有权限) ######回复 @psaux : 当然CHAR了,MD5之后固定的位数######password直接用char吧,事先确定了使用的hash加密方式和位数。######因为就一个线下和线上 2种情况,所以我觉得没必要搞个用户组来单独搞权限操作######如果线上会员和线下会员区别很大,建议分开表。只是要区分线上线下而已,可以只用一个字段识别。线下会员首次登录需要改密码使用登录次数字段+判断初始密码是否未更改来实现######回复 @ZhangKevin2 : 使用SQL的Left Join就可以。会员类型(线上线下)放置详细表######THANS,登录次数字段还没用过。。 其实还有一点很麻烦,一般会员,如果想登录块点都是分2个会员表,一个是 帐号和密码会员表用户登录,与之相关联的是会员详细信息表,比如会员的个人资料等等。 如此 如果结合线上线下的话 更纠结。。###### 显然分成两个表比较好 而且分成两个系统开发测试也快。 ###### 显然一个表,会员是一个对象,线上线下只是这个对象的属性。你的需求和苏宁电器的会员很类似,苏宁7000多万会员也分线下pos端申请和线上易购网申请,我们用的是一个表,这里存在一个线上线下同步问题,如果会员量小不用考虑都指向一个表就行。另外我们会根据地理区域分库,也就是水平分割,否则检索太慢。首次登陆改密码这个很多会员都有这样的需求,也可以用属性来区别。另外我说的会员一个表是逻辑上的一个表,由于会员有很多属性,都放一个表会影响性能,可以根据业务类型进行垂直切割。 ###### 引用来自“风飞雪”的答案 显然一个表,会员是一个对象,线上线下只是这个对象的属性。你的需求和苏宁电器的会员很类似,苏宁7000多万会员也分线下pos端申请和线上易购网申请,我们用的是一个表,这里存在一个线上线下同步问题,如果会员量小不用考虑都指向一个表就行。另外我们会根据地理区域分库,也就是水平分割,否则检索太慢。首次登陆改密码这个很多会员都有这样的需求,也可以用属性来区别。另外我说的会员一个表是逻辑上的一个表,由于会员有很多属性,都放一个表会影响性能,可以根据业务类型进行垂直切割。 求详细点。。。  之前做的都是普通会员和VIP会员,ECSHOP二次开发等等,这次虽然项目不大也不难,但我还是想尽力做完善点,关键是我们公司策划烂到家了 天天改需求,线上线下会员肯定会有区别,但以后有多大区别还不晓得。 最烦的是有区别又有联系,如果分2个表 以后还要联表。 有些业务处理也麻烦。但如果放在一个表里,2种会员字段数也不一样,线上可能就5个,线下可能还要身份证什么的因为是发卡一对一发展现实中的会员,而且线下会员安全性要求更高 ###### 引用来自“ZhangKevin2”的答案 引用来自“风飞雪”的答案 显然一个表,会员是一个对象,线上线下只是这个对象的属性。你的需求和苏宁电器的会员很类似,苏宁7000多万会员也分线下pos端申请和线上易购网申请,我们用的是一个表,这里存在一个线上线下同步问题,如果会员量小不用考虑都指向一个表就行。另外我们会根据地理区域分库,也就是水平分割,否则检索太慢。首次登陆改密码这个很多会员都有这样的需求,也可以用属性来区别。另外我说的会员一个表是逻辑上的一个表,由于会员有很多属性,都放一个表会影响性能,可以根据业务类型进行垂直切割。 求详细点。。。  之前做的都是普通会员和VIP会员,ECSHOP二次开发等等,这次虽然项目不大也不难,但我还是想尽力做完善点,关键是我们公司策划烂到家了 天天改需求,线上线下会员肯定会有区别,但以后有多大区别还不晓得。 最烦的是有区别又有联系,如果分2个表 以后还要联表。 有些业务处理也麻烦。但如果放在一个表里,2种会员字段数也不一样,线上可能就5个,线下可能还要身份证什么的因为是发卡一对一发展现实中的会员,而且线下会员安全性要求更高 建议一个,如果按你的思路以后如果手机可以注册,为了区别手机注册的还要建个会员表?如果会员系统提供接口给别的系统用,你还要根据会员号去不同的表里查找?显然这样会给系统开发带来不必要的麻烦,这些一个字段就可以解决。现在存储设备如此便宜,为了减少系统复杂而浪费些空间非常值得。不同注册方法的会员字段不同,没有值得让它空着好了,不要太完美了。我们很多表都冗余了浮点和字符的字段,因为需求总是不断变化,时不时的要增加字段,这种冗余字段的方法带来了很多方便。设计系统和表结构首先一个原则就是尽可能的简单,然后再考虑性能最后考虑空间问题。如果会员有上百万建议切割表,将不常用的字段放在另一张表中。 一些拙见,不足之处还望不吝赐教。

kun坤 2020-06-08 16:18:16 0 浏览量 回答数 0

问题

关于多线程编程您不知道的 5 件事:报错

kun坤 2020-06-07 21:21:26 0 浏览量 回答数 1

回答

与 C++ 不同,Java 不支持运算符重载。Java 不能为程序员提供自由的标准算术运算符重载,例如+, - ,*和/等。如果你以前用过 C++,那么 Java 与 C++ 相比少了很多功能,例如 Java 不支持多重继承,Java中没有指针,Java中没有引用传递。另一个类似的问题是关于 Java 通过引用传递,这主要表现为 Java 是通过值还是引用传参。虽然我不知道背后的真正原因,但我认为以下说法有些道理,为什么 Java 不支持运算符重载。 1)简单性和清晰性。清晰性是Java设计者的目标之一。设计者不是只想复制语言,而是希望拥有一种清晰,真正面向对象的语言。添加运算符重载比没有它肯定会使设计更复杂,并且它可能导致更复杂的编译器, 或减慢 JVM,因为它需要做额外的工作来识别运算符的实际含义,并减少优化的机会, 以保证 Java 中运算符的行为。 2)避免编程错误。Java 不允许用户定义的运算符重载,因为如果允许程序员进行运算符重载,将为同一运算符赋予多种含义,这将使任何开发人员的学习曲线变得陡峭,事情变得更加混乱。据观察,当语言支持运算符重载时,编程错误会增加,从而增加了开发和交付时间。由于 Java 和 JVM 已经承担了大多数开发人员的责任,如在通过提供垃圾收集器进行内存管理时,因为这个功能增加污染代码的机会, 成为编程错误之源, 因此没有多大意义。 3)JVM复杂性。从JVM的角度来看,支持运算符重载使问题变得更加困难。通过更直观,更干净的方式使用方法重载也能实现同样的事情,因此不支持 Java 中的运算符重载是有意义的。与相对简单的 JVM 相比,复杂的 JVM 可能导致 JVM 更慢,并为保证在 Java 中运算符行为的确定性从而减少了优化代码的机会。 4)让开发工具处理更容易。这是在 Java 中不支持运算符重载的另一个好处。省略运算符重载使语言更容易处理,这反过来又更容易开发处理语言的工具,例如 IDE 或重构工具。Java 中的重构工具远胜于 C++。

YDYK 2020-04-25 14:34:17 0 浏览量 回答数 0

回答

在日常开发中,我们会经常要在类中定义布尔类型的变量,比如在给外部系统提供一个RPC接口的时候,我们一般会定义一个字段表示本次请求是否成功的。 关于这个"本次请求是否成功"的字段的定义,其实是有很多种讲究和坑的,稍有不慎就会掉入坑里,作者在很久之前就遇到过类似的问题,本文就来围绕这个简单分析一下。到底该如何定一个布尔类型的成员变量。 一般情况下,我们可以有以下四种方式来定义一个布尔类型的成员变量: boolean success boolean isSuccess Boolean success Boolean isSuccess 以上四种定义形式,你日常开发中最常用的是哪种呢?到底哪一种才是正确的使用姿势呢? 通过观察我们可以发现,前两种和后两种的主要区别是变量的类型不同,前者使用的是boolean,后者使用的是Boolean。 另外,第一种和第三种在定义变量的时候,变量命名是success,而另外两种使用isSuccess来命名的。 首先,我们来分析一下,到底应该是用success来命名,还是使用isSuccess更好一点。 success 还是 isSuccess 到底应该是用success还是isSuccess来给变量命名呢?从语义上面来讲,两种命名方式都可以讲的通,并且也都没有歧义。那么还有什么原则可以参考来让我们做选择呢。 在阿里巴巴Java开发手册中关于这一点,有过一个『强制性』规定:  那么,为什么会有这样的规定呢?我们看一下POJO中布尔类型变量不同的命名有什么区别吧。 class Model1 { private Boolean isSuccess; public void setSuccess(Boolean success) { isSuccess = success; } public Boolean getSuccess() { return isSuccess; } } class Model2 { private Boolean success; public Boolean getSuccess() { return success; } public void setSuccess(Boolean success) { this.success = success; } } class Model3 { private boolean isSuccess; public boolean isSuccess() { return isSuccess; } public void setSuccess(boolean success) { isSuccess = success; } } class Model4 { private boolean success; public boolean isSuccess() { return success; } public void setSuccess(boolean success) { this.success = success; } } 以上代码的setter/getter是使用Intellij IDEA自动生成的,仔细观察以上代码,你会发现以下规律: 基本类型自动生成的getter和setter方法,名称都是isXXX()和setXXX()形式的。包装类型自动生成的getter和setter方法,名称都是getXXX()和setXXX()形式的。 既然,我们已经达成一致共识使用基本类型boolean来定义成员变量了,那么我们再来具体看下Model3和Model4中的setter/getter有何区别。 我们可以发现,虽然Model3和Model4中的成员变量的名称不同,一个是success,另外一个是isSuccess,但是他们自动生成的getter和setter方法名称都是isSuccess和setSuccess。 Java Bean中关于setter/getter的规范 关于Java Bean中的getter/setter方法的定义其实是有明确的规定的,根据JavaBeans(TM) Specification规定,如果是普通的参数propertyName,要以以下方式定义其setter/getter: public <PropertyType> get<PropertyName>(); public void set<PropertyName>(<PropertyType> a); 但是,布尔类型的变量propertyName则是单独定义的: public boolean is<PropertyName>(); public void set<PropertyName>(boolean m);  通过对照这份JavaBeans规范,我们发现,在Model4中,变量名为isSuccess,如果严格按照规范定义的话,他的getter方法应该叫isIsSuccess。但是很多IDE都会默认生成为isSuccess。 那这样做会带来什么问题呢。 在一般情况下,其实是没有影响的。但是有一种特殊情况就会有问题,那就是发生序列化的时候。 序列化带来的影响 关于序列化和反序列化请参考Java对象的序列化与反序列化。我们这里拿比较常用的JSON序列化来举例,看看看常用的fastJson、jackson和Gson之间有何区别: public class BooleanMainTest { public static void main(String[] args) throws IOException { //定一个Model3类型 Model3 model3 = new Model3(); model3.setSuccess(true); //使用fastjson(1.2.16)序列化model3成字符串并输出 System.out.println("Serializable Result With fastjson :" + JSON.toJSONString(model3)); //使用Gson(2.8.5)序列化model3成字符串并输出 Gson gson =new Gson(); System.out.println("Serializable Result With Gson :" +gson.toJson(model3)); //使用jackson(2.9.7)序列化model3成字符串并输出 ObjectMapper om = new ObjectMapper(); System.out.println("Serializable Result With jackson :" +om.writeValueAsString(model3)); } } class Model3 implements Serializable { private static final long serialVersionUID = 1836697963736227954L; private boolean isSuccess; public boolean isSuccess() { return isSuccess; } public void setSuccess(boolean success) { isSuccess = success; } public String getHollis(){ return "hollischuang"; } } 以上代码的Model3中,只有一个成员变量即isSuccess,三个方法,分别是IDE帮我们自动生成的isSuccess和setSuccess,另外一个是作者自己增加的一个符合getter命名规范的方法。 以上代码输出结果: Serializable Result With fastjson :{"hollis":"hollischuang","success":true} Serializable Result With Gson :{"isSuccess":true} Serializable Result With jackson :{"success":true,"hollis":"hollischuang"} 在fastjson和jackson的结果中,原来类中的isSuccess字段被序列化成success,并且其中还包含hollis值。而Gson中只有isSuccess字段。 我们可以得出结论:fastjson和jackson在把对象序列化成json字符串的时候,是通过反射遍历出该类中的所有getter方法,得到getHollis和isSuccess,然后根据JavaBeans规则,他会认为这是两个属性hollis和success的值。直接序列化成json:{"hollis":"hollischuang","success":true} 但是Gson并不是这么做的,他是通过反射遍历该类中的所有属性,并把其值序列化成json:{"isSuccess":true} 可以看到,由于不同的序列化工具,在进行序列化的时候使用到的策略是不一样的,所以,对于同一个类的同一个对象的序列化结果可能是不同的。 前面提到的关于对getHollis的序列化只是为了说明fastjson、jackson和Gson之间的序列化策略的不同,我们暂且把他放到一边,我们把他从Model3中删除后,重新执行下以上代码,得到结果: Serializable Result With fastjson :{"success":true} Serializable Result With Gson :{"isSuccess":true} Serializable Result With jackson :{"success":true} 现在,不同的序列化框架得到的json内容并不相同,如果对于同一个对象,我使用fastjson进行序列化,再使用Gson反序列化会发生什么? public class BooleanMainTest { public static void main(String[] args) throws IOException { Model3 model3 = new Model3(); model3.setSuccess(true); Gson gson =new Gson(); System.out.println(gson.fromJson(JSON.toJSONString(model3),Model3.class)); } } class Model3 implements Serializable { private static final long serialVersionUID = 1836697963736227954L; private boolean isSuccess; public boolean isSuccess() { return isSuccess; } public void setSuccess(boolean success) { isSuccess = success; } @Override public String toString() { return new StringJoiner(", ", Model3.class.getSimpleName() + "[", "]") .add("isSuccess=" + isSuccess) .toString(); } } 以上代码,输出结果: Model3[isSuccess=false] 这和我们预期的结果完全相反,原因是因为JSON框架通过扫描所有的getter后发现有一个isSuccess方法,然后根据JavaBeans的规范,解析出变量名为success,把model对象序列化城字符串后内容为{"success":true}。 根据{"success":true}这个json串,Gson框架在通过解析后,通过反射寻找Model类中的success属性,但是Model类中只有isSuccess属性,所以,最终反序列化后的Model类的对象中,isSuccess则会使用默认值false。 但是,一旦以上代码发生在生产环境,这绝对是一个致命的问题。 所以,作为开发者,我们应该想办法尽量避免这种问题的发生,对于POJO的设计者来说,只需要做简单的一件事就可以解决这个问题了,那就是把isSuccess改为success。这样,该类里面的成员变量时success,getter方法是isSuccess,这是完全符合JavaBeans规范的。无论哪种序列化框架,执行结果都一样。就从源头避免了这个问题。 引用以下R大关于阿里巴巴Java开发手册这条规定的评价(https://www.zhihu.com/question/55642203):  所以,在定义POJO中的布尔类型的变量时,不要使用isSuccess这种形式,而要直接使用success! Boolean还是boolean 前面我们介绍完了在success和isSuccess之间如何选择,那么排除错误答案后,备选项还剩下: boolean success Boolean success 那么,到底应该是用Boolean还是boolean来给定一个布尔类型的变量呢? 我们知道,boolean是基本数据类型,而Boolean是包装类型。关于基本数据类型和包装类之间的关系和区别请参考一文读懂什么是Java中的自动拆装箱 那么,在定义一个成员变量的时候到底是使用包装类型更好还是使用基本数据类型呢? 我们来看一段简单的代码 /** * @author Hollis */ public class BooleanMainTest { public static void main(String[] args) { Model model1 = new Model(); System.out.println("default model : " + model1); } } class Model { /** * 定一个Boolean类型的success成员变量 */ private Boolean success; /** * 定一个boolean类型的failure成员变量 */ private boolean failure; /** * 覆盖toString方法,使用Java 8 的StringJoiner */ @Override public String toString() { return new StringJoiner(", ", Model.class.getSimpleName() + "[", "]") .add("success=" + success) .add("failure=" + failure) .toString(); } } 以上代码输出结果为: default model : Model[success=null, failure=false] 可以看到,当我们没有设置Model对象的字段的值的时候,Boolean类型的变量会设置默认值为null,而boolean类型的变量会设置默认值为false。 即对象的默认值是null,boolean基本数据类型的默认值是false。 在阿里巴巴Java开发手册中,对于POJO中如何选择变量的类型也有着一些规定: 这里建议我们使用包装类型,原因是什么呢? 举一个扣费的例子,我们做一个扣费系统,扣费时需要从外部的定价系统中读取一个费率的值,我们预期该接口的返回值中会包含一个浮点型的费率字段。当我们取到这个值得时候就使用公式:金额*费率=费用 进行计算,计算结果进行划扣。 如果由于计费系统异常,他可能会返回个默认值,如果这个字段是Double类型的话,该默认值为null,如果该字段是double类型的话,该默认值为0.0。 如果扣费系统对于该费率返回值没做特殊处理的话,拿到null值进行计算会直接报错,阻断程序。拿到0.0可能就直接进行计算,得出接口为0后进行扣费了。这种异常情况就无法被感知。 这种使用包装类型定义变量的方式,通过异常来阻断程序,进而可以被识别到这种线上问题。如果使用基本数据类型的话,系统可能不会报错,进而认为无异常。 以上,就是建议在POJO和RPC的返回值中使用包装类型的原因。 但是关于这一点,作者之前也有过不同的看法:对于布尔类型的变量,我认为可以和其他类型区分开来,作者并不认为使用null进而导致NPE是一种最好的实践。因为布尔类型只有true/false两种值,我们完全可以和外部调用方约定好当返回值为false时的明确语义。 后来,作者单独和《阿里巴巴Java开发手册》、《码出高效》的作者——孤尽 单独1V1(qing) Battle(jiao)了一下。最终达成共识,还是尽量使用包装类型。 但是,作者还是想强调一个我的观点,尽量避免在你的代码中出现不确定的null值。 总结 本文围绕布尔类型的变量定义的类型和命名展开了介绍,最终我们可以得出结论,在定义一个布尔类型的变量,尤其是一个给外部提供的接口返回值时,要使用success来命名,阿里巴巴Java开发手册建议使用封装类来定义POJO和RPC返回值中的变量。但是这不意味着可以随意的使用null,我们还是要尽量避免出现对null的处理的。

montos 2020-06-01 21:26:05 0 浏览量 回答数 0

问题

【精品问答】python技术1000问(2)

问问小秘 2019-12-01 22:03:02 68 浏览量 回答数 0

问题

安卓与iOS百问,开发者系统指南

yq传送门 2019-12-01 20:14:48 27317 浏览量 回答数 26

问题

MySQL 数据类型:报错

kun坤 2020-06-20 10:57:10 0 浏览量 回答数 1
阿里云大学 云服务器ECS com域名 网站域名whois查询 开发者平台 小程序定制 小程序开发 国内短信套餐包 开发者技术与产品 云数据库 图像识别 开发者问答 阿里云建站 阿里云备案 云市场 万网 阿里云帮助文档 免费套餐 开发者工具 企业信息查询 小程序开发制作 视频内容分析 企业网站制作 视频集锦 代理记账服务 2020阿里巴巴研发效能峰会 企业建站模板 云效成长地图 高端建站