1 内部类
内部类的作用:
- 内部类提供了更好的封装,可以把内部类隐藏于外部类之内,不允许同一个包中的其他类访问该类。(例如给“牛”这个类组合一个“牛腿”,则可以把牛腿定义成内部类,因为牛腿脱离了牛没有意义)
- 内部类成员可以直接访问外部类私有数据,因为内部类被当成其外部类成员。
- 匿名内部类适合用于创建仅需要使用一次的类。
内部类除了必须定义在其他类里面之外,还有如下区别:
- 内部类可以比外部类多使用三个修饰符:private 、protected 、static (被当作成员)
- 非静态内部类不能拥有静态成员。
成员内部类(方法外,外部类里面定义的)的 class 文件的格式是 OuterClass$InnerClass.class。
1.1 非静态内部类
当调用非静态内部类的实例方法时,必须有一个非静态内部类实例,而非静态内部类实例必须寄生在外部类实例里。
- 非静态内部类方法访问某个变量时:优先在方法内寻找局部变量;接着在内部类找;接着在外部类找;如果依然不存在,则编译错误,提示找不到改变量。如果外部类成员变量、内部类成员变量、局部变量同名,则可以用 外部类名.this.val、this.val、val 来区分。
- 如果外部类需要访问非静态内部类的成员,则必须显示创建非静态内部类对象来调用访问其实例成员。
- 外部类的静态方法、静态代码块不能访问非静态内部类,包括不能使用非静态内部类定义变量、创建实例等。
- 非静态内部类里不能有静态方法、静态成员变量、静态代码块。(可以有普通初始化块)
1.1.1 创建非静态内部类实例
- 在外部类中,可以直接按照普通的创建对象的形式创建内部类实例
- 在其它类中,需要先创建外部类实例,再通过外部类实例创建内部类实例,内部类实例寄生于外部类实例。
OuterClass outer = new OuterClass();
(OuterClass.)InnerClass inner = outer.new Inner();
或
(OuterClass.)InnerClass inner = new OuterClass().new Inner();
1.2 静态内部类
使用 static 来修饰一个内部类,则这个内部类就属于外部类本身。静态内部类可以包含静态成员,也可以包含非静态成员,但是静态内部类不能访问外部类的实例成员,只能访问外部类的类成员。
静态内部类是外部类的一个静态成员,因此外部类的所有方法、所有初始化块中可以使用静态内部类来定义变量、创建对象。
外部类依然不能直接访问静态内部类的成员,但可以通过类名(或对象)作为调用者访问静态内部类的成员。
1.2.1 创建静态内部类实例
- 在外部类中,可以直接按照普通的创建对象的形式创建内部类实例
- 其他类中:
(OuterClass.)InerClass iner = new (OuterClass.)InnerClass();
1.3 局部内部类
声明周期和所在方法相同,生成 class 文件 格式为:OuterClass$NInnerClass.class。对比成员内部类,多了一个数字(N),因为同一个类了里可能有两个以上同名的局部内部类。
局部内部类若访问了局部变量(方法中定义的),则会自动给改变量加上 final 修饰符,意味着不能修改局部变量的值。
1.4 匿名内部类
适合创建只需要使用一次的类,定义格式如下:
new 实现接口()|父类构造器(实参列表){
//类体
}
匿名内部类必须继承一个父类,或实现一个接口,且最多继承一个父类或实现一个接口。
规则:
- 匿名内部类不能是抽象类
- 不能定义构造器,但可以定义初始化块,来完成构造器的工作。
当通过实现接口创建匿名内部类时,括号里不能有参数;
当通过继承父类来创建匿名内部类时,将拥有和父类相似的父类构造器(相同的形参列表),会根据参数列表调用父类构造器。类体里可以重写父类的普通方法。
同样的匿名内部类访问局部变量时,会自动添加 final 修饰。
2 异常处理
2.1 异常
异常是指在程序的运行过程中所发生的不正常的情况,它会中断正在运行的程序。java中通过异常处理机制为程序提供异常处理的能力,保持程序继续运行而不中断!
涉及异常处理的常用关键字有:try、catch、finally、throws、throw
2.2 try - catch
把有可能产生异常的代码放到try代码块中,catch代码块负责捕获并处理异常。
Exception是所有异常类的直接或者间接父类。往下又分为 RuntimeException 和 检查时异常。检查时异常要求编译时必须对其进行异常处理,而 RuntimeException 没有要求。
运行时异常:包括RuntimeException及其所有子类。不要求程序必须对它们作出处理,比如InputMismatchException、ArithmeticException、NullPointerException等。即使没有使用try-catch或throws进行处理,仍旧可以进行编译和运行。如果运行时发生异常,会输出异常的堆栈信息并中止程序执行。
Checked异常(非运行时异常):除了运行时异常外的其他异常类都是Checked异常。程序必须捕获或者声明抛出这种异常,否则出现编译错误,无法通过编译。处理方式包括两种:通过try-catch捕获异常,通过throws声明抛出异常从而交给上一级调用方法处理
常见的运行时异常
常见的检查时异常:
当 catch 后面的参数和发生的异常类型不匹配时,捕获异常失败,程序会终止,并由系统抛出异常提示。
可以采用多重 catch ,分别对不同类型的异常进行处理,类似于 if - else if。
2.3 try - catch - finally
把有可能产生异常的代码放到try中,catch负责匹配并处理异常,finally块用于进行收尾工作(关闭数据库、关闭文件、释放内存等资源),不管是否发生异常,finally内的代码都将执行。只有一种情况下不执行 finally 内的代码:在 try 或 catch 内部用 System.exit(0); 退出 JVM,finally 将没机会执行。
可以省略掉 catch,捕获到异常之后不对异常进行任何处理,直接进入 finally。
若有 return 关键字在 try - catch - fianlly 内部,系统的执行顺序总是执行到 return 的前一句, 接着执行完 finally 内部代码后再 return,不管 return 是存在于 try 还是 catch。
2.4 声明异常
2.4.1 throws 关键字
当一个方法可能存在异常,而此时自身又无法更好的处理,可以交给外界处理。此时用throws声明并抛出异常。开发者可以根据需要声明检查时异常(Exception或者非运行时异常)和运行时异常(RuntimeException或其子类)。如果调用处也不知道如何处理异常,可选择继续声明异常,我们把这个过程称为异常上抛,继续 throws。
2.4.2 声明异常与方法重载、重写
方法的重载完全不会受到声明异常的影响,若类中某一方法声明了异常,其重载的方法声不声明异常都可以。
但是重写不一样,以下四种情况都是合法的:
- 父类方法声明了异常(检测时或运行时),子类可以不声明任何异常。
- 父类方法声明没有声明任何异常(检测时或运行时),子类不声明异常或者声明运行时异常。(不能声明检测时异常)
- 父类声明了异常(检测时或运行时),子类声明完全一样的异常。
- 父类声明了检测时异常,子类声明了运行时异常。
2.5 手动抛出异常
除了系统自动抛出异常外,有些问题需要开发者手动抛出异常。使用关键字 throw
public void setGender(String gender) throws Exception{
if(gender.equals("男") || gender.equals("女")) {
this.gender = gender;
}else {
throw new Exception("性别不合法!");
}
}
2.6 自定义异常
如果开发者需要手动抛出的异常在系统不存在,可以自定义异常。如果要自定义异常,首先要确定异常类型,如果异常是运行时异常,必须继承 RuntimeException 或其子类;如果异常是检查时异常,必须继承 Exception 或其子类。异常的命名方式,参考系统命名方式,以Exception结尾。
public class AgeException extends Exception{ public AgeException() {
super();
} public AgeException(String message) {
super(message);
}
}
3 LeetCode
给定一个仅包含大小写字母和空格 ' '
的字符串,返回其最后一个单词的长度。如果不存在最后一个单词,请返回 0 。
说明:一个单词是指由字母组成,但不包含任何空格的字符串。
示例:
源码:
class Solution {
public int lengthOfLastWord(String s) {
int num = 0;
for(int i = s.length() - 1; i >= 0; i--){
if (s.charAt(i) == ' ' && num == 0)
continue;
if (s.charAt(i) != ' ')
num++;
else
break;
}
return num;
}
}