1、多态:
1)多态:对象在不同时刻表现出来的不同状态。
针对引用类型:
编译期间状态。
运行期间状态。
举例:
水,冰,水蒸气
狗:狗是狗,狗是动物,狗是宠物。在不同时刻有不同状态。
继承是多态的前提。
前提条件:
A:要有继承关系
B:要有方法重写
C:要有父类引用指向子类对象。
class Fu
{
public void show()
{
System.out.println("fu show");
}
}
class Zi extends Fu
{
public void show()
{
System.out.println("zi show");
}
}
class DuoTaiDemo
{
public static void main(String[] args)
{
Fu f = new Fu();//f是Fu的引用。
Zi z = new Zi();//new Zi()是zi的对象
//多态
Fu fu = new Zi();
}
}
2)多态间的成员特点:方法有重写,而变量没有。
A:成员变量
编译看左边,运行看左边。
B:成员方法
编译看左边,运行看右边。
class Fu
{
public int num = 10;
public void show()
{
System.out.println("fu show");
}
}
class Zi extends Fu
{
public int num = 20;
public void show()
{
System.out.println("zi show");
}
public void method()
{
System.out.println("zi method");
}
}
class DuoTaiDemo2
{
public static void main(String[] args)
{
Fu f = new Fu();//f是Fu的引用。
f.show();
System.out.println(f.num);
Zi z = new Zi();//new Zi()是zi的对象
z.show();
System.out.println(z.num);
//多态
Fu fu = new Zi();//父类引用指向子类对象。
fu.show();//成员方法用的是儿子的
System.out.println(fu.num);//成员变量用的是父亲的。
//fu.method();编译时看左边是父类,首先从父类中找,没找到就报错。
}
}
3)多态弊端:
父类引用不能使用子类特有功能。
基本类型:隐式转换(小到大),强制转换(大到小)。
引用类型:向上转型(小到大),向下转型(大到小)。
class Fu
{
public void show()
{
System.out.println("fu show");
}
}
class Zi extends Fu
{
public void show()
{
System.out.println("zi show");
}
public void method()
{
System.out.println("zi method");
}
}
class DuoTaiDemo3
{
public static void main(String[] args)
{
//多态
Fu fu = new Zi(); //向上转型
fu.show();
//fu.method();是报错的,因为父类引用不能使用子类的特有功能
//如果想使用怎么办
/*
Zi z = new Zi();
z.show();
z.method();
这样可以,但是不好,因为fu表面是FU类的对象,但是实际是zi类的对象
又通过new Zi创建一个zi类对象就会有两个对象,这样不好。
那么怎么在只创建一个zi类对象的情况下访问子类特有的方法呢?
强制转换,把表面看似Fu类实际是Zi类的对象,向下转换成表面和实际都是zi类的对象。1
*/
Zi z = (Zi)fu;//向下转型
z.show();
z.method();
}
}
4)多态转型中遇到的一个问题:
类型转换异常
类型不匹配。
你存储的是cat想转换成dog肯定是不行的,因为存储类型不匹配。
class Animal
{
public void show()
{
System.out.println("show animal");
}
}
class Cat extends Animal
{
public void show()
{
System.out.println("show cat");
}
public void run()
{
System.out.println("捉迷藏");
}
}
class Dog extends Animal
{
public void show()
{
System.out.println("show dog");
}
}
class DuoTaiDemo4
{
public static void main(String[] args)
{
//多态
Animal a = new Dog();//向上转型
a.show();
//给a重新赋值
a = new Cat();//向上转型
a.show();
Cat c = (Cat)a;//向下转型
c.show();
c.run();
//Dog d = (Dog)a;
//报错,因为a已经经过向下转型转换成了cat类并用c接收。不能再次转换为Dog类。
}
}
5)多态的好处:为了提高代码的扩展性和可维护性。
class Animal
{
public void show()
{
System.out.println("show ");
}
public void eat()
{
System.out.println("eat");
}
}
class Cat extends Animal
{
public void show()
{
System.out.println("show cat");
}
public void eat()
{
System.out.println("cat eat 老鼠");
}
}
class Dog extends Animal
{
public void show()
{
System.out.println("show dog");
}
public void eat()
{
System.out.println("Dog eat 骨头");
}
}
class Pig extends Animal
{
public void show()
{
System.out.println("show pig");
}
public void eat()
{
System.out.println("pig eat 饲料");
}
}
class AnimalTool
{
private AnimalTool(){}
/*
public static void printDog(Dog d)
{
d.show();
d.eat();
}
public static void printCat(Cat c)
{
c.show();
c.eat();
}
public static void printPig(Pig p)
{
p.show();
p.eat();
}
*/
public static void printAnimal(Animal a)
{
a.show();
a.eat();
}
}
class DuoTaiDemo5
{
public static void main(String[] args)
{
//一开始养了一只狗
Dog d = new Dog();
d.show();
d.eat();
//接着又养了一只狗
Dog d2 = new Dog();
d2.show();
d2.eat();
Dog d3 = new Dog();
d3.show();
d3.eat();
/*
我们发现,每养一只狗都要调用show和eat这两个方法,那么随着对象的增加,代码重复度会越高,
如果父类的方法不只有show和eat这两个,那么重复度还会更高。
解决办法,能不能把调用的整体封装一下,比如把show和eat这两个方法封装在一个方法中,
然后传递一个变化的对象。
比如:
public static void printDog(Dog d)
{
d.show();
d.eat();
}
*/
//改进版
System.out.println("============");
Dog d4 = new Dog();
Dog d5 = new Dog();
Dog d6 = new Dog();
/*
printDog(d4);
printDog(d5);
printDog(d6);
*/
/*
AnimalTool.printDog(d4);
AnimalTool.printDog(d5);
AnimalTool.printDog(d6);
*/
AnimalTool.printAnimal(d4);
AnimalTool.printAnimal(d5);
AnimalTool.printAnimal(d6);
//然后,不喜欢狗了,喜欢猫了。
//基本养猫动作和基本养狗动作一样。
//直接写改进版
System.out.println("============");
Cat c = new Cat();
Cat c2 = new Cat();
Cat c3 = new Cat();
/*
printCat(c);
printCat(c2);
printCat(c3);
*/
/*
AnimalTool.printCat(c);
AnimalTool.printCat(c2);
AnimalTool.printCat(c3);
*/
AnimalTool.printAnimal(c);
AnimalTool.printAnimal(c2);
AnimalTool.printAnimal(c3);
System.out.println("============");
//后来又喜欢宠物猪了
Pig p = new Pig();
Pig p2 = new Pig();
Pig p3 = new Pig();
/*
printPig(p);
printPig(p2);
printPig(p3);
*/
/*
AnimalTool.printPig(p);
AnimalTool.printPig(p2);
AnimalTool.printPig(p3);
*/
AnimalTool.printAnimal(p);
AnimalTool.printAnimal(p2);
AnimalTool.printAnimal(p3);
/*
发现一个问题,我们一直在修改main里面的东西,但是测试类只能创建对象引用对象不能包含其他方法,
下面三个方法不能包含在测试类里面。测试类只能做测试用。
解决方法?
重新定义一个工具类,实现输出效果。
*/
/*
后来又喜欢宠物狼了,做法是创建一个宠物狼类继承animal,里面有两个方法show和eat。
并且在工具类中,创建一个方法print包含show和eat。
那么问题来了,工具类是不变的。不能随意改动,不能每次添加一个对象就在工具类中添加。
解决方法,多态!!!!!!
*/
}
/*
public static void printDog(Dog d)
{
d.show();
d.eat();
}
public static void printCat(Cat c)
{
c.show();
c.eat();
}
public static void printPig(Pig p)
{
p.show();
p.eat();
}
*/
}
2、抽象类
1)由来
/*class Dog
{public void eat
{
System.out.println("eat 骨头");
}
}
class Cat
{
public void eat
{
System.out.println("eat 鱼");
}
}
*/
/*
目前来看,这两个类能不能抽取内容呢?能,因为他们的方法声明是一样的
所以我们抽取了方法的声明。但是方法的声明不能单独存在,只能依据类而存在。
*/
abstract class Animal
{
public abstract void eat();
}
//用abstract修饰的类不能创建对象,称为抽象类
//抽象类是提取方法声明,不要方法内容。
/*
上面类只有方法声明没有方法体,没有方法体的方法没有意义。
针对没有方法体的方法,Java提供了一种表示形式:抽象方法。
这个时候就有了一个关键字用来表示抽象方法,abstract
*/
class Dog extends Animal
{
public void eat
{
System.out.println("eat 骨头");
}
}
class Cat extends Animal
{
public void eat
{
System.out.println("eat 鱼");
}
}
2) 抽象类的特点:
A:抽象方法和抽象类用关键字abstract表示
B:有抽象方法的类一定是抽象类(或者接口)。
抽象类中不一定有抽象方法。
C:抽象类不能被实例化,不具体的
那么怎么实例化呢? 通过子类的对象实例化。
D:如果一个类继承抽象类,那么这个子类要么本身也是抽象类,要么这个子类重写父类的所有抽象方法。
在继承中我们为什么有些时候会选择父类是抽象类?
作用是,强制子类必须完成某些功能。
abstract class Animal
{
public abstract void eat();
}
class Dog extends Animal
{
public void eat()
{
System.out.println("Dog eat 骨头");
}
}
class AbstractDemo1
{
public static void main(String[] args)
{
Animal a = new Dog();
a.eat();
}
}
3)抽象类的成员特点:
A:成员变量 可以有成员变量,也可以有常量
B:构造方法 有构造方法的。
既然不能被实例化,那么有构造方法有什么用?
可以用于子类访问父类数据前,对父类进行初始化。
C:成员方法:
可以有抽象方法,也可以有非抽象方法。
抽象方法是为了要求子类做某些事情,
非抽象方法是为了提高代码复用性,被子类继承。
abstract class Animal
{
public int num = 10;
public final int num2 = 20;
public abstract void show();
public void method()
{
System.out.println("fu method");
}
}
class Dog extends Animal
{
public void show()
{
System.out.println(num);
System.out.println(num2);
}
public void method()
{
System.out.println("zi method");
}
}
class AbstractDemo2
{
public static void main(String[] args)
{
//多态
Animal a = new Dog();
a.show();
a.method();
Dog d = new Dog();
d.show();
d.method();
}
}
3、接口:
1)/*
abstract class Animal
{
public abstract void eat();
}
这个抽象类中,只有抽象方法,针对这种情况,java就提供了一种更加抽象的形式:接口
类用class表示,接口用interface表示。
这样的话我们就把上面抽象类改写为:
interface Animal
{
public abstract void eat();
}
有了接口怎么用呢?
必须也要有具体的类来实现?
“实现”用哪个关键字来实现呢?implements
格式:
class 类名 implements 接口名
{
}
*/
interface Animal
{
public abstract void eat();
}
class Cat implements Animal
{
public void eat()
{
System.out.println("cat eat fish");
}
}
class Dog implements Animal
{
public void eat()
{
System.out.println("dog eat 骨头");
}
}
class Interface
{
public static void main(String[] args)
{
Animal c = new Cat();
c.eat();
c= new Dog();
c.eat();
}
}
2)/*
接口:接口是一种特殊的抽象类,比抽象类更抽象,因为它里面的方法都是抽象的。
接口的特点:
A:接口是抽象的,不能被实例化。
B:接口中的方法要么被子类实现,要么子类也是抽象类。
*/
interface Animal
{
public abstract void eat();
}
class Dog implements Animal
{
public void eat()
{
System.out.println("eat");
}
}
class InterfaceDemo
{
public static void main(String[] args)
{
//Animal a = new Animal();接口是抽象的,不能被实例化。
Dog d = new Dog();
d.eat();
//接口多态
Animal a = new Dog();
a.eat();
}
}
3)接口的成员特点:
成员变量:接口中的变量和常量可以被子类接收。
接口中只有常量,所有的接口变量有默认修饰符
public static final
推荐:永远手动给出修饰符
构造方法:
接口是没有构造方法的,接口不需要对数据进行初始化,
因为变量都是静态修饰的常量,随着类的创建而创建,
并且赋值,不需要重新赋值。
任何类如果没有继承父类,那么这个类就继承自object类。
成员方法:
接口中的方法都是抽象的,没有非抽象之说。
接口中的成员方法有默认修饰符:
public abstract
*/
interface Animal
{
public int x = 10;
public final int y = 20;
//void method();
}
class Dog implements Animal
{
public void show()
{
System.out.println(x);
System.out.println(y);
System.out.println(Animal.x);//通过Animal.x即类名.变量名可以访问,说明这个变量是静态的。
}
/*void method()
{
System.out.println("method");
}
这样会报错,因为在接口中虽然写的是void method但是由于默认修饰符的原因,最终的声明是
public abstract void method;当在子类中写void method();系统会认为是默认修饰符,又因为接口中是Public
不是默认的。
*/
}
class InterfaceDemo2
{
public static void main(String[] args)
{
Dog d = new Dog();
d.show();
}
}
4、类与接口的关系:
类与类的关系:
类与类之间是继承关系,并且只是单继承,但是可以多层继承。
类与接口的关系:
类与接口之间是实现的关系,可以单实现也可以多实现。
并且可以在继承一个类的同时实现多个接口。
接口与接口的关系:
接口与接口之间是继承关系,可以单继承也可以多继承。
在类中不能实现多继承,是因为两个类中可能有相同的方法,会出现调用不明确的问题。
但是接口中的方法都是抽象的,两个接口中有相同的抽象方法不会出现调用不明确的问题
因为两个类都是抽象的。
所有类都直接或者间接的继承object,object是所有类的超类
5、成员特点:
抽象类:
成员变量:可以是变量也可以是常量
构造方法:有构造方法
成员方法:可以是抽象方法,也可以是非抽象方法。
接口:
成员变量:只能是常量,因为有默认修饰符,public final
成员方法:只能是抽象方法,因为有默认修饰符public abstract
2、关系特点:
类与类的关系:
继承关系,只能单继承,可以多层继承。
类与接口的关系:
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口。
接口与接口:
继承关系,可以单继承也可以多继承。
3、设计理念的区别:
抽象类被继承,类中定义的是整个继承体现的共性内容。
体现:is a
接口被实现,这个接口中定义的是整个体现的扩展内容。
体现:like a