首页 文章 精选 留言 我的

精选列表

搜索[最权威安装],共10000篇文章
优秀的个人博客,低调大师

简单的docker教程:在docker里运行nginx服务器

命令行docker search nginx搜索名为nginx的docker image,返回结果的第一个,github上有10293个star,这就是我们想要搜索的结果: 使用命令docker pull把这个镜像拖下来: docker pull nginx 然后以detach模式运行这个镜像: docker run -d --name jerry-nginx nginx docker run的输出是docker实例的uuid,它的一部分也出现在docker ps的结果里: 因为我们在运行docker镜像时,没有使用-P参数暴露端口,所以我们无法通过localhost:80来访问这个nginx服务器。 重新创建一个新的docker实例: docker run -d -P --name jerry-nginx-expose nginx 在docker

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

2018年,经典的26个JavaScript面试题和答案!

根据 Stack Overflow 的 2018 年度调查,JavaScript 连续六年成为最常用的编程语言。所以我们必须面对这样的现实,JavaScript 已经成为全栈开发技能的基石,在全栈开发面试中都会不可避免地涉及到与 JavaScript 有关的问题。FullStack.Cafe 汇编了最常见的 JavaScript 面试问题和答案,希望能够帮助读者找到下一份梦想中的工作。 Q1:JavaScript 中的强制转型(coercion)是指什么? 难度:0 在 JavaScript 中,两种不同的内置类型间的转换被称为强制转型。强制转型在 JavaScript 中有两种形式:显式和隐式。 这是一个显式强制转型的例子: var a = "42"; var b = Number( a ); a; // "42" b; // 42 -- 是个数字! 这是一个隐式强制转型的例子: var a = "42"; var b = a * 1; // "42" 隐式转型成 42 a; // "42" b; // 42 -- 是个数字! Q2:JavaScript 中的作用域(scope)是指什么? 难度:⭐ 在 JavaScript 中,每个函数都有自己的作用域。作用域基本上是变量以及如何通过名称访问这些变量的规则的集合。只有函数中的代码才能访问函数作用域内的变量。 同一个作用域中的变量名必须是唯一的。一个作用域可以嵌套在另一个作用域内。如果一个作用域嵌套在另一个作用域内,最内部作用域内的代码可以访问另一个作用域的变量。 Q3:解释 JavaScript 中的相等性。 难度:⭐ JavaScript 中有严格比较和类型转换比较: 严格比较(例如 ===)在不允许强制转型的情况下检查两个值是否相等; 抽象比较(例如 ==)在允许强制转型的情况下检查两个值是否相等。 var a = "42"; var b = 42; a == b; // true a === b; // false 一些简单的规则: 如果被比较的任何一个值可能是 true 或 false,要用 ===,而不是 ==; 如果被比较的任何一个值是这些特定值(0、“”或 []),要用 ===,而不是 ==; 在其他情况下,可以安全地使用 ==。它不仅安全,而且在很多情况下,它可以简化代码,并且提升代码可读性。 Q4:解释什么是回调函数,并提供一个简单的例子。 难度:⭐⭐ 回调函数是可以作为参数传递给另一个函数的函数,并在某些操作完成后执行。下面是一个简单的回调函数示例,这个函数在某些操作完成后打印消息到控制台。 function modifyArray(arr, callback) { // 对 arr 做一些操作 arr.push(100); // 执行传进来的 callback 函数 callback(); } var arr = [1, 2, 3, 4, 5]; modifyArray(arr, function() { console.log("array has been modified", arr); }); Q5:“use strict”的作用是什么? 难度:⭐⭐ use strict 出现在 JavaScript 代码的顶部或函数的顶部,可以帮助你写出更安全的 JavaScript 代码。如果你错误地创建了全局变量,它会通过抛出错误的方式来警告你。例如,以下程序将抛出错误: function doSomething(val) { "use strict"; x = val + 10; } 它会抛出一个错误,因为 x 没有被定义,并使用了全局作用域中的某个值对其进行赋值,而 use strict 不允许这样做。下面的小改动修复了这个错误: function doSomething(val) { "use strict"; var x = val + 10; } Q6:解释 JavaScript 中的 null 和 undefined。 难度:⭐⭐ JavaScript 中有两种底层类型:null 和 undefined。它们代表了不同的含义: 尚未初始化的东西:undefined; 目前不可用的东西:null。 Q7:编写一个可以执行如下操作的函数。 难度:⭐⭐ var addSix = createBase(6); addSix(10); // 返回 16 addSix(21); // 返回 27 可以创建一个闭包来存放传递给函数 createBase 的值。被返回的内部函数是在外部函数中创建的,内部函数就成了一个闭包,它可以访问外部函数中的变量,在本例中是变量 baseNumber。 function createBase(baseNumber) { return function(N) { // 我们在这里访问 baseNumber,即使它是在这个函数之外声明的。 // JavaScript 中的闭包允许我们这么做。 return baseNumber + N; } } var addSix = createBase(6); addSix(10); addSix(21); Q8:解释 JavaScript 中的值和类型。 难度:⭐⭐ JavaScript 有类型值,但没有类型变量。JavaScript 提供了以下几种内置类型: string number boolean null 和 undefined object symbol (ES6 中新增的) Q9:解释事件冒泡以及如何阻止它? 难度:⭐⭐ 事件冒泡是指嵌套最深的元素触发一个事件,然后这个事件顺着嵌套顺序在父元素上触发。 防止事件冒泡的一种方法是使用 event.cancelBubble 或 event.stopPropagation()(低于 IE 9)。 Q10:JavaScript 中的 let 关键字有什么用? 难度:⭐⭐ 除了可以在函数级别声明变量之外,ES6 还允许你使用 let 关键字在代码块({..})中声明变量。 Q11:如何检查一个数字是否为整数? 难度:⭐⭐ 检查一个数字是小数还是整数,可以使用一种非常简单的方法,就是将它对 1 进行取模,看看是否有余数。 function isInt(num) { return num % 1 === 0; } console.log(isInt(4)); // true console.log(isInt(12.2)); // false console.log(isInt(0.3)); // false Q12:什么是 IIFE(立即调用函数表达式)? 难度:⭐⭐⭐ 它是立即调用函数表达式(Immediately-Invoked Function Expression),简称 IIFE。函数被创建后立即被执行: (function IIFE(){ console.log( "Hello!" ); })(); // "Hello!" 在避免污染全局命名空间时经常使用这种模式,因为 IIFE(与任何其他正常函数一样)内部的所有变量在其作用域之外都是不可见的。 Q13:如何在 JavaScript 中比较两个对象? 难度:⭐⭐⭐ 对于两个非原始值,比如两个对象(包括函数和数组),== 和 === 比较都只是检查它们的引用是否匹配,并不会检查实际引用的内容。 例如,默认情况下,数组将被强制转型成字符串,并使用逗号将数组的所有元素连接起来。所以,两个具有相同内容的数组进行 == 比较时不会相等: var a = [1,2,3]; var b = [1,2,3]; var c = "1,2,3"; a == c; // true b == c; // true a == b; // false 对于对象的深度比较,可以使用 deep-equal 这个库,或者自己实现递归比较算法。 推荐一个交流学习群,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多 点击:加入 Q14:你能解释一下 ES5 和 ES6 之间的区别吗? 难度:⭐⭐⭐ ECMAScript 5(ES5):ECMAScript 的第 5 版,于 2009 年标准化。这个标准已在所有现代浏览器中完全实现。 ECMAScript 6(ES6)或 ECMAScript 2015(ES2015):第 6 版 ECMAScript,于 2015 年标准化。这个标准已在大多数现代浏览器中部分实现。 以下是 ES5 和 ES6 之间的一些主要区别: 箭头函数和字符串插值: const greetings = (name) => { return `hello ${name}`; } const greetings = name => `hello ${name}`; 常量 常量在很多方面与其他语言中的常量一样,但有一些需要注意的地方。常量表示对值的“固定引用”。因此,在使用常量时,你实际上可以改变变量所引用的对象的属性,但无法改变引用本身。 const NAMES = []; NAMES.push("Jim"); console.log(NAMES.length === 1); // true NAMES = ["Steve", "John"]; // error 块作用域变量。 新的 ES6 关键字 let 允许开发人员声明块级别作用域的变量。let 不像 var 那样可以进行提升。 默认参数值 默认参数允许我们使用默认值初始化函数。如果省略或未定义参数,则使用默认值,也就是说 null 是有效值。 // 基本语法 function multiply (a, b = 2) { return a * b; } multiply(5); // 10 类定义和继承 ES6 引入了对类(关键字 class)、构造函数(关键字 constructor)和用于继承的 extend 关键字的支持。 for…of 操作符 for…of 语句将创建一个遍历可迭代对象的循环。 用于对象合并的 Spread 操作 const obj1 = { a: 1, b: 2 } const obj2 = { a: 2, c: 3, d: 4} const obj3 = {...obj1, ...obj2} promise promise 提供了一种机制来处理异步操作结果。你可以使用回调来达到同样的目的,但是 promise 通过方法链接和简洁的错误处理带来了更高的可读性。 const isGreater = (a, b) => { return new Promise ((resolve, reject) => { if(a > b) { resolve(true) } else { reject(false) } }) } isGreater(1, 2) .then(result => { console.log('greater') }) .catch(result => { console.log('smaller') }) 模块导出和导入 const myModule = { x: 1, y: () => { console.log('This is ES5') }} export default myModule; import myModule from './myModule'; 问题 15:解释 JavaScript 中“undefined”和“not defined”之间的区别。 难度:⭐⭐⭐ 在 JavaScript 中,如果你试图使用一个不存在且尚未声明的变量,JavaScript 将抛出错误“var name is not defined”,让后脚本将停止运行。但如果你使用 typeof undeclared_variable,它将返回 undefined。 在进一步讨论之前,先让我们理解声明和定义之间的区别。 “var x”表示一个声明,因为你没有定义它的值是什么,你只是声明它的存在。 var x; // 声明 x console.log(x); // 输出: undefined “var x = 1”既是声明又是定义(我们也可以说它是初始化),x 变量的声明和赋值相继发生。在 JavaScript 中,每个变量声明和函数声明都被带到了当前作用域的顶部,然后进行赋值,这个过程被称为提升(hoisting)。 当我们试图访问一个被声明但未被定义的变量时,会出现 undefined 错误。 var x; // 声明 if(typeof x === 'undefined') // 将返回 true 当我们试图引用一个既未声明也未定义的变量时,将会出现 not defined 错误。 console.log(y);//输出: ReferenceError: yisnotdefined Q16:匿名和命名函数有什么区别? 难度:⭐⭐⭐ var foo = function() { // 赋给变量 foo 的匿名函数 // .. }; var x = function bar(){ // 赋给变量 x 的命名函数 bar // .. }; foo(); // 实际执行函数 x(); Q17:Javascript 中的“闭包”是什么?举个例子? 难度:⭐⭐⭐⭐ 闭包是在另一个函数(称为父函数)中定义的函数,并且可以访问在父函数作用域中声明和定义的变量。 闭包可以访问三个作用域中的变量: 在自己作用域中声明的变量; 在父函数中声明的变量; 在全局作用域中声明的变量。 var globalVar = "abc"; // 自调用函数 (function outerFunction (outerArg) { // outerFunction 作用域开始 // 在 outerFunction 函数作用域中声明的变量 var outerFuncVar = 'x'; // 闭包自调用函数 (function innerFunction (innerArg) { // innerFunction 作用域开始 // 在 innerFunction 函数作用域中声明的变量 var innerFuncVar = "y"; console.log( "outerArg = " + outerArg + "\n" + "outerFuncVar = " + outerFuncVar + "\n" + "innerArg = " + innerArg + "\n" + "innerFuncVar = " + innerFuncVar + "\n" + "globalVar = " + globalVar); // innerFunction 作用域结束 })(5); // 将 5 作为参数 // outerFunction 作用域结束 })(7); // 将 7 作为参数 innerFunction 是在 outerFunction 中定义的闭包,可以访问在 outerFunction 作用域内声明和定义的所有变量。除此之外,闭包还可以访问在全局命名空间中声明的变量。 上述代码的输出将是: outerArg = 7 outerFuncVar = x innerArg = 5 innerFuncVar = y globalVar = abc Q18:如何在 JavaScript 中创建私有变量? 难度:⭐⭐⭐⭐ 要在 JavaScript 中创建无法被修改的私有变量,你需要将其创建为函数中的局部变量。即使这个函数被调用,也无法在函数之外访问这个变量。例如: function func() { var priv = "secret code"; } console.log(priv); // throws error 要访问这个变量,需要创建一个返回私有变量的辅助函数。 function func() { var priv = "secret code"; return function() { return priv; } } var getPriv = func(); console.log(getPriv()); // => secret code Q19:请解释原型设计模式。 难度:⭐⭐⭐⭐ 原型模式可用于创建新对象,但它创建的不是非初始化的对象,而是使用原型对象(或样本对象)的值进行初始化的对象。原型模式也称为属性模式。 原型模式在初始化业务对象时非常有用,业务对象的值与数据库中的默认值相匹配。原型对象中的默认值被复制到新创建的业务对象中。 经典的编程语言很少使用原型模式,但作为原型语言的 JavaScript 在构造新对象及其原型时使用了这个模式。 Q20:判断一个给定的字符串是否是同构的。 难度:⭐⭐⭐⭐ 如果两个字符串是同构的,那么字符串 A 中所有出现的字符都可以用另一个字符替换,以便获得字符串 B,而且必须保留字符的顺序。字符串 A 中的每个字符必须与字符串 B 的每个字符一对一对应。 paper 和 title 将返回 true。 egg 和 sad 将返回 false。 dgg 和 add 将返回 true。 isIsomorphic("egg", 'add'); // true isIsomorphic("paper", 'title'); // true isIsomorphic("kick", 'side'); // false function isIsomorphic(firstString, secondString) { // 检查长度是否相等,如果不相等, 它们不可能是同构的 if (firstString.length !== secondString.length) return false var letterMap = {}; for (var i = 0; i < firstString.length; i++) { var letterA = firstString[i], letterB = secondString[i]; // 如果 letterA 不存在, 创建一个 map,并将 letterB 赋值给它 if (letterMap[letterA] === undefined) { letterMap[letterA] = letterB; } else if (letterMap[letterA] !== letterB) { // 如果 letterA 在 map 中已存在, 但不是与 letterB 对应, // 那么这意味着 letterA 与多个字符相对应。 return false; } } // 迭代完毕,如果满足条件,那么返回 true。 // 它们是同构的。 return true; } Q21:“Transpiling”是什么意思? 难度:⭐⭐⭐⭐ 对于语言中新加入的语法,无法进行 polyfill。因此,更好的办法是使用一种工具,可以将较新代码转换为较旧的等效代码。这个过程通常称为转换(transpiling),就是 transforming + compiling 的意思。 通常,你会将转换器(transpiler)加入到构建过程中,类似于 linter 或 minifier。现在有很多很棒的转换器可选择: Babel:将 ES6+ 转换为 ES5 Traceur:将 ES6、ES7 转换为 ES5 Q22:“this”关键字的原理是什么?请提供一些代码示例。 难度:⭐⭐⭐⭐ 在 JavaScript 中,this 是指正在执行的函数的“所有者”,或者更确切地说,指将当前函数作为方法的对象。 function foo() { console.log( this.bar ); } var bar = "global"; var obj1 = { bar: "obj1", foo: foo }; var obj2 = { bar: "obj2" }; foo(); // "global" obj1.foo(); // "obj1" foo.call( obj2 ); // "obj2" new foo(); // undefined Q23:如何向 Array 对象添加自定义方法,让下面的代码可以运行? 难度:⭐⭐⭐⭐ var arr = [1, 2, 3, 4, 5]; var avg = arr.average(); console.log(avg); JavaScript 不是基于类的,但它是基于原型的语言。这意味着每个对象都链接到另一个对象(也就是对象的原型),并继承原型对象的方法。你可以跟踪每个对象的原型链,直到到达没有原型的 null 对象。我们需要通过修改 Array 原型来向全局 Array 对象添加方法。 Array.prototype.average = function() { // 计算 sum 的值 var sum = this.reduce(function(prev, cur) { return prev + cur; }); // 将 sum 除以元素个数并返回 return sum / this.length; } var arr = [1, 2, 3, 4, 5]; var avg = arr.average(); console.log(avg); // => 3 Q24:什么是 JavaScript 中的提升操作? 难度:⭐⭐⭐⭐ 提升(hoisting)是 JavaScript 解释器将所有变量和函数声明移动到当前作用域顶部的操作。有两种类型的提升: 变量提升——非常少见 函数提升——更常见 无论 var(或函数声明)出现在作用域的什么地方,它都属于整个作用域,并且可以在该作用域内的任何地方访问它。 var a = 2; foo(); // 因为`foo()`声明被"提升",所以可调用 function foo() { a = 3; console.log( a ); // 3 var a; // 声明被"提升"到 foo() 的顶部 } console.log( a ); // 2 Q25:以下代码输出的结果是什么? 难度:⭐⭐⭐⭐ 0.1+0.2===0.3 这段代码的输出是 false,这是由浮点数内部表示导致的。0.1 + 0.2 并不刚好等于 0.3,实际结果是 0.30000000000000004。解决这个问题的一个办法是在对小数进行算术运算时对结果进行舍入。 Q26:请描述一下 Revealing Module Pattern 设计模式。 难度:⭐⭐⭐⭐⭐ 暴露模块模式(Revealing Module Pattern)是模块模式的一个变体,目的是维护封装性并暴露在对象中返回的某些变量和方法。如下所示: var Exposer = (function() { var privateVariable = 10; var privateMethod = function() { console.log('Inside a private method!'); privateVariable++; } var methodToExpose = function() { console.log('This is a method I want to expose!'); } var otherMethodIWantToExpose = function() { privateMethod(); } return { first: methodToExpose, second: otherMethodIWantToExpose }; })(); Exposer.first(); // 输出: This is a method I want to expose! Exposer.second(); // 输出: Inside a private method! Exposer.methodToExpose; // undefined 它的一个明显的缺点是无法引用私有方法。 推荐一个交流学习群,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多 点击:加入

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

史上牛JAVA思维导图学习笔记从初级到架构

我做过有道云笔记、印象笔记,也写过博客,几十篇文章之后,发现对笔记分类整理很麻烦,查询不容易,往往自己写的内容都不愿意看一眼,后来用了思维导图,很好用,结构清晰,一目了然,又灵机一动把导图做成chm格式,你知道的API文档都是chm,离线状态下就以查看了。在开发过程中,遇到某个点有些印象模糊了就打开看一下,跳槽求职前拿出来复习下,会很方便。而且更棒的是,手机下个chm阅读软件,手机上也能看了。好东西拿出来分享,如果大家觉得不错,就请我喝杯奶茶吧^_^ 另外还有一些Javaweb前端、后台,5个流行框架的思维导图,等整理好了也可以分享给大家! 欢迎工作一到五年的Java工程师朋友们加入Java架构开发:744677563 群内提供免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!

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

物联网哪里容易诞生BAT?万物病毒告诉你…

上篇文章,我提到了在物联网平台企业这个赛道,诞生新一轮BAT的概率很低。 那么本期,我来回答一个问题,在物联网触发的哪个市场最有可能诞生新一轮的BAT呢?我的价值观是光“大”不足以成为BAT,想要成为BAT必须身处战略要道。除了时下火热的人工智能、共享经济、XaaS,还有一个老生常谈、又常谈常新的领域,在不知不觉中已经发生了质变,它就是正在从“科技”走向“文化”的物联网安全。 一如19世纪末,被经典物理体系认为是“大厦已经建成,只剩修修补补”的两朵乌云,最终推开了量子力学的全新大门。如今,在大多数公司熟视无睹或束手无策、在巨头们视之为配角、处于价值洼地但内涵已经被重新定义的物联网安全领域,正在酝酿着新生的BAT。 永远不要觉得物联网安全事件离你很远,如果你自认为有足够的能力和免疫力应对各种安全漏洞,呵呵,“万物病毒”算是找对人了。

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

Java高级特性——注解,这也许是简单易懂的文章了

博主在初学注解的时候看到网上的介绍大部分都是直接介绍用法或者功能,没有实际的应用场景,篇幅又很长导致学习的时候难以理解其意图,而且学完就忘QAQ。本篇文章中我将结合实际的应用场景尽可能由浅入深,平缓的介绍java注解。 java注解是jdk1.5以后新出的特性,对于它的应用非常广泛,我们首先来看一下注解的应用,百度百科上这样说: 我们可以看到,注解的作用有三方面: 编写doc文档:这个就我们很常用的 @return 以及 @author,加了这些注解以后,就可以用jdk帮我们自动生成对应的API文档了 编译检查:这个也很常见 @Override,而且功能很强大,我将会在以后的文章中介绍 进行代码分析:这是本篇文章的重点。这个和编译检查一样也是一个强大的功能,但相比与编译检查由于其用到了反射,在性能上存在一些问题 后台开发中的SSH三大框架,以及咱们安卓端的retrofit,ButterKnife,Lombok等框架和插件也是大量的用到了注解。这里我将通过手撸一个假的ButterKnife来具体演示注解有什么用,怎么用。 我们首先来看段代码 public class MainActivity extends AppCompatActivity { @OnClick(R.id.test_btn) void test(){ test_tv.setText("恭喜您,绑定成功了!"); } @FindViewByID(R.id.test_tv) TextView test_tv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bindView(this); } } 这是一个最基本的activity,里面有2个控件,Button和TextView 在我点击Button后TextView的文字被改变。而我所做的仅仅是ButterKnife.bindView(this)并添加2个注解而已,这样就实现了控件的绑定,省去了很多与业务无关的代码,是不是简洁了很多。 看了注解的功能是不是很想了解它是怎么做到的,接下来我就来看看它是什么,怎么用,怎么利用 什么是注解 官方把它叫做元数据,即一种描述数据的数据。所以,可以说注解就是源代码的元数据。用它来可以来描述、标记我们的源代码。 怎样定义一个注解 以下是我上文中定义的一个 @OnClick注解 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface OnClick { int value() default 0; } 可见和定义一个类一样,只是将class改为 @interface,并且顶部通过几个原注解来说明这个注解的一些重要信息,具体如下: J2SE5.0版本在 java.lang.annotation提供了四种元注解,专门注解其他的注解: @Documented –注解是否将包含在JavaDoc中@Retention –什么时候使用该注解@Target? –注解用于什么地方@Inherited – 是否允许子类继承该注解 @Documented–一个简单的Annotations标记注解,表示是否将注解信息添加在java文档中,一般不用管。 @Retention– 定义该注解的生命周期,很重要,必须指定,以下是3种生命周期的介绍 RetentionPolicy.SOURCE – 在编译阶段丢弃。这些注解在编译结束之后就不再 有任何意义,所以它们不会写入字节码。@Override, @SuppressWarnings都属 于这类注解。 RetentionPolicy.CLASS – 在类加载的时候丢弃。在字节码文件的处理中有用。 注解默认使用这种方式。 RetentionPolicy.RUNTIME– 始终不会丢弃,运行期也保留该注解,因此可以使 用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。 @Target – 表示该注解用于什么地方。如果不明确指出,该注解可以放在任何地方。以下是一些可用的参数。需要说明的是:属性的注解是兼容的,如果你想给7个属性都添加注解,仅仅排除一个属性,那么你需要在定义target包含所有的属性。 ElementType.TYPE:用于描述类、接口或enum声明 ElementType.FIELD:用于描述实例变量 ElementType.METHOD ElementType.PARAMETER ElementType.CONSTRUCTOR ElementType.LOCAL_VARIABLE ElementType.ANNOTATION_TYPE 另一个注释 ElementType.PACKAGE 用于记录java文件的package信息 @Inherited – 定义该注释和子类的关系 那么注解体里的内容有该怎样定义 Annotations只支持基本类型、String及枚举类型。注释中所有的属性被定义成方法,并允许提供默认值。 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @interface Book{ public enum Priority {LOW, MEDIUM, HIGH} String author() default "Yash"; int price() default 20; Status status() default Status.NOT_STARTED; } 看看怎么用它 @Todo(priority = Todo.Priority.MEDIUM, author = "zsq", status = Todo.Status.STARTED) public void incompleteMethod1() { } 通过字段名 = 的形式给字段赋值,如果没赋值,则使用缺省值。如果注解中只有一个属性,可以直接命名为“value”,使用时无需再标明属性名,例如我定义的 @OnClick注解。 好了,花了那么多精力来认识他,该看看该怎么来利用它了 我们定义了自己的注解并将其应用在业务逻辑的方法上。现在我们需要写一个用户程序调用我们的注解。这里我们需要使用反射机制。如果你熟悉反射代码,就会知道反射可以提供类名、方法和实例变量对象。所有这些对象都有getAnnotation()这个方法用来返回注解信息。我们需要把这个对象转换为我们自定义的注释(使用 instanceOf()检查之后),同时也可以调用自定义注释里面的方法。 所有这些对象都有getAnnotation()! 所有这些对象都有getAnnotation()! 所有这些对象都有getAnnotation()! 重要的API说3遍,另外用到的几个方法也很重要,下面的代码会演示,更多的API使用参考可以去查阅JDK文档。 具体到我们本编文章的实例,调用注解的家伙就是我们刚刚在MainActivity里用到的 ButterKnife,我们通过设置监听的注解来看看它到底做了什么 public static final void bindView(final Activity activity){ traversalMethod(activity); traversalField(activity); } 在我们调用的ButterKnife.bindView(this)中我们拿到了MainActivity的实例,并且通过反射遍历里面所有的方法: private static void traversalMethod(final Activity activity) { Method[] methodArray = getObjectMethodArray(activity); for (final Method method:methodArray){ if(isAnnotationPresent(method)){ int viewID = getViewID(method); setOnClickListenerForControl(activity, method, viewID); } } } private static Method[] getObjectMethodArray(Activity activity) { return activity.getClass().getMethods(); } 接着判断方法是否被我们注解: private static boolean isAnnotationPresent(Method method) { return method.isAnnotationPresent(OnClick.class); } 如果是我们用注解标注的方法则通过注解获取注解里保存的空间ID,并且通过MainActivity的实例为其设置点击监听,在监听内调用被注解标注的方法。 private static int getViewID(Method method) { return method.getAnnotation(OnClick.class).value(); } private static void setOnClickListenerForControl(final Activity activity, final Method method, int viewID) { activity.findViewById(viewID).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { try { method.invoke(activity); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } }); } 大功告成!是不是很简单 我们用反射获取注解的方式实现了ButterKnife的功能,但文章开头说过反射的存在性能上的不足。而实际上ButterKnife本身用的也不是反射,而是用的apt工具在编译时期就可以获取到所有的方法、字段、以及他们的注解,从而避免了使用反射,解决了性能的问题。接下来的文章我会讲解本文开头提到的第三点,也就是ButterKnife实际使用的方法,将我们自己的ButterKnife改为ButterKnife官方的实现方法。 以下github地址是本篇文章讲解用到的demo:https://github.com/sally519/MyButterKnief 文章很长,希望看完的人能收获点东西 作者水平有限,如有遗漏和错误,欢迎指正!

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

开年CES大秀:人工智能火的风口-AI芯片

AI浪潮席卷而来,投资者追风竞逐,半导体行业格局大变。此前传统而稳定的产业格局,正逐渐被瓦解。博通1050亿美元欲恶意收购高通,英伟达瞄准自动驾驶AI芯片的远方,英特尔通过收购和投资全方位布局,华为推智能手机AI芯片…嵌入式开发 AI处理器.jpg 中国的AI芯片初创企业,在酝酿着新一轮的攻势,等待着暗夜中迎来的黎明。而这一切一切的野心,都在今年的科技开年大秀CES上展露无遗。 芯片产业是对技术的要求和附加值最高的一环,老牌芯片巨头凭借几千个技术专利堆起了丰厚利益的护城河。若能在AI时代掌握芯片的核心技术和话语权,其产业价值和战略地位远远大于应用层创新。 英特尔在自动驾驶的野心也从未掩饰。高通在汽车芯片、语音操控音箱芯片以及无线耳机原件等市场都拥有统治力。高通今年的CES展会上发布了一系列产品,如发布新芯片,可将蓝牙耳机续航延长3倍。 知名芯片厂商中,美国有13家公司,领军者既有谷歌、英特尔、IBM这样的科技巨头,也有高通、英伟达、AMD、赛灵思这样在各自领域中有绝对优势的大公司。但中国则主要以初创公司为主,没有巨头。但AI芯片方兴未艾,机遇正在逐渐显露,AI领域未来必然也会产生类似英特尔、AMD这样的世界级企业。而英伟达、英特尔、谷歌再强势,也无法一统江山,这是酝酿中国“芯”成为世界“芯“的绝佳机会。 回顾当年,GPU赢过CPU就是因为GPU比CPU专用,如今异构智能也是这样的思路。GPU,得益于深度学习的发展,目前锐气正盛,是当下看来发展人工智能最有竞争力的芯片。CPU,通用性强,用于串行计算,为PC时代的宠儿。但对于AI的算力而言,非常缓慢。 「边缘计算」、「嵌入式人工智能」、「终端智能」这些拗口的词汇正逐渐走热。 云端AI芯片市场终究是大公司的游戏,云端发展不只需要有高运算力的芯片,还得营造出生态系,基本上供应商就是这几家巨头,小公司不仅难以打入,可能连架设这么多的服务器都没有办法。这当中就蕴藏着智慧物联网这样一个巨大的机会,它远离数据中心,但拥有极其强大的能量。 从通用走向专用,打造AI专用芯片,这是AI芯片发展必经之路。而正是其专用性,决定了可以在各个不同的行业都能针对性发力。由于功耗问题,以前想都不敢想的终端智能,让万物智能的时代因为专属芯片的打造变得值得期待。但是真正实现“万物智能”,就需要将AI从云端拉向终端。当然,未来云计算以及边缘计算,也将成为优势互补的发展趋势。 2018,「终端」AI芯片或将呈燎原之势。

资源下载

更多资源
优质分享App

优质分享App

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

腾讯云软件源

腾讯云软件源

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

Rocky Linux

Rocky Linux

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

Sublime Text

Sublime Text

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