Dart 语言和java差异:修饰符、匿名函数、可选参数、级联、
可以不用指定类型
Dart 语言可以不用指定类型,当然也可以指定数据类型(可以提高运行速度)
var a = "canzhang";
String b = "canzhang";
print(a==b);//true
Dart 语言的程序入口
void main() {}
例如flutter的入口
void main() => runApp(new MyApp()); //dart 中单行函数的写法
Drat 语言中没有java的 private
public
protected
概念
但是如果变量或函数以下划线(_)开始,则该函数或变量属于这个dart文件私有(private)的方法。
新建两个dart文件(这里和java的类还是有区别的,这里的dart文件不用像java类一样和主类必须同名)
- 1、 引用来不在同一个dart文件下person类的表现
person.dart
class Person {
String _name;//私有
int age;
Person(String name, int age) {
this._name = name;
this.age = age;
}
String getName() {
return this._name;
}
int _getAge() {//私有
return this.age;
}
}
同级目录创建main_test.dart
import 'person.dart';
void main() {
Person person = new Person("canzhang",18);
// print(person._name); // 会爆红 The getter '_name' isn't defined for the class 'Person'
print(person.getName());//canzhang
print(person.age);// 18
// print(person._getAge());//会爆红 The method 'getAge' isn't defined for the class 'Person'
}
- 引用来在同一个dart文件下person类的表现
都可以正常引用
void main() {
Person person = new Person("canzhang",18);
print(person._name); // 正常使用 canzhang
print(person.getName());//canzhang
print(person.age);// 18
print(person._getAge());//正常使用 18
}
class Person {
String _name;//私有变量的写法
int age;
Person(String name, int age) {
this._name = name;
this.age = age;
}
String getName() {
return this._name;
}
int _getAge() {//私有方法的写法
return this.age;
}
}
变量和基本数据类型
- var 修复符号 变量
var a = "canzhang";
var b = 2;
var c = false;
这个也可以这么写(这种写法就指定了类型,可以提高编译运行速度)
String a = "canzhang";
int b = 2;
bool c = false;
- Dart 语言里面一切皆对象,包括int、bool等,如果初始化没有赋值,那么他的默认值就是null
void main() {
int a ;
bool b;
double c;
String d;
print(a==null);//true
print(b==null);//true
print(c==null);//true
print(d==null);//true
}
- final 和const
如果定义的变量不会变化,可以使用final或者const来声明。
final int a =1;
a= 0;//报错 'a', a final variable, can only be set once
const int b = 2;
b=0;//报错 Constant variables can't be assigned a value
final int c;//报错 The final variable 'c' must be initialized
const int d;//报错 The const variable 'd' must be initialized
都是只能被赋值一次,二次赋值会报错,初始化不赋值同样也会报错。
那么这两个值有什么区别呢?
它们的区别在于,const比final更加严格。final只是要求变量在初始化后值不变,但通过final,我们无法在编译时(运行之前)知道这个变量的值;而const所修饰的是编译时常量,我们在编译时就已经知道了它的值,显然,它的值也是不可改变的。
通过下面这个例子,可以对比一下。
import 'dart:math';
void main() {
final int a = getRandomValue(); //正常
// const int b = getRandomValue();// 报错 Const variables must be initialized with a constant value
print(a);
}
//取随机数
int getRandomValue() {
return new Random().nextInt(100);
}
在上面的例子中,a的值是不变的,但如果不把代码运行起来,你无法知道a是多少。而如果想把一个不确定的值赋值给const 则会直接报错。
- Dart 常用的基本数据类型:num、String、bool、List、Map。
重点说几个和java有差异的点
1、num
num类型包括如下两个子类类:
int 整形。 取值范围: -2^53 到 2^53。
double 浮点型。 64 位长度的浮点型数据 , 即双精度浮点型。
int a =1 ;
double b = 1.1;
num c = 1;
num d = 1.1;
2、String
基本和java一样,有一点不同可以直接兼容单引号和双引号
String a = "canzhang";
String b = 'canzhang';
print(a == b);//true
还有就是可以直接包裹多行内容
//多行的 可以添加换行符
String a = "canzhang\n666666";
//也可以通过""" xxx """ 来包裹多行内容,这里连续三个单引号也是可以的。
String b = """canzhang
哈哈哈哈哈
嘿嘿嘿""";
print(a);
print(b);
3、List
最不同的点就是看着写法有点像java中的数组和集合的结合
List list = [1, 2, 3];
list.add(666);
print(list.length);//4
print(list[list.length - 1]);//666
List list2 = new List();
list2.addAll(list);
print(list2[3]);//666
3、Map
Map map = {
"a": 1,
"b": 2,
"a": 3, //测试下会不会覆盖
3: "a"
};
print(map.length); //2
print(map["a"]); //3 同一个key 会覆盖
print(map["b"]); //2
print(map[3]); //a 没有添加泛型,可以塞不同类型数据
//也可以通过new map
Map map2 = new Map();
map2["a"] = 1;
map2["b"] = 2;
print(map2["a"]);//1
Dart 中的函数
- 首先来个普通函数,和java中并没有什么区别
void main() {
var num = add(1, 2);
print(num);//3
}
int add(int a, int b) {
return a + b;
}
- 可选·
位置
参数函数(中括号[])
这里需要注意位置可选参数必须前面位置已经有对应的值,即使存在默认值也是不行的,前面的参数必须按照顺序一一赋值,后面的参数才可以正常使用,否则会报错
void main() {
print(getMsg("canzhang", "男")); //canzhang 的性别是 男
print(getMsg("canzhang", "男", 18)); //canzhang 的性别是 男 年龄 18 附带信息 哈哈哈
print(getMsg("canzhang", "男", 18,"嘿嘿嘿")); //canzhang 的性别是 男 年龄 18 附带信息 嘿嘿嘿
//这里需要注意位置可选参数必须前面位置已经有对应的值,即时存在默认值也是不行的,前面的参数必须按照顺序一一赋值
print(getMsg("canzhang", "男", "呵呵呵")); // 报错 The argument type 'String' can't be assigned to the parameter type 'int'
}
String getMsg(String name, String sex, [int age =1 ,String extra =" 哈哈哈"]) {
String msg = "$name 的性别是 $sex";
if (age != null) {
msg = "$msg 年龄 $age";
}
if (extra != null) {
msg = "$msg 附带信息 $extra";
}
return msg;
}
- 可选
命名
参数函数(大括号{})
可选命名参数和可选位置不同,不关心顺序,名字对应上就可以了
void main() {
print(getMsg("canzhang", "男")); //canzhang 的性别是 男
print(getMsg("canzhang", "男", age:18)); //canzhang 的性别是 男 年龄 18 附带信息 哈哈哈
print(getMsg("canzhang", "男", age:18,extra:"嘿嘿嘿")); //canzhang 的性别是 男 年龄 18 附带信息 嘿嘿嘿
//可选命名参数和可选位置不同,不关心顺序,名字对应上就可以了
print(getMsg("canzhang", "男",extra: "呵呵呵")); // canzhang 的性别是 男 年龄 1 附带信息 呵呵呵
}
String getMsg(String name, String sex, {int age =1 ,String extra =" 哈哈哈"}) {
String msg = "$name 的性别是 $sex";
if (age != null) {
msg = "$msg 年龄 $age";
}
if (extra != null) {
msg = "$msg 附带信息 $extra";
}
return msg;
}
- 参数可设置默认值
void main() {
print(getMsg("canzhang", "男")); //canzhang 的性别是 男 年龄 100 (没有传入值的时候使用默认值)
print(getMsg("canzhang", "男", 18)); //canzhang 的性别是 男 年龄 18
}
String getMsg(String name, String sex, [int age = 100]) {
String msg = "$name 的性别是 $sex";
if (age != null) {
msg = "$msg 年龄 $age";
}
return msg;
}
- dart 中的函数都是有返回值的,如果没有明确写返回值则返回值为null
void main() {
print(doNothing()==null);//true
}
doNothing() {}
- 匿名函数
一切皆对象,函数当然也是个对象,也是可以当做入参的
下面演示几个把匿名参数当入参的例子。
main() {
var child = new Child("canzhang", "18");
printName(() => child); //canzhang 传递无参数的匿名函数当做入参
printStr((String s) => s); //xxxx 传递有参数的匿名函数当做入参
printStr((_) => null); //null 有参数的也可以不传递参数直接使用下划线
}
void printName(Child getChild()) {
print(getChild().name);
}
void printStr(String getString(String str)) {
print(getString("xxxx"));
}
一些不同的操作符
-
is 判断是否是某种类型
相当于java中的instanceof
import 'person.dart';
void main() {
var person = new Person("zhangcan", 18);
var b = 10;
print(getName(person)); //zhangcan
print(getName(b)); //不支持类型
}
String getName(var object) {
if (object is Person) {
return object.getName();
}
return "不支持类型";
}
-
as 转换成某种类型
相当于java中的强转
import 'person.dart';
void main() {
var person = new Person("zhangcan", 18);
var b = 10;
print(getName(person)); //zhangcan
print(getName(b)); //抛出类型转换异常
}
String getName(var object) {
return (object as Person).getName();
}
- ??= 仅在变量为null的时候赋值
void main() {
var a = 10;
var b;
a ??= 1;
b ??= 1;
print(a); // 10 (a不为null,所以不会重新赋值)
print(b); //1 (b为null,则会赋值)
}
-
expr1 ?? expr2
如果 exprl 为非空, 则返回其值;否则, 计算并返回 expr2 的值:
void main() {
var a =10;
var b;
var c = a??b;
var d = b??a;
print(c);//10
print(d);//10
}
- .. 级联
示例1:按照如下所示的代码 . 和 .. 是没有区别的
void main() {
var user = new User().setAge(18).setName("canzhang");
var user02 = new User()
..setAge(18)
..setName("canzhang");
print(user.toString()); //User{name: canzhang, age: 18}
print(user02.toString()); //User{name: canzhang, age: 18}
}
class User {
String name;
int age;
User setName(String name) {
this.name = name;
return this;//注意这里返回的是当前对象
}
User setAge(int age) {
this.age = age;
return this;//注意这里返回的是当前对象
}
@override
String toString() {
return 'User{name: $name, age: $age}';
}
}
示例2:把 return this 去掉之后,就出现差别了
void main() {
var user = new User().setAge(18).setName("canzhang");//报错 The expression here has a type of 'void', and therefore cannot be used
var user02 = new User()
..setAge(18)
..setName("canzhang");//正常运行
print(user.toString()); //User{name: canzhang, age: 18}
print(user02.toString()); //User{name: canzhang, age: 18}
}
class User {
String name;
int age;
void setName(String name) {
this.name = name;
}
void setAge(int age) {
this.age = age;
}
@override
String toString() {
return 'User{name: $name, age: $age}';
}
}
结论:从上面两个简单的示例我们就可以看出差别了,级联操作符不同于我们在java中的链式调用,dart的级联操作符更强大一些,不需要我们当前的方法返回当前的对象,就可以一直操作这个对象,变的更方便了,代码也更简洁了。
-
operator
重载操作符
operator 是 Dart 的一个关键字,它和运算符(如=)一起使用,表示一个 运算符重载函数,在理解时可将operator和运算符(如operator=)视为一个函数名。
对于 Dart 提供的所有操作符,通常只支持对于基本数据类型和标准库中提供的类的操作,而对于用户自己定义的类,如果想要通过该操作符实现一些基本操作(比如比较大小,判断是否相等),就需要用户自己来定义关于这个操作符的具体实现了。
比如要定义一个向量类,让这个类的对象可以直接相加或者相减(使用常规运算符),这个时候就可以重载这俩操作符,并在里面实现自己的逻辑。
//定义一个向量类
class Vector {
final int x;
final int y;
const Vector(this.x, this.y);
//重载加号 + (a + b).
Vector operator +(Vector v) {
return new Vector(x + v.x, y + v.y);
}
//重载减号 - (a - b).
Vector operator -(Vector v) {
return new Vector(x - v.x, y - v.y);
}
}
main() {
//实例化两个向量
final v = new Vector(2, 3);
final w = new Vector(2, 2);
final r1 = v + w;
print('r1.x='+r1.x.toString() + ' r1.y=' + r1.y.toString());
final r2 = v - w;
print('r2.x='+r2.x.toString() + ' r2.y=' + r2.y.toString());
流程操作语句
-
assert (断言)
Dart 语言通过使用 assert 语句来中断正常的执行流程,当 assert 判断的条件为 false 时发生中断。判断为 true则继续执行下面的语句;反之则会抛出一个断言错误异常 AssertionError。 代码如下所示:
void main() {
var a = 10;
print("assert ----->>前");//正常执行
assert(a >= 10);
print("assert ----->>后");//可以执行到
}
void main() {
var a = 10;
print("assert ----->>前");//正常执行
assert(a < 10);
print("assert ----->>后");//不能执行到,并且抛出异常:Failed assertion: line 4 pos 10: 'a < 10': is not true.
}
异常处理
- 首先先来个最基本的处理方法处理一下上面案例抛出的异常(这种写法和java相同)
void main() {
var a = 10;
print("assert ----->>前"); //正常执行
try {
assert(a < 10);
} catch (e) {
print("exception detail :\n$e"); //异常信息
} finally {
print("finally do somthing");
}
print("assert ----->>后"); //可以执行到
}
- 捕获指定异常写法
本案例抛出的是AssertionError ,我们指定该exception进行捕获,当然如果指定的异常不包含真正抛出的异常,是和java一样没有效果的,还是会抛出异常终止流程
void main() {
var a = 10;
print("assert ----->>前"); //正常执行
try {
assert(a < 10);
} on AssertionError catch (e) {
print("exception detail :\n$e"); //异常信息
} finally {
print("finally do somthing");
}
print("assert ----->>后"); //可以执行到
}
- 打印异常堆栈信息
void main() {
var a = 10;
try {
assert(a < 10);
} on AssertionError catch (e,s) {//使用这个catch方法 s就是异常堆栈信息
print("异常堆栈信息 :\n$s"); //异常堆栈信息
}
}
异常堆栈信息 :
#0 _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:40:39)
#1 _AssertionError._throwNew (dart:core-patch/errors_patch.dart:36:5)
#2 main (file:///C:/Users/canzhang/AndroidStudioProjects/DemoAll/my_flutter/test/my_dart_test.dart:5:12)
#3 _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:300:19)
#4 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:171:12)
- 捕获指定异常
如果一个代码块内容可能发生多种异常,每种异常我们的操作又不同,则可以指定捕获指定异常,如下所示
void main() {
var a = 10;
try {
int c = "canzhang" as int; //抛出CastError 异常
assert(a < 10); //抛出 AssertionError 异常
} on AssertionError catch (e) {
//只捕获AssertionError
print("AssertionError :\n$e");
} on CastError catch (e) {
//只捕获CastError
print("CastError:\n$e");
} catch (e) {
//捕获所有异常
print("all error:\n$e");
}
print("gogogoo");
}
- 需要注意dart中是不会提醒可能有异常抛出的,不会在方法上抛出异常。
void main() {
test();//这里不会提示需要捕获异常
}
void test(){//方法也没有提供抛出异常功能
throw new CastError();
}
- 可以抛任意对象,等同正常异常抛出(中断流程)
void test(){
throw 1;
throw false;
throw "hahahah";
}
- 要接收处理异常,同时允许它传播,请使用
rethrow
关键字。
void main() {
try {
test1();
} catch (e) {
print('main 异常信息:\n $e');
}
}
void test1() {
try {
test();
} catch (e) {
print('test1 异常信息:\n $e');
rethrow; // 继续抛出异常Allow callers to see the exception.
}
}
void test() {
throw "6666666";
}
日志
test1 异常信息:
6666666
main 异常信息:
6666666
面向对象
dart一些皆为对象,对象也都继承于object
- 基本类结构(和java相同)
class User {
String name;
int age;
User(String name, int age) {
this.name = name;
this.age = age;
}
}
上面代码除了没有修饰符,其他都和java一样,不过在dart中上面的代码可以简化成下面这个样子
class User {
String name;
int age;
User(this.name, this.age);
}
- 命名构造函数
当我们需要定义一个有特别含义的构造函数的时候,可以通过构造函数.XXX来命名构造函数
class User {
String name;
int age;
User.test(Map map) {//命名构造函数,从map中取出参数
this.name = map["name"];
this.age = map["age"];
}
@override
String toString() {
return 'User{name: $name, age: $age}';
}
}
使用
import '../test02/user.dart';
void main() {
Map map = new Map();
map["name"] = "canzhang";
map["age"] = 18;
var user = new User.test(map);
print(user.toString()); //User{name: canzhang, age: 18}
}
-
dart 不支持构造函数重载
像java中,我们经常写不同参数的构造函数,以便于使用,在是在dart中,是不支持构造函数重载的,它只允许有一个构造函数,不过不用担心,可以通过可选参数来实现重载类似的功能即可,反而更方便了。具体细节可参见上方可选参数。 -
继承中的构造函数
1、默认情况下,子类中的构造函数调用父类的未命名无参数构造函数,这种场景和java没有什么区别
void main() {
new Child();//i am from parent
}
class Child extends Parent {
}
class Parent {
Parent() {
print("i am from parent");
}
}
2、如果父类没有未命名的无参数构造函数,则须手动调用父类中构造函数。 在子类的构造函数体之后用冒号(:)指定父类构造函数
void main() {
print(new Child("canzhang", "18").toString());//Child{name: canzhang, age: 18}
}
class Parent {
String name;
String age;
Parent(String name,String age) {
this.name = name;
this.age = age;
print("i am from parent");
}
@override
String toString() {
return 'Parent{name: $name, age: $age}';
}
}
class Child extends Parent {
Child(String name, String age) : super(name, age);//指定父类构造函数,这里不指定会报错
@override
String toString() {
return 'Child{name: $name, age: $age}';
}
}
子类也可以这么写,不是必须和调用的父类构造函数入参一样
class Child extends Parent {
Child() : super(null, null);
@override
String toString() {
return 'Child{name: $name, age: $age}';
}
}
构造函数是不能继承的,所以子类是不能继承父类的命名构造函数的。如果你希望使用父类中的构造函数创建子类的实例,你必须在子类中实现父类中的构造函数。
- 构造函数初始化列表
构造函数初始化列表能干什么:可以通过初始化列表在子类的构造函数运行前来初始化实例的成员变量值(划重点),
继续上方的例子改造
class Parent {
String name;
String age;
Parent(String name, String age)
: name = name,//这个构造函数后面冒号的形式,就是初始化列表
age = age {
print("i am from parent");
}
@override
String toString() {
return 'Parent{name: $name, age: $age}';
}
}
void main() {
print(new Child("canzhang", "18").toString());//Child{name: canzhang, age: 18}
}
从结果来看就和上面案例的结果是一样的,好像就是一个赋值并无不同。
到底哪里不同呢,上面这种场景确实没有什么不同,但是初始化列表这个动作是在子类的构造函数运行之前执行的。
比如如果我想在构造函数里面初始化final变量则会报错
class Parent {
final String name;
final String age;
Parent(String name, String age) {
this.name = name;//'name'can't be used as a setter because it is final
this.age = age;//'age'can't be used as a setter because it is final
print("i am from parent");
}
}
而如果换成构造函数初始化列表形式则可以正常使用。
class Parent {
final String name;
final String age;
Parent(String name, String age)
: name = name,//正常
age = age {//正常
print("i am from parent");
}
}
异步支持
Dart是单线程模型,也就没有了所谓的主线程/子线程之分。
那异步是怎么弄的?
遇到有延迟的运算(比如IO操作、延时执行)时,线程中按顺序执行的运算就会阻塞,用户就会感觉到卡顿,于是通常用异步处理来解决这个问题。当遇到有需要延迟的运算(async)时,将其放入到延迟运算的队列(await)中去,把不需要延迟运算的部分先执行掉,最后再来处理延迟运算的部分。(这就好比排了个顺序,把有延迟的任务扔到最后面去执行)
具体原理参见:https://www.cnblogs.com/zhujiabin/p/10313065.html
- async和await
发起一个最简单的异步网络请求
import 'package:dio/dio.dart';
void getHttp() async {
try {
print("请求前");
Response response = await Dio().get("https://raw.githubusercontent.com/gudujiucheng/DemoAll/dev/storage/test.json");
print(response);
} catch (e) {
print(e);
}
}
main() {
getHttp();
print("似曾相识燕归来");
}
可以看到如下输出日志(await 之后延迟的部分会滞后执行)
请求前
似曾相识燕归来
{
"content":"年年岁岁花相似,岁岁年年人不同![the msg is from gitHup]"
}
简单改造一下,测试下如果任务不耗时会不会依然被排到后面
void getHttp() async {
try {
print("请求前");
String response = await getStr();
print(response);
} catch (e) {
print(e);
}
}
main() {
getHttp();
print("似曾相识燕归来");
print("似曾相识燕归来");
print("似曾相识燕归来");
print("似曾相识燕归来");
print("似曾相识燕归来");
}
Future<String> getStr() {//就是简单的返回一个字符串,并无太久耗时
return new Future<String>(() {
return "666666";
});
}
请求前
似曾相识燕归来
似曾相识燕归来
似曾相识燕归来
似曾相识燕归来
似曾相识燕归来
666666
结论:从日志可以看出依然被排到了后面。
- Future
持续更新中...