Java笔记(6) 接口,内部类,多态 ,lambda表达式

1 接口

序言:我们还延续介绍是抽象类的时候所举的例子,我们知道继承抽象类的所有子类,需要将抽象类中的所有抽象方法进行重写,这样在多态的机制中,就可以将父类修改为抽象类,将draw()方法设置为抽象方法,然后每个子类都重写这个方法来处理。但这又会使代码变的冗余,同样这样的父类局限性很大,也许某个不需要draw()方法的子类也不得不重写draw()方法。如果将draw()方法放置在另一个类中,让那些需要draw()方法的类继承该类,不需要draw()方法的类继承图形类,又会产生新的问题:所有的子类都需要继承图形类,因为这些类是从图形类中导出的,同时某些类还需要draw()方法,而Java中规定类不能同时继承多个父类。为了应对这一个概念,接口的概念便出现了。

总纲: 我们可以把接口当成一种特殊的抽象类。

1.1 接口的组成

接口用关键字interface修饰
public interface 接口名 { }
类实现接口用implements表示
public class 类名 implements 接口名 { }

(1)注意事项:

★接口不能实例化,但我们可以创建接口的实现类对象使用。

★接口的子类:要么重写接口中的所有抽象方法,要么子类也是抽象类。

(2)接口的成员组成:

  • 成员变量:

只能是常量

默认修饰符:public static final

  • 构造方法:

没有,因为接口主要是扩展功能的,而没有具体存在的

  • 成员方法:

只能是抽象方法

默认修饰符:public abstract

(3)代码演示:

接口
public interface Inter {
    public static final int NUM = 10;

    public abstract void show();
}
实现类
class InterImpl implements Inter{

    public void method(){
        // NUM = 20;
        System.out.println(NUM);
    }

    public void show(){

    }
}
测试类
public class TestInterface {
    /*
        成员变量: 只能是常量 系统会默认加入三个关键字
                    public static final
        构造方法: 没有
        成员方法: 只能是抽象方法, 系统会默认加入两个关键字
                    public abstract
     */
    public static void main(String[] args) {
        System.out.println(Inter.NUM);
    }
  
}

1.2类和接口的关系

  • 类与类的关系

继承关系,只能单继承,但是可以多层继承

  • 类与接口的关系

实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口

  • 接口与接口的关系

继承关系,可以单继承,也可以多继承

1.3 接口中默认方法

  • 格式

public default 返回值类型 方法名(参数列表) { }

  • 作用

解决接口升级的问题

范例

public default void show3() { 
}

注意事项

  • 默认方法不是抽象方法,所以不强制被重写。但是可以被重写,重写的时候去掉default关键字

  • public可以省略,default不能省略

  • 如果实现了多个接口,多个接口中存在相同的方法声明,子类就必须对该方法进行重写

1.4接口中静态方法【应用】

  • 格式

public static 返回值类型 方法名(参数列表) { }

  • 范例
public static void show() {
}

注意事项

  • 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用

  • public可以省略,static不能省略

1.5接口中私有方法

  • 私有方法产生原因

Java 9中新增了带方法体的私有方法,这其实在Java 8中就埋下了伏笔:Java 8允许在接口中定义带方法体的默认方法和静态方法。这样可能就会引发一个问题:当两个默认方法或者静态方法中包含一段相同的代码实现时,程序必然考虑将这段实现代码抽取成一个共性方法,而这个共性方法是不需要让别人使用的,因此用私有给隐藏起来,这就是Java 9增加私有方法的必然性

  • 定义格式
格式1:  private 返回值类型 方法名(参数列表) { 
        
        }

案例:  private void show() {  

        }

格式2:  private static 返回值类型 方法名(参数列表) {   

        }

案例:    private static void method() {  

          }
  • 注意事项

默认方法可以调用私有的静态方法和非静态方法

静态方法只能调用私有的静态方法

3.多态

3.1 多态的相关用法

(1)多态的好处与弊端

好处:提高程序的拓展性,定义方法的时候,使用父类型作为参数,在使用的时候,使用具体的子类型参数操作。

弊端:不能使用子类的特有成员。

(2)多态中的转型:

向上转型:当有子类对象赋值给父类应用时,便是向上转型。多态本身就是向上转型的过程。

向下转型:一个已经向上转型的子类对象可以使用强制类型转换的格式,将父类引用转为子类引用,这个过程是向下转型。(注意前提是有向上转型的子类对象,如果是直接创建父类对象是无法向下转型的)。

为什么要有向下转型:

这是由于多态的弊端,通过向上转型不能使用子类的特有功能。

如果就想使用子类的特有功能,怎么做?

A:最原始的创建对象调用方法呗。(但是很多时候不合理。而且建了太多对象会占内存)

B: 把父类的引用强制转换为子类的引用。(向下转型)

(3)我们最好理解下这里的方法重写,以及继承的意义就会很好理解

public class fu {
    int age =42;
  public void teach(){
      System.out.println("我是一名教Java的老师");
  }
}

public class zi extends fu {
    int age =19;
    public void teach(){
        System.out.println("我也会教Java,我从我爸那学的");
    }
    public void student(){
        System.out.println("我还是一名学生");
    }
}

public class Test01 {
    public static void main(String[] args) {
        //向上转型  (只能调出子类的重写方法)
        fu f= new zi();
        f.teach();       //我也会教Java,我从我爸那学的
        System.out.println(f.age);    //*******注意 42*******注意
        //向下转型        //就是为了方便能调用子类所有的对象,实质和父类没啥太大关系
        zi z =(zi)f;
        System.out.println(z.age);     //19
        z.student();       //我还是一名学生
        z.teach();         //我也会教Java,我从我爸那学的
    }
}

3.2 instanceof关键字

instanceof 是 Java 的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型。

注意:

  • 类的实例包含本身的实例,以及所有直接或间接子类的实例
  • instanceof左边显式声明的类型与右边操作元必须是同种类或存在继承关系,也就是说需要位于同一个继承树,否则会编译错误
abstract class Animal {
    public abstract void eat();
}

class Dog extends Animal {
    public void eat() {
        System.out.println("狗吃肉");
    }

    public void watchHome(){
        System.out.println("看家");
    }
}

class Cat extends Animal {
    public void eat() {
        System.out.println("猫吃鱼");
    }
}

public class Test4Polymorpic {
    public static void main(String[] args) {
        useAnimal(new Dog());
        useAnimal(new Cat());
    }

    public static void useAnimal(Animal a){  // Animal a = new Dog();
                                             // Animal a = new Cat();
        a.eat();
        //a.watchHome();

//        Dog dog = (Dog) a;
//        dog.watchHome();  // ClassCastException  类型转换异常
      
        // 判断a变量记录的类型, 是否是Dog
        if(a instanceof Dog){
            Dog dog = (Dog) a;
            dog.watchHome();
        }
    }
}
结果:
    狗吃肉
    看家
    猫吃鱼

 4 内部类

内部类的概念:在一个类中定义一个类。举例:在一个类A的内部定义一个类B,类B就被称为内部类

class Outer {
     public class Inner {
     
    }
}

内部类的访问特点 :

  • 内部类可以直接访问外部类的成员,包括私有

  • 外部类要访问内部类的成员,必须创建对象

public class Outer {
    private int num = 10;
    public class Inner {
        public void show() {
            System.out.println(num);
        }
    }
    public void method() {
        Inner i = new Inner();
        i.show();
    }
}

4.1 成员内部类

位置:在类中方法,跟成员变量一个位置

外界创建成员内部类格式(万能):

  • 格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;

  • 举例:Outer.Inner oi = new Outer().new Inner();

(1)私有成员内部类

将一个类,设计为内部类的目的,大多数都是不想让外界去访问,所以内部类的定义应该私有化,私有化之后,再提供一个可以让外界调用的方法,方法内部创建内部类对象并调用。

class Outer {
    private int num = 10;
    private class Inner {
        public void show() {
            System.out.println(num);
        }
    }
    public void method() {
        Inner i = new Inner();
        i.show();
    }
}
public class InnerDemo {
    public static void main(String[] args) {
                //Outer.Inner oi = new Outer().new Inner();
                //oi.show();
        Outer o = new Outer();
        o.method();
    }
}

(2)静态成员内部类

  • 静态成员内部类访问格式:外部类名.内部类名 对象名 = new 外部类名.内部类名();

  • 静态成员内部类中的静态方法:外部类名.内部类名.方法名();

class Outer {
    static class Inner {
        public void show(){
            System.out.println("inner..show");
        }

        public static void method(){
            System.out.println("inner..method");
        }
    }
}
public class Test3Innerclass {
  public static void main(String[] args) {
        // 外部类名.内部类名 对象名 = new 外部类名.内部类名();
        Outer.Inner oi = new Outer.Inner();
        oi.show();
         Outer.Inner.method();
    }
}

4.2 局部内部类

局部内部类定义位置

  • 局部内部类是在方法中定义的类

局部内部类方式方式

  • 局部内部类,外界是无法直接使用,需要在方法内部创建对象并使用
  • 该类可以直接访问外部类的成员,也可以访问方法内的局部变量
class Outer {
    private int num = 10;
    public void method() {
        int num2 = 20;
        class Inner {
            public void show() {
                System.out.println(num);
                System.out.println(num2);
            }
        }
        Inner i = new Inner();
        i.show();
    }
}
public class OuterDemo {
    public static void main(String[] args) {
        Outer o = new Outer();
        o.method();
    }
}

4.3 匿名内部类

★匿名类:是只在创建对象时才会编写类体的一种写法。匿名类的特点是“现用现写”。

★语法:最后一个大括号之后有分号

// 标 准 语 法
new 父类/父接口(){
子类实现的内容
};


//案例
new Inter(){
    @Override
    public void method(){}
} 

★本质:匿名内部类的本质是匿名的对象,这个对象继承了这个类,或者实现了这个接口吗,new标志了这是一个对象,new后面的那个是该对象继承的那个类或者是实现的那个接口。

(1)匿名内部类可以通过多态的形式接受:

Inter i = new Inter(){
  @Override
    public void method(){  
    }
}

(2)匿名内部类直接调用方法

interface Inter{
    void method();
}

class Test{
    public static void main(String[] args){
        new Inter(){
            @Override
            public void method(){
                System.out.println("我是匿名内部类");
            }
        }.method();        // 直接调用方法
    }
}

(3)综合运用

interface Swimming {
    void swim();
}

public class TestSwimming {
    public static void main(String[] args) {
        goSwimming(new Swimming() {
            @Override
            public void swim() {
                System.out.println("老铁, 我们去游泳吧");
            }
        });
    }

    public static void goSwimming(Swimming swimming){
        /*
            Swimming swim = new Swimming() {
                @Override
                public void swim() {
                    System.out.println("老铁, 我们去游泳吧");
                }
            }
         */
        swimming.swim();
    }
}

5 Lambda表达式

★函数式接口:仅包含一个抽象方法的接口。

★lambda表达式不能独立执行,因此必须实现函数式接口,并且会返回一个函数式接口的对象。

★lambda表达式的理解:

( ) -> {代码块}

这个方法 按照 这样的代码来执行

5.1 lambda表达式调用无参抽象方法

无参抽象方法在lambda表达式中使用“()”表示

//通过多态的形式
public interface Eatable {
    void eat();
}
public class EatableDemo {
    public static void main(String[] args) {
       useEatable(() -> {
            System.out.println("一天一苹果,医生远离我");
        });
    }
//lambda表达式不能独立执行,因此必须实现函数式接口,并且会返回一个函数式接口的对象(子类)。
    private static void useEatable(Eatable e) {
        e.eat();
    }
}
//***************************************************************
//通过创建对象的形式
public interface Eatable {
    void eat();
}
public class EatableDemo {
    public static void main(String[] args) {
     Eatable e =  () -> {
            System.out.println("一天一苹果,医生远离我");
        };
    }
    System.out.println(e.eat());
}

5.2 lambda表达式实现有参抽象方法

public interface Addable {
    int add(int x,int y);
}
public class AddableDemo {
    public static void main(String[] args) {
        useAddable((int x,int y) -> {
            return x + y;
        });
    }
    private static void useAddable(Addable a) {
        int sum = a.add(10, 20);
        System.out.println(sum);
    }
}

5.3 lambda表达式调用外部变量

(1)lambda表达式无法更改局部变量(注意局部变量的定义)

interface defin{
void method();
}
public class s2{
public static void main(String[] args) {
  int value =100;             //创建局部变量
  defin v=()->{                   
  int num =value -90;
  value =12;                 //更改局部变量,报错
  };
}
}

(2)lambda 表达式可以更改类成员变量

interface  face2{
    void method();
}
public class Server {
 int value =100;
 public void action(){
     face2 v =()->{
         value =-12;
     };
     System.out.println("运行前"+value);  //100
     v.method();
     System.out.println("运行后"+value);  //-12
 }

    public static void main(String[] args) {
        Server d =new Server();
        d.action();
    }
}

总结:

lambda表达式只是描述了函数式接口的抽象方法是如何实现的,在抽象方法没有调用前,lambda表达式中的代码并没有被执行

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

  • 所需类型不同
    • 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
  • Lambda表达式:只能是接口
  • 使用限制不同
    • 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
  • 如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式
  • 实现原理不同
    • 匿名内部类:编译之后,产生一个单独的.class字节码文件
  • Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值