内部类
java允许将一个类A声明在另一个类B中,则A是内部类,类B称为外部类
内部类的分类:
成员内部类 静态+非静态
局部内部类 方法内,代码块内,构造器内
成员内部类 :
1 作为外部类的成员
调用外部类的结构 外部class.this.结构
可以被static修饰
可以被四种不同的
2 作为一个类
类可以定义属性,方法,构造器等
可以被final修饰,表示此类不能被继承
可以被abstract修饰
package t1_InnerClass;
/**
* 内部类是在类里定义的类
* @Author: zhangfan
* version 1.0
* @Date: 2022/07/18/10:48
*/
public class InnerClassTest {
private String name="小花";
public static void main(String[] args) {
//static修饰的结构随着类的加载而加载
InnerClassTest.EE ee=new InnerClassTest.EE();
ee.show();
//非静态成员应该由对象调用
InnerClassTest innerClassTest=new InnerClassTest();
InnerClassTest.DD dd=innerClassTest.new DD();
dd.show();
}
//代码块内内部类
{
class AA{
}
}
//方法内内部类
void method() {
class BB{
void method(){
}
}
}
public InnerClassTest(){
//构造器内内部类
class CC{
}
}
//成员内部类
class DD{
String name="静态";
public void show(){
String name="小紫";
System.out.println("我是一个非静态成员内部类");
//成员内部类访问内部类方法里的同名属性
System.out.println(name);
//成员内部类访问内部类的同名属性
System.out.println(this.name);
//成员内部类访问外部类的同名属性
System.out.println(InnerClassTest.this.name);
}
}
//静态成员内部类
static class EE{
public void show(){
System.out.println("我是一个静态成员内部类");
}
}
}
成员内部类和局部内部类,在编译之后都会生成字节码文件,
格式: 成员内部类:外部类$内部类名.class
局部内部类:外部类$数字内部类.class
方法里的局部内部类调用外部类的方法中的属性时,属性默认为final的
package t1_InnerClass;
/**
* 内部类的字节码文件
* @Author: zhangfan
* version 1.0
* @Date: 2022/07/18/16:25
*/
public class InnerClassTest2 {
public static void main(String[] args) {
System.out.println(AA.class);
InnerClassTest2 innerClassTest2=new InnerClassTest2();
innerClassTest2.method();
}
class AA{
}
void method(){
int num=10;
class BB{
public void show(){
//方法里的局部内部类调用外部类的方法中的属性时,属性默认为final的
//num=200; 会报错
System.out.println(num);
}
}
System.out.println(BB.class);
}
}
异常
throwable异常区分为两类:Error和Exception
Error:Java虚拟机无法解决的严重问题
Exception:其他因编程错误或偶然的外在因素导致的一般性问题
编译时异常(受检异常),执行javac.exe时可能出现
运行时异常(非受检异常),执行java.exe时可能出现
常见异常举例
package t1_InnerClass;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.FileInputStream;
import java.util.Date;
import java.util.Scanner;
/**
* 常见运行时异常
*
* @Author: zhangfan
* version 1.0
* @Date: 2022/07/18/17:16
*/
public class t2_exception {
//NullPointerException
@Test
public void test1(){
String str=null;
System.out.println(str.charAt(0));
}
//ArrayIndexOutofBoundsException
@Test
public void test2(){
int[] arr=new int[3];
arr[3]=5;
}
//ClassCastException
@Test
public void test3(){
Date date = new Date();
String str=(String)(Object)date;
}
//NumberFormatEcception
@Test
public void test4(){
Integer integer=Integer.parseInt("abc");
}
//InputMismatchException
@Test
public void test5(){
Scanner scanner=new Scanner(System.in);
int score=scanner.nextInt();
System.out.println(score);
scanner.close();
}
//ArithmeticException
@Test
public void test6(){
int a=10;
int b=0;
System.out.println(a/b);
}
//编译时异常
//FileNotFoundException
@Test
public void test7(){
String road="hello.txt";
File file=new File(road);
FileInputStream fis=new FileInputStream(file);
int data = fis.read();
while (data!=-1){
System.out.println((char)data);
data=fis.read();
}
fis.close();
}
}
异常处理:抓抛模型
抛 程序遇到异常,在异常代码处生成一个异常类对象,将对象抛出,后续代码不再执行
抓 对抛出的异常进行处理1 try catch finally, throws
说明:
finally是可选的
使用try将可能出现异常代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常的都西昂,根据此对象的类型,去catch中进行匹配。
一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理,一旦处理完成,就跳出当前的try-catch结构,在没有finally的情况下,继续执行其后的代码
catch中的异常类型如果没有子父类关系,则声明在上在下无所谓
catch中的异常类型如果满足子父类关系,则要求子类一定声明在父类的上面,否则报错
常用的异常处理方式:1 String getMessage() 2 printStackTrace()
try-catch-finally 处理编译时异常,编译时不再报错,运行时仍然可能报错,相当于将编译时可能出现的异常延迟到运行时出现
try中声明的变量出了try之后不可以再用。
try-catch-finally中finally的使用
1 finally是可选的,即可写可不写
2 finally中声明的代码是一定会执行的代码。例如以下情况也会执行
1 try中return
2 catch中有异常
3 cathch中有return
3 finally内部一般语句。数据库连接,输入输出流,网络编程Socket等资源,JVM不会自动回收,我们需要手动进行资源释放。此时资源释放,需要放入finally中
public class finallyTest1 {
/**
*TODO
* finally代码和异常之后代码的区别
* 1 try中无异常有return语句,finally执行
* 2 try中有位于return语句之前的异常发生,且此异常catch语句中有return,finally执行
*/
@Test
public void test1(){
try {
//两者都会执行
int a=2,b=0;
//b=1; 普通执行不会执行
System.out.println(a/b);
//return; 普通执行不会执行
}catch (ArithmeticException e){
e.printStackTrace();
//return; 普通执行不会执行
}finally {
System.out.println("finally执行");
}
System.out.println("普通执行");
}
/**
* TODO
* try,catch,finally中都有返回值,且会执行catch语句时
* 结论:finally中有返回finally
* 如果无try无异常返回try,try有异常返回catch
*/
@Test
public void test2(){
System.out.println(method());
}
public int method(){
try {
//两者都会执行
int a=2,b=0;
System.out.println(a/b);
return 1;
}catch (ArithmeticException e){
e.printStackTrace();
return 2;
}finally {
System.out.println("finally执行");
return 3;
}
}
/**
* TODO
* finally在文件读取中的使用
* 1 创建文件对象
* 2 创建文件输入流
* 3 绑定文件
* 4 读取文件
* 5 处理FileNotFoundException
* 6 处理IOException
* 7 关闭文件输入流
* 8 处理IOException
*/
@Test
public void test3(){
String road="hello.txt";
File file=new File(road);
FileInputStream fis= null;
try {
fis = new FileInputStream(file);
int data = fis.read();
while (data!=-1){
System.out.println((char)data);
data=fis.read();
}
fis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}finally {
//如果没找到文件直接进入异常可能会导致fis为空指针,所以要进行判断
if (fis!=null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
throws一般是将异常往上抛,但是一般main中就不应该再抛了,try—catch才是处理异常
一旦当方法体执行时,出现异常,仍会再异常代码处生成一个异常对象,
此对象满足throws后异常类型时,就会被抛出。异常代码后续代码不会
再执行
开发中异常处理的选择:
1 父类被重写的方法没有throws方式处理异常,子类重写也不能使用throws,如果子类重写的方法有异常,必须使用try-catch-finally处理
2 在执行方法中,又调用另外的几个方法,这几个方法是递进关系,建议使用throws处理,执行方法中可以考虑使用try-catch-finally处理,因为前面的方法中出现异常,意味着要抛出,而不应当是处理之后将无用的数据继续执行
手动抛出异常
使用throw手动抛出异常:
throws处理的是系统自动生成的异常对象,throw用于手动生成对象并抛出
public class ThrowTest {
public static void main(String[] args) {
try {
Student student = new Student();
student.register(-1001);
System.out.println(student);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
class Student{
private int id;
public void register(int id) throws Exception {
if(id>0){
this.id=id;
}else{
//生成异常并抛出
throw new Exception("您输入的数据非法!");
}
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
'}';
}
}
自定义异常:
1 继承于现有的结构:RuntimeException,Exception
2 提供全局常量:serialVersionUID 用于标识异常的序列号
3 提供重载的构造器
public class MyException extends RuntimeException{
//标识异常的序列号
static final long serialVersionUID = -7034897190745766939L;
public MyException() {
}
public MyException(String msg){
//调用父类构造器
super(msg);
}
}
异常练习
public class EcmDef {
public static void main(String[] args) {
try{
Integer a=Integer.parseInt(args[0]);
Integer b=Integer.parseInt(args[1]);
System.out.println(ecm(a,b));
}catch (ArithmeticException e){
System.out.println("除0");
}catch (NumberFormatException e){
System.out.println("数据格式不对");
}catch (ArrayIndexOutOfBoundsException e){
System.out.println("缺少参数");
}catch (EcDef e){
System.out.println(e.getMessage());
}
}
public static int ecm(int a,int b)throws EcDef{
if(a<0||b<0){
throw new EcDef("不可以为负数");
}
return a/b;
}
}
class EcDef extends RuntimeException{
private static final long serialVersionUID = -7034897190745766940L;
public EcDef(){
}
public EcDef(String msg){
super(msg);
}
}
使用命令行调试:
记得去掉包名,并且注意文档的字符编码,我用的ANSI编码中文才不会乱码