Java基础|1-02-面向对象和封装 @面向对象篇

写在前面:
此文是笔者在学习Java系列课程的过程中,参考相关课件、视频讲解、课程代码,并结合一些文档、思维导图及个人理解,对所学内容做的阶段性梳理与总结。

  • 写于:2021年1月25日
  • 内容:Java后端系列笔记002(Java基础-面向对象和封装)
  • 全文:7316字

一、面向对象思想

1. 1 面向对象思想概述

概述

  • Java语言是一种面向对象的程序设计语言,而面向对象思想是一种程序设计思想,我们在面向对象思想的指引下,使用Java语言去设计、开发计算机程序。 这里的对象泛指现实中一切事物,每种事物都具备自己的属性行为
  • 面向对象思想就是在计算机程序设计过程中,参照现实中事物,将事物的属性特征、行为特征抽象出来,描述成计算机事件的设计思想。
  • 它区别于面向过程思想,强调的是通过调用对象的行为来实现功能,而不是自己一步一步的去操作实现。

理解

  • 乔布斯是这样解释面向对象编程的:

Jeff Goodell:请你用尽量简练的语言解释一下,究竟 什么是面向对象的软件

乔布斯:对象就像人一样,也是活生生的生命。他们有知识,知道怎么完成任务;他们有记忆,可以把发生的事情记下来。而你和他们的互动并不是低层次的,你是与他们在一个 高度抽象 的层面上互动,就像我们现在的对话一样。

我举个例子来说明。如果我是一个 “洗衣”对象 ,你可以把脏衣服给我,然后告诉我说:“请帮我把这些衣服洗了吧!” 而我恰好知道旧金山最好的洗衣房在哪,并且我会说英语,兜里也有美元。于是我出门打了一辆出租车,告诉司机带我去位于旧金山的洗衣房。我到了那里洗好衣服之后,又坐车回到这里。我把洗好的衣服交还给你,说:“你的衣服已经洗好了。”

你并不知道我是怎么做到的。你不知道哪里有洗衣店,也可能只会说法语,或者是兜里没钱,连车都打不了。但是我知道怎么完成这项任务,而你 不需要知道任何细节 。所有的这些复杂流程都隐藏在我的内部,而我们之间可以 高度抽象地互动 ,这就是对象。他们把复杂过程封装在内部,而对外呈现的接口是高层次的,抽象的。

——摘自 1994 年 Rolling Stone 对乔布斯的采访

  • 例子分析:

    总的来说,就是:“你”只需要把事情交代给“我”,“我”来实现该需求,等事情完成,“你”看到的就是一个完成后的结果。至少细节,“你”不关心,也无需知道,原本复杂的事情,对“你”来说,变得简单了不少。

区别

  • 面向过程(强调步骤)

    • 当需要实现一个功能的时候,每一个具体的步骤都要亲力亲为(过程),详细处理每一个细节
  • 面向对象(强调对象)

    • 当需要实现一个功能的时候,不关心具体的步骤,而是找一个已经具有该功能的人(对象),来帮我做事

程序中的面向对象

public static void main(String[] args) {
   
 int[] array = {
    10, 20, 30, 40, 50, 60 };

    // 要求打印格式为:[10, 20, 30, 40, 50,60]
    // 使用【面向过程】,每一个步骤细节都要亲力亲为。
    System.out.print("[");
    for (int i = 0; i < array.length; i++) {
   
        if (i == array.length - 1) {
    // 如果是最后一个元素
            System.out.println(array[i] + "]");
        } else {
    // 如果不是最后一个元素
            System.out.print(array[i] + ", ");
        }
    }
}
public static void main(String[] args) {
   
  	int[] array = {
    10, 20, 30, 40, 50, 60 };

     // 使用【面向对象】
     // 找一个JDK给我们提供好的Arrays类,(import java.util.Arrays;)
     // 其中有一个toString方法,直接就能把数组变成想要的格式的字符串
     System.out.println(Arrays.toString(array));
 }

小贴士:面向对象的语言中,包含了三大基本特征,即封装、继承和多态

1. 2 类和对象 [ 导图 ]

环顾周围,你会发现很多对象,比如桌子,椅子,同学,老师等。桌椅属于办公用品,师生都是人类。那么什么是类呢?什么是对象呢?

实例:我们根据抽象的设计图,设计出具体的各种车,这个过程就是——实例化具体的对象

1. 3 类的定义

事物与类的对比

  • 现实世界的一类事物:
    • 属性:事物的状态信息
    • 行为:事物能够做什么
  • Java中用class描述事物也是如此:
    • 成员变量:对应事物的属性
    • 成员方法:对应事物的行为

类的定义格式

public class ClassName {
   
	//成员变量
	//成员方法
}
  • 定义类:就是定义类的成员,包括成员变量和成员方法
  • 成员变量:和以前定义变量几乎是一样的,只不过位置发生了改变(在类中,方法外)
  • 成员方法:和以前定义方法几乎是一样的,只不过把static去掉

举例:

  • 定义一个类,用来模拟“学生”事物。其中就有两个组成部分:
    • 属性(是什么):姓名、年龄
    • 行为(能做什么):吃饭、睡觉、学习
  • 对应到Java的类当中:
    • 成员变量(属性)
    • 成员方法(行为)
/* 注意事项: 1. 成员变量是直接定义在类当中的,在方法外边。 2. 成员方法不要写static关键字。 */
public class Student {
   
    // 成员变量
    String name; // 姓名
    int age; // 年龄

    // 成员方法
    public void eat() {
    System.out.println("吃饭!"); }

    public void sleep() {
    System.out.println("睡觉!");}

    public void study() {
    System.out.println("学习!");}

}

1. 4 对象的使用

通常情况下,一个类并不能直接使用,需要根据类创建一个对象,才能使用。

  1. 导包:指出需要使用的类在什么位置
import 包名称.类名称;
import cn.itcast.day06.demo01.Student;
//对于和当前类属于同一个包的情况,可以省略导包语句不写
  1. 创建,格式:
类名称 对象名 = new 类名称();
Student stu = new Student();
  1. 使用,分为两种情况:
使用成员变量:对象名.成员变量名
使用成员方法:对象名.成员方法名(参数)
//也就是,想用谁,就用对象名点谁

举例:

public class Demo02Student {
   
    public static void main(String[] args) {
   
        // 1. 导包
        // 我需要使用的Student类,和我自己Demo02Student位于同一个包下,所以省略导包语句不写

        // 2. 创建:类名称 对象名 = new 类名称();
        Student stu = new Student(); // 根据Student类,创建了一个名为stu的对象

        // 3. 使用其中的成员变量:对象名.成员变量名
        System.out.println(stu.name); // null
        System.out.println(stu.age); // 0
        System.out.println("=============");

        // 改变对象当中的成员变量数值内容
        // 将右侧的字符串,赋值交给stu对象当中的name成员变量
        stu.name = "赵丽颖";
        stu.age = 18;
        System.out.println(stu.name); // 赵丽颖
        System.out.println(stu.age); // 18
        System.out.println("=============");

        // 4. 使用对象的成员方法:对象名.成员方法名()
        stu.eat();
        stu.sleep();
        stu.study();
    }
}

小贴士: 如果成员变量没有进行赋值,那么将会有一个默认值,规则和数组一样

成员变量的默认值

数据类型 默认值
基本类型 整数(byte、short、int、long) 0
浮点数(float、double) 0.0
字符(char) ‘\u0000’
布尔(boolean) false
引用类型 数组、类、接口 null

1. 5 对象内存图

1-① 一个对象,调用一个方法内存图

  • 通过上图,我们可以理解,在栈内存中运行的方法,遵循 “先进后出,后进先出” 的原则。变量one指向堆内存中的空间,寻找方法信息,去执行该方法。
  • 但是,这里依然有问题存在。创建多个对象时,如果每个对象内部都保存一份方法信息,这就非常浪费内存了,因为所有对象的方法信息都是一样的。那么如何解决这个问题呢?请看如下图解。

1-② 两个对象,调用同一方法内存图

  • 对象调用方法时,根据对象中方法标记(地址值),去类中寻找方法信息。这样哪怕是多个对象,方法信息只保存一份,节约内存空间。

1-③ 两个引用,指向同一个对象内存图

2-① 使用对象类型,作为方法的参数

  • 使用对象类型作为方法的参数,传递的是对象的地址值

2-② 使用对象类型,作为方法的返回值

  • 使用对象类型作为方法的返回值,返回的是对象的地址值

1. 6 成员变量和局部变量区别 [ 导图 ]


二、封装

2. 1 封装概述

面向对象三大特征:封装、继承、多态

概述

  • 面向对象编程语言是对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界无法直接操作和修改
  • 封装可以被认为是一个保护屏障,防止该类的代码和数据被其他类随意访问。要访问该类的数据,必须通过指定的方式
  • 适当的封装可以让代码更容易理解与维护,也加强了代码的安全性

原则

  • 属性隐藏起来,若需要访问某个属性,提供公共方法对其访问

小贴士:封装就是将一些细节信息隐藏起来,对于外界不可见。

优点

  1. 良好的封装能够减少耦合
  2. 类内部的结构可以自由修改
  3. 可以对成员变量进行更精确的控制
  4. 隐藏信息,实现细节

封装性在Java当中的体现

  1. 方法就是一种封装
  2. 关键字private也是一种封装(私有)

封装的步骤

  1. 使用 private 关键字来修饰成员变量
  2. 对需要访问的成员变量,提供对应的一对 getXxx方法 、setXxx 方法(Getter/Setter方法)

小贴士:对于Getter来说,不能有参数,返回值类型和成员变量对应; 对于Setter来说,不能有返回值,参数类型和成员变量对应。

2. 2 封装的操作 - private关键字

private的使用

  • 问题描述:定义Person的年龄时,无法阻止不合理的数值被设置进来
  • 解决方案:用private关键字将需要保护的成员变量进行修饰
    • 格式: private 数据类型 变量名 ;
  • 说明:一旦使用了private进行修饰,那么本类当中仍可随意访问。但是!超出了本类范围之外就不能再直接访问了
  • 如何访问:可间接访问private成员变量 —— 定义一对Getter/Setter方法(以setXxx或getXxx命名)
public class Person {
   
    String name; // 姓名
    private int age; // 年龄(将age属性设置为私有的)

    public void show() {
   
        // 本类中可随意访问被 private 修饰的成员变量age
        System.out.println("我叫:" + name + ",年龄:" + age);
    }

    // 这个setXxx成员方法,专门用于向age设置数据
    public void setAge(int num) {
   
        if (num < 100 && num >= 9) {
    // 如果是合理情况
            age = num;
        } else {
   
            System.out.println("数据不合理!");
        }
    }

    // 这个getXxx成员方法,专门用于获取age的数据
    public int getAge() {
   
        return age;
    }

}
public class Demo03Person {
   
    public static void main(String[] args) {
   
        Person person = new Person();
        person.show();// 我叫:null,年龄:0

        person.name = "赵丽颖";
// person.age = -20; // 直接访问private内容,错误写法!
        person.setAge(20);// 使用setAge方法设置年龄
        person.show();// 我叫:赵丽颖,年龄:20
    }
}

private的含义

  1. private是一个权限修饰符,代表最小权限
  2. 可以修饰成员变量和成员方法
  3. 被private修饰后的成员变量和成员方法,只在本类中才能访问

注意事项

  • 对于基本类型当中的boolean值,Getter方法一定要写成isXxx的形式,而setXxx规则不变
private boolean male; // 是不是爷们儿
public void setMale(boolean b) {
    
	male = b;
}

public boolean isMale() {
    // isXxx的形式
    return male;
}

2. 3 封装优化 1 - this关键字

this的使用

  • 当方法的局部变量和类的成员变量重名的时候,根据“就近原则”,优先使用局部变量
  • 如果需要访问本类当中的成员变量,需要使用格式:this.成员变量名
  • 含义:this代表所在类的当前对象的引用(地址值),即对象自己的引用
  • 提示:“通过谁调用的方法,谁就是this。”

图例-理解this的含义:

封装优化 - this

  • 使用 this 修饰方法中的变量,解决成员变量被隐藏的问题
public class Person{
   
    private String name;
    private int age;public void setName(String name){
   
        // 赋值失败!由于形参变量名与成员变量名重名,导致成员变量名被隐藏
        // name = name;

        //使用this修饰方法中的变量,解决成员变量被隐藏的问题
        this.name = name;
    }

    public String getName(){
   
        return name; 
        // 也可写成return this.name (this可省略)
        // 理由:方法中只有一个变量名时,默认也是使用 this 修饰
    }

    public void setAge(int age) {
   
        this.age = age;
    }
    
    public int getAge() {
   
        return age;
    }
}

小贴士:采用 this 关键字是为了解决实例变量(private String name)和局部变量(setName(String name)中的name变量)之间发生的同名的冲突

2. 4 封装优化 2 - 构造方法

概念

  • 构造方法是专门用来创建对象的方法
  • 当我们通过关键字new来创建对象时,其实就是在调用构造方法

格式

public 类名称(参数类型 参数名称) {
   
    方法体
}

示例:

public class Student {
   
    // 成员变量
    private String name;
    private int age;

    // 无参数的构造方法
    public Student() {
   
        System.out.println("无参构造方法执行啦!");
    }

    // 全参数的构造方法
    public Student(String name, int age) {
   
        System.out.println("全参构造方法执行啦!");
        this.name = name;
        this.age = age;
    }
}

注意事项

  1. 构造方法的名称必须和所在的类名称完全一样,就连大小写也要一样

  2. 构造方法不要写返回值类型,连void都不写

  3. 构造方法不能return一个具体的返回值

  4. 如果没有编写任何构造方法,那么编译器将会默认赠送一个构造方法,没有参数、方法体什么事情都不做 public Student() {}

  5. 一旦编写了至少一个构造方法,那么编译器将不再赠送

  6. 构造方法也是可以进行重载的(重载:方法名称相同,参数列表不同)

2. 5 标准代码 - JavaBean

一个标准的类(也叫JavaBean)通常要拥有下面四个组成部分:

  1. 所有的成员变量都要使用 private 关键字修饰
  2. 为每一个成员变量编写一对 Getter/Setter 方法
  3. 编写一个无参数的构造方法
  4. 编写一个全参数的构造方法

下面来编写一个完整的程序:

  • 快速编写JavaBean,步骤:
    • 写好成员变量后,使用快捷键Alt + Insert
    • 使用 Alt + Insert → 选择Constructor → 点击Select None → 自动生成无参构造方法
    • 使用 Alt + Insert → 选择Constructor → 选择成员变量→ OK → 自动生产有参构造方法
    • 使用 Alt + Insert → 选择Getter and Setter → 选择所有成员变量 → OK → 自动生成Getter/Setter 方法
public class Student {
   
    // 成员变量
    private String name; // 姓名
    private int age; // 年龄
   
    //无参构造方法【必须】
    public Student() {
   }
	
	//有参构造方法【建议】
    public Student(String name, int age) {
   
        this.name = name;
        this.age = age;
    }

	//成员方法:getXxx()、setXxx()
    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 class Demo04Student {
   
    public static void main(String[] args) {
   
        Student stu1 = new Student();
        stu1.setName("小黑");
        stu1.setAge(20);
        System.out.println("姓名:" + stu1.getName() + ",年龄:" + stu1.getAge());
        System.out.println("=================");

        Student stu2 = new Student("小白", 21);
        System.out.println("姓名:" + stu2.getName() + ",年龄:" + stu2.getAge());
        stu2.setAge(22);
        System.out.println("姓名:" + stu2.getName() + ",年龄:" + stu2.getAge());
    }
}
  • 执行结果:
姓名:小黑,年龄:20
=================
姓名:小白,年龄:21
姓名:小白,年龄:22

全部评论

相关推荐

11-04 14:10
东南大学 Java
_可乐多加冰_:去市公司包卖卡的
点赞 评论 收藏
分享
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务