活动

天翼云最新优惠活动,涵盖免费试用,产品折扣等,助您降本增效!
查看全部活动
热门活动
  • 免费体验DeepSeek,上天翼云息壤 NEW 新老用户均可免费体验2500万Tokens,限时两周
  • 云上钜惠 HOT 爆款云主机全场特惠,更有万元锦鲤券等你来领!
  • 算力套餐 HOT 让算力触手可及
  • 天翼云脑AOne NEW 连接、保护、办公,All-in-One!
  • 一键部署Llama3大模型学习机 0代码一键部署,预装最新主流大模型Llama3与StableDiffusion
  • 中小企业应用上云专场 产品组合下单即享折上9折起,助力企业快速上云
  • 息壤高校钜惠活动 NEW 天翼云息壤杯高校AI大赛,数款产品享受线上订购超值特惠
  • 天翼云电脑专场 HOT 移动办公新选择,爆款4核8G畅享1年3.5折起,快来抢购!
  • 天翼云奖励推广计划 加入成为云推官,推荐新用户注册下单得现金奖励
免费活动
  • 免费试用中心 HOT 多款云产品免费试用,快来开启云上之旅
  • 天翼云用户体验官 NEW 您的洞察,重塑科技边界

智算服务

打造统一的产品能力,实现算网调度、训练推理、技术架构、资源管理一体化智算服务
智算云(DeepSeek专区)
科研助手
  • 算力商城
  • 应用商城
  • 开发机
  • 并行计算
算力互联调度平台
  • 应用市场
  • 算力市场
  • 算力调度推荐
一站式智算服务平台
  • 模型广场
  • 体验中心
  • 服务接入
智算一体机
  • 智算一体机
大模型
  • DeepSeek-R1-昇腾版(671B)
  • DeepSeek-R1-英伟达版(671B)
  • DeepSeek-V3-昇腾版(671B)
  • DeepSeek-R1-Distill-Llama-70B
  • DeepSeek-R1-Distill-Qwen-32B
  • Qwen2-72B-Instruct
  • StableDiffusion-V2.1
  • TeleChat-12B

应用商城

天翼云精选行业优秀合作伙伴及千余款商品,提供一站式云上应用服务
进入甄选商城进入云市场创新解决方案
办公协同
  • WPS云文档
  • 安全邮箱
  • EMM手机管家
  • 智能商业平台
财务管理
  • 工资条
  • 税务风控云
企业应用
  • 翼信息化运维服务
  • 翼视频云归档解决方案
工业能源
  • 智慧工厂_生产流程管理解决方案
  • 智慧工地
建站工具
  • SSL证书
  • 新域名服务
网络工具
  • 翼云加速
灾备迁移
  • 云管家2.0
  • 翼备份
资源管理
  • 全栈混合云敏捷版(软件)
  • 全栈混合云敏捷版(一体机)
行业应用
  • 翼电子教室
  • 翼智慧显示一体化解决方案

合作伙伴

天翼云携手合作伙伴,共创云上生态,合作共赢
天翼云生态合作中心
  • 天翼云生态合作中心
天翼云渠道合作伙伴
  • 天翼云代理渠道合作伙伴
天翼云服务合作伙伴
  • 天翼云集成商交付能力认证
天翼云应用合作伙伴
  • 天翼云云市场合作伙伴
  • 天翼云甄选商城合作伙伴
天翼云技术合作伙伴
  • 天翼云OpenAPI中心
  • 天翼云EasyCoding平台
天翼云培训认证
  • 天翼云学堂
  • 天翼云市场商学院
天翼云合作计划
  • 云汇计划
天翼云东升计划
  • 适配中心
  • 东升计划
  • 适配互认证

开发者

开发者相关功能入口汇聚
技术社区
  • 专栏文章
  • 互动问答
  • 技术视频
资源与工具
  • OpenAPI中心
开放能力
  • EasyCoding敏捷开发平台
培训与认证
  • 天翼云学堂
  • 天翼云认证
魔乐社区
  • 魔乐社区

支持与服务

为您提供全方位支持与服务,全流程技术保障,助您轻松上云,安全无忧
文档与工具
  • 文档中心
  • 新手上云
  • 自助服务
  • OpenAPI中心
定价
  • 价格计算器
  • 定价策略
基础服务
  • 售前咨询
  • 在线支持
  • 在线支持
  • 工单服务
  • 建议与反馈
  • 用户体验官
  • 服务保障
  • 客户公告
  • 会员中心
增值服务
  • 红心服务
  • 首保服务
  • 客户支持计划
  • 专家技术服务
  • 备案管家

了解天翼云

天翼云秉承央企使命,致力于成为数字经济主力军,投身科技强国伟大事业,为用户提供安全、普惠云服务
品牌介绍
  • 关于天翼云
  • 智算云
  • 天翼云4.0
  • 新闻资讯
  • 天翼云APP
基础设施
  • 全球基础设施
  • 信任中心
最佳实践
  • 精选案例
  • 超级探访
  • 云杂志
  • 分析师和白皮书
  • 天翼云·创新直播间
市场活动
  • 2025智能云生态大会
  • 2024智算云生态大会
  • 2023云生态大会
  • 2022云生态大会
  • 天翼云中国行
天翼云
  • 活动
  • 智算服务
  • 产品
  • 解决方案
  • 应用商城
  • 合作伙伴
  • 开发者
  • 支持与服务
  • 了解天翼云
    • 关系数据库SQL Server版
    • 企业主机安全
    • 云防火墙
    • CDN加速
    • 物理机
    • GPU云主机
    • 天翼云电脑(政企版)
    • 天翼云电脑(公众版)
    • 云主机备份
    • 弹性云主机
      搜索发现
      关系数据库SQL Server版企业主机安全云防火墙CDN加速物理机GPU云主机天翼云电脑(政企版)天翼云电脑(公众版)云主机备份弹性云主机
    • 文档
    • 控制中心
    • 备案
    • 管理中心
    • 登录
    • 免费注册

    JDK8

    首页 知识中心 其他 文章详情页

    JDK8

    2023-05-30 08:04:45 阅读次数:422

    lambda

    介绍

    随着java的发展,越来越多的企业开始使用​​java8​​版本。Java8是自从java5之后最重要的版本,这个版本包含语言、编译器、库、工具、JVM等方面的十多个新特性。

    ????JDK8新增的特性如下

    • Lambda表达式
    • 新的日期API、Datetime
    • 引入Optional防止空指针异常
    • 使用Base64
    • 接口的默认方法和静态方法
    • 新增方法引用格式
    • 新增Stream类
    • 注解相关的改变
    • 支持并行(parallel)数组
    • 对并发类(Concurrency)的扩展
    • JavaFX

    接口新特性

    接口默认方法

    当我们去实现某个框架提供的一个接口时,需要实现其所有的抽象方法,当该框架更新版本,在这个借口中加入了新的抽象方法时,我们就需要对项目重新编译,并且实现其新增的方法。当实现类太多时,操作起来很麻烦

    JDK之前是使用开闭设计模式:对扩展开放,对修改关闭。即:创建一个新的接口,继承原有的接口,定义新的方法

    但是这样的话,原本的那些实现类并没有新的方法

    这时候可以使用接口默认方法

    关键字使用​​default​​进行修饰, 方法需要方法体。这样的方法所有的子类会默认实现(不用自己写),如果想要覆盖重写,也可以在实现类中覆盖重写

    public interface IUserService {
        void method1();
        void method2();
    }
    public class IUserImpl implements IUserService {
        @Override
        public void method1() {
            System.out.println("method1");
        }
    
        @Override
        public void method2() {
            System.out.println("method2");
        }
    }
    public class IUser2Impl implements IUserService {
        @Override
        public void method1() {
            System.out.println("method1");
        }
    
        @Override
        public void method2() {
            System.out.println("method2");
        }
    }

    在上面的示例中一个接口有两个实现类,假如这时需要在接口当中新增一个功能,该接口的所有实现类都需要实现该接口,这样就很麻烦,这时就可以使用接口默认方法,改造后的接口为

    public interface IUserService {
        void method1();
        void method2();
    
        default void methodNew(){
            System.out.println("新增的功能");
        };
    }

    进行测试

    public class MyTest {
    
        @Test
        public void test(){
            IUserService user = new IUser2Impl();
            user.methodNew();
        }
    
    }

    这个时候假如

    这里需要注意的是:这里的default是JDK8新增的关键字,和访问限定修饰符​​default​​不是一个概念,与switch中的default功能完全不同

    与抽象类的不同:抽象类更多的是提供一个模板,子类之间的某个流程大致相同,仅仅是某个步骤可能不一样(模板方法设计模式),这个时候使用抽象类,该步骤定义为抽象方法。而default关键字是用于扩展

    接口静态默认方法

    /**
     * @author :tangyihao
     * @version :V1.0
     * @program :jdk8
     * @date :Created in 2020/8/12 16:25
     * @description :从Java8开始,接口当中允许定义静态方法
     *                修饰符:static xxx
     *                一般类的静态方法用法相同
     */
    public interface IAnimal {
        void method();
    
        static void getUser() {
            System.out.println("静态接口方法");
        }
    }

    接口的静态方法不会被实现类所继承

    测试

    public class MyTest {
    
        @Test
        public void test(){
            IAnimal.getUser();
        }
    
    }

    函数式接口

    概念

    函数式接口在Java中是指:有且仅有一个抽象方法的接口

    函数式接口,即适用于函数式编程场景的接口。而Java中的函数式编程体现就是Lambda,所以函数式接口就是可以适用于Lambda使用的接口。只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导

    格式

    确保接口中有且只有一个抽象方法即可

    public interface 接口名称 {
        返回值 方法名称();
    }

    ????@FunctionalInterface注解

    有的注解是在编译期起作用,如@Override注解。而@FunctionalInterface也是在编译期起作用。该注解是Java8专门为函数式接口引入的新的注解,作用于一个接口上。一旦使用该注解来定义接口,编译期会强制检查该接口是否符合函数式接口的条件,不符合则会报错。需要注意的是:即使不使用该注解,只要满足函数式接口的定义,该接口就是一个函数式接口

    使用注解

    @FunctionalInterface
    public interface MyFunctionInterface {
        void method();
    
        default void defaultMethod(){
            System.out.println("接口默认方法");
        }
    }

    不使用注解

    public interface MyFunctionInterface {
        void method();
    
        default void defaultMethod(){
            System.out.println("接口默认方法");
        }
    }

    Lambda表达式

    在面向对象的基础上,Java8通过Lambda表达式与方法引用等,为开发者打开了函数式编程的大门。Lambda表达式不是语法糖,而是新的语法

    语法

    三要素:参数、箭头、代码

    (参数) -> {
      代码
    }

    (参数类型 参数1, 参数类型 参数2, ....) -> {代码}

    首先定义函数式接口

    @FunctionalInterface
    public interface MyFunctionInterface {
        void method(String str);
    
        default void defaultMethod() {
            System.out.println("接口默认方法");
        }
    }

    测试

    public class MyTest {
    
        @Test
        public void test() {
            getMethod((String str) -> {
                System.out.println(str);
            });
        }
    
        private void getMethod(MyFunctionInterface functionInterface) {
            functionInterface.method("lamdba表达式");
        }
    }
    1. 如果参数有多个,那么使用逗号分隔。如果参数没有,则留空
    @FunctionalInterface
    public interface MyFunctionInterface {
        void method();
    
        default void defaultMethod() {
            System.out.println("接口默认方法");
        }
    }
    public class MyTest {
    
        @Test
        public void test() {
            getMethod(() -> {
            });
        }
    
        private void getMethod(MyFunctionInterface functionInterface) {
            functionInterface.method();
        }
    }
    1. 箭头是固定写法
    2. 大括号相当于方法体

    使用Lambda表达式的必要前提:必须是​​函数式接口​​

    Lambda 省略规则

    • 参数类型可以省略。但是只能同时省略所有参数的类型,或者干脆都不省略
    @FunctionalInterface
    public interface MyFunctionInterface {
        void method(String str, Integer age);
    
        default void defaultMethod() {
            System.out.println("接口默认方法");
        }
    }
    public class MyTest {
    
        @Test
        public void test() {
            getMethod((str, age) -> {
                System.out.println(str + age);
            });
        }
    
        private void getMethod(MyFunctionInterface functionInterface) {
            functionInterface.method("BNTang", 23);
        }
    }
    • 如果参数有且仅有一个,那么小括号可以省略
    @FunctionalInterface
    public interface MyFunctionInterface {
        void method(String str);
    
        default void defaultMethod() {
            System.out.println("接口默认方法");
        }
    }
    public class MyTest {
    
        @Test
        public void test() {
            getMethod(str -> {
                System.out.println(str);
            });
        }
    
        private void getMethod(MyFunctionInterface functionInterface) {
            functionInterface.method("BNTang");
        }
    }
    • 如果大括号内的语句有且仅有一条,那么无论是否有返回值,return、大括号、分号都可以省略
    public class MyTest {
    
        @Test
        public void test() {
            getMethod(str ->
                    System.out.println(str)
            );
        }
    
        private void getMethod(MyFunctionInterface functionInterface) {
            functionInterface.method("BNTang");
        }
    }

    Lambda的延迟执行

    有些场景的代码执行后,结果不一定会被使用,从而造成性能浪费。而Lambda表达式是延迟执行的,这正好可以作为解决方案,提升性能

    ????性能浪费

    public class MyTest {
    
        @Test
        public void test() {
            getMethod(str ->
                    System.out.println(str)
            );
        }
    
        private void getMethod(MyFunctionInterface functionInterface) {
            functionInterface.method("BNTang");
        }
    }public class DemoLogger {
        private static void log(int level, String msg) {
            if (level == 1) {
                System.out.println(msg);
            }
        }
    
        public static void main(String[] args) {
            String msgA = "Hello ";
            String msgB = "World ";
            String msgC = "Java";
            log(1, msgA + msgB + msgC);
        }
    }

    这段代码存在问题:无论级别是否满足要求,作为 log 方法的第二个参数,三个字符串一定会首先被拼接并传入方法内,然后才会进行级别判断。如果级别不符合要求,那么字符串的拼接操作就白做了,存在性能浪费

    ????Lambda表达式优化写法

    @FunctionalInterface
    public interface MessageBuilder {
        String buildMessage();
    }
    public class DemoLogger {
        private static void log(int level, MessageBuilder messageBuilder) {
            if (level == 1) {
                System.out.println(messageBuilder.buildMessage());
            }
        }
    
        public static void main(String[] args) {
            String msgA = "Hello ";
            String msgB = "World ";
            String msgC = "Java";
            log(2, () -> msgA + msgB + msgC);
        }
    }

    这样一来,只有当满足条件的时候才会进行三个字符串的拼接。否则不会拼接

    ????证明Lambda的延迟

    public class DemoLogger {
        private static void log(int level, MessageBuilder messageBuilder) {
            if (level == 1) {
                System.out.println(messageBuilder.buildMessage());
            }
        }
    
        public static void main(String[] args) {
            String msgA = "Hello ";
            String msgB = "World ";
            String msgC = "Java";
            log(2, () -> msgA + msgB + msgC);
        }
    }

    从打印结果可以看出,在不符合条件要求的情况下,lambda将不会执行

    使用Lambda作为参数和返回值

    如果抛开实现原理不说,Java中的Lambda表达式可以被当作是匿名内部类的替代品。如果方法的参数是一个函数式接口类型,那么就可以使用Lambda表达式进行替代。使用Lambda表达式作为方法参数,其实就是使用函数式接口作为方法参数

    例如:java.lang.Runnable接口就是一个函数式接口,假设有一个startThread方法使用该接口作为参数,那么就可以使用Lambda进行传参。这种情况其实和Thread类的构造方法参数为Runnable没有本质区别

    public class DemoRunnable {
        private static void startThread(Runnable task) {
            new Thread(task).start();
        }
    
        public static void main(String[] args) {
            startThread(() -> System.out.println("线程任务执行!"));
        }
    }

    类似地,如果一个方法的返回值类型是一个函数式接口,那么就可以直接返回一个Lambda表达式。当需要通过一个方法来获取一个java.util.Comparator接口类型的对象作为排序器时,就可以调该方法获取

    public class DemoComparator {
        private static Comparator<String> newComparator() {
            return (a, b) -> b.length() - a.length();
        }
    
        public static void main(String[] args) {
            String[] array = {"abc", "ab", "abcd"};
            System.out.println(Arrays.toString(array));
    
            Arrays.sort(array, newComparator());
            System.out.println(Arrays.toString(array));
        }
    }

    常用函数式接口

    JDK提供了大量的函数式接口以及丰富的Lambda应用场景。下面是最简单的几个接口以及使用实例

    JDK8

    JDK8

    JDK8

    JDK8

    Supplier

    java.util.function.Supplier 接口仅包含一个无参的方法: T get() 。用来获取一个泛型参数指定类型的对象数据。由于这是一个函数式接口,这也就意味着对应的Lambda表达式需要​​对外提供​​一个符合泛型类型的对象数据

    public class DemoSupplier {
        private static String getString(Supplier<String> function) {
            return function.get();
        }
    
        public static void main(String[] args) {
            String msgA = "Hello";
            String msgB = "World";
            System.out.println(getString(() -> msgA + msgB));
        }
    }

    ????求数组元素的最大值

    public class DemoTest {
        public static int getMax(Supplier<Integer> sup) {
            return sup.get();
        }
    
        public static void main(String[] args) {
            int[] arr = {2, 3, 4, 52, 333, 23};
            int maxNum = getMax(() -> {
                int max = arr[0];
                for (int i : arr) {
                    if (i > max) {
                        max = i;
                    }
                }
                return max;
            });
            System.out.println(maxNum);
        }
    }

    Consumer接口

    java.util.function.Consumer 接口则正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据,其数据类型由泛型决定

    • 抽象方法:accept,为消费一个指定泛型的数据
    public class DemoConsumer {
        private static void consumeString(Consumer<String> function) {
            function.accept("Hello");
        }
    
        public static void main(String[] args) {
            consumeString(s -> System.out.println(s));
        }
    }
    • 默认方法:andThen

    如果一个方法的参数和返回值全都是Consumer类型,那么就可以实现效果:消费数据的时候,首先做一个操作,然后再做一个操作,实现组合。而这个方法就是Consumer接口中的default方法 andThen

    要想实现组合,需要两个或多个Lambda表达式即可,而andThen的语义正是​​一步接一步​​操作

    public class DemoConsumerAndThen {
        private static void consumeString(Consumer<String> one, Consumer<String> two) {
            one.andThen(two).accept("Hello");
        }
    
        public static void main(String[] args) {
            consumeString(s -> System.out.println(s.toUpperCase()), s -> System.out.println(s.toLowerCase()));
        }
    }

    ????格式化打印信息

    • 下面的字符串数组当中存有多条信息,请按照格式“ 姓名:XX。性别:XX。 ”的格式将信息打印出来。要求将打印姓名的动作作为第一个Consumer接口的Lambda实例,将打印性别的动作作为第二个Consumer接口的Lambda实例,将两个 Consumer接口按照顺序“拼接”到一起
    public class DemoConsumer {
        public static void main(String[] args) {
            String[] array = {"迪丽热巴,女", "蛇哥,男", "骚男,男"};
    
            printInfo(s -> System.out.print("姓名:" + s.split(",")[0]), s -> System.out.println("。性别:" + s.split(",")[1] + "。"), array);
        }
    
        private static void printInfo(Consumer<String> one, Consumer<String> two, String[] array) {
            for (String info : array) {
                one.andThen(two).accept(info);
            }
        }
    }

    Predicate接口

    有时候我们需要对某种类型的数据进行判断,从而得到一个boolean值结果。这时可以使用java.util.function.Predicate 接口

    • 抽象方法:test
    public class DemoPredicateTest {
        private static void method(Predicate<String> predicate) {
            boolean veryLong = predicate.test("HelloWorld");
            System.out.println("字符串很长吗:" + veryLong);
        }
    
        public static void main(String[] args) {
            method(s -> s.length() > 5);
        }
    }

    条件判断的标准是传入lambda表达式逻辑

    • 默认方法:and

    既然是条件判断,就会存在与、或、非三种常见的逻辑关系。其中将两个Predicate条件使用​​与​​​逻辑连接起来实现​​并且​​的效果时,可以使用default方法and,如果要判断一个字符串既要包含大写“H”,又要包含大写“W”

    public class DemoPredicateAnd {
        private static void method(Predicate<String> one, Predicate<String> two) {
            boolean isValid = one.and(two).test("Helloworld");
            System.out.println("字符串符合要求吗:" + isValid);
        }
    
        public static void main(String[] args) {
            method(s -> s.contains("H"), s -> s.contains("W"));
        }
    }
    • 默认方法:or

    如果希望实现逻辑“字符串包含大写H或者包含大写W”,那么代码只需要将“and”修改为“or”名称即可,其他都不变

    public class DemoPredicateAnd {
        private static void method(Predicate<String> one, Predicate<String> two) {
            boolean isValid = one.or(two).test("Helloworld");
            System.out.println("字符串符合要求吗:" + isValid);
        }
    
        public static void main(String[] args) {
            method(s -> s.contains("H"), s -> s.contains("W"));
        }
    }
    • 默认方法:negate

    表示取反

    public class DemoPredicateNegate {
        private static void method(Predicate<String> predicate) {
            boolean veryLong = predicate.negate().test("HelloWorld");
            System.out.println("字符串很长吗:" + veryLong);
        }
    
        public static void main(String[] args) {
            method(s -> s.length() < 5);
        }
    }

    ????集合信息筛选

    • 数组当中有多条“姓名+性别”的信息如下,请通过Predicate接口的拼接将符合要求的字符串筛选到集合ArrayList 中,需要同时满足两个条件
    • 必须为女生
    • 姓名为4个字
    public class DemoPredicate {
        public static void main(String[] args) {
            String[] array = {"迪丽热巴,女", "蛇哥,男", "骚男,男", "赵丽颖,女"};
            List<String> list = filter(array, s -> "女".equals(s.split(",")[1]), s -> s.split(",")[0].length() == 4);
            System.out.println(list);
        }
    
        private static List<String> filter(String[] array, Predicate<String> one, Predicate<String> two) {
            List<String> list = new ArrayList<>();
            for (String info : array) {
                if (one.and(two).test(info)) {
                    list.add(info);
                }
            }
            return list;
        }
    }

    Function接口

    java.util.function.Function<T,R> 接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件

    • 抽象方法:apply

    Function 接口中最主要的抽象方法为: R apply(T t) ,根据类型T的参数获取类型R的结果。使用的场景例如:将String类型转换为Integer类型

    public class DemoFunctionApply {
        private static void method(Function<String, Integer> function) {
            int num = function.apply("10");
            System.out.println(num + 20);
        }
    
        public static void main(String[] args) {
            method(s -> Integer.parseInt(s));
        }
    }
    • 默认方法:andThen
    public class DemoFunctionAndThen {
        private static void method(Function<String, Integer> one, Function<Integer, Integer> two) {
            int num = one.andThen(two).apply("10");
            System.out.println(num + 20);
        }
    
        public static void main(String[] args) {
            method(str -> Integer.parseInt(str) + 10, i -> i *= 10);
        }
    }

    ????自定义函数模型拼接

    请使用Function进行函数模型的拼接,按照顺序需要执行的多个函数操作为

    String str = "赵丽颖,20";

    • 将字符串截取数字年龄部分,得到字符串
    • 将上一步的字符串转换成为int类型的数字
    • 将上一步的int数字累加100,得到结果int数字
    public class DemoFunction {
        public static void main(String[] args) {
            String str = "赵丽颖,20";
            int age = getAgeNum(str, s -> s.split(",")[1], s -> Integer.parseInt(s), n -> n += 100);
            System.out.println(age);
        }
    
        private static int getAgeNum(String str, Function<String, String> one, Function<String, Integer> two, Function<Integer, Integer> three) {
            return one.andThen(two).andThen(three).apply(str);
        }
    }

    方法引用

    冗余的Lambda场景

    在使用Lambda表达式的时候,我们实际上传递进去的代码就是一种解决方案:拿什么参数做什么操作。那么考虑一种情况:如果我们在Lambda中所指定的操作方案,已经有地方存在相同方案,那是否还有必要再写重复逻辑

    先看一个简单的函数式接口

    @FunctionalInterface
    public interface Printable {
        void print(String str);
    }
    public class DemoPrintSimple {
        private static void printString(Printable data) {
            data.print("Hello, World!");
        }
    
        public static void main(String[] args) {
            printString(s -> System.out.println(s));
        }
    }

    其中printString方法只管调用Printable接口的print方法,而并不管print方法的具体实现逻辑会将字符串打印到什么地方去。而 main方法通过Lambda表达式指定了函数式接口Printable的具体操作方案为:拿到String(类型可推导,所以可省略)数据后,在控制台中输出它

    问题分析

    这段代码的问题在于,对字符串进行控制台打印输出的操作方案,明明已经有了现成的实现,那就是System.out对象中的 println(String)方法。既然Lambda希望做的事情就是调用println(String) 方法,那何必自己手动调用呢

    能否省去Lambda的语法格式(尽管它已经相当简洁)呢?只要“引用”过去就好了

    public class DemoPrintRef {
        private static void printString(Printable data) {
            data.print("Hello, World!");
        }
    
        public static void main(String[] args) {
            printString(System.out::println);
        }
    }

    请注意其中的双冒号​​::​​写法,这被称为“方法引用”,而双冒号是一种新的语法

    方法引用符

    双冒号​​::​​为引用运算符,而它所在的表达式被称为方法引用。如果Lambda要表达的函数方案已经存在于某个方法的实现中,那么则可以通过双冒号来引用该方法作为Lambda的替代者

    ????语义分析

    例如上例中, System.out对象中有一个重载的println(String)方法恰好就是我们所需要的。那么对printString方法的函数式接口参数,对比下面两种写法,完全等效

    • Lambda表达式写法: s -> System.out.println(s);
    • 方法引用写法: System.out::println

    第一种语义是指:拿到参数之后经Lambda之手,继而传递给System.out.println方法去处理

    第二种等效写法的语义是指:直接让 System.out 中的 println 方法来取代Lambda。两种写法的执行效果完全一样,而第二种方法引用的写法复用了已有方案,更加简洁。

    注:Lambda中传递的参数一定是方法引用中的那个方法可以接收的类型,否则会抛出异常

    ????推导与省略

    如果使用Lambda,那么根据“可推导就是可省略”的原则,无需指定参数类型,也无需指定的重载形式——它们都将被自动推导。而如果使用方法引用,也是同样可以根据上下文进行推导

    函数式接口是Lambda的基础,而方法引用是Lambda的孪生兄弟

    下面这段代码将会调用 println 方法的不同重载形式,将函数式接口改为int类型的参数

    @FunctionalInterface
    public interface PrintableInteger {
        void print(int str);
    }

    由于上下文变了之后可以自动推导出唯一对应的匹配重载,所以方法引用没有任何变化

    public class DemoPrintOverload {
        private static void printInteger(PrintableInteger data) {
            data.print(1024);
        }
    
        public static void main(String[] args) {
            printInteger(System.out::println);
        }
    }

    通过类名称引用静态方法

    由于在java.lang.Math类中已经存在了静态方法 abs ,所以当我们需要通过Lambda来调用该方法时,有两种写法。首先是函数式接口

    @FunctionalInterface
    public interface Calcable {
        int calc(int num);
    }

    第一种写法使用Lambda

    public class DemoLambda {
        private static void method(int num, Calcable lambda) {
            System.out.println(lambda.calc(num));
        }
    
        public static void main(String[] args) {
            method(-10, n -> Math.abs(n));
        }
    }

    第二种使用方法引用

    public class DemoMethodRef {
        private static void method(int num, Calcable lambda) {
            System.out.println(lambda.calc(num));
        }
    
        public static void main(String[] args) {
            method(-10, Math::abs);
        }
    }

    两种方式等价

    通过super引用成员方法

    如果存在继承关系,当Lambda中需要出现super调用时,也可以使用方法引用进行替代。首先是函数式接口

    @FunctionalInterface
    public interface Greetable {
        void greet();
    }

    父类Human的内容

    public class Human {
        public void sayHello() {
            System.out.println("Hello!");
        }
    }

    子类Man的内容

    public class Man extends Human {
        @Override
        public void sayHello() {
            System.out.println("大家好,我是Man!");
        }
    
        /**
         * 定义方法method,参数传递Greetable接口
         * @param g
         */
        public void method(Greetable g) {
            g.greet();
        }
    
        public void show() {
            // 调用method方法,使用Lambda表达式 
            method(() -> {
                // 创建Human对象,调用sayHello方法 
                new Human().sayHello();
            });
            // 简化Lambda 
            method(() -> new Human().sayHello());
            // 使用super关键字代替父类对象 
            method(() -> super.sayHello());
        }
    }

    但是如果使用方法引用会更好

    public class Woman extends Human {
        @Override
        public void sayHello() {
            System.out.println("大家好,我是Man!");
        }
    
        public void method(Greetable g) {
            g.greet();
        }
    
        public void show() {
            method(super::sayHello);
        }
    }

    通过this引用成员方法

    this代表当前对象,如果需要引用的方法就是当前类中的成员方法,那么可以使用“this::成员方法”的格式来使用方法引用。首先是简单的函数式接口

    @FunctionalInterface
    public interface Richable {
        void buy();
    }
    public class Husband {
        private void marry(Richable lambda) {
            lambda.buy();
        }
    
        public void beHappy() {
            marry(() -> System.out.println("买套房子"));
        }
    }

    开心方法 beHappy 调用了结婚方法 marry ,后者的参数为函数式接口 Richable ,所以需要一个Lambda表达式。但是如果这个Lambda表达式的内容已经在本类当中存在了,则可以对 Husband 丈夫类进行修改

    public class Husband {
        private void buyHouse() {
            System.out.println("买套房子");
        }
    
        private void marry(Richable lambda) {
            lambda.buy();
        }
    
        public void beHappy() {
            marry(this::buyHouse);
        }
    }

    通过对象名引用成员方法

    这是最常见的一种用法,与上例相同。如果一个类中已经存在了一个成员方法

    public class User {
    
        private Integer id;
        private String name;
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }

    那么当需要使用这个 setName 成员方法来替代 Function 接口的Lambda的时候,已经具有了User 类的对象实例,则可以通过对象名引用成员方法,代码为

    @Test
    public void testGetUserName() {
        String userName = testGetUserName(User::getName);
        System.out.println(userName);
    }
    
    private String testGetUserName(Function<User, String> function) {
        User user = new User();
        user.setName("BNTang");
        return function.apply(user);
    }

    Stream流

    说到Stream便容易想到I/O Stream,而实际上,在Java 8中,得益于Lambda所带来的函数式编程,引入了一个全新的Stream概念,用于解决已有集合类库既有的弊端

    Stream流式操作性能比传统的For循环要低,就性能而言,传统的for循环最高

    传统集合的遍历代码

    几乎所有的集合(如 Collection 接口或 Map 接口等)都支持直接或间接的遍历操作。而当我们需要对集合中的元素进行操作的时候,除了必需的添加、删除、获取外,最典型的就是集合遍历。例如

    public class DemoForEach {
        public static void main(String[] args) {
            List<String> list = new ArrayList<>();
            list.add("BNTang");
            list.add("赵六");
            list.add("王五");
            list.add("李四");
            list.add("张三");
            for (String name : list) {
                System.out.println(name);
            }
        }
    }

    循环遍历的弊端

    Java 8的Lambda让我们可以更加专注于做什么(What),而不是怎么做(How),这点此前已经结合内部类进行了对比说明。现在,我们仔细体会一下上例代码,可以发现

    • for循环的语法就是“怎么做”
    • for循环的循环体才是“做什么”

    为什么使用循环?因为要进行遍历。但循环是遍历的唯一方式吗?遍历是指每一个元素逐一进行处理,而并不是从第一个到最后一个顺次处理的循环。前者是目的,后者是方式。

    思想一下,如果希望对集合中的元素进行筛选过滤

    1. 将集合A根据条件,过滤为子集B
    2. 然后再根据条件二过滤为子集C

    那怎么办?在Java 8之前的做法可能为

    public class DemoNormalFilter {
        public static void main(String[] args) {
            List<String> list = new ArrayList<>();
            list.add("BNTang");
            list.add("赵六");
            list.add("王五");
            list.add("李四");
            list.add("张三维");
            List<String> zhangList = new ArrayList<>();
            for (String name : list) {
                if (name.startsWith("张")) {
                    zhangList.add(name);
                }
            }
            List<String> shortList = new ArrayList<>();
            for (String name : zhangList) {
                if (name.length() == 3) {
                    shortList.add(name);
                }
            }
            for (String name : shortList) {
                System.out.println(name);
            }
        }
    }

    这段代码中含有三个循环,每一个作用不同

    1. 首先筛选所有姓张的人
    2. 然后筛选名字有三个字的人
    3. 最后进行对结果进行打印输出

    每当我们需要对集合中的元素进行操作的时候,总是需要进行循环、循环、再循环。这是理所当然的么?不是。循环是做事情的方式,而不是目的。另一方面,使用线性循环就意味着只能遍历一次。如果希望再次遍历,只能再使用另一个循环从头开始,那,Lambda的衍生物​​Stream​​能给我们带来怎样更加优雅的写法呢

    Stream更优写法

    public class DemoStreamFilter {
        public static void main(String[] args) {
            List<String> list = new ArrayList<>();
            list.add("BNTang");
            list.add("赵六");
            list.add("王五");
            list.add("李四");
            list.add("张三维");
            list.stream().filter(s -> s.startsWith("张"))
                    .filter(s -> s.length() == 3)
                    .forEach(System.out::println);
        }
    }

    直接阅读代码的字面意思即可完美展示无关逻辑方式的语义:获取流、过滤姓张、过滤长度为3、逐一打印。代码中并没有体现使用线性循环或是其他任何算法进行遍历,我们真正要做的事情内容被更好地体现在代码中

    获取流

    java.util.stream.Stream 是Java 8新加入的最常用的流接口。(这并不是一个函数式接口。)

    获取一个流非常简单,有以下几种常用的方式

    • 所有的 Collection 集合都可以通过 stream 默认方法获取流
    • Stream 接口的静态方法 of 可以获取数组对应的流

    根据Collection获取流

    首先, java.util.Collection 接口中加入了default方法 stream 用来获取流,所以其所有实现类均可获取流

    public class DemoGetStream {
        public static void main(String[] args) {
            List<String> list = new ArrayList<>();
            Stream<String> stream1 = list.stream();
            Set<String> set = new HashSet<>();
            Stream<String> stream2 = set.stream();
        }
    }

    根据Map获取流

    java.util.Map 接口不是 Collection 的子接口,且其K-V数据结构不符合流元素的单一特征,所以获取对应的流需要分key、value或entry等情况

    public class DemoGetStream {
        public static void main(String[] args) {
            Map<String, String> map = new HashMap<>();
            Stream<String> keyStream = map.keySet().stream();
            Stream<String> valueStream = map.values().stream();
            Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream();
        }
    }

    根据数组获取流

    如果使用的不是集合或映射而是数组,由于数组对象不可能添加默认方法,所以 Stream 接口中提供了静态方法 of ,使用很简单

    public class DemoGetStream {
        public static void main(String[] args) {
            String[] array = {"骚男", "蛇哥", "张三", "BNTang"};
            Stream<String> stream = Stream.of(array);
        }
    }

    常用方法

    逐一处理forEach

    虽然方法名字叫forEach,但是与for循环不同

    public class DemoStreamForEach {
        public static void main(String[] args) {
            Stream<String> stream = Stream.of("BNTang", "JonathanLee", "张三");
            stream.forEach(System.out::println);
        }
    }

    过滤filter

    可以通过 filter 方法将一个流转换成另一个子集流

    public class DemoStreamFilter {
        public static void main(String[] args) {
            Stream<String> original = Stream.of("BNTang", "JonathanLee", "张三");
            Stream<String> result = original.filter(s -> s.startsWith("张"));
        }
    }

    在这里通过Lambda表达式来指定了筛选的条件:必须姓张

    映射map

    如果需要将流中的元素映射到另一个流中,可以使用 map 方法。方法签名

    public class DemoStreamMap {
        public static void main(String[] args) {
            Stream<String> original = Stream.of("10", "12", "18");
            Stream<Integer> result = original.map(Integer::parseInt);
        }
    }

    这段代码中, map 方法的参数通过方法引用,将字符串类型转换成为了int类型(并自动装箱为 Integer 类对象)

    统计个数count

    public class DemoStreamCount {
        public static void main(String[] args) {
            Stream<String> original = Stream.of("BNTang", "JonathanLee", "张三");
            Stream<String> result = original.filter(s -> s.startsWith("张"));
            System.out.println(result.count());
        }
    }

    取用前几个limit

    public class DemoStreamLimit {
        public static void main(String[] args) {
            Stream<String> original = Stream.of("BNTang", "JonathanLee", "张三");
            Stream<String> result = original.limit(2);
            result.forEach(System.out::println);
        }
    }

    跳过前几个skip

    public class DemoStreamSkip {
        public static void main(String[] args) {
            Stream<String> original = Stream.of("BNTang", "JonathanLee", "张三");
            Stream<String> result = original.skip(2);
            result.forEach(System.out::println);
        }
    }

    组合concat

    如果有两个流,希望合并成为一个流,那么可以使用 Stream 接口的静态方法 concat

    public class DemoStreamConcat {
        public static void main(String[] args) {
            Stream<String> streamA = Stream.of("BNTang");
            Stream<String> streamB = Stream.of("张三");
            Stream<String> result = Stream.concat(streamA, streamB);
            result.forEach(System.out::println);
        }
    }

    现在有两个 ArrayList 集合存储队伍当中的多个成员姓名,要求使用传统的for循环(或增强for循环)依次进行以下若干操作步骤

    1. 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中
    2. 第一个队伍筛选之后只要前3个人;存储到一个新集合中
    3. 第二个队伍只要姓张的成员姓名;存储到一个新集合中
    4. 第二个队伍筛选之后不要前2个人;存储到一个新集合中
    5. 将两个队伍合并为一个队伍;存储到一个新集合中
    6. 根据姓名创建 User 对象;存储到一个新集合中
    7. 打印整个队伍的User对象信息

    ????集合元素处理(传统方式)

    public class User {
    
        private Integer id;
        private String name;
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public User(String name) {
            this.name = name;
        }
        
        public User() {}
    
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", name=&#39;" + name + &#39;\&#39;&#39; +
                    &#39;}&#39;;
        }
    }
    public class DemoArrayListNames {
        public static void main(String[] args) {
            ArrayList<String> one = new ArrayList<>();
            one.add("迪丽热巴");
            one.add("宋远桥");
            one.add("苏星河");
            one.add("石破天");
            one.add("石中玉");
            one.add("老子");
            one.add("庄子");
            one.add("洪七公");
            ArrayList<String> two = new ArrayList<>();
            two.add("古力娜扎");
            two.add("张无忌");
            two.add("赵丽颖");
            two.add("张三丰");
            two.add("尼古拉斯赵四");
            two.add("张天爱");
            two.add("张二狗");
    
            // 第一个队伍只要名字为3个字的成员姓名;
            List<String> oneA = new ArrayList<>();
            for (String name : one) {
                if (name.length() == 3) {
                    oneA.add(name);
                }
            }
            // 第一个队伍筛选之后只要前3个人; 
            List<String> oneB = new ArrayList<>();
            for (int i = 0; i < 3; i++) {
                oneB.add(oneA.get(i));
            }
            // 第二个队伍只要姓张的成员姓名; 
            List<String> twoA = new ArrayList<>();
            for (String name : two) {
                if (name.startsWith("张")) {
                    twoA.add(name);
                }
            }
            // 第二个队伍筛选之后不要前2个人; 
            List<String> twoB = new ArrayList<>();
            for (int i = 2; i < twoA.size(); i++) {
                twoB.add(twoA.get(i));
            }
            // 将两个队伍合并为一个队伍; 
            List<String> totalNames = new ArrayList<>();
            totalNames.addAll(oneB);
            totalNames.addAll(twoB);
            // 根据姓名创建User对象; 
            List<User> totalUserList = new ArrayList<>();
            for (String name : totalNames) {
                User user = new User();
                user.setName(name);
                totalUserList.add(user);
            }
        }
    }public class DemoArrayListNames {
        public static void main(String[] args) {
            ArrayList<String> one = new ArrayList<>();
            one.add("迪丽热巴");
            one.add("宋远桥");
            one.add("苏星河");
            one.add("石破天");
            one.add("石中玉");
            one.add("老子");
            one.add("庄子");
            one.add("洪七公");
            ArrayList<String> two = new ArrayList<>();
            two.add("古力娜扎");
            two.add("张无忌");
            two.add("赵丽颖");
            two.add("张三丰");
            two.add("尼古拉斯赵四");
            two.add("张天爱");
            two.add("张二狗");
    
            // 第一个队伍只要名字为3个字的成员姓名;
            List<String> oneA = new ArrayList<>();
            for (String name : one) {
                if (name.length() == 3) {
                    oneA.add(name);
                }
            }
            // 第一个队伍筛选之后只要前3个人; 
            List<String> oneB = new ArrayList<>();
            for (int i = 0; i < 3; i++) {
                oneB.add(oneA.get(i));
            }
            // 第二个队伍只要姓张的成员姓名; 
            List<String> twoA = new ArrayList<>();
            for (String name : two) {
                if (name.startsWith("张")) {
                    twoA.add(name);
                }
            }
            // 第二个队伍筛选之后不要前2个人; 
            List<String> twoB = new ArrayList<>();
            for (int i = 2; i < twoA.size(); i++) {
                twoB.add(twoA.get(i));
            }
            // 将两个队伍合并为一个队伍; 
            List<String> totalNames = new ArrayList<>();
            totalNames.addAll(oneB);
            totalNames.addAll(twoB);
            // 根据姓名创建User对象; 
            List<User> totalUserList = new ArrayList<>();
            for (String name : totalNames) {
                User user = new User();
                user.setName(name);
                totalUserList.add(user);
            }
        }
    }

    ????集合元素处理(Stream方式)

    public class DemoArrayListNames {
        public static void main(String[] args) {
            ArrayList<String> one = new ArrayList<>();
            one.add("迪丽热巴");
            one.add("宋远桥");
            one.add("苏星河");
            one.add("石破天");
            one.add("石中玉");
            one.add("老子");
            one.add("庄子");
            one.add("洪七公");
            ArrayList<String> two = new ArrayList<>();
            two.add("古力娜扎");
            two.add("张无忌");
            two.add("赵丽颖");
            two.add("张三丰");
            two.add("尼古拉斯赵四");
            two.add("张天爱");
            two.add("张二狗");
    
            // 第一个队伍只要名字为3个字的成员姓名;
            // 第一个队伍筛选之后只要前3个人;
            Stream<String> streamOne = one.stream().filter(s -> s.length() == 3).limit(3);
            // 第二个队伍只要姓张的成员姓名;
            // 第二个队伍筛选之后不要前2个人;
            Stream<String> streamTwo = two.stream().filter(s -> s.startsWith("张")).skip(2);
            // 将两个队伍合并为一个队伍;
            // 根据姓名创建User对象;
            // 打印整个队伍的User对象信息。
            Stream.concat(streamOne, streamTwo).map(User::new).forEach(System.out::println);
        }
    }
    版权声明:本文内容来自第三方投稿或授权转载,原文地址:https://blog.51cto.com/u_15652665/5349840,作者:qq628b229e2808e,版权归原作者所有。本网站转在其作品的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如因作品内容、版权等问题需要同本网站联系,请发邮件至ctyunbbs@chinatelecom.cn沟通。

    上一篇:k8s创建pod和service的过程

    下一篇:在Linux中安装JDK8

    相关文章

    2025-05-14 10:07:38

    30天拿下Python之函数

    在Python中,函数是一段可以重复使用的代码块,它可以提高代码的可重用性和可维护性,是编程中非常重要的概念。

    2025-05-14 10:07:38
    lambda , 代码 , 传递 , 关键字 , 函数 , 参数 , 定义
    2025-05-06 09:19:51

    函数模板 函数参数

    函数模板 函数参数

    2025-05-06 09:19:51
    lambda , 函数 , 函数指针 , 定义 , 模板
    2025-04-14 09:27:25

    【C++11】lambda表达式

    随着 C++11 的发布,C++ 标准引入了许多新特性,使语言更加现代化,开发者编写的代码也变得更加简洁和易于维护。Lambda 表达式是其中一个重要的特性,它提供了一种方便的方式来定义匿名函数,这在函数式编程范式中非常常见。Lambda 表达式允许我们在函数内部定义小型的无名函数,减少了不必要的函数定义和代码冗余,是现代 C++ 开发的重要工具。

    2025-04-14 09:27:25
    C++ , lambda , Lambda , 变量 , 捕获
    2025-03-17 08:48:47

    算法探索_多序列合并去重

    算法探索_多序列合并去重

    2025-03-17 08:48:47
    lambda , 合并 , 测试代码 , 耗时
    2025-03-05 09:24:43

    【Python】练习题——函数相关(lambda表达式和filter、sum函数、递归)

    【Python】练习题——函数相关(lambda表达式和filter、sum函数、递归)

    2025-03-05 09:24:43
    filter , lambda , 函数 , 列表 , 递归
    2025-02-10 08:56:02

    【C++11】lambda函数及其基本用法

    【C++11】lambda函数及其基本用法

    2025-02-10 08:56:02
    amp , lambda , 函数 , 列表 , 变量 , 捕获 , 表达式
    2025-01-17 09:05:56

    BFS:边权相同的最短路问题

    BFS:边权相同的最短路问题

    2025-01-17 09:05:56
    bfs , lambda , LeetCode , map , sort , vector , 力扣
    2025-01-16 09:30:08

    恕我直言你可能真的不会java第3篇:Stream的Filter与谓词逻辑

    恕我直言你可能真的不会java第3篇:Stream的Filter与谓词逻辑

    2025-01-16 09:30:08
    filter , lambda , 代码 , 输出 , 逻辑
    2024-12-02 09:45:53

    Java中的lambda表达式与Stream API:高效的函数式编程

    lambda表达式是Java 8引入的一项重要特性,它允许将函数作为方法的参数传递给其他方法,或者将代码像数据一样进行传递。lambda表达式的引入使得Java编程更加灵活和简洁。

    2024-12-02 09:45:53
    API , lambda , Stream
    2024-11-20 06:55:59

    Lambda 表达式与C++并发编程

    Lambda 表达式(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。

    2024-11-20 06:55:59
    lambda , 函数 , 表达式
    查看更多
    推荐标签

    作者介绍

    天翼云小翼
    天翼云用户

    文章

    33564

    阅读量

    5026109

    查看更多

    最新文章

    函数模板 函数参数

    2025-05-06 09:19:51

    BFS:边权相同的最短路问题

    2025-01-17 09:05:56

    线性代数复习:矩阵的特征值与特征向量

    2024-11-07 08:22:07

    java8流式操作

    2024-04-17 02:49:19

    转载-java 8 为什么引入 lambda

    2024-04-16 08:57:13

    使用java8的lambda表达式,优雅的解决 if-else

    2023-07-26 07:51:09

    查看更多

    热门文章

    使用lambda表达式对两个list并集、去重并集、交集、差集处理

    2022-12-27 10:00:39

    【QT】QT中使用Lambda表达式

    2023-05-29 10:48:16

    使用java8的lambda表达式,优雅的解决 if-else

    2023-07-26 07:51:09

    浅谈lambda表达式

    2023-07-07 08:10:58

    java8流式操作

    2024-04-17 02:49:19

    转载-java 8 为什么引入 lambda

    2024-04-16 08:57:13

    查看更多

    热门标签

    linux java python javascript 数组 前端 docker Linux vue 函数 shell git 节点 容器 示例
    查看更多

    相关产品

    弹性云主机

    随时自助获取、弹性伸缩的云服务器资源

    天翼云电脑(公众版)

    便捷、安全、高效的云电脑服务

    对象存储

    高品质、低成本的云上存储服务

    云硬盘

    为云上计算资源提供持久性块存储

    查看更多

    随机文章

    函数模板 函数参数

    使用lambda表达式对两个list并集、去重并集、交集、差集处理

    java8流式操作

    【QT】QT中使用Lambda表达式

    线性代数复习:矩阵的特征值与特征向量

    浅谈lambda表达式

    • 7*24小时售后
    • 无忧退款
    • 免费备案
    • 专家服务
    售前咨询热线
    400-810-9889转1
    关注天翼云
    • 权益商城
    • 天翼云APP
    • 天翼云微信公众号
    服务与支持
    • 备案中心
    • 售前咨询
    • 智能客服
    • 自助服务
    • 工单管理
    • 客户公告
    • 涉诈举报
    账户管理
    • 管理中心
    • 订单管理
    • 余额管理
    • 发票管理
    • 充值汇款
    • 续费管理
    快速入口
    • 权益商城
    • 文档中心
    • 最新活动
    • 免费试用
    • 信任中心
    • 天翼云学堂
    云网生态
    • 甄选商城
    • 渠道合作
    • 云市场合作
    了解天翼云
    • 关于天翼云
    • 天翼云APP
    • 服务案例
    • 新闻资讯
    • 联系我们
    热门产品
    • 云电脑
    • 弹性云主机
    • 云电脑政企版
    • 天翼云手机
    • 云数据库
    • 对象存储
    • 云硬盘
    • Web应用防火墙
    • 服务器安全卫士
    • CDN加速
    热门推荐
    • 云服务备份
    • 边缘安全加速平台
    • 全站加速
    • 安全加速
    • 云服务器
    • 云主机
    • 智能边缘云
    • 应用编排服务
    • 微服务引擎
    • 共享流量包
    更多推荐
    • web应用防火墙
    • 密钥管理
    • 等保咨询
    • 安全专区
    • 应用运维管理
    • 云日志服务
    • 文档数据库服务
    • 云搜索服务
    • 数据湖探索
    • 数据仓库服务
    友情链接
    • 中国电信集团
    • 189邮箱
    • 天翼企业云盘
    • 天翼云盘
    ©2025 天翼云科技有限公司版权所有 增值电信业务经营许可证A2.B1.B2-20090001
    公司地址:北京市东城区青龙胡同甲1号、3号2幢2层205-32室
    • 用户协议
    • 隐私政策
    • 个人信息保护
    • 法律声明
    备案 京公网安备11010802043424号 京ICP备 2021034386号