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());
}
}