纸上学来终觉浅
绝知此事要躬行
1.初始面向对象
1.1认识面向对象
面向对象是解决问题的一种思想,Java就是采用了面向对象的思想,所以它就叫做面向对象的语言。在面向对象的世界里一切皆为对象,主要是靠对象的交互去完成一件事情。
1.2 面向对象和面向过程的区别
上面我们简单认识了一下面向对象,那么接下来我们就通过** 面向对象和面向过程的区别** 来理解面向对象。
大家应该都学过 c 语言了,大家都知道 c 语言采用了面向过程的思想,所以它也就是面向过程的语言了。在 c 语言的世界,做任何一件事情都需要自己一步一步去完成,假设你想对一个数组进行排序,那么你就得写一个数组排序的方法。
比如现在需要完成洗衣服,那么我们就一起看看在面向过程的世界里和面向对象的世界里有何区别吧!
**①洗衣服在面向过程的世界里 **
手洗衣服:注重的是洗衣服的过程,少了一个环节可能都不行。 而且不同衣服洗的方式,时间长度,拧干方式都不同,处理起来就比较麻烦。比如:白色的衣服可能就不能跟彩色衣服放在一起洗,鞋子可能就跟衣服的洗法不一样。
面向过程的方式来写代码,将来扩展或者维护起来会比较麻烦。
**②洗衣服在面向对象的世界里 **
在面向对象的世界里,一切皆为对象,人是一个对象,衣服是一个对象,洗衣粉是一个对象,洗衣机又是一个对象。人把衣服、洗衣粉放入洗衣机启动开关即可,洗衣机将自动把衣服洗好,人不需要去关注洗衣服的过程,以及洗衣机是怎么来洗衣服,如何来甩干的。
通过人、衣服、洗衣粉、洗衣机这些对象之间的交互来完成的。
注:面向过程和面相对象并不是一门语言,而是解决问题的方法,没有那个好坏之分,都有其专门的应用场景
**注:面向对象的程序设计中关注的是对象 **
2.类的定义和使用
2.1 认识类
面相对象程序设计关注的是对象,而对象是现实生活中的实体
例如:电视。计算机并不认识什么是电视,就需要开发人员告诉它什么是电视。
那么开发人员就需要对电视简单的描述,描述它的品牌、大小、功能,该过程称为对电视对象(实体)进行抽象(对一个复杂事物的重新认知),那抽象出来的就称为类。但是这些简化的抽象结果计算机还是不能识别,开发人员可以采用某种面相对象的编程语言来进行描述,比如:Java语言。
通过上面的例子,我们也就知道了类是用来对一个实体(对象)来进行描述的,主要描述该实体(对象)具有哪些属性(品牌外观尺寸等),哪些功能(用来干什么的),描述完成后计算机就可以识别了
上述的电视的
- 属性: 海尔品牌、70寸屏幕
- 功能:看电视、玩游戏、投影
现在想必大家都知道如何描述一个实体了,对一个实体的描述其实就是抽象出一个类,那么接下来我们就来学如何定义一个类
2.2 类的定义格式
在 java 中定义类时需要用到 class 关键字
类的定义格式:
- class:定义类的关键字
- ClassName:类名
- {}:类的主体
类的主体中包含的内容称为类的成员,类的成员可以分为成员属性(也称为成员变量),还可以包含成员方法
- 成员属性:也就是我们上面讲的电视品牌尺寸,可以用变量存储的
- 成员方法:也就是就是我们上面讲的电视功能,需要用方法去实现
我们现在会了如何定义类,那我们就一起用Java语言将上述的电视,进行抽象出来成一个类
class Television {
//属性主要是用来描述类的,称为类的成员属性或者类的成员变量
public String brand = "Haier";
public int size = 70;
//方法主要说明类具有哪些功能,称为类的成员方法
public void fun1() {
System.out.println("看电视");
}
public void fun2() {
System.out.println("玩游戏");
}
public void fun3() {
System.out.println("投影");
}
}
这样我们就将电视抽象成了一个类,经过** javac **编译之后形成 .class 文件,在 JVM 的基础上计算机就可以识别了。
注:
- 类名采用大驼峰
- 方法名采用小驼峰
- public修饰的类名通常要与文件名一致 ,main方法所在的类一般要使用public修饰
- 一般一个文件当中只定义一个类
- 不要轻易去修改public修饰的类的名称,如果要修改,通过开发工具修改
通过开发工具修改 public 修饰的类的名称:右击需要改的 Java 文件,在弹出的下拉框中选择 Refactor ,然后在弹出的框中选择 Rename
3.类的实例化
3.1 实例化是什么
我们用一个 int 类型定义了一个变量,就相当于用 Java 中的内置类型定义了一个变量。那我们自定义了一个类,那么这个类就相当于我们自定义的一个类型,然后用这个类的类型创建了一个对象就相当于类的实例化
**在 java 中采用 new 关键字,配合类名来实例化对象 **
那我们接下来我们就用刚刚定义的电视类,来实例化一个电视对象:
class Television {
//属性主要是用来描述类的,称为类的成员属性或者类的成员变量
public String brand;
public int size;
//方法主要说明类具有哪些功能,称为类的成员方法
public void fun1() {
System.out.println(brand+"看电视");
}
public void fun2() {
System.out.println(brand+"玩游戏");
}
public void fun3() {
System.out.println(brand+"投影");
}
}
public class Test {
public static void main(String[] args) {
Television television = new Television();//这样我们就实例化了一个电视对象
television.brand = "Haier";
television.size = 70;
television.fun1();
television.fun2();
television.fun3();
}
}
运行结果:
实例化类的格式:类名 自定义实例化对象名 = new 类名();
注:
- new 关键字用于创建一个对象的实例
- 使用** . **来访问对象中的属性和方法
- 同一个类可以创建多个实例
3.2 类和对象的说明
类可以看做一个图纸,比如我们需要建一个房子,首先得先画一个图纸。
注:一个房子图纸可以建立好多房子
也就相当于**一个类可以实例化出多个对象,实例化出的对象占用实际的物理空间,存储类的成员变量 **
接下来我们来看一个动物类:
通过上面动物类以及实例化动物类,我们就可以形象的理解** 我们自定义的类 就相当于建房子的图纸,实例化对象就相当于建立起来的房子。一个图纸可以建立起来多个房子也就是一个类可以创建多个对象**
4.this引用
4.1 什么是 this 引用
class Data {
public int year;
public int month;
public int day;
public void setData(int y,int m,int d) {
year = y;
month = m;
day = d;
}
public void print() {
System.out.println(year+"-"+month+"-"+day);
}
}
public class Test {
public static void main(String[] args) {
Data data = new Data();
data.setData(2022,9,10);
data.print();
}
}
打印结果:
上述代码抽象出来一个** Data 类,然后在 Test 类主方法中实例化了一个 Data 类型的对象,通过对象调用了 data.setData 方法,然后把日期传给了setData 方法中的形参 y、m、d,然后把 y 赋值给了 year**,把 m 赋值给了 month,把 d 赋值给了 day
**那么大家再设想一下假如,setData 里面的形参名跟成员变量同名那又是赋值给谁了? **
如果把setData方法改成这样:
public void setData(int year,int month,int day) {
year = year;
month = month;
day = day;
}
打印结果:
如果将 setData 里面的形参名改成跟成员变量同名,那么我们调用 data.print 的时候打印就是成员变量的默认值,原因就是当局部变量与成员变量同名时,优先使用局部变量,赋值的时候是局部变量赋值给了自己
**注:当成员变量没有初始化的时候,里面存的是默认值 **
所以为了避免上述局部变量跟成员变量同名,想要将局部变量的值赋值给成员变量,那么就可以在**成员变量名的前面加上 this. **
public void setData(int year,int month,int day) {
this.year = year;
this.month = month;
this.day = day;
}
这样就可以将局部变量的值赋值给成员变量了,那么我们就通过 **data.print **来打印一下
运行结果:
**注:当局部变量与成员变量同名时,在局部区域里,变量名前加了 this. 就是成员变量 **
this引用指向当前对象(成员方法运行时调用该成员方法的对象),在成员方法中所有成员变量的操作,都是通过 this 引用去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
**this引用的是调用成员方法的对象 **
this 就可以相当于实例化的对象 data。this.year 等价于 data.year 。
只有实例化了类,才会占空间:
那么实例化类的对象是在堆上开辟的空间,实例化对象名在栈上存的是堆中开辟空间的地址,我们通过这个地址就可以找到这个对象。堆中对象开辟的空间里面存的是成员变量
通过 new 进行实例化对象,那么在 new 的时候就在堆上开辟了对象的空间
4.3 this引用的特性
- this的类型:对应类类型引用,即哪个对象调用就是哪个对象的引用类型
- this只能在"成员方法"中使用
- 在"成员方法"中,this只能引用当前对象,不能再引用其他对象
- this是“成员方法”第一个隐藏的参数,编译器会自动传递,在成员方法执行时,编译器会负责将调用成员方法对象的引用传递给该成员方法,this负责来接收
编译器编译之后将 “成员方法” 隐藏的this参数还原,在方法中所以 **“成员变量” **都通过this引用来访问
5.对象的构造及初始化
5.1 如何初始化对象
我们知道局部变量必须先赋值在使用,如果不赋值直接使用则会编译失败 。
对于实例化对象来说如果不初始化成员变量,成员变量里面存放的是默认值。
①局部变量先赋值后使用,则编译成功
**②局部变量不赋值直接使用,则编译失败 **
③上面我们讲了对于实例化对象来说如果不初始化成员变量,成员变量里面存放的是默认值
class Data {
public int year;
public int month;
public int day;
public void print() {
System.out.println(year+"-"+month+"-"+day);
}
}
public class Test {
public static void main(String[] args) {
Data data = new Data();
data.print();
}
}
运行结果:
那么现在就出现了两个问题:
- 那对象究竟如何初始化?
- 为什么局部变量必须先赋值后使用,而对象中的成员变量就不需要赋值可以直接使用?
**注:实例化对象里面存的就是成员变量,初始化也就是对成员变量进行初始化 **
那接下来我们就带着以上两个问题进入构造方法的学习
5.2 构造方法
**1.概念 **
**构造方法的概念:构造方法又称为构造器,它是一个特殊的成员方法,名字必须要与类名相同,在创建对象时,由编译器自动调用,并且在整个对象的生命周期内只调用一次 **
**2.特性 **
- 它是一个特殊的成员方法
- 没有返回值类型,设置为void也不行
- 名字与类名相同
- 一般情况下使用public修饰
- 创建对象的时候编译器自动调用,且在整个对象的生命周期内只会调用一次
- 构造方法可以重载
class Data {
public int year;
public int month;
public int day;
public Data(int year,int month,int day) {
this.year = year;
this.month = month;
this.day = day;
System.out.println("编译器自动调用了构造方法");
}
}
public class Test {
public static void main(String[] args) {
Data data = new Data(2022,9,10);
}
}
运行结果:
上述代码中主方法里面就只有一条实例化 Data 类的语句,通过实例化语句编译器自动调用了对象构造方法,并在构造方法中给成员变量初始化了
注意:构造方法的作用就是对对象中的成员进行初始化,并不负责给对象开辟空间。开辟空间是实例化的时候开辟的也就是new的时候开辟的
**3.构造方法的重载 **
构造方法可以重载:
class Data {
public int year;
public int month;
public int day;
public Data() {
System.out.println("编译器自动调用了没有形参的构造方法");
}
public Data(int year,int month,int day) {
this.year = year;
this.month = month;
this.day = day;
System.out.println("编译器自动调用了有形参的构造方法");
}
}
public class Test {
public static void main(String[] args) {
Data data = new Data(2022,9,10);
Data data1 = new Data();
}
}
运行结果:
**上述两个构造方法:名字相同,参数列表不同,因此构成了方法重载 **
**当构造方法重载时,根据实例化对象后面的参数进行匹配构造方法 **
**4.编译器什么时候自动生成一个无参的构造方法 **
**如果用户没有自己定义构造方法,那么编译器将会自动生成一份默认无参的构造方法: **
class Data {
public int year;
public int month;
public int day;
}
public class Test {
public static void main(String[] args) {
Data data = new Data();
}
}
我们看不到编译器自动生成的构造方法,但是不代表它没有生成
**如果用户自己定义了一个构造方法那么,编译器将不会自动生成: **
运行结果:
我们说如果用户不自己定义一个构造方法,那么编译器将会自动生成一个无参构造方法。上述代码自定义了一个有参的构造方法,那么编译器将不会自动生成了,所以当 data1 在编译时就直接报错了,原因就是用户自定义了一个有参的构造方法,编译器无法自动生成一个无参的构造方法了,如果想解决这个问题那么用户在定义一个无参的构造方法即可
5. 构造方法中,可以通过 this 调用其他构造方法来简化代码
class Data {
public int year;
public int month;
public int day;
public Data() {
this(2022,9,10);
}
public Data(int year,int month,int day) {
this.year = year;
this.month = month;
this.day = day;
}
public void print() {
System.out.println(year+"-"+month+"-"+day);
}
}
public class Test {
public static void main(String[] args) {
Data data = new Data();
data.print();
}
}
运行结果:
- this(...)必须在构造方法中的第一条
- 不能形成环
不能形成环:不能在无参构造方法中调用有参构造方法,在有参构造方法中调用无参构造方法 ,这样就容易形成环
**6.默认初始化 **
学习了上面的构造方法我们也就解决了第一个问题,就是对象如何初始化。
那我们接下来看第二个问题, 为什么局部变量必须先赋值后使用,而对象中的成员变量就不需要赋值可以直接使用?
要搞清楚第二个问题,就要了解 new 关键字背后所发生的一些事情
Data data = new Data();
在程序层面只是简单的一条语句,在JVM层面需要做好多事情,下面简单介绍一下:
- 检测对象对应的类是否加载了,如果没有加载则加载
- 为对象分配内存空间
- 处理并发安全问题。比如:多个线程同时申请对象,JVM要保证给对象分配的空间不冲突
- 初始化所分配的空间
- 设置对象头信息
- 调用构造方法,给对象中各个成员赋值
所以总结下来,为什么对象中的成员变量就不需要赋值可以直接使用,因为它调用了构造方法完成了初始化
**7.就地初始化 **
class Data {
public int year = 2022;
public int month = 9;
public int day = 10;
public void print() {
System.out.println(year+"-"+month+"-"+day);
}
}
public class Test {
public static void main(String[] args) {
Data data = new Data();
data.print();
}
}
** 在声明成员变量时,就直接给出了初始值,就叫做就地初始化**
版权归原作者 拼命阿紫 所有, 如有侵权,请联系我们删除。