一、多态
1. 概述
- 理解:多态可以理解为事物存在的多种体(表)现形态。
- 例如:
- 动物中的猫和狗。
- 猫这个对象对应的是猫类型,例如:猫 x = new 猫();
- 同时猫也是动物中的一种,也可以把猫称为动物,例如:动物 y = new 猫();
- 动物就是猫和狗这些具体事物中抽取出来的父类型,父类型的引用指向了子类对象。
- 例如:
- 体现
- 父类的引用指向了子类的对象。
- 父类的引用也可以接收自己子类的对象。
- 例如:Animal a = new Cat();
- 父类型 a 的引用指向了子类的对象。
- 例如:Animal a = new Cat();
- 前提
- 类与类之间必须有关系,要么是继承,要么是实现。
- 存在重写(覆盖),父类中有方法被子类重写(覆盖)。
2. 利与弊
利:提高了程序的可扩展性和后期的可维护性。
-
弊:只能使用父类中的引用去访问父类中的成员。
- 使用了多态,父类型的引用在使用功能时,不能直接调用子类中特有的方法。
- 例如:Animal a = new Cat();//多态的体现
- 假设子类 Cat 中有抓老鼠的特有功能,父类型 a 就不能直接调用该功能,可以理解为 Cat 类型向上转型,提升了。
- 父类的引用想要调用子类 Cat 中特有的方法,就要将父类的引用强制转换成子类的类型,向下转型。例如:Cat c = (Cat)a;
- 注:
- 如果父类可以创建对象的话,例如:Animal a = new Animal(); 就不能进行向下转型了,Cat c = (Cat)a; 这样编写代码在编译时会报错,不要出现将父类对象转换成子类类型。
- 多态转换的是父类引用指向了自己子类的对象时,该引用可以被提升,也可以被强制转换,多态始终都是子类对象在做变化的。
- 例如:Animal a = new Cat();//多态的体现
- 使用了多态,父类型的引用在使用功能时,不能直接调用子类中特有的方法。
-
例如:
3. 特点
1. 成员变量
- 无论编译还是运行时期,都参考左边(引用变量)所属的类。
- 例如:多态中父类引用调用成员变量时,如果父类和子类有同名的成员变量,被调用的则是父类中的成员变量。
2. 非静态成员函数
- 编译时期:参考引用型变量所属的类中是否有调用的方法,如果有,编译通过,如果没有,编译失败。
- 例如:a.catchMouse();//编译报错,这种情况只能通过强制转换,向下转型后才可以使用子类中特有的功能。
- 运行时期:参考对象所属的类中是否有调用的方法,如果父类中有一个非抽象方法,子类继承后将其重写(覆盖)了,多态运行时,父类的引用调用同名函数时,运行的是父类中的方法。
- 总而言之:成员函数在多态调用时,编译看左边,运行看右边。
3. 静态成员函数
- 无论编译还是运行时期,都参考左边,就是父类的引用在调用同名静态成员函数时,调用的是父类中的静态成员函数。
- 原因:
当类一被加载时,静态成员函数随类绑定在内存中,不需要创建对象,可以通过类名直接调用静态成员函数,并且父类中的静态成员函数一般是不被重写(覆盖)的。
-
类在方法区中的分配:静态方法区和非静态方法区(关键字 super 和 this 在非静态方法区中)。
- 原因:
4. 应用
- 定义工具类,将共同行为封装在同一个类中。
- 对类型进行抽取(多态的产生)。
- 操作同一个父类型,对其中的子类型均可进行操作。
-
例如:
-
二、内部类
1. 概述
- 概述:将一个类定义在另一个类里面,定义在类里面的那个类称为内部类(内置类、嵌套类)。
- 理解:当描述事物时,事物的内部还存在事物,事物内的事物使用内部类来描述,印务内部事物在使用外部事物的内容。
- 例如:定义一个人的类,手、脚、心脏等等都属于人,这些又各自有自己的功能描述,这时候就可以在人这个描述类中,定义一些描述手、脚、心脏的类,也就是定义一些内部类。
- 编译现象:编译 Java 文件时,如果类中有内部类,生成的 class 文件中会含有 Test$1.class 等等这样的文件,编译器会把内部类翻译为用 $(美元符号)分隔外部类名和内部类名的类文件,这是内部类的一种编译现象。
2. 访问规则
- 外部类要访问内部类,必须建立内部类的对象。
- 内部类可以直接访问外部类中的成员(包括私有成员)。
- 原因:因为内部类中持有外部类的引用。
- 格式:外部类名.this
3. 访问格式
- 内部类定义在外部类的成员位置,而且非私有时,可以在外部的其它类中直接建立内部类对象。
- 格式:外部类名.内部类名 变量名 = 外部类对象.内部类对象;
- 例如:Outer.Inner in = new Outer().new Inner();
- 内部类定义在外部类的成员位置时,可以被成员修饰符所修饰。
- 例如:
- private:内部类在外部类中进行封装。
- static:内部类有局部 statci 的特性,内部类被 static 修饰后,只能访问外部类中的 static 成员。
- 外部的其它类访问 static 内部类中静态成员的格式:外部类名.内部类名.方法名();
- 例如:Outer.Inner.function();
- 外部的其它类访问 static 内部类中非静态成员的格式:new 外部类名.内部类名().方法名();
- 例如:new Outer.Inner().function();
- 外部的其它类访问 static 内部类中静态成员的格式:外部类名.内部类名.方法名();
- 例如:
- 注:
- 内部类中定义了静态成员时,该内部类必须是 static 的。
- 外部类中的静态方法访问内部类时,内部类也必须是 static 的。
- 实际开发中,内部类一般定义为 private,很少定义为 public 的。
- 格式:外部类名.内部类名 变量名 = 外部类对象.内部类对象;
- 内部类定义在局部位置
- 不可被成员修饰符所修饰,作用域限定在声明这个局部内部类的代码块中。
- 例如:public、private、static 等修饰符。
- 可以直接访问外部类中的成员,还持有外部类的引用。
注:内部类不可以访问所在局部中的非最终变量,只能访问被 final 修饰的局部变量。
-
例如:
- 分析:上述代码中的打印结果为 7、8,会改变,并没有因为被 final 所修饰就无法改变,原因是因为类调用方法结束后,被 final 修饰的变量已经从桟内存中消失,类再次调用方法时,已经是另一个变量了,所以可以重新传值。
- 不可被成员修饰符所修饰,作用域限定在声明这个局部内部类的代码块中。
4. 匿名内部类
匿名内部类就是内部类的简写格式,匿名内部类其实就是一个匿名子类对象,可以理解为带内容的对象。
格式:new 父类或接口(){定义子类的内容}
-
定义匿名内部类的前提:
- 匿名内部类必须是继承一个类或者实现接口。
- 特殊情况:所有的类都有一个父类 Object,定义时也可用 Object。
- 匿名内部类必须是继承一个类或者实现接口。
-
利与弊:
- 利:简化书写。
- 弊:
- 不能做强转的动作。
- 不能直接调用自己的特有方法。
- 如果继承的父类或接口中方法很多时,使用匿名内部类的可阅读性会非常差,调用也非常麻烦,因此匿名内部类中定义的方法一般不超过3个。
-
例如:
三、异常
1. 概述
- 异常是 Java 中的重要机制,使用面向对象的思想进行了封装,通常使用的是异常类。异常类中描述的就是程序中可能出现的错误或者问题。
- 程序在运行时出现不正常的情况称为异常。
- 由来:异常问题也是现实生活中一个具体的事物,可以通过 Java 中类的形式进行描述,并封装成对象,异常类就是 Java 对不正常的情况进行描述后的对象体现。
- 程序中可能出现的错误或者问题:
- 用户输入错误导致的异常:非法参数、不按正常的使用程序等。
- 设备硬件等发生的错误的异常:硬盘损坏等。
- 物理限制的异常:存储空间不足、SDCard不存在等。
- 代码错误的异常:程序编写的方法不正确、返回错误的参数等。
2. 体系
异常问题的划分方式
- 严重的异常问题。
- 严重的异常问题,Java 通过 Error 类进行描述,对于 Error 类一般不编写针对性的代码进行处理。
- 非严重的异常问题。
-
非严重的异常问题,Java 通过 Exception 类进行描述,对于 Exception 类可以使用针对性的代码进行处理。
注:无论是 Error 或 Exception 都具有一些共性内容,例如:不正常情况的相关信息、引发异常的原因等。
-
体系
- Throwable
- Error//出现重大异常问题。例如:运行的类不存在或者内存溢出等。
- Exception//运行时出现的异常情况。
- RuntimeException//特殊的异常类,Exception 的子类,抛出时不需要做声明。
- 注:Error 和 Exception 的子类名称都是以父类名做为后缀。
特点
- 异常体系中所有类以及建立的对象都具备可抛性;
- 可以被 throw 和 throws 关键字所修饰操作;
- 只有异常体系才具备这个特点。
3. 分类
编译时异常
- 异常在编译时,如果没有做任何处理(没有抛也没有 try),编译失败。
- 异常被标识,代表可以被处理。
运行时异常
- 编译时不需要处理,编译器不检查。
- 异常发生时,Java 建议不处理,让程序停止。
- 需要对代码进行修正。(例如:RuntimeException 以及 RuntimeException 的子类)
4. 处理
-
Java 中提供了特有的语句进行处理:
try{ 需要检测的语句; }catch(异常类 变量){ 处理异常的语句(方式); }finally{ 一定要执行的语句; }
-
这些特有语句的处理方式有三种结合情况:
1. try{ 需要检测的语句; }catch(异常类 变量){ 处理异常的语句(方式); } 2. try{ 需要检测的语句; }finally{ 一定要执行的语句; } 3. try{ 需要检测的语句; }catch(异常类 变量){ 处理异常的语句(方式); }finally{ 一定要执行的语句; }
-
注:
- finally 中定义的一般是关闭资源的代码,资源使用完后必须释放。
- 如果存在一些功能是必须执行的,可以使用 try{}finally{} 的方式,将必须执行的语句放在 finally 代码块中。
- 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
- 让自定义异常类具备可抛性。
- 让自定义异常类具备操作异常的共性方法。
- 如何自定义异常信息?
自定义异常信息可以使用父类已定义好的功能,异常信息传递给父类的构造函数,由于父类中已经把异常信息的操作都完成了,子类只需在构造函数中,通过 super 语句将异常信息传递给父类,就可以通过 getMessage() 方法获取自定义的异常信息了。
-
例如:
- 说明:定义异常时,如果异常的发生,会导致无法继续进行运算,则让自定义异常类继承 RuntimeException。
-
注意:自定义异常必须是自定义异常类有继承关系,一般继承 Exception。
- 自定义异常类继承 Exception 的原因?
- 异常体系的特点就是异常类和异常对象都具备可抛性,这个可抛性是 Throwable 体系中独有的特点,只有这个体系中的类和对象才可以被 throw 和 throws 所操作。
- 自定义异常类继承 Exception 的原因?
- 自定义异常类继承 Exception 或 RuntimeException
6. 好处与原则
好处
- 将问题进行封装。
- 将正常流程的代码和问题处理的代码分离,便于阅读。
原则
- 处理方式:try 或 throws。
- 调用到抛出异常的功能时,需要对所有抛出的异常进行处理,一个 try 可以对应多个 catch。
- 多个 catch 时,父类的 catch 需放到最后面,否则编译会报错,因为执行了父类的 catch 语句后,其余的 catch 语句将无法执行到。
- catch 中需要定义针对性的处理方式,一般不建议简单的定义 PrintStackTrace 输出语句,也不建议不做处理。
-
如果捕获的异常在本功能内处理不了的话,可继续在 catch 中抛出。
-
例如:
-
如果捕获的异常在本功能内处理不了的话,但并不属于本功能出现的异常,可以将异常转换后再抛出。
-
如果捕获的异常在本功能内可以处理的话,但需要将异常产生后和本功能相关的问题提供出去给调用者,可以将异常转换后再抛出。
-
例如:
-
-
7. 注意事项
- 异常问题在内部被解决就无需声明。
- catch 是用于处理异常,如果没有 catch,就代表异常问题没有被处理,该异常如果是检测时异常,就必须声明。
- 子父类重写(覆盖)时:
- 子类抛出的异常必须是父类异常的子类或者子集。
- 父类或接口没有异常抛出时,子类重写(覆盖)出现异常时,只能 try,不能抛。
-
例如:
-
四、包(package)
1. 概述
- Java 中将 package 称为包,相当于文件夹,包里面一般存放的是类文件。由于在编写程序时,难免存在类名相同的情况,为了便于对类进行分类管理,Java 便提供了包,以包的形式存放不同的类,在不同的包中可以有相同的类名,只需在调用时连同包名一起即可。
- 包也是一种封装的形式。
2. 作用
- 为了避免多个类重名的情况,出现多个相同名字的类时,可通过包将其区分,避免冲突。
- 对类文件进行分类管理,可以将相关的类放在同个包中。
- 包的出现将 Java 的源文件和类文件分离。
- 给类提供了多层命名空间。
- 例如:
- a 包中的 Demo.class 文件,如果要创建 Demo 对象,在使用时需加上 a.
- a.Demo demo = new a.Demo();
- 例如:
3. 规则
- 包必须写在程序的第一行,先有包,才知道类文件的存放位置。
- 类的全称:包.类名。
- 编译定义了包的程序文件时,在编译时需指定包的存放目录。
- 例如:javac -d e:\Demo\mypack 类名.java
4. 包与包之间的访问
- 被访问的包里类的权限必须是 public。
- 访问其它包中的类时,需定义类的全称:包名.类名。
- 包如果不在当前路径的话,需使用 classpath 设置环境变量,为 JVM 指明路径。
- 类中的成员权限必须是 public 或 protected。
类公有后,被访问的成员也要公有才能被访问。
protected 是为子类提供的一种权限。
同个包中,protected 只做用为覆盖。
不同包中的子类可以直接访问父类中被 protected 权限修饰符所修饰的成员。
-
四种权限修饰符:
- 注:由于被 public 所修饰的类名必须与 java 文件名相同,因此在一个 .java 文件中,不允许出现两个或以上的公有类或接口。
5. 导入(import)
- 一个程序文件中只有一个 package,可以有多个 import,import 导入的是包中的类,不导入包中的包。
- import 可以简化类名,在调用其它包中的类时,需要写类的全称,就是连同包名一起书写,当类存在多层包中时,使用 import 导入后,使用该类时,可以不加包名。
- 例如:
- a.b.c.pack.Demo
- 导入的格式为 import a.b.c.pack.Demo;
- 使用该类时,可直接写 Demo
- 例如:
- 注意事项:
- 导入不同包中有想同类时,必须写类的全称以做区分,否则会报错。
- 定义包名不能重复,可以使用 url 来定义,url 是唯一的。
- 例如:package com.java.Demo
- 导入包时,如果包中有很多类,可以使用通配符 * 代替包中所有的类,但不建议使用通配符 *,因为这样会将不需要使用的类一同导入,会占用内存空间,编写程序时,需要使用包里的哪些类就导入哪些类。
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
- 创建 jar 包
- 好处:
- 数据库、驱动、SSH框架等都是以 jar 包体现的。
- 将多个包压缩为一个 jar 包文件,便于项目携带。
- 便于使用,只要在 classpath 设置 jar 包的路径,即可执行 jar 包中的 java 程序。