Java 接口与内部类

Java 接口与内部类

接口(Interface)

接口的概念与设计哲学

抽象类是从多个类中抽象出来的模板,如果将这种抽象进行的更彻底,则可以提炼出一种更加特殊的"抽象类"——接口(interface)。接口是从多个相似类中抽象出的规范,体现“规范与实现分离”的设计思想。它不关心类的内部状态和方法实现细节,仅规定类必须提供的公共方法,是类与外部交流的通道。接口里不能包含普通方法,接口里所有的方法都是抽象方法。java8对接口进行了改进,允许在接口中定义默认方法及静态方法,默认方法可以提供方法实现,静态方法让接口具备一定功能。

接口的核心价值在于松耦合:软件组件面向接口耦合时,无需关注实现细节,只需遵守规范即可协同工作。例如主板的PCI插槽,只要显卡符合PCI接口规范,无论品牌和内部实现如何,都能正常使用。

接口的定义与语法

接口是与类相似的结构,Java 8及以后的接口可包含:

  • 常量(默认public static final
  • 抽象方法(默认public abstract
  • 默认方法(default修饰,提供方法实现,java 8之前不允许)
  • 静态方法(static修饰,通过接口名调用,java 8之前不允许)

定义格式:

public interface 接口名 {
    // 常量(可省略public static final)
    数据类型 常量名 = 常量值;
    
    // 抽象方法(可省略public abstract)
    返回值类型 方法名(参数列表);
    
    // 默认方法
    default 返回值类型 方法名(参数列表) {
        方法实现;
    }
    
    // 静态方法
    static 返回值类型 方法名(参数列表) {
        方法实现;
    }
}

接口的实现与使用

类通过implements关键字实现接口,必须实现接口中所有抽象方法(默认方法可选择性重写)。一个类可实现多个接口,弥补Java单继承的限制。以下图为例:
image

可食用接口(Edible)与相关类实现

可食用接口定义
package com.inherit.implement;

/**
 * 可食用接口
 * 在jdk1.8以前,接口中只能包含常量和抽象方法。
 * 定义的方法默认就是public abstract
 * @author Jing61
 */
public interface Ediable {
    /**
     * 所有可食用的都具有怎么吃共同的行为
     * 接口中的所有方法自动地属于public。因此public可以省略
     */
    String howToEat();
}
抽象动物类(Animal)
package com.inherit.implement;

/**
 * 抽象类
 * @author Jing61
 */
public abstract class Animal {
    private double weight;

    public abstract String sound();
}
具体实现类
  • 老虎类(不可食用,仅继承Animal)
package com.inherit.implement;

public class Tiger extends Animal{

    @Override
    public String sound() {
        return "两只老虎跑的快,跑得快";
    }
}
  • 鸡类(可食用,继承Animal并实现Edible)
package com.inherit.implement;

public class Chicken extends Animal implements Edible{

    @Override
    public String howToEat() {
        return "可乐鸡翅";
    }

    @Override
    public String sound() {
        return "叽叽叽。。。。";
    }
}

  • 抽象水果类(可食用,实现Edible但不实现抽象方法,需为abstract)
package com.inherit.implement;

public abstract class Fruit implements Edible {
    
}
  • 橙子类(继承Fruit,实现howToEat)
package com.inherit.implement;

public class Orange extends Fruit{
    @Override
    public String howToEat() {
        return "榨汁->橙汁";
    }
}

  • 苹果类(继承Fruit,实现howToEat)
package com.inherit.implement;

public class Apple extends Fruit{
    
    @Override
    public String howToEat() {
        return "apple 削皮";
    }
}

测试类(EdibleTest)
package com.inherit.implement;

public class EdibleTest {
    public static void main(String[] args) {
        Object[] instances = {new Tiger(),new Chicken(),new Orange()};
        for(Object obj : instances) {
            if(obj instanceof Edible)
                System.out.println(((Edible)obj).howToEat());

            if(obj instanceof Animal)
                System.out.println(((Animal)obj).sound());
        }
    }
}

示例2:电脑组件接口与组装实战

需求:电脑由主板、CPU、内存组成,通过接口抽象各组件规范,支持灵活替换配件。
如图:
image
image
image
image
image

核心接口定义
  • 电脑组件接口(PCComponent):所有电脑组件的基础规范
package com.inherit.computer;

/**
 * 电脑组件接口
 * @author Jing61
 */
public interface PCComponent {
    String getName(); // 获取组件名称
    double getPrice(); // 获取组件价格
    String getCompany(); // 获取组件厂家
}

  • CPU接口(继承PCComponent)
package com.inherit.computer;

/**
 * CPU接口
 * @author Jing61
 */
public interface CPU extends PCComponent{
    int getSpeed(); // 获取CPU速度
    void doInstr(); // 做指令运算
    void outResult(); // 输出结果
}

  • 内存接口(继承PCComponent)
package com.inherit.computer;

/**
 * 内存接口
 * @author Jing61
 */
public interface Ram extends PCComponent{
    int getSize(); // 获取内存大小
    void inData(); // 读数据
    void outData(); // 取数据
}

  • 主板接口(继承PCComponent)
package com.inherit.computer;

/**
 * 主板接口
 * @author Jing61
 */
public interface Mainboard extends PCComponent{
    void setCpu(CPU cpu);//安装cpu
    CPU getCpu();//得到主板上的CPU
    void setRam(Ram ram);//安装内存
    Ram getRam();//得到主板上的内存
    boolean havePower();//是否有电源
    void startPower();//开电源
    void shutdownPower();//关电源
}

组件实现类
  • 主板实现(抽象主板类+具体实现类),在现实中,有很多种类型的主板,只要这些主板符合我们的标准,就能接到我们的电脑中来用。本例提供了AUSUBoard及IntelBoard两种类别的主板。

    • 抽象主板类(AbstractMainboard)
    package com.inherit.computer;
    
    /**
     * 主板抽象类
     * @author Jing61
     */
    public abstract class AbstractMainboard implements Mainboard {
        private CPU cpu;
        private Ram ram;    
        private boolean power;
        public void setCpu(CPU cpu) {
            this.cpu=cpu;
        }
        public CPU getCpu() {    
            return cpu;
        }
        public void setRam(Ram ram) {
            this.ram=ram;
        }
        public Ram getRam() {    
            return ram;
        }
        public boolean havePower() {
            return power;
        }
        public void startPower() {
            power=true;
        }
        public void shutdownPower() {
                power=false;
        }
    }
    
    
    • Intel主板类(IntelBoard)
    package com.inherit.computer;
    
    /**
     * Intel主板
     * @author Jing61
     */
    public class IntelBoard extends AbstractMainboard {
        public String getName() {
            return "Intel主板";
        }
        public double getPrice() {
            return 3500.00;
        }
        public String getCompany() {
            return "Intel公司";
        }    
    }
    
    
    • AUSU主板类(AUSUBoard)
    package com.inherit.computer;
    
    /**
     * AUSU主板
     * @author Jing61
     */
    public class AUSUBoard extends AbstractMainboard {
        public String getName() {
            return "AUSU主板";
        }
        public double getPrice() {
            return 3000.00;
        }
        public String getCompany() {
            return "SUSU公司";
        }    
    }
    
    
  • CPU实现类,在现实中,有很多种类型的CPU可供使用,只要这些CPU符合我们的标准,就能接到我们的电脑中来用。本例提供了AMDCpu及IntelCPU两种类别的主板。

    • AMD CPU
    package com.inherit.computer;
    
    /**
     * AMD CPU
     * @author Jing61
     */
    public class AMDCpu implements CPU {
        public int getSpeed() {
            return 1500;
        }
        public void doInstr() {
        System.out.println("做指令运算");        
        }
        public void outResult() {        
        }
        public String getName() {    
            return "AMD CPU";
        }
        public double getPrice() {
            return 1800;
        }
        public String getCompany() {    
            return "AMD公司";
        }
    }
    
    
    • Intel CPU
    package com.inherit.computer;
    
    /**
     * Intel CPU
     * @author Jing61
     */
    public class IntelCPU implements CPU {
        public int getSpeed() {
            return 1700;
        }
        public void doInstr() {
        System.out.println("做指令运算");        
        }
        public void outResult() {    
        }
        public String getName() {    
            return "IntelCPU";
        }
        public double getPrice() {
            return 2500;
        }
        public String getCompany() {    
            return "Intel公司";
        }
    }
    
    
  • 内存实现类,本例我们也只提供两款可供选择的内存。分别是Kingmax的内存,以及Kingstone的内存。

    • Kingmax内存
    package com.inherit.computer;
    
    /**
     * Kingmax内存
     * @author Jing61
     */
    public class KingmaxRam implements Ram {
        public int getSize() {        
            return 512;
        }
        public void inData() {
        //读入数据
        }
        public void outData() {
        //输出数据
        }
        public String getName() {
            return "Kingmax内存";
        }
        public double getPrice() {    
            return 300;
        }
        public String getCompany() {    
            return "Kingmax公司";
        }
    }
    
    
    • Kingstone内存
    package com.inherit.computer;
    
    /**
     * Kingmax内存
     * @author Jing61
     */
    public class KingstoneRam implements Ram {
        public int getSize() {        
            return 512;
        }
        public void inData() {
            //读入数据
        }
        public void outData() {
            //输出数据
        }
            public String getName() {
                return "Kingstone内存";
        }
        public double getPrice() {    
            return 200;
        }
        public String getCompany() {    
            return "Kingstone公司";
        }
    }
    
    
电脑类(Computer)设计

每台Computer需要一个主板,每个主板上需要插上CPU及内存条,电脑才能正常工作。

package com.inherit.computer;

/**
 * 电脑类
 * @author Jing61
 */
public class Computer {
    private Mainboard mainboard;

    public void setMainboard(Mainboard mainboard) {
        this.mainboard = mainboard;
    }

    public void doWork() {
        System.out.println("开始工作");
        for (int i = 0; i < 10; i++) {
            System.out.println("第" + i + "次工作");
        }
        System.out.println("工作结束");
    }

    public void start() {
        mainboard.startPower();
    }

    public void shutdown() {
        mainboard.shutdownPower();
    }

    public double getPrice() {
        return mainboard.getPrice() + mainboard.getCpu().getPrice() + mainboard.getRam().getPrice();
    }

    public String getSetting() {
        String ret = "电脑组成如下!主板:" + mainboard.getName() + ",CPU:" + mainboard.getCpu().getName() + ",内存:" + mainboard.getRam().getName() + "\n";
        ret += "这个配置的价格为:"+getPrice();
        return ret;
    }
}

传统组装电脑方法(ClientOldDemo)

传统的情况下,要组装一台电脑,需要在代码中分别new出每一个电脑组件的实例,再把他们组装到电脑上面,再运行程序。

package com.inherit.computer;

/**
 * @author Jing61
 */
public class ClientOldDemo {
    public static void main(String[] args) {
        CPU cpu = new IntelCPU();
        Ram ram = new KingmaxRam();
        Mainboard myMainboard = new IntelBoard();
        myMainboard.setCpu(cpu);
        myMainboard.setRam(ram);
        Computer computer = new Computer();
        computer.setMainboard(myMainboard);
        //执行computer的doWork方法,使得commputer开始工作
        computer.doWork();
        //输出电脑的配置信息
        System.out.println(computer.getSetting());
    }
}

常用系统接口

Comparable接口

用于定义对象的比较规则,实现该接口的类可通过Arrays.sort()排序。需实现compareTo()方法,定义比较逻辑。

示例:Circle类实现Comparable接口(按半径比较)

package com.inherit.implement;

public class Circle implements Comparable<Circle> {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    public double getRadius() {
        return radius;
    }

    public void setRadius(double radius) {
        this.radius = radius;
    }

    public double getArea() {
        return Math.PI * radius * radius;
    }

    @Override
    public String toString() {
        return "Circle [radius=" + radius + ",area: " + getArea() + "]";
    }

    /**
     * 比较规则:如果当前对象比circle小,返回小于0的整数,如果相等,返回0,如果大于circle,返回一个大于0的整数
     */
    @Override
    public int compareTo(Circle circle) {
        if(this.radius == circle.radius) return 0;
        return this.radius < circle.radius ? -1 : 1;
    }
}

Cloneable接口

用于实现对象克隆,当拷贝一个变量时,原始变量和拷贝变量引用同一个对象,也就是说,改变一个变量所引用的对象将会对另一个变量产生影响;如果创建一个对象的新copy,它的初始状态和原始对象一样,但以后将可以各自改变各自的状态,那就需要使用clone方法:

Circle copy = circle.clone();
copy.getArea();

不过,事情并没有这么简单。clone()方法是Object类的protected方法,也就是说,在用户编写的代码中不能直接调用它。
image
由于这个类对具体的类对象一无所知,所以只能将各个域进行对应的拷贝。如果对象中的所有数据域都属于数值或基本类型,这样拷贝域没有任何问题。但是,如果在对象中包含了子对象的引用,拷贝的结果会使得两个域引用同一个子对象,因此原始对象与克隆对象共享这部分信息。
默认的克隆操作是浅拷贝,它并没有克隆包含在对象中的内部对象。如果进行浅拷贝会发生什么呢?这要根据具体情况而定。如果原始对象与浅克隆对象共享的子对象是不可变的,将不会产生任何问题。也确实存在这种情形。例如,子对象属于像String类这样的不允许改变的类,也有可能子对象在其生命周期内不会发生变化,既没有更改它们的方法,也没有创建对它引用的方法。然而,更常见的情况是子对象可变,因此必须重新定义clone方法,以便实现克隆子对象的深拷贝。

需满足以下条件才能使用:

  1. 类实现Cloneable接口(标记接口,无抽象方法);
  2. 重写clone()方法并改为public访问权限。

克隆分为:

  • 浅拷贝:仅拷贝对象本身,子对象引用不变(默认克隆行为);
  • 深拷贝:拷贝对象及所有子对象,需重写clone()方法实现。

使用格式:

Circle copy = (Circle)circle.clone();

接口与抽象类

接口 抽象类
不考虑java8中default方法的情况下,接口中是没有实现代码的实现 抽象类中可以有普通成员方法 ,并且可以定义变量
接口中的方法修饰符号 只能是public 抽象类中的抽象方法可以有public,protected,default
接口中没有构造方法 可以有构造方法

选择:
1、当我们需要一组规范的方法的时候,我们就可以用接口,在具体的业务中,来对接口进行实现,能达到以不变应对万变,多变的需求的情况我们只需要改变对应的实现类 。
2、如果多个实现类中有者相同可以复用的代码 这个时候就可以在实现类和接口之间,添加一个抽象类,把公共的代码抽出在抽象类中。然后要求不同实现过程的子类可以重写抽象类中的方法,来完成各自的业务。

Java 8接口改进

Java 8对接口的升级解决了接口扩展的兼容性问题:

  • 默认方法(default):提供方法实现,实现类可选择性重写,避免接口新增方法导致所有实现类编译报错;
  • 静态方法(static):接口可直接调用,无需实现类,增强接口功能。

示例:带默认方法和静态方法的接口

public interface MyInterface {
    // 抽象方法
    void abstractMethod();
    
    // 默认方法
    default void defaultMethod() {
        System.out.println("默认方法实现");
    }
    
    // 静态方法
    static void staticMethod() {
        System.out.println("静态方法实现");
    }
}

接口与回调(Callback)

回调是一种程序设计模式,指特定事件发生时执行预设动作。接口常作为回调的载体,要求事件触发者调用接口定义的方法。

示例:定时器回调(每1秒打印时间并发出提示音)

package com.inherit.implement;

import javax.swing.JOptionPane;
import javax.swing.Timer;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.LocalDateTime;

/**
 * @author Jing61
 */
public class BeepDemo {
    public static void main(String[] args) {
        /**
         * 创建一个定时器
         * 每1000毫秒触发定时器,定时器就会调用该事件里的回调方法
         */
        Timer timer = new Timer(1000, new BeepActionListner());
        //启动定时器
        timer.start();
        // 系统提示框
        JOptionPane.showMessageDialog(null, "退出定时器");
        // 退出程序
        System.exit(0);
    }
}

class BeepActionListner implements ActionListener{
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("At the tone,the time is " + LocalDateTime.now());
        Toolkit.getDefaultToolkit().beep();
    }
}

内部类(Inner Class)

内部类的概念与优势

内部类是在一个类内部定义的类,核心优势:

  1. 可直接访问外部类的所有成员(包括私有属性和方法);
  2. 对外部包隐藏,提高代码封装性;
  3. 匿名内部类简化回调和事件驱动程序编写;
  4. 丰富类的设计层次,解决复杂场景下的逻辑组织问题。

内部类的分类与语法

成员内部类

定义在类内部、方法外部,作为外部类的成员。

特点
  • 可使用publicprotectedprivate、默认权限修饰;
  • 可被abstractfinal修饰,有构造器,可定义属性和方法;
  • 分为非静态和静态成员内部类。
非静态成员内部类
  • 创建对象:需先创建外部类对象,通过外部类对象.new 内部类()创建;
  • 访问规则:
    • 直接访问外部类非同名属性/方法;
    • 访问外部类同名属性:外部类名.this.属性名
    • 访问自身属性:this.属性名

示例:

package com.inherit.implement;

/**
 * @author Jing61
 */
public class Outer {
    private int value = 100;
    private String name = "Outer";

    public void show() {
        System.out.println("Outer class value: " + value + " name: " + name);
    }

    public class Inner {
        private String name = "Inner";

        public void show() {
            // 内部类可以直接访问外部类的成员数据
            System.out.println("Inner class value: " + value + " name: " + name);
            System.out.println("Inner class value: " + value + " Outer name: " + Outer.this.name);
        }
    }

    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.show();
        Outer.Inner inner = outer.new Inner();
        inner.show();
    }
}
静态成员内部类
  • 定义:用static修饰的成员内部类;
  • 创建对象:外部类名.内部类名 对象名 = new 外部类名.内部类名()
  • 访问规则:仅能访问外部类的静态属性和方法,不能访问非静态成员。

示例:数组工具类(静态内部类存储最值)

package com.inherit.implement;

/**
 * 只是为了把一个类隐藏在另外一个类的内部,并不需要内部类引用外围对象,可以把内部类声明为static的,以便取消产生的引用
 * 查找数字中的最大值和最小值
 * @author Jing61
 */
public class ArrayAlg {
    public static Pair getMinAndMax(int[] list) {
        int min = list[0], max = list[0];
        for(int i = 1; i < list.length; i++) {
            if(min > list[i]) min = list[i];
            if(max < list[i])max = list[i];
        }
        return new Pair(min, max);
    }
    
    public static class Pair{
        private int min;
        private int max;
        public Pair(int min,int max) {
            this.min = min;
            this.max = max;
        }
        public int getMin() {
            return min;
        }
        
        public int getMax() {
            return max;
        }
    }

    public static void main(String[] args) {
        int[] list = {11, 2, 32, 4, 53, 61, 7, 82, 91, 10};
        Pair pair = ArrayAlg.getMinAndMax(list);
        System.out.println("min: " + pair.getMin() + " max: " + pair.getMax());
    }
}

局部内部类

定义在方法或代码块内部,仅在当前方法内有效。

优势

方便获取接口实现类对象,无需单独定义外部类。

示例:定时器局部内部类实现

package com.inherit.implement;

import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.LocalDateTime;
import javax.swing.Timer;

/**
 * 局部内部类
 * 发现:前面的TimerPrintActionListener类只是在Talking类的start()方法内部使用,可以使用局部内部类
 * @author Jing61
 */
public class LocalInnerClassTalking {
    private boolean beep;
    private int interval = 1000;
    
    public LocalInnerClassTalking(boolean beep, int interval) {
        this.beep = beep;
        this.interval = interval;
    }
    
    public void start() {
        class TimerPrintActionListener implements ActionListener{
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("注意,当前时间:" + LocalDateTime.now());
                //内部类可以直接访问外部类的数据
                if(beep) Toolkit.getDefaultToolkit().beep();
            }
        }
        TimerPrintActionListener listener = new TimerPrintActionListener();
        Timer timer = new Timer(interval, listener);
        timer.start();
    }
}

匿名内部类

没有类名的局部内部类,通过new 接口/抽象类(){} 隐含实现接口或抽象类,实现逻辑写在大括号内。

适用场景

仅需使用一次的接口/抽象类实现,简化代码。

示例:匿名内部类实现定时器

package com.inherit.implement;

import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.LocalDateTime;
import javax.swing.Timer;

/**
 * @author Jing61
 */
public class AnonymousInnerClassTalking {
    private int interval;
    private boolean beep;
    public AnonymousInnerClassTalking(int interval, boolean beep) {
        this.interval = interval;
        this.beep = beep;
    }
    
    public void start() {
        //匿名内部类:new的是实现了ActionListener接口类的实例,该类没有名字
        Timer timer = new Timer(interval, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("注意,当前时间:" + LocalDateTime.now());
                //内部类可以直接访问外部类的数据
                if(beep) Toolkit.getDefaultToolkit().beep();
            }
        });
        timer.start();
    }
}

内部类的特殊语法规则

  • 外部类引用:内部类中通过OuterClass.this获取外部类当前对象;
  • 内部类对象创建:非静态内部类需通过外部类对象创建,静态内部类直接通过外部类名创建;
  • 访问权限:内部类可访问外部类成员,外部类需通过内部类对象访问内部类成员。

函数式接口(Functional Interface)

函数式接口的定义

有且仅有一个抽象方法的接口,可使用@FunctionalInterface注解校验(编译时检查是否符合规范)。函数式接口是Lambda表达式的载体。

Lambda表达式

语法格式

(参数列表) -> { 方法体 }

简化规则

  • 若参数列表仅有一个参数,括号可省略;
  • 若方法体仅有一条语句,大括号和return可省略;
  • 参数类型可省略(编译器自动推断)。

方法引用

当Lambda表达式仅调用一个已存在的方法时,可通过方法引用简化,格式:类名/对象::方法名

分类

  1. 静态方法引用:类名::静态方法名
  2. 对象方法引用:对象::实例方法名
  3. 任意对象的实例方法引用:类名::实例方法名
  4. 构造方法引用:类名::new

常见函数式接口

接口名 核心方法 功能描述
Function<T, R> R apply(T t) 接收T类型参数,返回R类型结果
BiFunction<T, U, R> R apply(T t, U u) 接收T、U类型参数,返回R类型结果
Consumer<T> void accept(T t) 接收T类型参数,无返回值
BiConsumer<T, U> void accept(T t, U u) 接收T、U类型参数,无返回值
Supplier<T> T get() 无参数,返回T类型结果
Predicate<T> boolean test(T t) 接收T类型参数,返回布尔值

实战示例

以下示例结合常见函数式接口,实现“学生数据处理”场景,涵盖Lambda简化、方法引用用法:

package com.inherit.implement;

import java.util.Arrays;
import java.util.function.*;

/**
 * @author Jing61
 */
// 学生实体类(用于数据处理)
class Student {
    private String name;
    private int score;

    // 构造方法
    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }

    public Student() {

    }

    // getter/setter
    public String getName() { return name; }
    public int getScore() { return score; }
    public void setScore(int score) { this.score = score; }

    // 静态方法:分数翻倍(用于静态方法引用)
    public static int doubleScore(int score) {
        return score * 2;
    }

    // 实例方法:打印学生信息(用于对象方法引用)
    public void printInfo() {
        System.out.println("姓名:" + name + ",分数:" + score);
    }

    @Override
    public String toString() {
        return name + "(" + score + ")";
    }
}

// 测试类
public class FunctionalInterfaceDemo {
    public static void main(String[] args) {
        // 初始化学生数组
        Student[] students = {
            new Student("张三", 85),
            new Student("李四", 60),
            new Student("王五", 92),
            new Student("赵六", 78)
        };

        // 一、Function<T, R>:接收T类型,返回R类型(分数处理)
        System.out.println("=== 1. Function:分数翻倍 ===");
        // Lambda表达式:接收Student,返回翻倍后的分数
        Function<Student, Integer> scoreDoubler = s -> Student.doubleScore(s.getScore());
        for (Student s : students) {
            int newScore = scoreDoubler.apply(s);
            System.out.println(s.getName() + "原分数:" + s.getScore() + ",翻倍后:" + newScore);
        }

        // 简化:方法引用(静态方法引用)
        Function<Student, Integer> scoreDoublerRef = s -> Student.doubleScore(s.getScore());
        System.out.println("方法引用实现:" + scoreDoublerRef.apply(students[0]));

        // 二、Predicate<T>:接收T类型,返回布尔值(条件筛选)
        System.out.println("\n=== 2. Predicate:筛选及格学生(≥60分) ===");
        // Lambda表达式:判断分数是否及格
        Predicate<Student> isPass = s -> s.getScore() >= 60;
        for (Student s : students) {
            if (isPass.test(s)) {
                System.out.println(s.getName() + ":及格");
            } else {
                System.out.println(s.getName() + ":不及格");
            }
        }

        // 三、Consumer<T>:接收T类型,无返回值(数据消费)
        System.out.println("\n=== 3. Consumer:打印所有学生信息 ===");
        // Lambda表达式:打印学生信息
        Consumer<Student> printStudent = s -> s.printInfo();
        Arrays.stream(students).forEach(printStudent);

        // 简化:方法引用(对象方法引用)
        Consumer<Student> printStudentRef = Student::printInfo;
        System.out.println("方法引用实现:");
        Arrays.stream(students).forEach(printStudentRef);

        // 四、BiFunction<T, U, R>:接收两个参数,返回R类型(修改分数)
        System.out.println("\n=== 4. BiFunction:给学生加分 ===");
        // Lambda表达式:接收Student和加分值,返回加分后的学生
        BiFunction<Student, Integer, Student> addScore = (s, num) -> {
            s.setScore(s.getScore() + num);
            return s;
        };
        Student updatedStudent = addScore.apply(students[1], 10);
        System.out.println(updatedStudent.getName() + "加分后:" + updatedStudent.getScore());

        // 五、Supplier<T>:无参数,返回T类型(创建对象)
        System.out.println("\n=== 5. Supplier:创建新学生 ===");
        // Lambda表达式:无参数,返回新的Student对象
        Supplier<Student> studentCreator = () -> new Student("孙七", 88);
        Student newStudent = studentCreator.get();
        newStudent.printInfo();

        // 简化:方法引用(构造方法引用)
        Supplier<Student> studentCreatorRef = Student::new; // 对应无参构造(需补全无参构造)
        // 若用带参构造,需用BiFunction(因为两个参数)
        BiFunction<String, Integer, Student> studentCreatorWithParams = Student::new;
        Student newStudent2 = studentCreatorWithParams.apply("周八", 95);
        newStudent2.printInfo();

        // 六、BiConsumer<T, U>:接收两个参数,无返回值(批量修改+打印)
        System.out.println("\n=== 6. BiConsumer:批量加分并打印 ===");
        BiConsumer<Student, Integer> addAndPrint = (s, num) -> {
            s.setScore(s.getScore() + num);
            s.printInfo();
        };
        addAndPrint.accept(students[2], 5);
    }
}

关键用法说明

  1. Lambda简化对比

    • 完整Lambda:(s, num) -> { s.setScore(s.getScore() + num); return s; }
    • 简化后(单语句):s -> Student.doubleScore(s.getScore())(省略大括号和return)
  2. 方法引用场景

    • 静态方法引用:Student::doubleScore(对应Student.doubleScore(score)
    • 对象方法引用:Student::printInfo(对应student.printInfo()
    • 构造方法引用:Student::new(对应new Student()new Student(name, score)
  3. 运行结果
    会依次输出分数翻倍、及格筛选、学生信息打印、加分修改、对象创建等结果,直观体现各函数式接口的核心作用。

类设计原则(SOLID)

  1. 单一职责原则(SRP):一个类仅负责一个功能,仅有一个引起变化的原因;
  2. 开放封闭原则(OCP):一个软件实体如类、模块和函数应该对外外扩展开放,对修改关闭;
  3. 里氏替换原则(LSP):子类必须能够替换掉父类,且不会改变程序的正确性;
  4. 接口隔离原则(ISP):客户端不应依赖不需要的接口,接口应细化;
  5. 依赖倒置原则(DIP):抽象不依赖细节,细节依赖抽象;高层模块与低层模块均依赖抽象。
posted @ 2025-11-06 16:48  Jing61  阅读(6)  评论(0)    收藏  举报