内部类和异常类
内部类
ava支持在一个类中声明另一个类,这样的类称作内部类,而包含内部类的类成为内部类的外嵌类。
注意
(1)内部类的类体中不可以声明类变量和类方法。
(2)外嵌类的类体中可以用内部类声明对象,作为外嵌类的成员。
内部类的使用规则:
声明内部类如同在类中声明方法或变量一样,一个类把内部类看作是自己的成员。
外嵌类的类体中可以用内部类声明的对象,作为外嵌类的成员。
外嵌类的成员变量在内部类中仍然有效,内部类中的方法也可以调用外嵌类中的方法。
内部类的类体中不可以声明类变量和方法。
外嵌类和内部类在编译时,生成两个.class文件。
在Java中,内部类可以被以下几种关键字修饰:
(1)private: 内部类可以被private修饰,这样只有外部类的成员方法可以访问内部类。
(2)protected: 内部类也可以被protected修饰,这样它可以被外部类的子类访问。
(3)public: 如果内部类被public修饰,那么它对于任何类都是可见的,可以在外部类以及外部类所在的包中使用。
(4)static: 静态内部类可以被static修饰,这样它就成为了外部类的静态成员,可以直接通过外部类名访问。
static静态内部类无法操作外嵌类的实例成员变量
(5)abstract: 内部类也可以是抽象的,这样就不能直接实例化该内部类,而是需要通过继承该内部类并实现其抽象方法来创建实例。
(6)内部类可以是final类,也可以不是。换句话而言,非匿名内部类可以存在它自己的(内部)子类
。
public class Example7_1 {
public static void main(String args[]) {
RedCowForm form = new RedCowForm("红牛农场");
form.showCowMess();
form.cow.speak();
//RedCowForm.RedCow redCow = new RedCowForm.RedCow(180,119,6000);
//redCow.speak();
}
}
class RedCowForm {
static String formName;
RedCow cow; //内部类声明对象
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);
}
} //内部类结束
}
匿名类
Java允许我们直接使用一个类的子类的类体创建一个子类对象。
1.和子类有关的匿名类
创建子类对象时,除了使用父类的构造方法外还有类体,此类体被认为是一个子类去掉类声明后的类体,称作匿名类。
假设Bank是类,那么下列代码就是用Bank的一个子类(匿名类)创建对象:
new Bank ()
{
匿名类的类体
};
2.和接口有关的匿名类
假设Computable是一个接口,那么,Java允许直接用接口名和一个类体创建一个匿名对象,此类体被认为是实现了Computable接口的类去掉类声明后的类体,称作匿名类。
下列代码就是用实现了Computable接口的类(匿名类)创建对象:
new Computable()
{
实现接口的匿名类的类体
} ;
3.匿名类的规则
(1)编译器会给每一个匿名类一个名字
在Java中,虽然我们可以创建匿名类,但实际上编译器会为每个匿名类自动分配一个名称,这个名称通常是一些形如"OuterClass$1"、"OuterClass$2"等的字符串,其中"OuterClass"是包含匿名类的外部类的名称。
编译器为匿名类分配名称的原因主要有两个:
调试和诊断: 当程序出现错误时,编译器会生成一个带有堆栈跟踪信息的异常。这个跟踪信息通常包括类名和方法名等信息,用于指示在哪里发生了异常。如果编译器不为匿名类分配名称,那么在错误跟踪信息中将无法准确显示匿名类的信息,这会给调试和错误诊断带来困难。
字节码文件结构: Java字节码文件需要包含类名等元数据信息,而编译器需要为每个类分配一个唯一的名称。即使在源代码中没有为匿名类指定名称,编译器也需要为其生成一个唯一的名称,以便生成对应的字节码文件。因此,为了保证编译后的字节码文件的正确性和完整性,编译器会为匿名类分配一个名称。
尽管编译器为匿名类分配了名称,但这些名称对于程序员来说通常是不可见的,因为它们是编译器自动生成的,并且在源代码中并不直接暴露。
(2)匿名类不能声明自己的static成员变量和static方法
静态成员变量和静态方法属于类本身,而不是类的实例。因此,为了使用静态成员变量和静态方法,需要通过类名来访问,而不是通过类的实例。然而,由于匿名类没有名称,无法通过类名来访问它们,因此在匿名类中声明静态成员变量和静态方法是没有意义的。
(3)匿名类一定是final类
匿名类只存在其实例,因此不能被继承或者重写。
异常类
所谓异常就是程序运行时可能出现一些错误,比如试图打开一个根本不存在的文件等,异常处理将会改变程序的控制流程,让程序有机会对错误作出处理。程序运行出现异常时,Java运行环境就用异常类Exception的相应子类创建一个异常对象,并等待处理。
try…catch语句
Java使用trycatch语句来处理异常,将可能出现的异常操作放在trycatch语句的try部分,将发生异常后的处理放在catch部分。
try~catch语句的格式如下:
try
{
包含可能发生异常的语句
}
catch(ExceptionSubClass1 e)
{
…
}
catch(ExceptionSubClass2 e)
{
…
}
例子:
class DangerException extends Exception {
final String message = "超重";
public String warnMess() {
return message;
}
}
class CargoBoat {
int realContent; //装载的重量
int maxContent; //最大装载量
public void setMaxContent(int c) {
maxContent = c;
}
public void loading(int m) throws DangerException {
realContent += m;
if(realContent>maxContent) {
throw new DangerException();
}
}
}
public class Example7_8 {
public static void main(String args[]) {
CargoBoat ship = new CargoBoat();
ship.setMaxContent(1000);
int m = 0;
try{
m =600;
ship.loading(m);
m = 400;
ship.loading(m);
m = 367;
ship.loading(m);
m = 555;
ship.loading(m);
}
catch(DangerException e) {
System.out.println(e.warnMess());
System.out.println("无法再装载重量是"+m+"吨的集装箱");
try {
ship.loading(-m); //卸载货物
}
catch(DangerException exp) {
System.exit(0); //程序退出,不再给机会
}
}
finally {
System.out.printf("货船将正点启航\n");
System.out.println("目前装载了"+ship.realContent+"吨货物");
}
}
}