一、多态
多态(Polymorphism)按字面的意思就是“多种状态”。在面向对象语言中,接口的多种不同的实现方式即为多态。引用Charlie Calverts对多态的描述——多态性是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的对象赋值给父类类型的引用。
猫这个对象对应的类型是猫类型,用伪代码可以表示为:猫x=new猫();同时猫也是动物中的一种,也可以把猫称为动物:动物y=new猫();动物是猫具体事物中抽取出来的父类型父类型引用指向了子类对象。
多态体现在:父类或者接口的引用指向或者接收自己的子类对象。
多态的存在提高了程序的扩展性和后期可维护性。
多态实现的前提:1、需要存在继承或者实现关系。2、需要有覆盖操作。
多态提高了代码的扩展性,前期定义的代码可以使用后期的内容。但是不能调用子类对象特有的内容。
多态的设计类型的向上转换和向下转换,下面以一段代码来说明转换的过程和需要注意的地方:
1 class DuoTaiDemo 2 { 3 public static void main(String[] args) 4 { 5 // 自动类型提升,猫对象提升到了动物类型。 6 Animal animal = new Cat();//猫对象向上转型,将子类型隐藏,animal就不能使用子类cat的特有内容了 7 8 //如果还想用具体动物猫的特有功能。你可以将该对象进行向下转型。 9 Cat cat=(Cat)animal;//向下转型之后就能使用子类的特有内容了。 10 11 // 注意:对于转型,自始至终都是子类对象在做类型的变化。 12 // 但是类型不能随意转换,否则可能会抛出 ClassCastException 的异常 13 //就像我们不能把狗对象强制转换为猫 14 Dog dog=(Dog)animal;//animal指向的是猫对象,如果强制转换为狗对象就会抛出类型不匹配的异常。 15 } 16 }
在涉及到类型转换的时候我们需要用到instanceof,instanceof:用于判断对象的具体类型,只能用于引用数据类型判断,通常在向下转型前用于健壮性的判断。防止出现类型不匹配的异常。
我们将上面的例子的代码,用instanceof来改进一下:
1 class DuoTaiDemo 2 { 3 public static void main(String[] args) 4 { 5 Animal animal = new Cat(); 6 7 if(animal instanceof Cat) 8 { 9 Cat cat=(Cat)animal; 10 System.out.println("这是一只猫"); 11 } 12 else if(animal instanceof Dog) 13 { 14 Dog dog=(Dog)animal; 15 System.out.println("这是一只狗"); 16 } 17 else 18 { 19 System.out.println("暂时还不认识这只动物!"); 20 } 21 } 22 }
最后讲一下多态是各个成员的特点:
1、成员变量:
编译时:参考引用型变量所属的类中是否有调用的成员变量。有,编译通过,没有,编译失败。
运行时:参考引用型变量所属的类中是否有调用的成员变量,并运行该所属类中的成员变量。
简单说:编译和运行都参考等号的左边。
2.成员函数(非静态)
编译时:参考引用型变量所属的类中是否有调用的函数。有,编译通过。没有,编译失败。
运行时:参考的是对象所属的类中是否有调用的函数。
简单说:编译看左边,运行看右边。
3.静态函数
编译时:参考的是对象所属的类中是否有调用的函数
运行时:参考的是对象所属的类中是否有调用的函数。
简单说:编译和运行看左边。
二、内部类
将一个类定义在另一个类的里面,里面那个类就称为内部类(内置类,嵌套类)。内部类可以直接访问外部类中的成员,包括私有成员。而外部类要访问内部类中的成员必须要建立内部类的对象。
分析事物时,发现该事物描述中还有事物,而且这个事物还在访问被描述事物的内容,这时候就需要定义内部类。
1 class InnerClassDemo 2 { 3 public static void main(String[] args) 4 { 5 //直接方位外部类中内部类的成员 6 Outer.Inner in = new Outer().new Inner(); 7 in.show(); 8 } 9 } 10 class Outer 11 { 12 private int num=3; 13 class Inner 14 { 15 void show() 16 { 17 System.out.println("inner show: "+num); 18 } 19 } 20 }
上面的代码中写除了直接访问外部类中内部类的成员的方式: new Outer.new Inner();
运行的结果如下:
内部类定义在成员位置上,可以被private、static成员修饰符修饰。被static修饰的内部类只能访问外部类中的静态成员。那么如果内部类是静态的,就与外部类没有什么区别。如果内部类是静态的,内部类成员也是静态的,可以不用创建内部类对象,直接调用。如果内部类中定义了静态成员,该内部类也必须是静态的!
为什么内部类能直接访问外部类中的成员呢?
那是因为内部类持有了外部类的引用,外部类名.this。
内部类定义在局部位置上,也可以直接访问外部类中的成员。同时可以访问所在局部中的局部变量,但必须是被final修饰的。
匿名内部类
匿名内部类就是内部类的简化写法。
前提:内部类可以继承或实现一个外部类或者接口。
格式:new外部类名或者接口名(){覆盖类或者接口中的代码,(也可以自定义内容。)}
使用的时机:通常使用方法是接口类型参数,并且该接口中的方法不超过三个,可以将匿名内部类作为参数传递。
作用:简化书写,增强阅读性。
用一个简单的例子来说明一下匿名内部类的使用:
1 class InnerClassDemo 2 { 3 public static void main(String[] args) 4 { 5 show(//调用show方法 6 new Inter()//传递一个内部类的参数 7 { 8 public void show()//实现Inter接口的方法 9 { 10 System.out.println("inner show... "); 11 } 12 } 13 ); 14 } 15 //参数是Inter接口类型的静态方法,在主函数中调用,接受实现了Inter接口的对象 16 static void show(Inter in) 17 { 18 in.show(); 19 } 20 } 21 22 interface Inter//inter接口用来实现内部类 23 { 24 public abstract void show(); 25 }
结果为:
三、异常
异常是在程序在编译、运行时期发生的不正常情况。在java中用类的形式对不正常情况进行了描述和封装对象。描述不正常的情况的类,就称为异常类。不同的问题用不同的类进行具体的描述。比如角标越界、空指针异常等等。问题很多,意味着描述的类也很多,将其共性进行向上抽取,形成了异常体系。
不正常情况分为两类:1.一般不可处理的:Error,这种问题发生,一般不针对性处理,直接修改程序;2.可以处理的:Exception
异常可以抛出,无论是error,还是异常、问题,问题发生就应该可以抛出,让调用者知道并处理。该体系的特点就在于Throwable及其所有的子类都具有可抛性。可抛性其实是通过两个关键字来体现的:throwsthrow,凡是可以被这两个关键字所操作的类和对象都具备可抛。
Throwable中的方法:
1.getMessage():获取异常信息,返回字符串。
2.toString():获取异常类名和异常信息,返回字符串。
3.printStackTrace():获取异常类名和异常信息,以及异常出现在程序中的位置,返回值void。
4.printStackTrace(PrintStream s):通常用该方法将异常内容保存在日志文件中,以便查阅。
1 class MyException //常见的异常演示 2 { 3 public static void main(String[] args) 4 { 5 int[] arr=new int[3]; 6 int[] arrNull=null; 7 int index=4; 8 if(arrNull==null) 9 throw new NullPointerException("数组的引用为空!"); 10 if(index>=arr.length) 11 throw new ArrayIndexOutOfBoundsException("数组脚标越界!"); 12 } 13 }
自定义异常
可以自定义出的问题称为自定义异常。对于角标为负数的情况,可以用负数角标异常来表示,负数角标这种异常在java中并没有定义过。那就按照java异常的创建思想,面向对象,将负数角标进行自定义描述,并封装成对象。这种自定义的问题描述称为自定义异常。
如果让一个类成为异常类,必须要继承异常体系,因为只有成为异常体系的子类才有资格具备可抛性,才可以被两个关键字所操作:throws、throw。自定义类继承Exception或者其子类,通过构造函数定义异常信息。
throws和throw的区别:
1.throws用于标识函数暴露出的异常类,并且可以抛出多个,用逗号分隔。throw用于抛出异常对象。
2.thorws用在函数上,后面跟异常类名。throw用在函数内,后面跟异常对象。
定义功能方法时,需要把出现的问题暴露出来让调用者去处理,那么就通过throws在函数上标识。在功能方法内部出现某种情况,程序不能继续运行,需要进行跳转时,就用throw把异常对象抛出。
异常的分类:
1.编译时被检测异常:只要是Exception和其子类都是,除了特殊子类RuntimeException体系。
这种问题一旦出现,希望在编译时就进行检测,让这种问题有对应的处理方式。这样的问题都可以针对性的处理。
2.编译时不检测异常(运行时异常):就是Exception中的RuntimeException和其子类。
这种问题的发生,无法让功能继续,运算无法运行,更多是因为调用的原因导致的或者引发了内部状态的改变导致的。那么这种问题一般不处理,直接编译通过,在运行时,让调用者调用时的程序强制停止,让调用者对代码进行调整。所以自定义异常时,要么继承Exception,要么继承RuntimeException。
RuntimeException是那些可能在Java虚拟机正常运行期间抛出的异常的超类。可能在执行方法期间抛出但未被捕获的RuntimeException的任何子类都无需在throws子句中进行声明。
抛出异常就需要捕捉异常:
捕捉异常的具体格式是:
try{//需要被检测的异常代码;}catch(异常类变量){/处理异常的代码。}finally{//一定会执行的代码;}
PS:finally代码块只有一种情况不会被执行,就是在之前执行了System.exit(0)。
try中检测到异常会将异常对象传递给catch,catch捕获到异常进行处理。
finally里通常用来关闭资源。比如:数据库资源,IO资源等。
需要注意:try是一个独立的代码块,在其中定义的变量只在该变量块中有效。
异常的处理原则:
1、函数内容如果抛出需要检测的异常,那么函数上必须要声明。否则,必须在函数内用try/catch捕捉,否则编译失败。
2、如果调用到了声明异常的函数,要么try/catch,要么throws,否则编译失败。
3、功能内容可以解决,用catch。解决不了,用throws告诉调用者,由调用者解决。
4、一个功能如果抛出了多个异常,那么调用时,必须有对应多个catch进行针对性处理。内部有几个需要检测的异常,就抛几个异常,抛出几个,就catch几个。
下面我们用一个综合实例来练习一下异常的代码:
1 /*老师用电脑上课 2 问题中涉及两个对象,老师、电脑 3 电脑有两类问题:一类可以解决的,蓝屏--》重启就好了,另外一类解决不了的,硬件损坏 4 还有一类是未知的错误, 5 */ 6 //那么首先我们先定义这三类我们的自定义异常 7 class LanPingException extends Exception//蓝屏异常 8 { 9 LanPingException(String str) 10 { 11 super(str); 12 } 13 } 14 class YingJianException extends Exception//硬件损坏异常 15 { 16 YingJianException(String str) 17 { 18 super(str); 19 } 20 } 21 class NoPlanException extends Exception//未知的异常 22 { 23 NoPlanException(String str) 24 { 25 super(str); 26 } 27 } 28 //接下来定义电脑类 29 class Computer 30 { 31 private int state=1;//0,1,2 32 public void run()throws LanPingException,YingJianException 33 { 34 if(state==1) 35 throw new LanPingException("电脑蓝屏啦!。。。"); 36 if(state==2) 37 throw new YingJianException("电脑硬件损坏啦!。。。"); 38 System.out.println("电脑运行。。。"); 39 } 40 public void reset() 41 { 42 state=0; 43 System.out.println("电脑重启了!"); 44 } 45 } 46 //接下来定义老师类 47 class Teacher 48 { 49 private String name; 50 private Computer com; 51 Teacher(String name) 52 { 53 this.name=name; 54 this.com=new Computer(); 55 } 56 //教学方法 57 public void teaching()throws NoPlanException 58 { 59 try 60 { 61 com.run(); 62 System.out.println(name+" 开始讲课"); 63 } 64 catch (LanPingException e) 65 { 66 e.printStackTrace(); 67 com.reset(); 68 teaching(); 69 } 70 catch (YingJianException e) 71 { 72 e.printStackTrace(); 73 test(); 74 throw new NoPlanException("进度无法完成!原因:"+e.getMessage()); 75 } 76 } 77 public void test() 78 { 79 System.out.println(" 大家自己练习!"); 80 } 81 } 82 class MyException 83 { 84 public static void main(String[] args) 85 { 86 Teacher t=new Teacher("毕老师"); 87 try 88 { 89 t.teaching(); 90 } 91 catch (NoPlanException e) 92 { 93 e.printStackTrace(); 94 } 95 } 96 }
结果为:
整个异常的综合练习代码就运行完了,这里在操作异常的时候要注意:
1.RuntimeException以及其子类如果在函数中被throw抛出,可以不用在函数上声明。
2.子类在覆盖父类方法时,父类的方法如果抛出了异常,那么子类的方法只能抛出父类的异常或者该异常的子类。
3.如果父类抛出多个异常,那么子类只能抛出父类异常的子集。如果父类的方法没有抛出异常,那么子类覆盖时绝对不能抛,就只能try-catch。
四、包
为了更好地组织类,Java提供了包机制。包是类的容器,用于分隔类名空间。如果没有指定包名,所有的示例都属于一个默认的无名包。包对类文件进行分类管理,给类提供多层命名空间。写在程序文件的第一行。类名的全称的是:包名.类名。包也是一种封装形式。
包与包之间的类进行访问,被访问的包中的类必须是public的,被访问的包中的类的方法也必须是public的。
具体的访问权限如下图所示
一个程序文件中只有一个package,但可以有多个import。也就是说可以导入多个包。
jar包
jar包是java的压缩包,方便项目的携带。方便于使用,只要在classpath设置jar路径即可。数据库驱动,SSH框架等都是以jar包体现的。
通过jar.exe工具对jar的操作,具体的常用操作命令见下图:
本部分的内容到这里就结束了,这部分的内容比较多,比较杂需要后面反复的复习,加深记忆。
不断努力加油!为了明天更好的自己。