一.抽象
- 如果父类当中的方法不确定如何进行方法体实现,那么这就应该是一个抽象方法。
- 如:动物吃东西 猫吃鱼 狗吃骨头
- 将eat定义为抽象方法
抽象类:抽象方法所在的类,必须是抽象类才行。在class之前写上abstract即可
如何使用抽象类和抽象方法,
1.不能直接创建new抽象类对象
2.必须用一个子类来继承抽象父类
3.子类必须重写抽象父类当中所有的抽象方法
重写(实现):子类去掉抽象方法的abstract关键字,然后补上方法体大括号。
4.创建子类对象进行使用
public class DemoMain {
public static void main(String[] args) {
// Abimal animal = new Animal();//错误写法,不能直接创建抽象类对象
Cat cat = new Cat();
cat.eat();
}
}
public abstract class Animal {//抽象类
public abstract void eat();//这是一个抽象方法,代表吃东西,但是具体吃什么不确定
public void normalMethod(){//这是普通的成员方法
}
}
public class Cat extends Animal {
@Override
public void eat(){
System.out.println("猫吃鱼!");
}
}一个抽象类不一定含有抽象方法
只要保证抽象方法所在的类是抽象类,即可。
1.抽象类不能创建对象,如果创建,编译无法通过而报错,只能创建其非抽象子类的对象
理解:加入创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。
2.抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的
理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。
3.抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类
理解:未包含抽象方法的类,目的是不想让调用者创建该类对象,通常用于特殊类结构设计。
4.抽象类的子类,必须重写抽象父类中所有的抽象方法,否则编译无法通过而报错,除非该子类也是抽象类。全部重写之后,此时这个子类就是一个普通的类。
理解:假设不重写所有的抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有意义。
5.抽象类中可以使用静态方法、变量
理解:静态属于类,类名.调用。与有无对象无关public class DemoMain {
public static void main(String[] args) {
Zi zi = new Zi();
zi.eat();
}
}
public abstract class Fu {//抽象类
public Fu(){
System.out.println("抽象父类构造方法执行");
}
public abstract void eat();//抽象方法
}
public class Zi extends Fu {
public Zi (){
//super();无参构造Fu要想运行,只能通过Zi中赠送的的super调用,直接new是不允许的
System.out.println("子类构造方法执行!");
}
@Override
public void eat() {//抽象方法的实现
System.out.println("吃饭饭!");
}
}二.final关键字
- 对于类、方法来说,abstract关键字和final关键字不能同时使用,矛盾。
1.final修饰类
格式:
public final class 类名称{
//...
}
含义:
当前这个类不能有任何的子类(因为不能被继承,即不能作为父类)2.final修饰成员变量
- 只能手动赋值一次 或 只能在构造方法中赋值一次
- 必须保证类当中所有重载的构造方法,都最终会对final的成员变量进行赋值
- 例如:π = 3.1415926... 说明:final修饰的变量,称为常量
3.final修饰方法
- (子父类的重写关系当中)不能被重写
public /*final*/ class Final{//1.final修饰的类不能被继承
final int j ;//2.final修饰的成员变量,只能手动赋值一次
public Final(){//3.或 final修饰的成员变量没有手动赋值,只能构造方法中赋值一次
j = 999;
}
public final void method(){
System.out.println("我是一个父类中final修饰的方法");
}
public static void main(String[] args) {
final int a = 10;
/*a = 20;4.final修饰的变量不能被修改,报错*/
Final f = new Final();
/*f.j = 20;final修饰的成员变量不能被修改,报错*/
}
}
public class FianalSub extends Final{//从父类Final中继承到了,i,j,a,method()
/*@Override 5.报错,父类中final修饰的方法,不能被重写
public final void method(){
}*/
public static void main(String[] args) {
Final f = new Final();
f.method();//继承使用父类中的method方法
}
}三.多态
- extends继承 或 implements实现,是多态性的前提。
1.概念
- 表示事物的多种形态
- 举例:Person类,人类的表现方式,工程师,医生,学生...
- 多态的定义:
格式:
父类名称 对象名 = new 子类名称();
接口名称 对象名 = new 实现类名称();
public class Demo01Multi {
public static void main(String[] args) {
//使用多态写法
Fu obj = new Zi();//父类的引用 指向 子类的对象
obj.method();//method是子类父类都有的成员方法,规则:new的是谁就是谁,没有则向上找
obj.methodFu();//methodFu是父类特有的成员方法,new的是Zi,子类没有,向上找,父类中有
}
}
public class Fu {
public void method(){
System.out.println("父类方法!!");
}
public void methodFu(){
System.out.println("父类特有方法!");
}
}
public class Zi extends Fu{
@Override
public void method(){//覆盖重写
System.out.println("子类方法!!");
}
}2.多态中成员变量的使用特点
- 1.直接通过对象名称访问成员变量:编译看左,运行看左,没有则向上找
- 2.间接通过成员方法访问成员变量:编译看左,运行看右,没有则向上找
public class Demo01MultiField {
public static void main(String[] args) {
//使用多态的写法,父类引用指向子类对象
Fu obj = new Zi();
System.out.println(obj.num);//10
//System.out.println(obj.age);//错误写法,左边Fu中没有
System.out.println("==========================");
obj.showNUm();//10
obj.showNUm();//20
}
}
public class Fu {
int num = 10;
public void showNUm(){
System.out.println(num);
}
}
public class Zi extends Fu{
int num = 20;
int age = 16;
@Override
public void showNUm(){
System.out.println(num);
}
}3.多态中成员方法使用特点
- 编译看左,运行看右,没有则向上找
public class Demo02MultiMethod {
public static void main(String[] args) {
Fu obj = new Zi();//多态 父类引用指向子类对象
obj.method();//编译看左 父类,父类中有method编译通过,运行看右 子类,子类中有method运行
obj.methodFu();//编译看左 父类,父类中特有methodFu,编译通过,运行看右 子类,子类中没有向上找运行
// obj.methodZi();//错误写法,编译看左,左边是Fu,Fu中没有methodZi方法,所以编译报错
}
}
public class Zi extends Fu{
int num = 20;
int age = 16;
public void method (){
System.out.println("子类方法!");
}
public void methodZi(){
System.out.println("子类特有方法!");
}
}
public class Fu {
int num = 10;
public void method (){
System.out.println("父类方法!");
}
public void methodFu(){
System.out.println("父类特有方法!");
}
}4.多态中静态的使用特点
- 编译看左,运行看左
public class Test(){
public static void main(String[] args){
Fu f = new Zi();
f.speak();//父类中的静态speak!
}
}
public class Fu(){
public static void speak(){
sout("父类中的静态speak!");
}
}
public class Zi extends Fu(){
public static void speak(){//静态不可以继承,静态中的同名静态方法
sout("子类中的静态speak!");
}
}5.使用多态的好处
- 提高了代码的维护性(继承保证)
- 提高了代码的扩展性(由多态保证)

榨汁机案例:
要求:定义一个榨汁机,类中有一个榨汁方法,榨所有水果汁
public class Test {
public static void main(String[] args) {
Juicer j = new Juicer();//榨汁机对象
//水果类型多态
Fruit f1 = new Apple();
j.juicing(f1);//榨苹果汁儿!
Fruit f2 = new Orange();
j.juicing(f2);//榨橙汁儿!
}
}
public class Juicer {//定义一个榨汁机类
public void juicing(Fruit fruit){//榨汁机入口,不同的水果,出不同的果汁
fruit.flow();
}
}
public class Fruit {
public void flow(){//抽象类型,水果变果汁
}
}
public class Orange extends Fruit{
@Override
public void flow() {
System.out.println("榨橙汁儿!");
}
}
public class Apple extends Fruit{
@Override
public void flow() {
System.out.println("榨苹果汁儿");
}
}6.对象的上下转型
- 对象的向上转型,其实就是多态写法:
格式:
父类名称 对象名 = new 子类名称();
Animal animal = new Cat();
含义:
右侧创建一个子类对象,把它当做父类来看待使用
创建了一只猫,当做动物看待,没问题。
注意事项:
向上转型一定是安全的,从小范围转向了大范围。从小范围的猫,向上转换成为更大范围的动物。
类似于:
double num = 100;//正确,int -> double 自动类型转换。
缺点:
对象一旦向上转型为父类,那么就无法调用子类原本特有的内容
解决方案:
用对象的向下转型[还原]- 对象的向下转型,其实就是一个[还原]的动作
格式:
子类名称 对象名 = (子类名称)父类对象;
含义:
将父类对象,[还原]为本来的子类对象
Animal animal = new Cat();//本来是猫,向上转型成动物。
Cat c = (Cat)animal;//本来是猫,已经当做成动物了,还原为本来的猫
注意事项:
a.必须保证对象创建的时候,就是猫,才能向下转型称为猫。
b.如果对象创建的时候本来是猫,现在非要向下转型成为狗,报错。
类似于:
int num = (int)10.0;//可以 int num = (int)10.5;//不可以,精度损失public class Demo01Main {
public static void main(String[] args) {
Animal animal = new Cat();//左父,右子。本来创建的时候是一只猫,向上转型成动物
animal.eat();//猫吃鱼 //编译看左,动物类确实有,运行看右,右边覆盖重写了
// animal.catchMouse();错误写法,编译看左,父类无此方法
//向下转型进行还原动作===============================
Cat c = (Cat)animal;//本来就是一只猫,转为动物了,还原为本来的猫
c.catchMouse();
// Dog d = (Dog)animal;//本来new的时候是一只猫,现在向下转型成为一只狗,编译不报错,运行出现异常java.lang.ClassCastException 类转换异常
}
}
public abstract class Animal {//抽象类
public abstract void eat();//抽象方法
}
public class Cat extends Animal {
public void eat(){
System.out.println("猫吃鱼");
}
public void catchMouse(){//子类特有方法
System.out.println("猫抓老鼠");
}
}
public class Dog extends Animal {
@Override
public void eat (){
System.out.println("狗吃骨头");
}
public void watchHouse(){
System.out.println("狗看家");
}
}7.如何知道一个父类引用的对象,本类是什么子类?
- instanceof 关键字 - 返回值类型,Boolean
- 格式:
public class Demo02Instanceof {
public static void main(String[] args) {
Animal animal = new Cat();//本来是一只猫,向上转型为动物了,
animal.eat();//猫吃鱼
//希望调用子类特有方法,向下转型。
if (animal instanceof Dog) {//判断一下父类引用本来是不是Dog
Dog d = (Dog) animal;
d.watchHouse();
}
if (animal instanceof Cat) {//判断一下父类引用本来是不是Cat
Cat c = (Cat) animal;
c.catchMouse();
}
giveMeAPet(new Dog());
}
public static void giveMeAPet(Animal animal) {
if (animal instanceof Dog) {//判断一下父类引用本来是不是Dog
Dog d = (Dog) animal;
d.watchHouse();
}
if (animal instanceof Cat) {//判断一下父类引用本来是不是Cat
Cat c = (Cat) animal;
c.catchMouse();
}
}
}