Java 多态、内部类、异常、包

时间:2024-09-06 19:03:20

一、多态

1. 概述

  • 理解:多态可以理解为事物存在的多种体(表)现形态。
    • 例如:
      • 动物中的猫和狗。
      • 猫这个对象对应的是猫类型,例如:猫 x = new 猫();
      • 同时猫也是动物中的一种,也可以把猫称为动物,例如:动物 y = new 猫();
      • 动物就是猫和狗这些具体事物中抽取出来的父类型,父类型的引用指向了子类对象。
  • 体现
    1. 父类的引用指向了子类的对象。
    2. 父类的引用也可以接收自己子类的对象。
      • 例如:Animal a = new Cat();
        • 父类型 a 的引用指向了子类的对象。
  • 前提
    1. 类与类之间必须有关系,要么是继承,要么是实现。
    2. 存在重写(覆盖),父类中有方法被子类重写(覆盖)。

2. 利与弊

  • 利:提高了程序的可扩展性和后期的可维护性。

  • 弊:只能使用父类中的引用去访问父类中的成员。

    • 使用了多态,父类型的引用在使用功能时,不能直接调用子类中特有的方法。
      • 例如:Animal a = new Cat();//多态的体现
        • 假设子类 Cat 中有抓老鼠的特有功能,父类型 a 就不能直接调用该功能,可以理解为 Cat 类型向上转型,提升了。
        • 父类的引用想要调用子类 Cat 中特有的方法,就要将父类的引用强制转换成子类的类型,向下转型。例如:Cat c = (Cat)a;
      • 注:
        • 如果父类可以创建对象的话,例如:Animal a = new Animal(); 就不能进行向下转型了,Cat c = (Cat)a; 这样编写代码在编译时会报错,不要出现将父类对象转换成子类类型。
        • 多态转换的是父类引用指向了自己子类的对象时,该引用可以被提升,也可以被强制转换,多态始终都是子类对象在做变化的。
  • 例如:

    Java 多态、内部类、异常、包

    Java 多态、内部类、异常、包

3. 特点

1. 成员变量

  • 无论编译还是运行时期,都参考左边(引用变量)所属的类。
    • 例如:多态中父类引用调用成员变量时,如果父类和子类有同名的成员变量,被调用的则是父类中的成员变量。

2. 非静态成员函数

  • 编译时期:参考引用型变量所属的类中是否有调用的方法,如果有,编译通过,如果没有,编译失败。
    • 例如:a.catchMouse();//编译报错,这种情况只能通过强制转换,向下转型后才可以使用子类中特有的功能。
  • 运行时期:参考对象所属的类中是否有调用的方法,如果父类中有一个非抽象方法,子类继承后将其重写(覆盖)了,多态运行时,父类的引用调用同名函数时,运行的是父类中的方法。
  • 总而言之:成员函数在多态调用时,编译看左边,运行看右边。

3. 静态成员函数

  • 无论编译还是运行时期,都参考左边,就是父类的引用在调用同名静态成员函数时,调用的是父类中的静态成员函数。
    • 原因:
      • 当类一被加载时,静态成员函数随类绑定在内存中,不需要创建对象,可以通过类名直接调用静态成员函数,并且父类中的静态成员函数一般是不被重写(覆盖)的。

      • 类在方法区中的分配:静态方法区和非静态方法区(关键字 super 和 this 在非静态方法区中)。

        Java 多态、内部类、异常、包

4. 应用

  1. 定义工具类,将共同行为封装在同一个类中。
  2. 对类型进行抽取(多态的产生)。
  3. 操作同一个父类型,对其中的子类型均可进行操作。
    • 例如:

      Java 多态、内部类、异常、包

      Java 多态、内部类、异常、包

二、内部类

1. 概述

  • 概述:将一个类定义在另一个类里面,定义在类里面的那个类称为内部类(内置类、嵌套类)。
  • 理解:当描述事物时,事物的内部还存在事物,事物内的事物使用内部类来描述,印务内部事物在使用外部事物的内容。
    • 例如:定义一个人的类,手、脚、心脏等等都属于人,这些又各自有自己的功能描述,这时候就可以在人这个描述类中,定义一些描述手、脚、心脏的类,也就是定义一些内部类。
  • 编译现象:编译 Java 文件时,如果类中有内部类,生成的 class 文件中会含有 Test$1.class 等等这样的文件,编译器会把内部类翻译为用 $(美元符号)分隔外部类名和内部类名的类文件,这是内部类的一种编译现象。

2. 访问规则

  1. 外部类要访问内部类,必须建立内部类的对象。
  2. 内部类可以直接访问外部类中的成员(包括私有成员)。
    • 原因:因为内部类中持有外部类的引用。
    • 格式:外部类名.this

3. 访问格式

  1. 内部类定义在外部类的成员位置,而且非私有时,可以在外部的其它类中直接建立内部类对象。
    • 格式:外部类名.内部类名 变量名 = 外部类对象.内部类对象;
      • 例如:Outer.Inner in = new Outer().new Inner();
    • 内部类定义在外部类的成员位置时,可以被成员修饰符所修饰。
      • 例如:
        • private:内部类在外部类中进行封装。
        • static:内部类有局部 statci 的特性,内部类被 static 修饰后,只能访问外部类中的 static 成员。
          • 外部的其它类访问 static 内部类中静态成员的格式:外部类名.内部类名.方法名();
            • 例如:Outer.Inner.function();
          • 外部的其它类访问 static 内部类中非静态成员的格式:new 外部类名.内部类名().方法名();
            • 例如:new Outer.Inner().function();
    • 注:
      1. 内部类中定义了静态成员时,该内部类必须是 static 的。
      2. 外部类中的静态方法访问内部类时,内部类也必须是 static 的。
      3. 实际开发中,内部类一般定义为 private,很少定义为 public 的。
  2. 内部类定义在局部位置
    1. 不可被成员修饰符所修饰,作用域限定在声明这个局部内部类的代码块中。
      • 例如:public、private、static 等修饰符。
    2. 可以直接访问外部类中的成员,还持有外部类的引用。
      • 注:内部类不可以访问所在局部中的非最终变量,只能访问被 final 修饰的局部变量。

      • 例如:

        Java 多态、内部类、异常、包

        • 分析:上述代码中的打印结果为 7、8,会改变,并没有因为被 final 所修饰就无法改变,原因是因为类调用方法结束后,被 final 修饰的变量已经从桟内存中消失,类再次调用方法时,已经是另一个变量了,所以可以重新传值。

4. 匿名内部类

  • 匿名内部类就是内部类的简写格式,匿名内部类其实就是一个匿名子类对象,可以理解为带内容的对象。

  • 格式:new 父类或接口(){定义子类的内容}

  • 定义匿名内部类的前提:

    • 匿名内部类必须是继承一个类或者实现接口。
      • 特殊情况:所有的类都有一个父类 Object,定义时也可用 Object。
  • 利与弊:

    • 利:简化书写。
    • 弊:
      1. 不能做强转的动作。
      2. 不能直接调用自己的特有方法。
      3. 如果继承的父类或接口中方法很多时,使用匿名内部类的可阅读性会非常差,调用也非常麻烦,因此匿名内部类中定义的方法一般不超过3个。
  • 例如:

    Java 多态、内部类、异常、包

三、异常

1. 概述

  • 异常是 Java 中的重要机制,使用面向对象的思想进行了封装,通常使用的是异常类。异常类中描述的就是程序中可能出现的错误或者问题。
  • 程序在运行时出现不正常的情况称为异常。
  • 由来:异常问题也是现实生活中一个具体的事物,可以通过 Java 中类的形式进行描述,并封装成对象,异常类就是 Java 对不正常的情况进行描述后的对象体现。
  • 程序中可能出现的错误或者问题:
    1. 用户输入错误导致的异常:非法参数、不按正常的使用程序等。
    2. 设备硬件等发生的错误的异常:硬盘损坏等。
    3. 物理限制的异常:存储空间不足、SDCard不存在等。
    4. 代码错误的异常:程序编写的方法不正确、返回错误的参数等。

2. 体系

异常问题的划分方式

  1. 严重的异常问题。
    • 严重的异常问题,Java 通过 Error 类进行描述,对于 Error 类一般不编写针对性的代码进行处理。
  2. 非严重的异常问题。
    • 非严重的异常问题,Java 通过 Exception 类进行描述,对于 Exception 类可以使用针对性的代码进行处理。

        注:无论是 Error 或 Exception 都具有一些共性内容,例如:不正常情况的相关信息、引发异常的原因等。

体系

  • Throwable
    • Error//出现重大异常问题。例如:运行的类不存在或者内存溢出等。
    • Exception//运行时出现的异常情况。
      • RuntimeException//特殊的异常类,Exception 的子类,抛出时不需要做声明。
  • 注:Error 和 Exception 的子类名称都是以父类名做为后缀。

特点

  1. 异常体系中所有类以及建立的对象都具备可抛性;
  2. 可以被 throw 和 throws 关键字所修饰操作;
  3. 只有异常体系才具备这个特点。

3. 分类

编译时异常

  • 异常在编译时,如果没有做任何处理(没有抛也没有 try),编译失败。
  • 异常被标识,代表可以被处理。

运行时异常

  • 编译时不需要处理,编译器不检查。
  • 异常发生时,Java 建议不处理,让程序停止。
  • 需要对代码进行修正。(例如:RuntimeException 以及 RuntimeException 的子类)

4. 处理

  • Java 中提供了特有的语句进行处理:

      try{
    需要检测的语句;
    }catch(异常类 变量){
    处理异常的语句(方式);
    }finally{
    一定要执行的语句;
    }
    • 这些特有语句的处理方式有三种结合情况:

        1.
      try{
      需要检测的语句;
      }catch(异常类 变量){
      处理异常的语句(方式);
      }
      2.
      try{
      需要检测的语句;
      }finally{
      一定要执行的语句;
      }
      3.
      try{
      需要检测的语句;
      }catch(异常类 变量){
      处理异常的语句(方式);
      }finally{
      一定要执行的语句;
      }
    • 注:

      1. finally 中定义的一般是关闭资源的代码,资源使用完后必须释放。
      2. 如果存在一些功能是必须执行的,可以使用 try{}finally{} 的方式,将必须执行的语句放在 finally 代码块中。
      3. finally 只有一种情况是不会执行的,在进入 finally 之前就先执行了 System.exit(0); 时,已经是退出了程序,finally 就不会执行的。
  • throw 和 throws 的用法:

    • throw 定义在函数内,用于抛出异常对象。
    • throws 定义在函数上,用于抛出异常类,可以抛出多个对象,使用 , 隔开即可。
      • 当函数的内容有 throw 抛出异常对象,但没 try 处理时,必须在函数上声明,否则会导致编译失败。
    • 注:RuntimeException 除外,函数内如果抛出的是 RuntimeException 异常,函数上不需要声明。
  • 抛出信息的处理:

    • 函数中出现了 throw 抛出异常对象时,要么是在函数内部进行 try 的处理,要么是在函数上声明,让函数的调用者去处理。

    • 对于捕获到的异常对象常见的操作方法:

        getMessage();//获取异常信息,返回字符串
      toString();//获取异常类名和异常信息,返回字符串
      printStackTrace();//获取异常类名、异常信息以及异常在程序中出现的位置,返回值 void,JVM 默认的异常处理机制中,就是调用 PrintStackTrace() 方法来打印异常的堆栈跟踪信息
      printStackTrace(PrintStream s);//将异常内容保存在日志文件中

5. 自定义

  • 对于一些特有的问题,这些特有的问题没有被 Java 所描述封装为对象,对于这些特有的问题,可以按照 Java 的面向对象思想将问题进行自定义的异常封装,这就是自定义异常。
    • 自定义异常类继承 Exception 或 RuntimeException
      1. 让自定义异常类具备可抛性。
      2. 让自定义异常类具备操作异常的共性方法。
    • 如何自定义异常信息?
      • 自定义异常信息可以使用父类已定义好的功能,异常信息传递给父类的构造函数,由于父类中已经把异常信息的操作都完成了,子类只需在构造函数中,通过 super 语句将异常信息传递给父类,就可以通过 getMessage() 方法获取自定义的异常信息了。

      • 例如:

        Java 多态、内部类、异常、包

        • 说明:定义异常时,如果异常的发生,会导致无法继续进行运算,则让自定义异常类继承 RuntimeException。
      • 注意:自定义异常必须是自定义异常类有继承关系,一般继承 Exception。

        • 自定义异常类继承 Exception 的原因?
          • 异常体系的特点就是异常类和异常对象都具备可抛性,这个可抛性是 Throwable 体系中独有的特点,只有这个体系中的类和对象才可以被 throw 和 throws 所操作。

6. 好处与原则

好处

  1. 将问题进行封装。
  2. 将正常流程的代码和问题处理的代码分离,便于阅读。

原则

  1. 处理方式:try 或 throws。
  2. 调用到抛出异常的功能时,需要对所有抛出的异常进行处理,一个 try 可以对应多个 catch。
  3. 多个 catch 时,父类的 catch 需放到最后面,否则编译会报错,因为执行了父类的 catch 语句后,其余的 catch 语句将无法执行到。
  4. catch 中需要定义针对性的处理方式,一般不建议简单的定义 PrintStackTrace 输出语句,也不建议不做处理。
    1. 如果捕获的异常在本功能内处理不了的话,可继续在 catch 中抛出。

      • 例如:

        Java 多态、内部类、异常、包

    2. 如果捕获的异常在本功能内处理不了的话,但并不属于本功能出现的异常,可以将异常转换后再抛出。

    3. 如果捕获的异常在本功能内可以处理的话,但需要将异常产生后和本功能相关的问题提供出去给调用者,可以将异常转换后再抛出。

      • 例如:

        Java 多态、内部类、异常、包

7. 注意事项

  1. 异常问题在内部被解决就无需声明。
  2. catch 是用于处理异常,如果没有 catch,就代表异常问题没有被处理,该异常如果是检测时异常,就必须声明。
  3. 子父类重写(覆盖)时:
    1. 子类抛出的异常必须是父类异常的子类或者子集。
    2. 父类或接口没有异常抛出时,子类重写(覆盖)出现异常时,只能 try,不能抛。
      • 例如:

        Java 多态、内部类、异常、包

四、包(package)

1. 概述

  • Java 中将 package 称为包,相当于文件夹,包里面一般存放的是类文件。由于在编写程序时,难免存在类名相同的情况,为了便于对类进行分类管理,Java 便提供了包,以包的形式存放不同的类,在不同的包中可以有相同的类名,只需在调用时连同包名一起即可。
    • 包也是一种封装的形式。

2. 作用

  1. 为了避免多个类重名的情况,出现多个相同名字的类时,可通过包将其区分,避免冲突。
  2. 对类文件进行分类管理,可以将相关的类放在同个包中。
  3. 包的出现将 Java 的源文件和类文件分离。
  4. 给类提供了多层命名空间。
    • 例如:
      • a 包中的 Demo.class 文件,如果要创建 Demo 对象,在使用时需加上 a.
      • a.Demo demo = new a.Demo();

3. 规则

  1. 包必须写在程序的第一行,先有包,才知道类文件的存放位置。
  2. 类的全称:包.类名。
  3. 编译定义了包的程序文件时,在编译时需指定包的存放目录。
    • 例如:javac -d e:\Demo\mypack 类名.java

4. 包与包之间的访问

  1. 被访问的包里类的权限必须是 public。
  2. 访问其它包中的类时,需定义类的全称:包名.类名。
  3. 包如果不在当前路径的话,需使用 classpath 设置环境变量,为 JVM 指明路径。
  4. 类中的成员权限必须是 public 或 protected。
    • 类公有后,被访问的成员也要公有才能被访问。

    • protected 是为子类提供的一种权限。

    • 同个包中,protected 只做用为覆盖。

    • 不同包中的子类可以直接访问父类中被 protected 权限修饰符所修饰的成员。

    • 四种权限修饰符:

      Java 多态、内部类、异常、包

      • 注:由于被 public 所修饰的类名必须与 java 文件名相同,因此在一个 .java 文件中,不允许出现两个或以上的公有类或接口。

5. 导入(import)

  • 一个程序文件中只有一个 package,可以有多个 import,import 导入的是包中的类,不导入包中的包。
  • import 可以简化类名,在调用其它包中的类时,需要写类的全称,就是连同包名一起书写,当类存在多层包中时,使用 import 导入后,使用该类时,可以不加包名。
    • 例如:
      • a.b.c.pack.Demo
      • 导入的格式为 import a.b.c.pack.Demo;
      • 使用该类时,可直接写 Demo
  • 注意事项:
    1. 导入不同包中有想同类时,必须写类的全称以做区分,否则会报错。
    2. 定义包名不能重复,可以使用 url 来定义,url 是唯一的。
      • 例如:package com.java.Demo
    3. 导入包时,如果包中有很多类,可以使用通配符 * 代替包中所有的类,但不建议使用通配符 *,因为这样会将不需要使用的类一同导入,会占用内存空间,编写程序时,需要使用包里的哪些类就导入哪些类。

6. jar 包

  • 类越来越多时,可以使用包类封装。包越来越多时,可以将包进行压缩,Java 中通过 jar 工具对包进行压缩,压缩后的的后缀名为 jar。
  • jar.exe 工具的常用命令:
    • 创建 jar 包
      • jar -cvf mypack.jar mypack
    • 查看 jar 包
      • jar -tvf mypack.jar
    • 解压缩
      • jar -xvf mypack.jar
    • 自定义 jar 包的清单文件
      • jar -cvfm mypack.jar mypack.mf mypack
  • 好处:
    1. 数据库、驱动、SSH框架等都是以 jar 包体现的。
    2. 将多个包压缩为一个 jar 包文件,便于项目携带。
    3. 便于使用,只要在 classpath 设置 jar 包的路径,即可执行 jar 包中的 java 程序。