# 面向对象

  • 程序是对现实世界的建模
  • class的概念,object的概念 => 实例(Instance)
    • 对应现实世界中的某一类事物
    • 人、动物、车、飞机、形状、国家
    • 张三、那只猫、这辆汽车、天上飞过的一架飞机、这个正方形、斯里兰卡。。。
  • 程序中如何建立一类事物
    • class关键字
  • 程序中如何建立一个对象
    • 有的是new,有的可以省略
// Java 
// 形状类
class Shape{
    
}
public class Shape{
    public static void main(String[] args){
        new Shape();
    }
}
1
2
3
4
5
6
7
8
9
10
# Python
# 形状类
class Shape:
    pass

Shape()
1
2
3
4
5
6
// C++
#include <iostream>
using namespace std;

class Shape {
    
};

int main()
{
    new Shape();
}
1
2
3
4
5
6
7
8
9
10
11
12

# 类的构造方法(构造函数)

  • 指的是在创建这个类的对象的时候,被自动调用的方法
    • Java/C++与类同名,没有返回值
    • Python __init__
    • Java默认会提供一个参数为空的构造方法
    • 自己写了一个构造方法,Java就不再为你提供构造方法了
// Java 
// 形状类
class Shape{
    Shape(){
        System.out.println("Shape");
    }
}
public class Shape{
    public static void main(String[] args){
        new Shape();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
# Python
# 形状类
class Shape:
    def __init__(self):
        print('Shape')

Shape()
1
2
3
4
5
6
7
// C++
#include <iostream>
using namespace std;

class Shape {
public:
    Shape(){
        cout << "Shape" << endl;
    }
};

int main()
{
    new Shape();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 类的属性

  • 对应的是现实世界中某一类事物共有的属性
    • 人的名字、车的品牌、飞机的翼展
  • 创建对象的时候,每一个对象内部都有这些属性的对应值
// Java 
// 形状类
class Shape{
    String name;
    Float area;
    Shape(){
        System.out.println("Shape");
    }
}
public class Shape{
    public static void main(String[] args){
        new Shape();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Python
# 形状类
class Shape:
    
    name = 'Shape'
    area = 0
    
    def __init__(self):
        print('Shape')

Shape()
1
2
3
4
5
6
7
8
9
10
11
// C++
#include <iostream>
using namespace std;

class Shape {
    string name;
    float area;
public:
    Shape(){
        name = "Shape";
        cout << "Shape" << endl;
    }
};

int main()
{
    new Shape();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 操纵对象

  • C++的指针、Java的引用、Python中的变量、Windows中的句柄...
  • 其实都是同样的东西
  • 这些都是存储的对象的地址
  • 除Python外,都必须指定这个指针指向的类型
  • 通过这个指向对象的指针,可以对对象进行操纵

image-20201208221830767

对象的本质就是一段地址范围,而指针是访问这个范围用的

指定类型、访问属性

// Java 
// 形状类
class Shape{
    String name;
    Float area;
    Shape(){
        System.out.println("Shape");
    }
}
public class Shape{
    public static void main(String[] args){
        Shape s = new Shape();
        s.name = "";
        s.area = 1.0f;
        System.out.println(s.name);
        System.out.println(s.area);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Python
# 形状类
class Shape:
    
    name = 'Shape'
    area = 0
    
    def __init__(self):
        print('Shape')

s = Shape()
print(s.name)
s.area = 1.0
print(s.area)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// C++
#include <iostream>
using namespace std;

class Shape {
public:
    string name;
    float area;
public:
    Shape(){
        name = "Shape";
        cout << "Shape" << endl;
    }
};

int main()
{
    Shape* s = new Shape();
    s->name = "Shape2";
    s->area = 1.0;
    cout << s->name << endl;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

PS:名称为s的内存里存放着地址指针,地址指针指向着new Shape()对象的内存

问题:变量s的大小是多少?

答:由操作系统是多少位决定,32位则大小为32位,64位则大小为64位

# 回收对象所占用的内存

  • Java/Python自动回收
  • C++手动回收 delete s 将s指向的那个内存标记已废除,可以用了
    • 忘了回收,内存泄漏
    • 回收多次,数据丢失
// C++
#include <iostream>
using namespace std;

class Shape {
public:
    // 指针为指定对象时 默认是随机的内存地址 这时要是delete的话就可能会发生数据丢失
    // 给指针指定为NULL则不是随机的内存地址 而是NULL了
    string* name = NULL;
    float area;
public:
    //构造函数
    Shape(){
        name = new strin("Shape");
        cout << "Shape" << endl;
    }
    // 析构函数
    ~Shape() {
        cout << "deconstructor" << endl;
        if(name)
        	delete name;
    }
};

int main()
{
    /*
    Shape* s = new Shape();
    //s->name = "Shape2";
    s->area = 1.0;
    cout << s->name << endl;
    delete s; // delete s的时候会触发 指向的对象的析构函数
    */
    Shape s; 
    // 会执行构造函数与析构函数 生成对象在栈上 而new出来的对象在堆中
    // 由于主函数main结束后和main函数相关的变量都清理 会自动将栈上的对象进行清理 
    // 所以调用了析构函数
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

复习

  • 类、对象
  • 构造方法(函数)
  • 如何创建对象
  • 如何回收对象
  • 如何操纵对象

理解了内存就理解了一切

# 成员方法(成员函数)

  • 方法名也是指针
  • 通过引用或指针访问成员方法
  • 通过方法名找到方法代码的过程叫做绑定(binding)
  • 编译时vs运行时
    • compile-time 编译时(静态)绑定(static、final、private方法、构造方法) 在编译时通过方法名找到方法代码
    • run-time 运行时(动态)绑定 在运行时通过方法名找到方法代码
      • mian函数开始运行到结束期间
  • Java中大多数方法都是动态绑定
    • dynamic binding 动态绑定
    • run-time binding 运行时绑定 这俩个是一回事
class Person{
    String name = "张三";
    int age = 16;
    public void printName(){
        System.out.println(name);
    }
}

public class TestPerson{
    public static main(String[] args){
        Person p = new Person();
        p.printName();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

思考:Person p = new Person();在编译的时候就能确定啊,为什么运行时绑定呢?

答:因为在main函数里执行了Person p = new Person(); 当执行Person p调用ClassLoader将class文件装载到内存中,执行new Person()创建Person对象并由p指向这个对象所占的内存空间,当调用p.printName()方法时 会找到类所在内存里中方法代码,所以这个过程时运行时绑定

一个方法(不能被继承=>不会指向多份代码块)(static、final、private方法、构造方法)绑定一块代码块没有歧义,是静态绑定 如构造方法

一个方法绑定一块代码块有歧义,是动态态绑定 如printName()

# 继承、重写、多态

  • Animal

    • 属性:name
    • 方法:makeSound()
  • Dog extends Animal

    • name
    • swimmingSpeed
    • swim()
  • Cat extends Animal

    • name
    • tailLength
    • climb()
  • 子类对象的布局

  • 调用子类方法的绑定过程

    • makeSound()
  • 父类引用可以指向子类对象

  • 通过父类引用只能够"看到"共有的属性或方法

  • 父类引用与子类引用的转换

  • 通过父类引用调用方法的绑定过程

    • 多态

先抓核心,再针对核心进行展开

class Animal {
    String name;
    void makeSound() {
        System.out.println("Animal Sound.");
    }
}
class Dog extends Animal {
    int swimmingSpeed;
    void swim() {
        
    }
    void makeSound() {
        System.out.println("wangwang.");
    }
}
class Cat extends Animal {
    int tailLength;
    void climb() {
        
    }
    void makeSound() {
        System.out.println("miaomiao.");
    }
}
class Goose extends Animal {
    void makeSound() {
        System.out.println("gagagaga.");
    }
}
// 多态 Polymophysm 面向对象的核心优势就是多态
public class Polymophysm {
    public static main(String[] args){
        //System.out.println("hello");
        /*
        Dog d = new Dog();
        d.name = "wangwang";
        d.swim();
        d.makeSound();
        */
        Animal a = new Dog();
        a.makeSound();
        a.name = "";
        Dog d = (Dog)a;
        Animal aa = new Cat();
        aa.makeSound();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

私有的属性从父类继承下来了 但是不能访问

高手的定义 要有自己的作品好的项目 这才是强