java-object-class.md 8.6 KB
Newer Older
沉默王二's avatar
优化  
沉默王二 已提交
1
## 对象和类
沉默王二's avatar
更新  
沉默王二 已提交
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 142 143 144 145 146 147 148 149 150 151

“二哥,我那天在图书馆复习[上一节](https://mp.weixin.qq.com/s/WzMEOEdzI0fFwBQ4s0S-0g)你讲的内容,刚好碰见一个学长,他问我有没有‘对象’,我说还没有啊。结果你猜他说什么,‘要不要我给你 new 一个啊?’我当时就懵了,new 是啥意思啊,二哥?”三妹满是疑惑的问我。

“哈哈,三妹,你学长还挺幽默啊。new 是 Java 中的一个关键字,用来把类变成对象。”我笑着对三妹说,“对象和类是 Java 中最基本的两个概念,可以说撑起了面向对象编程(OOP)的一片天。”

### 01、面向过程和面向对象

三妹是不是要问,什么是 OOP?

OOP 的英文全称是 Object Oriented Programming,要理解它的话,就要先理解面向对象,要想理解面向对象的话,就要先理解面向过程,因为一开始没有面向对象的编程语言,都是面向过程。

举个简单点的例子来区分一下面向过程和面向对象。

有一天,你想吃小碗汤了,怎么办呢?有两个选择:

1)自己买食材,豆腐皮啊、肉啊、蒜苔啊等等,自己动手做。

2)到饭店去,只需要对老板喊一声,“来份小碗汤。”

第一种就是面向过程,第二种就是面向对象。

面向过程有什么劣势呢?假如你买了小碗汤的食材,临了又想吃宫保鸡丁了,你是不是还得重新买食材?

面向对象有什么优势呢?假如你不想吃小碗汤了,你只需要对老板说,“我那个小碗汤如果没做的话,换成宫保鸡丁吧!”

面向过程是流程化的,一步一步,上一步做完了,再做下一步。

面向对象是模块化的,我做我的,你做你的,我需要你做的话,我就告诉你一声。我不需要知道你到底怎么做,只看功劳不看苦劳。

不过,如果追到底的话,面向对象的底层其实还是面向过程,只不过把面向过程进行了抽象化,封装成了类,方便我们的调用。

### 02、类

对象可以是现实中看得见的任何物体,比如说,一只特立独行的猪;也可以是想象中的任何虚拟物体,比如说能七十二变的孙悟空。

Java 通过类(class)来定义这些物体,这些物体有什么状态,通过字段来定义,比如说比如说猪的颜色是纯色还是花色;这些物体有什么行为,通过方法来定义,比如说猪会吃,会睡觉。

来,定义一个简单的类给你看看。

```java
/**
 * 微信搜索「沉默王二」,回复 Java
 *
 * @author 沉默王二
 * @date 2020/11/19
 */
public class Person {
    private String name;
    private int age;
    private int sex;

    private void eat() {
    }

    private void sleep() {
    }

    private void dadoudou() {
    }
}
```

一个类可以包含:

- 字段(Filed)
- 方法(Method)
- 构造方法(Constructor)

在 Person 类中,字段有 3 个,分别是 name、age 和 sex,它们也称为成员变量——在类内部但在方法外部,方法内部的叫临时变量。

成员变量有时候也叫做实例变量,在编译时不占用内存空间,在运行时获取内存,也就是说,只有在对象实例化(`new Person()`)后,字段才会获取到内存,这也正是它被称作“实例”变量的原因。

方法 3 个,分别是 `eat()``sleep()``dadoudou()`,表示 Person 这个对象可以做什么,也就是吃饭睡觉打豆豆。

那三妹是不是要问,“怎么没有构造方法呢?”

的确在 Person 类的源码文件(.java)中没看到,但在反编译后的字节码文件(.class)中是可以看得到的。

```java
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.itwanger.twentythree;

public class Person {
    private String name;
    private int age;
    private int sex;

    public Person() {
    }

    private void eat() {
    }

    private void sleep() {
    }

    private void dadoudou() {
    }
}
```

`public Person(){}` 就是默认的构造方法,因为是空的构造方法(方法体中没有内容),所以可以缺省。Java 聪明就聪明在这,有些很死板的代码不需要开发人员添加,它会偷偷地做了。

### 03、new 一个对象

创建 Java 对象时,需要用到 `new` 关键字。

```java
Person person = new Person();
```

这行代码就通过 Person 类创建了一个 Person 对象。所有**对象**在创建的时候都会在**堆内存中分配空间**

创建对象的时候,需要一个 `main()` 方法作为入口, `main()` 方法可以在当前类中,也可以在另外一个类中。

第一种:`main()` 方法直接放在 Person 类中。

```java
public class Person {
    private String name;
    private int age;
    private int sex;

    private void eat() {}
    private void sleep() {}
    private void dadoudou() {}

    public static void main(String[] args) {
        Person person = new Person();
        System.out.println(person.name);
        System.out.println(person.age);
        System.out.println(person.sex);
    }
}
```

输出结果如下所示:

```
null
0
0
```

第二种:`main()` 方法不在 Person 类中,而在另外一个类中。

沉默王二's avatar
沉默王二 已提交
152
![](https://cdn.jsdelivr.net/gh/itwanger/jmx-java/images/object-class/16-01.png)
沉默王二's avatar
更新  
沉默王二 已提交
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215

实际开发中,我们通常不在当前类中直接创建对象并使用它,而是放在使用对象的类中,比如说上图中的 PersonTest 类。

可以把 PersonTest 类和 Person 类放在两个文件中,也可以放在一个文件(命名为 PersonTest.java)中,就像下面这样。

```java
/**
 * @author 微信搜「沉默王二」,回复关键字 PDF
 */
public class PersonTest {
    public static void main(String[] args) {
        Person person = new Person();
    }
}

class Person {
    private String name;
    private int age;
    private int sex;

    private void eat() {}
    private void sleep() {}
    private void dadoudou() {}
}
```

### 04、初始化对象

在之前的例子中,程序输出结果为:

```
null
0
0
```

为什么会有这样的输出结果呢?因为 Person 对象没有初始化,因此输出了 String 的默认值 null,int 的默认值 0。

那怎么初始化 Person 对象(对字段赋值)呢?

第一种:通过对象的引用变量。

```java
public class Person {
    private String name;
    private int age;
    private int sex;

    public static void main(String[] args) {
        Person person = new Person();
        person.name = "沉默王二";
        person.age = 18;
        person.sex = 1;
        
        System.out.println(person.name);
        System.out.println(person.age);
        System.out.println(person.sex);
    }
}
```

person 被称为对象 Person 的引用变量,见下图:

沉默王二's avatar
沉默王二 已提交
216
![](https://cdn.jsdelivr.net/gh/itwanger/jmx-java/images/object-class/16-02.png)
沉默王二's avatar
更新  
沉默王二 已提交
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295

通过对象的引用变量,可以直接对字段进行初始化(`person.name = "沉默王二"`),所以以上代码输出结果如下所示:

```
沉默王二
18
1
```

第二种:通过方法初始化。

```java
/**
 * @author 沉默王二,一枚有趣的程序员
 */
public class Person {
    private String name;
    private int age;
    private int sex;

    public void initialize(String n, int a, int s) {
        name = n;
        age = a;
        sex = s;
    }

    public static void main(String[] args) {
        Person person = new Person();
        person.initialize("沉默王二",18,1);

        System.out.println(person.name);
        System.out.println(person.age);
        System.out.println(person.sex);
    }
}
```

在 Person 类中新增方法 `initialize()`,然后在新建对象后传参进行初始化(`person.initialize("沉默王二", 18, 1)`)。

第三种:通过构造方法初始化。

```java
/**
 * @author 沉默王二,一枚有趣的程序员
 */
public class Person {
    private String name;
    private int age;
    private int sex;

    public Person(String name, int age, int sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    public static void main(String[] args) {
        Person person = new Person("沉默王二", 18, 1);

        System.out.println(person.name);
        System.out.println(person.age);
        System.out.println(person.sex);
    }
}
```

这也是最标准的一种做法,直接在 new 的时候把参数传递过去。

补充一点知识,匿名对象。匿名对象意味着没有引用变量,它只能在创建的时候被使用一次。

```java
new Person();
```

可以直接通过匿名对象调用方法:

```java
new Person().initialize("沉默王二", 18, 1);
```