30分钟掌握Dart语言

  • 在Dart中,一切都是对象,一切对象都是class的实例,哪怕是数字类型、方法甚至null都是对象,所有的对象都是继承自Object
  • 虽然Dart是强类型语言,但变量类型是可选的因为Dart可以自动推断变量类型
  • Dart支持范型,List<int>表示一个整型的数据列表,List<dynamic>则是一个对象的列表,其中可以装任意对象
  • Dart支持顶层方法(如main方法),也支持类方法或对象方法,同时你也可以在方法内部创建方法
  • Dart支持顶层变量,也支持类变量或对象变量
  • 跟Java不同的是,Dart没有public protected private等关键字,如果某个变量以下划线(_)开头,代表这个变量在库中是私有的,具体可以看这里
  • Dart中变量可以以字母或下划线开头,后面跟着任意组合的字符或数字

变量

变量定义

以下代码是Dart中定义变量的方法:

你可以明确指定某个变量的类型,如int bool String,也可以用var或 dynamic来声明一个变量,Dart会自动推断其数据类型。

变量的默认值

注意:没有赋初值的变量都会有默认值null

final和const

如果你绝不想改变一个变量,使用final或const,不要使用var或其他类型,一个被final修饰的变量只能被赋值一次,一个被const修饰的变量是一个编译时常量(const常量毫无疑问也是final常量)。可以这么理解:final修饰的变量是不可改变的,而const修饰的表示一个常量。

注意:实例变量可以是final的但不能是const的

下面用代码说明:

final和const的区别:

区别一:final 要求变量只能初始化一次,并不要求赋的值一定是编译时常量,可以是常量也可以不是。而 const 要求在声明时初始化,并且赋值必需为编译时常量。

区别二:final 是惰性初始化,即在运行时第一次使用前才初始化。而 const 是在编译时就确定值了。

内建数据类型

Dart有如下几种内建的数据类型:

  • numbers
  • strings
  • booleans
  • lists(或者是arrays)
  • maps
  • runes(UTF-32字符集的字符)
  • symbols
    下面用一段代码来演示以上各类数据类型:

函数

函数的返回值

Dart是一个面向对象的编程语言,所以即使是函数也是一个对象,也有一种类型Function,这就意味着函数可以赋值给某个变量或者作为参数传给另外的函数。虽然Dart推荐你给函数加上返回值,但是不加返回值的函数同样可以正常工作,另外你还可以用=>代替return语句,比如下面的代码:

命名参数、位置参数、参数默认值

命名参数

可以看到,定义命名参数时,你可以以 {type paramName} 或者 {paramName: type} 两种方式声明参数,而调用命名参数时,需要以 funcName(paramName: paramValue) 的形式调用。

命名参数的参数并不是必须的,所以上面的代码中,如果调用sayHello()不带任何参数,也是可以的,只不过最后打印出来的结果是:hello, my name is null,在Flutter开发中,你可以使用@required注解来标识一个命名参数,这代表该参数是必须的,你不传则会报错,比如下面的代码:

位置参数

使用中括号[]括起来的参数是函数的位置参数,代表该参数可传可不传,位置参数只能放在函数的参数列表的最后面,如下代码所示:

参数默认值

你可以为命名参数或者位置参数设置默认值,如下代码所示:

main()函数

不论在Dart还是Flutter中,必须都需要一个顶层的main()函数,它是整个应用的入口函数,main()函数的返回值是void,还有一个可选的参数,参数类型是List<String>。

函数作为一类对象

你可以将一个函数作为参数传给另一个函数,比如下面的代码:

你也可以将一个函数赋值给某个变量,比如下面的代码:

匿名函数

大多数函数都是有名称的,比如main() printName()等,但是你也可以写匿名函数,如果你对Java比较熟悉,那下面的Dart代码你肯定也不会陌生:

匿名函数类似于Java中的接口,往往在某个函数的参数为函数时使用到。

函数返回值

所有的函数都有返回值,如果没有指定return语句,那么该函数的返回值为null。

运算符

Dart中的运算符与Java中的类似,比如++a a == b b ? a : b,但是也有一些与Java不太一样的运算符,下面用代码说明:

..运算符(级联操作)

如果你对Java中的建造者模式比较熟悉的话,Dart中的..运算符也很好理解,先看下面的代码:

可以看到,使用..调用某个对象的方法(或者成员变量)时,返回值是这个对象本身,所以你可以接着使用..调用这个对象的其他方法,这不就类似于Java中的建造者模式,每次build某个属性时,都返回一个this对象吗。

控制流程

if / else switch for /while try / catch语句跟Java中都类似,try / catch语句可能稍有不同,下面用一段代码说明:

类(Class)

类的定义与构造方法

Dart中的类没有访问控制,所以你不需要用private, protected, public等修饰成员变量或成员函数,一个简单的类如下代码所示:

上面的Person类中有3个成员变量,一个构造方法和一个成员方法,看起来比较奇怪的是Person的构造方法,里面传入的3个参数都是this.xxx,而且没有大括号{}包裹的方法体,这种语法是Dart比较独特而简洁的构造方法声明方式,它等同于下面的代码:

要调用Person类的成员变量或成员方法,可以用下面的代码:

类除了有跟类名相同的构造方法外,还可以添加命名的构造方法,如下代码所示:

Dart中使用extends关键字做类的继承,如果一个类只有命名的构造方法,在继承时需要注意,如下代码:

由于Human类没有默认构造方法,只有一个命名构造方法fromJson,所以在Man类继承Human类时,需要调用父类的fromJson方法做初始化,而且必须使用Man.fromJson(Map data) : super.fromJson(data)这种写法,而不是像Java那样将super写到花括号中。

有时候你仅仅只是在某个类的构造方法中,调用这个类的另一个构造方法,你可以这么写:

类的成员方法

一个类的成员方法是一个函数,为这个类提供某些行为。上面的代码中已经有了一些类的成员方法的定义,这些定义方式跟Java很类似,你可以为某个类的成员变量提供getter/setter方法,如下代码:

抽象类和抽象方法

使用abstract修饰一个类,则这个类是抽象类,抽象类中可以有抽象方法和非抽象方法,抽象方法没有方法体,需要子类去实现,如下代码:

运算符重载

Dart中有类似于C++中的运算符重载语法,比如下面的代码定义了一个向量类,重载了向量的+ -运算:

枚举类

使用enum关键字定义一个枚举类,这个语法跟Java类似,如下代码:

mixins

mixins是一个重复使用类中代码的方式,比如下面的代码:

静态成员变量和静态成员方法

泛型(Generics)

Java和C++语言都有泛型,Dart语言也不例外,使用泛型有很多好处,比如:

正确指定泛型类型会产生更好的生成代码。
泛型可以减小代码的复杂度

Dart内置的数据类型List就是一个泛型数据类型,你可以往List中塞任何你想的数据类型比如整型、字符串、布尔值等
关于Dart更多的泛型知识点,可以查看这里。

Dart库(Libraries)

Dart目前已经有很多的库提供给开发者,许多功能不需要开发者自己去实现,只需要导入对应的包即可,使用import语句来导入某个包,比如下面的代码:

如果你想导入自己写的某个代码文件,使用相对路径即可,例如当前有一个demo.dart文件,跟该文件同级目录下有个util.dart文件,文件代码如下:

在demo.dart文件中如果要引用util.dart文件,使用下面的方式导入:

你可以使用as关键字为导入的某个包设置一个前缀,或者说别名,比如下面的代码:

你也可以在导入包时使用show hide关键字来导入某个包中的部分功能,比如下面的代码:

导入包时使用deferred as可以让这个包懒加载,懒加载的包只会在该包被使用时得到加载,而不是一开始就加载,比如下面的代码:

异步

Dart提供了类似ES7中的async await等异步操作,这种异步操作在Flutter开发中会经常遇到,比如网络或其他IO操作,文件选择等都需要用到异步的知识。
async和await往往是成对出现的,如果一个方法中有耗时的操作,你需要将这个方法设置成async,并给其中的耗时操作加上await关键字,如果这个方法有返回值,你需要将返回值塞到Future中并返回,如下代码所示:

下面的代码使用Dart从网络获取数据并打印出来:

参考链接


30分钟掌握Dart语言

RSA加密算法原理

学过算法的朋友都知道,计算机中的算法其实就是数学运算。所以,再讲解RSA加密算法之前,有必要了解一下一些必备的数学知识。我们就从数学知识开始讲解。

必备数学知识

RSA加密算法中,只用到素数、互质数、指数运算、模运算等几个简单的数学知识。所以,我们也需要了解这几个概念即可。

素数

素数又称质数,指在一个大于1的自然数中,除了1和此整数自身外,不能被其他自然数整除的数。这个概念,我们在上初中,甚至小学的时候都学过了,这里就不再过多解释了。

互质数

百度百科上的解释是:公因数只有1的两个数,叫做互质数。;维基百科上的解释是:互质,又称互素。若N个整数的最大公因子是1,则称这N个整数互质。

  常见的互质数判断方法主要有以下几种:

两个不同的质数一定是互质数。例如,2与7、13与19。

一个质数,另一个不为它的倍数,这两个数为互质数。例如,3与10、5与 26。

相邻的两个自然数是互质数。如 15与 16。

相邻的两个奇数是互质数。如 49与 51。

较大数是质数的两个数是互质数。如97与88。

小数是质数,大数不是小数的倍数的两个数是互质数。例如 7和 16。

2和任何奇数是互质数。例如2和87。

1不是质数也不是合数,它和任何一个自然数在一起都是互质数。如1和9908。

辗转相除法。

指数运算

指数运算又称乘方计算,计算结果称为幂。nm指将n自乘m次。把nm看作乘方的结果,叫做”n的m次幂”或”n的m次方”。其中,n称为“底数”,m称为“指数”。

模运算

模运算即求余运算。“模”是“Mod”的音译。和模运算紧密相关的一个概念是“同余”。数学上,当两个整数除以同一个整数,若得相同余数,则二整数同余

两个整数a,b,若它们除以正整数m所得的余数相等,则称a,b对于模m同余,记作: a ≡ b (mod m);读作:a同余于bm,或者,ab关于模m同余。例如:26 ≡ 14 (mod 12)。

RSA加密算法简史

  RSA是1977年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的。当时他们三人都在麻省理工学院工作。RSA就是他们三人姓氏开头字母拼在一起组成的。

公钥与密钥的产生

假设Alice想要通过一个不可靠的媒体接收Bob的一条私人讯息。她可以用以下的方式来产生一个公钥和一个私钥

随意选择两个大的质数pqp不等于q,计算N=pq

根据欧拉函数,求得

选择一个小于 r 的整数 e,求得 e 关于模 r 的模反元素,命名为d。(模反元素存在,当且仅当e与r互质)

 p  q 的记录销毁。

(N,e)是公钥,(N,d)是私钥。Alice将她的公钥(N,e)传给Bob,而将她的私钥(N,d)藏起来。

加密消息

假设Bob想给Alice送一个消息m,他知道Alice产生的Ne。他使用起先与Alice约好的格式将m转换为一个小于N的整数n,比如他可以将每一个字转换为这个字的Unicode码,然后将这些数字连在一起组成一个数字。假如他的信息非常长的话,他可以将这个信息分为几段,然后将每一段转换为n。用下面这个公式他可以将n加密为c

计算c并不复杂。Bob算出c后就可以将它传递给Alice。

解密消息

Alice得到Bob的消息c后就可以利用她的密钥d来解码。她可以用以下这个公式来将c转换为n

得到n后,她可以将原来的信息m重新复原。

解码的原理是:

以及

费马小定理可证明(因为pq是质数)

这说明(因为pq不同的质数,所以pq互质)

签名消息

RSA也可以用来为一个消息署名。假如甲想给乙传递一个署名的消息的话,那么她可以为她的消息计算一个散列值(Message digest),然后用她的密钥(private key)加密这个散列值并将这个“署名”加在消息的后面。这个消息只有用她的公钥才能被解密。乙获得这个消息后可以用甲的公钥解密这个散列值,然后将这个数据与他自己为这个消息计算的散列值相比较。假如两者相符的话,那么他就可以知道发信人持有甲的密钥,以及这个消息在传播路径上没有被篡改过。

编程实践

  下面,开始我们的重点环节:编程实践。在开始编程前,我们通过计算,来确定公钥和密钥。

计算公钥和密钥

假设p = 3、q = 11(p,q都是素数即可。),则N = pq = 33;

根据模反元素的计算公式,我们可以得出,e·d ≡ 1 (mod 20),即e·d = 20n+1 (n为正整数);我们假设n=1,则e·d = 21。e、d为正整数,并且e与r互质,则e = 3,d = 7。(两个数交换一下也可以。)

  到这里,公钥和密钥已经确定。公钥为(N, e) = (33, 3),密钥为(N, d) = (33, 7)。

编程实现

  下面我们使用Java来实现一下加密和解密的过程。具体代码如下:

RSA算法实现:

RSA算法结果:
加密前:24
加密后:30
解密后:24

(看程序最清楚了,对于要加密的数字m, m^e%N=c, c就是加密之后的密文。c^d%N=m, 就能解密得到m)

RSA加密算法的安全性

  当p和q是一个大素数的时候,从它们的积pq去分解因子p和q,这是一个公认的数学难题。然而,虽然RSA的安全性依赖于大数的因子分解,但并没有从理论上证明破译RSA的难度与大数分解难度等价。

1994年彼得·秀尔(Peter Shor)证明一台量子计算机可以在多项式时间内进行因数分解。假如量子计算机有朝一日可以成为一种可行的技术的话,那么秀尔的算法可以淘汰RSA和相关的衍生算法。(即依赖于分解大整数困难性的加密算法)

另外,假如N的长度小于或等于256位,那么用一台个人电脑在几个小时内就可以分解它的因子了。1999年,数百台电脑合作分解了一个512位长的N。1997年后开发的系统,用户应使用1024位密钥,证书认证机构应用2048位或以上。

RSA加密算法的缺点

  虽然RSA加密算法作为目前最优秀的公钥方案之一,在发表三十多年的时间里,经历了各种攻击的考验,逐渐为人们接受。但是,也不是说RSA没有任何缺点。由于没有从理论上证明破译RSA的难度与大数分解难度的等价性。所以,RSA的重大缺陷是无法从理论上把握它的保密性能如何。在实践上,RSA也有一些缺点:

产生密钥很麻烦,受到素数产生技术的限制,因而难以做到一次一密;

分组长度太大,为保证安全性,n 至少也要 600 bits 以上,使运算代价很高,尤其是速度较慢。

参考链接


使用AsyncTask防止Memory Leaks(内存泄漏)

1. 合理的使用context–比如我们常使用的Toast,Service,最好使用getApplicationContext(),因为这些在activity结束的时候可能仍在运行
下图展示了一些场景我们该用哪种context(图是盗的,附原文地址https://possiblemobile.com/2013/06/context/ 是国外的大牛写的关于context的使用)

继续阅读使用AsyncTask防止Memory Leaks(内存泄漏)

Android获取存储路径

Android Q(Android 10)之前,需要添加权限,如下:

Android Q(Android 10)开始 在App专属目录下本App可以随意操作,无需申请权限,不过App专属目录会在App卸载时跟随删除。看下面几个目录(通过Applicationcontext就可以访问)。

  • getFilesDir() :/data/user/0/本应用包名/files
  • getCacheDir():/data/user/0/本应用包名/cache
  • getExternalFilesDir(null):/storage/emulated/0/Android/data/本应用包名/files
  • getExternalCacheDir():/storage/emulated/0/Android/data/本应用包名/cache

getFilesDirgetCacheDir是在手机自带的一块存储区域(internal storage),通常比较小,SD卡取出也不会影响到,Appsqlite数据库和SharedPreferences都存储在这里。所以这里应该存放特别私密重要的东西。

getExternalFilesDirgetExternalCacheDir是在SD卡下(external storage),在sdcard/Android/data/包名/filessdcard/Android/data/包名/cache下,会跟随App卸载被删除。

filescache下的区别是,在手机设置-找到本应用-在存储中,点击清除缓存,cache下的文件会被删除,files下的文件不会。

谷歌推荐使用getExternalFilesDir。我们项目的下载是个本地功能,下载完成后是存本地数据库的,不是放网络上的,所以下载的音视频都放到了这下面,项目卸载时跟随App都删除了。getExternalFilesDir方法需要传入一个参数,传入null时得到就是sdcard/Android/data/包名/files,传入其他字符串比如"Picture"得到sdcard/Android/data/包名/files/Picture

参考代码如下:

参考链接

IntelliJ IDEA 提示 'try' can use automatic resource management Java7新特性

IntelliJ IDEA会提示

Java 7 build 105版本开始,Java 7的编译器和运行环境支持新的try-with-resources语句,称为ARM 块(Automatic Resource Management) ,自动资源管理。

新的语句支持包括流以及任何可关闭的资源。

使用try-with-resources语句来简化代码如下: 

在这个例子中,数据流会在try执行完毕后自动被关闭,前提是,这些可关闭的资源必须实现java.lang.AutoCloseable接口。

注:目前java.lang.AutoCloseable接口的子接口或实现类如下:

所有已经子接口: 

 所有已知实现类: 

对于Android用户来说,只有编译工程的 minSdkVersion大于 19(Android 4.4)的时候才能生效。

参考链接


使用HP Service Pack for ProLiant Gen8.1升级HP ProLiant MicroServer Gen8固件

家里使用了HP Gen8作为NAS服务器,对于iLO/BIOS来说,可以直接通过控制台升级。但是网卡/磁盘阵列的固件升级需要使用HP Service Pack for ProLiant(HP SSP)来升级。具体升级过程如下:

继续阅读使用HP Service Pack for ProLiant Gen8.1升级HP ProLiant MicroServer Gen8固件