6.2 Java类

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

6.2.1 Java类的定义

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

  1. public class 类名{
  2. //定义类属性
  3. 属性1类型:属性1名;
  4. 属性2类型:属性2名;
  5. //定义方法
  6. 方法1定义
  7. 方法2定义

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

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

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

  1. public class Student
  2. {
  3. String stuName; //学生姓名
  4. int stuAge; //学生年龄
  5. int stuSex; //学生性别
  6. int stuGrade; //学生年级
  7. //定义听课的方法,在控制台直接输出
  8. public void learn()
  9. {
  10. System.out.println(stuName + "正在认真听课!");
  11. }
  12. //定义写作业的方法,输入时间,返回字符串
  13. public String doHomework(int hour)
  14. {
  15. return "现在是北京时间:" + hour + "点," + stuName + " 正在写作业!";
  16. }
  17. }

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

6.2.2 Java类的创建和使用

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

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

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

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

  1. Student wangYun = new Student();

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

  1. 对象名.属性;
  2. 对象名.方法;

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

  1. wangYun.stuName ="王云";
  2. wangYun.stuAge = 22;
  3. wangYun.stuSex = 1; //1代表男,2代表女
  4. wangYun.stuGrade = 4; //4代表大学四年级
  5. wangYun.learn(); //调用学生听课的方法
  6. wangYun.doHomework(22); //调用学生写作业的方法,输入值22代表现在是22点
  7. //该方法返回一个String类型的字符串

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

  1. public class TestStudent
  2. {
  3. public static void main(String[] args)
  4. {
  5. Student wangYun = new Student(); //创建wangYun学生类对象
  6. wangYun.stuName = "王云";
  7. wangYun.stuAge = 22;
  8. wangYun.stuSex = 1; //1代表男,2代表女
  9. wangYun.stuGrade = 4; //4代表大学四年级
  10. wangYun.learn(); //调用学生听课的方法
  11. String rstString = wangYun.doHomework(22); //调用学生写作业的方法,输入值22代表现在是22点
  12. System.out.println(rstString);
  13. }
  14. }

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

6.2 Java类 - 图1
图6.3 创建和使用Student类

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

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

  1. public class TestStudent
  2. {
  3. public static void main(String[] args)
  4. {
  5. Student wangYun = new Student(); //创建wangYun学生类对象
  6. wangYun.stuName = "王云";
  7. wangYun.stuAge = 22;
  8. wangYun.stuSex = 1; //1代表男,2代表女
  9. wangYun.stuGrade = 4; //4代表大学四年级
  10. wangYun.learn(); //调用学生听课的方法
  11. String rstString = wangYun.doHomework(22); //调用学生写作业的方法,输入值22代表现在是22点
  12. System.out.println(rstString);
  13. }
  14. }
  15. class Student //不能使用public修饰
  16. {
  17. String stuName; //学生姓名
  18. int stuAge; //学生年龄
  19. int stuSex; //学生性别
  20. int stuGrade; //学生年级
  21. //定义听课的方法,在控制台直接输出
  22. public void learn()
  23. {
  24. System.out.println(stuName + "正在认真听课!");
  25. }
  26. //定义写作业的方法,输入时间,返回字符串
  27. public String doHomework(int hour)
  28. {
  29. return "现在是北京时间:" + hour + "点," + stuName + " 正在写作业!";
  30. }
  31. }

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

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

  1. public class TestStudent
  2. {
  3. public static void main(String[] args)
  4. {
  5. Student wangYun = new Student(); //创建wangYun学生类对象
  6. System.out.println("未赋值前的学生姓名为:" + wangYun.stuName);
  7. System.out.println("未赋值前的学生年龄为:" + wangYun.stuAge);
  8. System.out.println("未赋值前的学生性别数值为:" + wangYun.stuSex);
  9. System.out.println("未赋值前的学生年级为:" + wangYun.stuGrade);
  10. //给对象的属性赋值
  11. wangYun.stuName = "王云";
  12. wangYun.stuAge = 22;
  13. wangYun.stuSex = 1; //1代表男,2代表女
  14. wangYun.stuGrade = 4; //4代表大学四年级
  15. System.out.println("赋值后的学生姓名为:" + wangYun.stuName);
  16. System.out.println("赋值后的学生年龄为:" + wangYun.stuAge);
  17. System.out.println("赋值后的学生性别数值为:" + wangYun.stuSex);
  18. System.out.println("赋值后的学生年级为:" + wangYun.stuGrade);
  19. }
  20. }

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

6.2 Java类 - 图2
图6.4 未赋值对象属性的值

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

6.2.3 Java类的简单运用

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

6.2 Java类 - 图3
图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.2 Java类 - 图4
图6.6 Java类的简单运用

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

  1. import java.util.Scanner;
  2. public class TestStuTea
  3. {
  4. static Scanner input = new Scanner(System.in);
  5. public static void main(String[] args)
  6. {
  7. Teacher[] tea = new Teacher[2]; //创建长度为2的数组tea,用于存放2个老师对象
  8. Student[] stu = new Student[4]; //创建长度为4的数组stu,用于存放4个学生对象
  9. for(int i = 0;i < tea.length;i++)
  10. {
  11. System.out.println("请创建并输入第" + (i+1) + "个老师的基本信息:");
  12. tea[i] = createTeacher(); //调用createTeacher方法创建第i+1个老师对象并赋值
  13. }
  14. for(int j = 0;j < stu.length;j++)
  15. {
  16. System.out.println("请创建并输入第" + (j+1) + "个学生的基本信息:");
  17. stu[j] = createStudent(); //调用createStudent方法创建第j+1个学生对象并赋值
  18. }
  19. //调用第一个老师讲课的方法,在控制台输出
  20. tea[0].teach();
  21. //依次调用所有学生听课的方法,在控制台输出
  22. for(int j = 0;j < stu.length;j++)
  23. {
  24. stu[j].learn();
  25. }
  26. //依次调用所有学生写作业的方法,在控制台输出
  27. for(int j = 0;j < stu.length;j++)
  28. {
  29. String tempStr = stu[j].doHomework(20); //其中20是作为参数传递给写作业的方法的
  30. System.out.println(tempStr);
  31. }
  32. for(int j = 0;j < stu.length;j++)
  33. {
  34. //调用第二个老师批改作业的方法,依次批改所有学生的作业,在控制台输出
  35. tea[1].checkHomework(stu[j]);
  36. }
  37. }
  38. //创建老师对象并赋值
  39. public static Teacher createTeacher()
  40. {
  41. Teacher tea = new Teacher();
  42. System.out.print("请输入老师姓名:");
  43. tea.teaName = input.next();
  44. System.out.print("请输入老师专业:");
  45. tea.teaSpecialty = input.next();
  46. System.out.print("请输入老师所讲授的课程:");
  47. tea.teaCourse = input.next();
  48. System.out.print("请输入老师教龄:");
  49. tea.teaYears = input.nextInt();
  50. return tea;
  51. }
  52. //创建学生对象并赋值
  53. public static Student createStudent()
  54. {
  55. Student stu = new Student();
  56. System.out.print("请输入学生姓名:");
  57. stu.stuName = input.next();
  58. System.out.print("请输入学生年龄:");
  59. stu.stuAge = input.nextInt();
  60. System.out.print("请输入学生性别数值(1代表男、2代表女):");
  61. stu.stuSex = input.nextInt();
  62. System.out.print("请输入学生年级:");
  63. stu.stuGrade = input.nextInt();
  64. return stu;
  65. }
  66. }
  67. class Teacher //不能使用public修饰
  68. {
  69. String teaName; //老师姓名
  70. String teaSpecialty; //老师专业
  71. String teaCourse; //老师所讲授的课程
  72. int teaYears; //老师教龄
  73. //定义讲课的方法,在控制台直接输出
  74. public void teach()
  75. {
  76. System.out.println(teaName + "正在辛苦讲:" + teaCourse + " 课程!");
  77. }
  78. //定义批改作业的方法,输入值为一个学生对象,在控制台直接输出结果
  79. public void checkHomework(Student stu)
  80. {
  81. System.out.println("讲授:" + teaCourse + " 课程的老师:" + teaName + " 已经批改完毕:
  82. " + stu.stuName +" 的作业!");
  83. }
  84. }
  85. class Student //不能使用public修饰
  86. {
  87. String stuName; //学生姓名
  88. int stuAge; //学生年龄
  89. int stuSex; //学生性别
  90. int stuGrade; //学生年级
  91. //定义听课的方法,在控制台直接输出
  92. public void learn()
  93. {
  94. System.out.println(stuName + "正在认真听课!");
  95. }
  96. //定义写作业的方法,输入时间,返回字符串
  97. public String doHomework(int hour)
  98. {
  99. return "现在是北京时间:" + hour + "点," + stuName + " 正在写作业!";
  100. }
  101. }

6.2.4 封装

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

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

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

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

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

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

  1. public class Student
  2. {
  3. private String stuName; //学生姓名——私有属性
  4. private int stuAge; //学生年龄——私有属性
  5. private int stuSex; //学生性别——私有属性
  6. private int stuGrade; //学生年级——私有属性
  7. public String getStuName(){ //公有方法获得学生姓名
  8. //这里的this表示本对象
  9. return this.stuName; //返回这个类的私有属性stuName
  10. }
  11. //公有方法设置学生姓名,参数为要设置的学生姓名
  12. public void setStuName(String name){
  13. this.stuName = name;
  14. }
  15. public int getStuAge(){ //公有方法获得学生年龄
  16. return this.stuAge;
  17. }
  18. //公有方法设置学生年龄,参数为要设置的学生年龄
  19. public void setStuAge(int age){
  20. this.stuAge = age;
  21. }
  22. public int getStuSex(){ //公有方法获得学生性别
  23. return this.stuSex;
  24. }
  25. //公有方法设置学生性别,参数为要设置的学生性别
  26. public void setStuSex(int sex){
  27. this.stuSex = sex;
  28. }
  29. public int getStuGrade(){ //公有方法获得学生年级
  30. return this.stuGrade;
  31. }
  32. //公有方法设置学生年级,参数为要设置的学生年级
  33. public void setStuGrade(int grade){
  34. this.stuGrade = grade;
  35. }
  36. //定义听课的方法,在控制台直接输出
  37. public void learn()
  38. {
  39. System.out.println(stuName + "正在认真听课!");
  40. }
  41. //定义写作业的方法,输入时间,返回字符串
  42. public String doHomework(int hour)
  43. {
  44. return "现在是北京时间:" + hour + "点," + stuName + " 正在写作业!";
  45. }
  46. }

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

  1. public class TestStudent3
  2. {
  3. public static void main(String[] args)
  4. {
  5. Student wangYun = new Student(); //创建wangYun学生类对象
  6. wangYun.stuName = "王云"; //不能给私有属性stuName赋值
  7. wangYun.stuAge = 22; //不能给私有属性stuAge赋值
  8. wangYun.stuSex = 1; //不能给私有属性stuSex赋值
  9. wangYun.stuGrade = 4; //不能给私有属性stuGrade赋值
  10. wangYun.learn(); //可以调用公有的学生听课的learn()方法
  11. String rstString = wangYun.doHomework(22);//可以调用公有的学生写作业的
  12. //doHomework(22)方法
  13. System.out.println(rstString);
  14. }
  15. }

  正确的代码如下。

  1. public class TestStudent4
  2. {
  3. public static void main(String[] args)
  4. {
  5. Student wangYun = new Student(); //创建wangYun学生类对象
  6. wangYun.setStuName("王云"); //调用公有方法给stuName赋值
  7. wangYun.setStuAge(22); //调用公有方法给stuAge赋值
  8. wangYun.setStuSex(1); //调用公有方法给stuSex赋值
  9. wangYun.setStuGrade(4); //调用公有方法给stuGrade赋值
  10. wangYun.learn(); //调用公有的学生听课的learn()方法
  11. String rstString = wangYun.doHomework(22); //调用公有的学生写作业的doHomework(22)方法
  12. System.out.println(rstString);
  13. }
  14. }