flutter学习4 - dart语言学习

一、Dart中的变量

1.1、变量

var :解释器自动推断变量的类型。注意:再次赋值的类型不一样会报错
dynamic:变量类型可动态改变
final:最终变量,不可变的变量。在声明时,需要对变量进行赋值
const:常量,不可变的变量。在声明时,需要对变量进行赋值

注意:无论什么类型的变量,如果不对其进行赋值,那么它的默认值都是 null。

// 以下代码会报错:
var name = "变量"; 
 name = 1;

// 以下代码不会报错
dynamic age = 26; 
age = "26";

进行推断

1.2 变量的数据类型

Dart 中内置了 7 类特殊的数据类型:
numbers:数值类型
strings:字符串类型
booleans:布尔类型
lists:列表类型
maps:图类型
runes:字符类型
symbols:符号类型

1.2.1 数值类型

在 Dart 中,数值类型有两种,分别为 int 和 double。

如果使用 0x 开头定义数值,就表示使用十六进制数值。

如果一个数值包含小数,就需要将其定义为 double 类型。

e 符号表示科学计数法。

整型数值是可以直接赋值给浮点型变量的。

int a = 99; 
int b = 0xA1; 
print(a);//99 
print(b);//161

double c = 3.14; 
double d = 1.4e2; 
print(c);//3.14 
print(d);//140.0

double e = 1; 
print(e);//1.0

数值类型常用属性

var count1 = 1; 
var count2 = 1.1; 

//runtimeType 属性获取运行时类型
print(count1.runtimeType);//int 
print(count2.runtimeType);//double

//获取当前数值是否为有限值,返回 true 或者 false 
print(count1.isFinite); 

//获取当前数值是否为无限值,返回 true 或者 false 
print(count1.isInfinite); 

//获取当前数值是否为 NaN,NaN 描述非数值
print(count1.isNaN); 

//获取当前数值是否为负数 
print(count1.isNegative); 

//获取当前数值的符号。若返回 1,则表示为正数;若返回-1,则表示为负数;若返回 0,则表示当前数值为 0 
print(count1.sign); 

//获取存储当前数值需要的最少位数,int 类型独有的属性
print(count1.bitLength); 

//获取当前数值是否为偶数,int 类型独有的属性
print(count1.isEven); 

//获取当前数值是否为奇数,int 类型独有的属性
print(count1.isOdd);

数值类型常用方法

var count1 = -1; 
var count2 = 1.1; 

//返回当前数值的绝对值
print(count1.abs()); 

//返回不小于当前数值的最小整数
print(count2.ceil());//2 

//返回不小于当前数值的最小整数,返回数值为浮点类型
print(count2.ceilToDouble());//2.0 

//返回指定范围内离当前数值最近的数值,如果当前数值在范围内,就直接返回
print(count1.clamp(1,10)); 

//将当前数值与传入的参数进行比较:如果大于传入的参数,就返回 1;如果小于传入的参数,就返回-1;如果等于传入的参数,就返回 0 
print(count1.compareTo(0)); 

//返回不大于当前数值的最大整数
print(count2.floor());//1 

//返回不大于当前数值的最大整数,返回数值为浮点类型
print(count2.floorToDouble());//1.0 

//获取当前数值除以参数后的余数
print(count1.remainder(5)); 

//获取离当前数值最近的整数,四舍五入
print(count2.round()); 

//获取离当前数值最近的整数,四舍五入,返回浮点数
print(count2.roundToDouble()); 

//将当前数值转换成浮点数返回
print(count1.toDouble()); 

//将当前数值转换成整型数返回
print(count2.toInt()); 

//将当前数值转换成字符串返回
print(count1.toString()); 

//将当前数值的小数部分丢弃后返回整数值
print(count2.truncate()); 

//将当前数值的小数部分丢弃后返回整数值,浮点类型
print(count2.truncateToDouble());

int类型独有的方法

var num1 = 35; 
//获取当前数与传入参数的最大公约数
print(num1.gcd(7));//7 
//求模逆运算
print(num1.modInverse(6)); 
num1 = 2; 
//对当前数值进行幂运算,之后进行取模运算 
print(num1.modPow(3,5));//2 的 3 次方除以 5 取余数

1.2.2 字符串类型

可以使用单引号或者双引号来创建字符串

$来进行字符串的格式化

字符串也支持直接使用“+”运算符来进行拼接

即使不使用运算符,相邻的字符串也会自行进行拼接

使用 3 对单引号或者 3 对双引号可以进行多行字符串的创建

反斜杠进行字符转译

var name = "变量"; 
var str3 = "Hello ${name} $name"; 
var num1 = 3; 
var num2 = 4; 
var str4 = "3+4=${num1+num2}"; 
print(str3);//Hello 变量
print(str4);//3+4=7

// “+”运算符来进行拼接
var str1 = "Hello"; 
var str2 = 'World'; 
print(str1+str2);//HelloWorld

// 相邻的字符串自行进行拼接
print('hello''world');//helloworld

// 3 对单引号或者 3 对双引号创建多行字符串
var str5 = '''第一行
第二行
第三行'''; 
var str6 = """第一行
第二行
第三行""";

print('hello \'变量\'');//hello '变量'

常用属性和方法

//获取字符串的字符码集合
print(str8.codeUnits);//[97, 98, 99] 
//获取当前字符串是否为空字符串,返回布尔值
print("".isEmpty);//true 
//获取当前字符串是否为非空,返回布尔值
print("".isNotEmpty);//false 
//获取当前字符串长度
print(str8.length);//3 
//获取类型
print(str8.runtimeType);//String

//通过下标获取某个字符串中某个字符的 code 码,下标从 0 开始
print("hello".codeUnitAt(0));//104 
//进行字符串比较,逐个字符进行 code 码的比较
print("hello".compareTo('a'));//1 
//获取当前字符串是否包含参数字符串
print("hello".contains('l'));//true 
//判断当前字符串是否以某个字符串结尾
print("hello".endsWith("llo"));//true 
//判断当前字符串是否以某个字符串开头
print("hello".startsWith('h'));//true 
//获取要进行匹配的字符串在当前字符串中的位置,如果没找到,就返回-1 
print("hello".indexOf("l"));//2 
//获取要进行匹配的字符串在当前字符串中的位置,逆向查找,如果没找到,就返回-1 
print("hello".lastIndexOf("l"));//3 
//在左边进行字符串位数补齐
print("hello".padLeft(10,"*"));//*****hello 
//在右边进行字符串位数补齐
print("hello".padRight(10,"&"));//hello&&&&& 
//进行字符串替换,将匹配到的字符串替换成指定的字符串
print("hello".replaceAll("o","p"));//hellp 
//将指定范围内的字符串进行替换,左闭右开区间
print("hello".replaceRange(0,3,"000"));//000lo 
//使用指定字符串作为标记对原字符串进行分割,结果会放进列表返回
print("hello".split('e'));//[h, llo] 
//进行字符串截取,左闭右开区间
print("hello".substring(1,3));//el 
//将字符串全部转为小写
print("Hello".toLowerCase());//hello 
//将字符串全部转为大写
print("hello".toUpperCase());//HELLO 
//将字符串首尾的空格去掉
print(" hello ".trim()); 
//将字符串首部的空格去掉
print(" hello".trimLeft()); 
//将字符串尾部的空格去掉
print("hello ".trimRight());
// 对字符串进行拷贝,使用*运算符
print("hello"*2);//hellohello
// 使用中括号来获取集合内的某个元素
print("hello"[0]);//h

1.2.3 布尔类型

一种简单的数据类型,其只有两个字面量值:true 和 false。
使用 runtimeType 来获取类型
调用 toString 方法来将布尔值转换成字符串

bool a = true; 
bool b = false;
print(a.runtimeType);//bool

bool a = true; 
print(a.toString());//true

1.2.4 列表类型

列表用来存放一组数据,也被称为数组。在 Dart 中,列表具体的数据类型由其中的元素类型决定。
想在列表中存放不同类型的数据,则可以将列表声明成动态类型的。
列表类型可以通过构造方法来创建

下标超出了列表元素的个数,就会产生溢出异常。

List<int> list = [1,2,3,4];
List<dynamic> list = [1,2,3,4,"5"];

//创建长度为 5 的列表,默认使用 null 填充
var list2 = new List(5);//new 可以省略 [null, null, null, null, null] 
//创建指定长度的列表,并使用指定的值作为默认值
var list3 = List.filled(3,1);//[1,1,1] 
//通过另一个集合类型的数据来创建列表
var list4 = List.from(list3);//[1,1,1]

//获取列表的第一个元素
print([1,2].first);//1 
//获取列表中的最后一个元素
print([1,2].last);//2 
//获取列表的长度
print([1,2].length);//2

print(["a","b","c","d"][3]);//d 
print([1,2]+[2,3]);//[1, 2, 2, 3] 
var data = [1,2,3]; 
data[2] = 4; 
print(data);//[1, 2, 4]

List 类中封装了大量的实例方法,这些方法可以极大地提高开发者的工作效率

var l = []; 
 //向列表中增加元素
 l.add(1); 
 //向列表中增加一组元素
 l.addAll([2,3]); 
 //将列表对象映射成字典对象,下标为键,元素为值
 print(l.asMap());//{0: 1, 1: 2, 2: 3} 
 //将列表中某个范围的元素进行覆盖
 l.fillRange(0,2,"a");//[a, a, 3] 
 //获取列表某个范围内的元素集合
 print(l.getRange(0,3)); 
 //获取列表中某个元素的下标,从前向后找,如果没有,就返回-1 
 print(l.indexOf('a')); 
 //获取列表中某个元素的下标,从后向前找,如果没有,就返回-1 
 print(l.lastIndexOf("a"));
//向列表中的指定位置插入一个元素
 l.insert(0,'s');//[s, a, a, 3] 
 //向列表的指定位置插入一组元素
 l.insertAll(0,["a","b","c"]);//[a, b, c, s, a, a, 3] 
 //删除列表中的指定元素,从前向后找到第一个删除
 l.remove("a"); 
 //删除列表中指定位置的一个元素
 l.removeAt(0); 
 //删除列表中的最后一个元素
 l.removeLast(); 
 //删除列表中指定范围内的元素
 l.removeRange(0,2); 
 //将列表中指定范围的元素进行替换,替换为集合参数中的元素
 l.replaceRange(0,2,[1,2,3,4]); 
 //截取列表中范围内的元素返回新的列表
 print(l.sublist(0,3)); 
 //判断列表中是否包含指定元素
 print(l.contains(2)); 
 //使用指定拼接符将列表拼接为字符串
 print(l.join("-"));//1-2-3-4 
 //将列表转换为字符串
 print(l.toString());//[1, 2, 3, 4] 
 print(l); 
 //删除列表中所有的元素
 l.clear();

1.2.5 字典类型

字典是一组键值对的集合,通过键可以完成对值的修改、
查找、添加或删除。在 Dart 中,字典类型叫作 Map

//键为字符串类型、值为整数类型的字典
Map<String,int> map2 = {"1":1,"2":2};

//使用构造方法创建字典
var map3 = Map(); 
//新增键值对
map3["name"] = "珲少"; 
print(map3["name"]);//珲少
//修改键值
map3["name"] = "Lucy"; 
print(map3["name"]);//Lucy 
//不存在的键值将返回 null 
print(map3["age"]);//null 
//将某个键的值置为 null,并不会将此键值对删除
map3["name"] = null; 
print(map3["name"]);//null

Map 对象中常用的属性列

//判断 Map 是否为空
print({"1":1,"2":2}.isEmpty);//false 
//判断 Map 是否为非空
print({"1":1,"2":2}.isNotEmpty);//true 
//获取 Map 所有的键
print({"name":"Lucy","age":25}.keys);//(name, age) 
//获取 Map 所有的值
print({"name":"Lucy","age":25}.values);//(Lucy, 25) 
//获取 Map 中键值对的个数
print({"name":"Lucy","age":25}.length);//2 
//获取类型
print({"name":"Lucy"}.runtimeType);//_InternalLinkedHashMap<String, String>

Map 类中的实例方法

var map = {}; 
//向 Map 中追加键值对
map.addAll({"name":"Lucy","age":28});//{name: Lucy, age: 28} 
//判断 Map 中是否包含某个键
print(map.containsKey("name"));//true 
//判断 Map 中是否包含某个值
print(map.containsValue("Lucy"));//true 
//删除某个键值对
map.remove("name"); 
//将 Map 转换成字符串
print(map.toString()); 
//清空 Map 中的键值对
map.clear();

二、Dart中的运算符

2.1 算数运算符

算数运算符通常用来进行简单的数据运算,例如加、减、乘、除等。

print(1+2);//3 
print("1"+"2");//12
print(3-1);//2
print(-(-1));//1
print(2*4);//8
print(10/2);//5.0
// 整除
print(9~/2);//4
// 取模运算符
print(9%2);//1

// 自加和自减
var a = 3; 
a++; 
print(a);//4 
++a; 
print(a);//5

2.2 比较运算符

比较运算符的作用是进行两个值的比较。虽然在 Dart 中,比较运算符的操作数可以是任意类型的值,但是对于 List、Map 或 String 对象,一般会使用函数来进行比较,比较运算符更多用于数值之间的比较。可用的比较运算符如下表所示。


image.png

2.3 类型运算符

Dart 中的类型运算符有 3 种:as、is 和 is!。

as 运算符用来进行类型的“转换”,需要注意,这里的类型转换并不是真正意义上的转换,其并不会真正修改数据的类型,而是告诉 Dart将当前数据当成某个类型的数据来进行处理。在面向对象开发中,这个运算符非常有用,后面我们会介绍。

is 运算符用来判断数据是否属于某个类型:如果属于,就返回布尔值 true;如果不属于,就返回布尔值 false。

is!运算符的作用则与 is 刚好相反,它用来判断数据是否不属于某个类型,示
例如下:

var a = 1; 
var b = "2"; 
print(a is int);//true 
print(b is! String);//false

2.4 复合运算符

简单理解,复合运算符是多种简单运算符的复合。复合运算符通常也叫作赋值复合运算符,因为其总是一种运算符与赋值运算符的组合。


image.png

2.5 逻辑运算符

逻辑运算符是针对布尔值进行运算的运算符。我们知道,布尔值只有两种:true 和 false。逻辑运算符所适用的操作数也只有 true 或者 false。

“!”被称为逻辑非运算符,进行逻辑非运算,它是一个前置运算符,且只有一个操作数,当操作数为布尔值 true 时,运算结果为布尔值 false,当操作数为布尔值 false 时,运算结果为布尔值true。

“||”被称为逻辑或运算符,进行逻辑或运算。逻辑或运算遵守下面的运算规则:
(1)当两个操作数中至少有一个操作数为 true 时,运算结果为 true。
(2)当两个操作数都为 false 时,运算结果才为 false。

“&&”被称为逻辑与运算符,进行逻辑与运算。逻辑与运算遵守下面的运算规则:
(1)当两个操作数中至少有一个操作数为 false 时,运算结果为 false。
(2)当两个操作数都为 true 时,运算结果为 true。

print(!false);//true 
print(!true);//false

print(false||false);//false 
print(false||true);//true 
print(true||false);//true 
print(true||true);//true

print(false&&false);//false 
print(true&&false);//false 
print(false&&true);//false 
print(true&&true);//true

2.6 位运算符

位运算符是对二进制位进行操作的运算符。在计算机中,所有的数据存储实际上采用的都是二进制。

“&”用来进行按位与运算。所谓按位与运算,是指将两个运算符的每一个二进制位分别进行与运算,即若两个对应二进制位都为 1,则运算结果为 1,否则为 0。

“|”用来进行按位或运算。与按位与运算一样,按位或运算将两个运算数的每个二进制位分别进行或运算,若两个对应二进制位有一个为 1,则运算结果为 1,否则运算结果为 0。

“~”用来进行按位非运算。按位非运算是一个前置的单元运算符,其只有一个操作数,对操作数的每一个二进制位进行取反,即为 0 的位运算后结果为 1,为 1 的位运算后结果为 0。

“^”用来进行按位异或运算。关于按位异或运算,只需要牢记进行运算的两个数位相同时,运算结果为 0,否则运算结果为 1 即可,即两个二进制位都为 0 或者都为 1 时,运算结果为 0,否则运算结果为 1。

“<<”用来进行按位左移运算,即将每一个二进制位向左移动指定位数。对于二进制数据,
一个很重要的特点是每左移一位,会使原数值进行乘 2 操作

“>>”用来进行按位右移操作

var a = 10; //二进制 1010 
var b = 3; //二进制 0010 
print(a&b);//2 即二进制 0010

var c = 10; //二进制 1010 
var d = 4; //二进制 0100
print(c|d);//14 即二进制 1110

var e = 4; //00000100 
print(~e); //11111011 以补码表示 原码为 00000101 且为负数 即-5

var f = 3; // 0011 
var g = 5; // 0101 
print(f^g);// 0110 十进制 6

var h = 3;//0011 
print(h<<1);//0110 十进制 6

var i = 4;//0100 
print(i>>1);// 0010 十进制 2

2.7 条件运算符

条件运算符与流程控制语句中的条件语句作用很像.

“?:”是一个三元的运算符,其有 3 个操作数,第一个操作数可以是一个布尔值或者运算结果为布尔值的表达式,
当这个操作数为 true 时,条件运算的结果为第二个操作数的值,当第一个操作数为 false 时,条件
运算的结果为第三个操作数的值。

“??”是 Dart 中的空条件运算符,其有两个操作数,若第一个操作数为 null,则运算后的值为第二个操作数的值,若第一个操作数为非 null 值,则运算后的值为第一个操作数的值。这个运算符最大的作用是保证运算的结果不为 null 值,通常用来进行安全保证。

var a = 3; 
 var b = 5; 
 var res = a>b ? "a>b" : "a<=b"; 
 print(res);//a<=b

var c = null; 
print(c==null?"无作为":"额外操作 a:$c");

var c = 3; 
print(c??"无作为");

var c = null; 
c ??= 0;//与 c = c??0;意义完全一样

2.8 级联运算符

级联运算符使用“..”表示,级联运算符是 Dart 中比较高级的一种运算符,它可以让开发者对某个对象连续地进行一系列操作。这样的好处是可以减少中间变量的生成,并且让开发者更畅快地体验 Dart 编码的乐趣。

var p =People()..name="珲少"..age=26; 
print("name:${p.name},age:${p.age}");

2.9 点运算符

点运算符用来对对象的属性和方法进行操作。

class People { 
 String name; 
 int age; 
 void printSelf(){ 
 print("name:${name},age:${age}"); 
 } 
} 

main() { 
 var p = People(); 
 p.name = "珲少"; 
 p.age = 26; 
 p.printSelf();//name:珲少,age:26 
}

补充,条件成员访问运算符“?.”。这个运算符的作用是,如果所调用的对象是非 null 值,就会正常进行访问,否则返
回 null,但是不会产生错误

var c = null; 
print(c?.a);//null

3 Dart 中的流程控制语句

条件语句:if-else
循环语句:while,do-while,for,for-in
中断语句:break,continue
多分支条件语句:if-else if-else,switch-case
异常处理:throw,try-catch-finally

补充:

while 语句会首先进行循环条件的判定,如果不满足,
就不再执行循环体,满足条件才会进行循环;

do-while 语句则是首先执行一次循环体中的代码,之后进行循环条件的判定,如果满足,就继续执行循环体,如果不满足,就跳出循环

break 语句会直接跳出本层循环,执行循环后面的代码,而 continue 语句则是跳过本次循环后,还会进行循环条件的判定

3 Dart 高级进阶

3.1构造方法

一旦重写了构造方法,默认的无参构造方法将不再可用。

构造方法的实质是将对象属性的赋值过程由外界封装到类的内部。

构造方法的编写还有一个小技巧,一般情况下,我们可以直接将构造方法定义成如下模样,Dart 会自动进行参数和属性的匹配,进行赋值,非常方便.

main() { 
    var circle = new Circle(6,1,1);//使用参数构造圆形对象
    print(circle.radius);//6.0 
} 
class Circle { 
    //半径
    double radius = 0;
    //圆心 X 
    double centerX = 0; 
    //圆心 Y 
    double centerY = 0; 
    //构造方法
    Circle(this.radius,this.centerX,this.centerY); 
}

一个类需要有多个构造方法,比如自定义的圆形类,很多时候需要快速创建出单位圆(圆心为坐标原点、半径为 1 的圆)。这时,就可以定义一个便捷的构造方法帮助我们直接生成单位圆,这类构造方法也被称为命名构造方法.

main() { 
    var circle2 = Circle.standard(); 
    print(circle2.radius);//1 
} 
class Circle { 
    //半径
    double radius = 0; 
    //圆心 X 
    double centerX = 0; 
    //圆心 Y 
    double centerY = 0; 
    //构造方法
    Circle(this.radius,this.centerX,this.centerY); 
    //命名构造方法,单位圆
    Circle.standard(){ 
        this.radius = 1; 
        this.centerX = 0; 
        this.centerY = 0; 
    } 
}

命名构造方法通常用来快速地创建标准对象,同样,命名构造方法也可以有参数,并且只要参数名与类中定义的属性名一致,也可以使用 Dart 自动匹配赋值的特性。

3.2实例方法

类封装了属性和方法,属性用来存储描述类的数据,方法用来描述类的行为。

方法的用法和函数一样,只是在调用时需要用对象来调用,并且方法中会自动将当前对象绑定到 this 关键字上。也就是说,在方法中可以通过 this 关键字获取对象的属性信息,也可以调用其他方法。方法也需要通过点语法来进行调用。

Setters 方法与 Getters 方法
Setters 方法用来设置对象属性,Getters 方法用来获取对象属性。其实当我们使用点语法访问对象属性信息时,调用的就是 Setters方法或 Getters 方法,在定义属性时,Dart 会自动生成默认的 Setters 方法和 Getters 方法。Setters方法和 Getters 方法的另一大作用是定义附加属性,附加属性也可以理解为计算属性,即这些数据通常不是描述对象的最原始数据,而是通过计算得来的,例如下面的description就是附加属性:

main() { 
    var teacher = Teacher("珲少",1101,"Dart"); 
    teacher.sayHi("小明"); //Hello 小明,我是珲少老师!编号为1101 
    teacher.teaching(); //珲少老师正在进行 Dart 教学
    print(teacher.description); //珲少:Dart 
    teacher.description = "Lucy:JavaScript"; 
    teacher.teaching(); //Lucy 老师正在进行 JavaScript 教学
} 
class Teacher { 
    String name; 
    int number; 
    String subject; 
    Teacher(this.name,this.number,this.subject); 
    void sayHi(String toName){ 
       print("Hello ${toName},我是${this.name}老师!编号为${this.number}"); 
    } 
    void teaching(){ 
       print("${this.name}老师正在进行${this.subject}教学。"); 
    } 
    String get description{ 
       return "${this.name}:${this.subject}"; 
    }
    set description(String value){ 
       this.name = (value.split(":") as List)[0]; 
       this.subject = (value.split(":") as List)[1]; 
    } 
}

上面的代码中,description 就是附加属性,其并没有真正占用内存空间进行存储,而是通过其他属性计算而来的

3.3 抽象类与抽象方法

抽象类,是面向对象开发中较为难理解的一点。在 Dart 中,抽象类中可以定义抽象方法。
抽象方法,是指只有定义却没有实现的方法,抽象是面向接口开发的基础。
抽象类实际上就是一个接口,接口中定义了未实现的方法告诉调用者:如果有类实现了这个接口,这个类就拥有接口所描述的功能。例如,我们可以为教师类定义一个接口,示例如下:

abstract class TeacherInterface { 
    void teaching(); 
}

上面的 TeacherInterface 接口中只定义了一个抽象方法,Teacher 类可以对这个接口进行实现,示例代码如下:

abstract class TeacherInterface { 
    void teaching(); 
} 
class Teacher implements TeacherInterface { 
    String name; 
    int number; 
    String subject; 
    Teacher(this.name,this.number,this.subject); 
    void sayHi(String toName){ 
        print("Hello ${toName},我是${this.name}老师!编号为${this.number}"); 
    } 
    void teaching(){ 
        print("${this.name}老师正在进行${this.subject}教学。"); 
    } 
}

一个类也可以同时实现多个接口,例如再定义一个人类接口,示例如下:

abstract class TeacherInterface {
    void teaching(); 
} 
abstract class PeopleInterface { 
    void sayHi(String name); 
} 
class Teacher implements TeacherInterface,PeopleInterface { 
    String name; 
    int number; 
    String subject; 
    Teacher(this.name,this.number,this.subject); 
    void sayHi(String toName){ 
        print("Hello ${toName},我是${this.name}老师!编号为${this.number}"); 
    } 
    void teaching(){ 
        print("${this.name}老师正在进行${this.subject}教学。"); 
    } 
}

抽象类不可以被实例化,即不能直接使用抽象类来构造实例对象,只能通过实现这个抽象类接口的类或者继承它的子类来实例化对象。

遗留问题:类实现了抽象类,则类中必须重写抽象类中的方法吗?

3.4 类的继承

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容