Exam1

  1. 编写一个程序,在程序中定义一个抽象类Shape,再定义两个抽象方法Area和printArea,定义两个Shape类的子类Rectangle和Circle类,在子类中实现父类的抽象方法。

code

Shape.java

1
2
3
4
5
6
7
8
package project4B.abstractTest;

public abstract class Shape{
double area;
//定义抽象方法Area和printArea
public abstract void Area();
public abstract void printArea();
}

Rectangle.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package project4B.abstractTest;

public class Rectangle extends Shape{
@Override
public void Area() {
area = height*width;
}

@Override
public void printArea() {
System.out.println(String.format("%.3f",area));
}

float height,width;

public Rectangle(float height, float width) {
this.height = height;
this.width = width;
}
}

Circle.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package project4B.abstractTest;

public class Circle extends Shape{
//实现抽象方法
@Override
public void Area() {
area = Math.PI*r*r;
}

@Override
public void printArea() {
System.out.println(String.format("%.3f",area));
}

//定义属性
float r;

public Circle(float r) {
this.r = r;
}
}

Application.java

1
2
3
4
5
6
7
8
9
10
11
12
package project4B.abstractTest;

public class Application {
public static void main(String[] args) {
Rectangle rectangle = new Rectangle(1.2f, 4.2f);
rectangle.Area();
rectangle.printArea();
Circle circle = new Circle(3);
circle.Area();
circle.printArea();
}
}

运行结果

img

Exam2

2、编写下面程序写出运行结果,谈谈成员变量的继承与隐藏,方法的覆盖与重载。

code

Test.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
package project4B;

//定义一个A类
class A{
int sum,num1,num2;
public A(){
num1 = 10;
num2 = 20;
sum = 0;
}

void sum1(){
sum = num1+num2;
System.out.println("sum ="+num1+"+"+num2+"="+sum);
}

void sum2(int n){
num1 = n;
sum = num1+num2;
System.out.println("sum ="+num1+"+"+num2+"="+sum);
}

}

//B继承A,为A的子类
class B extends A{
int num2;

public B() {
this.num2 = 200;
}
void sum2(){
sum = num1+num2;
System.out.println("sum ="+num1+"+"+num2+"="+sum);

}

//覆盖重写了父类的有参方法sum2
void sum2(int n){
num1 = n;
sum = num1+num2;
System.out.println("sum ="+num1+"+"+num2+"="+sum);
}

void sum3(int n){
//调用父类的有参方法sum2,调用时,用的是父类的num2 = 20
super.sum2(n);
//下面的num2指的是本类的num2
System.out.println("sum ="+num1+"+"+num2+"="+sum);
}

void testVar(){
System.out.println("this.num1==super.num1:\t"+(this.num1==super.num1));
System.out.println("this.num2==super.num2:\t"+(this.num2==super.num2));
}

}

public class Test {
public static void main(String[] args) {
//new一个B类对象
B b = new B();
//调用父类方法sum1
b.sum1();
//调用本类的无参方法sum2
b.sum2();
//调用本类的重写的有参方法sum2
b.sum2(50);
//调用本类的方法sum3
b.sum3(50);
//测试子类和父类成员变量的共享性和差异性
b.testVar();
}
}

运行结果

img

总结:

  1. 子类会继承父类的成员变量,但当子类中定义了与父类相同的成员变量时,父类中相应的成员变量会被隐藏,并没有被覆盖,当需要调用被隐藏的变量时仍然可以调用。没有被隐藏的成员变量则为子类与父类共享的变量。
  2. 方法覆盖和方法重载的区别:方法覆盖是存在于继承当中的,当子类中显示定义了和父类完全相同的方法时,则会将父类对应的方法覆盖,并重写。(重写常用于子类继承父类后,对父类方法的具体实现不满意,但是仍然想用该方法的方法名和实参等,则对该方法的具体实现进行重写)。而方法重载是存在于同一个类中的,且要求形参不同,重载的方法时并行存在的。

Exam3

  1. 理解下面3个类的定义,分析它们之间的关系,写出运行结果。

code

Inheritance.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package project4B;

class SuperClass{
int x;
//显示定义无参构造
SuperClass(){
x = 3;
System.out.println("in SuperClass:x="+x);
}
void doSomething(){
System.out.println("in SuperClass.doSomething()");
}
}

//定义SuperClass的子类SubClass
class SubClass extends SuperClass{
//隐藏父类的属性x,但仍然可以调用
int x;
//显示定义无参构造
SubClass(){
super();
x = 5;
System.out.println("in SubClass:x="+x);
}
void doSomething(){
super.doSomething();
System.out.println("in SubClass.doSomething()");
System.out.println("super.x="+super.x+"\nsub.x="+x);
}
}

public class Inheritance {
public static void main(String[] args) {
SubClass subClass = new SubClass();
subClass.doSomething();
}
}

三个类的关系:

SuperClass属于正常的类,在此程序中作为SubClass的父类。

SubClass继承SuperClass,隐藏了SuperClass的属性x并重写了doSomething()方法。

Inheritance为公共类,在此程序中负责包含主进程。

运行结果

img

程序运行过程分析:

  1. 主进程第一条代码new了一个SubClass类型的对象,调用SubClass的无参构造。SubClass()首先调用父类无参构造SuperClass(),则super.x = 3,在控制台输出“in SuperClass:x=3”,this.x = 5,在控制台输出“in SubClass:x=5”。
  2. 第二条代码调用了subClass的doSomething()方法。首先调用父类的doSomething()方法,控制台输出“in SuperClass.doSomething()”,控制台紧接着输出“in SubClass.doSomething()”和“super.x=3”和“sub.x=5”。

Exam4

  1. 接口和继承的综合应用

首先定义一个接口(IShapeArea),其中包含返回面积的方法(getArea)。然后定义一个矩形类(Rectangle)和一个圆类(Circle),并派生出一个正方形类(Square),即正方形类的父类为矩形类,三者都要求实现接口IShapeArea,自行扩充成员变量和方法(比如周长和面积)。

在主方法 main 中建一数组,数组中放入一些上述类型的对象,并计算它们的周长和面积之和。

code

IShapeArea.java

1
2
3
4
5
6
7
package project4B.interfaceTest;

public interface IShapeArea {

double getArea();
float getPerimeter();
}

Rectangle.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package project4B.interfaceTest;

public class Rectangle implements IShapeArea{
float length,width,perimeter;
double area;
public Rectangle(){

}

public Rectangle(float length, float width) {
this.length = length;
this.width = width;
this.perimeter = getPerimeter();
this.area = getArea();
}

@Override
public float getPerimeter() {
return 2*(width+length);
}

@Override
public double getArea() {
return length*width;
}

}

Circle.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package project4B.interfaceTest;

public class Circle implements IShapeArea{
float r,perimeter;
double area;

public Circle(float r) {
this.r = r;
this.perimeter = getPerimeter();
this.area = getArea();
}

@Override
public float getPerimeter() {
return (float)(2*Math.PI*r);
}

@Override
public double getArea() {
return Math.PI*r*r;
}
}

Square.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package project4B.interfaceTest;

public class Square extends Rectangle implements IShapeArea{

public Square(float length) {
this.length = length;
this.perimeter = getPerimeter();
this.area = getArea();
}

@Override
public float getPerimeter() {
return 4*length;
}

@Override
public double getArea() {
return length*length;
}
}

Test.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package project4B.interfaceTest;

public class Test {
public static void main(String[] args) {
//分别创建容量为3的Rectangle类、Circle类、Square类对象数组
Rectangle[] rectangles = new Rectangle[3];
Circle[] circles = new Circle[3];
Square[] squares = new Square[3];

for(int i = 0;i < 3;i++){
rectangles[i] = new Rectangle((float)(100*Math.random()),(float)(100*Math.random()));
circles[i] = new Circle((float)(100*Math.random()));
squares[i] = new Square((float)(100*Math.random()));
}

//输出显示三个矩形对象的长宽、周长和面积
for(int i = 0; i < 3;i++)
System.out.println("rectangles["+i+"]的长为"+String.format("%.3f",rectangles[i].length)+",宽为"+String.format("%.3f",rectangles[i].width)+",周长为"+String.format("%.3f",rectangles[i].perimeter)+",面积为"+String.format("%.3f",rectangles[i].area));

System.out.println("================================================");
//输出显示三个圆形对象的半径、周长和面积
for (int i = 0;i < 3;i++)
System.out.println("circle["+i+"]的半径为"+String.format("%.3f",circles[i].r)+",周长为"+String.format("%.3f",circles[i].perimeter)+",面积为"+String.format("%.3f",circles[i].area));

System.out.println("================================================");
//输出显示三个正方形对象的边长、周长和面积
for(int i = 0;i < 3;i++)
System.out.println("square["+i+"]的边长为"+String.format("%.3f",squares[i].length)+",周长为"+String.format("%.3f",squares[i].perimeter)+",面积为"+String.format("%.3f",squares[i].area));
}
}

运行结果

img

程序分析:

程序通过实现接口IShapeArea得到类Rectangle、Circle和Square,Square实现接口的同时继承Rectangle。在实现接口和继承中,均完成重写接口的方法或父类的方法。

并定义一个主进程所在类Test,创建三个不同类的对象数组,利用Math.random()方法给三个对象数组的各个对象的属性赋值。最后利用String.format()方法进行格式化输出将数据的精度控制在三位小数。

Exam5

  1. 接口的练习为某研究所编写一个通用程序,用来计算每一种交通工具运行1000米所需的时间,已知每种交通工具的参数都是3个整数A、B、C的表达式。现有两种工具:Car和Plane,其中Car的速度运算公式为:A*B/C,Plane的速度运算公式为:A+B+C。需要编写4类:ComputeTime、Plane、Car和接口(ICommon),要求在未来如果增加第3种交通工具的时候,不必修改以前的任何程序,只需要编写新的交通工具的程序即可,其运行过程如下,从命令行输入ComputeTime的四个参数,第一个是交通工具的类型,第二、三、四个参数分别是整数A、B、C,我们在Dos下运行时输入如下:计算Plane的时间:”java ComputeTime Plane 20 30 40”计算Car的时间:“java ComputeTime Car 23 34 45”

code

ComputeTime.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package project4B;

import javax.swing.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

interface ICommon {
float getSpeed();
}

class Plane implements ICommon {

float A,B,C;
public Plane(float a, float b, float c) {
A = a;
B = b;
C = c;
}

public float getA() {
return A;
}

public void setA(float a) {
A = a;
}

public float getB() {
return B;
}

public void setB(float b) {
B = b;
}

public float getC() {
return C;
}

public void setC(float c) {
C = c;
}


@Override
public float getSpeed() {
return A+B+C;
}
}


class Car extends Plane {

public Car(float a, float b, float c) {
super(a,b,c);
}

@Override
public float getA() {
return super.getA();
}

@Override
public void setA(float a) {
super.setA(a);
}

@Override
public float getB() {
return super.getB();
}

@Override
public void setB(float b) {
super.setB(b);
}

@Override
public float getC() {
return super.getC();
}

@Override
public void setC(float c) {
super.setC(c);
}

@Override
public float getSpeed() {
return A*B/C;
}
}


public class ComputeTime {
@SuppressWarnings("unchecked")
public static void main(String args[]) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
String s = "project4B."+args[0];
float Atemp = Float.parseFloat(args[1]);
float Btemp = Float.parseFloat(args[2]);
float Ctemp = Float.parseFloat(args[3]);
//获取类
Class Trans = Class.forName(s);
//获取类的指定构造器
Constructor constructor = Trans.getDeclaredConstructor(float.class,float.class,float.class);
//调用类的有参构造
ICommon trans = (ICommon) constructor.newInstance(Atemp,Btemp,Ctemp);

Method getA = Trans.getDeclaredMethod("getA");
Method getB = Trans.getDeclaredMethod("getB");
Method getC = Trans.getDeclaredMethod("getC");
Method getSpeed = Trans.getDeclaredMethod("getSpeed");
float A = (float)getA.invoke(trans);
float B = (float)getB.invoke(trans);
float C = (float)getC.invoke(trans);
float speed = (float) getSpeed.invoke(trans);


System.out.println("A:"+A+"\tB:"+B+"\tC"+C);
System.out.println("Speed of "+args[0]+" is "+speed);
}

}

运行结果

Dos运行ComputeTime.java操作过程:

在文件当前目录下打开命令行终端

  1. 输入javac ComputeTime.javaimg编译java文件成功生成class文件。img
  2. 输入cd..退回到java文件所在根目录(Java_Code)下的第一子目录(project4B)img
  3. 输入java project4B.ComputeTime Plane 20 30 40img 得到飞机速度结果。
  4. 输入java project4B.ComputeTime Car 23 34 45img得到汽车速度的结果。

拓展

添加一个Bike类,不对其他类进行任何修改,实现Bike的数据处理,Bike的速度计算公式为A*C/B。

在上述ComPuteTime.java文件中添加如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class Bike extends Plane{
public Bike(float a, float b, float c) {
super(a, b, c);
}

@Override
public float getA() {
return super.getA();
}

@Override
public void setA(float a) {
super.setA(a);
}

@Override
public float getB() {
return super.getB();
}

@Override
public void setB(float b) {
super.setB(b);
}

@Override
public float getC() {
return super.getC();
}

@Override
public void setC(float c) {
super.setC(c);
}

@Override
public float getSpeed() {
return A*C/B;
}
}

保存程序并重新编译(操作步骤和上面相同,不再赘述)。

运行结果:

img

程序总结:

该程序主要思想是运用了Java的反射机制,对在Dos下输入的数据进行处理,对第一个输入的字符串进行className查询,并将查询到的class进行调用构造器和调用方法操作,从而达到对输入的字符串进行分类数据处理。

在Plane实现接口后,往后的交通工具都可以对其进行继承操作从而得到Plane对ICommon接口的实现,再重写speed的算法即可。

遇到问题:

开始时,我是在一个package中添加了ICommon.java, Plane.java, Car.java, ComputeTime.java。但是在编译过程中,ICommon因为没有import其他自定义的java文件,所以编译顺利通过生成ICommon.class文件。但是在编译Plane.java时,import了ICommon,提示错误“无法找到字符”。上网查了一个小时没找到处理方法,最后就将接口和所有类合并到ComputeTime.java中了。报错的原因是因为只有import库中有的文件才行吗?而自定义的文件没有放到库中所以报错?

实验总结:

Java有很多有趣的机制,例如反射、注解、异常等。非常值得学习且有趣的一种高级语言。