目录
1 面向对象的基本认识
1.1 什么是面向对象
Java 是一门纯面向对象的语言(Object Oriented Program,简称OOP),在面向对象的世界里,一切皆为对象。面向对象是解决问题的一种思想,主要依靠对象之间的交互完成一件事情。
1.2 面向对象和面向过程
面向过程和面向对象都是解决问题的思考方式。
面向过程(Procedure Oriented Programming, POP)是以“过程”为中心的,遇到问题时,想的是解决问题的步骤,然后用函数把步骤一一实现,最后再依次调用。面向过程更侧重于“怎么做”,以执行者的角度思考问题,比较适合解决小型问题或底层问题。
面向对象(Object Oriented Programming, OOP)以“对象”为中心,遇到问题时,想的是需要哪些对象一起来解决问题,然后找到这些对象,并把它们组织在一起,然后取各家之所长来共同解决一个问题。面向对象更侧重于“谁来做”,以指挥者的角度思考问题,比较适合解决中大型问题。
拿洗衣服举例。如果一个人选择手洗衣服,那他就是面向过程:拿个盆子——接一盆水——放衣服——放洗衣粉——搓衣服——拧干衣服——倒水——再接一盆水——衣服过水——拧干——倒水——晾衣服。但如果一个人选择用洗衣机来洗衣服,那他就是面向对象。总共 4 个对象:人,衣服,洗衣粉,洗衣机。人要做的就是把衣服放进洗衣机里,倒点洗衣粉,启动洗衣机。等洗衣机洗好了,拿去晾就可以了。整个过程是人,衣服,洗衣粉,洗衣机交互完成的,人并不需要操心洗衣机是如何清洗的。
注意:面向过程和面向对象并不是一门语言,而是解决问题的方法,没有那个好坏之分,都有其专门的应用场景。
2 类的定义和使用
将客观世界中存在的一切可以描述的事物称为对象(实体),也就是“万物皆对象”。这些众多的对象有些是有相同的属性和功能(或行为)的,按照属性和功能(或行为)等对它们进行分类、抽象,就形成了概念世界中的类。类实际上就是根据生活经验总结抽取出来的产物,相当于一组对象的类型。
类是抽象的,对象是具体的,类相当于创建对象的蓝图。例如汽车,类与对象的关系就如汽车设计图与汽车实物的关系。面向对象的程序实现需要通过类创建对应的实例化对象,来对应客观世界中的实体。
2.1 简单认识类
在有对象之前,我们得先有类。类是用来对一个实体(对象)来进行描述的,主要描述该实体(对象)具有哪些属性(外观尺寸等),哪些功能(用来干 啥),描述完成后计算机就可以识别了。
比如:洗衣机,在Java中可以将其看成是一个类别。
属性:产品品牌,型号,产品重量,外观尺寸,颜色,价格......
功能:洗衣,烘干、定时......
类的声明:
类可以包含3种最常见的成员,即属性、构造器、方法,3种成员可以定义为0个或多个,如果3种成员都定义为0个,那么就相当于一个空类,语法上没问题,但是没有实际意义。当然类的成员除了这3种,还有内部类和代码块,这些在后续章节中会讲到。类中各个成员的排列顺序是随意的,但是习惯上人们会按照属性、构造器、方法的顺序去定义。
在 Java 中定义类时需要用到 class 关键字,具体语法如下:
//定义类
class 类名{
field; // 字段(属性) 或者 成员变量,通常多个属性
method; // 行为 或者 成员方法,通常多个方法
}
拿洗衣机举例:
class WashMachine{
//洗衣机的属性
public String brand; //品牌
public String color; //颜色
public String type; //型号
public String weight; //重量
public String length; //长
public String width; //宽
public String heigth; //高
//洗衣机的功能
public void washClothes(){
System.out.println("洗衣服");
}
public void dryClothes(){
System.out.println("烘干衣服");
}
}
注意事项:
1. 类名注意采用大驼峰定义
2. 成员前写法统一为 public
3. 此处写的方法不带 static 关键字
4. 一般一个文件当中只定义一个类
5. main 方法所在的类一般要使用 public 修饰
6. public 修饰的类必须要和文件名相同,不要轻易去修改 public 修饰的类的名称,如果要修改,通过开发工具修改
3. 类的实例化——对象
3.1 什么是实例化
定义了一个类,就相当于在计算机中定义了一种新的类型,与 int,double 类似,只不过 int 和double 是 Java 语言自带的内置类型,而类是用户自定义了一个新的类型,比如上述的WashMachine 类。
用 类 类型创建对象的过程,称为类的实例化,在 Java 中采用 new 关键字,配合类名来实例化对象。
public static void main(String[] args) {
WashMachine machine1 = new WashMachine();
machine1.brand = "haiEr";
machine1.color = "Yellow";
System.out.println(machine1.brand + " " + machine1.color);
machine1.washClothes();
machine1.dryClothes();
}
注意:
1. new 关键字用于创建一个对象的实例.
2. 使用 . 来访问对象中的属性和方法.
3. 同一个类可以创建对个实例
3.2 类和对象的说明
1. 类只是一个模型一样的东西,用来对一个实体进行描述,限定了类有哪些成员.
2. 类是一种自定义的类型,可以用来定义变量.
3. 一个类可以实例化出多个对象,实例化出的对象才占用实际的物理空间,存储类成员变量。
为说明类实例化之后的存储情况,下面创建了一个 Person 类,实例化一个对象 person
class Person{
public String name;
public int age;
public String gender;
public void sayHello(){
System.out.println("Hello! How do you do!");
}
}
public class Practice {
Person person = new Person();
}
有同学会问,Person 类里面还有方法 sayHello 呢,怎么没有开辟空间了?是这样子的,方法还未调用之前,只存放于方法区里面,等调用了再分配内存。
4. this 引用
4.1 为什么要用 this 引用
下面来看一下一个日期的例子
class Date{
public int year ;
public int month;
public int day;
public void setDate(int y ,int m ,int d){
year = y;
month = m;
day = d;
}
public void printDate(){
System.out.println("年: "+ year + " 月: " + month + " 日: " + day);
}
}
public class Practice {
public static void main(String[] args) {
//类Date实例化三个对象,分别是date1,date2,date3
Date date1 = new Date();
Date date2 = new Date();
Date date3 = new Date();
//设置日期
date1.setDate(2022,11,8);
date2.setDate(2022,11,9);
date3.setDate(2022,11,10);
//打印日期
date1.printDate();
date2.printDate();
date3.printDate();
}
}
仔细阅读上述代码,我们不难提出以下问题。
三个对象都在调用 setDate 和 printDate 方法,但是这两个方法中没有任何有关对象的说明,setDate 和 printDate 方法如何知道打印的是那个对象的数据呢?
在 main 方法里面,我们可以很清晰地看出,具体哪个对象调用了 setDate 以及 printDate。
可是在成员方法真正执行的时候,方法体 内部根本就没有对象的信息,那么每次执行,setDate 怎么知道给哪个对象设置时间,而 printDate 又是如何知道打印哪个对象的呢?
这就要用到下一小节介绍的 this 引用。
4.2 什么是 this 引用
this 引用指向当前对象(成员方法运行时调用该成员方法的对象)。在成员方法中所有成员变量的操作,都是通过该引用去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
所以上述日期的例子中的 setDate 方法实际上长这样:
//Date this 是一个隐藏的参数
public void setDate(Date this, int y ,int m ,int d){
year = y;
month = m;
day = d;
}
哪个对象调用 setDate,那么 this 就代表哪个对象的引用。
而对于那个无参数的 printDate 来说,亦是如此:
public void printDate(Date this){
System.out.println("年: "+ year + " 月: " + month + " 日: " + day);
}
只不过在平时使用的时候,并不需要把类名 this 这个隐藏参数写出来。可是在方法体中,却最好在成员变量前,使用 this 。因为这样一来,就算形参与成员变量名一样,编译器依旧可以分辨得出,有 this. 的是成员变量,而没有的,则是形参,也就知道谁给谁赋值了。
所以最好养成在方法体中的成员变量前用 this 的好习惯。
public void setDate(int year ,int month ,int day){
this.year = year;
this.month = month;
this.day = day;
}
public void printDate(){
System.out.println("年: "+ this.year + " 月: " + this.month + " 日: " + this.day);
}
4.3 总结一下 this 的特性
1. this 的类型:对应类类型引用,即哪个对象调用就是哪个对象的引用类型
2. this 只能在"成员方法"中使用
3. 在"成员方法"中,this 只能引用当前对象,不能再引用其他对象
4. this 是“成员方法”第一个隐藏的参数,编译器会自动传递,在成员方法执行时,编译器会负责将调用成员方法对象的引用传递给该成员方法,this 负责来接收
5. 对象的构造和初始化
5.1 对象的初始化与局部变量的初始化
根据前面所学的知识可知,在 Java 方法内部定义一个局部变量时,必须要先初始化,否则会编译失败。比如:
public static void main(String[] args) {
int z;
System.out.println(z);
}
//未初始化,编译报错!!!!!
但是对比对象的创建,还未对成员变量初始化,调用了 printDate,正常编译:
public static void main(String[] args) {
Date day = new Date();
day.printDate();
}
输出
年: 0 月: 0 日: 0
为何会如此呢?请看下一小节的讲解:
5.2 构造方法
5.2.1 什么是构造方法
在了解对象是如何初始化之前,我们得先了解一个新的知识,叫构造方法。
构造方法(也称为构造器)是一个特殊的成员方法,名字必须与类名相同,在创建对象时,由编译器自动调用,给成员变量初始化,并且在整个对象的生命周期内只调用一次。
注意:
构造方法的作用是给对象中的成员进行赋值,并不负责给对象开辟空间。
如果用户没有显式定义,编译器会生成一份默认的构造方法,生成的默认构造方法一定是无参的。
但如果用户自定义了一个含参的构造类型,那么编译器就不再生成默认的构造方法,所以以后每次实例化一个新的对象都必须带参数。
下面是在 Date 类中写入构造方法:
// 构造方法:
// 名字与类名相同,没有返回值类型,设置为void也不行
// 一般情况下使用public修饰
// 在创建对象时由编译器自动调用,并且在对象的生命周期内只调用一次
public Date(int year , int month, int day){
this.year = year;
this.month = month;
this.day = day;
}
5.2.2 构造方法中的 this 引用
this 必须放在第一行。
//该构造方法无参
public Date(){
this(1999,6,18);//但这里使用this,便可以调用其他带有三个参数的构造方法
}
//比如这个:
public Date(int year , int month, int day){
this.year = year;
this.month = month;
this.day = day;
}
注意不要过度使用 this,以免造成闭环。比如:
public Date(){
this(1900,1,1);
}
public Date(int year, int month, int day) {
this();
}
5.2.3 构造方法的重载
构造方法可以有多个,虽然方法名都一样,但是参数不同,彼此构成重载的关系。具体使用哪个构造方法,视用户输入的参数而定。
public Date(int year , int month, int day){
this.year = year;
this.month = month;
this.day = day;
}
public Date(){
this.year = 1998;
this.month = 8;
this.day = 9;
}
5.3 默认初始化
在这里就可以回答 5.1 节提出的问题了。而这一切与 new 关键字有关。
Date d = new Date(2022, 11, 10);
在程序层面只是简单的一条语句,在 JVM 层面需要做好多事情,下面简单介绍下:
1. 检测对象对应的类是否加载了,如果没有加载则加载
2. 为对象分配内存空间
3. 处理并发安全问题
比如:多个线程同时申请对象,JVM 要保证给对象分配的空间不冲突
4. 初始化所分配的空间,即:对象空间被申请好之后,对象中包含的成员已经设置好了默认值。
如果是基本数据类型:
类型 | 默认值 |
byte | 0 |
short | 0 |
int | 0 |
long | 0L |
float | 0.0f |
double | 0.0 |
char | /u0000 |
boolean | false |
如果数组中存储元素类型为引用类型,默认值为 null
之后如果写了带参的构造方法,那么就会再一次赋值。
5. 设置对象头信息
6. 调用构造方法,给对象中各个成员赋值
5.4 就地初始化
在声明成员变量时,就直接给出了初始值。
public class Date {
public int year = 1900;
public int month = 1;
public int day = 1;
public Date(){
}
public Date(int year, int month, int day) {
}
public static void main(String[] args) {
Date d1 = new Date(2021,6,9);
Date d2 = new Date();
}
}
注意:代码编译完成后,编译器会将所有给成员初始化的这些语句添加到各个构造函数中