Featured image of post 第四章 类和对象

第四章 类和对象

面向对象的基本概念

万物皆对象,客观存在的事物都是对象,大到名胜古迹,小到剪刀、钟表、信封等,所有的一切都围绕对象进行,找对象、建对象,用对象等。

面向对象编程和面向过程编程

Java 是一门纯 面向对象 的语言(Object Oriented Program,简称OOP),在面向对象的世界里,一切皆为对象。面向对象是解决问题的一种思想,主要依靠对象之间的交互完成一件事情。用面向对象的思想来设计程序,更符合人们对事物的认知,对于大型程序的设计、扩展以及维护都非常友好。

面向过程(Procedure Oriented Programming)就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了;面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。

面向过程编程

面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再依次调用。面向过程编程性能高,适合跟硬件联系紧密的东西,如单片机。

面向对象编程

面向对象就是把事务分解成一个个对象, 由对象之间分工合作。面向对象编程具有灵活、代码可复用、容易维护和开发的优点,更适合多人合作的大型软件项目。

面向对象的特性:

	**1. 封装性:** **不需要知道运作原理,直接使用即可。**

	**2. 继承性:** **继承已有的功能和方法,且能够扩展新方法。**

	**3. 多态性:** **有多种功能。**

类和对象

类是描述某一对象的统称,对象是这个类的一个实例。 有类之后就能根据这个类来产生具体的对象。一类对象所具备的共同属性和行为(方法)都在类中定义。

在 Java 中,必须先设计类,才能获得对象。

类的定义

定义一个 Student 类,里面的成员变量有 name 名字、age 年龄、height 身高、id 学号、gender 性别等,定义一个成员方法 study,调用该方法时在控制台打印一条语句。

 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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package com.company.arr;

public class Student {
    /*
        public 公共的
        private 私有的
        定义Student类的属性,成员变量
    */
    private String name;
    public int age;
    private double height;
    private int id;
    private char gender;

    // 定义 study 方法
    public static void study(){
        System.out.println("学习");
    }
    
    // 生成 javabean -> alt + insert 键
    /*
        1. 空参构造函数
        2. 全参构造函数
        3. getter 和 setter
        4. toString 方法
     */

    public Student(String name, int age, double height, int id, char gender) {
        this.name = name;
        this.age = age;
        this.height = height;
        this.id = id;
        this.gender = gender;
    }

    public Student() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public char getGender() {
        return gender;
    }

    public void setGender(char gender) {
        this.gender = gender;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", height=" + height +
                ", id=" + id +
                ", gender=" + gender +
                '}';
    }
}

通过 new 关键字获取对象,语法为:类名 对象名 = new 类名(); 。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package com.company.arr;

public class Pt2 {
    public static void main(String[] args) {
        Student s1 = new Student("张洼", 18, 175.6, 0001, '男');
        System.out.println(s1);
        System.out.println(s1.getName() + "的年龄为:" + s1.age + "岁");

        Student s2 = new Student("陈琪", 21, 169.2, 0002, '女');
        System.out.println(s2);
        // s2.study();
        Student.study();
    }
}

Javabean 类

  • 用来描述一类事物的类,叫做:Javabean 类,在 Javabean 类中是不写 main 方法的。

  • 编写 main 方法的类,叫做:测试类,我们可以在测试类中创建 Javabean 类的对象并进行赋值调用。

定义类的注意事项

  • 类名首字母建议大写,需要见名知意,驼峰模式。

  • 一个 Java 文件中可以定义多个 class 类,且只能有一个类是 public 修饰的,而且 public 修饰的类名必须 成为代码文件名。实际开发中建议还是一个文件定义一个class类。

  • 成员变量的完整定义格式是:修饰符 数据类型 变量名称 = 初始化值;, 一般无需指定初始化值,存在默认值。

数据类型 默认值
byte、short、int、long 0
float、double 0.0
boolean flase
类、接口、数组、String null

面向对象详解

封装

对象代表什么,就得封装对应的数据,并提供数据对应的行为。

封装的作用

① sun 公司已经将大量的对象封装起来,我们可以根据自己需要调用相应的方法。

1
2
3
4
String str = "hello world";
System.out.println(str.length());
System.out.println(Arrays.toString(str.getBytes()));
System.out.println(new String(str.getBytes(), "GBK"));

② 封装可以告诉我们,如何正确的设计对象的属性和方法。

private关键字

	1. 是一个权限修饰符

	2. 可以修饰成员(成员变量和成员方法)

	3. 被 private 修饰的成员只能在本类中才能访问

对于每一个私有化的成员变量,都需要提供 set 和 get 方法,set 方法用于给成员变量赋值,get 方法用于对外提供成员变量的值。

练习:创建一个朋友类,自己思考,朋友有哪些属性?哪些行为?将其添加到类中。

局部变量和成员变量

什么是成员变量

成员变量就是类中,方法外的变量。

成员变量没有上下顺序,不过我们一般将其写在类的最上方。

 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
package com.company.object;

public class People {
    // javabean 类 , 不写 main 方法
    public String name;
    public int age;
    public char gender;
    public double height;

    public People() {
    }

    public People(String name, int age, char gender, double height) {
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.height = height;
    }

    public static void speak() {
        System.out.println("你好!");
    }

    public static void getSum(int a, int b) {
        System.out.println("a + b = " + (a + b));
    }
}

其中,成员变量为 name,age,gender,height 。

什么是局部变量

局部变量就是方法里的变量。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package com.company.object;

public class Demo2 {
    public static void main(String[] args) {
        int sum = 0;
        for (int i = 0; i < 10; i++) {
            sum += i;
        }
        System.out.println(sum);
    }
}

其中,全局变量为 sum,局部变量为 i 。

成员变量和局部变量的区别

区别 成员变量 局部变量
类中位置不同 类中,方法外 方法内、方法申明上
初始化值不同 有默认初始化值 没有,使用之前需要完成赋值
内存位置不同 堆内存的对象里 栈内存的方法里
生命周期不同 随对象的创建而存在,对象消失就消失 随方法的调用而存在,方法运行结束就消失
作用域 整个类中有效 当前方法中有效

方法申明上指方法形参,形参也是局部变量。

this 关键字

就近原则

以下代码输出的结果是?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package com.company.object;

public class Demo2 {
        int a = 10;
        a = 20;
        System.out.println(a);
        a = 30;
        a = 40;
        System.out.println(a);
    }
}

在给成员变量 age 赋值时,通常在 public void setAge() 方法中,通过传入参数的方式传入我们想要赋予的值,根据 java “见名知意” 的命名规则,此时我们的形参应该为 int age,形参名与成员变量名一致的情况下,如何区分?

1
2
3
4
5
6
7
8
9
package com.company.object;

public class StuTest {
    private int age;

    public void setAge(int age){
        age = age;
    }
}

this 的原理

this 的作用:区分局部变量和成员变量重名。

this 的本质:记录所在方法调用者的地址值。

 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
package com.company.object;

public class Demo2 {
    public static void main(String[] args) {
        StuTest s1 = new StuTest(20);
        s1.method();	// 此时方法调用者为 s1,因此 this 记录 s1 的地址值,获取 s1 中的 age
    }
}

class StuTest {
    private int age;

    public StuTest(int age) {
        this.age = age;
    }

    public void method(){
        int age = 18;
        System.out.println(age);	// 没有 this 时,触发就近原则
        System.out.println(this.age);
    }
    
    public void setAge(int age){
        this.age = age;		// 左边表示方法调用者的 age,右边表示就近的形参里的 age
    }
}

构造方法

构造方法概述

构造方法,又叫构造函数、构造器,能够在创建对象的时候为成员变量进行初始化。在创建对象时,构造方法自动调用,每创建一次对象,都会调用一次构造方法。

	① public 类名(形参){...}

	② 方法与类名必须完全一致。

	③ 没有返回值类型,不能有 void,不能有 return。

	④ 当我们没有写构造方法时,虚拟机会为我们自动生成一个空参的构造方法。

构造方法注意事项

① 构造方法的定义

▷ 如果没有定义构造方法,系统将给出一个默认的无参数构造方法

▷ 如果定义了构造方法,系统将不再提供默认的构造方法。

② 构造方法的重载

▷ 带参构造方法,和无参数构造方法,两者方法名相同,但是参数不同,这叫做构造方法的重载。

③ 推荐的使用方式

无论是否使用,都手动书写无参数构造方法,和带全部参数的构造方法。

回合制格斗小游戏

  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
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package com.company.object.PKTest;

import java.util.Random;

public class Character {
    private String name;
    private int blood;
    private char gender;
    private String face;

    String[] boyfaces = {"风流俊雅", "气宇轩昂", "相貌英俊", "五官端正", "相貌平平", "一塌糊涂", "面目狰狞"};
    String[] girlfaces = {"美奂绝伦", "沉鱼落雁", "婷婷玉立", "身材娇好", "相貌平平", "相貌简陋", "惨不忍睹"};

    String[] attacks_desc = {
            "%s使出了一招【背心钉】,转到对方的身后,一掌向%s背心的灵台穴拍去。",
            "%s使出了一招【游空探爪】,飞起身形自半空中变掌为抓锁向%s。",
            "%s大喝一声,身形下伏,一招【劈雷坠地】,捶向%s双腿。",
            "%s运气于掌,一瞬间掌心变得血红,一式【掌心雷】,推向%s。",
            "%s阴手翻起阳手跟进,一招【没遮拦】,结结实实的捶向%s。",
            "%s上步抢身,招中套招,一招【劈挂连环】,连环攻向%s。"
    };

    String[] injureds_desc = {
            "结果%s退了半步,毫发无损",
            "结果给%s造成一处瘀伤",
            "结果一击命中,%s痛得弯下腰",
            "结果%s痛苦地闷哼了一声,显然受了点内伤",
            "结果%s摇摇晃晃,一跤摔倒在地",
            "结果%s脸色一下变得惨白,连退了好几步",
            "结果『轰』的一声,%s口中鲜血狂喷而出",
            "结果%s一声惨叫,像滩软泥般塌了下去"
    };

    public Character(String name, int blood, char gender) {
        this.name = name;
        this.blood = blood;
        this.gender = gender;
        setFace(gender);
    }

    public char getGender() {
        return gender;
    }

    public void setGender(char gender) {
        this.gender = gender;
    }

    public String getFace() {
        return face;
    }

    private void setFace(char gender) {
        Random r = new Random();
        if (gender == '男') {
            int index = r.nextInt(boyfaces.length);
            this.face = boyfaces[index];
        } else if (gender == '女') {
            int index = r.nextInt(girlfaces.length);
            this.face = girlfaces[index];
        } else {
            this.face = "面目狰狞";
        }
    }

    @Override
    public String toString() {
        return "Character{" +
                "name='" + name + '\'' +
                ", blood=" + blood +
                ", gender=" + gender +
                ", face='" + face + '\'' +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getBlood() {
        return blood;
    }

    public void setBlood(int blood) {
        this.blood = blood;
    }

    public Character() {
    }

    public Character(String name, int blood) {
        this.name = name;
        this.blood = blood;
    }

    // ----------------------------------------------

    public void attack(Character character) {
        Random r = new Random();
        // 攻击招式
        int index = r.nextInt(attacks_desc.length);
        String skill = attacks_desc[index];
        System.out.printf(skill, this.getName(), character.getName());
        System.out.println();
        // 伤害计算
        int hurt = r.nextInt(20) + 1;
        // 剩余血量
        int remainBlood = character.blood - hurt;
        remainBlood = Math.max(remainBlood, 0);
        // 更新血量
        character.setBlood(remainBlood);
        // 受伤描述
        if (remainBlood > 90) {
            System.out.printf(injureds_desc[0], character.getName());
        } else if (remainBlood > 80) {
            System.out.printf(injureds_desc[1], character.getName());
        } else if (remainBlood > 70) {
            System.out.printf(injureds_desc[2], character.getName());
        } else if (remainBlood > 60) {
            System.out.printf(injureds_desc[3], character.getName());
        } else if (remainBlood > 40) {
            System.out.printf(injureds_desc[4], character.getName());
        } else if (remainBlood > 20) {
            System.out.printf(injureds_desc[5], character.getName());
        } else if (remainBlood > 0) {
            System.out.printf(injureds_desc[6], character.getName());
        } else {
            System.out.printf(injureds_desc[7], character.getName());
        }
        System.out.println();
    }

    public void showInfo() {
        // 角色信息
        System.out.println("姓名:" + getName() + ",血量:" + getBlood() + ",性别:" + getGender() + ",长相:" + getFace());
    }
}
 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
package com.company.object.PKTest;

public class MainTest {
    public static void main(String[] args) {
        Character c1 = new Character("老王", 100, '男');
        Character c2 = new Character("老李", 86, '男');

        c1.showInfo();
        c2.showInfo();

        System.out.println("==================== 决斗开始 ====================");

        while (true) {
            c1.attack(c2);
            if (c2.getBlood() == 0) {
                System.out.println("================= " + c1.getName() + "K.O了!" + c2.getName() + "! =================");
                break;
            }
            c2.attack(c1);
            if (c1.getBlood() == 0) {
                System.out.println("================= " + c2.getName() + "K.O了" + c1.getName() + "! =================");
                break;
            }
        }
    }
}

Blog for Sandy Memories