目录
一. 什么是类?什么是对象?
Java是一门面向对象的编程语言(Object Oriented Program,简称OOP),
在面向对象的世界里,一切皆为对象,
你是一个对象,我是一个对象,房子是一个对象,汽车是一个对象,电脑,手机等等都是对象。
在Java看来,这些对象都是一个一个实体,那Java如何描述上面一个个具体的对象?需要用类来描述。
类是什么?
类可以看作是对象的模板或蓝图,用来描述对象的属性(数据)和行为(方法)。
Java怎么用类描述一个对象的属性和方法?
类首先需要一个语法来定义:
class 类名 {
}
例子:定义一个名为 Person 的类,
该类有两个属性:name 和 age,
并且有一个sleep方法来描述人的睡觉行为。
//用类描述一个人
class Person{
//属性 比如每个人都有name属性和age属性
public String name;
public int age;
//行为 / 方法
//比如用一个sleep方法描述人在睡觉的行为
Public void sleep() {
}
}
对象则是类的实例化,可以具体地表示现实世界中的一个个实体。通过定义一个类,然后把类当成一个模版进行实例化,就可以创建具体的对象。
例如,可以通过已经定义好的Person类,创建一个名为小明,年龄为18岁的Person对象,并调用其sleep方法来描述他在睡觉。
二. 什么是面向对象?什么是面向过程?
面向对象和面向过程是两种解决问题的思维方式,编程语言只是向计算机表达和传达思维的工具。
面向过程的编程语言 主要代表是 C语言。
面向对象的编程语言 主要代表是 C++ 和 Java 。
面向过程
面向过程是一种以过程为中心的思维方式,面向过程的重点在于将问题分解为多个步骤或函数,通过函数之间的调用来解决问题。
比如,要用洗衣机洗衣服需要对下面的步骤一步一步的执行:
面向对象
面向对象是以对象为中心的思维方式,面向对象的重点是将问题分解为不同的对象,然后创建每个对象的属性和方法,让每个对象可以负责完成特定的任务,最后通过对象之间的交互来完成整个任务。
比如,要洗衣服时,首先找到人,衣服,洗衣液,洗衣机四个对象,如果要完成洗衣服,需要这四个对象相互协作才能完成:人会拿衣服和洗衣液并且把它们放进洗衣机里,洗衣机会洗涤放进去的衣服,洗衣液可以通过与衣服和洗衣机的相互作用达到清洁作用。
三. 类的定义
面向对象程序设计关注的是对象,而对象是实体,计算机并不知道这些实体,所以需要先定义类,把对象的属性和行为 (方法) 抽象出来,计算机才能对具体的对象进行描述和操作。
一个类包含 属性 和 方法 。
- 属性是类的特征,它们描述了 对象的状态和特性。
- 方法是类的行为,它们定义了 对象可以执行的操作。方法可以用来改变属性的值,或者执行一些特定的功能。
比如一个洗衣机抽象成一个类,则:
- 定义的属性(状态)可以是:洗衣机的产品品牌,型号,产品重量,外观尺寸,颜色…
- 定义的方法(功能)可以是:洗衣,烘干、定时…
类的定义 格式
已知一个类包含了 属性 和 方法,类的关键字是 class :
// 创建类
class ClassName{
field; // 可以叫 字段,属性 或者 成员变量
method; // 可以叫 行为 或者 成员方法
}
-
属性 也可以称作 字段,或者 成员变量
成员变量与局部变量不同,局部变量定义在方法的内部;而成员变量定义在方法的外部, 类的内部。
成员变量还分为 普通成员变量 和 静态成员变量 (静态成员变量被 static 修饰)。 -
方法 也可以称作 行为 或者 成员方法
成员方法还分为 普通成员方法 和 静态成员方法。
//定义一个ClassName类
class ClassName {
//普通成员变量
public 数据类型 field;
//静态成员变量
public static 数据类型 staticField;
//普通成员方法
public 数据类型 method() {
}
//静态成员方法
public static 数据类型 staticMethod() {
}
}
比如定义一个洗衣机类:
class WashMachine{
public String brand; // 品牌
public String type; // 型号
public double weight; // 重量
public double length; // 长
public double width; // 宽
public double height; // 高
public String color; // 颜色
public void washClothes() {
// 洗衣服
System.out.println("洗衣功能");
}
public void dryClothes() {
// 脱水
System.out.println("脱水功能");
}
public void setTime() {
// 定时
System.out.println("定时功能");
}
}
还有定义一个狗类:
class PetDog {
//狗的属性
public String name;//名字
public String color;//颜色
// 狗的行为
public void barks() {
System.out.println(name + ": 旺旺旺~~~");
}
// 狗的行为
public void wag() {
System.out.println(name + ": 摇尾巴~~~");
}
}
采用 Java 语言将洗衣机类在计算机中定义完成,经过 javac 编译之后形成 .class 文件,在JVM的基础上计算机就可以识别了。
类的定义和命名的基本原则和规范
- 类名采用 大驼峰 定义 (即每个单词的首字母大写)
- 成员变量名 和 成员方法名 采用 小驼峰 定义 (即第一个单词首字母小写,后面的单词首字母大写)
- static 和 public 会在后面的文章中解释,定义 成员变量 和 成员方法 前面可以先默认加上 public (public 称为访问修饰限定符)
- 一般一个文件当中被 public 修饰的类 只能定义一个,但也不建议一个文件有多个类,这样不利于管理。
- public修饰的类必须要和文件名相同
- 每一个类编译之后都会产生一个字节码文件,而不是一个Java文件产生一个字节码文件(建议一个类 放到一个java文件当中,而不是一个java文件放多个类,这里在一个java文件中定义3个类只是证明每一个类产生一个字节码文件)
查看字节码文件的方法:
然后点击项目文件就能看到 .class 文件
可以看到:一个类编译之后都会产生一个字节码文件
- 不要轻易去修改public修饰的类的名称,如果要修改,通过开发工具修改
正确的修改步骤(这个方法只针对一个java文件只有一个类的时候):
四. 类的实例化
定义了一个类,就相当于在计算机中定义了一种新的类型
与int,double类似,只不过int和double是java语言自带的内置类型,而类是用户自定义了一个新的
类型。有了这些自定义的类型之后,就可以使用这些类来定义实例(或者称为对象)。
用 类 类型创建对象的过程,称为类的实例化
在java中采用 new 关键字,配合类名来实例化对象(实例化对象指的是通过new关键字对类进行实例化)
用一个例子加深理解
之前已经定义了一个宠物狗类:
class PetDog {
//狗的属性
public String name;//名字
public String color;//颜色
// 狗的行为
public void barks() {
System.out.println(name + ": 旺旺旺~~~");
}
// 狗的行为
public void wag() {
System.out.println(name + ": 摇尾巴~~~");
}
}
现在用宠物狗类进行实例化,注意 通过 new 关键字 可以实例化 多个对象
我这里实例化两个对象:哈士奇 和 柯基
public class Main {
public static void main(String[] args) {
//通过 new 关键字 可以实例化 多个对象
//实例化一只哈士奇作为对象
PetDog Husky = new PetDog();
Husky.name = "奇奇";
Husky.color = "黑白";
Husky.barks();
Husky.wag();
//实例化一只柯基作为对象
PetDog Corgi = new PetDog();
Corgi.name = "小基";
Corgi.color = "黄白";
Corgi.barks();
Corgi.wag();
}
}
代码运行结果:
类的实例化 注意事项
- new 关键字用于创建一个对象的实例
- 使用 . 来访问对象中的属性和方法
- 同一个类可以创建多个实例,比如一个宠物狗类可以生成多个品种的宠物狗对象。
对象存储的基本原理
在Java当中,当使用 new 关键字来实例化一个类时,会在堆中分配内存空间用于存储对象的实例。而在虚拟机栈中,会分配一个引用变量,该变量存储了对象在堆中的地址,使得该引用指向堆中实际存储的对象本身。
同样用 哈士奇 和 柯基 的例子:
哈士奇和柯基的具体细节还需要通过访问该对象中的属性,进行赋值。
五. this 引用
这里从一个例子开始引入 this的引用,
this . 成员变量
比如创建一个日期类并 实例化多个对象:
public class Date {
//日期类的属性
public int year;
public int month;
public int day;
//日期类的方法,用来设置具体的日期
public void setDay(int y, int m, int d){
year = y;
month = m;
day = d;
}
//在main方法中实例化2个对象
public static void main(String[] args) {
Date date1 = new Date();
date1.setDate(2023,8,4);
Date date2 = new Date();
date2.setDate(2012,6,1);
}
}
画图分析:
实例化第一个对象时:
实例化第二个对象时:
这里可以发现一个问题:对象可以有很多个,可是 setDate方法 只有一个,setDate方法是怎么确定是在给哪个对象赋值呢?
其实在setDate方法中隐藏了一个参数(程序员不需要主动加上Date this参数,这里只是更方便的展示):
date1 对象调用了setDate方法,那么 this 就是 date1对象;
date2 对象调用了setDate方法,那么 this 就是 date2对象。
那 setDate方法 中, year , month ,date 是怎么确定是哪个对象的呢?这就需要在 year , month ,date 前面用到 this (此时不加 this 也没问题,但加了一定对)
以上就是 this 的第一种引用场景,完整代码如下:
public class Date {
public int year;
public int month;
public int date;
public void setDate(int y, int m, int d) {
//this引用
this.year = y;
this.month = m;
this.date = d;
}
public static void main(String[] args) {
Date date1 = new Date();
date1.setDate(2023,8,4);
Date date2 = new Date();
date2.setDate(2012,6,1);
}
}
还是创建一个 Date类并且实例化一个 date1对象:
public class Date {
public int year;
public int month;
public int date;
public void setDate(int year, int month, int date) {
year = year;
month = month;
date = date;
}
public void printDate() {
System.out.println("year:" + year + " month:" + month + " date:" + date);
}
public static void main(String[] args) {
Date date1 = new Date();
date1.setDate(2023,8,4);
date1.printDate();
}
}
输出结果:
显然,这个代码是有问题的。
解决办法:通过 this 引用成员变量。
public class Date {
public int year;
public int month;
public int date;
public void setDate(int year, int month, int date) {
//通过this引用date1对象的成员变量
this.year = year;
this.month = month;
this.date = date;
}
public void printDate() {
System.out.println("year:" + year + " month:" + month + " date:" + date);
}
public static void main(String[] args) {
Date date1 = new Date();
date1.setDate(2023,8,4);
date1.printDate();
}
}
输出结果:
this . 成员方法
public class Date {
public int year;
public int month;
public int date;
public void setDate(int year, int month, int date) {
this.year = year;
this.month = month;
this.date = date;
}
public void initalDate() {
this.year = 2000;
this.month = 1;
this.date = 1;
}
public void printDate() {
System.out.println("year:" + this.year + " month:" + this.month + " date:" + this.date);
//this调用成员方法
this.initalDate();
}
public static void main(String[] args) {
Date date1 = new Date();
date1.setDate(2023,8,4);
date1.printDate();//year:2023 month:8 date:4
date1.printDate();//year:2000 month:1 date:1
}
}
this引用 总结
- this 代表当前对象的引用 (this 引用指向当前对象),谁调用这个方法,this 指的就是谁
- this 只能在 成员方法 中使用
- this是 成员方法 第一个隐藏的参数,编译器会自动传递,在成员方法执行时,编译器会负责将调用成员方法对象的引用传递给该成员方法,this负责来接收
- 在成员方法中,this 只能引用当前对象,不能再引用其他对象
六. 初始化 对象中的成员变量
局部变量在使用前一定需要先初始化
int a;
System.out.println(a);//error
int a = 10;
System.out.println(a);//OK
但成员变量不是局部变量,所以可以不初始化,也可以初始化。
成员变量的初始化需要分类介绍(就地初始化,默认初始化,构造方法)。
1. 成员变量的就地初始化
在声明成员变量时,就直接给出了初始值
class Person{
//对成员变量就地初始化
public String name = "zhangsan";
public int age = 18;
public void show() {
System.out.println("name: " + this.name + " age: " + this.age);
}
public static void main(String[] args) {
Person person1 = new Person();
person1.show();//输出的是name: zhangsan age: 18
}
}
2. 成员变量的默认初始化
成员变量 在没有初始化的时候,都会有一个默认值
在成员变量中,基本数据类型的默认值是 0 (布尔类型是 false),引用数据类型的默认值都是 null
数据类型 | 默认值 |
---|---|
byte | 0 |
char | ‘\u0000’ |
short | 0 |
int | 0 |
long | 0L |
boolean | false |
float | 0.0f |
double | 0.0 |
reference (引用类型) | null |
class Person{
//对成员变量默认初始化
public String name;
public int age;
public void show() {
System.out.println("name: " + this.name + " age: " + this.age);
}
public static void main(String[] args) {
Person person1 = new Person();
person1.show();//输出的是name: null age: 0
}
}
3. 通过 构造方法 对成员变量初始化
构造方法是非常特殊的方法,成为构造方法需要满足两个条件:
- 没有返回值,设置为 void 也不行
- 方法名必须和类名一样
class Person{
public String name;
public int age;
//构造方法
public Person() {
this.name = "zhangsan";
this.age = 18;
}
public void show() {
System.out.println("name: " + this.name + " age: " + this.age);
}
public static void main(String[] args) {
Person person1 = new Person();
person1.show();//输出的是name: zhangsan age: 18
}
}
这个构造方法是如何被调用的呢?
原理:
注意 构造方法在整个对象的生命周期内只调用一次,只有在创建对象时编译器自动调用构造方法(比如一个人只能出生一次,如果再次出生,那一定是另一个人出生了)
对象中的成员变量可以不初始化的原因
实例化一个对象(初始化一个对象的过程)的时候,需要进行两个关键步骤:
- 为对象分配内存
- 调用 合适 的构造方法
可是在默认初始化和就地初始化时代码中并没有构造方法,那为什么还能正常运行?
因为当我们没有写任何一个构造方法的时候,Java会自动提供一个不带参数的构造方法。
我们还可以写多个构造方法,因为 构造方法可以有多个,每个构造方法名又要与类名一样,因此构造方法之间构成了 重载关系。(方法的重载要保证方法名相同且参数不同,跟返回值没有关系)
class Person{
public String name;
public int age;
//第一个构造方法
public Person() {
this.name = "zhangsan";
this.age = 18;
}
//第二个构造方法
public Person(String name,int age) {
this.name = name;
this.age = age;
}
public void show() {
System.out.println("name: " + this.name + " age: " + this.age);
}
public static void main(String[] args) {
Person person1 = new Person();//调用的是第一个构造方法
person1.show();//name: zhangsan age: 18
Person person2 = new Person("lisi",23);//调用的是第二个构造方法
person2.show();//name: lisi age: 23
}
}
那上面的代码中有两种构造方法,我把第一种 没有参数的构造方法 删掉可以吗? 答案:不可以。
原因:只有一个构造方法都没有的情况下,Java才会默认给一个不带参数的构造方法。一旦你写了任何的构造方法,编译器都不会再给你任何的构造方法了。
class Person{
public String name;
public int age;
//第一个构造方法忽略掉 就无法编译
/*public Person() {
this.name = "zhangsan";
this.age = 18;
}*/
public Person(String name,int age) {
this.name = name;
this.age = age;
}
public void show() {
System.out.println("name: " + this.name + " age: " + this.age);
}
public static void main(String[] args) {
Person person1 = new Person();//error
person1.show();
Person person2 = new Person("lisi",23);
person2.show();
}
}
在IDEA中通过快捷键快速生成 有参数的构造方法
两种方法:
- 键盘 alt + insert (或 alt + F12 ), 然后用鼠标点击constructor
- 鼠标右击代码空白处,选中Generate, 然后用鼠标点击constructor
为构造方法选择参数,可以按住 shift键 选择多个参数
如果只选择name作为参数,那么就会自动创建好有name参数的构造方法
如果选择 name 和 age 作为参数,那么就会自动创建好有 name 和 age 参数的构造方法
this访问构造方法
构造方法中可以通过 this 调用当前类当中的其他构造方法
this调用其他构造方法,只能在构造方法当中写,且只能出现在当前构造方法的第一条语句(注释除外)
class Person{
public String name;
public int age;
public Person() {
//this调用其他构造方法,只能在构造方法当中写,且只能出现在当前构造方法的第一条语句(注释除外)
this("zhangsan",18);//调用当前类当中,带有两个参数的构造方法
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void show() {
System.out.println("name: " + this.name + " age: " + this.age);
}
public static void main(String[] args) {
Person person1 = new Person();
person1.show();//name: zhangsan age: 18
}
}
注意构造方法不能自己调用自己
也不能形成环
至此,我们已学习了3种 this 引用 的场景:
- this 引用成员变量
- this 引用成员方法
- this 引用构造方法
结语
类和对象是Java编程中的重要概念,但这篇博客只是一个开始。深入学习和理解类和对象需要持续的探索和实践。在这个过程中,我们可能会遇到各种错误和挑战,但正是通过不断解决问题,我们才能更好地理解和掌握这些知识。
如果在我的博客中存在任何错误或不准确的地方,还请指正。非常欢迎你的意见和建议,让我们一起不断学习和成长!