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定义
…
}
在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 + " 正在写作业!";
}
}
需要注意的是,这个类里面没有main方法,所以只能编译,不能运行。
6.2.2 Java类的创建和使用
定义好Student类后,就可以根据这个类创建(实例化)对象了。类就相当于一个模板,可以创建多个对象。创建对象的语法形式如下。
类名 对象名 = new 类名();
在学习使用String类创建String字符串时,其实已经创建了类的对象,所以大家对这样的语法形式并不陌生。创建对象时,要使用new关键字,后面要跟着类名。
根据上面创建对象的语法,创建王云这个学生对象的代码如下。
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点
//该方法返回一个String类型的字符串
接下来通过创建一个测试类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);
}
}
编译并运行该程序,运行结果如图6.3所示。
这个程序虽然非常简单,但却是我们第一次使用两个类完成的一个程序。其中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 + " 正在写作业!";
}
}
在上面的一些例子中,对对象的属性都是先赋值后再使用,如果没有赋值就直接使用对象的属性,会有什么样的结果呢?
下面将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);
}
}
程序运行结果如图6.4所示。
从图6.4所示的程序运行结果可以看出,在未给对象属性赋值前使用属性时,如果该属性为引用数据类型,其初始默认值为null,如果该属性是int型,其初始默认值为0。
6.2.3 Java类的简单运用
在上一小节中,定义了Student类后,使用TestStudent测试类创建了一个Student类的对象wangYun,然后给wangYun对象的属性赋值并调用对象的方法。接下来再定义一个老师类Teacher,Teacher类具有的属性和方法如图6.5所示。
下面将新定义一个TestStuTea类,用于组织这个新程序的程序结构。该程序中包含2个老师对象(基本信息如表6.1所示)和4个学生对象(基本信息如表6.2所示)。
姓名 | 专业 | 课程 | 教龄 |
---|---|---|---|
蒋涵 | 计算机应用 | Java基础 | 5 |
田斌 | 软件工程 | 前端技术 | 10 |
姓名 | 年龄 | 性别 | 年级 |
---|---|---|---|
王云 | 22 | 男 | 4 |
刘静涛 | 21 | 女 | 3 |
南天华 | 20 | 男 | 3 |
雷静 | 22 | 女 | 4 |
程序要完成的功能描述如下。
(1)在程序开始运行时,需要在控制台依次输入所有老师和学生的基本信息。
(2)在控制台输入完毕这些老师和学生的基本信息后,调用第一个老师讲课的方法,在控制台输出“**(该老师的姓名)老师正在辛苦讲**(该老师所授课程)课程”的信息。
(3)依次调用所有学生听课的方法,在控制台输出“**(该学生姓名)学生正在认真听课!”的信息。
(4)依次调用所有学生写作业的方法,在控制台“现在是北京时间:20点,**(该学生姓名)正在写作业!”的信息,其中20是作为参数传递给写作业的方法的。
(5)调用第二个老师批改作业的方法,依次批改所有学生的作业,在控制台输出“讲授**(该老师所授课程)课程的老师**(该老师姓名)已经批改完毕:**(该学生姓名)的作业!”。
程序运行结果如图6.6所示。
程序代码如下所示,其中使用了两个数组,分别存放了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 + " 正在写作业!";
}
}
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 + " 正在写作业!";
}
}
如果继续使用下面的代码创建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);
}
}
正确的代码如下。
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);
}
}