JAVA——面向对象进阶(抽象类、接口、内部类)

抽象类

抽象方法

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

抽象类和抽象方法的定义格式

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

 示例:

package com.itheima.test39;

public abstract class Person {
    public abstract void work();
}

抽象类和抽象方法的注意事项

  • 抽象类不能实例化
  • 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
    • package com.itheima.test39;
      
      public abstract class Person {
      //    public abstract void work();
          public void sleep() {
              System.out.println("睡觉");
          }
      }
      
  • 可以有构造方法
    • package com.itheima.test39;
      
      public abstract class Person {
          private String name;
          private int age;
      
          // 创建子类对象时,给属性进行赋值
          public Person() {
          }
      
          public Person(String name, int age) {
              this.name = name;
              this.age = age;
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      
          public int getAge() {
              return age;
          }
      
          public void setAge(int age) {
              this.age = age;
          }
      
          public abstract void work();
          public void sleep() {
              System.out.println("睡觉");
          }
      }
      
  • 抽象类的子类
    • 要么重写抽象类的所有抽象方法(更多使用)
      • package com.itheima.test39;
        
        public class Student extends Person{
            public Student() {
            }
        
            public Student(String name, int age) {
                super(name, age);
            }
        
            @Override
            public void work() {
                System.out.println("学生正在学习");
            }
        
        }
        
    • 要么是抽象类
      • package com.itheima.test39;
        
        public abstract class Teacher extends Person{
        }
        
package com.itheima.test39;

public class Test {
    public static void main(String[] args) {
        Student s = new Student("zhangsan", 20);

        System.out.println(s.getName() + "," + s.getAge());
    }
}

运行结果:

因此,可知,如果想创建一个对象,就必须定义一个类继承抽象类,并重写抽象类中所有的抽象方法,才可以对其进行实例化。抽象类中的构造方法起到创建子类对象时,给属性进行赋值的作用。

抽象类和抽象方法的意义

抽取共性时,如果无法确定方法体,就将方法定义为抽象的,并且强制让子类按照这种格式进行重写

编写带有抽象类的标准Javabean类

接口 

接口的定义和使用

  • 接口用关键字interface来定义        
    • public interface 接口名 {}
  • 接口不能实例化
  • 接口与类之间是实现关系,通过implements关键字表示
    • public class 类名 implements 接口名 {}
  • 接口的子类(实现类)
    • 要么重写接口中所有的抽象方法
    • 要么是抽象类
  • 注意:
    • 接口和类的实现关系,可以单实现,也可以多实现。
      • public class 类名 implements 接口名1,接口名2 {}
    •  实现类可以在继承一个类的同时实现多个接口
      • public class 类名 extends 父类 implements 接口名1,接口名2 {}

 编写带有接口和抽象类的标准Javabean类

package com.itheima.test40;

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 String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public abstract void eat();
}
package com.itheima.test40;

public interface Swim {
    public abstract void swim();
}
package com.itheima.test40;

public class Rabbit extends Animal{
    public Rabbit() {
    }

    public Rabbit(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println("吃胡萝卜");
    }
}
package com.itheima.test40;

public class Dog extends Animal implements Swim{
    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("狗刨");
    }
}
package com.itheima.test40;

public class Frog extends Animal implements Swim{
    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("蛙泳");
    }
}
package com.itheima.test40;

public class Test {
    public static void main(String[] args) {
        Rabbit r = new Rabbit("lhj", 20);
        System.out.println(r.getName() + "," + r.getAge());
        r.eat();

        Frog f = new Frog("fjq", 21);
        System.out.println(f.getName() + "," + f.getAge());
        f.eat();
        f.swim();
    }
}

运行结果:

接口中成员的特点

  • 成员变量
    • 只能是常量
    • 默认修饰符:public static final
  • 构造方法
    • 没有
  • 成员方法
    • 只能是抽象方法
    • 默认修饰符:public abstract
  • JDK7以前:接口中只能定义抽象方法
  • JDK8的新特性:接口中可以定义有方法体的方法
  • JDK9的新特性:接口中可以定义私有方法

接口和类之间的关系

  • 类和类的关系:
    • 继承关系,只能单继承,不能多继承,但是可以多层继承
  • 类和接口的关系:
    • 实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
  • 如果实现多个接口的时候,发现不同接口里面有同名方法,只用重写一次抽象方法即可,无需重复重写

示例:Inter1中有抽象方法method1、method2、method3;Inter2中有抽象方法method1、method2、method3、method4,Test实现Inter1、Inter2时,只需要重写四个抽象方法即可

package com.itheima.test42;

public interface Inter1 {
    public abstract void method1();
    public abstract void method2();
    public abstract void method3();
}
package com.itheima.test42;

public interface Inter2 {
    public abstract void method1();
    public abstract void method2();
    public abstract void method3();
    public abstract void method4();
}
package com.itheima.test42;

public class Test implements Inter1, Inter2 {

    @Override
    public void method1() {

    }

    @Override
    public void method2() {

    }

    @Override
    public void method3() {

    }

    @Override
    public void method4() {

    }
}
  • 接口和接口的关系:
    • 继承关系,可以单继承,也可以多继承
  • 但是如果一个实现类实现类最下面的子接口,那么就需要重写子接口所继承的所有的接口的抽象方法(包含子接口自身的抽象方法)

示例:Inter3继承Inter1、Inter2,Test实现Inter3的同时,不仅需要将Inter3中的抽象方法重写,还需要将Inter1、Inter2中的抽象方法重写

package com.itheima.test41;

public interface Inter1 {
    public abstract void method1();
}
package com.itheima.test41;

public interface Inter2 {
    public abstract void method2();
}
package com.itheima.test41;

public interface Inter3 extends Inter1, Inter2 {
    public abstract void method3();
}
package com.itheima.test41;

public class Test implements Inter1, Inter2, Inter3 {
    @Override
    public void method1() {

    }

    @Override
    public void method2() {

    }

    @Override
    public void method3() {

    }
}

编写带有接口和抽象类的标准Javabean类

 

package com.itheima.test43;

// 之所以定义Person类为抽象类
// 是因为直接创建Person类的对象没有意义
public abstract class Person {
    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
package com.itheima.test43;

public abstract class Sporter extends Person{
    public Sporter() {
    }

    public Sporter(String name, int age) {
        super(name, age);
    }

    public abstract void study();
}
package com.itheima.test43;

public abstract class Coach extends Person{
    public Coach() {
    }

    public Coach(String name, int age) {
        super(name, age);
    }

    public abstract void teach();
}
package com.itheima.test43;

public interface SpeakEnglish {
    public abstract void speakEnglish();
}
package com.itheima.test43;

public class PingPongPlayer extends Sporter implements SpeakEnglish{
    public PingPongPlayer() {
    }

    public PingPongPlayer(String name, int age) {
        super(name, age);
    }

    @Override
    public void study() {
        System.out.println("乒乓球运动员正在学打乒乓");
    }


    @Override
    public void speakEnglish() {
        System.out.println("乒乓球运动员正在学习英语");
    }
}
package com.itheima.test43;

public class BasketballPlayer extends Sporter{
    public BasketballPlayer() {
    }

    public BasketballPlayer(String name, int age) {
        super(name, age);
    }

    @Override
    public void study() {
        System.out.println("篮球运动员正在学打篮球");
    }

}
package com.itheima.test43;

public class PingPongCoach extends Coach implements SpeakEnglish{
    public PingPongCoach() {
    }

    public PingPongCoach(String name, int age) {
        super(name, age);
    }

    @Override
    public void teach() {
        System.out.println("乒乓球教练正在教打乒乓");
    }

    @Override
    public void speakEnglish() {
        System.out.println("乒乓球教练正在学习英语");
    }
}
package com.itheima.test43;

public class PingPongCoach extends Coach implements SpeakEnglish{
    public PingPongCoach() {
    }

    public PingPongCoach(String name, int age) {
        super(name, age);
    }

    @Override
    public void teach() {
        System.out.println("乒乓球教练正在教打乒乓");
    }

    @Override
    public void speakEnglish() {
        System.out.println("乒乓球教练正在学习英语");
    }
}
package com.itheima.test43;

public class Test {
    public static void main(String[] args) {
        PingPongPlayer pp = new PingPongPlayer("zhangsan", 20);
        System.out.println(pp.getName() + "," + pp.getAge());
        pp.study();
        pp.speakEnglish();

        BasketballCoach bc = new BasketballCoach("lisi", 41);
        System.out.println(bc.getName() + "," + bc.getAge());
        bc.teach();
    }
}

运行结果:

JDK8开始接口中新增的方法

  • JDK7以前:接口只能定义抽象方法
  • JDK8的新特性:接口中可以定义有方法体的方法。(默认、静态)
  • JDK9的新特性:接口中可以定义私有方法

默认方法

允许在接口中定义默认方法,需要使用关键字default修饰

  • 作用:解决接口升级的问题

接口中默认方法的定义格式:

  • 格式:public default 返回值类型 方法名(参数列表) { }
  • 范例:public default void show() { }

接口中默认方法的注意事项

  • 默认方法不是抽象方法,所以不强制被重写。但是如果被重写,重写的时候去掉default关键字
    • package com.itheima.test44;
      
      public interface Inter1 {
          public abstract void method();
          public default void show() {
              System.out.println("接口里面的默认方法————show");
          }
      }
      
      package com.itheima.test44;
      
      public class InterImp implements Inter1{
      
          @Override
          public void method() {
              
          }
      }
      

      可以看到default关键字修饰的默认方法并不强制重写

    • 修改一下InterImp里面的代码

    • package com.itheima.test44;
      
      public class InterImp implements Inter1{
      
          @Override
          public void method() {
      
          }
      
          @Override
          public void show() {
              Inter1.super.show();
              System.out.println("接口实现类的show方法");
          }
      }
      

      可以看到重写默认方法的时候default关键字去掉了

    • package com.itheima.test44;
      
      public class Test {
          public static void main(String[] args) {
              InterImp i = new InterImp();
              i.show();
          }
      }
      

      运行结果:

                

  • public可以省略,但default不能省略
  • 如果实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写

 可以看到InterImp同时实现的Inter1、Inter2接口中存在同名的默认方法show,InterImp就必须对默认方法show进行重写

静态方法

允许在接口中定义静态方法,需要用static修饰

接口中静态方法的定义格式:

  • 格式:public static 返回值类型 方法名(参数列表) { }
  • 范例:public static void show() { }

接口中静态方法的注意事项

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

可以看到哪怕是在Inter的实现类InterImp中写了一个show方法,也不是对Inter内静态方法show的重写,顶多算是有同名的方法,并且接口中的静态方法show只能用接口名调用

运行结果:

JDK9新增的方法

  • 之所以会有私有方法的出现,就是在一个接口当中,定义的多个默认方法的方法体中存在相同的代码,就会将这行代码提取出来,定义成一个私有方法,再以”方法名()“的形式进行调用
    • package com.itheima.test47;
      
      public interface Inter {
          public default void show1() {
              System.out.println("show1");
      //        System.out.println("这是一行代码");
              log();
          }
      
          public default void show2() {
              System.out.println("show2");
      //        System.out.println("这是一行代码");
              log();
          }
          
          private void log() {
              System.out.println("这是一行代码");
          }
      }
      
  • 若是定义的多个静态方法的方法体中存在相同的代码,也会将这行代码提取出来,定义成一个私有方法,再以”方法名()“的形式进行调用
    • package com.itheima.test47;
      
      public interface Inter {
      
          public static void show3() {
              System.out.println("show3");
      //        System.out.println("这是一行代码");
              log1();
          }
      
          public static void show4() {
              System.out.println("show4");
      //        System.out.println("这是一行代码");
              log1();
          }
      
          private static void log1() {
              System.out.println("这是一行代码");
          }
      }
      

接口中私有方法的定义格式:

为默认方法服务
  • 格式1:private 返回值类型 方法名(参数列表) { }
  • 范例1:private void show() { }
为静态方法服务
  • 格式2:private static 返回值类型 方法名(参数列表) { }
  • 范例2:private static void method() { }

接口总结

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

适配器设计模式

  • 当一个接口中抽象方法过多,但是我一直要使用其中一部分的时候,就可以使用适配器设计模式
  • 书写步骤:
    • 编写中间类XXXAdapter,实现对应的接口
    • 对接口中的抽象方法进行空实现
    • 让真正的实现类继承中间类,并重写需要用的方法
    • 为了避免其他类创建适配器类的对象,中间的适配器类用abstract进行修饰

内部类

内部类概述

在一个类的里面,再定义一个类

举例:在A类的内部定义B类,B类就被称为内部类

package com.itheima.test49;

public class Car {
    private String carName;
    private int carAge;
    private String carColor;
    
    // 但是在外部类中并不能直接访问内部类中的成员
    // 必须先在外部类当中创建内部类的对象
    public void show() {
        Engine e = new Engine();
        System.out.println(carAge + "," + e.engineAge);
    }

    class Engine {
        String engineName;
        int engineAge;
        
        // 内部类可以直接访问外部类中的成员,包括私有的成员
        public void show() {
            System.out.println(carName + "," + engineName);
        }
    }
}

内部类的分类

成员内部类

 获取成员内部类的两种方式
  • 方式一:当成员内部类被private修饰时。在外部类中编写方法,对外提供内部类对象
package com.itheima.test51;

public class Outer {
        private int a;
        private class Inner {
            public void show() {
                System.out.println("Inner的show");
            }
        }

        public Inner getInnerImp() {
            return new Inner();
    }
}
package com.itheima.test51;


public class Test {
    public static void main(String[] args) {
        Outer o = new Outer();
        Object i = o.getInnerImp();
        System.out.println(i);

    }
}
  • 方式二:当成员内部类被非私有修饰时,直接创建对象。
    • Outer.Inner oi = new Outer().new Inner();
package com.itheima.test50;

public class Outer {
    private int a;
    class Inner {
        public void show() {
            System.out.println("Inner的show");
        }
    }
}
package com.itheima.test50;

public class Test {
    public static void main(String[] args) {
        Outer.Inner oi = new Outer().new Inner();
        oi.show();
    }
}
外部类成员变量和内部类成员变量重名时,在内部类如何访问?
System.out.println(Outer.this.变量名);

package com.itheima.test52;

public class Outer {
    private int a = 10;

    class Inner {
        int a = 20;
        public void show(){
            int a = 30;
            System.out.println(Outer.this.a);
            System.out.println(this.a);
            System.out.println(a);
        }
    }

}
package com.itheima.test52;

public class Test {
    public static void main(String[] args) {
        Outer.Inner oi = new Outer().new Inner();
        oi.show();
    }
}

静态内部类

  • 静态内部类只能访问外部类中的静态变量和静态方法,如果想要访问外部类当中非静态的成员需要创建对象
  • 静态内部类和成员内部类的区别就在于,静态内部类是以static关键词修饰的成员内部类

  • 创建静态内部类对象的格式:外部类名.内部类名 对象名 = new 外部类名.内部类名();
  • 相比于创建成员内部类,静态内部类的创建无需再创建外部类的对象
如何调用静态内部类中的方法
  • 调用非静态方法的格式:先创建对象,用对象调用
  • 调用静态方法的格式:外部类名.内部类名.方法名();
package com.itheima.test53;

public class Outer {

    private static int a = 10;

    static class Inner {
        public static void show1() {
            System.out.println("静态内部类中的静态方法");
        }

        public void show2() {
            System.out.println("静态内部类中的非静态方法");
        }
    }
}

package com.itheima.test53;

public class Test {
    public static void main(String[] args) {
        Outer.Inner.show1();
        Outer.Inner oi = new Outer.Inner();
        oi.show2();
    }
}

 运行结果:

局部内部类

  • 将内部类定义在方法里面就叫做局部内部类,类似于方法里面的局部变量
  • 外界无法直接使用,需要在方法内部创建对象并使用
  • 该类可以直接访问外部类的对象,也可以访问方法内的局部变量
package com.itheima.test54;

public class Outer {
    private int a = 10;

    public void show() {

        class Inner {
            int b = 20;

            public void method1() {
                System.out.println(a);
                System.out.println(b);
                System.out.println("局部内部类中的非静态方法");
            }

            private static void method2() {
                System.out.println("局部内部类中的静态方法");
            }
        }

        // 如果需要调用局部内部类,就需要在方法中对其实例化
        Inner i = new Inner();
        i.method1();
        Inner.method2();
    }
}
package com.itheima.test54;

public class Test {
    public static void main(String[] args) {
        Outer o =new Outer();
        o.show();
    }
}

匿名内部类

什么是匿名内部类

匿名内部类本质上就是隐藏了名字的内部类,可以写在成员位置,也可以写在局部位置。

匿名内部类的格式

格式的细节
  • 包含了继承或实现,方法重写,创建对象。
  • 整体就是一个类的子类对象或者接口的实现类对象
使用场景
  • 当方法的参数是接口或者类时,以接口为例,可以传递这个接口的实现类对象,如果实现类是要使用一次,就可以用匿名内部类简化代码
package com.itheima.test55;

public abstract class Animal {
    public abstract void eat();
}
package com.itheima.test55;

public interface Swim {
    public void swim();
}
package com.itheima.test55;

public class Test {
    public static void main(String[] args) {
        new Animal(){

            @Override
            public void eat() {
                System.out.println("狗吃骨头");
            }
        };

        new Swim(){

            @Override
            public void swim() {
                System.out.println("游泳");
            }
        };

        method(
                new Animal() {
                    @Override
                    public void eat() {
                        System.out.println("狗吃骨头");
                    }
                }
        );

    }

    public static void method(Animal a) {
        a.eat();
    }
}

运行结果:

package com.itheima.test55;

public class Test2 {
    public static void main(String[] args) {

        Swim s = new Swim(){
            @Override
            public void swim() {
                System.out.println("游泳");
            }
        };

        new Animal(){
            @Override
            public void eat() {
                System.out.println("吃饭");
            }
        }.eat();
    }
}

运行结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值