Dart 语言学习笔记,最新Android面试合集

本文详细介绍了Dart编程语言中的类继承机制、基于mixin的继承、抽象类、构造器的使用、final和factory关键字、操作符重载以及枚举类型和库的导入导出规则。作者强调了多继承的复杂性以及mixin作为替代继承的灵活解决方案。
摘要由CSDN通过智能技术生成

try {
breedMoreLlamas();
} on OutOfLlamasException {
// 捕获 OutOfLlamasException 类型的异常,不需要使用异常对象
buyMoreLlamas();
} on Exception catch (e) {
// 捕获 Exception 异常,打印异常对象
print(‘Unknown exception: $e’);
} catch (e) {
// 捕获并处理任何类型的 thrown 对象
print(‘Something really unknown: $e’);
}

catch() 可以有两个参数,第一个是抛出的异常对象,第二个是堆栈跟踪对象。如果捕获异常并处理后还允许该异常继续传播,可以使用 rethrow 关键字:

final foo = ‘’;

void misbehave() {
try {
foo = “You can’t change a final variable’s value.”;
} catch (e) {
print(‘misbehave() partially handled ${e.runtimeType}.’);
rethrow; // Allow callers to see the exception.
}
}

void main() {
try {
misbehave();
} catch (e) {
print(‘main() finished handling ${e.runtimeType}.’);
}
}

finally 子句会在 catch 子句执行完执行。如果没有 catch 子句,那么会在finally 子句运行完才开始传播异常。

类与继承

Dart 的继承是基于 mixin 的继承,也就是说,虽然每个类(除了 Object 类)有且只有一个超类,但类体仍然可以在多个类层级上重用。
构造器的名字可以是 ClassName(无名构造器),也可以是 ClassName.identifier(命名构造器),实例化时的 new 关键字可以省略。
所有的实例变量都会自动生成隐式的 getter 方法,非 final 的实例变量也会自动生成隐式的 setter 方法,使用 getset 关键词可以创建额外的属性和其 getter/setter 方法的实现。
Dart 有个语法糖可以方便地将构造器参数赋值给实例变量(在构造器体执行之前):

class Point {
num x, y;
Point(this.x, this.y);
}

如果不声明构造器,会隐式包含一个无名无参构造器,并调用父类的无名无参构造器。
子类不能继承父类的构造器,所以,如果如果子类想利用父类的命名构造器创建实例,就必须实现父类的命名构造器。如果父类没有无名无参构造器,那么你就必须手动调用父类的一个构造器:

class Person {
String firstName;
Person.fromJson(Map data) {
print(‘in Person’);
}
}
class Employee extends Person {
Employee.fromJson(Map data) : super.fromJson(data) {
print(‘in Employee’);
}
}

初始化的顺序为: 初始化列表 → 父类无参构造器 → 主类无参构造器
可以在构造器函数体执行前初始化实例变量,初始化列表使用逗号隔开,初始化列表特别适合初始化 final 字段:

class Point {
final num x;
final num y;
final num distanceFromOrigin;

Point(x, y)
x = x,
y = y,
distanceFromOrigin = sqrt(x * x + y * y);
}

重定向到同类其他构造器:

class Point {
num x, y;

// The main constructor for this class.
Point(this.x, this.y);

// Delegates to the main constructor.
Point.alongXAxis(num x) : this(x, 0);
}

如果类的对象永远不会改变,就可以让这个对象成为编译时常量,只需要定义 const 构造器并确保所有的实例变量是 final 的:

class ImmutablePoint {
static final ImmutablePoint origin =
const ImmutablePoint(0, 0);

final num x, y;

const ImmutablePoint(this.x, this.y);
}

可以使用 factory 关键词声明工厂构造器,工厂构造器不一定要创建一个新的实例,可以直接返回缓存的实例,也可以返回子类型的实例,如:

class Logger {
final String name;
bool mute = false;

// _cache is library-private, thanks to
// the _ in front of its name.
static final Map<String, Logger> _cache =
<String, Logger>{};

factory Logger(String name) {
if (_cache.containsKey(name)) {
return _cache[name];
} else {
final logger = new Logger._internal(name);
_cache[name] = logger;
return logger;
}
}

Logger._internal(this.name);

void log(String msg) {
if (!mute) print(msg);
}
}

可以使用 abstract 修饰符声明一个不能直接实例化的抽象类,抽象类可以包含抽象方法,抽象方法没有方法体,以分号结尾。
每个类都隐式地定义了一个接口,该接口包含该类的所有实例成员及其实现的所有接口。可以通过 implements 子句实现一或多个接口,以逗号隔开。
使用 extends 创建子类,使用 super 引用父类,使用 @override 注解有意地重载成员。

在面向对象的语言中,多继承是个非常糟糕的设计,因为多继承会产生一系列问题,如:
多继承会使类之间的关系更加复杂。如果一个类有多个超类,超类又可能有多个超类,那么类之间的关系将会变得尤为复杂,你很难理清一个类的超类到底有哪些,继承关系又是怎样的。
多继承可能会出现菱形继承的情况,这种情况下会出现二义性,虽然 C++ 使用虚继承的方式可以暂时避免这种二义性,但会使程序可读性和可调试性变差。
所以现代的面向对象语言都不会允许多继承这种情况存在,其实继承的目的无非是为了重用并拓展类的功能,而通过继承来重用类体并不是一个好的方式,首先继承会“白箱复用”,会将超类细节暴露给子类,不管子类愿不愿意。如果超类更改,其所有子类也不得不改变。而且继承在运行时的行为不易改变。
所以尽可能少用继承,多用组合。继承是 is-a 的关系,而组合是 has-a 的关系。

Mixin 的概念源于 LISP 社区,在那里是指被设计用来与各个超类一起工作的类。对 Mixin 介绍比较详细的是 Mixins in Strongtalk 这篇论文。
一个类会隐式地定义一个 mixin, mixin 被类体定义,与类和它的超类构成函数关系,类实际上就是 mixin 的应用,是将其隐式定义的 mixin 应用于其超类的结果。Mixin应用函数应用 类似,从数学上来说,一个 mixin 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 可以看成是超类到子类的函数,也就是说,将 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 作用于超类 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传,结果是新的 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 子类,在研究资料里通常写作 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传。因此对于 mixin 的组合来说: 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传。 一个类隐式定义的 mixin 通常只会在给定超类定义处应用一次,所以为了让 mixin 能够应用不同的超类,我们需要独立于任何特定超类来声明 mixin,或者将类隐式定义的 mixin 剥离出来并在原始声明之外重用它。

Mixin 可以通过普通类定义的方式隐式定义,但要想提取 mixin 就必须要保证类不能声明构造器,这也避免了由于在继承链上传递构造器参数引起的各种并发症。

abstract class Collection {
Collection newInstance();
Collection map((f) {
var result = newInstance();
forEach((E e) { result.add(f(e)); });
return result;
});
void forEach(void f(E element)) {
// …
}
void add(E element) {
// …
}
}

abstract class DOMElementList = DOMList with Collection;

abstract class DOMElementSet = DOMSet with Collection;

在这里,Collection 就是一个隐式声明了 mixin 的普通类,它没有构造器。而 DOMElementListDOMElementSet 类就是 mixin 的应用,它们被特殊的类声明格式定义,也就是通过 with 子句声明 mixin 到 父类的应用。这两个类也是抽象的,因为他们没有实现 Collection 类的抽象方法 newInstance()
也就是说,DOMElementList 就是 Collection mixin ▷ DOMList,而 DOMElementSet 就是 Collection mixin ▷ DOMSet
这样的好处是,Collection 类的代码可以在多个类层次上共享。我们这里列举了两个类层次,一个是以 DOMList 为根的,一个是以 DOMSet 为根的。我们不需要复制粘贴Collection 类的代码,而 Collection 的任何更改都会传播到这两个类层次中,这样也极大地解放了代码的维护。
上面的例子只是简单说明了一种形式的 mixin 应用,应用的结果是一个有名字的类。而另一种形式,with 子句后跟上以逗号分隔的标识符列表,所有的标识符必须表示一个类,这种形式下,多个 mixin 会被组合并应用到 extends 子句的超类中,从而产生一个匿名超类,即:

class DOMElementList extends DOMList with Collection {
DOMElementList newInstance() => new DOMElementList();
}

class DOMElementSet extends DOMSet with Collection {
DOMElementSet newInstance() => new DOMElementSet();
}

这里的 DOMElementList 不再是 Collection mixin ▷ DOMList 的应用,而是一个新类,它的超类是这个应用。
如果 DOMList 有非平凡构造器,那么构造将会是这样:

class DOMElementList extends DOMList with Collection {
DOMElementList newInstance() => new DOMElementList(0);
DOMElementList(size): super(size);
}

每个 mixin 都有属于自己的被独立调用的构造器,超类也是如此。由于 mixin 的构造器不能被声明,对它的调用可以用 ; 语法省略,在底层实现时,这个调用会被放在初始化列表的开始处。
为了让多个 mixin 应用到一个类是而又不引入多个中间声明,可以使用这种方便的语法糖:

class Person {
String name;
Person(this.name);
}

class Maestro extends Person with Musical, Aggressive, Demented {
Maestro(name):super(name);
}

在这里,Maestro 的超类就是这样的 mixin 应用:
Demented mixin ▷ Aggressive mixin ▷ Musical mixin ▷ Person
mixin 与普通继承一样,隐私性取决于其声明的位置。mixin 的应用的静态元素同样不能被继承使用,Dart 中静态元素是不能被继承的。
那 mixin 应用后的实例类型到底是什么类型呢?毫无疑问,它必须是命名 mixin 类的子类型,也必须是原来类的子类型。原来的类有它自己的超类,为了确保指定的 mixin 应用 兼容原来的类,Dart 会给 with 子句的类额外的要求。
如果类 A 定义时的 with 子句是 mixin M,而 M 又是继承的 类 K,那么类 A 就必须支持类 K 的接口。

class S {
twice(int x) => 2 * x;
}

abstract class I {
twice(x);
}

abstract class J {
thrice(x);
}
class K extends S implements I, J {
int thrice(x) => 3* x;
}

class B {
twice(x) => x + x;
}
class A = B with K;

A 必须支持 K 的超类 S 的隐式接口,以确保 A 确实是 M 的子类型。K 必须实现 twice() 以满足 I 的需要,同时必须实现 thrice() 以满足 J 的需要,显然 K 已经满足了这些需要。
现在我们声明了 A,我们从 K 的 mixin 中 拿到了 thrice() 的实现,但这个 mixin 没有提供 twice() 的实现,幸运的是,B 有这个实现,所以总的来说,A 确实满足了 I,J 和 S 的需要。
相反,对于这样的类 D:

class D {
double(x) => x+x;
}

class E = D with K;

会收到一个警告,因为 E 没有 twice() 方法,因此不满足 I 和 S,也就不能使用预期的 K 了。
如果一个类有泛型参数,那么它的 mixin 也必须有相同类型的参数。
在 1.13 版本之后,Mixin 可以继承普通类而不要求一定是 Object,Mixin 可以使用 super.method()

操作符重载

可以通过 operator 关键字重载包括 +>== 在内的操作符,但要注意,重载 == 操作符的同时也要重载 hashCodegetter 方法:

class Person {
final String firstName, lastName;

Person(this.firstName, this.lastName);

// Override hashCode using strategy from Effective Java,
// Chapter 11.
@override
int get hashCode {
int result = 17;
result = 37 * result + firstName.hashCode;
result = 37 * result + lastName.hashCode;
return result;
}

// You should generally implement operator == if you
// override hashCode.
@override
bool operator ==(dynamic other) {
if (other is! Person) return false;
Person person = other;
return (person.firstName == firstName &&
person.lastName == lastName);
}
}

枚举类型

使用 enum 关键词定义枚举类型,枚举类型的值有一个 index getter,枚举值的列表可以通过它的 values 常量获取,枚举支持 switch 语句,你不能继承,mix in 或者实现枚举类,不能显式实例化枚举:

enum Color { red, green, blue }
assert(Color.red.index == 0);
List colors = Color.values;

library 和 可见性

每个 Dart app 都是一个 library,即使它没使用 library 指令,library 不仅提供 API,还是个隐私单元,以下划线(_)开头的标识符只在当前 library 内可见。
使用 import 指令可以指定在另一个 library 范围内如何使用另一个 library 的命名空间。对于内置的 library,URI 是特定的 dart: scheme,而其他的 URI 可以是文件系统路径或者 package: scheme:

import ‘dart:html’;
import ‘package:test/test.dart’;

如果两个 library 的标识符冲突,可以使用前缀加以区分:

import ‘package:lib1/lib1.dart’;
import ‘package:lib2/lib2.dart’ as lib2;

// Uses Element from lib1.
Element element1 = new Element();

// Uses Element from lib2.
lib2.Element element2 = new lib2.Element();

如果只想引入 library 的部分功能,需要使用 showhide 指定:

// Import only foo.
import ‘package:lib1/lib1.dart’ show foo;

// Import all names EXCEPT foo.
import ‘package:lib2/lib2.dart’ hide foo;

如果想要懒加载某个 library,先在 import 时通过 deferred as 指定 library 的标识符,然后在需要使用它的时候调用标识符的 loadLibrary():

import ‘package:greetings/hello.dart’ deferred as hello;

Future greet() async {
await hello.loadLibrary();
hello.printGreeting();
}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

《960全网最全Android开发笔记》

《379页Android开发面试宝典》

《507页Android开发相关源码解析》

657056430)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
[外链图片转存中…(img-yl1J407J-1710657056431)]

《960全网最全Android开发笔记》

[外链图片转存中…(img-b2DBlDh5-1710657056431)]

《379页Android开发面试宝典》

[外链图片转存中…(img-KlVHat95-1710657056431)]

《507页Android开发相关源码解析》

[外链图片转存中…(img-ESOZQSPP-1710657056432)]

因为文件太多,全部展示会影响篇幅,暂时就先列举这些部分截图,大家可以**点击这里**自行领取。

  • 16
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值