8.Java基础知识:面向对象04

1、抽象类

1.1 概念:将abstract关键字写在class前面的类叫抽象类

抽象函数格式:

抽象函数没有函数体,连大括号都没有

抽象函数用abstract关键字修饰,写在返回值类型前面

如果一个函数描述不清,不知道具体该如何实现,就要定义为抽象函数;

如果一个类中存在抽象函数,这个类就是描述不清的,也要定义为抽象类

1.2 特点:

1.抽象类不能实例化(不能创建对象),只能由子类继承

2.子类继承抽象类,必须实现父类的抽象函数;否则子类也是抽象的

3.抽象函数不能直接使用,必须通过子类的继承使用

1.3 部分问题

1.抽象类一定是父类吗?

是的;

因为抽象类本来就是多个不同子类不断向上抽取,最终发现无法具体实现而得到的;

如果要使用抽象类,必须有子类继承它,然后实现所有抽象函数;

2.抽象类可以书写那些成员?

任意成员

3.抽象类中可以不书写抽象方法吗?

可以

4.抽象关键字abstract不能和哪些关键字共存?

private;final:因为抽象函数必须被子类重写; 而private和final修饰的函数都不能被重写;

static:因为static修饰的函数是静态的,不需要对象就可以直接使用;

如果抽象函数是静态的,也就可以直接通过类名调用;而调用一个抽象函数没有意义

5.一个类什么时候需要定义为抽象类?

1、类中存在抽象函数;

2、如果一个类中没有抽象函数,但是不希望被实例化,也需要定义为抽象类;

3、如果一个类不希望创建对象,就可以定义为一个抽象类;

6.抽象类可以继承非抽象类吗?

可以

2、多态

2.1 概念

指的就是同一个事物可以有多种不同的表示形态;

2.2 java中的多态

父类引用指向子类对象;(接口类型引用指向实现类对象)

2.3 使用前提

两个类型之间必须有继承关系(或者存在接口的实现关系)

2.4 类型转换

  • 自动向上转型

可以直接将子类型引用赋值给父类型变量,可以自动进行,叫做自动向上转型;

class  Fu{}
class Zi extends Fu{}
Zi zi = new Zi();

Fu f = zi;//这行代码就发生了自动向上转型
  • 强制向下转型

java中允许将父类型引用赋值给子类型变量,叫做向下转型;但是这种转型不能自动实现,需要强制进行,所以叫做强制向下转型;

class  Fu{}
class Zi extends Fu{}
Fu  f  = new Zi();//这行使用了多态,发生了向上转型;

Zi  z = (Zi)f; //这行发生了强制向下转型
  • 好处:

使用多态的形式,可以让一个函数可以接收不同子类的对象当参数,可以提高程序的扩展性(降低耦合性);可以提高代码复用性;

  • 弊端:

1.父类型引用不能使用子类独有属性和功能

2.编译期,编译器只会进行语法检查,不会启动JVM去允许

  • 解决弊端:

使用强制向下转型,将父类型引用转换为子类型引用;

2.5 成员使用的特点

编译期,全部成员都看父类的;运行期,非静态函数使用子类的,其它成员还用父类的;

2.6 instanceof关键字

使用格式:

引用类型的变量名  instanceof  类名

作用:表示判断左边这个变量指向的对象,是否是右边这个类的对象;返回的结果一定是一个布尔值;

2.7 示例:使用多态描述并测试猫和狗的行为

//使用多态描述并测试猫和狗的行为;

class Cat extends Animal{//表示猫
   //吃
   public void eat(){
     System.out.println("吃小鱼");
   }
   //睡
   public void sleep(){
     System.out.println("睡沙发");
   }
   //叫
   public void say(){
     System.out.println("喵喵");
   }
   //抓老鼠
   public void catchMouse(){
     System.out.println("抓老鼠");
   }
}

class Dog extends Animal{//表示狗
   //吃
   public void eat(){
     System.out.println("吃骨头");
   }
   //睡
   public void sleep(){
     System.out.println("睡狗窝");
   }
   //叫
   public void say(){
     System.out.println("汪汪");
   }
   //看家
   public void lookHome(){
     System.out.println("看家");
   }
}

abstract class Animal{//表示动物
   //吃
   public abstract void eat();
   //睡
   public abstract void sleep();
   //叫
   public abstract void say();
}

class Test{
	//测试动物的行为
   public static void testAnimal(Animal a){
     a.eat();
     a.sleep();
     a.say();
     //测试猫抓老鼠的功能
     if(a  instanceof  Cat ){//因为只有猫对象才能强制向下转型为猫,所以在转型之前先判断
         Cat c = (Cat)a;//因为catchMouse函数是Cat类中定义的,所以这里要使用,可以使用强制向下转型
         c.catchMouse();
     }
     /*
         测试狗看家的功能
         因为看家是狗特有的行为,动物类中没有这个函数,所以要对动物类型的变量a进行强制向下转型;
         因为这个函数不只是接收狗类型的对象,所以为了避免出先类型转换异常,
         所以强制类型转换之前先要进行判断,如果是狗的对象,就转型;
     */
     if(a instanceof Dog){
         Dog d = (Dog)a;
         d.lookHome();
     }
   }
   public static void main(String[] args) {
     //创建一个猫对象
     Animal c = new Cat();
     testAnimal(c);
     //分割
     System.out.println("------------------------");
     //创建一个狗的的对象
     Dog d = new Dog();
     testAnimal(d);
   }
}

3、接口

3.1 概念:

就是用来表示不属于一个继承体系的公共的行为的;

3.2 声明格式:

接口不是一个类,不使用class关键字声明,使用interface关键字声明;

interface Inter{}

3.3 能书写的成员

  • 成员变量:全部都使用public static final 修饰;所以接口中的成员变量都是常量;
  • 成员函数:全部都使用 public abstract修饰;
  • 接口中没有构造函数;
  • 从jdk8开始,接口中可以有默认实现的函数,使用关键字default修饰

3.4 多实现和多继承

  • 接口和类是实现的关系;
  • 接口和接口是继承的关系;
  • 多实现:一个类可以同时实现多个接口;
  • 多继承:一个接口可以同时继承多个父接口;接口也可以多重继承;

3.5 作用:

  • 描述非继承体系中的共性内容;
  • 通过多态使用接口,可以提高程序的扩展性;
  • 实现的java的多继承;
  • 接口可以定义规则;

4、接口和抽象类的区别

共同点:都可以有抽象函数,都不能实例化;

不同点

  • 从声明上

抽象类是一个类,需要使用关键字:class声明

接口不是一个类,使用关键字:interface声明

  • 从能够书写的成员上看

抽象类可以书写类中的所有成员

接口中只能书写成员变量和抽象函数;(从JDK8开始,接口中可以有实现的函数)

  • 从有无构造函数上看

抽象类有构造函数,是给子类实例化对象使用的

接口没有构造函数

  • 从作用上看

抽象类是描述的继承体系中的共同的特征和行为,如果行为不确定,就定义为抽象函数;

接口描述的是不属于继承体系的共同的功能;接口可以用来定义规则;

  • 继承关系上

抽象类是一个类,只支持单一继承和多重继承;

接口和接口可以多继承和多重继承;接口和实现类可以多实现;

5、适配器设计模式

5.1 概念

解决的是怎么将不符合使用要求的类、对象或接口转换为符合使用要求的;

5.2 作用

就是适配、转换,将不符合使用要求的东西转换为符合使用要求的;

5.3 分类

根据适配的对象不同,可以将适配器分为类的适配、对象的适配和接口的适配;

5.4 接口的适配的步骤

1.创建一个适配器类实现接口,在这个类中使用空实现实现接口中的所有抽象函数;

2.因为适配器类中的函数都是空实现的,创建该类的对象没有意义,所以要将适配器类定义为抽象类;

3.要使用适配器类,只需书写类继承适配器类即可;

interface Inter{//接口
   public void method1();
   public void method2();
   public void method3();
   public void method4();
   public void method5();
}

class User{//用户类
   /*
     要求:三个函数接收的具体的对象的类型不一样;
   */
   public static void test1(Inter i){
     i.method1();
     i.method2();
   }
   public static void test2(Inter i){
     i.method2();
   }
   public static void test3(Inter i){
     i.method4();
   }
}

/*
   思考:必须有接口的实现类,而且一个类实现接口,必须实现接口中所有抽象函数;
   因为实际使用的类,只会用到一个两个方法,所以不能让实际的类直接实现这个接口;
   所以可以考虑将接口的实现,和具体业务逻辑的实现,分开,不放在同一个类中;
   所以可以先定义一个类实现接口,实现接口中所有抽象函数,然后再这个类里面只是空实现,
   不具体实现任何业务逻辑,而是让需要具体实现的类继承这个类;
   
   这个Adaptor类,只是起到了一个连接接口和具体实现类的桥梁,是一个适配器类;
*/

//定义为抽象类的目的,是为了不能创建对象,因为这个类创建对象没有意义;
abstract class Adaptor implements Inter{
   public void method1(){}
   public void method2(){}
   public void method3(){}
   public void method4(){}
   public void method5(){}
}

class InterImpl1 extends Adaptor{
   //根据具体需要,重写自己需要的函数
   public void method1(){
     System.out.println("哈哈哈,11111");
   }
   public void method2(){
     System.out.println("嘿嘿嘿,22222");
   }
}
class InterImpl2 extends Adaptor{
   public void method2(){
     System.out.println("呵呵,22222");
   }
}
class InterImpl3 extends Adaptor{
   public void method4(){
     System.out.println("嘻嘻,44444");
   }
}

class Test{
   public static void main(String[] args) {
     //使用匿名对象传参
     User.test1(new InterImpl1());	
     User.test2(new InterImpl2());	
     User.test3(new InterImpl3());	
   }
}