(一)多态
多态的应用场景:
1.多态用于形参类型的时候,可以接收更多类型的数据。
2.多态用于返回值类型的时候,可以返回更多类型的参数。
多态的好处:提高了程序的拓展性 。
多态情况下,不能访问子类特有的成员。
多态情况下如果需要调用子类特有的成员,需要进行强制类型转换。
引用数据类型:
小数据类型--》大数据类型:自动类型转换
大数据类型--》小数据类型:强制类型转换
例如:
abstract class Animal
{
String name;
String color;
public Animal(String name,String color){
this.name=name;
this.color=color;
}
public abstract void run();
}
//鱼
class Fish extends Animal
{
public Fish(String name,String color){
super(name,color);
}
public void run(){
System.out.println(name+"摇摇尾巴游啊游~~~");
}
public void bubble(){
System.out.println(name+"开心开心吐泡泡~~~");
}
}
//兔
class Rabbit extends Animal
{
public Rabbit(String name,String color){
super(name,color);
}
public void run(){
System.out.println(name+"蹦蹦跳跳真可爱!!!");
}
public void tricky(){
System.out.println(name+"狡兔三窟。。。");
}
}
class demo2
{
public static void main(String[] args)
{
/* Animal a=new Fish("锦鲤","金红色");//自动类型转换
Fish f=(Fish)a; //强制类型转换 把动物看成鱼
Animal b=new Rabbit("小白兔","白色");
Rabbit r=(Rabbit)b;
f.run();
f.bubble();//多态情况下不能调用子类特有方法,要用的话
r.run();
r.tricky();*/
// System.out.println("Hello World!");
Fish f=new Fish("草鱼","黑色");
print(f);
}
//定义一个方法可以接受任何类型的动物,在方法内部调用动物特有的方法
public static void print(Animal a){
if(a instanceof Fish){
Fish f=(Fish)a;
f.bubble();
}else if(a instanceof Rabbit){
Rabbit r=(Rabbit)a;
r.tricky();
}
}
}
接口中的方法都是abstract,不能与static配合使用,所以接口中的函数都是非静态。
多态情况下,接口和实现类存在同名函数,永远访问实现类。
(二)内部类
内部类:在一个类的内部定义另一个类,另一个类就是内部类。
内部类的类型:
1.成员内部类:内部类作为成员,与成员变量、成员函数并列。
class文件名:外部类名$内部类名.class 区分开内部类属于哪一个外部类。
访问方式:
方式一:在外部类提供一个方法创建内部类的对象进行访问。
方式二:在其他类中直接创建内部类的对象进行访问。
格式 :外部类.内部类 变量名=new 外部类().new 内部类()
注意:在其他类中直接创建静态内部类的对象进行访问。
格式:外部类.内部类 变量名=new 外部类.内部类()
要注意的细节:
1.内部类可以直接访问外部类的所有成员。
2.内部类与外部类存在同名的成员时,在内部类中默认是访问内部类的成员。(就近原则)
可以通过"外部类.this.成员变量"访问外部类的成员。
3.私有的成员内部类只能通过在外部类提供一个公共的方法进行访问,在其他类中无法访问。
4.如果一个成员内部类定义了静态的成员,那么该类也必须使用static修饰。
原因:静态成员变量不依赖于对象。
例如:
class Outer
{
//外部类的成员变量
private String name="外部类";
//成员内部类
static class Inner
{
static int x=10;
String name="内部类";//就近原则
public void print(){
System.out.println("这个是内部的print方法:"+name);
}
}
//外部类成员函数
public void newInstance(){
Inner inner=new Inner();
inner.print();
//System.out.println(name);
}
}
//其他类
class demo4
{
public static void main(String[] args)
{
/*Outer outer=new Outer();
outer.newInstance();*/
//创建内部类对象(非静态)
/*Outer.Inner inner=new Outer().new Inner();
inner.print();*/
//创建内部类对象(静态)
Outer.Inner inner=new Outer.Inner();
inner.print();
//Inner inner=new Inner();
//System.out.println("x:"+Outer.Inner.x);
}
}
2.局部内部类:在一个方法的内部定义的类称作局部内部类。
要注意的细节:
1.如果局部内部类要访问局部变量,局部变量需要用final修饰。
原因:局部内部类的生命周期比局部变量的长,在访问时会对局部变量进行复制,用final修饰。
例如:
class Outer
{
public void print(){
final int y=90;//y的生命周期:执行该语句的时候存在在内存中,方法执行完毕消失。
class Inner
{
int x=10;
public void show(){
System.out.println("这是内部类的show方法"+y);//若y已消失,还需要访问y,感觉y的生命周期被拉长
/*解决方法:让局部内部类访问局部变量的复制品,即用final定义*/
}
}
//创建一个局部内部类对象
Inner inner=new Inner();//Inner对象的生命周期比y的长:执行到该语句时存在在内存中,方法执行完毕时还没有消失,等待垃圾回收期回收
inner.show();
}
}
class demo5
{
public static void main(String[] args)
{
Outer outer=new Outer();
outer.print();
// System.out.println("Hello World!");
}
}
3.匿名内部类(用最多)
匿名内部类:没有类名的内部类就称为匿名内部类。
好处:简化书写。
使用前提:必须存在继承或实现的关系。
注意事项:
1.匿名内部类只是没有类名而已,其他的一切成员都有。
例如:
abstract class Animal
{
public abstract void run();
public abstract void memory();
}
class Outer
{
public void print(){
//匿名内部类,创建对象时,由于没有类名,只能借用父类名
//new的是一个Animal的子类对象,必须要实现Animal中的抽象方法
//多态 父类的引用类型变量指向了子类的对象
Animal a=new Animal(){
//大括号里面是匿名内部类的成员,也可以写专属成员变量
String name;
public void run(){
System.out.println("鱼在游~~~");
}
public void memory(){
System.out.println("鱼只有七秒钟记忆哦。。。");
//return this;//代表了当前对象,可以调用专有函数.memory().run()
}
};//可以直接在这里调用函数,对象.函数名()
a.run();
a.memory();
/*new Animal(){
//匿名内部类的成员
public void run(){
System.out.println("鱼在游~~~");
}
}.run();*/
}
}
class demo6
{
public static void main(String[] args)
{
Outer outer=new Outer();
outer.print();
}
}
(接口)实现关系下的匿名内部类:
interface A
{
public void add();
}
class Outer
{
//实现关系下的匿名内部类
public void print(){
//创建的是接口实现类的对象
new A(){
//匿名内部类的对象
public void add(){
System.out.println("添加~");
}
}.add();
}
}
内部类的好处:可以直接访问外部类的所有成员。
应用场景:我们在描述A事物的时候,A事物内部还维护了另一个B事物,而且B事物还必须要访问到A事物的成员,这时候就可以使用内部类描述B事物。
(三)java中的异常体系
java异常体系:描述不正常情况的类的集合。
异常:
Throwable常用的方法:
1.toString() //返回的是用于描述该异常情况的类的完整类名。(包名+类名=完整类名)
2.getMessage() //返回创建Throwable对象的时候传入的消息字符串
3.printStackTrace()//打印异常的栈消息
Throwable
Error:错误一般都是由于jvm或者硬件引发的问题,所以一般不会通过代码处理。
Exception:异常 如果程序出现了异常,那么一般就需要通过代码去处理了。
程序出现了不正常情况,怎么区分到底是错误还是异常呢?
如果不正常情况的消息是以Error结尾的,是错误。
如果不正常情况的消息是以Exception结尾的,是异常。
疑问:怎么会有异常的栈信息出现呢?
异常的栈信息是通过printStackTrace的方法打印的,那么异常对象从何而来?
原因:java虚拟机在执行a/b代码的时候,发现b(除数)等于0,在现实生活中属于一种不正常的情况,
jvm一旦发现了不正常的情况,那么马上就会创建一个相应的异常对象,并且调用该异常对象的
printStackTrac方法把异常的栈信息打印出来。
异常的处理方式:
方式一:捕获处理
捕获处理的格式:
try{
可能会发生异常的代码
}catch(异常的类型 变量名){
异常处理代码;
}
捕获处理要注意的细节:
1.如果一个try块的代码出现了异常,经过处理之后,那么try-catch块外面的代码可以正常执行。
2.如果一个try块的代码出现了异常,那么在try块中出现异常的代码后面的所有代码都无法正常执行。
class demo9
{
public static void main(String[] args)
{
div(8,0);
}
public static void div(int a,int b){
int c=0;
try{
c=a/b;
System.out.println("我能执行吗?");
}catch(ArithmeticException e){ //jvm会在该句创建一个异常对象
//异常的处理代码
System.out.println("出错了。。");
System.out.println(e.toString());
}
//int c=a/b;//jvm会创建一个异常对象,调用相应的方法,打印栈信息
System.out.println(c);
}
}