Java SE(6)——类和对象
1.初始面向对象
1.1 什么是面向对象
Java是一门纯面向对象的编程语言(Object Oriented Program,简称OOP),在面向对象的世界里,一切皆为对象。面向对象是解决问题的一种思想,主要依靠对象之间的交换来完成一件事情
1.2 面向过程&面向对象的区别
这里以洗衣服为例:
1.手洗衣服过程
按照以上方式洗衣服,我们必须要亲自完成每一个阶段,其中每个阶段的处理方式也要我们自己来把握,比如:加水量,洗衣粉的量,搓衣服的力度等等。如果某个以后不洗衣服,改成洗鞋子,那么以上的流程就不一样了。按照这种方式来写代码,将来对代码进行扩展和维护就比较困难
2.现在洗衣服的过程
现在洗衣服会经常用到洗衣机来完成,那么使用洗衣机来洗衣服一般涉及四个对象:人,衣服,洗衣机,洗衣粉
洗衣服的过程如下:
人把衣服放进洗衣机,加入洗衣粉,启动洗衣机
以上整个过程中,主要是人,衣服,洗衣机,洗衣粉这四个对象在交互。人不需要关注洗衣机是怎么洗衣服的,人只需要把衣服放进去,加洗衣粉,然后启动开关就行了。
换言之,人只需要面向洗衣机这个对象,就能完成洗衣服,而不需要面向洗衣服的各个过程,这就是面向对象和面向过程的区别
2.类的定义和使用
面向对象编程关注的是对象,对象就相当于是生活中的实物,比如:洗衣机。但是计算机不知道什么是洗衣机,这需要我们开发人员来告诉计算机什么是洗衣机
上图左边的信息就是对洗衣机的描述,我们可以用这种描述信息来表示一个具体的实物,这个过程称为抽象。所谓抽象,指的是从具体事物中抽取共同的、本质的特征,忽略次要的、非本质的特征。但是这些简化的抽象结果(图片左侧的描述信息)也不能被计算机识别,开发人员就需要使用某种面向对象的编程语言来进行描述,比如:Java
2.1 简单认识类
**类是对一个实体(对象)来进行描述的。**主要描述该实体(对象)有哪些属性,有哪些功能,描述完成后计算机就可以识别了。以上述洗衣机为例,我们使用左边的描述信息来抽象一台洗衣机,但是计算机并不能识别这些描述信息,我们可以将这些信息放进一个类里面,然后计算机就可以识别了
2.2 类的定义格式
定义类的具体语法如下:
field; //属性或者成员变量method; //行为或者成员方法 } ```
class
是定义类使用的关键字,ClassName是类的名字,{}中是类的主体,类的主体中定义了类有哪些属性,有哪些方法
下面我们来定义一个类来描述一台洗衣机:
public class WashingMachine {//品牌public String brand;//型号public String model;//大小public Double size;//重量public Double weight;//价格public Double price;//洗衣服public void wash() {System.out.println("Washing clothes...");}//脱水public void dehydrate() {System.out.println("Dehydrating clothes...");}
}
上述过程中,我们使用Java语言定义了一个洗衣机类,通过javac编译后转换为字节码文件,再由JVM翻译就可以被计算机识别了(Java文件如何被计算机识别,请看博文Java虚拟机——JVM(Java Virtual Machine)解析一)
注意1:
- 类名采用大驼峰形式。大驼峰:每个单词的首字母大写
- 类的成员变量/方法暂时使用public修饰。public是访问限定修饰符之一,后面讲到继承时再介绍
- 类的成员变量/方法暂时不使用static修饰
下面再来定义两个类来熟悉一下类的定义
(1)定义Dog类
class Dog{public String name;public int age;public String color;//public void eat(){System.out.println("eat food");}public void bark(){System.out.println("汪汪汪...");}
}
(2)定义学生类
class Student{public String name;public int age;public int id;public double score;public String height;public String weight;//public void exam(){System.out.println("参加考试");}public void doHomework(){System.out.println("写作业");}
}
注意2:
- 一个Java文件建议定义一个类
- 使用public修饰的类在一个Java文件中只能存在一个
- 使用public修饰的类的类名和所在文件的文件名相同
3.类的实例化
3.1 什么是实例化?
在上述代码中,我们定义了WashingMachine、Dog和Student类,就相当于在计算机中定义了这三个新的数据类型。和int、float等基本数据类型一样,只不过int和float是Java内置的2类型,而WashingMachine这些是用户自定义的类型,我们通过这些自定义类型就可以定义实例/对象。而通过类来定义实例/对象的过程,称之为实例化,在Java中需要借助new关键字来完成实例化
3.2 如何访问对象中的成员变量/方法?
public static void main(String[] args) {Dog dog = new Dog();dog.name = "Dog";System.out.println(dog.name);dog.age = 18;System.out.println(dog.age);dog.color = "blue";System.out.println(dog.color);dog.eat();dog.bark();} } ```
运行结果如下:
注意:
- Java中使用
.
来访问对象中的成员变量/方法 - 同一个类可以实例化多个对象
3.3 类和对象的关系
类
是一种抽象的数据类型,它可以实例化一组具有相同属性(成员变量)和行为(成员方法)的对象。类可以看作是一个蓝图或者模板,用于创建对象
对象
是类的具体实现,每个对象都可以给自己的属性(成员变量)赋予独特的值
房屋和具体的房子
- 类(Class):房屋(House)
属性(Attributes):房间数量、面积、楼层数、地址
方法(Methods):建造、入住、出售- 对象(Object):具体的房子,如“张三的家”
实例化(Instantiation):张三的家是一个具体的房子,它有5个房间,面积是150平方米,3层楼,地址是“北京市朝阳区XX路”
行为(Behavior):张三的家可以被建造、入住或出售
4.this关键字
4.1 为什么要使用this?
这里举个例子
预期结果:2025-5-1
实际结果:null-null-null
原因分析:
public void setDate(String year, String month, String day) {year = year;month = month;day = day;}
这个方法中,形参的参数名和MyDate类的成员变量吗名一致,当给year赋值时,编译器无法区分year是成员变量还是方法的局部变量,在这种情况下会优先调用局部变量,相当于是自己给赋值
public void printDate(){System.out.println(year + "-" + month + "-" + day);}
这个方法中没有形参,调用的参数(year/month/day)只能是MyDate类的成员变量,由于setDate方法没有给成员变量赋值,所以printDate打印的时候才会打印出null-null-null。分析到这里就明白了,问题在于当方法的形参名和类的成员变量名一致时,该如何进行区分呢?这就要借助this关键字
4.2 this是什么?
this引用指向当前对象(成员方法运行时调用该成员方法的对象),在成员方法中所有成员变量的操作,都是通过该引用去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完 成
通过代码来解释一下:
当编译器去调用成员变量的时候都会通过this关键字来调用。this引用指向当前对象
,这句话又该怎么理解呢?以上面的代码为例
这里的当前引用指的就是myDate。换言之,this = myDate。
public void setDate(String year1, String month1, String day1) {//this.year的含义是:myDate指向的对象中的year//this.year = year1;这段代码的意思是:把 方法形参year1 赋值给 myDate指向的对象中的yearthis.year = year1;this.month = month1;this.day = day1;}
修改之后的代码如下:
public class MyDate {public String year;public String month;public String day;//public void setDate(String year1, String month1, String day1) {//使用this关键字来区分哪个是成员变量,哪个是方法形参this.year = year1;this.month = month1;this.day = day1;}//public void printDate(){System.out.println(this.year + "-" + this.month + "-" + this.day);}//public static void main(String[] args) {MyDate myDate = new MyDate();myDate.setDate("2025","5","1");myDate.printDate();}
}
注意:
在刚学习Java的使用就提醒过,使用一个变量之前需要给该变量赋初值,否则会报错
但是:
打印结果是null-null-null,并没有报错。这是怎么回事呢?
原因是,对于成员变量来说,如果没有进行初始化,会有⼀个对应的默认值
String类型是引用类型,会赋予默认值null,所以不会报错
默认值遵循如下规则:
数据类型 | 默认值 |
---|---|
byte | 0 |
short | 0 |
int | 0 |
long | 0 |
boolean | FALSE |
float | 0.0f |
double | 0 |
引用类型(reference) | null |
5.对象的构造及初始化
上文已经介绍到,成员变量在没有赋初值的时候会赋予默认值,这就是对象的默认初始化。说实话,我个人认为这是防止程序报错而出现的一个措施,不算正常的初始化操作,那么对象的初始化有哪些方式呢?
5.1 就地初始化
在声明成员变量时,就直接给出了初始值
public class MyDate {public String year = "2025";public String month = "5";public String day = "1";//public void setDate(String year1, String month1, String day1) {this.year = year1;this.month = month1;this.day = day1;}//public void printDate(){System.out.println(this.year + "-" + this.month + "-" + this.day);}//public static void main(String[] args) {MyDate myDate = new MyDate();myDate.printDate();}
}
5.2 自定义方法初始化
用户自定义一个方法来进行成员变量的初始化操作
public class MyDate {public String year;public String month;public String day;//public void setDate(String year1, String month1, String day1) {this.year = year1;this.month = month1;this.day = day1;}//public void printDate(){System.out.println(this.year + "-" + this.month + "-" + this.day);}//public static void main(String[] args) {MyDate myDate = new MyDate();myDate.setDate("2025","5","1");myDate.printDate();}
}
5.3 构造方法初始化
上面介绍的两种初始化方法,说实话都不是很常见。就地初始化灵活性太差;自定义方法初始化还需要创建并调用自定义的方法,不太方便
那么,有没有既灵活又方便的初始化方式呢?
那就是使用构造方法来初始化
5.3.1 什么是构造方法?
构造方法(也称为构造器)是一个特殊的成员方法,方法名必须和类型一致,在创建对象时由编译器自动调用,并且在整个对象的生命周期内构造方法只会调用一次
public class MyDate {public String year;public String month;public String day;//public MyDate(String year, String month, String day) {this.year = year;this.month = month;this.day = day;}//public void printDate(){System.out.println(this.year + "-" + this.month + "-" + this.day);}//public static void main(String[] args) {MyDate myDate = new MyDate("2025","5","1");myDate.printDate();}
}
在使用new关键字实例化对象的时候,编译器会根据括号内的参数数量和类型来判断调用哪个构造方法。这段代码中,new对象的时候一共传递了三个String类型的参数,编译器就会查找哪个构造方法的参数列表能与之对应
5.3.2 构造方法注意事项&使用规范
注意:
- 1.构造方法名必须和类名一致
- 2.没有返回值,设置void也不行
- 3.创建对象时由编译器自动调用,并且在对象的生命周期内只调用一次
- 4.构造方法可以重载(用户根据自己的需求创建不同参数列表的构造方法)
- 一般来说,构造方法使用public修饰
注意6:
当用户没有手动添加构造方法的时候,编译器会默认生成一份无参的构造方法。当用户显式地添加构造方法(不论有参还是无参)的时候,编译器将不再默认生成构造方法
左图中,用户没有显式地添加构造方法,编译器会默认生成一个无参的构造方法,当用户需要实例化一个不赋初值地对象时,就能匹配到该无参构造方法;右图中,用户显式地添加一个构造方法,参数列表有三个形参,当用户需要实例化一个不赋初值地对象时,由于编译器没有默认生成一个无参的构造方法,导致匹配失败。所以,构造方法也有一个不成文的使用规范:当用户显式地添加构造方法后,不论无参的构造方法有没有用,都建议添加上
5.3.2 构造方法中使用this来简化代码
public class MyDate {public String year;public String month;public String day;//public MyDate() {this("2025","5","1");}public MyDate(String year, String month, String day) {this.year = year;this.month = month;this.day = day;}//public void printDate(){System.out.println(this.year + "-" + this.month + "-" + this.day);}
}
class Test{public static void main(String[] args) {MyDate myDate = new MyDate();myDate.printDate();}
}
在上述代码中,new对象的时候会默认调用无参的构造方法,无参的构造方法内使用了this()代码,编译器就会根据括号内的参数数量和类型去查找参数列表与之匹配的构造方法
注意:
- this()必须是构造方法中的第一条语句
- this()不能形成环的调用