Dart中的类和单例模式

Posted 闪亮的大早

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Dart中的类和单例模式相关的知识,希望对你有一定的参考价值。

文章目录

Dart中的类和单例模式

先了解Dart中的类:

Dart也是一门面向对象的开发语言,面向对象中非常重要的概念就是类,通过类的初始化创建一个对象

Dart 是支持基于 mixin 继承机制的面向对象语言,所有对象都是一个类的实例,而除了 Null 以外的所有的类都继承自 Object 类。 基于 mixin 的继承 意味着尽管每个类(top class Object? 除外)都只有一个超类,一个类的代码可以在其它多个类继承中重复使用。 扩展方法 是一种在不更改类或创建子类的情况下向类添加功能的方式。

类的定义

  • Dart中,定义类用class关键字
  • 类通常有两部分组成:成员(member)和方法(method)。
  • 当未指明其父类的时候, 默认是继承自Object的, 定义类的伪代码如下:
class 类名 
  类型 成员名;
  返回值类型 方法名(参数列表) 
    方法体
  

  • Dart语言中, 在类中使用属性(成员/实例变量)时, 有必要时是通过this获取的
  • 但是下面在getsize方法中并没有加this
  • 这里需要注意的是: Dart的开发风格中,在方法中通常使用属性时,会省略this,但是有命名冲突时,this不能省略
// 创建类
class Point 
  // 定义变量
  int x;

  void getsize() 
    print('x = $x');
  


// 类的初始化
main(List<String> args) 
    // 从Dart2开始,new关键字可以省略
    var point = new Point();
    point.x = 1;
    point.getsize();

使用类的成员

对象的 成员 由函数和数据(即 方法实例变量)组成。方法的 调用 要通过对象来完成,这种方式可以访问对象的函数和数据。

使用(.)来访问对象的实例变量或方法:

var p = Point(2, 2);

// Get the value of y.
assert(p.y == 2);

// Invoke distanceTo() on p.
double distance = p.distanceTo(Point(4, 4));

使用 ?. 代替 . 可以避免因为左边表达式为 null 而导致的问题:

// If p is non-null, set a variable equal to its y value.
var a = p?.y;

构造函数

  • 当通过类创建一个对象时,会调用这个类的构造方法
    • Dart语言中,如果类中没有明确指定构造方法时,将默认拥有一个无参的构造方法()
    • 上面得到的point对象调用的就是默认的无参构造方法
  • 也可以根据自己的需求自定义构造方法
    • 当我们创建了自己的构造方法时,默认的无参的构造方法将会失效,不能使用,否则会报错
    • 因为Dart本身不支持函数的重载, 所以如果我们明确的写一个默认的构造方法,就会和我们自定义的构造方法冲突
class Student 
  String name;
  int age;

  Student(String name, int age) 
    this.name = name;
    this.age = age;
  

  • 在上面构造方法中主要实现的就是通过构造函数的参数给类的户型赋值
  • 为了简化这一过程, Dart提供了一种更加简洁的语法糖形式
class Student1 
  String name;
  int age;

  // 这里和上面的Studeng的构造方法等价
  Student1(this.name, this.age);

命名构造方法

  • 在实际开发中, 很明显一个构造方法的确是不够我们使用的
  • 而且Dart又不支持函数的重载, 不能创建爱你相同名称不同参数的构造方法
  • 这就衍生出了另外一中构造方法:命名构造方法
class Model 
  String name;
  int age;

  Model(this.name, this.age);

  // 命名构造方法
  Model.withNameAndAge(String name, int age) 
    this.name = name;
    this.age = age;
  
  // 命名构造方法
  Model.initJson(Map<String, Object> map) 
    this.name = map['name'];
    this.age = map['age'];
  



// 初始化对象
main() 
  // 普通构造方法
  var model0 = Model('name', 12);
  // 命名构造方法
  var model1 = Model.withNameAndAge('titan', 12);
  var model2 = Model.initJson('name': 'jun', 'age': 18);

初始化列表

几种方式定义的属性都是可变的, 如果定义的属性是final不可重新赋值的又该如何实现

class Teacher 
  final String name;
  final int age;

  // 1. 这里会有一个错误提示: All final variables must be initialized, but 'age' and 'name' are not
  Teacher(String name, int age) 
    //2. 这里也会有一个错误提示: 'name' can't be used as a setter because it's final
    this.name = name;
    this.age = age;
  

  • 上面第一处错误主要是因为: 在Dart中在执行下面 中的代码的时候, 表示Teacher对象已经初始化完毕了
  • 所以在执行 之前, 必须保证nameage被初始化了
  • 而且final修饰的属性是不可被重新赋值的, 所以才会报错
  • 或者也可以使用函数中的命名可选参数处理
class Size 
  final double width;
  final double height;
  final double area;

  // 命名可选参数
  Size(this.width, this.height,  this.area = 10 ); 

  • 上面通过命名可选参数的形式, 给参数设置默认值也是可以的, 但是不同的是area只能设置具体的数值, 不能设置表达式
  • 初始化列表的形式不但可以设置具体的数值, 也可以设置默认值为表达式的形式
class Size 
  final double width;
  final double height;
  final double area;

  // 多个属性使用逗号分隔
  Size(double width, double height): 
    this.width = width,
    this.height = height,
    this.area = width * height;

重定向构造方法

  • 下面的构造函数中, 我们只能通过传入两个参数来获取一个对象
  • 如果在某些情况下, 希望只通过一个name变量来获取一个对象
  • 这种情况下, 就可以通过在构造方法中去调用另外一个构造方法, 这个时候可以使用重定向构造方法
  • 需要注意的是: 在一个构造函数中,去调用另外一个构造函数, 是在冒号后面使用this调用
class Point 
  String name;
  int age;

  Point(this.name, this.age);

  // 重定向的构造方法
  Point.fromName(String name): this(name, 0);


// 使用方法
var point = Point.fromName("name");
print(point.age);  // 输出: 0

常量构造函数

  • 在某些情况下, 我们希望通过构造函数, 只要传入相同的参数, 那么得到的对象就是同一个
  • Dart中判断两个对象是否是同一个的方法是通过函数identical判断, 返回值是一个布尔值
// 普通构造函数
class Person 
  String name;
  int age;

  Person(this.name, this.age);


// 初始化列表
class Size 
  final double width;
  final double height;
  final double area;

  // 多个属性使用逗号分隔
  Size(double width, double height): 
    this.width = width,
    this.height = height,
    this.area = width * height;


main(List<String> args) 
  var p1 = Person("name", 10);
  var p2 = Person("name", 10);
  // 判断两个对象是不是同一个
  print(identical(p1, p2));    /// false


  var s1 = Size(10, 20);
  var s2 = Size(10, 20);
  // 判断两个对象是不是同一个
  print(identical(s1, s2));    /// false

  • 很明显上面两种方式初始化的对象都不是同一个
  • 其实在Dart中如果将构造方法前加const进行修饰,那么可以保证相同的参数,创建出来的对象是相同的
  • 这样的构造方法就称之为常量构造方法
// 常量构造方法
class Teacher 
  final String name;

  const Teacher(this.name);


main(List<String> args) 
  // 常量构造方法
  // 这里的const不可以省略
  var t1 = const Teacher("name");
  var t2 = const Teacher("name");
  print(identical(t1, t2));    /// true
  
  // 这里的const可以省略
  const t3 = Teacher("name");
  const t4 = Teacher("name");
  print(identical(t3, t4));    /// true
  print(identical(t1, t4));    /// true

常量构造方法有一些注意点:

  • 拥有常量构造方法的类中,所有的成员变量必须是final修饰的.
  • 为了可以通过常量构造方法,创建出相同的对象,不再使用new关键字,而是使用const关键字
  • 如果是将结果赋值给const修饰的标识符时,const可以省略.

工厂构造方法

  • Dart提供了factory关键字, 用于通过工厂去获取对象
  • 普通的构造函数, 会默认返回创建出来的对象, 不需要我们手动return
  • 工厂构造方法, 需要手动返回一个对象
  • 同样和上面一样的目的, 只要传入相同的参数, 那么得到的对象就是同一个, 下面通过工厂构造函数的方式实现
main(List<String> args) 
  
  var p1 = Person.fromName("titan");
  var p2 = Person.fromName("titan");
  print(identical(p1, p2)); // true


class Person 
  String name;

  // 用于缓存创建的对象, 避免大量的创建和销毁对象
  static final Map<String, Person> _cache = <String, Person>;

  factory Person.fromName(String name) 
    if (_cache.containsKey(name)) 
      return _cache[name];
     else 
      final p = Person(name);
      _cache[name] = p;
      return p;
    
  

  Person(this.name);

类的继承

setter和getter

  • Dart中类定义的属性默认是可以直接被外界访问的
  • Dart中也存在settergetter方法, 用于监听累的属性被访问的过程
main() 
  var people = People('top');
  people.setName = 'top';
  print(people.getName);
  print(people.name);

  var person = Person('titan');
  person.setName = 'jun';
  print(person.getName);



class People 
  String name;

  // setter
  set setName(String value) 
    this.name = value;
  
  // getter
  String get getName 
    return 'titanjun';
  

  People(this.name);

  • 上面setNamegetName是自定义的, 你也可以命名为setterNamegetterName
  • 还有就是上述两个方法不是系统自动生成的, 是需要我们手动添加的
  • 简单的方式也可以使用箭头函数
class Person 
  String name;

  // setter
  set setName(String value) => this.name = value;
  // getter
  String get getName => 'titanjun';

  Person(this.name);

类的继承

  • Dart中同样支持类的继承, 继承使用extends关键字,子类中使用super来访问父类
  • 父类中除了构造方法外, 所有的成员变量和方法都会被继承
  • 子类可以拥有自己的成员变量, 并且可以对父类的方法进行重写
  • 子类中可以调用父类的构造方法,对某些属性进行初始化:
    • 子类的构造方法在执行前,将隐含调用父类的无参默认构造方法(没有参数且与类同名的构造方法)
    • 如果父类没有无参默认构造方法,则子类的构造方法必须在初始化列表中通过super显式调用父类的某个构造方法
class People 
  String name;

  People(this.name);

  void eat() 
    print('people -- eat');
  


class Person extends People 
  int age;

  Person(String name, int age): this.age = age, super(name);

  
  void eat() 
    // 这里的super, 看个人需求是否调用
    super.eat();
    print('Person -- eat');
  


main(List<String> args) 
  var people = People('name');
  people.eat();

  var person = Person("top", 10);
  person.eat();

抽象类

  • Dart中抽象类是使用abstract声明的类
  • Dart中没有具体实现的方法(没有方法体),就是抽象方法
  • 抽象方法,必须存在于抽象类中, 抽象类不能实例化
  • 抽象类中的抽象方法必须被子类实现, 抽象类中的已经被实现方法, 可以不被子类重写
abstract class Size 
  int width;
  int height;

  Size(this.width, this.height);

  void getSize();

  int getArea() 
    return this.width * this.height;
  


class Area extends Size 
  
  void getSize() 
    print('width = $width, height = $height');
  

  Area(int width, int height): super(width, height);


main(List<String> args) 
  // 实例化Size会报错: Abstract classes can't be instantiated
  // var size = Size(20, 2);
  var area = Area(10, 2);
  area.getArea();
  print(area.getArea());

多继承

Dart中只有单继承, 是不支持多继承的, 但是我们却可以通过其他方式间接实现多继承问题

隐式接口

  • Dart中的接口比较特殊, 没有一个专门的关键字来声明接口, 默认情况下所有的类都是隐式接口
  • 默认情况下,定义的每个类都相当于默认也声明了一个接口,可以由其他的类来实现
  • Dart开发中,我们通常将用于给别人实现的类声明为抽象类
  • 当将一个类能够做接口使用时, 那么实现这个接口的类, 必须实现这个接口中的所有方法
  • Dart中通过implements来实现多继承问题, 但是必须实现这个接口中的所有方法, 而且在方法的实现中不能调用super方法
abstract class Woman 
  void eat();

  void student() 
    print("student");
  


class Man 
  void run() 
    print("runner");
  



class Student implements Woman, Man 
  
  void eat() 
    print("eat");
  

  
  void student() 
    print("student--student");
  

  
  void run() 
    // 这里不能调用super方法
    // super.run(); 
    print("run");
  


main(List<String> args) 
  var stu = Student();
  stu.eat();
  stu.run();
  stu.student();

Mixin混入

  • 在通过implements实现某个类时,类中所有的方法都必须被重新实现(无论这个类原来是否已经实现过该方法)
  • 但是某些情况下,一个类可能希望直接复用之前类的原有实现方案
  • Dart提供了另外一种方案: Mixin混入的方式
    • 除了可以通过class定义类之外,也可以通过mixin关键字来定义一个类。
    • 只是通过mixin定义的类用于被其他类混入使用,通过with关键字来进行混入
mixin Runner 
  run() 
    print('在奔跑');
  


mixin Flyer 
  fly() 
    print('在飞翔');
  


// 这里可以对原方法不做任何实现
class Bird with Runner, Flyer   

main(List<String> args) 
  var bird = Bird();
  bird.run();
  bird.fly();

抽象方法和反射和单例模式

抽象类和方法

抽象类:它相当于是象棋中的炮架子,而非炮,主要功能是用于重写的。

抽象方法:包含于抽象类之中,换言之,有抽象类才可能有抽象方法,当然抽象类中也可以无抽象方法,且抽象方法的方法体为空。

说明:abstract不能修饰属性和构造方法。

 

抽象类和抽象方法的声明格式:

抽象类如:public abstract class Person{

      …...

}

 

抽象方法如:public abstract void learn();

/*多态与抽象的联合利用:不同图像计算不同面积的方式*/

/*多态与抽象的联合利用:不同图像计算不同面积的方式*/

abstract class Shape{
    abstract float area();
}

class Circle extends Shape{
    public int r;
    public Circle(int r){
        this.r=r;
    }
    public void setR(int r){
        this.r=r;
    }
    public int getR(){
        return r;
    }

    public float area(){
        float s=(float) (3.14*r*r);
        System.out.println("半径为"+r+"的圆形面积为:"+s);
        return s;
    }
}
class Square extends Shape{
    public int a,b;
    public Square(int x,int y){
            a=x;
            b=y;
    }

    public float area(){
        float s=(float) (a*b);
        System.out.println("长宽分别为"+a+","+b+"的长方形面积为:"+s);
        return s;

    }
}

public class AbstractArray{
    public static void main(String[] args) {
        float mianJi=0;
        //左边是父类,右边赋值的是子类
        Shape[] s={new Circle(3),new Circle(4),new Circle(5),new Square(1,2),new Square(3,4)};
        for (int i=0;i<s.length;i++) {
            mianJi +=s[i].area();
        }
        System.out.println("图形总面积为:"+mianJi);
    }
}

/*思路总结:

首先写一个shape类作为空架子用于后面重写;

然后,分别写一个圆类和一个长方形类来继承shape类,

思路顺序:成员属性、构造器、设置并用this指代、获得并并返回,计算面积方法并返回;

*/

/*运行结果:

半径为3的圆形面积为:28.26

半径为4的圆形面积为:50.24

长宽分别为1,2的长方形面积为;2.0

长宽分别为3, 4的长方形面积为:12.0

图像总面积为:171.0;

*/

 

接口和抽象的异同?

相同点:

1.都含抽象方法,声明多个公用方法的返回值和输入参数列表

2.都不能被实例化

3.都是引用数据类型,可以声明抽象类即接口变量,并将子类对象赋值给抽象类变量,或将

实现接口类的变量赋值给接口变量。

不同点:

1.接口可以多继承,抽象类只能单继承

2.抽象类即成员都具有与普通类一样的权限,而接口只有public和默认权限,接口成员变量的访问权限都是public

3.抽象类可以声明变量,而接口中只能声明常量。

4.抽象类可以声明抽象方法、普通方法、构造函数,而接口中只能声明抽象方法。

 

 

反射

一.定义:

java反射机制是运行状态中,对于任意一个类,都能够获取这个类的所有属性和方法;对于任意一个对象,都能调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的发射

机制。(获取类的方法和属性或调用对象的方法和属性称为java反射机制)

 

二.特点:

指的是我们可以运行加载、探知、使用编译期间完全

未知的classes。java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。

 

三.功能:

1.运行时判断对象所属的类;

2.在运行时构造任意一个类的对象;

3.在运行时判断任意一个类所具有的成员变量和方法;

4.在运行时调用一个对象的方法;

5.生成动态代理;

 

四.反射机制的作用:

1.反编译:.class—>.java

2.通过反射机制访问java对象的属性、方法、构造方法等;

 

五.Sun公司提供反射机制中的类:

java.lang.class;

java.lang.reflect.Constructor;

java.lang.reflect.Field;

java.lang.reflect.Method;

java.lang.reflect.Modifier;

学会查API

 

六.具体功能的实现:

1.反射机制获取类有三种方法,我们来获取Employee类型

第一种方式:

Classc1=Class.forName(“Employee”);

 

第二种方式:

//java中每个类型都有class属性

Classc2=Employee.class;

 

第三种方式:

//java语言中任何一个java对象都有getClass方法

Classc3=e.getClass();

//C3是运行时类(e的运行时类是Employee)

 

2.创建对象:获取以后我们来创建它的对象,利用newInstance.

Class c=Class.forName(“Employee”);

 

//创建此Class对象所表示的类的一个新实例

Objecto=c.newInstance();

//调用了Employee的无参数构造方法。

 

//获取整个类

 

Class c=Class.forName("java.lang.Integer");

//获取所有的属性

Field[] fs=c.getDeclaredFields();

//定义可以变长的字符串,用来存储属性

StringBuffer sb=new StringBuffer();

//通过追加的方法,将每个属性拼接到此字符串中

sb.append(Modifier.toString(c.getModifiers())+"class"+c.getSimplename()+"{\n");

for (Field  field:fs) {

sb.append("\t");

//空格

sb.append(Modifier.toString(field.getModifiers())+" ");

//获取属性的修饰符,如public,static等

sb.append(field.getSimplename()+" ");

//属性的类型的名字

sb.append(field.getName()+";\n");

//属性的名字+回车

}

sb.append("}");

System.out.println(sb);

 

三种对String的累加操作

1.String tmp=“a”+”b”+”c”;

2.String tmp=null;

            tmp=“a”;

            tmp=“b”;

            tmp=“c”;

3.String tmp=null;

            StringBuffer buf =new StringBuffer();

            buf.append(“a”);

            buf.append(“b”);

            buf.append(“c”);

            tmp=buf.toString();

 

程序执行过程:

对于编译生成的.class文件,ClassLoader将其Load到内存中

CodeSegment,然后运行环境找到main方法开始执行,运行

过程中会有更多的Class被load到内存中,到有需要时,再加载,不是一次性加载运行期间动态加载(用到的时候再加载)。

 

public class TestDynamicLoading{
    public static void main(String args[]){
    new A();
    System.out.println("**----------**");
    new B();

    new C();
    new C();
    new D();
    new D();
    }
}
class A(){
    
}
class B(){
    
}
class C(){//静态语句块,对于C它只加载一行(对于两个new c())
    static{
         System.out.println("CCCCCCCCC");
    }
}
class D{//动态语句块,对于D它加载两行(对于两个new D)
      D(int i){}
    {
      System.out.println("DDDDDDDDD");
    }
}//此代码无条件加载在每个构造方法的前面

/*

总结:static语句块每次new新的对象都会执行

dynamic语句块每次new新的对象都会执行。

1.等用于构造方法中语句

2.用的较少 */

public class TestJDKClassLoader{
    
    public static void main(String[] args){
           System.out.println(String.class.getClassLoader());
    }
}

//把反射学完了,可以看看马士兵在反射中最后讲到的工厂模式
/*单例模式*/
public class SingletonTest{
    public static void main(String[] args){
    Singleton instance=Singleton.getInstance();
    instance.test();
    }
}
/*单例类*/
class Singleton{
    private static Singleton sing=new Singleton();
    private Singleton(){

    }
    public static Singleton getInstance(){
    return sing;
    }
    public void test(){
    System.out.println("test");
    }
}

/*说明:

通过类方法返回本类的对象,其中单例类的构造器设为私有的,只能本类内部才能调用它,还初始化一个本类对象sing,只能通过类外部

来访问getInstance对象,即使用程序时只有一个实例,也就是我们所说的单例模式。

*/

以上是关于Dart中的类和单例模式的主要内容,如果未能解决你的问题,请参考以下文章

FlutterDart中的类和对象

静态类和单例模式区别

抽象方法和反射和单例模式

编写高质量代码改善C#程序的157个建议——建议107:区分静态类和单例

Kotlin中数据类和单例类的实现和讲解面向对象编程接口的实现

flutter 中factory(构造方法上的唯一对象) 与单例模式