内部类
我们已经可以知道,类可以有两种重要的成员:成员变量和方法,实际上java还允许类可以有一种成员:内部类。
java支持在一个类中定义另一个类,这样的类称为内部类,而包含内部类的类称为内部类的外嵌类。
内部类和外嵌类之间的重要关系如下:
内部类的外嵌类的成员变量在内部类中仍然有效,内部类中的方法也可以调用外嵌类中的方法。
内部类的类体中不可以声明类变量和类方法。外嵌类的类体中可以用内部类声明对象,作为外嵌类的成员。
内部类仅供它的外嵌类使用,其他类不可以用某个类的内部类声明对象。
例如:
public class RedCowForm {
static String formName;
RedCow cow;
public RedCowForm() {
}
RedCowForm(String s){
cow = new RedCow(150,112,5000);
formName = s;
}
public void showCowMess() {
cow.speak();
}
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+"cm 体重"+weight+"kg,生活在"+formName);
}
}
}
public class Example7_1{
public static void main(String[] args) {
RedCowForm form = new RedCowForm("红牛农场");
form.showCowMess();
form.cow.speak();
}
}
运行结果如图所示:
需要特别注意的是,java编译器生成的内部类的字节码文件的名字和通常的类不同,内部类对应的字节码文件的名字格式是“外嵌类名$内部类名”,因此,当需要把字节码文件复制给其他开发人员时,不要忘记了内部类的字节码文件。
内部类可以被修饰为static内部类,类是一种数据类型,那么static内部类就是外嵌类中的一种静态数据类型,这样一来,程序就可以在其他类中使用static内部类来创建对象了。但是需要注意的是,static内部类不能操作外嵌类中的实例成员变量。
注:非内部类不可以是static类
匿名类
假如没有显示的声明一个类的子类,而又想用子类创建一个对象,那么该如何实现这个目的呢?java允许我们直接使用一个类的子类的类体创建一个子类对象,也就是说,创建子类对象时,除了使用父类的构造方法外还有类体,此类体被认为是一个子类去掉类声明后的类体,称作匿名类。匿名类就是一个子类,由于无名可用,所以不可能用匿名类声明对象,但却直接可以用匿名类创建一个对象。
假设Bank是类,那么下列代码就是用Bank的一个子类(匿名类)创建对象。
new Bank(){
匿名类的类体
};
匿名类有如下特点:
匿名类可以继承父类的方法也可以重写父类的方法。
使用匿名类时,必然是在某个类中直接用匿名类创建对象,因此匿名类一定是内部类。
匿名类可以访问外嵌类中的成员变量和方法,匿名类中的类体中不能声明static类型的成员变量和方法。
由于匿名类是一个子类,但没有类名,所以在用匿名类创建对象时,要直接使用父类的构造方法。
尽管匿名类创建的对象没有经过类声明步骤,但匿名对象的引用可以传递给一个匹配的参数。
abstract class OutputAlphabet {
public abstract void output();
}
public class OutputEnglish extends OutputAlphabet{
public void output() {
for (char c='a';c<='z';c++) {
System.out.printf("%3c",c);
}
System.out.println();
}
}
public 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() {
public void output() {
for(char c = 'α';c<='ω';c++)
System.out.printf("%3c", c);
}
}
);
}
}
运行结果如图所示
和接口有关的匿名类
假设computable是一个接口,那么,java允许直接用接口名和一个类体创建一个匿名对象,此类体被认为是实现了computable接口的类去掉类声明后的类体,称作匿名类。下列代码就是实现了computable接口的类(匿名类)创建对象。
new Computable(){
实现接口的匿名类的类体
};
如果某个方法的参数是接口类型,那么可以使用接口名和类体组合创建一个匿名对象传递给方法的参数,类体必须要重写接口中的所有方法。
interface SpeakHellol{
void speak();
}
class HelloMachine{
public void turnOn(SpeakHellol hello) {
hello.speak();
}
}
public class Example7_3 {
public static void main(String[] args) {
HelloMachine machine = new HelloMachine();
machine.turnOn(new SpeakHellol() {
public void speak() {
System.out.println("hello,you are welcome");
}
});
machine.turnOn(new SpeakHellol() {
public void speak() {
System.out.println("你好,欢迎光临!");
}
});
}
}
异常类
所谓异常就是程序运行时可能会出现一些错误,比如试图打开一个根本不存在的文件等,异常处理将会改变程序的控制流程,让程序有机会对错误进行处理。这一节将对异常给出初步的介绍,而java程序中出现的具体异常问题在相应的章节中还将讲述。
java使用throw关键字抛出一个Exception子类的实例表示异常发生。例如,java.lang包中的Integer类调用其类方法public static int parseInt(Strings s)可以将“数字”格式的字符串,如“6789”,转化为int型数据,但是当试图将字符串“ab89”转化成数字时,例如:
int number = Integer.parseInt("ab89");
方法parseInt()在执行过程中就会抛出NumberFormatException对象(使用throw抛出一个NumberFormatException对象),即程序运行出现NumberFormatException异常。
java允许定义方法时声明该方法调用过程中可能出现的异常,即允许方法调用过程中抛出异常对象,终止当前方法的继续执行。
try-catch语句
java使用try-catch语句来处理异常,将可能出现的异常操作放在try-catch语句的try部分,一旦try部分抛出异常对象,或调用某个可能抛出异常对象的方法,并且该方法抛出了异常对象,那么try部分将会立刻结束执行,转向执行相应的catch部分。所以程序可以将发生异常后的处理放在catch部分。try-catch语句可以由几个catch组成,分别处理发生的相应异常。
try-catch语句的格式如下:
try{
包含可能发生异常的语句
}
catch(ExceptionSubClass1 e){
...
}
catch(ExceptionSubClass2 e){
...
}
各个catch参数中的异常类都是Exception的某个子类,表明try部分可能发生的异常,这些子类之间不能有父子关系,否则保留一个含有父类参数的catch即可。
import java.io.IOException;
public class Example7_4 {
public static void main(String[] args) {
int n = 0,m = 0,t = 1000;
try {
m = Integer.parseInt("8888");
n = Integer.parseInt("ab89");//发生异常,转向catch
t = 7777;//t没有机会被赋值
}
catch(NumberFormatException e) {
System.out.println("发生异常:"+e.getMessage());
}
System.out.println("n="+n+",m="+m+",t="+t);
try {
System.out.println("故意抛出I/O异常!");//故意抛出异常
throw new java.io.IOException("我是故意的");
//System.out.println("这个输出语句肯定没有机会执行,必须注释,否则编译出错");
}
catch(java.io.IOException e) {
System.out.println("发生异常:"+e.getMessage());
}
}
}
运行结果如图
自定义异常类
在编写程序时可以拓展Exception类定义自己的异常类,然后根据程序的需要来规定哪些方法产生这样的异常,一个方法在声明时可以使用throws关键字声明要产生的若干个异常,并在该方法的方法体中具体给出产生异常的操作,即用相应的异常类创建对象,并使用throw关键字抛出该异常对象,导致该方法结束执行。程序必须在try-catch块语句中调用可能发生异常的方法,其中catch的作用就是捕获throw关键字抛出的异常对象。
注:throw是java的关键字,该关键字的作用就是抛出异常。throw和throws是两个不同的关键字。
通常情况下,计算两个整数之和的方法不应当有任何异常发生,但是,对某些特殊应用程序,可能不允许同号的整数做求和运算,比如当有一个整数代表收入,一个整数代表支出时,这两个整数就不能是同号。
public class BankException extends Exception {
String message;
public BankException(int m,int n) {
message = "入账资金"+m+"是负数或支出"+n+"是正数,不符合系统要求。";
}
public String warnMess() {
return message;
}
}
public class Bank {
private int money;
public void income(int in,int out) throws BankException{
if(in<=0||out>=0||in+out<=0) {
throw new BankException(in,out);
}
int netIncome = in+out;
System.out.printf("本次计算出的纯收入是:%d元\n",netIncome);
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);
bank.income(400, -100);
System.out.printf("银行目前有%d元\n",bank.getMoney());
bank.income(200, 100);
bank.income(99999, -100);
}
catch(BankException e) {
System.out.println("计算受益的过程出现如下问题:");
System.out.println(e.warnMess());
}
System.out.printf("银行目前有%d元\n",bank.getMoney());
}
}
运行结果如图所示
断言
断言语句在调试代码阶段非常有用,断言语句一般用于程序不准备通过捕获异常来处理的错误,例如,当发生某个错误时,要求程序必须立即停止执行。在调试代码阶段让断言语句发挥作用,这样就可以发现一些致命的错误,当程序正式运行时就可以关闭断言语句,但扔把断言语句保留在源代码中,如果以后程序应用又需要调试,可以重新启用断言语句。
断言语句的语法格式
使用关键字assert声明一条断言语句,断言语句有以下两种格式:
assert booleanExpression;
assert booleanExpression:messageException;
例如,对于断言语句:
assert number >= 0;
如果表达式number>=0的值为true,程序继续执行,否则程序立刻结束执行。在上述断言语句的语法格式中,booleanExpression必须是求值为boolean型的表达式。messageException可以是求值为字符串的表达式。
如果使用
assert booleanExpression;
形式的断言语句,当booleanExpression的值是true时,程序从断言语句处执行;值是false时,程序从断言语句处停止执行。
如果使用
assert booleanExpression:messageException;
形式的断言语句,当booleanException的值是true时,程序从断言语句处继续执行;值是false时,程序从断言语句处停止执行,并输出messageException表达式的值,提示用户出现了怎样的问题。
启用与关闭断言语句
当使用java解释器直接运行应用程序时,默认的关闭断言语句,在调试程序时可以使用-ea启用断言语句,例如
java -ea mainClass
例子
import java.util.Scanner;
public class Example7_6 {
public static void main(String[] args) {
int [] score = {-120,98,89,120,99};
int sum = 0;
for(int number:score) {
assert number>=0:"负数不能是成绩";
sum +=number;
}
System.out.println("总成绩:"+sum);
}
}