java核心技术卷1---第6章 接口 lambda表达式 内部类

接口

接口用来描述类应该做什么,不指定具体应该怎么做,一个类可以实现一个或多个接口

接口概念

如果你的类符合某个特定接口,我就会履行这项服务

接口中的所有方法自动是public方法,在接口中声明方法时,不必提供关键字public

接口可以有多个方法,不可以有实例字段

提供实例字段和方法实现的任务应该有实现接口的哪个类来完成,可以将接口看作没有实例字段的抽象类

让类实现一个接口:

  1. 将类声明为实现给定的接口,implements,java强类型语言
  2. 对接口中的所有方法提供定义
package com.fu.javatec.chapter06;

import java.util.Arrays;

public class Demo01 { }
class EmployeeSortTest{
    public static void main(String[] args) {
        Employee[] staff = new Employee[3];
        staff[0] = new Employee("H_H",35000);
        staff[1] = new Employee("C_C",75000);
        staff[2] = new Employee("T_T",38000);
        Arrays.sort(staff);
        for (Employee e : staff) {
            System.out.println("name=" + e.getName() + ",salary=" + e.getSalary());
        }
    }
}
class Employee implements Comparable<Employee>{
    private String name;
    private double salary;

    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    public String getName() {
        return name;
    }

    public double getSalary() {
        return salary;
    }
    public void raiseSalary(double byPercent){
        double raise = salary * byPercent / 100;
        salary += raise;
    }
    public int compareTo(Employee other){
        return Double.compare(salary,other.salary);
    }
}

接口的属性

接口不是类,不能new对象,能声明变量

接口变量必须引用实现接口的类对象

用instanceof检查对象是否实现接口

可以扩展接口,让接口继承接口

接口方法自动public 字段自动public static final

有些接口只定义常量,不定义方法

每个类只能继承一个超类,可以实现多个接口

接口和抽象类

使用抽象类表示通用属性存在问题,一个类只能继承(扩展)一个类,接口不会有这种问题

默认方法

接口方法提供一个默认实现 default修饰符标记

public interface Comparable<T>{
    default int compareTo(T other){return 0;}
   
}

这实际没有太大的用处,知道即可

默认方法重要用法: 接口演化

解决默认方法冲突

  1. 超类优先(类优先,超类优先原则)

  2. 接口冲突


public class Test implements A,B{
    @Override
    public void a() {
        A.super.a();
    }
}
interface A{default void a(){}}
interface B{default void a(){}}

接口与回调

回调是一种常见的程序设计模式,指定某个特定事件发生时应该采取的动作

例如:按下鼠标,播放▶音乐

定时器和动作监听器的具体使用

package com.fu.javatec.chapter06;

import javax.swing.*;
import java.awt.*;
import java.time.*;
import java.awt.event.*;

public class Demo02 {
}
class TimerTest{
    public static void main(String[] args) {
        TimePrinter listener = new TimePrinter();
        Timer timer = new Timer(1000, listener);
        timer.start();
        JOptionPane.showMessageDialog(null,"Quit program?");
        System.exit(0);
    }
}
class TimePrinter implements ActionListener{
    @Override
    public void actionPerformed(ActionEvent event) {
        System.out.println("At the tone, the time is " + Instant.ofEpochMilli(event.getWhen()));
        Toolkit.getDefaultToolkit().beep();
    }
}

Comparator接口

比较器

利用lambda有奇效

对象克隆

clone();

初始状态相同,之后他们就会有自己不同的状态

(浅拷贝)浅克隆对象和原对象共享子对象

(深拷贝)深克隆会克隆所有子对象

对于一个类需要确定:

  1. 默认的clone方法是否满足需求
  2. 是否可以在可变的子对象上调用clone来修补默认的clone方法
  3. 是否不该使用clone

实际上第三个选项时必选项,选择第一个和第二个选项,类必须:

  1. 实现Cloneable接口
  2. 重新定义clone方法,指定public访问修饰符
package com.fu.javatec.chapter06;

import java.util.Date;
import java.util.GregorianCalendar;

public class Demo03 { }
class CloneTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Employee_ original = new Employee_("J", 50000);
        original.setHireDay(2000,1,1);
        Employee_ copy = original.clone();
        copy.raiseSalary(10);
        copy.setHireDay(2002,12,31);
        System.out.println("original=" + original);
        System.out.println("copy=" + copy);
    }
}
class Employee_ implements Cloneable{
    private String name;
    private double salary;
    private Date hireDay;

    public Employee_(String name, double salary) {
        this.name = name;
        this.salary = salary;
        hireDay = new Date();
    }

    @Override
    protected Employee_ clone() throws CloneNotSupportedException {
        Employee_ cloned = (Employee_)super.clone();
        cloned.hireDay = (Date) hireDay.clone();
        return cloned;
    }
    public void setHireDay(int year,int month,int day){
        Date newHireDay = new GregorianCalendar(year, month - 1, day).getTime();
        hireDay.setTime(newHireDay.getTime());
    }
    public void raiseSalary(double byPercent){
        double raise = salary * byPercent / 100;
        salary += raise;
    }

    @Override
    public String toString() {
        return "Employee_{" +
                "name='" + name + '\'' +
                ", salary=" + salary +
                ", hireDay=" + hireDay +
                '}';
    }
}

Lambda表达式 λ

Lambda 表达式采用一种简洁的语法定义代码块,实现对代码块的有效处理

Lambda是一个可传递的代码块,可以执行一次或多次

像写方法一样,把代码放在{}中,包含显式的return语句

lamba表达式没有参数,仍然要提供空括号

可以推导一个lambda表达式的参数类型,可以忽略其类型(含泛型<>)

无需指定lambda表达式的返回类型,lambda表达式返回类型会由上下文推导出

package com.fu.javatec.chapter06;


import java.util.*;
import javax.swing.*;
import javax.swing.Timer;


public class Demo04 {
    public static void main(String[] args) {
        String [] planets = new String[]{"Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune"};
        System.out.println(Arrays.toString(planets));
        System.out.println("Sorted by length:");
        Arrays.sort(planets,(first,second) -> first.length() - second.length());
        System.out.println(Arrays.toString(planets));
        Timer timer = new Timer(1000, event -> System.out.println("The time is " + new Date()));
        timer.start();
        JOptionPane.showMessageDialog(null,"Quit program?");
        System.exit(0);
    }
}

函数式接口

java有很多封装代码块的接口,对于只有一个抽象方法的接口,需要这种接口的对象,就可以提供一个lambda表达式,这种接口叫 函数式接口

接口可以声明非抽象方法,接口可以重新声明Object类方法,函数式接口必须有一个抽象方法

方法引用

lambda表达式设计一个方法

Timer timer = new Timer(1000,event -> System.out.println(event));
Timer timer = new Timer(1000,System.out::println);

方法引用也不是一个对象,为一个类型为函数式接口的变量赋值时会生成一个对象

用::运算符分隔方法名与对象或类名

  1. Object::instanceMethod
  2. Class::instanceMethod
  3. Class::staticMethod
方法引用实例等价的lambda表达式说明
separator::equalsx -> separator.equals(x)包含一个对象和一个是实例方法的方法表达式,lambda参数作为这个方法的显示参数传入
String::trimx -> x.trim()包含一个类和一个实例方法的方法表达式,lambda表达式会成为隐式参数
String::concat(x,y) -> x.concat(y)有一个实例方法,有一个显示参数,第一个lambda参数会成为隐式参数,其余参数会传递到方法
Integer::valueOfx -> Integer::valueOf(x)包含一个静态方法的方法表达式,lambda参数会传递到这个静态方法
Integer::sum(x,y) -> Integer::sum(x,y)另一个静态方法,有两个参数
Integer::newx -> new Integer(x)构造器引用
Integer[ ]::newn -> new Integer[n]数组构造器引用

只有当lambda表达式的体只调用一个方法而不做其他操作时,才能把lambda表达式重写为方法引用

可以在方法引用中使用this参数

this::equals 等同于 x -> this.equals(x) super也可以

构造器引用

方法名为new

例如:Person :: new

有一个String参数的构造器,上下文推导出这是调用一个字符串的构造器

限制:无法构造泛型类型 T 的数组

变量作用域

lambda表达式三个部分:

  1. 一个代码块
  2. 参数
  3. 自由变量的值,指非参数而且不在代码中定义的变量

java中lambda表达式就是一个闭包

lambda只能捕获值不变的变量,如果更爱变量,并发执行多个动作就会不安全

处理lambda表达式

使用lambda表达式重点是 延迟执行

原因:

  1. 在一个单独线程中运行代码
  2. 多次运行代码
  3. 在算法的适当位置运行代码
  4. 发生某种情况执行代码
  5. 只在必要时执行代码

再谈 Comparator

Comparator接口有很多静态方法创建比较器,用于lambda表达式和方法引用

去一个键提取器函数,键函数可以返回null,产生比较的比较器

Array.sort(people,Comparator.comparing(Person::getName));

内部类

内部类是定义另一个类中的类

内部类可以对同一个包中的其他类隐藏

内部类方法可以访问定义这个类的作用域中的数据,包括原本私有的数据

内部类访问对象状态

一个内部类方法可以访问自身的数据字段,可以访问创建它的外围类对象数据字段

内部类对象有一个隐式引用,指向创建他的外部类对象,这个引用在内部类的定义中是不可见的

外围类的引用在构造器中设置,编译器会修改所有的内部类构造器,添加一个对应外围类引用的参数

package com.fu.javatec.chapter06;

import javax.swing.*;
import java.awt.*;
import java.time.*;
import java.awt.event.*;

public class Demo05 { }
class InnerClassTest{
    public static void main(String[] args) {
        TalkingClock clock = new TalkingClock(1000, true);
        clock.start();
        JOptionPane.showMessageDialog(null,"Quit program?");
        System.exit(0);
    }
}
class TalkingClock{
    private int interval;
    private boolean beep;

    public TalkingClock(int interval, boolean beep) {
        this.interval = interval;
        this.beep = beep;
    }
    public  void start() {
        TimePrinter2 listener = new TimePrinter2();
        Timer timer = new Timer(interval, listener);
        timer.start();
    }
    public  class TimePrinter2 implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent event) {
            System.out.println("At the tone, the time is "+ Instant.ofEpochMilli(event.getWhen()));
            if (beep) Toolkit.getDefaultToolkit().beep();
        }
    }
}

内部类的特殊语法规则

内部类有一个外围类的引用,我们叫他outer

内部类中声明的所有静态字段都是final,初始化一个编译时常量

内部类不能有static方法

内部类是否有用,必要,安全

内部类语法很复杂,内部类是一个编译器现象与虚拟机无关,内部类可以访问外围类私有属性,外部的其他类则不行

内部类有更大的访问权限,天生就比常规类功能更加强大

内部类访问私有数据字段,可能通过外围类所在包中增加的其他类访问那些字段

程序员不可能无意之中获得对类的访问权限,必须可以构建或修改类文件才有可能达到目的

局部内部类

声明局部内部类没有访问修饰符,局部内部类作用域限定在声明这个局部类的块中

优势:对外部世界完全隐藏,其他代码不能访问它

public void start(){
        class TimePrinter implements ActionListener{
            public void actionPerformed(ActionEvent event){
                System.out.println("At the tone, the time is " + Instant.ofEpochMilli(event.getWhen()));
                if (beep) Toolkit.getDefaultToolkit().beep();
            }
        }
        TimePrinter listener = new TimePrinter();
        timer.start();
    }

由外部方法访问变量

局部类另一个优势:访问外部类字段和局部变量

匿名内部类

创建这个类的一个对象,甚至不需要为类指定名字,这个类叫匿名内部类

public void start(int interval,boolean beep){
	var listener = new ActionLitener(){
        public void actionPerformed(ActionEvent event){
            System.out.println("At the tone, the time is " + Instant.ofEpochMilli(event.getWhen()));
            if(beep)Toolkit.getDefaultToolkit().beep();
        }
	};
    var timer = new Timer(interval , listener);
    timer.start();
}

含义:创建一个类的新对象,这个类实现了ActionListener接口,需要的方法actionPerformerd中定义

匿名内部类要扩展这个类

构造器的名字必须与类名相同,匿名内部类没有类名,匿名内部类不能有构造器

只要内部类实现一个接口,就不能有任何参数,仍要有一组小括号

package com.fu.javatec.chapter06;


import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.time.*;

public class Demo06 {}
class AnonymousInnerClassTest{
    public static void main(String[] args) {
        TalkingClock1 clock = new TalkingClock1();
        clock.start(1000,true);
        JOptionPane.showMessageDialog(null,"Quit program?");
        System.exit(0);
    }
}

class TalkingClock1{
    public void start(int interval,boolean beep){
        ActionListener listener = new ActionListener(){
            public void actionPerformed(ActionEvent event){
                System.out.println("At the tone,the time is " + Instant.ofEpochMilli(event.getWhen()));
                if (beep) Toolkit.getDefaultToolkit().beep();
            }
        };
        Timer timer = new Timer(interval, listener);
        timer.start();
    }
}



双括号初始化

利用内部类语法,构造一个数组列表,传递给一个方法

ArrayList<String> friends = new ArrayList<String>();
friends.add("Harry");
friends.add("Tony");
invite(friends);

等价于:

invite(new ArrayList<String>(){add("Harry");add("Tony");}});

静态内部类

为了把一个类隐藏在另外一个类内部,不需要内部类有外围类对象的一个引用

把内部类声明为static,就不会有引用

静态内部类可以有静态字段和方法,常规类不行

服务加载器

采用一个服务架构的应用,用偶遇开发环境,应用服务器,其他复杂的应用

实现类可以放在任意包中,不一定是服务接口所在的包,每个实现类有一个无参构造器

代理

使用代理可以在运行时创建实现一组给定接口的新类,在编译时无法确定需要实现哪个接口时用这种代理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值