博客主页:爱敲代码的小杨.
✨专栏:《Java SE语法》
❤️感谢大家点赞收藏⭐评论✍,您的三连就是我持续更新的动力❤️
面向对象程序设计(object-oriented programming,OOP),是当今主流的程序设计范畴,它取代了20世纪70年代的”结构化“或过程式编程技术。
面向对象的程序是由对象组成的,每个对象包含对用户公开的特点功能部分和隐藏的实现部分。
类(class)是构造对象的模板或蓝图。由类构造(construct)对象的过程称为类的实例(instance)。
封装(encapsulation,有时称为数据隐藏)是处理对象的一个重要概念。封装就是将数据和行为组合在一个包中,并对对象的使用者隐藏具体的实现方式。对象的数据称为实例字段(instance field),操作数据的过程称为方法(method)。
实现封装的关键在于,绝对不能让类中的方法直接访问其他类的实例字段。程序只能通过对象的方法与对象数据进行交互。封装给对象赋予了“黑盒”特征,这是提高重用性和可靠性的关键。这意味着一个类可以完全改变存储数据的方式,只要仍旧使用同样的方法操作数据,其他对象就不会知道也不用关心这个类所发生的变化。
要想使用OOP,一定要清楚对象的三个主要特性:
同一个类的所有对象实例,由于支持相同的行为而具有家族式的相似性。对象的行为是可调用的方法来定义的。
在类之间,最常见的关系有:
依赖(dependence),即“uses-a”关系,是一种最明显、最常见的关系。如果一个类的方法使用或操纵另一个类的对象,我们就说一个类依赖于另一个类。
聚合(aggregation),即“has-a”关系,很容易理解,因为这种关系很具体。包含关系意味着类A的对象包含类B的对象。
继承(inheritance),即“is-a”关系,表示一个更特殊的类与一个更一般类之间的关系。
面向对象程序设计关注的是对象,而对象是现实生活中的实体。
类是用来对一个实体(对象)来进行描述,主要描述该实体(对象)具有哪些属性,哪些功能,描述完成后计算机就可以识别了。
在java
中定义 类需要使用class
关键字,具体语法如下:
class ClassName{
field;// 字段 或 成员变量
method;// 方法 或 成员方法
}
class
为定义类的关键字,ClassName
为类的名字,{}
中为类的主体。
类中包含的内容称为类的成员。属性主要是用来描述类的,称之为类的成员属性或者类**成员变量**。方法主要说明类具有哪些功能,称为类的成员方法。
class Dog {
// 狗的属性
public String name;// 狗的名字
public String color;// 狗的颜色
// 狗的行为
public void barks() {
System.out.println(name + "在旺旺叫");
}
public void wag() {
System.out.println(name + "在摇尾巴");
}
}
注意事项:
main
方法所在的类一般要使用public
修饰。public
修饰的类必须要和文件相同。定义一个类,就相当于在计算机中定义了一种新的类型,与int
,double
类似,只不过int
和double
是java
语言自带的内置类型,而类是用户自定义了一个新的类型,比如上述的Dog
类。它就是类(一种新定义的类型)有了这些自定义的类型之后,就可以使用这些类来定义实例(或者称为对象)。
用类类型创建对象的过程,称为类的实例化,在java
采用new
关键字,配合类名来实例化对象。
public class Test {
public static void main(String[] args) {
Dog dog1 = new Dog();
dog1.name = "大黄";
dog1.color = "黄色";
dog1.barks();
dog1.wag();
Dog dog2 = new Dog();
dog2.name = "哈士奇";
dog2.color = "白黑色";
dog2.barks();
dog2.wag();
}
}
// 运行结果:
大黄在旺旺叫
大黄在摇尾巴
哈士奇在旺旺叫
哈士奇在摇尾巴
注意事项:
new
关键字用于创建一个对象的实例。.
来访问对象中属性和方法。先看一个日期类的例子:
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 Test2 {
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.setDay(2020,9,15);
d2.setDay(2020,9,16);
d3.setDay(2020,9,17);
// 打印日期中的内容
d1.printDate();
d2.printDate();
d3.printDate();
}
}
以上代码定义了一个日期类,然后main方法中创建了三个对象,并通过Date类中的成员方法对对象进行设置和打
印,代码整体逻辑非常简单,没有任何问题。
但是细思之下有以下两个疑问:
形参名不小心与成员变量名相同
public void setData(int year, int month, int day){
year = year;
month = month;
day = day;
}
那函数体中到底是谁给谁赋值?成员变量给成员变量?参数给参数?参数给成员变量?成员变量参数?估计
自己都搞不清楚了。
三个对象都在调用setDate
和printDate
函数,但是这两个函数中没有任何有关对象的说明,setDate
和
printDate
函数如何知道打印的是那个对象的数据呢?
this引用指向当前对象(成员方法运行时调用该成员方法的对象),在成员方法中所有成员变量的操作,都是通过该引用去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
public void setData(int year, int month, int day){
this.year = year;
this.month = month;
this.day = day;
}
注意:this引用的是调用成员方法的对象。
public class Test {
public static void main(String[] args) {
Date d = new Date();
d.setData(2020,9,15);
d.printDate();
}
}
this
的类型:对应类类型引用,即哪个对象调用就是哪个对象的引用类型。this
只能在“成员方法”中使用。this
只能引用当前对象,不能再引用其他对象。this
是“成员方法”第一个隐藏的参数,编译器会自动传递,在成员方法执行时,编译器负责将调用成员方法对象的引用传递给该成员方法,this
负责来接收。在java
方法内部定义一个局部变量,必须初始化,否则就会编译失败。
要上诉代码编译成功,只需要是在使用a
之前,给a
设置一个初始值。如果是对象:
public class Test {
public static void main(String[] args) {
Date d = new Date();
d.setData(2020,9,15);
d.printDate();
}
}
// 代码正常通过编译
需要调用之前写的setDate
方法才可以将具体的日期设置到对象中。通过上述例子发现两个问题:
setDate
方法设置具体日期,比较麻烦,那对象该如何初始化?构造方法(也称为构造器)是一种特殊的成员方法,名字必须与类名相同,在创建对象时,编译器自动调用并且在整个对象的生命周期内调用一次。
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;
System.out.println("Date(int year, int month, int day)被调用了");
}
public void printDate(){
System.out.println(this.year + "/" +this.month + "/" + this.day);
}
}
public class Test {
public static void main(String[] args) {
Date d = new Date(2020,9,15);
d.printDate();
}
}
注意:构造方法的作用就是对对象中的成员进行初始化,并不负责给对象开辟空间。
new
操作符一起调用class Date{
public int year;
public int month;
public int day;
// 构造方法
// 无参构造方法
public Date() {
}
// 带3个参数的构造方法
public Date(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);
}
}
public class Test {
public static void main(String[] args) {
Date d = new Date(2020,9,15);
d.printDate();
}
}
// 上述两个构造方法:名字相同,参数列表不同,因此构成了方法重载。
如果用户没有显式定义,编译器会生成一份默认的构造方法,生成的默认构造方法一定是无参的
class Date{
public int year;
public int month;
public int day;
public void printDate(){
System.out.println(this.year + "/" +this.month + "/" + this.day);
}
}
public class Test {
public static void main(String[] args) {
Date d = new Date();
d.printDate();
}
}
上述Date
类,没有定义任何构造方法,编译器会默认生成一个无参构造器。
构造方法中,可以通过this调用其他构造方法来简化代码
class Date{
public int year;
public int month;
public int day;
public Date() {
this(2005,5,9);// 必须是构造方法的第一条语句
}
public Date(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);
}
}
public class Test2 {
public static void main(String[] args) {
Date d = new Date();
d.printDate();
}
}
注意:this(…)必须是构造方法的第一条语句。
在上文中提出的第二个问题:为什么局部变量在使用时必须要初始化,而成员变量可以不用呢?
class Date{
public int year;
public int month;
public int day;
public Date(int year, int month, int day) {
// 成员变量在定义时,并没有给初始值, 为什么就可以使用呢?
System.out.println(this.year);
System.out.println(this.month);
System.out.println(this.day);
}
public void printDate(){
System.out.println(this.year + "/" +this.month + "/" + this.day);
}
}
public class Test2 {
public static void main(String[] args) {
Date d = new Date(2023,9,17);
}
}
要搞清楚这个过程,就需要知道new
关键字背后所发生的一些事情:
Date d = new Date(2023,9,17);
在程序层面只是简单的一条语句,在JVM层面需要做好多事情,下面简单介绍一下:
检测对象对应的类是否加载了,如果没有加载则加载
为对象分配内存空间
处理并发安全问题
比如:多个线程同时申请对象,JVM
要保证给对象分配的空间不冲突
初始化所分配的空间
即:对象空间被申请好之后,对象中包含的成员已经设置好了初始值,比如:
数据类型 | 默认值 |
---|---|
byte | 0 |
char | ‘\u0000’ |
short | 0 |
int | 0 |
long | 0L |
boolean | false |
float | 0.0f |
double | 0.0 |
reference | null |
设置对象头信息
调用构造方法,给对象中各个成员赋值
在声明成员变量的时候,就可以给出初始值。
class Date{
public int year = 2021;
public int month = 5;
public int day = 19;
public Date() {
}
public Date(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);
}
}
public class Test2 {
public static void main(String[] args) {
Date d = new Date(2023,9,17);
d.printDate();
Date d1 = new Date();
d1.printDate();
}
}
// 运行结果
// 2023/9/17
// 2021/5/19
注意:代码编译完成后,编译器会将所有给成员初始化的这些语句添加到各个构造函数中。