6.2 Java类

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

6.2.1 Java类的定义

  在编写第一个Java程序时就已经知道,类是Java程序的基本单元。Java是面向对象的程序设计语言,所有程序都是由类组织起来的。下面是类定义的语法形式。

public class 类名{

    //定义类属性

    属性1类型:属性1名;

    属性2类型:属性2名;


    //定义方法

    方法1定义

    方法2定义

copy

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

  类的主要内容也分两部分,第一部分是类的属性定义,在前面的课程中学习过,在类内部、方法外部定义的变量称为成员变量,这里类的属性就是类的成员变量,这两个概念是相同的。第二部分是类的方法定义,通过方法的定义,描述类具有的行为,这些方法也可以称为成员方法。

  接下来通过定义学生类,熟悉Java类定义的写法,具体代码如下所示。

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 + " 正在写作业!";

    }

}
copy

  需要注意的是,这个类里面没有main方法,所以只能编译,不能运行。

6.2.2 Java类的创建和使用

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

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

  在学习使用String类创建String字符串时,其实已经创建了类的对象,所以大家对这样的语法形式并不陌生。创建对象时,要使用new关键字,后面要跟着类名。

  根据上面创建对象的语法,创建王云这个学生对象的代码如下。

Student wangYun = new Student();
copy

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

对象名.属性;

对象名.方法;
copy

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

wangYun.stuName ="王云";

wangYun.stuAge = 22;

wangYun.stuSex = 1;                      //1代表男,2代表女

wangYun.stuGrade = 4;                  //4代表大学四年级

wangYun.learn();                            //调用学生听课的方法

wangYun.doHomework(22);          //调用学生写作业的方法,输入值22代表现在是22点

​                                              //该方法返回一个String类型的字符串
copy

  接下来通过创建一个测试类TestStudent(这个测试类需要和之前编译过的Student类在同一个目录),来测试Student类的创建和使用,具体代码如下所示。

public class TestStudent

{

    public static void main(String[] args) 

    {

        Student wangYun = new Student();                  //创建wangYun学生类对象

        wangYun.stuName = "王云";

        wangYun.stuAge = 22;

        wangYun.stuSex = 1;                                        //1代表男,2代表女

        wangYun.stuGrade = 4;                                    //4代表大学四年级

        wangYun.learn();                                              //调用学生听课的方法

        String rstString = wangYun.doHomework(22);  //调用学生写作业的方法,输入值22代表现在是22点

        System.out.println(rstString);

    }

}
copy

  编译并运行该程序,运行结果如图6.3所示。

图6.3 创建和使用Student类

  这个程序虽然非常简单,但却是我们第一次使用两个类完成的一个程序。其中TestStudent类是测试类,测试类中包含main方法,提供程序运行的入口。在main方法内,创建Student类的对象并给对象属性赋值,然后调用对象的方法。

  这个程序有两个Java文件,每个Java文件中编写了一个Java类,编译完成后形成2个class文件。也可以将两个Java类写在一个Java文件里,但其中只能有一个类用public修饰,并且这个Java文件的名称必须用这个public类的类名命名,具体代码如下。


public class TestStudent

{

    public static void main(String[] args) 

    {

        Student wangYun = new Student();                  //创建wangYun学生类对象

        wangYun.stuName = "王云";

        wangYun.stuAge = 22;

        wangYun.stuSex = 1;                                        //1代表男,2代表女

        wangYun.stuGrade = 4;                                    //4代表大学四年级

        wangYun.learn();                                              //调用学生听课的方法

        String rstString = wangYun.doHomework(22);  //调用学生写作业的方法,输入值22代表现在是22点

        System.out.println(rstString);

    }

}

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 + " 正在写作业!";

    }

}
copy

  在上面的一些例子中,对对象的属性都是先赋值后再使用,如果没有赋值就直接使用对象的属性,会有什么样的结果呢?

  下面将TestStudent测试类的代码修改成如下的形式。

public class TestStudent

{

    public static void main(String[] args) 

    {

        Student wangYun = new Student();                           //创建wangYun学生类对象

        System.out.println("未赋值前的学生姓名为:" + wangYun.stuName);

        System.out.println("未赋值前的学生年龄为:" + wangYun.stuAge);

        System.out.println("未赋值前的学生性别数值为:" + wangYun.stuSex);

        System.out.println("未赋值前的学生年级为:" + wangYun.stuGrade);

        //给对象的属性赋值

        wangYun.stuName = "王云";

        wangYun.stuAge = 22;

        wangYun.stuSex = 1;                                                 //1代表男,2代表女

        wangYun.stuGrade = 4;                                             //4代表大学四年级

        System.out.println("赋值后的学生姓名为:" + wangYun.stuName);

        System.out.println("赋值后的学生年龄为:" + wangYun.stuAge);

        System.out.println("赋值后的学生性别数值为:" + wangYun.stuSex);

        System.out.println("赋值后的学生年级为:" + wangYun.stuGrade);

    }

}
copy

  程序运行结果如图6.4所示。

图6.4 未赋值对象属性的值

  从图6.4所示的程序运行结果可以看出,在未给对象属性赋值前使用属性时,如果该属性为引用数据类型,其初始默认值为null,如果该属性是int型,其初始默认值为0。

6.2.3 Java类的简单运用

  在上一小节中,定义了Student类后,使用TestStudent测试类创建了一个Student类的对象wangYun,然后给wangYun对象的属性赋值并调用对象的方法。接下来再定义一个老师类Teacher,Teacher类具有的属性和方法如图6.5所示。

图6.5 老师类

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

表6.1 老师基本信息表
姓名 专业 课程 教龄
蒋涵 计算机应用 Java基础 5
田斌 软件工程 前端技术 10
表6.2 学生基本信息表
姓名 年龄 性别 年级
王云 22 4
刘静涛 21 3
南天华 20 3
雷静 22 4

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

  (1)在程序开始运行时,需要在控制台依次输入所有老师和学生的基本信息。

  (2)在控制台输入完毕这些老师和学生的基本信息后,调用第一个老师讲课的方法,在控制台输出“**(该老师的姓名)老师正在辛苦讲**(该老师所授课程)课程”的信息。

  (3)依次调用所有学生听课的方法,在控制台输出“**(该学生姓名)学生正在认真听课!”的信息。

  (4)依次调用所有学生写作业的方法,在控制台“现在是北京时间:20点,**(该学生姓名)正在写作业!”的信息,其中20是作为参数传递给写作业的方法的。

  (5)调用第二个老师批改作业的方法,依次批改所有学生的作业,在控制台输出“讲授**(该老师所授课程)课程的老师**(该老师姓名)已经批改完毕:**(该学生姓名)的作业!”。

  程序运行结果如图6.6所示。

图6.6 Java类的简单运用

  程序代码如下所示,其中使用了两个数组,分别存放了2个老师对象和4个学生对象,使用循环和方法createTeacher()、createStudent()创建对象并给对象赋值,之后再使用循环并调用对象方法按要求输出结果。

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 + " 正在写作业!";

    }

}
copy

6.2.4 封装

  在企业面试的过程中,经常会被问到,面向对象有哪些基本特性?答案应该是:封装、继承和多态。如果要求4个答案的话,可以增加一个抽象。继承和多态在后面的章节会详细介绍,这里给读者简要介绍一下封装。

  封装就是将抽象得到的属性和行为结合起来,形成一个有机的整体,就是类。类里面的一些属性和方法(尤其是属性),需要隐藏起来,不希望直接对外公开,但同时提供供外部访问的方法来访问这些需要隐藏的属性和方法。

  封装的目的是增强安全性和简化编程,使用者不必了解具体类的内部实现细节,而只是要通过提供给外部访问的方法,来有限制地访问类需要隐藏的属性和方法。

  还是使用学生的案例,例如要求一旦对学生对象的性别赋值之后就不能修改,但在6.2.2节的TestStudent.java程序中,完全可以在赋值以后,通过“学生对象名.stuSex = 2”语句,把学生对象的性别从男变成女。这样的做法就不符合程序的需求,如何解决这个问题呢?

  采用封装的形式,用private(私有的)关键字去修饰stuSex变量,其含义是为把stuSex变量封装到类的内部,只有在类的内部才可以访问stuSex变量,从而保证了这个变量不能被其他类的对象修改。这样的做法只起到了隐藏的作用,要想更好地使用这个变量,则应该提供一个对外的public(公有的)方法,其他对象可以通过这个方法访问这个私有的变量。

  所谓良好的封装,就是使用private对属性进行封装,从而保护信息的安全。使用public修饰方法时,外界可以调用该方法,通常将设置私有属性的值和获取私有属性值的方法称为setter和getter方法。接下来按照良好封装的要求,重新编写Student类,其代码如下。

public class Student 

{

    private String stuName;                //学生姓名——私有属性

    private int stuAge;                    //学生年龄——私有属性

    private int stuSex;                     //学生性别——私有属性

    private int stuGrade;                   //学生年级——私有属性

    public String getStuName(){         //公有方法获得学生姓名

    //这里的this表示本对象

        return this.stuName;             //返回这个类的私有属性stuName

    }

    //公有方法设置学生姓名,参数为要设置的学生姓名

    public void setStuName(String name){

        this.stuName = name;

    }

    public int getStuAge(){                 //公有方法获得学生年龄

        return this.stuAge;

    }

    //公有方法设置学生年龄,参数为要设置的学生年龄

    public void setStuAge(int age){

        this.stuAge = age;

    }       

    public int getStuSex(){                  //公有方法获得学生性别

        return this.stuSex;

    }

    //公有方法设置学生性别,参数为要设置的学生性别

    public void setStuSex(int sex){

        this.stuSex = sex;

    }       

    public int getStuGrade(){              //公有方法获得学生年级

        return this.stuGrade;

    }

    //公有方法设置学生年级,参数为要设置的学生年级

    public void setStuGrade(int grade){

        this.stuGrade = grade;

    }       

    //定义听课的方法,在控制台直接输出

    public void learn()

    {

        System.out.println(stuName + "正在认真听课!");

    }

    //定义写作业的方法,输入时间,返回字符串

    public String doHomework(int hour)

    {

        return "现在是北京时间:" + hour + "点," + stuName + " 正在写作业!";

    }

} 
copy

  如果继续使用下面的代码创建Student对象并给对象属性赋值,将不会编译通过,原因是不能在类外给类的私有属性赋值,具体参见代码注释。

public class TestStudent3

{

    public static void main(String[] args) 

    {

        Student wangYun = new Student();                  //创建wangYun学生类对象

        wangYun.stuName = "王云";                           //不能给私有属性stuName赋值

        wangYun.stuAge = 22;                                     //不能给私有属性stuAge赋值

        wangYun.stuSex = 1;                                        //不能给私有属性stuSex赋值

        wangYun.stuGrade = 4;                                    //不能给私有属性stuGrade赋值

        wangYun.learn();                                              //可以调用公有的学生听课的learn()方法

        String rstString = wangYun.doHomework(22);//可以调用公有的学生写作业的

        //doHomework(22)方法

        System.out.println(rstString);

    }

}
copy

  正确的代码如下。

public class TestStudent4

{

    public static void main(String[] args) 

    {

        Student wangYun = new Student();                  //创建wangYun学生类对象

        wangYun.setStuName("王云");                        //调用公有方法给stuName赋值

        wangYun.setStuAge(22);                                  //调用公有方法给stuAge赋值

        wangYun.setStuSex(1);                                    //调用公有方法给stuSex赋值

        wangYun.setStuGrade(4);                                 //调用公有方法给stuGrade赋值

        wangYun.learn();                                              //调用公有的学生听课的learn()方法

        String rstString = wangYun.doHomework(22);  //调用公有的学生写作业的doHomework(22)方法

        System.out.println(rstString);

    }

} 
copy