面向对象(四)
一、内部类
1.内部类的定义
将一个类定义在另一个类的内部,这个内部的类就被成为内部类(内置类,嵌套类)。
2.内部类的特点
1)内部类可以直接访问外部类的成员,包括私有成员(成员前省略了外部类名.this.
);
2)外部类要访问内部类的成员,必须建立内部类的对象,通过内部类对象访问
示例代码:
package com.heisejiuhuche;
public class TestInnerClass {
public static void main(String[] args) {
Outer out = new Outer();
out.methodOut();
}
}
class Outer {
//声明私有成员变量x
private int x = 3;
//声明内部类
class Inner {
void methodIn() {
//内部类直接调用外部类成员x
System.out.println("Inner: " + x);
}
}
void methodOut() {
//外部类要访问内部类的成员,必须先创建内部类对象
Inner in = new Inner();
//由内部类对象调用内部类成员
in.methodIn();
}
}
程序输出结果为:
Inner: 3
3)当内部来在外部类的成员位置上时,可以被private
关键字修饰,进行封装
示例代码:
package com.heisejiuhuche;
public class TestInnerClass {
public static void main(String[] args) {
}
}
class Outer {
//声明私有内部类,内部类在成员位置上时,可以被private关键字修饰
private class Inner {
void methodIn() {
}
}
}
4)外部其他类直接访问内部类成员(内部类声明在外部来成员位置上,且非私有)
示例代码:
package com.heisejiuhuche;
public class TestInnerClass {
public static void main(String[] args) {
//直接访问内部类成员格式
Outer.Inner oi = new Outer().new Inner();
oi.methodIn();
}
}
class Outer {
//声明成员变量x
int x = 3;
//声明内部类
class Inner {
void methodIn() {
//内部类直接调用外部类成员x
System.out.println("Inner: " + x);
}
}
}
5)不涉及特有数据的时候,内部类可以被static
关键字修饰,具备静态特性
内部类被静态修饰后,只能访问外部类的静态成员,有访问局限性。外部其他类直接访问静态内部类的非静态成员的方式为:
new 外部类名.内部类名().静态内部类的非静态成员;
示例代码:
package com.heisejiuhuche;
public class TestInnerClass {
public static void main(String[] args) {
//访问静态内部类的非静态成员的格式
new Outer.Inner().methodIn();
}
}
class Outer {
//声明成员变量x
static int x = 3;
//声明静态内部类
static class Inner {
void methodIn() {
System.out.println("Inner: " + x);
}
}
}
外部其他类直接访问静态内部类的静态成员的方式为:
package com.heisejiuhuche;
public class TestInnerClass {
public static void main(String[] args) {
//访问静态内部类的静态成员的格式
Outer.Inner.methodIn();
}
}
class Outer {
//声明成员变量x
static int x = 3;
//声明静态内部类
static class Inner {
static void methodIn() {
System.out.println("Inner: " + x);
}
}
}
6)内部类中若声明了静态成员,该内部类必须是静态的
7)外部类的静态方法访问内部类时,内部类必须是静态的
8)重名变量注意事项
示例代码:
package com.heisejiuhuche;
public class TestInnerClass {
public static void main(String[] args) {
Outer out = new Outer();
out.methodOut();
}
}
class Outer {
//声明成员变量x
int x = 3;
//声明内部类
class Inner {
int x = 4;
void methodIn() {
int x = 5;
//内部类直接调用外部类成员x
System.out.println("Inner: " + x);
}
}
void methodOut() {
//外部类要访问内部类的成员,必须先创建内部类对象
Inner in = new Inner();
//由内部类对象调用内部类成员
in.methodIn();
}
}
程序输出结果为:
Inner: 5
如果要输出4,输出语句应为System.out.println("Inner: " + this.x);
如果要输出3,输出语句应为System.out.println("Inner: " + Outer.this.x);
9)内部类可以被定义在局部位置上
内部类定义在局部位置上的示例及特点
示例代码:
package com.heisejiuhuche;
public class TestInnerClass2 {
public static void main(String[] args) {
new Outer().methodOut();
}
}
class Outer {
int x = 3;
void methodOut() {
//声明内部类于外部类的方法中
class Inner {
void methodIn() {
//同样可以直接访问外部类成员
System.out.println("Inner: " + x);
}
}
//创建Inner()匿名对象调用methodIn()方法
new Inner().methodIn();
}
}
特点:
1> 不可以被成员修饰符修饰,如private
2> 仍持有外部类的引用,仍可以直接访问外部类的成员
3> 如果要访问其所在局部的局部变量,要用final
修饰该变量
3.内部类的应用
描述事物的时候,如果事物内部还包含有事物。由于该内部事物在使用外部事物的内容,所以该内部的事物用内部类来描述。如描述人体的时候,人体由器官组成,包括心脏;心脏有属性(成员变量),行为(成员方法),比较复杂(应声明成一个类),心脏又和人体其他器官有直接的联系(需要直接访问外部类的成员),那么应将心脏作为对象封装,形成人体的内部类,并私有,对外提供方法访问。
二、匿名内部类
1.定义
没有名字的内部类叫做匿名内部类,即内部类的简写格式
2.匿名内部类使用的前提
内部类必须继承一个类或者实现接口
示例代码:
package com.heisejiuhuche;
public class TestAnonymousInnerClass {
public static void main(String[] args) {
new Outer2().methodOut();
}
}
abstract class Abs {
abstract void show();
}
class Outer2 {
int x = 3;
public void methodOut() {
//匿名内部类继承Abs类
new Abs() {
void show() {
System.out.println("Anonymous Inner: " + x);
}
}.show();
}
}
3.匿名内部类是一个匿名子类对象,是一个带内容的对象,是将定义类和创建对象封装为一体的表现方式,为了简化书写,方便方法调用而存在
匿名内部类就是一个子类继承父类的类体,除了复写父类的方法,还能在匿名内部类中定义子类的特有方法(面试用)
示例代码:
package com.heisejiuhuche;
public class TestAnonymousInnerClass {
public static void main(String[] args) {
new Outer2().methodOut();
}
}
abstract class Abs {
abstract void show();
}
class Outer2 {
int x = 3;
public void methodOut() {
//匿名内部类继承Abs类
new Abs() {
void show() {
System.out.println("Anonymous Inner Class: " + x);
}
void run() {
System.out.println("Anonymous Inner Class run...")
}
}.run();
}
}
匿名内部类中的方法不要超过3个
4.匿名内部类的应用
示例代码:
package com.heisejiuhuche;
public class TestAnonymInnerClassUse {
public static void main(String[] args) {
/*
* 使用匿名内部类作为参数
*/
show(new Inter() {
public void method() {
System.out.println("Run...");
}
});
}
/* 调用一个方法,这个方法需要接收一个接口对象作为参数
* 并且这个接口里面的方法不超过3个
* 那么就可以用匿名内部类
*/
static void show(Inter in) {
in.method();
}
}
interface Inter {
public abstract void method();
}
5.匿名内部类练习
根据main
方法中的代码,补全其他代码
示例代码:
package com.heisejiuhuche;
/**
* 根据main方法中的代码,补全Outer类
* @author jeremy
*
*/
public class TestAnonymInner {
public static void main(String[] args) {
/*
* 从这行代码分析:
* 1.由于由类名直接调用,所以function()方法是一个静态方法
* 2.由于method()一定是被一个对象所调用,所以Outer类在调用function()方法之后,返回值一定是一个对象
* 3.由于method()方法在Inter接口中,那么一定是一个类实现了Inter接口,然后复写了method()方法,并调用
* 该类的对象一定是Inter类型的对象,所以function()方法的返回值为Inter类型
*/
Outer.function().method();
}
}
interface Inter {
public abstract void method();
}
class Outer {
static Inter function() {
return new Inter() {
public void method() {
System.out.println("Anonymous Inner Class run...");
}
};
}
}
小扩展(面试用):
没有实现接口,也没有继承任何父类,仍然可以使用匿名内部类调用方法;只需使用所有类的父类Object
类即可
示例代码:
class InnerTest {
public static void main(String[] args) {
new Object() {
public void method() {
System.out.println("Run...");
}
}.method();
}
}
三、异常机制
1.定义
异常是程序在运行(非编译)过程中出现的不正常情况。异常用来描述生活中出现的问题,也是一个对象。
2.问题的分类
Java对于问题的描述分为两大类,有严重与非严重之分。对于严重的问题,Java用Error
类来描述,称为错误;对于非严重的问题,Java用Exception
类进行描述,称为异常。
1)对于Error
,一般不编写针对性的代码进行处理;
2)对于Exception
,可以针对性的编写代码进行处理
Error
和Exception
都是Throwable
的子类
3.异常的处理
Java提供了特定的语句对异常进行处理:
示例代码:
try {
运行时可能会出现异常的代码;
} catch() {
处理异常的代码;
} finally {
一定会执行的语句;
}
示例代码:
package com.heisejiuhuche;
public class TestException {
public static void main(String[] args) {
/* try catch语句,用于检测异常,并做出相应处理 */
try {
int x = Divide.div(7, 1);
System.out.println(x);
} catch(Exception e) {
System.out.println("Divide by zero...");
}
System.out.println("End...");
}
}
/* 除法类,有一个方法,返回两个int类型参数相除的结果 */
class Divide {
public static int div(int a, int b) {
return a / b;
}
}
4.异常对象常见方法
1)getMessage()
示例代码:
//显示异常信息
System.out.println(e.getMessage());
/ by zero
2)toString()
示例代码:
//显示异常名称+异常信息
System.out.println(e.toString());
java.lang.ArithmeticException: / by zero
3)printStackTrace()(JVM默认异常处理机制调用该方法)
示例代码:
//显示异常名称+异常信息+异常出现的位置
e.printStackTrace();
java.lang.ArithmeticException: / by zero
at com.heisejiuhuche.Divide.div(TestException.java:20)
at com.heisejiuhuche.TestException.main(TestException.java:7)
5.throws关键字
由于编写功能代码的时候,不知道调用者会不会传入非法参数,导致异常。那么,开发者应该在功能上,通过throws
关键字先声明,这个功能可能会抛出异常。
示例代码:
package com.heisejiuhuche;
public class TestException {
public static void main(String[] args) {
int x = Divide.div(7, 0);
System.out.println(x);
System.out.println("End...");
}
}
class Divide {
public static int div(int a, int b) throws Exception {
return a / b;
}
}
throws
关键字表示该方法的调用者一定要处理这个异常,否则编译失败。上述代码,在主函数中没有对可能出现的异常做任何处理,程序编译报错:
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
Unhandled exception type Exception
at com.heisejiuhuche.TestException.main(TestException.java:6)
处理throws
关键字有两种方式:
1)抛出异常
示例代码:
package com.heisejiuhuche;
public class TestException {
//在main方法上也加上throws声明
public static void main(String[] args) throws Exception {
int x = Divide.div(7, 1);
System.out.println(x);
System.out.println("End...");
}
}
class Divide {
public static int div(int a, int b) throws Exception {
return a / b;
}
}
在调用div()
方法的主函数上,也加上throws
关键字,将可能出现的异常抛出给虚拟机。抛给虚拟机之后,虚拟机启用默认异常处理机制,如果有异常,直接终止程序,打印异常信息。
2)捕捉异常
示例代码:
package com.heisejiuhuche;
public class TestException {
public static void main(String[] args) {
//用try catch捕捉异常并处理
try {
int x = Divide.div(7, 1);
System.out.println(x);
} catch(Exception e) {
System.out.println(e.printStackTrace());
}
System.out.println("End...");
}
}
class Divide {
public static int div(int a, int b) throws Exception {
return a / b;
}
}
预先编写处理代码try catch
,对可能出现的异常进行处理。
6.多异常处理
1)一个功能可能出现多个异常,那么在用throws
关键字声明异常的时候,应该声明多个;并且声明更为具体的异常,便于处理
声明具体异常示例代码:
package com.heisejiuhuche;
public class TestException {
public static void main(String[] args) {
try {
int x = Divide.div(7, 1);
System.out.println(x);
} catch(ArithmeticException e) {
System.out.println(e.printStackTrace());
}
System.out.println("End...");
}
}
class Divide {
//声明具体的ArithmeticException
public static int div(int a, int b) throws ArithmeticException {
return a / b;
}
}
将异常声明为更加具体的ArithmeticException
,那么在处理的时候直接处理这个具体异常即可。
声明多个异常示例代码:
package com.heisejiuhuche;
import javax.management.openmbean.ArrayType;
public class TestException {
public static void main(String[] args) {
try {
int x = Divide.div(3, 1);
System.out.println(x);
} catch(ArithmeticException e) { //处理ArithmeticException
System.out.println(e.toString());
System.out.println("Divide by zero...");
} catch(ArrayIndexOutOfBoundsException e) { //处理ArrayIndexOutOfBoundsException
System.out.println(e.toString());
System.out.println("Index out of bounds...");
}
System.out.println("End...");
}
}
class Divide {
//功能可能抛出ArithmeticException和ArrayIndexOutOfBoundsException
public static int div(int a, int b) throws ArithmeticException, ArrayIndexOutOfBoundsException {
int[] arr = new int[a];
System.out.println(arr[7]);
return a / b;
}
}
功能代码中抛出的所有异常,调用者要逐一进行具体捕捉,并处理。
2)注意,如果catch
的异常出现继承关系,父类异常放在最下面(不建议这么做,具体处理最好,不要直接处理Exception
)
示例代码:
catch(ArithmeticException e) {
System.out.println(e.toString());
System.out.println("Divide by zero...");
} catch(ArrayIndexOutOfBoundsException e) {
System.out.println(e.toString());
System.out.println("Index out of bounds...");
} catch(Exception e) {
System.out.println(e.toString());
System.out.println("Over...");
}
3)现实开发异常处理
现实开发中,开发者不会将错误信息打印在屏幕上,这没有意义。开发中应该将错误信息,保存在硬盘某个文件里,由开发者及时查阅,并修正程序。
7.自定义异常
实际开发中,程序会出现特有的问题,而这些问题没有被Java描述到并封装成对象。那么对于这些问题,可以按照Java对问题的封装思路,对特有问题进行自定义封装。例如,如果在一个程序(功能)中,除数不能为0,同时除数不能是负数,如果是负数,也视为异常。这个异常,就需要开发者自定义。
Java已经描述并封装成对象的异常,开发者可以选择由程序自动抛出,也可以选择手动抛出;而自定义异常,开发者只能选择手动抛出。手动抛出异常,用throw
关键字。
1)定义异常类
示例代码:
package com.heisejiuhuche;
class NegativeException extends Exception {
}
class Divide {
public static int div(int a, int b) throws NegativeException {
if(b < 0) {
//通过throw关键字,手动抛出自定义异常对对象
throw new NegativeException();
}
return a / b;
}
}
当方法内出现throw
抛出的异常对象,就必须要给出相应的处理。要么在方法内部try catch
;要么在方法上声明异常,让调用者处理(运行时异常除外)。一般情况下,方法内部出现异常,方法上需要声明异常。那么主函数(方法调用者)必须对异常进行处理(运行时异常除外)。
示例代码:
public class TestException {
public static void main(String[] args) throws Exception {
try {
int x = Divide.div(7, 1);
System.out.println(x);
} catch(NegativeException e) {
System.out.println("Negative divisor...")
}
System.out.println("End...");
}
}
程序输出结果为:
com.heisejiuhuche.NegativeException
Negative divisor...
End...
2)定义异常信息
由于Throwable
类中已经对异常信息进行了操作(初始化,获取等),所以子类只需要在构造函数中调用父类的构造方法,将异常信息传递给父类,即可使用。
Throwable
类对异常信息的基本处理代码示例:
class Throwable {
pricate String message;
Throwable(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
因此,子类自定义异常信息代码示例:
package com.heisejiuhuche;
public class CutomizeException {
public static void main(String[] args) {
try {
int x = Divide1.div(3, -1);
System.out.println(x);
} catch(NegativeException e) { //捕捉自定义异常
System.out.println(e.toString());
}
System.out.println("End...");
}
}
class Divide1 {
public static int div(int a, int b) throws NegativeException {
if(b < 0) {
//抛出自定义异常
throw new NegativeException("Negative divisor...");
}
return a / b;
}
}
class NegativeException extends Exception {
//定义异常信息
NegativeException(String msg) {
//调用父类的构造方法,获取自定义异常信息
super(msg);
}
}
程序输出结果为:
com.heisejiuhuche.NegativeException: Negative divisor...
End...
3)自定义异常方法
上述代码中,定义一个异常方法,获取出错的负数值。
示例代码:
package com.heisejiuhuche;
public class CutomizeException {
public static void main(String[] args) {
try {
int x = Divide1.div(3, -10);
System.out.println(x);
} catch(NegativeException e) {
System.out.println(e.toString());
System.out.println("Wrong divisor is: " + e.getNum());//打印错误值
}
System.out.println("End...");
}
}
class Divide1 {
public static int div(int a, int b) throws NegativeException {
if(b < 0) {
throw new NegativeException("Negative divisor...", b);
}
return a / b;
}
}
class NegativeException extends Exception {
private int num; //声明私有变量,用于存储错误数据
NegativeException(String msg, int num) {
super(msg);
this.num = num; //初始化错误数据
}
public int getNum() {
return num; //获取错误值
}
}
程序输出结果为:
com.heisejiuhuche.NegativeException: Negative divisor...
Wrong divisor is: -10
End...
8.throws和throw关键字的区别
1)throws
使用在方法上;throw
使用在方法内;
2)throws
后面跟异常类,可以跟多个;throw
后面跟异常对象
9.运行时异常(RuntimeException)
1)异常的分类
1> 编译时被检测的异常(非运行时异常及其子类)
方法中抛出了非运行时异常及其子类,方法上必须声明;方法上声明了非运行时异常及其子类,调用者必须进行捕捉处理
2> 编译时不被检测的异常(运行时异常及其子类)
2)运行时异常定义RuntimeException
(运行时异常)是Exception
里一个很特殊的异常。其特点有:
1> 运行时异常在方法内抛出,方法上不需要声明;
2> 运行时异常在方法上声明,调用者可以不用进行处理
示例代码:
package com.heisejiuhuche;
public class CutomizeException {
public static void main(String[] args) {
//运行时异常无须调用者进行处理,可以没有try catch,编译通过
int x = Divide1.div(3, 0);
System.out.println(x);
System.out.println(e.toString());
}
System.out.println("End...");
}
}
class Divide1 {
public static int div(int a, int b) {
if(b < 0) {
//运行时异常无须在方法上声明,直接抛出即可,编译通过
throw new ArithmeticException("Negative divisor...");
}
return a / b;
}
}
因为运行时异常,不需要让调用者处理。当运行时异常发生时,出现了无法继续运行的情况,虚拟机希望程序停止,由调用者对程序进行修正,排除异常。
如果自定义异常发生时,程序无法再进行运算,那么就让该自定义异常继承RuntimeException
示例代码:
class NegativeException extends RuntimeException {
NegativeException(String msg) {
super(msg);
}
}
10.异常练习
老师需要用电脑上课,电脑会出现蓝屏和温度过高烧毁这两个异常。利用异常机制,对每个异常进行处理。
示例代码:
package com.heisejiuhuche;
public class ExceptionTest {
/* 将main方法想像成教育机构的老板,老板命令老师教课 */
public static void main(String[] args) {
Teacher teacher = new Teacher("Glen");
try {
teacher.teach();
} catch(TeachingPlanFailException e) { /* 如果出现电脑烧毁,教学计划失败,老板就炒了老师鱿鱼! */
System.out.println(e.toString());
System.out.println("Glen is fired... 'Cause he broke a computer...");
}
}
}
class Teacher {
private String name;
private Computer computer;
/* 老师初始化时有名字和一台电脑 */
Teacher(String name) {
this.name = name;
computer = new Computer();
}
public void teach() throws TeachingPlanFailException {
try {
computer.run();
} catch(BlueScreenException e) { /* 蓝屏的话,打印蓝屏信息,重启电脑 */
System.out.println(e.toString());
computer.restart();
} catch(BurntException e) { /* 烧毁的话,打印烧毁信息,抛出教学计划失败异常 */
System.out.println(e.toString());
throw new TeachingPlanFailException("Teaching plan fials...");
}
System.out.println("Teacher teaching..."); /* 正常情况,老师上课 */
}
}
class Computer {
/* 电脑状态,1为正常,0为蓝屏,-1为烧毁 */
private int state = -1;
/* 电脑启动方法 */
public void run() throws BlueScreenException, BurntException {
/* 判断状态值,决定正常运行还是抛出相应异常对象 */
if(state == 1) {
System.out.println("Computer run...");
} else if(state == 0) {
throw new BlueScreenException("Blue screen occured...");
} else if(state == -1) {
throw new BurntException("Computer is down...");
}
}
/* 电脑重启方法 */
public void restart() {
state = 1;
System.out.println("Computer restarting...");
}
}
/* 电脑蓝屏异常 */
class BlueScreenException extends Exception {
BlueScreenException(String message) {
super(message);
}
}
/* 电脑烧毁异常 */
class BurntException extends Exception {
BurntException(String message) {
super(message);
}
}
/* 不能直接将烧毁异常抛给老板,老板处理不了,电脑烧毁了,
* 老师会出现教学计划失败异常,应该将这个异常封装成异常类
* 再抛给老板,让老板处理
*/
class TeachingPlanFailException extends Exception {
TeachingPlanFailException(String message) {
super(message);
}
}