1.面向对象的初步认知
1.1 什么是面向对象
Java是一门面向对象的语言,在面向对象的世界里,一切皆为对象。面向对象是解决问题的一种思想,主要依靠对象之间的交互完成一件事情。用面向对象的思想来涉及程序,更符合人们对事物的认知,对于大型程序的设计,扩展及维护都非常友好。
1.2 面向对象与面向过程
1.2.1 传统洗衣过程
传统的方式:注重的是洗衣服的过程,少了一个环节可能都不行。
而且不同衣服洗的方式、时间长度、拧干方式都不同,处理起来就比较麻烦。如果将来要洗鞋子,那就是另一种方式了。
如果按照这种方式来写代码,将来扩展或者维护起来会比较麻烦。
1.2.2 现代洗衣过程
总共有四个对象:人、衣服、洗衣粉和洗衣机。
整个洗衣服的过程:人将衣服放进洗衣机、倒入洗衣粉,启动洗衣机,洗衣机就完成洗衣过程并且甩干。
整个过程主要是:人、衣服、洗衣粉和洗衣机四个对象之间的交互完成,人不需要关心洗衣机具体是如何洗衣服的,是如何甩干的。
注意:面向对象和面向过程不是一门语言,而是一种解决问题的办法,没有好坏之分,都有其善于运用的场景。
2.类定义和使用
面向对象程序关注的是对象,而对象是现实生活中的实体,比如:洗衣机。但是洗衣机计算机并不认识,需要开发人员告诉计算机什么是洗衣机。
上图右侧就是对洗衣机简单的描述,该过程称为对洗衣机对象(实体)进行抽象(对一个复杂事物的重新认知),但是这些简化的抽象结果计算机也不能识别,开发人员可以采用某种面向对象的编程语言来进行描述,比如:Java语言。
2.1 简单认识类
类是用来对一个实体(对象)来进行描述,主要描述该实体(对象)具有哪些属性(外观尺寸等),哪些功能(用来干啥),没描述完成后计算机就可以识别了。
比如:洗衣机,它是一个实体,在Java中可以将其看成是一个类别。
- 属性:产品品牌,型号,产品重量,外观尺寸,颜色......
- 功能:洗衣,烘干,定时......
在Java语言中,如何对上述的洗衣机类来进行定义呢?
2.2 类的定义格式
在Java中定义类时需要用到class关键字,具体语法如下:
//创建类
class ClassName{
filed; //字段(属性)或者成员变量
method; //行为或者成员方法
}
class为定义类的关键字,ClassName为类的名字(由自己命名,需要采用大驼峰定义),{}中为类的主体。
类中包含的内容称为类的成员。属性主要是用来描述类的,称之为类的成员属性或者类成员变量。方法主要是用来说明类有哪些功能,称为类的成员方法。
类、属性和方法的关系为:
public class WashMachine { public String brand; //品牌 public String color; //颜色 public double weight; //重量 public void washClothes(){ //洗衣服 System.out.println("洗衣功能"); } public void dryClothes(){ //脱水 System.out.println("脱水功能"); } public void setTime(){ //定时 System.out.println("定时功能"); } }
1~18行:定义的一个名为WashMashine的类,里面包含类的成员属性和成员方法。
2~4行:类的成员属性,分别为所定义类的品牌、颜色和重量。
6~8行、10~12行、14~16行:三组均为所定义类里的成员方法。其内面语句的作用分别为打印出“洗衣功能”、“脱水功能”和“定时功能”。
2.3 练习
2.3.1 定义一个狗类
public class Dog {
public String name;
public String color;
//狗的属性
public void barks(){ //狗叫行为
System.out.println(color+name+"汪汪汪~~~");
}
public void wag(){ //狗的摇尾巴行为
System.out.println(color+name+"摇尾巴~~~");
}
}
2.3.2 定义一个学生类
public class Student {
public String name;
public String gender;
public short age;
public double score;
public void DoClass(){
System.out.println("按时上课,不要迟到,如果请假,及时去补");
};
public void DoHomeWork(){
System.out.println("教务系统,完成作业,一旦拖沓,神仙难救");
}
public void Exam(){
System.out.println("考试目的,了解学情,人人必考,暴露问题");
};
}
注意:
- 一般一个文件只定义一个类。
- public修饰的类必须要和文件名相同。
- 不要轻易去修改public修饰的类的名称,如果要修改,通过开发工具修改。
3.类的实例化
3.1 什么是实例化
定义了一个类,就相当于在计算机中定义了一种新的类型,与int,double类似,只不过int和double是Java语言自带的内置类型,而类是用户自定义的一个新的类型,比如上述的:Dog类和Student类。它们都是类(一种新定义的类型),有了这些自定义的类型之后,就可以使用这些类来定义实例(或者称为对象)。
用类类型创建对象的过程,称为类的实例化,在Java中采用new关键字,配合类名来实例化对象。
public class Main {
public static void main(String[] args) {
Dog dog1 = new Dog(); //通过new实例化对象
dog1.name = "阿黄";
dog1.color = "黑黄";
dog1.barks(); //输出结果:黑黄阿黄汪汪汪~~~
dog1.wag(); //输出结果:黑黄阿黄摇尾巴~~~
Dog dog2 = new Dog(); //通过new实例化对象
dog2.name = "赛虎";
dog2.color = "棕黄";
dog2.barks(); //输出结果:棕黄赛虎汪汪汪~~~
dog2.wag(); //输出结果:棕黄赛虎摇尾巴~~~
}
}
注意:
- new关键字用于创建一个对象的实例。
- 可以new多个对象,但不是无限多(内存限制)。
- 使用 . 来访问对象中的属性和方法。
3.2 类和对象的说明
1.类只是一个模型一样的东西,用来对一个实体进行描述,限定了类有那些成员。
2.类是一种自定义的类型,可以用来定义变量。
3.一个类可以实例化出多个对象,实例化出的对象占用实际的物理空间,存储类成员变量。
4.做个比方:类实例化出对象就像现实中使用建筑设计图建造出房子,类就是设计图,只要设计出需要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象才能实际存储数据,占用物理空间。
4.this引用
4.1 为什么要有this引用
先看一个日期的例子:
public 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 static void main(String[] args) {
//构造三个日期类型的对象d1,d2,d3
Date d1 = new Date();
Date d2 = new Date();
Date d3 = new Date();
//对d1,d2,d3的日期设置
d1.SetDate(2024,3,19);
d2.SetDate(2024,3,20);
d3.SetDate(2024,3,21);
//打印日期中的内容
d1.PrintDate();
d2.PrintDate();
d3.PrintDate();
}
}
以上代码定义了一个日期类,然后main方法中创建了三个对象,并通过Date类中的成员方法对对象进行设置和打印。
但细思之后有以下两个疑问:
1.若形参名不小心与成员变量名相同:
public void setDate(int year, int month, int day){
year = year;
month = month;
day = day;
}
那函数体中到底是谁给谁赋值?成员变量给成员变量?参数给参数?成员变量给参数?参数给成员变量?估计自己都搞不清楚了。
2.三个对象都在调用setDate和printDate函数,但是这两个函数中没有任何有关对象的说明,setDate和printDate函数如何知道打印的是哪个对象的数据呢?
一切让this引用来揭开这层神秘的面纱。
4.2 什么是this引用
this引用指向当前对象(成员方法运行时调用该成员方法的对象),在成员方法中所有成员变量的操作,都是通过该引用去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
public class Date {
public int year;
public int month;
public int day;
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);
}
}
注意:this引用的是调用成员方法的对象。
public static void main(String[] args) {
Date d = new Date();
d.setDate(2024,3,20);
d.printDate();
}
下图1:通过调试可以看到,被调用的方法地址均与被引用对象相同。
4.3 this引用的特性
1.this的类型:对应类型引用,即哪个对象调用就是哪个对象的引用类型。
2.this只能在“成员方法”中使用。
3.在“成员方法”中,this只能引用当前对象,不能再引用其他对象。
4.this是“成员方法”第一个隐藏的参数,编译器会自动传递,在成员方法执行时,编译器会负责将调用成员方法对象的引用传递给该成员方法,this负责来接收。
在代码层面来简单演示 ---> 注意:下图右侧中的Date类也是可以通过编译的。
5.对象的构造及初始化
5.1 如何初始化对象
通过前面的学习可以知道,在Java方法内部定义一个局部变量时,必须要初始化,否则会编译失败。
public static void main(String[] args) {
int a;
System.out.println(a);
}
//编译失败 Error:java: 可能尚未初始化变量a
若要上述代码通过编译,非常简单,只需要在正式使用之前,给a设置一个初始值即可。但如果是对象:
public static void main(String[] args) {
Date d = new Date();
d.setDate(2024,3,20);
d.printDate();
}
//代码可正常通过编译
需要调用之前写的setDate方法才能将具体的日期设置到对象中。
通过上述例子发现两个问题:
1.每次对象创建好后调用setDate方法设置具体日期,比较麻烦,那对象该如何初始化?
2.局部变量必须要初始化才能使用,为什么字段声明之后没有给值依然可以使用?
5.2 构造方法
5.2.1 概念
构造方法(也称为构造器)是一个特殊的成员方法,名字必须与类名相同,在创建变量时,由编译器自动调用,并且在整个对象的生命周期内只调用一次。
public class Date {
public int year;
public int month;
public int day;
//构造方法
//名字与类名相同,没有返回值类型,设置为void也不行
//一般情况下使用public修饰
//在创建对象时由编译器自动调节,并且在对象的生命周期内只调用一次
public Date(int year, int month, int day){
this.year = year;
this.month = month;
this.day = day;
}
public void printDate(){
System.out.println(year+"/"+month+"/"+day);
}
public static void main(String[] args) {
//此处创建了一个Date类型的对象,并没有显示调用构造方法
Date d = new Date(2024,3,20); //Date()方法被调用了
d.printDate(); //输出 2024/3/20
}
}
注意:构造方法的作用就是对对象中的成员进行初始化,并不负责给对象开辟空间。
5.2.2 特性
1.名字必须与类名相同。
2.没有返回值类型,设置为void也不行。
3.创建对象时由编译器自动调用,并且在对象的生命周期内只调用一次(相当于人的出生,每个人只能出生一次)
4.构造方法可以重载(用户根据自己的需求提供不同参数的构造方法)
public class Date {
public int year;
public int month;
public int day;
//无参数的构造方法
public Date() {
this.year = 1900;
this.month = 1;
this.day = 1;
}
//带有三个参数的构造方法
public Date(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
public void printDate() {
System.out.println(year + "/" + month + "/" + day);
}
public static void main(String[] args) {
Date d1 = new Date();
d1.printDate(); //输出 1900/1/1
Date d2 = new Date(2024,3,20);
d2.printDate(); //输出 2024/3/20
}
}
}
上述两个构造方法:名字相同,参数列表不同,因此构成了方法重载。
5.如果用户没有显示定义,编译器会生成一份默认的构造方法,但其一定是无参的。
public class Date {
public int year;
public int month;
public int day;
public void printDate(){
System.out.println(year+"/"+month+"/"+day);
}
public static void main(String[] args) {
Date d = new Date();
d.printDate(); //输出 0/0/0
}
}
上述Date类中,没有定义任何构造方法,编译器会默认生成一个不带参数的构造方法。
注意:一旦用户定义,编译器则不再生成。
public class Date {
public int year;
public int month;
public int day;
public Date(int year, int month, int day){
this.year = year;
this.month = month;
this.day = day;
}
public void printDate(){
System.out.println(year+"/"+month+"/"+day);
}
public static void main(String[] args) {
//若编译器会自动生成构造方法,则生成的一定是无参的
//则此处创建的对象是可以通过编译的
//但实际情况是:编译器报错
Date d = new Date();
d.printDate();
}
}
6.构造方法中,可以通过this调用其他构造方法来简化代码。
public class Date {
public int year;
public int month;
public int day;
//无参构造方法——内部各个成员赋值初始化,
//该部分功能与三个参数的构造方法重复,
//此处可以在无参构造方法中通过this调用带有三个参数的构造方法,
//但是this(1900,1,1);必须是构造方法中的第一条语句
public Date() {
//System.out.println(year); 若取消注释,编译会失败
this(1900, 1, 1);
//this.year = 1900;
//this.month = 1;
//this.day = 1;
}
//带有三个参数的工作方法
public Date(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
}
注意:
1.this(...)必须是构造方法中的第一条语句:
2.不能形成环: