实验4 包与异常处理
一、实验目的
理解Java包机制的作用,掌握Java中的四种访问控制级别;理解异常处理机制,掌握Java异常处理的基本方法。
二、实验内容
1.程序理解:
1)包的定义和引入
在包Com.tsinghua.p1中定义了一个ClassA类,里面定义的四个不同修饰符的变量和方法,在方法中分别打印出对应修饰符变量的值。在该类的main()方法中尝试通过该类的实例对象调用了四个变量和四个方法。说明了四个修饰符号在同一个类中是可调用的。
package com.tsinghua.p1;
public class ClassA{
private String s_pri = "classA private str";
String s_fri = "classA friendly str";
protected String s_pro = "classA protected str";
public String s_pub = "classA public str";
private void printPriStr(){
System.out.println(" ClassA s_pri :"+this.s_pri);
}
void printFriStr(){
System.out.println(" ClassA s_fri :"+this.s_fri);
}
protected void printProStr(){
System.out.println(" ClassA s_pro :"+this.s_pro);
}
public void printPubStr(){
System.out.println(" ClassA s_pub :"+this.s_pub);
}
public static void main(String[] args){
ClassA a = new ClassA();
a.printPriStr();
a.printFriStr();
a.printProStr();
a.printPubStr();
System.out.println(a.s_pri);
System.out.println(a.s_fri);
System.out.println(a.s_pro);
System.out.println(a.s_pub);
}
}
在同一个包中继承ClassA类创建子类ClassBA,然后实例了该类的一个对象,然后调用了父类中的方法和变量。私有变量和私有方法都不能在继承类中使用,不论是同类中的还是非同类中的。
package com.tsinghua.p1;
public class ClassBA extends ClassA{
public static void main(String[] args){
ClassBA ba = new ClassBA();
ba.printFriStr();
ba.printProStr();
ba.printPubStr();
//私有变量不能访问
// System.out.println(ba.s_pri);
System.out.println(ba.s_fri);
System.out.println(ba.s_pro);
System.out.println(ba.s_pub);
ClassA a = new ClassA();
//私有方法不能调用
// a.printPriStr();
a.printFriStr();
a.printProStr();
a.printPubStr();
//私有变量不能访问
// System.out.println(a.s_pri);
System.out.println(a.s_fri);
System.out.println(a.s_pro);
System.out.println(a.s_pub);
}
}
在同一个包中创建一个新的类,然后在新类中实例化一个ClassA的对象,然后尝试调用ClassA中定义的变量和方法。
注意:除了私有变量和私有方法外,其他的在同一个包中的不同类中都可以调用。
package com.tsinghua.p1;
public class ClassB {
public static void main(String[] args){
ClassA a = new ClassA();
// a.printPriStr(); 私有变量不可调用
a.printProStr();
a.printFriStr();
a.printPubStr();
// System.out.println(a.s_pri); 私有方法不可调用
System.out.println(a.s_pro);
System.out.println(a.s_fri);
System.out.println(a.s_pub);
}
}
在另一个包Com.tsinghua.p2中创建一个新的类ClassCA并继承ClassA,然后在该类中实例化该类的一个对象,然后调用父类中的变量和方法。
注意:
不同包中的子类,只能调用父类中protected和public定义的方法或变量。
package com.tsinghua.p2;
import com.tsinghua.p1.ClassA; //引入com.tsinghua.p1包中的ClassA类。
public class ClassCA extends ClassA{
public static void main(String[] args){
ClassCA ca = new ClassCA();
// ca.printPriStr();
// ca.printFriStr();
ca.printProStr();
ca.printPubStr();
// System.out.println(ca.s_pri);
// System.out.println(ca.s_fri);
System.out.println(ca.s_pro);
System.out.println(ca.s_pub);
ClassA a = new ClassA();
// a.printPriStr();
// a.printProStr();
// a.printFriStr();
a.printPubStr();
// System.out.println(a.s_pri);
// System.out.println(a.s_pro);
// System.out.println(a.s_fri);
System.out.println(a.s_pub);
}
}
2)程序无法预见的异常必须进行异常处理(try-catch)
系统已定义异常类的处理:NumberFormatException
package com.tsinghua.p2;
import java.io.*;//通过数据流、序列化和文件系统提供系统输入和输出。
public class IntTransfer {
public static void main(String[] args) {
// 从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
BufferedReader re = new BufferedReader(new InputStreamReader(System.in));
String str = "";
System.out.println("请输入整数字符串:");
while (true) {
try {
str = re.readLine();// 读取一个文本行。通过下列字符之一即可认为某行已终止:换行 (‘\n‘)、回车
// (‘\r‘) 或回车后直接跟着换行
int i = Integer.parseInt(str);// 将字符串参数作为有符号的十进制整数进行解析。
System.out.println("输入的整数为:" + i);
break;
} catch (NumberFormatException e) {// 当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常。
System.out.println("输入的整数格式不对,请重新输入:");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
2.编程题
(1)1) 设计一个类,类成员和方法分别用public、private、protected以及“default”访问权限。在本类的main方法中创建属于这个类的一个对象,并观察在试图访问所有类成员时会获得哪种类型的编译器错误提示。(为什么?)设计一个新类,并在新类的main方法中创建属于这个类的一个对象,并观察在试图访问所有类成员时会获得哪种类型的编译器错误提示。(注意同一个目录内的类属于“默认”包的一部分)(为什么?)
2) 用protected数据成员创建一个类,在相同的文件里创建第二个类,用一个方法操纵第一个类里的protected权限的数据成员,观察能否操纵?
(2)写一个类MikeOne,包括一个方法:methodA()打印出类名,放入包mypack.one中,写一个类MikeTwo,包括一个方法:methodB()打印出类名,放入包mypack.two中。定义一个类Cmain,要求在其中可以访问到前面的两个类中的方法。(提示,导入所需包中的类后再访问,获取对象的类名,可以通过对象.getClass().getName()来实现)
(3)在实验3的复数类中增加一个构造方法,该构造方法有两个字符串类型的参数,分别为复数的实部和虚部赋值。在构造方法里先将字符串参数转换为浮点数再赋值给实部或虚部。当字符串的内容不能转化为浮点数时,抛出数值格式异常NumberFormatException。在main函数中处理该异常,并进行测试。
(提示:请参照程序理解的第2题来完成本程序;字符串转换为浮点数调用Double类提供的静态方法parseDouble,转换失败时会抛出数值格式异常NumberFormatException,在main函数中处理此异常)。
(4)自定义非法年龄类IllegalAgeException,定义一个Person类,包含年龄,姓名,性别等属性,编写属性设置和读取函数,在设置年龄函数中,判断参数是否符合要求(1~150),如果不符合则抛出异常,编写main函数进行测试。
三、实验结果和分析
(1)程序1运行结果
1)
分析:设计一个类,类成员和方法分别用public、private、protected以及“default”访问权限。在本类的main方法中创建属于这个类的一个对象,从结果可以看出,private,default,protected,public这四种权限都可以在本类中访问。
分析:设计一个新类,并在新类的main方法中创建属于这个类的一个对象,并观察在试图访问所有类成员时,从结果可以看出:private权限的成员变量和成员函数都不能被访问,说明如果类的成员被private访问控制符来修饰,则这个成员只能被该类的其他成员访问,其他类无法直接访问。
2)
分析:用protected数据成员创建一个类,在同一个包里创建第二个类,在第二个类里创建第一个类的对象,访问protected权限的数据成员,从结果可以看出,如果一个类的成员被protected访问控制符修饰,那么这个成员既能被同一包下的其他类访问,也能被不同包下该类的子类访问。
(2)程序2运行结果
分析:写一个类MikeOne,包括一个方法:methodA()打印出类名,放入包mypack.one中,写一个类MikeTwo,包括一个方法:methodB()打印出类名,放入包mypack.two中。在mypack.two包下定义一个类Cmain,导入所需mypack.one,创建对象,访问前两个类中的方法,(可通过getClass().getName()获取对象的类名,可以通过对象.来实现)
(3)程序3运行结果
分析:自定义一个异常类NumberFormatException继承自Exception,在实验3的复数Complex类中增加一个构造方法,该构造方法有两个字符串类型的参数,分别为复数的实部和虚部赋值,在构造方法里先用 Double.parseDouble(),将字符串参数转换为浮点数,再赋值给实部或虚部。当字符串的内容不能转化为浮点数时,抛出数值格式异常NumberFormatException。在main函数中用try….catch…处理该异常.
(4)程序4运行结果
分析:自定义非法年龄类IllegalAgeException,定义一个Person类,包含年龄,姓名,性别等属性,编写属性设置和读取函数,在设置年龄函数中,判断参数是否符合要求(1~150),如果不符合则抛出异常,编写main函数进行测试。在main()方法中,定义了一个try….catch语句用于捕获setAge()方法跑出的异常,在调用setAge()方法时由于传入的年龄在1-150之间,否则就会抛出一个自定义异常IllegalAgeException,该异常被扑火后最终被catch代码块处理,并打印出异常信息。
四、实验源代码
(1)程序1源代码
1)
package example1;
//定义访问权限测试PermissionTest类
public class PermissionTest1 {
// 定义各种权限的成员变量
private String apri = "私有成员变量被访问鸟~";
String adef = "缺省成员变量被访问鸟~";
protected String apro = "保护成员变量被访问鸟~";
public String apub = "公有成员变量被访问鸟~";
// 定义各种权限的成员函数
private void bpri() {
System.out.println("私有成员函数被访问鸟~");
}
void bdef() {
System.out.println("缺省成员函数被访问鸟~");
}
protected void bpro() {
System.out.println("保护成员函数被访问鸟~");
}
public void bpub() {
System.out.println("公有成员函数被访问鸟~");
}
public static void main(String[] args) {
// 创建类对象
PermissionTest1 p1 = new PermissionTest1();
// 访问所有类成员
System.out.println("在本类的main方法中访问所有类成员的结果为:\n");
System.out.println(p1.apri);
System.out.println(p1.adef);
System.out.println(p1.apro);
System.out.println(p1.apub + "\n");
p1.bpri();
p1.bdef();
p1.bpro();
p1.bpub();
}
}
package example1;
public class PermissionTest2 {
public static void main(String[] args) {
// 创建类对象
PermissionTest1 p2 = new PermissionTest1();
// 访问所有类成员
System.out.println("在新类的main方法中访问所有类成员的结果为:\n");
// System.out.println(p2.apri);
System.out.println("私有成员变量无法访问!");
System.out.println(p2.adef);
System.out.println(p2.apro);
System.out.println(p2.apub + "\n");
// p2.bpri();
System.out.println("私有成员函数无法访问!");
p2.bdef();
p2.bpro();
p2.bpub();
}
}
2)
package example1;
public class PermissionTest3 {
// 定义一个protected成员变量
protected String cpro = "保护成员变量被访问了鸟~";
}
package example1;
public class PermissionTest4 {
public static void main(String[] args) {
PermissionTest3 p4 = new PermissionTest3();
System.out.println(p4.cpro);
}
}
(2)程序2源代码
package mypack.one;
//定义一个类 MikeOne
public class MikeOne {
// 定义一个方法,打印出类名
public void methodA() {
System.out.println(getClass().getName());
}
}
package mypack.two;
//定义一个类 MikeOne
public class MikeTwo {
// 定义一个方法,打印出类名
public void methodB() {
System.out.println(getClass().getName());
}
}
package mypack.two;
//导入mypack.one.MikeOne包
import mypack.one.MikeOne;
public class Cmain {
public static void main(String[] args) {
// 创建对象
MikeOne m1 = new MikeOne();
MikeTwo m2 = new MikeTwo();
// 调用方法
m1.methodA();
m2.methodB();
}
}
(3)程序3源代码
package com.tsinghua.p2;
package com.tsinghua.p2;
import java.io.*;//通过数据流、序列化和文件系统提供系统输入和输出。
//定义一个复数Complex类
public class Complex {
double real;
double im;
// 定义实部和虚部构造方法
public Complex(String real, String im) {
this.real = Double.parseDouble(real);
this.im = Double.parseDouble(im);
}
// 字符串描述(重写Object的toString方法,输出a+bi的形式)
public String toString() {
if (im < 0)
return real + "" + im + "i";
else
return real + "+" + im + "i";
}
public static void main(String[] args) {
// 从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
BufferedReader re = new BufferedReader(new InputStreamReader(System.in));
// 初始化
String str1 = "";
String str2 = "";
System.out.println("请输入复数的实部和虚部:");
while (true) {
try {
str1 = re.readLine();// 读取一个文本行。通过下列字符之一即可认为某行已终止:换行 (‘\n‘)、回车
str2 = re.readLine(); // (‘\r‘) 或回车后直接跟着换行
Complex c1 = new Complex(str1, str2);// 创建Complex对象
System.out.println("输入的复数为" + c1.toString());
break;
} catch (NumberFormatException e) {// 当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常。
System.out.println("输入的浮点数格式不对,请重新输入:");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
(4)程序4源代码
//下面的代码是自定义一个异常类继承自Exception
public class IllegalAgeException extends Exception {
public IllegalAgeException() {
super(); // 调用Exception无参的构造方法
}
public IllegalAgeException(String message) {
super(message); // 调用Exception有参的构造方法
}
}
package com.tsinghua.p2;
import java.util.Scanner;
//定义一个Person类
public class Person {
// 定义年龄,姓名,性别成员变量
private int age;
private String name;
private String sax;
// 编写属性设置和读取函数
void SetAge(int age) throws IllegalAgeException {
if (age >= 150 || age < 0) {
throw new IllegalAgeException("年龄不合法");// 使用throw关键字声明异常对象
}
this.age = age;
}
void SetName(String name) {
this.name = name;
}
void SetSax(String sax) {
this.sax = sax;
}
int GetAge() {
return age;
}
String GetName() {
return name;
}
String GetSax() {
return sax;
}
public static void main(String[] args) {
while (true) {
try {
// 创建对象
Person p = new Person();
Scanner sc = new Scanner(System.in);
System.out.println("请输入姓名,年龄,性别:");
String str1 = sc.next();
int a = sc.nextInt();
String str2 = sc.next();
// 调用属性设置和读取函数的方法
p.SetName(str1);
p.SetAge(a);
p.SetSax(str2);
System.out.println("姓名:" + p.GetName());
System.out.println("年龄:" + p.GetAge());
System.out.println("性别:" + p.GetSax());
break;
} catch (IllegalAgeException e) { // 对捕获到的异常进行处理
System.out.println(e.getMessage()); // 打印捕获的异常信息
}
}
}
}