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,d3Date 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/1Date 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.不能形成环: