6. 内部类与异常类
6.1 内部类
类有两种成员:成员变量和成员方法。
java还允许可以有一种成员:内部类。
-
什么是内部类?
在一个类中定义另一个类,这样定义在类中的类称为内部类,包含内部类的类称为内部类的外嵌类。
-
内部类和外嵌类的关系:
- 内部类的外嵌类的成员变量在内部类中依然有效,内部类中的方法也可以调用外部类中的方法
- 内部类的类体中不可以声明类变量和类方法。外嵌类的类体中可以用内部类声明对象,作为外嵌类的成员
- 内部类仅供它的外嵌类使用,其他类不可以用某个类的内部类声明对象
**案例:**某种类型的农场饲养了一种特殊种类的牛,但不需要其他农场饲养这种特殊种类的牛,就可以创建这种特殊种类的牛类作为农场类的内部类。
class RedCowForm{
static String formName;
RedCow cow; //用内部类声明对象,作为外嵌类的成员
RedCowForm(){
}
RedCowForm(String s){
cow = new RedCow(150,112,5000);
formName = s;
}
public void showCowMess(){
cow.speak();
}
// 定义内部类RedCow
class RedCow{
String cowName = "红牛";
int height,weight,price;
RedCow(int h,int w,int p){
height = h;
weight = w;
price = p;
}
void speak(){
System.out.println(cowName+height+weight+formName);
}
}
}
public class Example7_1 {
public static void main(String[] args) {
RedCowForm form = new RedCowForm("红牛农场");
form.showCowMess();
form.cow.speak();
}
}
注意:
- 内部类对应的字节码文件名格式:外嵌类名$内部类名
- 内部类可以是static类型,这样子可以使用static内部类在其他类中创建对象。(非内部类不可以是static类)
6.2 匿名类
java允许直接用一个类的子类的类体创建一个子类对象,这个子类的类体就称为匿名类。
-
例如:假设Bank是类,那么下面的代码就是使用Bank的匿名类创建对象:
new Bank(){ 类体 };
-
匿名类特点:
- 匿名类可以继承父类的方法也可以重写父类的方法
- 使用匿名类时,必然是在某个类中直接用匿名类创建对象,因此匿名类一定是内部类
- 匿名类可以可以访问外部类中的成员变量和方法,匿名类的类体中不可以声明static变量和static方法
- 匿名类创建对象时,直接使用父类的构造方法。
注意:因为匿名类没有类名,不可以声明变量,所以匿名类创建的对象的引用只能赋值给它的父类声明的变量,这样子就可以使用上转型对象的特点去调用匿名类类体中重写的继承的方法。
案例:
abstract class OutputAlphabet{
public abstract void output();
}
class OutputEnglish extends OutputAlphabet{
public void output(){
for(char c='a';c<='z';c++){
System.out.printf("%3c",c);
}
}
}
class ShowBoard{
void showMess(OutputAlphabet show){
show.output();
}
}
public class Example7_2 {
public static void main(String[] args) {
ShowBoard board = new ShowBoard();
board.showMess(new OutputEnglish());
board.showMess(new OutputAlphabet() { // 传递OutputAlphabet的匿名子类的对象。
@Override
public void output() {
for(char c = 'α';c<='ω';c++){
System.out.printf("%3c",c);
}
}
});
}
}
和接口有关的匿名类:
上面用的是关于类的匿名类,也有关于接口的匿名类。
java允许直接使用接口名和一个类体创建一个匿名对象,此类体被认为是实现了接口的去掉类声明后的类体,称作匿名类。
-
假设Computable是一个接口:
new Computable(){ // 实现接口的匿名类的类体 }
-
如果一个方法的参数是接口类型,就可以使用接口名和类体组合创建一个匿名对象作为参数传递给这个方法,然后方法中可以使用接口回调调用重写的接口方法。
案例:
interface SpeakHello{
void speak();
}
class HelloMachine{
public void f(SpeakHello sh){
sh.speak();
}
}
public class Example7_2 {
public static void main(String[] args) {
HelloMachine hm = new HelloMachine();
hm.f(new SpeakHello() { //和接口SpeakHello有关的匿名类。
@Override
public void speak() {
System.out.println("hello!");
}
});
}
6.3 异常类
c++有异常处理,python由异常处理,java也有自己的异常处理机制
何为异常?
异常就是程序运行过程中可能出现的错误,比如打开一个不存在的文件,异常处理会改变程序的控制流程,让程序有机会对错误做出处理。
java使用throw关键字抛出一个Exception子类的实例表示异常发生。
- 异常类
案例:使用Integer类的parseInt方法转化字符串为数字。字符串必须是纯数字字符串,如果是包含字母,就会报错
int number = Integer.parseInt("aa123");
解析:这时候方法parseInt()执行过程中就会使用throw关键字抛出NumberFormatException对象,表明程序出现NumberFormatException异常。
java允许定义方法时声明该方法调用过程中可能出现的异常。如,流对象调用read()读取一个不存在的文件时,就会抛出IOException异常对象
异常对象可以调用以下方法得到或者输出有关异常信息:
public String getMessage(); // 得到异常信息
public void printStackTrace(); //打印异常信息在程序中出错的位置及原因
public String toString(); //异常信息转化为字符串
- try-catch语句
java使用try-catch语句处理异常。
try部分:可能出现异常的操作
当try部分发生异常,立刻停止执行转向执行相应的catch部分。
catch部分:发生异常后的处理操作。
try-catch语句可以由多个catch组成,分别处理发生相应的异常。
try-cath语句格式:
try{
// 可能出现异常的操作
}
catch(Exception子类1 e){
// 发生异常后的处理操作1
}
catch(Exception子类2 e){
// 发生异常后的处理操作2
}
//各cathc中的异常类都是Exception的某个子类,表明可能发生的异常,这些子类之间不能存在父子关系,否则保留一个含有父类参数的catch即可。
案例:
import java.io.IOException;
public class Example7_4 {
public static void main(String[] args) {
int n = 0,m = 0, t = 100;
try{
m = Integer.parseInt("8888");
n = Integer.parseInt("ab88"); // 发生异常,转向catch
t = 7777;
}
catch (NumberFormatException e){
System.out.println("发生异常:"+e.getMessage());
}
System.out.println("m="+m+"\tn="+n+"\tt="+t);
try{
System.out.println("故意抛出I/O异常!");
throw new IOException("我是故意的"); // 用throw关键字主动抛出异常。
}
catch (IOException e){
System.out.println("故意异常:"+e.getMessage());
}
}
}
- 自定义异常类
编写程序时可以扩展Exception类定义自己的异常类,在编写某方法时,可以使用throws关键字声明该方法要产生若干个异常,然后在方法体中给出产生异常的操作并使用throw关键字抛出异常。
案例:当计算账单时,收入为正,支出为负。所以这时候就得判断收入和支出类型是否符合。
class BankException extends Exception{
String message;
public BankException(int m,int n){
message = "收入:"+m+"是负数或者支出:"+n+"是整数,不符合系统要求";
}
// 重写getMessage()方法
public String getMessage(){
return message;
}
}
class Bank{
private int money;
public void income(int in,int out) throws BankException{ // throws声明可能出现的异常
// 判断异常
if(in<=0||out>=0||in+out<=0){
throw new BankException(in,out); // 抛出异常
}
int netIncome = in + out;
System.out.println("本次计算纯收入:"+netIncome);
money = money+netIncome;
}
public int getMoney(){
return money;
}
}
public class Example7_5 {
public static void main(String[] args) {
Bank bank = new Bank();
try{
bank.income(200,-100);
bank.income(300,-100);
System.out.println("银行目前有:"+bank.getMoney()+"元");
bank.income(200,100);
}
catch (BankException e){
System.out.println("计算收入有以下问题:");
System.out.println(e.getMessage());
}
}
}
输出:
补充:try-catch语句还可以带finally子语句,finally子语句不管是否发生异常都会执行。try-cath中执行了return语句,finally子语句仍然会执行,如果执行了System.exit(0)程序退出代码,finally语句不会执行。
try{
...
}
catch (ExceptionSubClass e){
...
}
finally{
...
}
6.4 断言
断言语句在调试代码阶段非常有用。
断言语句一般用于程序不准备通过捕获异常来处理的错误。发生某错误时,就要立即停止。
使用断言语句可以发现一些致命的错误,当程序运行时可以关闭断言语句,但断言语句仍然保留在源代码中,以后程序又需要调试时,就可以重新启动断言语句。
-
断言语句语法格式:
-
使用关键字assert声明一条断言语句:
assert 布尔表达式; // 格式1 assert 布尔表达式 : 错误信息; // 格式2 //例如: assert number>=0; //格式1 assert number>=0:"number不能是负数"; //格式2 /*布尔表达式:number>=0 为true,程序从断言语句处继续执行,否则,程序在断言语句处停止,并输出错误信息:"number不能是负数"*/
-
-
启动与关闭断言语句
-
java解释器直接运行应用程序时,默认地关闭断言语句,调试时可以用-ea启用断言语句
java -ea mainClass
-