1.内部类的特性和使用方法
概念总述:
在一个类中定义另外一个类,这个类就叫做内部类或内置类 (inner class) 。
内部类可以让我们将逻辑上相关的一组类组织起来,并由外部类(outer class)来控制内部类的可见性。
当我们建立一个inner class时,其对象就拥有了与外部类对象之间的一种关系,这是通过一个特殊的this reference形成的,使得内部类对象可以随意的访问外部类中所有的成员。
详细内容:
(1)外部类的main静态方法中无法直接访问内部类,错误信息如下:
//*********************************************************************************
Outer.java:30: 错误: 无法从静态上下文中引用非静态 变量 this
Inner inner=new Inner();
(2)引用内部类时需要一个外部类的实例来引用
否则错误信息如下:
//*********************************************************************************
Outer.Inner inner=new Outer.Inner();
inner.print();//error
Outer.java:46: 错误: 需要包含Outer.Inner的封闭实例
Outer.Inner inner=new Outer.Inner();
(3)内部类访问外部类的成员、中间层类的成员、和本地变量
测试代码及结果如下:
//*********************************************************************************
class Outer //外部类
{
private int index=100;//外部类成员
//内部类Inner类不管嵌套层次多深,都可以访问外部类成员
void fn( final int a)
{
//ok 类的作用域限定在if语句中 仍然可以访问外部类成员
final int b=0;//需要初始化
if(true)
{
class Middle //中间类
{
private int index=70;////中间类成员
class Inner //内部类
{
private int index=50;//内部类成员
void print()
{
int index=30;
System.out.println(index);//30
System.out.println(this.index);//50
System.out.println(Middle.this.index);//70
System.out.println(Outer.this.index);//100
System.out.println(a);//ok 声明为final时
System.out.println(b);//ok 声明为final时
}
}
}
}
}
(4)在方法中定义的内部类,如果要访问方法中定义的本地变量或方法的参数,则变量必须被声明final,否则出错,错误信息如下:
//*********************************************************************************
Outer.java:38: 错误: 从内部类中访问本地变量a; 需要被声明为最终类型a=5;
Outer.java:39: 错误: 从内部类中访问本地变量b; 需要被声明为最终类型b=6;
(5)private内部类
内部类声明为private时无法访问,否则错误信息如下:
//*********************************************************************************
Outer.java:133: 错误: Outer.Inner可以在Outer中访问private Outer.Inner inner=outer.new Inner();
内部类还可以声明为protected、abstract或final。
(6)static内部类
内部类可以声明为static的,但此时就不能再使用外部类的非static的成员变量和非static的成员方法,否则错误信息如下:
//*********************************************************************************
Outer.java:102: 错误: 无法从静态上下文中引用非静态 变量 this
System.out.println(Outer.this.index);//error
(7)非static的内部类中的成员不能声明为static的,只有在顶层类或static的内部类中才可声明static成员,
否则错误信息如下:
//*********************************************************************************
Outer.java:110: 错误: 内部类Outer.Inner中的静态声明非法
static void print()
修饰符 'static' 仅允许在常量变量声明中使用
(8)内部类继承
内部类无法直接继承,类定义如下:
//*********************************************************************************
class Car
{
class Wheel{}
}
class PlaneWheel extends Car.Wheel
{
public static void main(String[] args) {
PlaneWheel pw=new PlaneWheel();
System.out.println("Hello World!");
}
}
则错误信息如下:
Car.java:9: 错误: 需要包含Car.Wheel的封闭实例
class PlaneWheel extends Car.Wheel
解决方法:
class Car
{
class Wheel{}
}
class PlaneWheel extends Car.Wheel
{
//产生外部类对象
PlaneWheel(Car car)
{
car.super();
}
public static void main(String[] args)
{
Car car=new Car();
PlaneWheel pw=new PlaneWheel(car);
}
}
(9)匿名内部类
匿名内部类测试代码1如下:
//*********************************************************************************
interface Animal
{
void eat();
void sleep();
}
class Zoom
{
//隐藏实现细节 通过接口访问内部类的方法
private class Tiger implements Animal
{
public void eat()
{
System.out.println("Tiger eat");
}
public void sleep()
{
System.out.println("Tiger sleep!");
}
}
Animal getanimal()
{
//接口无法直接实例化对象 需要使用匿名内部类 实现接口中的方法
return new Animal()
{
public void eat()
{
System.out.println("Animal eat");
}
public void sleep()
{
System.out.println("Animal sleep!");
}
};
}
}
class Test
{
public static void main(String[] args)
{
Zoom zoom=new Zoom();
Animal an=zoom.getanimal();
an.eat();
an.sleep();
}
}
//*********************************************************************************
//输出结果
Animal eat
Animal sleep!
//*********************************************************************************
匿名内部类测试代码2如下:
//*********************************************************************************
class A
{
void fn1(){}
}
abstract class B
{
abstract void fn2();
}
class C extends A
{
//使用匿名内置类 返回内置类对象
B getB()
{
return new B()
{
public void fn2(){};//实现抽象类B的方法
};
}
}
class Test
{
static void method1(A a)
{
a.fn1();
}
static void method2(B b)
{
b.fn2();
}
public static void main(String[] args)
{
C c=new C();
method1(c);
method2(c.getB());
}
}
(10)利用内部类解决方法重名问题
测试代码如下:
//*********************************************************************************
interface Machine
{
void run();
}
class Person
{
public void run()
{
System.out.println("run");
}
}
//需要继承 但是继承的同时方法与接口中方法重名 利用内部类解决
class Robot extends Person
{
private class MachineHeart implements Machine
{
public void run()
{
System.out.println("heart run");
}
}
Machine getmachine()
{
return new MachineHeart();
}
}
class Test
{
public static void main(String[] args)
{
Robot robot =new Robot();
Machine m=robot.getmachine();
m.run();
robot.run();
}
}
//*********************************************************************************
//输出结果
heart run
run
2.异常处理
概念总述:
Java程序在执行过程中如出现异常,会自动生成一个异常类对象,该异常对象将被提交给Java运行时系统,这个过程称为抛出(throw)异常。
当Java运行时系统接收到异常对象时,会寻找能处理这一异常的代码并把当前异常对象交给其处理,这一过程称为捕获(catch)异常。
如果Java运行时系统找不到可以捕获异常的方法,则运行时系统将终止,相应的Java程序也将退出。
详细内容:
(1)构造一个除0异常,捕获异常信息如下:
//*********************************************************************************
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Excep.division(Excep.java:5)
at ExcepTest.main(Excep.java:14)
(2)捕获异常的位置--try catch语句块
try语句中发生异常后的语句将不予执行,但是catch语句后的语句予以执行。异常处理代码放置在catch语句中。
(3)抛出异常而没有捕获异常时,错误信息如下:
//*********************************************************************************
Excep.java:53: 错误: 未报告的异常错误Exception; 必须对其进行捕获或声明以便抛出
System.out.println( excep.division(5,0));
(4)捕获异常顺序--特殊到一般
已经捕获了Exception,再次捕获ArithmeticException时错误信息如下:
//*********************************************************************************
Excep.java:51: 错误: 已捕获到异常错误ArithmeticException
catch( ArithmeticException e)
(5)RuntimeException运行时异常并不需要显示的捕获
对于RuntimeException,通常不需要我们去捕获,这类异常由Java运行系统自动抛出并自动处理。
(6)捕获异常后可以继续抛出异常
//*********************************************************************************
public int division(int a ,int b) throws ArithmeticException
{
//在函数内部捕获异常
try
{
return a/b;
}
catch (ArithmeticException e)
{
e.printStackTrace();//输出异常信息
throw e;//再次抛出异常 程序跳转
}
}
//捕获异常信息如下:
//*********************************************************************************
java.lang.ArithmeticException: / by zero
at Excep.division(Excep.java:33)
at ExcepTest.main(Excep.java:75)
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Excep.division(Excep.java:33)
at ExcepTest.main(Excep.java:75)
(7)捕获异常后可以重新抛出新的异常
//*********************************************************************************
public int division(int a ,int b) throws Exception
{
//在函数内部捕获异常
try
{
return a/b;
}
catch (ArithmeticException e)
{
e.printStackTrace();//输出异常信息后
throw new Exception("can not div zero!");//再次抛出新异常
}
}
//*********************************************************************************
捕获异常信息如下:
F:\java\JavaLesson\Lesson4>java ExcepTest
java.lang.ArithmeticException: / by zero
at Excep.division(Excep.java:33)
at Excep.fn1(Excep.java:45)
at ExcepTest.main(Excep.java:58)
java.lang.Exception: can not div zero!
at Excep.division(Excep.java:38)
at Excep.fn1(Excep.java:45)
at ExcepTest.main(Excep.java:58)
(8)自定义异常
从Exception派生子类如下:
//*********************************************************************************
class DivisorIsMinusException extends Exception
{
DivisorIsMinusException(String str)
{
super(str);
}
}
自定义异常类的使用如下:
public int division(int a ,int b) throws ArithmeticException ,DivisorIsMinusException
{
if(b<0)
throw new DivisorIsMinusException("Divisor can't be minus!");
return a/b;
}
int fn1(int a,int b) throws ArithmeticException ,DivisorIsMinusException
{
return division(a,b);
}
//*********************************************************************************
//异常捕获信息如下:
F:\java\JavaLesson\Lesson4>java ExcepTest
DivisorIsMinusException: Divisor can't be minus!
at Excep.division(Excep.java:52)
at Excep.fn1(Excep.java:57)
at ExcepTest.main(Excep.java:77)
(9)finally执行机会
不管异常发生没有,finally均会执行,适合在此处做一些清理和资源释放工作
finally语句执行不受return语句限制,但是使用exit退出时不会执行.
在Test.java中执行如下测试代码:
//*********************************************************************************
try
{
System.out.println( excep.fn1(5,-2));
return;//finally语句仍会执行
}
catch( ArithmeticException e)
{
System.out.println( e.toString() );
}
//捕获自定义异常
catch (DivisorIsMinusException ex)
{
ex.printStackTrace();
System.exit(-1);//finally语句不会执行
}
//捕获一般异常
catch (Exception e)
{
e.printStackTrace();//打印异常信息和位置
}
finally
{
System.out.println( "finally!");
}
//*********************************************************************************
//运行结果
F:\java\JavaLesson\Lesson4>java ExcepTest
DivisorIsMinusException: Divisor can't be minus!
at Excep.division(Excep.java:52)
at Excep.fn1(Excep.java:57)
at ExcepTest.main(Excep.java:77)
finally!
(10)子类覆盖父类的方法中抛出异常时受限制
如果父类中的方法抛出多个异常,则子类中的覆盖方法要么抛出相同的异常,要么抛出异常的子类,但不能抛出新的异常(注:构造方法除外)。
以下测试代码,子类试图覆盖父类方法的同时抛出新的异常.
//*****************************************************
class Excep
{
public int division(int a ,int b) throws Exception
{
try
{
return a/b;
}
catch (ArithmeticException e)
{
e.printStackTrace();//输出异常信息后
}
}
}
class ChildExcep extends Excep
{
//error 子类覆盖父类方法时抛出异常受限制
public int division(int a ,int b) throws FileNotFoundException
{
return a/b;
}
}
//*********************************************************************************
以上代码编译会出错,错误信息如下:
Excep.java:63: 错误: ChildExcep中的division(int,int)无法覆盖Excep中的division(in
t,int) public int division(int a ,int b) throws FileNotFoundException
被覆盖的方法未抛出FileNotFoundException
(11)我们可以在方法声明时,声明一个不会抛出的异常,Java编译器就会强迫方法的使用者对异常进行处理。这种方式通常应用于abstract base class和interface中。