一、面向对象——多态
1、多态概述
①什么是多态?某一事物在不同时刻表现出来的不同状态。
如:猫可以是猫的类型。猫 m = new猫();同时猫也是动物的一种,也可以把猫称为动物。
②多态的好处:提高了代码的可扩展性,前期定义的代码可以使用后期的内容。
③多态的弊端:前期定义的内容不能使用后期子类的特有功能。
④多态的前提:
- 必须要有关系:继承,实现。
- 有覆盖。
- 有父类引用指向子类对象。
2、多态时成员的特点
①成员变量:编译时,参考引用型变量所属的类中是否有调用的成员变量,有则编译通过,没有则编译失败。运行时,参考引用型变量所属的类是否有调用的成员变量,并运行该所属类中的成员变量。简单的说,编译和运行都参考等号的左边。
②成员函数(非静态):编译时,参考引用型变量所属的类中是否有调用的函数,有则编译通过,没有则编译失败。运行时,参考的是对象所属的类是否有调用的函数,简单的说,编译看左边,运行看右边。
③静态函数:简单说,编译和运行都看做左边。其实对于静态方法,是不需要对象的。直接用类名调用即可。
3、多态中的转型问题
①向上转型:从子到父,父类引用指向子类对象。
②向下转型:从父到子,父类引用转为子类对象。
在多台向下转型中容易出现的问题,代码演示如下:
/*
ClassCastException:类型转换异常
一般在多态的向下转型中容易出现
*/
class Animal {
public void eat(){}
}
class Dog extends Animal {
public void eat() {}
public void lookDoor() {
}
}
class Cat extends Animal {
public void eat() {
}
public void playGame() {
}
}
class DuoTaiDemo5 {
public static void main(String[] args) {
//内存中的是狗
Animal a = new Dog();
Dog d = (Dog)a;
//内存中是猫
a = new Cat();
Cat c = (Cat)a;
//内存中是猫
Dog dd = (Dog)a; //ClassCastException
}
}
二、面向对象——内部类
1、内部类(inner class)概述
①概念:内部类是指定义在另一个类中的类。当描述事物时,事物的内部还有事物,该事物就用内部类来描述。因为内部事物在使用外部事物的内容。
②内部类访问的特点:
- 内部类可以直接访问外部类中的成员。
- 外部类需要访问内部类,必须建立内部类的对象
- 如果内部类中有静态成员,那么外部类也必须是静态的。
2、内部类的分类
按照内部类在类中定义的位置的不同,可以分为如下两种格式:
- 成员位置——成员内部类
- 局部位置——局部内部类
①成员内部类
a、外界如何创建对象?外部类名.内部类名 对象名 = 外部类对象.内部类对象;
b、成员内部类常见的修饰符
- private 为了保证数据的安全性
- static 为了让数据访问更方便。被静态修饰的成员内部类只能访问外部的静态成员。
内部类被静态修饰后的方法有静态和非静态之分,它们的访问和不用静态不是一样的。
访问非静态方法:外部类名.内部类名对象名 = new 外部类名.内部类名();
访问静态方法:上面创建的对象访问,或者外部类名.内部类名.方法名();
成员内部类面试题:
class Outer {
public int num = 10;
class Inner {
public int num = 20;
public void show() {
int num = 30;
System.out.println(num);//30
System.out.println(this.num);//20
System.out.println(Outer.this.num);//10
}
}
}
class OuterDemo {
public static void main(String[] args) {
Outer.Inner oi = new Outer().new Inner();
oi.show();
}
}
②局部内部类
a、局部内部类可以直接访问外部类的成员。
b、可以创建内部类对象,通过对象调用内部类方法,来使用局部内部类功能。
c、局部内部类访问局部变量的注意事项:
- 必须被final修饰。为什么?因为局部变量会随着方法的调用完毕而消失,这个时候,局部对象并没有立马从堆内存中消失,还要使用那个变量。为了让数据还能继续被使用,就用fianl修饰,这样,在堆内存里面存储的其实是一个常量值。
如下面的代码所示:
class Outer
{
int x = 3;
void method(final int a)
{
final int y = 4;
//局部内部类
class Inner
{
void function()
{
System.out.println(y);
}
}
new Inner().function();//使用局部内部类中的方法。
}
}
class InnerClassDemo
{
public static void main(String[] args)
{
Outer out = new Outer();
out.method(7);//打印7
out.method(8);//打印8
}
}
③匿名内部类
a、什么是匿名内部类?匿名内部类其实就是内部类的简写格式。
b、匿名内部类的前提:存在一个类或接口。
c、匿名内部类的格式:new 类名或者接口名(){重写方法}。
d、匿名内部类的本质:是一个继承了类或者实现了接口的子类匿名对象。
e、匿名内部类的通常使用场景之一:当函数参数是接口类型时,而且接口中的方法不超过三个,可以使用匿名内部类作为实际参数进行传递。
匿名内部类在开发中的使用代码实例:
/*
匿名内部类在开发中的使用
*/
interface Person {
public abstract void study();
}
class PersonDemo {
//接口名作为形式参数
//其实这里需要的不是接口,而是该接口的实现类的对象
public void method(Person p) {
p.study();
}
}
//实现类
class Student implements Person {
public void study() {
System.out.println("好好学习,天天向上");
}
}
class InnerClassTest2 {
public static void main(String[] args) {
//测试
PersonDemo pd = new PersonDemo();
Person p = new Student();
pd.method(p);
System.out.println("--------------------");
//匿名内部类在开发中的使用
//匿名内部类的本质是继承类或者实现了接口的子类匿名对象
pd.method(new Person(){
public void study() {
System.out.println("好好学习,天天向上");
}
});
}
}
匿名内部类面试题:按照要求补齐代码 。
interface Inter { void show(); }
class Outer { //补齐代码 }
class OuterDemo {
public static void main(String[] args) {
Outer.method().show();
}
}
要求在控制台输出”HelloWorld”
题目分析:Outer.method.show();相当于Inter.in = Outer.method(); in.show();
Outer.method():Outer类中有个静态的方法method。
.show():method这个方法运算后的结果是一个对象。而且是一个Inter类型的对象。因为只有Inter类型的对象,才能调用show方法。
所以,补齐后的代码为:
/*
匿名内部类面试题:
按照要求,补齐代码
interface Inter { void show(); }
class Outer { //补齐代码 }
class OuterDemo {
public static void main(String[] args) {
Outer.method().show();
}
}
要求在控制台输出”HelloWorld”
*/
interface Inter {
void show();
//public abstract
}
class Outer {
//补齐代码
public static Inter method() {
//子类对象 -- 子类匿名对象
return new Inter() {
public void show() {
System.out.println("HelloWorld");
}
};
}
}
class OuterDemo {
public static void main(String[] args) {
Outer.method().show();
/*
1:Outer.method()可以看出method()应该是Outer中的一个静态方法。
2:Outer.method().show()可以看出method()方法的返回值是一个对象。
又由于接口Inter中有一个show()方法,所以我认为method()方法的返回值类型是一个接口。
*/
}
}
三、面向对象——异常
1、异常的概述
①什么是异常?
a、异常是指在运行时期发生的不正常情况。在java中用类的形式对不正常情况进行了描述和封装,描述不正常情况的类,就是异常类。
b、在以往的正常情况,流程代码和问题处理代码相结合。现在将正常流程代码和问题处理代码相分离,提高阅读性。
c、其实异常就是java通过面向对象的思想将问题封装成了对象,用异常类对其进行描述,不同的问题用不同的类进行具体描述。
②异常的由来
在理想状况下,用户输入的数据永远是正确的,打开的文件一定存在,并且永远不会出现bug。但是,在现实中我们往往会遇到许多的问题。如果一个用户在运行程序期间,由于程序的错误或一些外部环境的影响造成数据的丢失,用户就有可能不再使用这个程序了,为了避免这类事情的发生,至少应该做到以下几点:
- 向用户通报错误。
- 保存所有的操作结果。
- 允许用户以适当的形式退出程序。
2、异常体系
对于程序出现的问题,可能会有很多种,这就意味着描述的类也很多,将其共性进行向上抽取,就形成了异常体系。
异常体系(Throwable):无论是error还是异常,都应该抛出,让调用者知道并处理。该体系的优点就在于Throwable及其所有的子类都具有可抛性。可抛性是通过两个关键字来体现的,throw和throws,凡是可以被这两个关键字操作的类和对象都具有可抛性。
最终就将问题分成了两大类:
①error:一般不可处理。java运行时系统的内部错误和资源耗尽错误。是由jvn抛出的严重性问题,这种问题一般不针对性处理,直接修改程序。
②Exception:可处理的。Exception体系。
a、该体系的特点:子类的后缀都是用其父类名作为后缀,阅读性很强。
b、在java中没有定义的异常就按照java异常的创建思想,面向对象,进行自定义描述,并封装成对象。
注意:如果一个类称为异常类,则必须要继承异常体系,因为只要继承异常体系的子类才具有可抛性。才可以被两个关键字操作。
异常体系如下图所示:
3、异常的分类
①异常通常分为两种,一种是编译时被检测异常,一种是编译时不被检测异常(运行时异常)。
- 编译时被检测异常:只要是Exception及其子类都是,除了RuntimeException体系。这种问题一旦出现,希望在编译时就进行检测,让这种问题有对应的处理。
- 编译时不检测异常(运行时异常):就是Exception中的RuntimeException及其子类。这种问题的发生,无法让功能继续,运算无法进行,更多的是因为调用者的原因导致的,或者是引发了内部状态的改变而导致的,那么这种问题一般不处理,直接编译通过,在运行时,让调用者调用时的程序强行停止,让调用者修改程序代码。“如果出现RuntimeException,那么就一定是你的问题”。
4、异常的处理
①异常处理机制
在java应用程序中,异常处理机制为:抛出异常,捕获异常。
- 抛出异常:当一个方法出现错误引发异常时,方法创建异常对象并交付运行时系统,异常对象中包含了异常类型和异常出现时的程序状态等异常信息。运行时系统负责寻找处置异常的代码并执行。
- 捕获异常:在方法抛出异常之后,运行时系统将转为寻找合适的异常处理器(exception handler)。潜在的异常处理器是异常发生时依次存留在调用栈中的方法的集合。当异常处理器所能处理的异常类型与方法抛出的异常类型相符时,即为合适 的异常处理器。运行时系统从发生异常的方法开始,依次回查调用栈中的方法,直至找到含有合适异常处理器的方法并执行。当运行时系统遍历调用栈而未找到合适 的异常处理器,则运行时系统终止。同时,意味着Java程序的终止。
②throws和throw的区别
- throws使用在函数上,throw使用在函数内。
- throws抛出的是异常类,可以抛出多个,用逗号隔开;throw抛出的是异常对象。
③在java中,提供了特有的处理异常的方式,格式如下:
try
{
需要被检测的代码;
}
catch(异常类 变量)
{
处理异常的代码;(处理方式)
}
finally
{
一定会执行的语句;
}
除此之外还有两种格式,一种是没有finally代码块,一种是没有catch代码块。
注意:在finally中定义的通常是关闭资源代码,因为资源必须释放。finally只有一种情况不会执行:当系统执行到System.exit的时候。
④自定义异常
定义类继承Exception或者RuntimeException。目的有两个:一是为了让该自定义类具有可抛性,二是为了让该类具备操作异常的共性方法。
当要定义自定义异常信息时,可以使用父类已经定义好的功能。异常信息传递给父类的构造函数。代码实例如下:
class MyException extends Exception{
MyException(String message){
super(message);
}
}
自定义异常:按照java面向对象的思想,将程序中出现的特殊问题进行封装。
⑤异常处理原则:
- 函数内容如果要抛出需要检测的异常,那么函数上必须要有声明,否则必须在函数内用try catch进行捕捉,否则编译失败。
- 如果调用到了声明异常的函数,要么try catch,要么throws,否则编译失败。
- 什么时候try,什么时候throws呢?功能内容可以解决就用try…catch;解决不了,用throws告诉调用者,由调用者解决。
- 一个功能如果抛出了多个异常,那么调用时,必须有对应的多个catch进行针对性的处理,内部有几个需要检测的异常,就抛几个,抛出几个,就catch几个。
⑥异常处理注意事项
a、子类在覆盖父类方法时,父类如果抛出了异常,那么子类的方法只能抛出父类的异常或者该异常的子类。
b、如果父类抛出了多个异常,呢吗子类只能抛出父类异常的子集。简单的说,子类覆盖父类的异常或者子类或子集。
注意:如果父类的方法没有抛出异常,那么子类的方法绝对不能抛出异常。只能进行捕获处理。
⑦异常实际应用中的应用和总结
a、在处理运行时异常时,采用逻辑去合理规避同时辅助try—catch处理。
b、在多重catch块后面。可以加上一个catch(Exception)来处理可能被一楼的异常。
c、对于不确定的代码,也可以加上try—catch处理潜在的异常。
d、尽量去处理异常,切忌只是简单的调用printstack.Trance()去打印输出。
e、具体如何处理异常,要根据不同的业务需求和异常类型来决定。
f、尽量添加finally代码块去释放占用的资源。
⑧异常综合练习代码演示:
/*
毕老师用电脑上课。
开始思考上课中出现的问题。
比如问题是
电脑蓝屏。
电脑冒烟。
要对问题进行描述,封装成对象。
可是当冒烟发生后,出现讲课进度无法继续。
出现了讲师的问题:课时计划无法完成。
*/
//自定义电脑蓝屏异常
class LanPingException extends Exception{
LanPingException(String message){
super(message);
}
}
//自定义电脑冒烟异常
class MaoYanException extends Exception{
MaoYanException(String message){
super(message);
}
}
//出现了问题,无法上课,课时计划无法完成异常
class NoPlanException extends Exception{
NoPlanException(String msg){
super(msg);
}
}
class Computer{
private int state = 3;
//电脑启动
public void run()throws LanPingException,MaoYanException{
if(state==2)
throw new LanPingException("蓝屏了");
if(state==3)
throw new MaoYanException("冒烟了");
System.out.println("电脑运行");
}
//电脑重启
public void reset(){
state = 1;
System.out.println("电脑重启");
}
}
class Teacher{
private String name;
private Computer cmpt;
//对老师进行初始化
Teacher(String name){
this.name = name;
cmpt = new Computer();
}
//老师讲课
public void prelect()throws NoPlanException{
try{
cmpt.run();
}
catch (LanPingException e){
cmpt.reset();
}
catch (MaoYanException e){
test();
throw new NoPlanException("课时无法继续"+e.getMessage());
}
System.out.println("讲课");
}
public void test(){
System.out.println("练习");
}
}
class ExceptionTest {
public static void main(String[] args) {
Teacher t = new Teacher("毕老师");
try{
t.prelect();
}
catch (NoPlanException e){
System.out.println(e.toString());
System.out.println("换老师或者放假");
}
}
}
四、面向对象——包(package)
1、什么是包?
包其实就是文件夹。在包中,我们通常存放的是类文件,因为我们在编写程序时,难免会出现类名相同的情况。为了方便于对类进行管理,java中就有了包的出现,在不同的包中可以有相同的类名,调用的时候连同包名一起调用即可。包也是一种封装形式。
报名的书写规则:
- 包名必须写在程序的第一行。
- 类文件的全称:包名.类名。
2、包的作用
①对子类文件进行分类管理。将一些相关的类放在同一个包中。
②给类提供多层命名(名称)空间。
③避免多个类重命名,对于相同的类名,可通过包名将两者区分。
④包的出现可以将java 的类文件与源文件相分离。
3、包与包之间的访问
①要访问其它包中的类,需要定义类的全称。包名.类名。
②包如果不在当前路径,需要使用classpath设定环境变量,为JVM指明路径。
③被访问的包中的类权限必须是public的。被访问的包中的类的方法也必须是public的。
4、四种权限修饰词
| public | protected | default | private |
同一类中 | ok | ok | ok | ok |
同一包中 | ok | ok | ok | |
子类中 | ok | ok | | |
不同包中 | ok | | | |
5、import(导包)
①导包的原则:用到哪个类,就导哪个类,不要多导。(尽管多导也能运行)
②导包的作用:
- 可以简化书写。
- 一个程序文件中只有一个package,但可以有多个import。
③导包的注意事项:
- 在导包时,用到哪个写哪个,尽量不要import pack.*;一般是import pack.Demo;可以节省内存空间,也能提高点效率。
- 定义包名不要重复。一般我们定义包名是用url的倒写形式。如:package cn.itheima.Demo
- 定义包名时,包名都是小写字母。