1. 接口
1.1 地位
1.2 作用
让不同实现了接口的类的对象可以提供相同的功能,比如:
1.3 实现方式
- 创建一个接口
//Flyable接口
public interface Flyable {
//提供fly方法
//public与abstract修饰符默认提供,可以不写
//不能提供方法体
public abstract void fly();
}
- 让需要的类实现接口(通过implement关键字来实现)
//鸟类
class Birds implements Flyable {
//实现fly方法
public void fly(){
System.out.println("鸟儿在天空飞翔");
}
}
//超人类
class Supermans implements Flyable{
//实现fly方法
public void fly(){
System.out.println("超人在天空飞行");
}
}
- 调用即可
public class Test{
public static void main(String[] args) {
(new Birds()).fly();//输出结果:鸟儿在天空飞翔
(new Supermans()).fly();//输出结果:超人在天空飞行
}
}
1.4 接口的组成部分
JDK版本 | 全局静态常量 | 全局抽象方法 | 全局静态方法 | 全局默认方法 |
---|---|---|---|---|
JDK1.8之前 | yes | yes | no | no |
JDK1.8 | yes | yes | yes | yes |
1.4.1 全局静态常量
接口中的变量默认修饰符为public static final,无法修改,但可以不写
//显式
public static final int AGE=18;
//隐式
int AGE=18;
1.4.2 全局抽象方法
在JDK1.8之前,接口中方法的修饰符默认是public abstract,无法修改,但可以不写
//显式
public abstract void test();
//隐式
void test();
1.4.3 全局静态方法及全局默认方法
在JDK1.8,为了方便修改接口,减少实现接口的类的实现方法代码量,提供了全局静态方法与全局默认方法
1.4.3.1 全局静态方法
实现类调用全局静态方法只能通过接口名调用
public Interface Test1{
//全局静态方法
public static void test(){
System.out.println("实现类调用test方法只能通过接口名调用");
}
}
public class Test2 implements Test1{
//实现类调用接口中的全局静态方法
Test1.test();
}
1.4.3.2 全局默认方法
public Interface Test1{
//全局默认方法
public default void test(){
System.out.println("test方法可以实现类被重写,也可以不被重写")
}
全局默认方法可以被实现类重写,也可以不重写。如果重写必须去掉default关键字,如果不重写,调用只能用接口名.super调用
public class Test2 implements Test1{
//重写
public void test(){
System.out.println("重写接口的全局默认方法");
}
//不重写,调用
Test1.super.test();
}
1.5 特征
- 接口没有构造方法,不能使用new关键字创建对象
- 实现类可以实现多个接口
- 实现类如果需要继承,那么继承必须写在实现接口之前
public class Child extends Father implements Test1, Test2...{
}
- 实现类也可以向上转型为接口,所以接口可以作为形参,而实参可以是该接口的任意实现类
//创建一个测试接口
public interface Test1{};
//创建一个测试类
public class Test2 implements Test1{
//实现类向上转型为接口对象
Test1 t1=new Test2();
//接口作为形参
public void test(Test1 t2){
}
public void test2(){
test(new Test2());//实参可以是该接口的实现类
}
}
2. 内部类
2.1 作用
- 实现多重继承
- 提高隐藏性
- 避免修改接口而实现同一类中两种同名方法调用
2.2 类型
局部内部类 | 成员内部类 | 静态内部类 | 匿名内部类 |
2.3 实现方式
2.3.1 局部内部类
不能有访问修饰符
public class OuterClass {
//定义一个构造代码块
{
//在代码块定义一个局部内部类
class InnerClass1{
}
}
//定义一个成员方法
public void test(){
//在方法中定义一个局部内部类
class InnerClass2{
}
}
}
2.3.2 成员内部类
public class OuterClass {
//定义一个成员内部类
class InnerClass{
}
}
2.3.3 静态内部类
public class OuterClass {
//定义一个静态内部类
static class InnerClass{
}
}
2.3.4 匿名内部类
可通过接口和继承(必须重写或实现其至少一种方法)两种方式创建
public class OuterClass extends Father implements Test {
//通过接口定义匿名内部类及其对象
Test t=new Test() {
int a=16;
};
//实现父类的抽象方法
public void test(){
}
//通过创建匿名内部类及其对象
Father f=new Father() {
@Override
public void test() {
}
};
}
//创建一个接口
interface Test{
}
//创建一个父类
abstract class Father{
public abstract void test();
}
2.4 特征
- 内部类可以直接调用外部类的成员(静态内部类调用外部类的非静态成员需使用类名或外部类对象名调用)
- 内部类的权限修饰符可以是(按权限等级):private—>default—>protected—>public
- 外部类访问内部类需要先创建内部类的对象(局部内部类只能在局部内部类所在的方法或代码块内部访问)
- 局部内部类不能使用任何访问修饰符
2.5 创建内部类对象的方式
2.5.1 创建局部内部类的对象
只能在局部内部类所在的方法或代码块内部创建
public class OuterClass{
//构造代码块
{
//通过代码块创建局部内部类
class InnerClass1{
public void test1(){
}
}
//创建局部内部类的对象
InnerClass1 t1=new InnerClass1();
//调用局部内部类的方法
t1.test1();
}
//成员方法
public void test2(){
//通过成员方法创建局部内部类
class InnerClass2{
public void test3(){
}
}
//创建局部内部类的对象
InnerClass2 t2=new InnerClass2();
//调用局部内部类的方法
t2.test3();
}
}
2.5.2 创建成员内部类的对象
public class OuterClass {
//定义成员内部类
class InnerClass{
}
//创建成员内部类对象
OuterClass.InnerClass obj=new OuterClass().new InnerClass();
}
2.5.3 创建静态内部类的对象
public class OuterClass {
//定义静态内部类
static class InnerClass{
}
//创建成员内部类对象
OuterClass.InnerClass obj=new OuterClass.InnerClass();
}
2.5.4 创建匿名内部类的对象
见2.3.4示例代码
3. 垃圾回收机制
3.1 优缺点
- 优点:提高安全性、提高效率
- 缺点:更多的性能开销
3.2 特征
- 只回收JVM堆区的对象空间
- 回收时间不可预知
- 回收前系统会主动调用finalize方法(如果覆盖该方法,让一个新的引用变量重新引用该对象,则会重新激活对象)
- 可将对象的引用变量设置为null,以此暗示垃圾回收机制回收该对象
- 可以通过System.gc()或Runtime.getRuntime().gc()来通知系统进行垃圾回收,但何时回收不确定
3.3 实现代码
public class Test {
public static void main(String[] args) {
//创建没有引用变量的对象
new Test();
new Test();
new Test();
new Test();
//设置对象的引用变量为null,暗示垃圾回收机制
Test t=new Test();
t=null;
//提醒回收
System.gc();
Runtime.getRuntime().gc();
}
//重写finalize方法
//如果垃圾回收成功,打印语句
@Override
protected void finalize() throws Throwable {
System.out.println("垃圾回收成功");
}
}
4. 异常机制
4.1 作用
发生异常后,给出异常类型、异常提示信息及异常位置
4.2 异常的类型
4.2.1 Exception类
Exception类是所有异常类的父类
4.2.2 RuntimeException类
RuntimeException类是Exception类的子类,可以不作处理,系统会自动检测并处理
4.2.3 CheckedException类
必须处理CheckedException类异常,否则编译错误
4.3 捕获异常方式
4.3.1 try{}
将可能出现异常的语句放入try的代码块中
/**
* 功能:输入班级总分及人数,计算平均分
*/
public class Test {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
try{
//获取班级总分
System.out.print("请输入班级总分:");
int sum=sc.nextInt();
//获取班级总人数
System.out.print("请输入班级人数:");
int number=sc.nextInt();
//计算平均分并输出结果
System.out.println("班级平均分为:"+sum/number);
}
}
}
4.3.2 catch{}
4.3.2.1 单层catch{}
将异常处理语句放入catch的代码块中,catch处理异常信息有三种:
- 自定义输出异常信息
/**
* 功能:输入班级总分及人数,计算平均分
*/
public class Test {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
try{
//获取班级总分
System.out.print("请输入班级总分:");
int sum=sc.nextInt();
//获取班级总人数
System.out.print("请输入班级人数:");
int number=sc.nextInt();
//计算平均分并输出结果
System.out.println("班级平均分为:"+sum/number);
}catch (Exception e){
//自定义输出异常信息
System.out.println("欧,我死了");
}
}
}
- 调用异常对象的方法输出异常
/**
* 功能:输入班级总分及人数,计算平均分
*/
public class Test {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
try{
//获取班级总分
System.out.print("请输入班级总分:");
int sum=sc.nextInt();
//获取班级总人数
System.out.print("请输入班级人数:");
int number=sc.nextInt();
//计算平均分并输出结果
System.out.println("班级平均分为:"+sum/number);
}catch (Exception e){
//调用异常对象e的方法输出异常信息
e.printStackTrace();//输出异常的堆栈信息
System.out.println(e.getMessage());//输出异常信息描述
System.out.println(e.toString());//输出异常信息类名及异常原因
}
}
}
- 通过throw关键字向上抛出异常信息
/**
* 功能:输入班级总分及人数,计算平均分
*/
public class Test {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
try{
//获取班级总分
System.out.print("请输入班级总分:");
int sum=sc.nextInt();
//获取班级总人数
System.out.print("请输入班级人数:");
int number=sc.nextInt();
//计算平均分并输出结果
System.out.println("班级平均分为:"+sum/number);
}catch (Exception e){
//通过throw关键字向上抛出异常信息
throw e;
}
}
}
4.3.2.2 多重catch{}
public class Test {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
try{
//获取班级总分
System.out.print("请输入班级总分:");
int sum=sc.nextInt();
//获取班级总人数
System.out.print("请输入班级人数:");
int number=sc.nextInt();
//计算平均分并输出结果
System.out.println("班级平均分为:"+sum/number);
}catch (ArithmeticException e){ //如果是算数异常
e.printStackTrace();//先输出异常的堆栈信息
}catch (InputMismatchException e){//如果是输入不匹配异常
e.printStackTrace();//先输出异常的堆栈信息
throw e;//再向上抛出异常信息
}
catch (Exception e){//如果是除算术异常的其他异常
//通过throw关键字向上抛出异常信息
throw e;
}
}
}
在多重catch中,父类异常必须排在子类异常的后面,因为如果是父类异常在前,那么后面的子类异常根本无法执行
4.3.2.3 catch代码块执行顺序
- try中代码无异常,catch代码块不执行
- try中代码异常,且与catch中异常类型匹配(相同或父类),try中出现异常的代码的后面的代码将不再执行,执行catch对应的异常语句及catch代码块之后的语句(但catch中执行了throw关键字或return语句,那么catch代码块之后的语句也将不再执行)
- try中代码异常,但与catch中异常类型不匹配,程序直接中断,不再向后执行。
4.3.3 finally{}
无论有没有异常,无论catch语句中有没有执行throw或return,程序最后都会执行finally代码的内容
public class Test {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
try{
//获取班级总分
System.out.print("请输入班级总分:");
int sum=sc.nextInt();
//获取班级总人数
System.out.print("请输入班级人数:");
int number=sc.nextInt();
//计算平均分并输出结果
System.out.println("班级平均分为:"+sum/number);
}catch (ArithmeticException e){ //如果是算数异常
e.printStackTrace();//先输出异常的堆栈信息
return;//再结束程序
}catch (Exception e){//如果是除算术异常的其他异常
//通过throw关键字向上抛出异常信息
throw e;
}finally {
System.out.println("无论如何都要输出");
}
System.out.println("catch内不执行throw及return将执行本语句");
}
}
4.4 throw与throws的区别
差异 | throw | throws |
---|---|---|
使用位置 | 方法体中 | 方法声明中 |
后面内容差异 | 后面最多接一个对象 | 后面最少接一个类 |
作用 | 向上抛出异常信息 | 声明该方法中有异常被throw,告诉调用者必须处理 |
如果在try中throw,该方法必须throws才能通过编译
4.5 自定义异常
自定义异常用于更准确、个性、交互的异常信息传递
5.4.1 实现方式
- 新建一个继承Exception类的子类
- 至少提供无参构造方法及String参数的有参构造方法
public class InputNagativeException extends Exception {
//无参构造方法
public InputNagativeException(){
}
//String参数构造方法
public InputNagativeException(String massage){
System.out.println(massage);
}
}
public class Test {
public static void main(String[] args) throws InputNagativeException {
try{
//获取班级总分
Scanner sc=new Scanner(System.in);
System.out.print("请输入班级总分:");
int sum=sc.nextInt();
//总分不能为负
if(sum<0){
throw new InputNagativeException("总分不能为负");
}
//获取班级总人数
System.out.print("请输入班级人数:");
int number=sc.nextInt();
//班级人数不能为负
if(number<0){
throw new InputNagativeException("人数不能为负");
}
//计算平均分并输出结果
System.out.println("班级平均分为:"+sum/number);
}catch(ArithmeticException e){ //如果是算数异常
e.printStackTrace();//输出异常的堆栈信息
}catch(InputMismatchException e){//如果是输入异常
e.printStackTrace();//输出异常的堆栈信息
}catch(Exception e){//如果是除算术异常的其他异常
throw e;//向上抛出异常信息
}
}
}