JAVA 面对对象进阶- 下(九)

面对对象进阶- 下(九)

抽象方法和抽象类

抽象方法:将共性的行为(方法)抽取到父类之后,由于每个子类的执行的内容不一样,所以在父类中不能确定具体的方法体。该方法就可以定义为抽象方法。
抽象类:若一个类中存在抽象方法,那么该类就必须声明为抽象类。

定义格式

抽象方法的定义格式
public abstract 返回值类型 方法名(参数列表);
抽象类的定义格式
public abstract class 类名{}

注意事项

  • 抽象类不能实例化(不能创造对象)
  • 抽象类中不一定有抽象方法,但有抽象方法的类一定是抽象类
  • 可以有构造方法,交给子类,通过super访问
  • 抽象类的子类
    • 要么重写抽象类中的所有抽象方法
    • 要么是抽象类

abstract关键字冲突

  • final:被abstract修饰的方法,强制要求子类重写,被final修饰的方法子类不能重写
  • private:被abstract修饰的方法,强制要求子类重写,被private修饰的方法子类不能重写
  • static:被static修饰的方法可以类名调用,类名调用抽象方法没有意义

范例

//需求
青蛙frog    属性:名字,年龄    行为:吃虫子,喝水
狗dog       属性:名字,年龄    行为:吃骨头,喝水
山羊sheep   属性:名字,年龄    行为:吃草,喝水
//Animal.java
package com.ljsblog.domain2;
public abstract class Animal{
    private String name;
    private int age;
    public Animal(){}
    public Animal(String name,int age){
        this.name=name;
        this.age=age;
    }
    public void setName(String name){
        this.name=name;
    }
    public String getName(){
        return name;
    }
    public void setAge(int age){
        this.age=age;
    }
    public int getAge(){
        return age;
    }
    public abstract void eat();
    public void drink(){
        System.out.println("喝水");
    }
}
//Frog.java
package com.ljsblog.domain2;
public class Frog extends Animal {
    public Frog(){}
    public Frog(String name,int age){
        super(name,age);
    }
    @Override
    public void eat(){
        System.out.println("吃虫子");
    }
}

//Dog.java
package com.ljsblog.domain2;
public class Dog extends Animal{
    public Dog(){}
    public Dog(String name,int age){
        super(name,age);
    }
    @Override
    public void eat(){
        System.out.println("吃骨头");
    }
}

//Sheep.java
package com.ljsblog.domain2;
public class Sheep extends Animal{
    public Sheep(){}
    public Sheep(String name,int age){
        super(name,age);
    }
    @Override
    public void eat(){
        System.out.println("吃草");
    }
}

//Test.java
package com.ljsblog.domain2;
public class test {
    public static void main(String[] args) {
        Dog g = new Dog("傻狗", 18);
        Frog f = new Frog("癞蛤蟆", 20);
        Sheep s = new Sheep("喜洋洋", 3);
        g.eat();
        g.drink();
        f.eat();
        f.drink();
        s.eat();
        s.drink();
    }
}
/*
吃骨头
喝水
吃虫子
喝水
吃草
喝水
*/

接口

接口就是一种规则,是对行为的抽象。
思路:若存在一个类,所有的组成都是抽象方法。没有成员变量和普通方法,则该类通常会被设计成Java中的接口。因为现在这个类存在的唯一价值,就只是声明规则了。

定义和使用

  • 接口用关键字interface定义:
    • public interface 接口名{}
  • 接口不能实例化。
  • 接口与类之间是实现关系,通过implements关键字表示:
    • public class 类名 implements 接口名{}
  • 接口的子类(实现类):
    • 要么重写接口类中的所有抽象方法
    • 要么是抽象类

注意事项

  1. 接口与类可以单实现,也可以多实现
    • public class 类名 implements 接口名1,接口名2…{}
  2. 实现类可以在继承一个类的同时实现多个接口
    • public class 类名 extends 父类名 implements 接口名1,接口名2…{}

范例

//需求
青蛙    属性:名字,年龄    行为:吃虫子,蛙泳
狗      属性:名字,年龄    行为:吃骨头,狗刨
兔子    属性:名字,年龄    行为:吃胡萝卜
//父类
//Animal.java
package com.ljsblog.domain2;
public abstract class Animal{
    private String name;
    private int age;
    public Animal(){}
    public Animal(String name,int age){
        this.name=name;
        this.age=age;
    }
    public void setName(String name){
        this.name=name;
    }
    public String getName(){
        return name;
    }
    public void setAge(int age){
        this.age=age;
    }
    public int getAge(){
        return age;
    }
    public abstract void eat();
}
//接口
//Regularity
package com.ljsblog.domain2;
public interface Regularity{
    public abstract void swim();
}
//子类
//Frog.java
package com.ljsblog.domain2;
public class Frog extends Animal implements Regularity {
    public Frog(){}
    public Frog(String name,int age){
        super(name,age);
    }
    @Override
    public void eat(){
        System.out.println("吃虫子");
    }
    @Override
    public void swim(){
        System.out.println("蛙泳");
    }
}

//Dog.java
package com.ljsblog.domain2;
public class Dog extends Animal implements Regularity{
    public Dog(){}
    public Dog(String name,int age){
        super(name,age);
    }
    @Override
    public void eat(){
        System.out.println("吃骨头");
    }
    @Override
    public void swim(){
        System.out.println("狗刨");
    }
}

//Rabbit.java
package com.ljsblog.domain2;
public class Rabbit extends Animal{
    public Rabbit(){}
    public Rabbit(String name,int age){
        super(name,age);
    }
    @Override
    public void eat(){
        System.out.println("吃胡萝卜");
    }
}
//测试类
//Test.java
package com.ljsblog.domain2;
public class Test{
    public static void main(String[] args){
        Frog f=new Frog();
        f.eat();
        f.swim();
        Dog d=new Dog();
        d.eat();
        d.swim();
        Rabbit r=new Rabbit();
        r.eat();
    }
}
/*
吃虫子
蛙泳
吃骨头
狗刨
吃胡萝卜
*/

接口中成员的特点

  • 成员变量
    • 只能是常量,默认修饰符:public static final,修饰符可以省略
  • 构造方法-无
  • 成员方法
    • 只能是抽象方法,默认修饰符:public abstract,修饰符可以省略

接口与类的关系

  • 类与类的关系
    • 继承关系。只能单继承,不能多继承,但可多层继承
  • 类与接口的关系
    • 实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
    • 若多个接口中有重名的方法,只需重写一次
  • 接口与接口的关系
    • 继承关系,可以单继承,也可以多继承
    • 若实现最下方的子接口,则需要重写所有的抽象方法

抽象类和接口的对比

  • 成员变量
    • 抽象类:可定义变量和常量
    • 接口:只可定义常量
  • 成员方法
    • 抽象类:可定义具体方法和抽象方法
    • 接口:只能定义抽象方法
  • 构造方法
    • 抽象类:有
    • 接口:无

抽象类对事务做抽象(描述事物)
接口对行为抽象(指定规则)

范例

//需求
乒乓球运动员:  姓名,年龄,学打乒乓球,说英语
篮球运动员:    姓名,年龄,学打篮球
乒乓球教练:    姓名,年龄,教打乒乓球,说英语
篮球教练:      姓名,年龄,教打篮球
//Person.java
package com.ljsblog.domain3;
public class Test {
    public static void main(String[] args){
        PingAthlete pa=new PingAthlete("张三",16);
        pa.study();
        pa.speakEnglish();
        BasketballAthele ba=new BasketballAthele("李四",16);
        ba.study();
        PingTeacher pt=new PingTeacher("王五一",26);
        pt.teach();
        pt.speakEnglish();
        BasketBallTeacher bt=new BasketBallTeacher("王五二",26);
        bt.teach();
    }
//Athlete.java
package com.ljsblog.domain3;
public abstract class Athlete extends Person{
    public Athlete(){}
    public Athlete(String name,int age){
        super(name,age);
    }
    public abstract void study();
}

//Teacher.java
package com.ljsblog.domain3;
public abstract class Teacher extends Person{
    public Teacher(){}
    public Teacher(String name,int age){
        super(name,age);
    }
    public abstract void teach();
}
//Speak.java
package com.ljsblog.domain3;
public interface Speak{
    public abstract void speakEnglish();
}
//BasketballAthele.java
package com.ljsblog.domain3;
public class BasketballAthele extends Athlete{
    public BasketballAthele(){}
    public BasketballAthele(String name,int age){
        super(name,age);
    }
    @Override
    public void study(){
        System.out.println("年龄为"+getAge()+"的篮球运动员"+getName()+"学打篮球");
    }
}

//BasketBallTeacher.java
package com.ljsblog.domain3;
public class BasketBallTeacher extends Teacher{
    public BasketBallTeacher(){}
    public BasketBallTeacher(String name,int age){
        super(name,age);
    }
    @Override
    public void teach(){
        System.out.println("年龄为"+getAge()+"的篮球教练"+getName()+"教打篮球");
    }
}

//PingAthlete.java
package com.ljsblog.domain3;
public class PingAthlete extends Athlete implements Speak{
    public PingAthlete(){}
    public PingAthlete(String name,int age){
        super(name,age);
    }
    @Override
    public void study(){
        System.out.println("年龄为"+getAge()+"的乒乓球运动员"+getName()+"学打乒乓球");
    }
    @Override
    public void speakEnglish(){
        System.out.println("乒乓球运动员在说英语");
    }
}

//PingTeacher.java
package com.ljsblog.domain3;
public class PingTeacher extends Teacher implements Speak{
    public PingTeacher(){}
    public PingTeacher(String name,int age){
        super(name,age);
    }
    @Override
    public void teach(){
        System.out.println("年龄为"+getAge()+"的乒乓球教练"+getName()+"教打乒乓球");
    }
    @Override
    public void speakEnglish(){
        System.out.println("乒乓球教练在说英语");
    }
}
//测试类
package com.ljsblog.domain3;
public class Test {
    public static void main(String[] args){
        PingAthlete pa=new PingAthlete("张三",16);
        pa.study();
        pa.speakEnglish();
        BasketballAthele ba=new BasketballAthele("李四",16);
        ba.study();
        PingTeacher pt=new PingTeacher("王五一",26);
        pt.teach();
        pt.speakEnglish();
        BasketBallTeacher bt=new BasketBallTeacher("王五二",26);
        bt.teach();
    }
}
/*
年龄为16的乒乓球运动员张三学打乒乓球
乒乓球运动员在说英语
年龄为16的篮球运动员李四学打篮球
年龄为26的乒乓球教练王五一教打乒乓球
乒乓球教练在说英语
年龄为26的篮球教练王五二教打篮球
*/

JDK8后接口新增方法

默认方法

允许在接口中定义默认方法,需使用关键字default修饰,以此解决接口升级的问题。
接口中默认方法定义格式
public default 返回值类型 方法名(参数列表){}
注意事项

  • 默认方法不是抽象方法,不强制被重写;若要重写,重写时去掉default关键字。
  • public可省,default不可省
  • 若实现多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写。
  • 若重写默认方法需要使用接口的原代码,需要在重写时编写:接口名.super.方法名()

范例

//接口
public interface Inter{
    public abstract void show1();
    public default void show2(){
        System.out.println("默认方法");
    }
}
public class A implements Inter{
    @Override
    public void show1(){
        System.out.println("抽象方法");
    }
}
public class Test{
    public static void main(String[] args){
        A a=new A();
        a.show1();
        a.show2();
    }
}
/*
抽象方法
默认方法
*/
静态方法

允许在接口中定义静态方法,需要用static修饰。
定义格式
public static 返回值类型 方法名(参数列表){}
注意事项

  • 静态方法只能用接口名调用,不能通过实现类名或者对象名调用
  • public可省,static不能省
  • 静态方法不能被重写

范例

//接口
public interface Inter{
    public static void show(){
        System.out.println("静态方法");
    }
}
//测试类
//Test.java
public class Test{
    public static void main(String[] args){
        Inter.show();//静态方法
    }
}

JDK9接口特性

接口中允许定义private私有方法,以提高代码复用性。
格式1
private 返回值类型 方法名(参数列表){}
格式2:
private static 返回值类型 方法名(参数列表){}
范例

public interface Test {
    public abstract void a();
    int b();

	//格式1
    public default void print1(){
        print();
        System.out.println("1");
    }
    default void print2(){
        print();
        System.out.println("2");
    }
    private void print(){
        System.out.println("重复代码");
    }
	//格式2
    public static void print3(){
        printStatic();
        System.out.println("1");
    }
    static void print4(){
        printStatic();
        System.out.println("2");
    }
    private static void printStatic(){
        System.out.println("静态");
    }

}

应用

  1. 接口代表规则,是行为的抽象,想让哪个类拥有一个行为,就让这个类实现对应的接口就可以了。
  2. 当一个方法参数是接口时,可传递接口所有的实现类的对象,这种方式称为接口多态。

设计模式

设计模式简单来说就是解决问题的各种套路。

适配器设计模式

当一个接口中抽象方法过多,但只要使用其中一部分时,就可以使用适配器设计模式
书写步骤

  • 编写中间类XXXAdapter,实现对应的接口
  • 对接口中的抽象方法进行空实现,即方法体为空
  • 让真正的实现类继承中间类,并重写需要用的方法
  • 为避免其他类创建适配器类的对象,适配器类用abstract修饰。

范例

//接口
public interface Inter{
    public abstract void show1();
    public abstract void show2();
    public abstract void show3();
    public abstract void show4();
    public abstract void show5();
    public abstract void show6();
}
//适配器类
public abstract class InterAdapter implements Inter{

    @Override
    public void show1() {
        
    }

    @Override
    public void show2() {

    }

    @Override
    public void show3() {

    }

    @Override
    public void show4() {

    }

    @Override
    public void show5() {

    }

    @Override
    public void show6() {

    }
}
//实现类
public class Im extends InterAdapter{
    @Override
    public void show3(){
        System.out.println("方法3");
    }
}
//测试类
package com.ljsblog.domain5;
public class Test {
    public static void main(String[] args){
        Im i=new Im();
        i.show3();//方法3
    }
}

模板设计模式

模板设计模式:把抽象类整体就可以看成一个模板,模板中不能决定的东西定义成抽象方法,让使用模板的类(继承抽象类的类)去重写抽象方法实现需求。
作用:模板已经定义通用结构,使用者只需关心自己需要实现的功能即可。
范例

//模板
public abstract class Eat {
    //固定模板,子类不能重写,加final
    public final void eatFood(){
        System.out.println("做饭");
        food();
        System.out.println("刷碗");
    }
    public abstract void food();
}

//使用模板的类
public class EatFruit extends Eat{
    @Override
    public void food() {
        System.out.println("吃水果");
    }
}
public class EatMeat extends Eat{
    @Override
    public void food() {
        System.out.println("吃肉");
    }
}

//测试类
public class Test {
    public static void main(String[] args) {
        EatFruit eatFruit=new EatFruit();
        eatFruit.eatFood();
        System.out.println("=================");
        EatMeat eatMeat=new EatMeat();
        eatMeat.eatFood();
    }
}
/*
做饭
吃水果
刷碗
=================
做饭
吃肉
刷碗
*/

内部类

类的五大成员

  1. 属性
  2. 方法
  3. 构造方法
  4. 代码块
  5. 内部类

内部类定义:在一个类中,再定义一个类,例如,在A类内部定义B类,B类称为内部类。
内部类表示的事物是外部类的一部分,内部类单独出现没有任何意义。
访问特点

  • 内部类可以直接访问外部类的成员,包括私有
  • 外部类要访问内部类的成员,必须创建对象

应用场景:B类表示的事物是A类的一步,且B类存在没有任何意义。例如:ArrayList迭代器,汽车的发动机,人的心脏。
分类

  • 成员内部类
  • 静态内部类
  • 局部内部类
  • 匿名内部类(重点)

成员内部类

写在成员位置,即类中方法外,属于外部类的成员。
成员内部类可以被一些修饰符修饰,比如:private,默认,protected,public,static等。
在成员内部类中,JDK16之前不能定义静态变量,JDK16开始才可以定义静态变量。
获取内部类对象方式

  1. 在外部类中编写方法,对外提供内部类的对象。(private)
  2. 直接创建,格式:外部类名.内部类名 对象名=外部类对象.内部类对象;

范例
方法一

package com.ljsblog.domain5;
public class Outer{
    public Inner getInner(){
        return new Inner();
    }
    private class Inner{
        public void show(){
        }
    }
}
package com.ljsblog.domain5;
public class Test{
    public static void main(String[] args){
        Outer o=new Outer();
        System.out.println(o.getInner());
    }
}

方法二

package com.ljsblog.domain5;
public class Outer{
    public Inner getInner(){
        return new Inner();
    }
    public class Inner{
        public void show(){
        }
    }
}
package com.ljsblog.domain5;
public class Test{
    public static void main(String[] args){
        Outer.Inner oi=new Outer().new Inner();
        System.out.println(oi);
    }
}

当外部类成员变量和内部类成员变量重名时,内部类访问外部类成员变量:
System.out.println(Outer.this.变量名);
范例:

package com.ljsblog.domain5;
public class Outer{
    private int a=1;
    class Inner{
        private int a=2;
        public void show(){
            int a=3;
            System.out.println(Outer.this.a);//1
            System.out.println(this.a);//2
            System.out.println(a);//3
        }
    }
}
package com.ljsblog.domain5;
public class Test{
    public static void main(String[] args){
        Outer.Inner oi=new Outer().new Inner();
        oi.show();
    }
}

静态内部类

静态内部类只能访问外部类中的静态变量和静态方法,若要访问非静态的需要创建对象。
创建静态内部类对象的格式
外部类名.内部类名 对象名=new 外部类名.内部类名();
调用非静态方法的格式:先创建对象,用对象调用。
调用静态方法的格式
外部类名.内部类名.方法名();
范例

package com.ljsblog.domain5;
public class Outer{
    int a=10;
    static int b=20;
    static class Inner{
        public static void show1(){
            System.out.println("静态方法");
            Outer o=new Outer();
            System.out.println(o.a);
            System.out.println(b);
        }
        public void show2(){
            System.out.println("非静态方法");
            Outer o=new Outer();
            System.out.println(o.a);
            System.out.println(b);
        }
    }
}
package com.ljsblog.domain5;
public class Test{
    public static void main(String[] args){
        Outer.Inner oi=new Outer.Inner();
        Outer.Inner.show1();
        oi.show2();
    }
}
/*
静态方法
10
20
非静态方法
10
20
*/

局部内部类

  1. 将内部类定义在方法,代码块,构造器等执行体中就叫局部内部类,类似于方法中的局部变量
  2. 外界是无法直接使用,需要在方法内部创建对象并使用
  3. 该类可以直接访问外部类的成员,也可方法方法内部的局部变量
  4. 因为局部内部类没啥用,所以就不举例了。

匿名内部类

匿名内部类本质上就是隐藏了名字的内部类,并非真的没有名字。
格式

new 类名/接口名(){
    重写方法;
}
//其中包含了继承或实现,方法重写,创建对象,整体就是一个类的子类或者接口的实现类对象

使用场景:当方法的参数是接口或类时,以接口为例,可传递该接口的实现类对象,若实现类仅使用一次,可用匿名内部类简化代码。
范例

//接口
public abstract interface Inter{
    public abstract void speakEnglish();
}
//父类
public abstract class Animal{
    public abstract void eat();
}
//测试类
public class Test{
    public static void main(String[] args){
        Inter i=new Inter(){
            @Override
            public void speakEnglish(){
                System.out.println("讲英语");
            }
        };
        i.speakEnglish();
        Animal a=new Animal(){
            @Override
            public void eat(){
                System.out.println("狗吃骨头");
            }
        };
        a.eat();
    }
}
/*
讲英语
狗吃骨头
*/

Lambda表达式

Lambda表达式是JDK8开始后出现的一种新语法形式。

格式

()->{

}
//() 对应方法的形参
//-> 固定格式
//{} 对应着方法的方法体

基本作用:简化函数化接口的匿名内部类的写法。

函数化接口(函数式编程接口):有且仅有一个抽象方法的接口叫做函数化接口,接口上方可以加@FunctionalInterface注解。

使用前提:必须是接口的匿名内部类,接口中只能有一个抽象方法。

好处:Lambda是一个匿名函数,可以把Lambda表达式理解为一段可以传递的代码,它可以写成更简洁,更灵活的代码。

省略写法

省略核心:可推导,可省略。

  • 参数类型可省略
  • 若只有一个参数,参数类型和()均可省(若没有参数,小括号不能省略)
  • 若Lambda表达式的方法体只有一行,大括号,分号,若这行代码是return语句,则return可省略,但必须同时省略。
对比范例一
import java.util.Arrays;
import java.util.Comparator;
public class Test{
    public static void main(String[] args){
        Integer[] arr1={69,42,65,89,26,32,16};
        //原版
        Arrays.sort(arr1,new Comparator<Integer>(){
            @Override
            public int compare(Integer o1,Integer o2){
                return o1-o2;
            }

        });
        System.out.println(Arrays.toString(arr1));


        //Lambda表达式
        Integer[] arr2={69,42,65,89,26,32,16};
        Arrays.sort(arr2,(Integer o1,Integer o2)->{
            return o1-o2;
        });
        System.out.println(Arrays.toString(arr2));

        //Lambda表达式省略版
        Integer[] arr3={69,42,65,89,26,32,16};
        Arrays.sort(arr3,(o1,o2)->o1-o2);
        System.out.println(Arrays.toString(arr3));
    }
}
/*
[16, 26, 32, 42, 65, 69, 89]
[16, 26, 32, 42, 65, 69, 89]
[16, 26, 32, 42, 65, 69, 89]
*/
对比范例二
public class Test{
    public static void main(String[] args){
        //原版
        method("小白",new Swim(){
            @Override
            public void swiming(String name){
                System.out.println(name+"游泳");
            }
        });

        //Lambda表达式
        method("小白",(String name)->{
            System.out.println(name+"游泳");
        });

        //Lambda表达式省略版
        method("小白",name->System.out.println(name+"游泳"));
    }
    public static void method(String name,Swim s){
        s.swiming(name);
    }
}
@FunctionalInterface
interface Swim{
    public abstract void swiming(String name);
}
/*
小白游泳
小白游泳
小白游泳
*/
范例
/*
定义数组并存储一些字符串,利用Arrays中的sort方法进行排序
要求:
	按照字符串中的长度排序,从短到长。
*/
import java.util.Arrays;
import java.util.Comparator;
public class Test{
    public static void main(String[] args) {
        String[] arr={"aaaa","a","aaaaa","aaa","aaaaaaaaaaa"};
        //原版
        /*
        Arrays.sort(arr,new Comparator<String>(){
            @Override
            public int compare(String o1, String o2) {
                return o1.length()-o2.length();
            }
        });
        */
        //Lambda版
        /*
        Arrays.sort(arr,(String o1,String o2)->{
            return o1.length()-o2.length();
        });
        */
        //Lambda省略版
        Arrays.sort(arr,(o1,o2)->o1.length()-o2.length());
        System.out.println(Arrays.toString(arr));
    }
}
/*
[a, aaa, aaaa, aaaaa, aaaaaaaaaaa]
*/

Lambda表达式和匿名内部类的区别

  • 使用限制不同
    • 匿名内部类:可以操作类,接口
    • Lambda表达式:只能操作函数式接口
  • 实现原理不同
    • 匿名内部类:编译之后,会产生一个单独的.class字节码文件
    • Lambda表达式:编译之后,没有一个单独的.class字节码文件
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值