6.2 类和封装

类的基本语法

Java API 提供了一些现有的类,程序员可以使用这些类来创建对象,例如第 5 章学习的 String 类。除了使用现有的 Java 类,程序员还可以自定义 Java 类,接下来会详细地介绍如何定义和使用 Java 类。

我们在编写第一个 Java 程序时已经知道,类是 Java 程序的基本单元。Java 是面向对象的程序设计语言,所有程序都是由类组织起来的,也可以说“类是 Java 的一等公民”。下面是类定义的语法形式。

public class 类名{
    //定义类属性
    属性1类型:属性1;
    属性2类型:属性2;//定义方法
    方法1定义
    方法2定义
    …
}

在 Java 中,class 是用来定义类的关键字,class 关键字后面是要定义的类的名称,然后有一对大括号,大括号里写的是类的主要内容。

类的主要内容分两部分,第一部分是类的属性定义,在前面的课程中学习过,在类内部、方法外部定义的变量称为成员变量,也可以称为成员属性,或简称为“属性”。第二部分是类的方法定义,通过方法的定义,描述类(对象)具有的动态行为,这些方法也可以称为成员方法,或简称为“方法”。

例如我们可以创建一个学生类 Student,代码如下:

public class Student {
    String stuName;  //学生姓名
    int stuAge;      //学生年龄
    int stuSex;      //学生性别
    int stuGrade;    //学生年级

    //定义听课的方法,在控制台直接输出
    public void learn() {
        System.out.println(stuName + "正在认真听课!");
    }

    //定义写作业的方法,输入时间,返回字符串
    public String doHomework(int hour) {
        return "现在是北京时间:" + hour + "点," + stuName + " 正在写作业!";
    }
}

定义好一个类后,就可以根据这个类创建(实例化)对象了。类就相当于一个模板,可以创建多个对象。创建对象的语法形式如下。

类名 对象名 = new 类名();

在学习使用 String 类时,已经使用过这种语法,所以大家对这样的语法形式并不陌生。创建对象时,要使用 new 关键字,后面要跟着类名(构造方法名),类名后的括号内可传递构造参数。

根据上面创建对象的语法,例如我们要在 Student 类里创建王云这个学生对象,代码如下。

Student wangYun = new Student();

这里,只创建了 wangYun 这个对象,并没有对这个对象的属性赋值,考虑到每个对象的属性值不一样,所以通常在创建对象后给对象的属性赋值。在 Java 语言中,通过 . 操作符来引用对象的属性和方法,具体的语法形式如下。

对象名.属性 ;
对象名.方法() ;

通过上面的语法形式,可以给对象的属性赋值,也可以更改对象属性的值或者调用对象的方法,具体的代码如下。

wangYun.stuName ="王云";
wangYun.stuAge = 22;
wangYun.stuSex = 1;            //1代表男,2代表女
wangYun.stuGrade = 4;        //4代表大学四年级
wangYun.learn();            //调用学生听课的方法
wangYun.doHomework(22);        //调用学生写作业的方法,输入值22代表现在是22点

【练一练】实现一个简单的师生状态管理程序

前面的实验(类的创建和使用)中,我们定义了 Student 类后,使用 TestStudent 测试类创建了一个 Student 类的对象 wangYun,然后给 wangYun 对象的属性赋值并调用对象的方法。

接下来再定义一个老师类 Teacher,其具有的属性和方法如图所示。

图片描述

下面将新定义一个 TestStuTea 类,用于组织这个新程序的程序结构。该程序中包含 2 个老师对象和 4 个学生对象,其基本信息分别如以下两个表所示。

老师基本信息表:

姓名 专业 课程 教龄
蒋涵 计算机应用 Java 基础 5
田斌 软件工程 前端技术 10

学生基本信息表:

姓名 年龄 性别 年级
王云 22 男 4
刘静涛 21 女 3
南天华 20 男 3
雷静 22 女 4

程序要完成的功能描述如下。

在程序开始运行时,需要在控制台依次输入所有老师和学生的基本信息。
在控制台输入完毕这些老师和学生的基本信息后,调用第一个老师讲课的方法,在控制台输出“(该老师的姓名)老师正在辛苦讲(该老师所授课程)课程”的信息。
依次调用所有学生听课的方法,在控制台输出“xx(该学生姓名)学生正在认真听课!”的信息。
依次调用所有学生写作业的方法,在控制台“现在是北京时间:20 点,xx(该学生姓名)正在写作业!”的信息,其中 20 是作为参数传递给写作业的方法的。
调用第二个老师批改作业的方法,依次批改所有学生的作业,在控制台输出“讲授 xx(该老师所授课程)课程的老师 xx(该老师姓名)已经批改完毕:xx(该学生姓名)的作业!”。
本次实验功能相对复杂,我们将需求分步实现:

新建一个 TestStuTea.java 文件,并定义 Student 类。

import java.util.Scanner;

//定义Student类
class Student {                //不能使用public修饰

    String stuName;        //学生姓名
    int stuAge;            //学生年龄
    int stuSex;            //学生性别
    int stuGrade;            //学生年级

    //定义听课的方法,在控制台直接输出
    public void learn() {
        System.out.println(stuName + "正在认真听课!");
    }

    //定义写作业的方法,输入时间,返回字符串
    public String doHomework(int hour) {
        return "现在是北京时间:" + hour + "点," + stuName + " 正在写作业!";
    }
}

定义 Teacher 类,在 TestStuTea.java 文件最后添加如下代码:

class Teacher {                //不能使用public修饰

    String teaName;        //老师姓名
    String teaSpecialty;        //老师专业
    String teaCourse;            //老师所讲授的课程
    int teaYears;                //老师教龄

    //定义讲课的方法,在控制台直接输出
    public void teach() {
        System.out.println(teaName + "正在辛苦讲:" + teaCourse + " 课程!");
    }

    //定义批改作业的方法,输入值为一个学生对象,在控制台直接输出结果
    public void checkHomework(Student stu) {
        System.out.println("讲授:" + teaCourse + " 课程的老师:"
                + teaName + " 已经批改完毕: " + stu.stuName + " 的作业!");
    }
}

定义 TestStuTea 类,并在类中定义 createStudent 方法,实现创建学生对象,并对属性赋值:

import java.util.Scanner;

public class TestStuTea {
    //全局Scanner对象
    static Scanner input = new Scanner(System.in);

    //创建学生对象并赋值
    public static Student createStudent() {
        Student stu = new Student();
        System.out.print("请输入学生姓名:");
        stu.stuName = input.next();
        System.out.print("请输入学生年龄:");
        stu.stuAge = input.nextInt();
        System.out.print("请输入学生性别数值(1代表男、2代表女):");
        stu.stuSex = input.nextInt();
        System.out.print("请输入学生年级:");
        stu.stuGrade = input.nextInt();
        return stu;
    }
}

在 TestStuTea 类中定义 createTeacher 方法,实现创建老师对象,并对属性赋值:

import java.util.Scanner;

public class TestStuTea {
    //全局Scanner对象
    static Scanner input = new Scanner(System.in);

    //创建老师对象并赋值
    public static Teacher createTeacher() {
        Teacher tea = new Teacher();
        System.out.print("请输入老师姓名:");
        tea.teaName = input.next();
        System.out.print("请输入老师专业:");
        tea.teaSpecialty = input.next();
        System.out.print("请输入老师所讲授的课程:");
        tea.teaCourse = input.next();
        System.out.print("请输入老师教龄:");
        tea.teaYears = input.nextInt();
        return tea;
    }

编写 main 方法,完成需求,完整代码如下

import java.util.Scanner;

public class TestStuTea {
    static Scanner input = new Scanner(System.in);

    public static void main(String[] args) {
        Teacher[] tea = new Teacher[2];    //创建长度为2的数组tea,用于存放2个老师对象
        Student[] stu = new Student[4];    //创建长度为4的数组stu,用于存放4个学生对象
        for (int i = 0; i < tea.length; i++) {
            System.out.println("请创建并输入第" + (i + 1) + "个老师的基本信息:");
            tea[i] = createTeacher();    //调用createTeacher方法创建第i+1个老师对象并赋值
        }
        for (int j = 0; j < stu.length; j++) {
            System.out.println("请创建并输入第" + (j + 1) + "个学生的基本信息:");
            stu[j] = createStudent();    //调用createStudent方法创建第j+1个学生对象并赋值
        }
        //调用第一个老师讲课的方法,在控制台输出
        tea[0].teach();
        //依次调用所有学生听课的方法,在控制台输出
        for (int j = 0; j < stu.length; j++) {
            stu[j].learn();
        }
        //依次调用所有学生写作业的方法,在控制台输出
        for (int j = 0; j < stu.length; j++) {
            String tempStr = stu[j].doHomework(20);    //其中20是作为参数传递给写作业的方法的
            System.out.println(tempStr);
        }
        for (int j = 0; j < stu.length; j++) {
            //调用第二个老师批改作业的方法,依次批改所有学生的作业,在控制台输出
            tea[1].checkHomework(stu[j]);
        }
    }

    //创建老师对象并赋值
    public static Teacher createTeacher() {
        Teacher tea = new Teacher();
        System.out.print("请输入老师姓名:");
        tea.teaName = input.next();
        System.out.print("请输入老师专业:");
        tea.teaSpecialty = input.next();
        System.out.print("请输入老师所讲授的课程:");
        tea.teaCourse = input.next();
        System.out.print("请输入老师教龄:");
        tea.teaYears = input.nextInt();
        return tea;
    }

    //创建学生对象并赋值
    public static Student createStudent() {
        Student stu = new Student();
        System.out.print("请输入学生姓名:");
        stu.stuName = input.next();
        System.out.print("请输入学生年龄:");
        stu.stuAge = input.nextInt();
        System.out.print("请输入学生性别数值(1代表男、2代表女):");
        stu.stuSex = input.nextInt();
        System.out.print("请输入学生年级:");
        stu.stuGrade = input.nextInt();
        return stu;
    }
}

class Teacher {                //不能使用public修饰

    String teaName;        //老师姓名
    String teaSpecialty;        //老师专业
    String teaCourse;            //老师所讲授的课程
    int teaYears;                //老师教龄

    //定义讲课的方法,在控制台直接输出
    public void teach() {
        System.out.println(teaName + "正在辛苦讲:" + teaCourse + " 课程!");
    }

    //定义批改作业的方法,输入值为一个学生对象,在控制台直接输出结果
    public void checkHomework(Student stu) {
        System.out.println("讲授:" + teaCourse + " 课程的老师:"
                + teaName + " 已经批改完毕: " + stu.stuName + " 的作业!");
    }
}

class Student {                //不能使用public修饰

    String stuName;        //学生姓名
    int stuAge;            //学生年龄
    int stuSex;            //学生性别
    int stuGrade;            //学生年级

    //定义听课的方法,在控制台直接输出
    public void learn() {
        System.out.println(stuName + "正在认真听课!");
    }

    //定义写作业的方法,输入时间,返回字符串
    public String doHomework(int hour) {
        return "现在是北京时间:" + hour + "点," + stuName + " 正在写作业!";
    }
}

代码中使用了两个数组,分别存放了 2 个老师对象和 4 个学生对象(在对象被创建以前,数组中的元素值都是 null),然后使用 createTeacher()、createStudent() 方法创建了具体的老师和学生对象并赋值,之后再通过对 teach()、learn() 等方法的调用,输出老师和学生对象中的各个功能。

程序运行结果如图所示。
在这里插入图片描述

java中的封装

在企业面试中,经常问到,面向对象有哪些基本特性?答案应该是:封装、继承和多态。继承和多态在后面的章节会详细介绍,这里给同学们简要介绍一下封装。

封装的目的是简化编程和增强安全性。

简化编程是指,封装可以让使用者不必了解具体类的内部实现细节,而只是要通过提供给外部访问的方法来访问类中的属性和方法。例如 Java API 中的 Arrays.sort()方法,该方法可以用于给数组进行排序操作,开发者只需要将待排序的数组名放到 Arrays.sort()方法的参数中,该方法就会自动的将数组排好序。可见,开发者根本不需要了解 Arrays.sort()方法的底层逻辑,只需要简单的将数组名传递给方法即可实现排序。
增强安全性是指,封装可以使某个属性只能被当前类使用,从而避免被其他类或对象进行误操作。例如在 Student.java 的程序中,Student 的 stuAge 属性是 public 的形式体现的,但这样做实际存在着安全隐患:TestStudent 类(或在访问修饰符可见范围内的其他类)完全可以随意的对 stuAge 进行修改,如以下程序。

public class TestStudent {
    public static void main(String[] args) {
        Student wangYun = new Student();
        wangYun.stuAge = -10;
        ...
    }
}

如上,给 stuAge 赋了一个不符合逻辑的值,但语法是却正确的。因此这种做法,实际会给程序造成了一定程度上的安全问题。

封装的基本形式:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

封装如何保证数据的安全性

在初识封装中我们提到 public 的形式体现的属性可以随意进行修改,那么如何避免此类问题呢?我们可以使用 private 修饰符来修饰 stuAge 属性,以此禁止 Student 以外的类对 stuAge 属性的修改。但这么做未免显得“过犹不及”,为了保证安全,也不至于让其他类无法访问吧!有没有一种办法,既能让其他类可以访问 Student 类中的 stuAge 属性,又能保证其他类始终是在安全的数值范围内修改 stuAge 值呢?

有,我们可以先用 private 修饰 stuAge 属性,然后再给该属性提供两个 public 修饰的、保证属性安全的访问方法(setter 方法和 getter 方法),即:

  1. 用 private 禁止其他类直接访问属性;
  2. 给 1 中的属性新增两个 public 修饰的 setter 和 getter 方法,供其他类安全的访问。

setter 方法用于给属性赋值,而 getter 访问用于获取属性的值。并且一般而言,setter 方法的名字通常是 set+属性名,getter 方法的名字通常是 get+属性名。

根据以上描述,先用 private 修饰 stuAge,禁止 TestStudent 类对 stuAge 的直接访问,以此保证 stuAge 安全性;然后新增 setStuAge()和 getStuAge()方法,一方面供 TestStudent 类间接的访问 stuAge 属性,另一方面也保证了 stuAge 的数据安全,详见以下程序。

public class Student {
    private int stuAge ;

    //获取stuAge的值
    public int getStuAge() {
        return stuAge;
    }

    //给stuAge赋一个合法的值
    public void setStuAge(int age) {
        //如果年龄在合理范围内,则正常赋值
        if( age >0 && age <110)
            stuAge = age;
        else //如果对年龄的赋值不合理,则设置为默认值0
            age = 0 ;
    }
}

后续,其他类只需要调用 setStuAge()和 getStuAge()方法,就能对 stuAge 属性进行安全的赋值或取值,代码如下。

public class TestStudent3 {
    public static void main(String[] args) {
        Student wangYun = new Student();
        /* 如果将stuAge赋值负数,setStuAge()就会将stuAge设置为默认值0,防止出现安全问题
        wangYun.setStuAge(-10); */
        wangYun.setStuAge(22);
        int age = wangYun.getStuAge() ;
    }
}

实际上,使用 setter 和 getter 的解决方案用到了一个程序设计的基本原则逻辑代码不能写在变量中,而必须写在方法或代码块中

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值