面向对象程序设计与面向过程程序设计在思维方式上存在着很大的差别,如果之前没有接触过面向对象程序设计的应用背景,那么学习面向对象这种思维刚开始并不会那么容易
目录
一、面向对象程序设计
面向对象程序设计(简称 OOP) 是当今主流的程序设计范型, 它已经取代了 20 世纪 70年代的“ 结构化” 过程化程序设计开发技术。 Java 是完全面向对象的, 必须熟悉 OOP 才能够编写 Java 程序。
面向对象与面向过程的比较
我们用同一个例子来比较两个思想的区别
比如我们在洗衣服的时候,面向过程是这样来执行的
拿盆子——>接水——>放衣服——>放洗衣粉——>手搓——>换水——>放洗衣粉——>手搓——>涤衣服——>拧干——>晾衣服
面向对象是这样执行
人将衣服放进洗衣机里,倒入洗衣粉,启动洗衣机,把衣服拿出来晾衣服
整个过程主要是:人、衣服、洗衣粉、洗衣机四个对象之间交互完成的,人不需要关心洗衣机具体是如何洗衣服的。
面向对象就像现实世界一样,每一个操作都需要有实体,其中实体包含属性和方法,每一个实体都是动作的支配者。上图的每个对象都是实体,他们都有自己的属性和行为。
二、类和对象
1、类
类( class) 是构造对象的模板或蓝图。 我们可以将类想象成制作小甜饼的切割机,将对象想象为小甜饼。 由类构造 (construct ) 对象的过程称为创建类的实例 (instance ) .
2、对象
要想使用 OOP,—定要清楚对象的三个主要特性:
- 对象的行为(behavior) — 可以对对象施加哪些操作,或可以对对象施加哪些方法?
- 对象的状态 (state) — 当施加那些方法时,对象如何响应?
- 对象标识(identity) — 如何辨别具有相同行为与状态的不同对象?
每个对象都保存着描述当前特征的信息。
3、代码分析
class Person {
// 字段->成员变量 定义在方法外 类的里面
// 实例成员变量: 由对象产生
// 实例成员没有初始化 默认值为对应的0 引用类型默认为null
public String name;
public int age;
// 静态成员变量
// 不属于对象 属于类 放在方法区中
// 没有初始化和实例成员变量是一样的
public static int size;
// 方法->行为
// 实例成员方法 可以调用实例成员和静态成员的方法和变量
public void eat() {
// 局部变量在使用的时候必须要初始化
int a = 10;
System.out.println("eat!");
}
public void study() {
System.out.println("study!");
}
// 静态成员方法 只能调用静态成员方法和变量
public static void sleep() {
System.out.println("sleep!");
}
}
public class TestDemo {
public static void main() {
Person person = new Person();
Person person1 = new Person();
}
}
上述代码,我们首先创建一个TestDemo的java文件,会生成一个TestDemo的类,在这个java文件里面,创建Person(人)的一个类。我们都知道人具有姓名、年龄等属性,吃、学习、睡觉等方法。属性其实就是成员变量,方法是成员方法。成员变量/方法又分为普通成员变量/方法
和静态成员变量/方法
。在成员方法中,局部变量必须初始化。
当我们在TestDemo类中的main方法中创建一个对象,此时创建的对象都会存放在堆中,这个对象的地址作为一个值存放在person变量中。person是一个引用类型。在这个新创建的对象中包含了姓名、年龄属性,吃、学习的方法。他并没有包含类中的静态成员变量/方法,因为静态成员变量/方法是属于类的。而普通成员变量/方法属于对象。
普通成员变量必须在创建一个新对象之后使用,并且两个对象之间的变量/方法是互不干扰的。那么静态成员变量/方法又该如何使用呢?
既然静态成员变量/方法是属于类的,那么我们可以直接用类来调用。
public class TestDemo {
public static void main() {
Person.size++;
Person.size++;
System.out.println(Person.size);
}
}
结果为2
由于我们在类中定义的size是没有初始化的,所以初始值为0。为什么size这个时候变成了2呢?
我们就需要了解方法区,我们的java文件在编译的时候,会生成class的字节码文件,这个文件可以让机器识别。java文件里面的每一个类都会生成class文件,而我们的静态成员变量/方法此时也存放在方法区中,它是唯一的。这也就是为什么size作为静态成员变量只存在一份,下一次修改其实是在修改上一次的值。
4、成员方法/变量之间的关系
任何方法都不能定义静态成员变量
因为静态成员变量属于类,当方法被调用的时候,会创建一个栈帧空间用来存放方法内部的局部变量,当栈帧结束的时候,会释放所有的局部变量。而静态成员变量的生命周期和类一致。但是在静态成员方法中可以使用静态成员变量
静态成员方法不能调用普通成员方法
因为静态成员方法是不依赖与对象,而普通成员方法是依赖对象的。
普通成员方法可以调用静态成员方法
三、封装
封装 ( encapsulation , 有时称为数据隐藏)是与对象有关的一个重要概念。 从形式上看,封装不过是将数据和行为组合在一个包中, 并对对象的使用者隐藏了数据的实现方式。
封装的本质就是让类的调用者不必太多的了解类的实现者是如何实现类的, 只要知道如何使用类就行了。
1、private实现封装
被 public 修饰的成员变量或者成员方法, 可以直接被类的调用者使用.
被 private 修饰的成员变量或者成员方法, 不能被类的调用者使用
为什么会用到private呢?
如果使用public来修饰,那么该数据域允许程序中的任何方法对其进行读取和修改。这就完全破坏了封装。private同样也可以修饰方法,建议一个类中只提供“必要的”public方法。而不是把所有的方法都无脑设为public
2、getter 和 setter 方法
当我们使用private修饰变量的时候,那么该如何使用呢?
class Person {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void eat() {
System.out.println("eat!");
}
public void study() {
System.out.println("study!");
}
}
public class TestDemo {
public static void main(String[] args) {
Person person = new Person();
person.setName("小明");
person.setAge(18);
System.out.println(person.getName() + " " + person.getAge());
}
}
在Person这个类中我们定义了两个方法 getName
,getAge
和 setName
,setAge
方法。
getName 和 getAge : 获取private成员的值
setName 和 setAge : 设置private成员的值
注意:
- 当set方法的形参名字和类中的成员属性的名字一样的时候,如果不使用this, 相当于自赋值. this 表示当前实例的引用.
- 不是所有的字段都一定要提供 setter / getter 方法, 而是要根据实际情况决定提供哪种方法.
- 在 IDEA 中可以使用 alt + insert (或者 alt + F12) 快速生成 setter / getter 方法. 在 VSCode 中可以使用鼠标右键菜单 -> 源代码操作 中自动生成 setter / getter 方法
四、构造方法
1、构造器
一个对象的产生分为两步:
- 1、为对象分配内存
- 2、调用合适的构造方法 构造方法可以不止一个
语法规则
1.方法名称必须与类名称相同
2.构造方法没有返回值类型声明
3.每一个类中一定至少存在一个构造方法(没有明确定义,则系统自动生成一个无参构造)
构造器会将所有的实例域设置为默认值。于是, 实例域中的数值型数据设置为 0、 布尔型数据设置
为 false、 所有对象变量将设置为 null。构造方法支持重载,规则和普通方法的重载一致。
class Person {
private String name;
private int age;
public void eat() {
System.out.println("eat!");
}
public Person() {
System.out.println("Person.Person");
}
public Person(String str) {
System.out.println("Person.Person<str>");
}
public void study() {
System.out.println("study!");
}
}
public class TestDemo {
public static void main(String[] args) {
Person person1 = new Person();
Person person2 = new Person("小明");
}
}
打印
Person.Person
Person.Person<str>
new Person()
会实例化构造方法
2、this关键字
this表示当前对象的引用(注意不是当前对象). 可以借助 this 来访问对象的字段和方法。
为什么说this是当前对象的引用呢?
因为我们可以在构造方法中使用this关键字,而对象在被创建的时候首先需要实例化构造方法,只有实例化构造方法之后,对象才创建完成。
this的使用
this.name
this.study()
this()
在 getter 和 setter 方法中,当参数和成员变量名称相同时,这个时候我们可以利用this.name = name
来区分两个变量。
在当前类中在方法中调用该类中的其他方法,比如在eat方法中调用study方法this.study()
。
在构造方法中如果要调用该类中其他构造方法,则使用this()
,有参数则在括号内加上。注意该语句只能放在该构造方法的第一句,只能调用一次,并且只能在构造方法中使用。
五、代码块
1、什么是代码块
使用 { } 定义的一段代码.
根据代码块定义的位置以及关键字,又可分为以下四种:
- 普通代码块:定义在方法中的代码块
- 构造块:定义在类中的代码块(不加修饰符),也叫实例代码块,构造代码块一般用于初始化实例成员变量。
- 静态块:使用static定义的代码块,一般用于初始化静态成员属性。
- 同步代码块(多线程部分)
2、实例代码块和静态代码块
class Person {
private String name;
private int age;
public Person() {
System.out.println("Person.Person");
}
{
System.out.println("实例代码块!");
}
static {
System.out.println("静态代码块!");
}
public void eat() {
System.out.println("eat!");
}
}
public class TestDemo {
public static void main(String[] args) {
Person person1 = new Person();
}
}
打印
静态代码块!
实例代码块!
Person.Person
静态代码块不管生成多少个对象,其只会执行一次,且是最先执行的。如果都是静态,则看代码的顺序!
静态代码块执行完毕后, 实例代码块(构造块)执行,再然后是构造函数执行。
参考书
本文参考了《Java核心技术 卷Ⅰ 基础知识 原书第10版》