内部类、异常处理
6.1什么是内部类?内部类有几种?每一中内部类如何表示有那些些特征?
1.内部类
- 将一个类定义在另一个类里面,形成了包含关系。
- 内部类–被包含的类
- 外部类–包含内部类的类。
public class 外部类{
public class 成员内部类{
}
}
在这里我们可以认为成员内部类就相当于外部类中的一个成员变量/实例方法。
- 成员内部类中不能定义静态变量和静态方法。
- 外部类的静态方法是不能访问成员内部类的。
- 其他的类中如果想访问成员内部类中的变量和方法,就得先 创建外部类对象。
成员内部类中的变量和方法的调用关系
- 成员内部类的构造方法中可以访问成员内部类实例变量/成员内部类的实例方法,默认this.,可以省略this.
- 成员内部类的构造方法中可以访问成员内部类中其他的构造方法,通过new访问
- 成员内部类的实例方法中中可以访问成员内部类实例变量/成员内部类的实例方法,默认this.,可以省略this.
- 成员内部类的实例方法中中可以访问成员内部类构造方法,通过new访问
1. 成员内部类中的方法访问外部类中的变量和方法
- 成员内部类的构造方法中可以访问外部类的实例变量/外部类的实例方法,默认外部类的类名.this,可以省略外部类的类名.this。
- 成员内部类的构造方法中可以访问外部类的类变量/类方法,默认类名访问,也可以外部类的类名.this,可以省略类名/外部类的类名.this。
- 成员内部类的构造方法中可以访问外部类的构造方法,通过new访问。
- 成员内部类的实例方法中可以访问外部类的实例变量/外部类的实例方法,默认外部类的类名.this,可以省略外部类的类名.this。
- 成员内部类的实例方法中可以访问外部类的类变量/类方法,默认类名访问,也可以外部类的类名.this,可以省略类名/外部类的类名.this。
- 成员内部类的实例方法中可以访问外部类的构造方法,通过new访问。
2. 外部类中的方法访问成员内部类中的变量和方法
- 外部类的构造方法/实例方法中可以访问成员内部类的构造方法【new】,实例变量/实例方法【对象调用】
- 外部类的类方法中不能访问成员内部类。
3. 其他类中访问成员内部类中的变量和方法
其他中可以访问成员内部类,先创建外部类对象,然后在创建内部类对象
package com.click369.test1;
//外部类
public class TestClass3 {
//InnerClass--成员内部类
public class InnerClass{
//成员内部类的实例变量
public int innerid=1001;
//成员内部类的构造方法
public InnerClass(){
}
//成员内部类的实例方法
public void test1(){
}
}
}
package com.click369.test1;
public class OtherClass {
public OtherClass(){
//TestClass3.InnerClass inner=new TestClass3().new InnerClass();
TestClass3 t3=new TestClass3();
TestClass3.InnerClass inner=t3.new InnerClass();
System.out.println(inner.innerid);
inner.test1();
}
public void testOther(){
TestClass3.InnerClass inner=new TestClass3().new InnerClass();
//TestClass3 t3=new TestClass3();
//TestClass3.InnerClass inner=t3.new InnerClass();
System.out.println(inner.innerid);
inner.test1();
}
public static void testStaticOther(){
TestClass3.InnerClass inner=new TestClass3().new InnerClass();
//TestClass3 t3=new TestClass3();
//TestClass3.InnerClass inner=t3.new InnerClass();
System.out.println(inner.innerid);
inner.test1();
}
}
2.2方法内部类—在这里我们可以认为方法内部类就相当于方法中的一个局部变量。
将java类定义在某一个方法中。
-
定义方法内部类的时候没有访问修饰符
-
方法内部类中不能出现类变量和类方法
-
方法内部类中的构造方法/实例方法,可以访问本方法中的局部变量,jdk8.0之前需要使用final修饰局部变量,包括参数
-
方法内部类中的构造方法/实例方法,可以访问外部类的实例变量/实例方法,默认外部类类名.this. , 可以省略外部类类名.this.
-
方法内部类中的构造方法/实例方法,可以访问外部类的类变量/类方法,默认外部类类名,也可以外部类类名.this. ,可以省略外部类类名/外部类类名.this.
-
定义该方法内部类的方法中可以访问这个方法内部类中的变量和方法,对象访问
-
外部类中的方法中是不能访问方法内部类的。【方法内部类当于方法中的一个局部变量】
package com.click369.test2;
import com.click369.test1.TestClass2.InnerClass;
public class TestClass {
//外部类的实例变量
public int testid=1000;
//外部类的类变量
public static String testname="lisi";
//外部类的构造方法
public TestClass(){
}
//外部类的实例方法
public void dox(){
//外部类中的方法中是不能访问方法内部类的。
//InnerClass inner=new InnerClass();
}
//外部类的类方法
public static void doxStatic(){
}
public void test1(double num2){
//test1方法中的局部变量
int num1=100;
//方法内部类---就相当于本方法中的局部变量
//A 定义方法内部的时候没有访问修饰符
//B 方法内部类中不能出现类变量和类方法
//C 方法内部类可以访问本方法中的局部变量,jdk8.0之前需要使用final修饰局部变量,包括参数
class InnerClass{
//方法内部类中的实例变量
public int innerid=1001;
//方法内部类中不能有类变量
//public static String innername="1001";
//方法内部类中的构造方法
public InnerClass(){
//方法内部类中的构造方法,可以访问本方法中的局部变量,jdk8.0之前需要使用final修饰局部变量,包括参数
System.out.println(num1);
System.out.println(num2);
//方法内部类中的构造方法,可以访问外部类的实例变量/实例方法,默认外部类类名.this. , 可以省略外部类类名.this.
System.out.println(TestClass.this.testid);
System.out.println(testid);
TestClass.this.dox();
dox();
//方法内部类中的构造方法,可以访问外部类的类变量/类方法,默认外部类类名 ,
//也可以外部类类名.this. ,可以省略外部类类名/外部类类名.this.
System.out.println(TestClass.testname);
System.out.println(TestClass.this.testname);
System.out.println(testname);
TestClass.doxStatic();
TestClass.this.doxStatic();
doxStatic();
}
//方法内部类中的实例方法
public void innerMethod(){
//方法内部类中的实例方法,可以访问本方法中的局部变量,jdk8.0之前需要使用final修饰局部变量,包括参数
System.out.println(num1);
System.out.println(num2);
//方法内部类中的实例方法,可以访问外部类的实例变量/实例方法,默认外部类类名.this. , 可以省略外部类类名.this.
System.out.println(TestClass.this.testid);
System.out.println(testid);
TestClass.this.dox();
dox();
//方法内部类中的实例方法,可以访问外部类的类变量/类方法,默认外部类类名 ,
//也可以外部类类名.this. ,可以省略外部类类名/外部类类名.this.
System.out.println(TestClass.testname);
System.out.println(TestClass.this.testname);
System.out.println(testname);
TestClass.doxStatic();
TestClass.this.doxStatic();
doxStatic();
}
//方法内部类中不能有类方法
//public static void innerStaticMethod(){}
}
//在定义该方法内部类的方法中,访问方法内部类中的变量和方法,只能对象访问
InnerClass inner=new InnerClass();
System.out.println(inner.innerid);
inner.innerMethod();
}
}
匿名内部类—类/抽象类/接口的子类
没有名字的内部类
- 继承式的匿名内部类
package com.click369.test3;
public class TestMain {
public static void main(String[] args) {
//调用MyClass中的实例方法
MyClass mc=new MyClass();
//当一个方法的参数是抽象类类型的时候,可以是传递子类对象、上转型对象
//子类对象、上转型对象在创建的时候需要独立构建一个子类。
//如果我偏不构建这个子类,还想调用这个参数是抽象类类型的方法,怎么办?????
//可以使用匿名内部类的结构完成对子类的构建,只是现在构建出来的这个子类没有名称
//所以才叫匿名内部类
mc.myTest(new TestClass(){
@Override
public void testAbstractMethod() {
System.out.println("重写抽象类中的抽象方法");
}
});
/*
* 匿名内部类
new TestClass(){
@Override
public void testAbstractMethod() {
}
}
上面的这种结构就是一个匿名内部类,它代表TestClass类的子类,只是这个子类没有名字
*/
//优点:可以有效的减少类的创建
//缺点:不利于程序的阅读和理解
}
}
- 接口式的匿名内部类
package com.click369.test4;
public class TestMain {
public static void main(String[] args) {
//调用MyClass这个普通类中的实例方法
MyClass mc=new MyClass();
//当一个方法的参数为接口类型是,我们可以传递接口的子类对象/接口回调对象
//子类对象/接口回调对象,都需要构建一个独立的子类。
//如果我偏不构建这个子类,还想调用这个参数是接口类型的方法,怎么办?????
//可以使用匿名内部类的结构完成对接口子类的构建,只是现在构建出来的这个接口的子类没有名称
//所以才叫匿名内部类
mc.dox(new MyInterface() {
@Override
public void test1() {
System.out.println("重写接口的抽象方法");
}
});
/*
* new MyInterface() {
@Override
public void test1() {
System.out.println("重写接口的抽象方法");
}
}
上面的这种结构就是一个匿名内部类,它代表MyInterface接口的子类,只是这个接口子类没有名字
*/
//优点:可以有效的减少类的创建
//缺点:不利于程序的阅读和理解
}
}
静态嵌套类----就是成员内部类加上static修饰符。
1.静态嵌套类中的元素
1.实例变量 2.类变量 3.构造方法 4.实例方法 5.类方法
2.静态嵌套类中的方法访问其他的变量和方法
- 在静态嵌套类中构造方法和实例方法中访问静态嵌套类中的实例变量/实例方法,默认this.,可以省略this.
- 在静态嵌套类中构造方法和实例方法中访问静态嵌套类中的类变量/类方法,默认静态嵌套类类名.,也可以省略this.,可以省略静态嵌套类类名./this.
- 在静态嵌套类中类方法中不能访问静态嵌套类中的实例变量/实例方法。
- 在静态嵌套类中的类方法中访问静态嵌套类中的类变量/类方法,默认静态嵌套类类名.,可以省略静态嵌套类类名.
- 在静态嵌套类中的类方法中不能出现this.
- 在静态嵌套类中构造方法和实例方法/类方法可以访问构造方法,通过new访问。
3.静态嵌套类中访问外部类的变量和方法
- 静态嵌套类中的构造方法/实例方法/类方法可以访问外部类的实例变量/实例方法,只能通过外部类的对象访问
- 静态嵌套类中的构造方法/实例方法/类方法可以访问外部类的类变量/类方法,默认外部类的类名.,可以省略外部类的类名.
- 静态嵌套类中的构造方法/实例方法/类方法可以访问外部类的构造方法,通过new访问
4.外部类中访问静态嵌套类中的变量和方法
- 外部类中的构造方法/实例方法/类方法中可以访问静态嵌套类中的实例变量和实例方法,通过对象访问。
- 外部类中的构造方法/实例方法/类方法中可以访问静态嵌套类中的类变量和类方法,默认静态嵌套类的类名访问,也可以使用对象访问 ,以省略类名。
- 外部类中的构造方法/实例方法/类方法中可以访问静态嵌套类中的构造方法,通过new访问。
5.其他类中访问静态嵌套类中的变量和方法
- 在其他类中可以访问静态嵌套类中的实例变量和实例方法,通过对象访问。
- 在其他类中可以访问静态嵌套类中的类变量和类方法,默认静态嵌套类的类名访问,也可以使用对象访问 ,以省略类名。
- 在其他类中可以访问静态嵌套类中的构造方法,通过new访问。
- 注意:以上的访问是需要导包语句
- import 包名.外部类类名.静态嵌套类的类名;
6.2 异常类的体系结构?Error和Exception的区别?
1.Throwable
- 是所有错误和异常的超类,异常又分为运行时异常和非运行时异常,又可以分为不检查型日常和检查型异常。
2.Error和Exception
-
Error是错误,是程序无法处理的问题,一般由JVM抛出,像OOM等。
-
Exception是异常,程序要尽可能去处理这些异常。
3.运行时异常和非运行时异常
-
运行时异常一般称作 不检查异常,一般是由于逻辑错误导致的,可以处理也可以不处理。
-
非运行时异常称作 检查异常,是由编译器帮助检查的,如果不处理,程序不能编译通过,比如常见的IoeException,等等需要try catch或者throw出来的。
5.1 运行时异常与非运行时异常?
1. 运行时异常是不需要捕获的,程序员可以不去处理,当异常出现时,虚拟机会处理。常见的运行时异常有空指针异常。
我们常见的5中运行时异常:
- ClassCastException(类转换异常)
- IndexOutOfBoundsException(数组越界)
- NullPointerException(空指针)
- ArrayStoreException(数据存储异常,操作数组时类型不一致)
- 还有IO操作的BufferOverflowException异常
-
非运行时异常就必须得捕获了,否则编译不过去,java编译器要求程序员必须对这种异常进行catch,Java认为Checked异常都是可以被处理(修复)的异常,所以Java程序必须显式处理Checked异常。
常见的非运行异常有io异常和sql异常。
IOException、FileNotFoundExcetion 和SQLException
finally是异常处理工作的一部分,表示总是执行。一般finally写的代码语句就是流的关闭。也就是做了一项清理,工作清理工作对于我们来说是必不可少的,因为如果一些消耗资源的操作,比如IO,JDBC。如果我们用完以后没有及时正确的关闭,那后果会很严重,这意味着内存泄露。(有人说过,对于资源的及时正确的清理是程序员的基本素质之一)另外不得不说异常处理中的throws和throw的区别了。
1、throws出现在方法的声明中,表示该方法可能会抛出的异常,允许throws后面跟着多个异常类型
2、throw出现在方法体中,用于抛出异常。当方法在执行过程中遇到异常情况时,将异常信息封装为异常对象,然后throw。
5.1 如何处理异常?Try{}catch(){} throws throw finally
一、异常的处理:
1、捕捉:
1)、Java给我们提供了捕获异常的语句: Try{须要被检测的代码}
catch(异常类1 变量名){异常处理方法1}
catch(异常类2 变量名){异常处理方法2}
…
finally{一定会执行的语句}
2)、对捕获到的异常对象的常见操作:
(1)、getMessage()方法 :Throwbble中定义的返回异常信息的方法: String getMessage(){}
(2)、toString()方法 :在Object中定义,在异常类中重写的返回字符型信息的方法: String toString(){}
(3)、printStackTrace()方法:throwable中定义的打印异常信息流的方法: void printStackTrace(){}
其实jvm默认的处理方法就是在调用 printStackTrace()方法。
2、声明(抛出)
1)、Java给我们提供了throw 和 throws关键字来抛出异常。
2)、对于函数中含有可能出现异常的语句时,应在函数上使用 throws 异常类名 来声明(抛出)这个异常。(表明此处可能发生异常,
如果发生,抛给调用函数)
此举 强迫调用函数在调用处 处理异常(捕捉或者声明),增加安全性。
3、注意: 一旦程序被抓住处理了(try catch),程序会继续往下执行。
二、对多异常的处理
1、在函数上声明(抛出)异常时,尽量声明的更具体(也就是声明子类异常),这样, 在调用函数中处理异常的时候也可以处理的更具体
2、处理的原则 :对方声明几个异常, 就应该有几个 catch块儿。
如果几个catch块的异常类之间有继承关系,则 :父类对象的catch块儿放下面。
建议: 在catch中对异常进行处理的时候,要有具体的处理方法, 不要就是一件简单的 输出打印语句, 或者一个 e.printStackTrace(),
三、自定义异常
原因:项目中会有一些的特殊的问题,Java并未进行封装,所以我们可以按照Java封装异常的的方法来对这些异常进行自定义的异常封装。
创建方法:创建新的异常类的方法:定义一个新的异常类,并且此类必须要继承至Exception类。
for example : class FuShuException extends Exception 。
使用方法:在使用时,如果有可能会发生我们自定义的异常的时候, 我们就应当手动的写语句进行判断,当判断符合要求时就
通过 throw 关键字来人为的从方法内部抛出异常对象。
当我们从方法内部抛出异常之后,就必须对这个异常进行处理(要么在方法上进行声明(抛出), 或者进行try ()catch{})。
但是在一般的时候,我们都将这个内部抛出的方法做声明处理。
注意:我们自定义异常类的时候,就必须继承至 Exception 类,所以就继承了其中的方法,这个方法可以直接使用 也可以重写 ,
我们还可以直接定义其他的特有的方法。
继承Exception 的原因: 因为我的异常体系有一个特点:异常类 和异常对象 都具有可抛性(被 throw 和throws操作)。 而这个可抛性是异常类的特有特点。
四、throw 和throws 的区别:
1、throws:使用在函数上, 后面跟的是异常类,可以跟多个,使用逗号隔开。
2、throw :使用在函数内部, 后面跟的是异常对象。
五、RuntimeException.
定义:在Eception 中有一个特殊的子类异常: RuntimeException ,直译为运行时异常。
特点:在函数类进行了抛出时,可以不用再函数上进行声明。
在函数上进行了声明后,可以不用在调用处 进行处理(声明 或者 try)
老师的解释:之所以不在函数上声明,是因为该类的异常不需要让调用者处理, 当该异常发生, 是希望程序停下来。
因为出现了无法运算的情况,希望停止程序后,对代码进行修正。(避免隐藏异常)
所以:当我们在自定义异常时, 如果该异常一旦发生,会造成程序不能继续运行,那么此时就应让 自定义类去继承 RuntimeException ,
使得该类异常一旦发生就会让程序停下了。
六、finally关键字。
功能:是为异常处理提供一个统一的出口,使得在控制流程转到程序其他部分以前,能对程序的状态做统一的管理。
作用:通常在finally语句中可以进行资源的清楚工作,:
如: 关闭打开的文件。 删除临时文件。 关闭数据库连接动作。
注意:无论try语句是否检测到异常, finally语句中的代码都会执行, 而且是 一定会执行、一定会执行、一定会执行
但是,还是有一种情况finally语句将不能执行:其前面有System.exit; //系统推出,程序运行结束, jvm结束。
七:异常检测语句的三中格式:
1、 try{} catch(){}
2、 try{} catch(){} finally{}
3、 try{} finally{}
注意:如果语句中没有catch 语句,就不能算异常已经被处理,如第三句的异常实际上没有被处理。(此时的异常要在方法处标示:抛出)
八、异常在子类和父类之间的覆盖体现:
1、当子类在覆盖父类时,如果父类中的方法抛出了异常,那么子类中的覆盖方法只能抛出该类异常, 或者是其子类异常, 或者不抛出异常。
2、如果父类方法抛出了多个异常,那么子类方法在覆盖父类方法的时候就只能抛出父类异常的子集。
3、如果父类或者接口方法在定义时没有异常抛出,那么子类方法在覆盖时也不能有异常抛出。
总结: 就是说子类方法中不能抛出比父类异常范围更大的异常,
如果确实在子类方法中产生了超出父类方法异常范围的异常,就必须在子类中
进行try{} catch(){}处理。
原因:如果子类方法中抛出了父类方法没有抛出的异常,当父类引用指向子类对象的时候,给父类方法准备的异常处理语句无法处理子类所抛出的异常。
九、总结:
异常(Exception):
|----编译时被检测异常:
|----编译时不被检测异常(RuntimeException及其子类)
5.1 常见的异常?
算术异常类:ArithmeticExecption
空指针异常类:NullPointerException
类型强制转换异常:ClassCastException
数组负下标异常:NegativeArrayException
数组下标越界异常:ArrayIndexOutOfBoundsException
违背安全原则异常:SecturityException
文件已结束异常:EOFException
文件未找到异常:FileNotFoundException
字符串转换为数字异常:NumberFormatException
操作数据库异常:SQLException
输入输出异常:IOException
方法未找到异常:NoSuchMethodException
java.lang.AbstractMethodError
抽象方法错误。当应用试图调用抽象方法时抛出。
java.lang.AssertionError
断言错。用来指示一个断言失败的情况。
java.lang.ClassCircularityError
类循环依赖错误。在初始化一个类时,若检测到类之间循环依赖则抛出该异常。
java.lang.ClassFormatError
类格式错误。当Java虚拟机试图从一个文件中读取Java类,而检测到该文件的内容不符合类的有效格式时抛出。
java.lang.Error
错误。是所有错误的基类,用于标识严重的程序运行问题。这些问题通常描述一些不应被应用程序捕获的反常情况。
java.lang.ExceptionInInitializerError
初始化程序错误。当执行一个类的静态初始化程序的过程中,发生了异常时抛出。静态初始化程序是指直接包含于类中的static语句段。
java.lang.IllegalAccessError
违法访问错误。当一个应用试图访问、修改某个类的域(Field)或者调用其方法,但是又违反域或方法的可见性声明,则抛出该异常。
java.lang.IncompatibleClassChangeError
不兼容的类变化错误。当正在执行的方法所依赖的类定义发生了不兼容的改变时,抛出该异常。一般在修改了应用中的某些类的声明定义而没有对整个应用重新编译而直接运行的情况下,容易引发该错误。
java.lang.InstantiationError
实例化错误。当一个应用试图通过Java的new操作符构造一个抽象类或者接口时抛出该异常.
java.lang.InternalError
内部错误。用于指示Java虚拟机发生了内部错误。
java.lang.LinkageError
链接错误。该错误及其所有子类指示某个类依赖于另外一些类,在该类编译之后,被依赖的类改变了其类定义而没有重新编译所有的类,进而引发错误的情况。
java.lang.NoClassDefFoundError
未找到类定义错误。当Java虚拟机或者类装载器试图实例化某个类,而找不到该类的定义时抛出该错误。
java.lang.NoSuchFieldError
域不存在错误。当应用试图访问或者修改某类的某个域,而该类的定义中没有该域的定义时抛出该错误。
java.lang.NoSuchMethodError
方法不存在错误。当应用试图调用某类的某个方法,而该类的定义中没有该方法的定义时抛出该错误。
java.lang.OutOfMemoryError