一、多态
1.概念:某一事物的多种表现形态
2.程序的体现:
父类或接口的引用可以指向自己的子类对象
父类或接口的引用可以接受自己的子类对象
例如:
/*
猫和狗都属于动物,是动物的两种不同表现形式
*/
abstract class Animal
{
public abstract void eat();//吃东西为动物的共性方法,但是不明确吃什么,所以定义为抽象
}
class Cat extends Animal
{
public void eat()
{
System.out.println("吃鱼");
}
public void catchMouse()//猫的特有功能
{
System.out.println("抓老鼠");
}
}
class Dog extends Animal
{
public void eat()
{
System.out.println("吃骨头");
}
public void kanJia()//狗的特有功能
{
System.out.println("看家");
}
}
class DuoTaiDemo
{
public static void main(String[] args)
{
Cat c = new Cat();
c.eat();
Animal a = new Dog();//可以通过父类引用指向子类对象
a.eat();
method(new Cat());//通过父类接收自己的子类对象
}
public static void method(Animal a)
{
a.eat();
}
}
在上面的程序中,通过 Animal a = new Dog(); a不能访问Dog中的Kanjia()方法。要是父类的应用能访问子类的特有方法,这时就用到了类型转换
Dog d = (Dog)a;//向下转型
d.kanJia();
2.多态的前提:
类与类之间必须存在继承或实现;必须有覆盖操作
3.多态的好处和弊端:
好处:消除类型之间的耦合性
改善代码的组织结构和可读性
提高程序的扩展性
4.多态中成员的特点:
成员函数的特点:
编译时:参阅引用型变量所属的类中是否有调用方法,如果有就编译通过,否则反之。
运行时:参阅对象所属类中是否存在调用方法
成员变量的特点:
编译和运行,都参阅引用型变量所属类
静态成员函数的特点:同上
原因:被静态修饰的方法可以直接被类名调用,编译后,内存中会产生父类的class文件,运行时,会先找父类中的静态方法。
例:
class Fu
{
int num = 4;
void method_1()
{
System.out.println("fu--method_1");
}
void method_2()
{
System.out.println("fu--method_2");
}
static void method_4()
{
System.out.println("fu--method_4");
}
}
class Zi extends Fu
{
int num = 6;
void method_1()
{
System.out.println("zi--method_1");
}
void method_3()
{
System.out.println("zi--method_3");
}
static void method_4()
{
System.out.println("zi--method_4");
}
}
class DuoTaiDemo2
{
public static void main(String[] args)
{
Fu f = new Zi();
f.method_1();
f.method_2();
//f.method_3(); 编译失败,因为Fu类中没有method_3()这个方法
System.out.println(f.num);//运行结果为4,
f.method_4();//运行结果为fu--method_4
}
}
5.多态的应用:
/*
电脑是基于主板运行的
*/
interface PIC//PCI插槽
{
public void open();//打开方法
public void close();//关闭方法
}
class NetCard implements PIC
{
public void open()
{
System.out.println("netCard open");
}
public void close()
{
System.out.println("netCard close");
}
}
class SoundCard implements PIC
{
public void open()
{
System.out.println("SoundCard open");
}
public void close()
{
System.out.println("SoundCard open");
}
}
class Mainboard//主板
{
public void run()
{
System.out.println("Mainboard run");
}
public void usePIC(PIC p)//主板使用pci插槽
{
if(p!=null)
{
p.open();
p.close();
}
}
}
class DuoTaiTest
{
public static void main(String[] args)
{
Mainboard m = new Mainboard();
m.run();//运行结果 Mainboard run
m.usePIC(new NetCard());//netCard open
//netCard close
}
}
二、内部类
1、访问规则:内部类可以直接访问外部类成员,包括私有。原因:内部类中带有一个外部类的引用,外部类名.this
外部类要访问内部类,要建立内部类对象。
class Outer//外部类
{
int num = 2;
void method()
{
System.out.println("外部类:"+num);
Inner in = new Inner();//外部类通过new内部类对象
in.function();
}
class Inner//内部类
{
void function()
{
System.out.println("外部类:"+num);//内部类直接访问外部类成员变量num
}
}
}
class InnerClassDemo
{
public static void main(String[] args)
{
Outer ou = new Outer();
ou.method();
}
}
2、访问格式
如果要直接访问内部类的function,可以通过下面的格式
Outer.Inner in = new Outer().new Inner();
in.function();
2.1非私有内部类可以在外部其他类中直接建立对象
外部类名.内部类名 变量名 = 外部类对象.内部类对象2.2当内部类定义在外部类的成员位置上时,可以被private和static修饰
当内部类被static修饰时,便具有了静态的属性
2.3当内部类定义在外部类的局部位置时,也可以直接访问外部类的成员变量,但是访问外部类局部变量时,该变量要被final修饰。
3.匿名内部类:
3.1概念:内部类的简写格式,前提是内部类必须继承或实现接口
3.2好处:简化书写
3.3格式:new 父类或接口()
{
定义子类内容
}
3.4练习:
interface Inner
{
void method();
}
class Test
{
//补足代码
static Inner function()
{
return new Inner()
{
public void method()
{
System.out.println("method run");
}
};
}
}
class InnerClassTest
{
public static void main(String[] args)
{
Test.function().method();//根据这句代码,在Test类中补足代码
/*
分析:Test.function()这句代码,说明Test类中有一个静态function方法
function().method();说明function方法运行后是一个对象,而且是Inner对象,
因为Inner对象里面才定义了method方法
*/
}
}
3.5问题:一个类没有继承或者实现接口,能不能写匿名内部类,并使用其方法
例如:
new Object()
{
public void function()
{
}
}.function();
三、异常:
1、概念:就是程序运行中出项不正常的情况,java对其进行封装,并形成一个异常体系。
2、异常体系,通过查阅javaAPI帮助文档
Throwable类是所有异常类的父类
|——Error 严重问题,java没有编写针对性的代码进行处理
|——Exception 非严重问题,java编写了相应的代码进行处理
|——RuntimeException Exception的子类,运行时异常
例如
class ExceptionDemo
{
public static void main(String[] args)
{
int[] arr = new int[5];
System.out.println(arr[6]);
}
}
上面代码编译时没有问题,但运行时出现了角标越界异常,这种异常属于Exception,java虚拟机给出了对应的提示,并且将程序停止
3、异常的处理
try
{
需要被检测的代码;
}
catch(异常类 变量)
{
处理异常的代码;(处理方式)
}
finally
{
一定会执行的语句;通常用于释放资源
}
例如
class Demo
{
int div(int a,int b)
{
return a/b;
}
}
class ExceptionDemo2
{
public static void main(String[] args)
{
Demo d = new Demo();
try
{
int x = d.div(4,0);
System.out.println("结果是"+x);
}
catch (Exception e)//Exception e = new ArithmeticException(); 多态的体现
{
System.out.println("除数不能为0");
}
System.out.println("over");
}
}
运行结果
4、对捕获到的异常进行常见方法操作
5、在编写功能时,如果可能会出现异常,要在功能上抛出异常,比如
class Demo
{
int div(int a,int b)throws Exception//在有可能发生异常的方法进行标示
{
return a/b;
}
}
class ExceptionDemo3
{
public static void main(String[] args)
{
Demo d = new Demo();
int x = d.div(4,0);
System.out.println("结果是"+x);
System.out.println("over");
}
}
编译时会发生如下提示
一旦方法声明了抛出异常,在使用该方法时,必须将编写代码处理异常,要么是try和catch,要么是再将其抛出
6、自定义异常:继承Exception类
例如:
class FuShuException extends Exception
{
private int value;
FuShuException()//空参数构造函数
{
super();
}
FuShuException(String msg,int value)//自定义异常信息的构造函数
{
super(msg);
this.value = value;
}
public int getValue()
{
return value;
}
}
class Demo
{
int div(int a,int b)throws FuShuException
{
if(b<0)
throw new FuShuException("除数不能为负数",b);
return a/b;
}
}
class ExceptionDemo4
{
public static void main(String[] args)
{
Demo d = new Demo();
try
{
int x = d.div(4,-1);
System.out.println("结果是"+x);
}
catch (FuShuException e)
{
System.out.println(e.toString());
}
}
}
运行结果为:
在上面的程序中,对于除数为-1也定义为异常,所以需要自己自定义异常类
这里涉及到两个关键字:throws和throw,他们有什么区别呢?
throws 定义在函数上,后面跟异常类,可以声明多个,用逗号隔开
throw 定义在函数内,后面跟异常对象
7、RuntimeException(运行时异常)
如果在函数内容抛出该异常,函数上可以不用声明,编译一样通过。
如果在函数上声明了该异常。调用者可以不用进行处理。编译一样通过
为什么运行时异常不用在函数上进行声明呢?
不在函数上进行声明,调用方法时就不用进行处理,在运行时,发生该异常可以让程序停止,这时程序员就可以找到哪段代码出错
并且进行修正。这样就不会将问题隐藏。
在上面的程序中,自定义的负数异常应该继承RuntimeException更为合理。
8、异常在子父类中的特点
(1)、子类在覆盖父类方法时,如果父类抛出异常,那么子类覆盖的方法只能抛出父类的异常或者该异常的子类。
(2)、如果父类或接口没有抛出异常,那么子类也不能抛出异常,如果子类中有异常,只能进行try和catch处理。
9、练习
/*
需求:有圆和矩形,都能获取面积,但是对于矩形的宽高或者圆的半径作了规定,
必须大于0,否则就会发生异常
分析:圆和矩形都有获取面积的方法,那就可以抽取出来,可以定义一个接口,里面有获取面积的方法
但是怎么获取是不确定的。
当用户传入的值不合法时,会发生异常,这是就可以自定义异常,并且这个异常在编译时可以通过,
在运行时就会告诉用户,传入的值不合法,
*/
class LengthException extends RuntimeException//自定义异常,继承RuntimeException
{
public LengthException(String msg)
{
super(msg);
}
}
interface Area//定义接口,里面有获取面积的方法
{
void getArea();
}
class MyRectangle implements Area//矩形类,
{
private int width;//矩形的宽
private int height;//矩形的高
public MyRectangle(int width,int height)//具有宽和高的构造函数
{
if(width<=0 || height<=0)
throw new LengthException("长或宽必须大于0");
this.width = width;
this.height = height;
}
public void getArea()//矩形获取面积的特有方法
{
System.out.println("矩形的面积为"+width*height);
}
}
class Circle implements Area//圆形类
{
private final double PI = 3.14;//π的值是固定的,可以声明为final类型
private int radius;//圆的半径
public Circle(int radius)//具有半径的构造函数
{
if(radius<=0)
throw new LengthException("半径必须大于0");
this.radius = radius;
}
public void getArea()//获取圆的面积的特有方法
{
System.out.println("圆的面积为"+radius*PI*PI);
}
}
class ExceptionTest
{
public static void main(String[] args)
{
MyRectangle rec = new MyRectangle(1,4);
rec.getArea();
Circle c = new Circle(2);
c.getArea();
}
}