Skip to content

Java基础Q&A

Java

整理了一些Java编程基础的知识点以及代码。其实是选修课作业

附件:部分源代码

PART 1

1、如何进行JDK的安装与配置?如何编写并运行第一个Java程序?

(1)首先从Oracle官方网站下载安装适合系统的JDK,按默认安装路径进行JDK安装即可。

(2)配置环境变量:在系统环境变量中新建变量JAVA_HOME,值为JDK的安装路径;将%JAVA_HOME%\bin添加到系统变量Path。

设置环境变量如图所示

JAVA_HOME= C:\Program Files\Java\jdk1.8.0_92

CLASSPATH=.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar

PATH=%PATH%;%JAVA_HOME%\bin

注意:%PATH%为原来的环境变量值,添加";"和后面的内容到原来值的后面。

安装JDK环境变量配置和验证

验证是否配置成功,可点击开始→运行,输入cmd打开命令行窗口,输入java -version,显示版本,如图。说明JDK安装及环境变量配置成功。

(3)编写并运行:用记事本编码并存储在D盘;Win+R到命令行D:,找到java文件;命令行输入javac HelloWorld.java编译生成class文件,命令行输入java HelloWorld运行

2、下列哪个是JDK提供的编译器?

答案:B

A. java.exe B. javac.exe C. javap.exe D. javaw.exe

3、Java源程序文件的扩展名是什么?Java字节码文件的扩展名是什么?

答案:java,class

4、下列哪个是Java应用程序主类中正确的main方法声明?

答案:D

A. public void main(String args[ ])

B. static void main(String args[ ])

C. public static void Main(String args[ ])

D. public static void main(String args[ ])

5、编程:

(1)在控制台输出全部的大写英文字母。

1
2
3
4
5
6
7
public class Homework1 {
public static void main(String[] args) {
for (char ch = 'A'; ch <= 'Z'; ch++) {
System.out.print(ch + " ");
}
}
}

运行结果:

1
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 

(2)求和:1 + 2 + 3 + … + 100,并在控制台输出。

1
2
3
4
5
6
7
8
9
public class Homework2 {
public static void main(String[] args) {
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
}
System.out.println("1 + 2 + 3 + … + 100 = " + sum);
}
}

运行结果:

1
1 + 2 + 3 + … + 100 = 5050

6、什么是类?什么是对象?如何创建对象?试编程举例加以说明。

是对一类对象的描述;对象是类的具体实例。Java中创建对象前需要创建对象引用:类名 引用变量名;使用new关键字创建一个新的对象:new 类名( [初始化参数] )

例:

1
2
Clock clock;
clock = new Clock(12, 30, 15);

7、什么是实例变量?什么是类变量?什么是成员方法?试编程举例加以说明。

**实例变量(属于对象的属性):**没有 static 修饰的变量(数据成员)称为实例变量;存储所有实例都需要的属性,不同实例的属性值可能不同;可通过表达式 实例名.实例变量名 访问实例属性的值。

**类变量(静态变量)(为该类的所有对象共享):**用 static 修饰;在整个类中只有一个值;类初始化的同时就被赋值。适用情况于类中所有对象都相同的属性、经常需要共享的数据和系统中用到的一些常量值。引用格式:类名或实例名.类变量名

**实例方法:**用来表示每一个实例对象的功能或者行为。表示特定对象的行为;声明时前面不加 static 修饰符;实例方法调用格式:对象名.方法名([参数列表]);

例:

1
2
3
4
5
6
7
8
9
10
public class Circle {
double radius;
static final double PI = 3.14159;
Circle(double r){
radius = r;
}
public double area() {
return PI * radius * radius;
}
}

radius 是实例变量,PI 是类变量,area() 是实例方法。他们的调用方法如下:

1
2
3
4
5
6
7
8
9
public class ShapeTester {
public static void main(String[] args) {
Circle circle = new Circle(2.5);
System.out.println(Circle.PI);
System.out.println(circle.PI);
System.out.println(circle.radius);
System.out.println(circle.area());
}
}

运行结果:

1
2
3
4
3.14159
3.14159
2.5
19.6349375

8、类的继承格式是怎样的?试编程举例加以说明。

类继承的语法为:

1
2
3
[ClassModifier] class ClassName extends SuperClassName {
//类体
}

例:

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
//Person.java
public class Person {
private String name;
private String gender;
private int age;
public Person(String name, String gender, int age) {
this.name = name;
this.gender = gender;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String getGender() {
return gender;
}
public void grow() {
age++;
}
public String toString() {
return name+", "+gender+", "+age+" years old";
}
}
//Student.java
public class Student extends Person {
private int grade;
private String id;
public Student(String name, String gender, int age,
int grade, String id) {
super(name, gender, age);
this.grade = grade;
this.id = id;
}
public int getGrade() {
return grade;
}
public String getId() {
return id;
}
@Override
public void grow() {
super.grow();
grade++;
}
@Override
public String toString() {
return getName()+", "+"Grade "+grade+", "
+getGender()+", "+getAge()+" years old"+", Student id: "+id;
}
}
//Tester.java
public class Tester {
public static void main(String[] args) {
Student Peter = new Student("Peter", "male", 18, 1, "1927405001");
Person Mary = new Person("Mary", "female", 20);
System.out.println(Peter);
System.out.println(Mary);
Peter.grow();
Mary.grow();
System.out.println("Next year.");
System.out.println(Peter);
System.out.println(Mary);
}
}

运行结果:

1
2
3
4
5
Peter, Grade 1, male, 18 years old, Student id: 1927405001
Mary, female, 20 years old
Next year.
Peter, Grade 2, male, 19 years old, Student id: 1927405001
Mary, female, 21 years old

PART2

1、在面向对象编程技术中,什么是继承?试编程举例加以说明。

继承是根据已有类来设计新类,新类具有已有类的所有功能(属性和行为)。

继承机制可以提高程序的抽象程度,提高代码的可重用性。

例如 有以下继承关系:

继承

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
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("Name: " + name + "\tAge: " + age);
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void play() {
System.out.println("Playing......");
}
}
public class Student extends Person {
private String studentId;
public Student(String name, int age, String id) {
super(name, age);
studentId = id;
System.out.println("Student No." + studentId);
}
public String getStudentId() {
return studentId;
}
public void study() {
System.out.println("Studying......");
}
}
public class Teacher extends Person {
private String teacherId;
public Teacher(String name, int age, String id) {
super(name, age);
teacherId = id;
System.out.println("Teacher No." + teacherId);
}
public String getTeacherId() {
return teacherId;
}
public void teach() {
System.out.println("Teaching......");
}
}
public class Tester {
public static void main(String[] args) {
Person Jack = new Person("Jack", 20);
Jack.play();
System.out.println();
Student Holger = new Student("Holger", 18, "19274001");
Holger.play();
Holger.study();
System.out.println();
Teacher Mary = new Teacher("Mary", 30, "2020270001");
Mary.play();
Mary.teach();
}
}

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
Name: Jack	Age: 20
Playing......

Name: Holger Age: 18
Student No.19274001
Playing......
Studying......

Name: Mary Age: 30
Teacher No.2020270001
Playing......
Teaching......

2、在面向对象编程技术中,什么是重写?试编程举例加以说明。

如果子类不需要使用从超类继承来的方法,可以声明自己的同名方法,称为重写Override(方法覆盖),即外壳不变,核心重写

重写的方法的返回类型方法名称参数个数必须和被覆盖的方法完全一致。访问权限可以更宽松,但不能更严格。重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常。

重写的应用场合

  • 子类中实现与超类相同的功能,但算法或公式不一样;
  • 名字相同的方法中,要做比超类方法更多的事情;
  • 子类中要取消从超类继承过来的方法。

注意事项

  • 参数列表必须完全与被重写方法的相同
  • 返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类
  • 访问权限不能比父类中被重写的方法的访问权限更低
  • 父类的成员方法只能被它的子类重写
  • 必须覆盖的方法:派生类必须覆盖基类中的抽象方法,否则派生类自身将成为抽象类
  • 不能覆盖覆盖的方法:终结方法(final)、静态方法(static)
  • 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法;子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法
  • 调用被覆盖的方法:super.被覆盖的方法名();

例如 Student类重写了从Person类继承的grow方法。

重写

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
public class Person {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("Name: " + name + ",\tAge: " + age);
}
public void grow() {
age++;
}
}
public class Student extends Person {
public int grade;
public Student(String name, int age, int grade) {
super(name, age);
this.grade = grade;
System.out.println("Grade " + grade);
}
@Override
public void grow() {
age++;
grade++;
}
}
public class Tester {
public static void main(String[] args) {
Person Jack = new Person("Jack", 20);
Student Holger = new Student("Holger", 18, 1);
System.out.println("One year later...");
Jack.grow();
Holger.grow();
System.out.println(Jack.name+"\t"+Jack.age);
System.out.println(Holger.name+"\t"+Holger.age+"\t"+Holger.grade);
}
}

运行结果:

1
2
3
4
5
6
Name: Jack,	Age: 20
Name: Holger, Age: 18
Grade 1
One year later...
Jack 21
Holger 19 2

3、在面向对象编程技术中,什么是重载?试编程举例加以说明。

重载(overloading) 是在一个类里面,方法名字相同,而参数不同。

返回类型可以相同也可以不同。

每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。

最常用的地方就是构造方法的重载。

重载规则:

  • 被重载的方法必须改变参数列表(参数个数或类型不一样);
  • 被重载的方法可以改变返回类型;
  • 被重载的方法可以改变访问修饰符;
  • 被重载的方法可以声明新的或更广的检查异常;
  • 方法能够在同一个类中或者在一个子类中被重载。
  • 无法以返回值类型作为重载函数的区分标准。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Dog {
public void bark() {
System.out.println("Woof!");
}
public void bark(int num) {
for (int i = 0; i < num; i++) {
System.out.println("Woof!");
}
}
}
public class Tester {
public static void main(String[] args) {
Dog dog = new Dog();
dog.bark();
System.out.println();
dog.bark(3);
}
}

运行结果:

1
2
3
4
5
Woof!

Woof!
Woof!
Woof!

4、重写和重载的区别是什么?

(1)方法重载是一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法的重载(Overloading)。

(2)方法重写是在子类存在方法与父类的方法的名字相同,而且参数的个数与类型一样,返回值也一样的方法,就称为重写(Overriding)。

(3)方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。

区别点 重载方法 重写方法
参数列表 必须修改 一定不能修改
返回类型 可以修改 一定不能修改
异常 可以修改 可以减少或删除,一定不能抛出新的或者更广的异常
访问 可以修改 一定不能做更严格的限制(可以降低限制)

5、在面向对象编程技术中,什么是抽象类和抽象方法?试编程举例加以说明。

抽象类

规定了整个类家族公共的属性和方法。

  • 类名前加修饰符 abstract ;
  • 可包含常规类能包含的任何成员,包括非抽象方法;
  • 也可包含抽象方法:用 abstract 修饰,只有方法原型,没有方法的实现;
  • 没有具体实例对象的类,不能使用new方法进行实例化,只能用作超类
  • 只有当子类实现了抽象超类中的所有抽象方法,子类才不是抽象类,才能产生实例;
  • 如果子类中仍有抽象方法未实现,则子类也只能是抽象类。

抽象方法

  • 仅有方法原型,没有方法体;
  • 具体实现在各自的类声明中完成;
  • 只有抽象类可以包含抽象方法。

抽象方法的优点

  • 隐藏具体的细节信息,所有的子类使用的都是相同的方法原型,其中包含了调用该方法时需要了解的全部信息;
  • 强迫子类完成指定的行为,规定所有子类的“标准”行为。

例如 抽象类Animal类,Cat类和Dog类继承自Animal类。关系如下:

抽象

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
public abstract class Animal {
public abstract void run();
public abstract void eat();
}
public class Cat extends Animal {
@Override
public void run() {
System.out.println("The cat is running.");
}
@Override
public void eat() {
System.out.println("The cat is eating cat food and fish.");
}
public void jump() {
System.out.println("The cat can jump!");
}
}
public class Dog extends Animal{
@Override
public void run() {
System.out.println("The dog is running.");
}
@Override
public void eat() {
System.out.println("The dog is eating dog food and bones.");
}
public void bark() {
System.out.println("The dog is barking. Woof!");
}
}
public class Tester {
public static void main(String[] args) {
Cat cat = new Cat();
cat.eat();
cat.run();
cat.jump();
System.out.println();
Dog dog = new Dog();
dog.eat();
dog.run();
dog.bark();
}
}

运行结果:

1
2
3
4
5
6
7
The cat is eating cat food and fish.
The cat is running.
The cat can jump!

The dog is eating dog food and bones.
The dog is running.
The dog is barking. Woof!

6、在面向对象编程技术中,什么是接口?试编程举例加以说明。

接口可以看做是一个“纯”抽象类,它只提供一种形式,并不提供实现。

接口中可以规定方法的原型:方法名、参数列表以及返回类型,但不规定方法主体;也可以包含基本数据类型的数据成员,但它们都默认为static和final。

接口的作用

  • 是面向对象的一个重要机制

  • 是继承多个设计

  • 建立了类和类之间的“协议”:

    • 将类根据其实现的功能分组用接口代表,而不必顾虑它所在的类继承层次;这样可以最大限度地利用动态绑定,隐藏实现细节
    • 实现不同类之间的常量共享
  • 接口允许我们在看起来不相干的对象之间定义共同行为

例如 Shape是接口。Circle 类及 Rectangle 类实现接口 Shape。关系如下图表示:

接口

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
interface Shape {
double pi = 3.14159;
double area();
}
class Circle implements Shape {
double radius;
public Circle(double r) {
radius = r;
System.out.println("New Circle: radius = " + radius);
}
public double area() {
return (pi * radius * radius);
}
}
class Rectangle implements Shape {
int length, width;
public Rectangle(int l, int w) {
length = l;
width = w;
System.out.println("New Rectangle: length = " + length
+ ", width = " + width);
}
public double area() {
return (width * length);
}
}
public class InterfaceTester {
public static void main(String[] args) {
Circle cir;
cir = new Circle(2.0);
System.out.println("Area of Circle cir is " + cir.area());
System.out.println();
Rectangle rec;
rec = new Rectangle(5, 6);
System.out.println("Area of Rectangle rec is " + rec.area());
System.out.println();
}
}

运行结果:

1
2
3
4
5
New Circle: radius = 2.0
Area of Circle cir is 12.56636

New Rectangle: length = 5, width = 6
Area of Rectangle rec is 30.0

PART 3

1、Java异常处理是怎样的?试编程举例加以说明。

何为异常

简单地说,异常(Exception,又称例外)是指程序运行时所发生的不正常的事件。原因有很多,例如除零、下标越界、文件不存在、类型错误、装载一个不存在的类或者对 null 对象操作等等。

如果这些异常得不到正确的处理将会导致程序终止运行,而合理地使用异常处理结果可以使得程序更加健壮,具有更强的容错性,不会因为用户不小心的错误输入或其他运行时原因而造成程序终止;也可以使用异常处理结构为用户提供更加友好的提示。

当Java程序出现以上的异常时,就会在所处的方法中产生一个异常对象。这个异常对象包括异常的类型,异常出现时程序的运行状态以及对该异常的详细描述。

异常的类型

  • 检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
  • 运行时异常: 运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
  • 错误: 错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。

Exception 类的层次

所有的异常类是从java.lang.Exception类继承的子类。

Exception 类的层次

Exception类是Throwable类的子类。除了Exception类外,Throwable还有一个子类Error。Error用来指示运行时环境发生的错误。

捕获异常

Java的异常处理是通过5个关键字来实现的 try、catch、finally、throw、throws。

  • try:将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当try语句块内发生异常时,异常就被抛出。
  • catch:捕获异常——捕获try语句块中发生的异常。
  • finally:无论是否发生异常,代码总能执行。主要用于回收在try块里打开的物力资源(如数据库连接、网络连接和磁盘文件)。

声明异常

  • throws:用在方法签名中,声明方法可能要抛出的各种异常。

抛出异常

  • throw:手动抛出异常。

try-catch 结构

try/catch 代码块放在异常可能发生的地方。try/catch 代码块中的代码称为保护代码,使用 try/catch 的语法如下:

1
2
3
4
5
try {
//可能出错的程序代码
} catch(ExceptionClassName e) {
//catch块
}

catch语句包含要捕获异常类型的声明。当保护代码块中发生一个异常时,try 后面的 catch 块就会被检查。

如果发生的异常包含在 catch 块中,异常会被传递到该 catch 块,这和传递一个参数到方法是一样。

  • 情况1: try块中代码没有出现异常
    执行catch块代码,执行catch块后边的代码
  • 情况2: try块中代码出现异常,catch中异常类型匹配(相同或者父类)
    执行catch块代码,执行catch块后边的代码
  • 情况3: try块中代码出现异常, catch中异常类型不匹配
    不执行catch块代码,不执行catch块后边的代码,程序会中断运行

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.util.Scanner;

public class Question1_1 {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
try {
System.out.print("Please input an integer: ");
int num = in.nextInt();
System.out.println("3/" + num + "=" + 3 / num);
} catch (ArithmeticException error) {
System.out.println("A number cannot be divided by zero.");
}
System.out.println("Program finished.");
}
}

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 第一次运行:
Please input an integer: 3
3/3=1
Program finished.
# 第二次运行:
Please input an integer: 0
A number cannot be divided by zero.
Program finished.
# 第三次运行:
Please input an integer: c
Exception in thread "main" java.util.InputMismatchException
at java.base/java.util.Scanner.throwFor(Scanner.java:939)
at java.base/java.util.Scanner.next(Scanner.java:1594)
at java.base/java.util.Scanner.nextInt(Scanner.java:2258)
at java.base/java.util.Scanner.nextInt(Scanner.java:2212)
at homework0401.Question1_1.main(Question1_1.java:10)

注意

  • 出现异常后,Java会生成相应的异常对象寻找匹配的catch块,找到后将异常对象赋给catch块的异常参数。
  • 出现异常后,try块中尚未执行的语句不会执行;出现异常后并处理后,try-catch结构后面的语句还会执行。
  • catch块不能独立于try块存在;同样,try块也不能独立存在。

还可以调用异常对象的方法输出异常信息。下表是 Throwable 类的主要方法:

Throwable 类的主要方法

在catch块中,还可以继续向上抛出异常 throw e

多重捕获块

一个try块后面跟随多个catch块的情况就叫多重捕获。

1
2
3
4
5
6
7
8
9
10
try{
//程序代码
}catch(ExceptionClassName1 ExpectionObjectName1){
//程序代码
}catch(ExceptionClassName2 ExpectionObjectName2){
//程序代码
}catch(ExceptionClassName3 ExpectionObjectName3){
//程序代码
}
//……
  • 可以在try语句后面添加任意数量的catch块。
  • 如果保护代码中发生异常,异常被抛给第一个catch块。如果抛出异常的数据类型与ExceptionClassName匹配,它在这里就会被捕获;如果不匹配,它会被传递给后面的catch块,直到异常被捕获或者通过所有的catch块。

注意

  • 执行其中一条catch语句后,其后catch语句将被忽略。
  • 在安排catch语句的顺序时,首先应该捕获最特殊的异常, 然后再逐渐一般化,即先子类后父类。

例如,把上面的例子稍微改动一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.util.InputMismatchException;
import java.util.Scanner;

public class Question1_2 {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
try {
System.out.print("Please input an integer: ");
int num = in.nextInt();
System.out.println("3/" + num + "=" + 3 / num);
} catch (ArithmeticException e1) {
System.out.println("A number cannot be divided by zero.");
} catch (InputMismatchException e2) {
System.out.println("Not an integer.");
} catch (Exception e3) {
System.out.println("Undefined Exception.");
}
System.out.println("Program finished.");
}
}

运行结果:

1
2
3
4
5
6
7
8
# 第一次运行:
Please input an integer: 0
A number cannot be divided by zero.
Program finished.
# 第二次运行:
Please input an integer: c
Not an integer.
Program finished.

finally关键字

在try-catch块后加入finally块,可以确保无论是否发生异常,finally块中的代码总能被执行。

通常在finally中关闭程序块已打开的资源,比如:文件流、释放数据库连接等。

finally 代码块出现在 catch 代码块最后,语法如下:

1
2
3
4
5
6
7
8
9
10
try{
// 程序代码
}catch(ExceptionClassName1 ExpectionObjectName1){
//程序代码
}catch(ExceptionClassName2 ExpectionObjectName2){
//程序代码
}/*更多的catch块*/
finally{
//finally块
}

finally块中语句不执行的唯一情况:异常处理代码中执行 System.exit(1) 退出Java虚拟机。

注意

只有finally块执行完成之后,才会回来执行try块或者catch块中的return或throw语句,如果finally块中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止。

例如,上面的例子再做改动:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.util.InputMismatchException;
import java.util.Scanner;

public class Question1_3 {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
try {
System.out.print("Please input an integer: ");
int num = in.nextInt();
System.out.println("3/" + num + "=" + 3 / num);
} catch (ArithmeticException e1) {
System.out.println("A number cannot be divided by zero.");
} catch (InputMismatchException e2) {
System.out.println("Not an integer.");
} catch (Exception e3) {
System.out.println("Undefined Exception.");
} finally {
System.out.println("Program finished.");
}
}
}

运行结果略。

声明异常 throws

当检查性异常产生时,不一定立刻处理它,可以说明用throws把异常声明出来,让专门负责异常处理的代码来解决。throws关键字放在方法签名的尾部。

  • 如果一个方法抛出多个已检查异常,就必须在方法的首部列出所有的异常,之间以逗号隔开。
  • throw了运行时异常,则方法声明中不需要throws
  • 子类声明的异常范围不能超过父类声明范围。
  • 父类没有声明异常,子类也不能。
  • 不可抛出原有方法抛出异常类的父类或上层类。

手动抛出异常 throw

也可以使用throw关键字抛出一个异常,无论它是新实例化的还是刚捕获到的。

注意抛出运行时异常和检查性异常的区别:

  • 抛出检查性异常,该throw语句要么处于try块中,要么方法签名中使用throws抛出;
  • 抛出运行时异常,没有以上要求。

自定义异常

在程序中,可能会遇到任何标准异常类都没有充分的描述清楚的问题,这种情况下可以创建自己的异常类。

  • 所有异常都必须是Throwable的子类。-
  • 如果希望写一个检查性异常类,则需要继承Exception类。
  • 如果你想写一个运行时异常类,那么需要继承RuntimeException类。

可以像下面这样定义自己的异常类:

1
2
3
class MyException extends Exception {
//……
}

只继承Exception类来创建的异常类是检查性异常类。一个异常类和其它任何类一样,包含有变量和方法。

习惯上,定义的类应该包含2个构造方法:一个是默认构造方法,另一个是带有详细信息的构造方法。

2、Java匿名内部类的使用是怎样的?试编程举例加以说明。

在Java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。Thinking in Java中这样说:可以将一个类的定义放在另外的一个类的定义中,这就是一个内部类。

如果我们定义的一个内部类需要多次使用,那肯定要给他一个名字;但是如果我们定义一个内部类就唯一使用一次,仅仅在当前位置使用一下,那其实他可以连名字都没有,这就是匿名内部类。在Java里面提供了像这样的定义匿名内部类的方法,匿名内部类在swing事件处理和Android开发中都有广泛的用途。

使用匿名内部类

匿名内部类由于没有名字,所以它的创建方式有点儿奇怪。创建格式如下:

1
2
3
new SuperType(/* construction parameters */) | InterfaceType() { 
//匿名内部类的类体部分
}

使用匿名内部类必须要继承一个父类或者实现一个接口,也仅能只继承一个父类或者实现一个接口。由于匿名内部类不能是抽象类,所以它必须要实现它的抽象父类或者接口里面所有的抽象方法。

同时它也没有class关键字,匿名内部类直接使用new来生成隐式的对象的引用。创建匿名内部类时它会立即创建一个该类的实例,同时该类的定义会立即消失,所以匿名内部类是不能够被重复使用。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Parcel {
public Destination destination(String s) { //返回Destination引用
return new Destination() {
//这里用一个匿名类实现了Destination接口,并创建了一个实例引用
private String label = s;
public String readLabel() {
return label;
}
};
}

public static void main(String[] args) {
Parcel parcel = new Parcel();
Destination d = parcel.destination("Tasmania");
}
}

注意

  • 匿名内部类中不能存在任何的静态成员变量和静态方法。
  • 匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。
  • 匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。
  • 匿名内部类中是不能定义构造函数的。但我们可以使用实例初始化块达到与构造方法类似的效果,例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Parcel {
public Destination destination(String s, float price) {
return new Destination() {
private int cost;
{ //实例初始化块,相当于构造方法
cost = Math.round(price);
if(cost > 100)
System.out.println("Over budget!");
}
private String label = s;
public String readLabel() {
return label;
}
};
}

public static void main(String[] args) {
Parcel parcel = new Parcel();
Destination d = parcel.destination("Tasmania", 101.395F);
}
}

匿名内部类是唯一一种没有构造器的类。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。匿名内部类在编译的时候由系统自动起名为类似 Outter$1.class 的名字。一般来说,匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。

参考

  1. Java 教程 | 菜鸟教程
  2. Java 类与对象(上)
  3. Java 类与对象(下)
  4. Java 类的重用
  5. Java 接口

About this Post

This post is written by Holger, licensed under CC BY-NC 4.0.

#Java