反射,枚举,lambda表达式

news2024/9/21 22:34:27

目录

1、反射

1.1 基本概念

1.2 反射相关的类

1.3 创建 Class 对象

1.4 反射的使用

1.4.1 通过反射创建对象:

1.4.2 获取私有的构造方法

1.4.3 获取私有的成员变量

1.4.4 获取私有的方法

1.5 总结

2、枚举

2.1 认识枚举 

2.2 使用枚举

2.3 枚举与反射的那些事

3、Lambda 表达式

3.1 认识 Lambda 表达式

3.2 语法

3.3 函数式接口

3.4 Lambda 的基本使用


1、反射

1.1 基本概念

Java的反射(reflection)机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性,既然能拿到,那么我们就可以修改部分类型信息;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射(reflection)机制。 

1.2 反射相关的类

类名用途
Class 类代表类的实体,在运行的Java程序中表示类和接口
Field 类代表类的成员变量/类的属性
Method 类代表类的方法
Constructor 类代表类的构造方法

每个类里面都有很多相关的方法啊,这里具体的方法我就不一一列举出来了,详细的可以去查看 Java 的官方文档。

1.3 创建 Class 对象

创建一个 Class 对象,通常使用以下三种方法:

1. 调用某个对象里面的 getClass 方法:

public static void main(String[] args) {
    Student student = new Student();
    Class<?> c1 = student.getClass();
}

2. 采取类名.class的方法:

public static void main(String[] args) {
    Class<?> c2 = Student.class; //这种方法说明每个类默认隐式包含一个静态的成员变量 class
}

3. 通过 Class.forName() 获取:

public static void main(String[] args) {
    Class<?> c3 = null;
    try {
        c3 = Class.forName("Student");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}

注意:这个 forName 中要放传入类的完整路径,比如如果是 String 的话,即:java.lang.String

1.4 反射的使用

这里我们自定义一个 Student 类:

public class Student {
    private String name;
    private int age;

    public Student() {
        System.out.println();
    }

    private Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void eat() {
        System.out.println(name + "正在吃饭!");
    }

    private void sleep() {
        System.out.println(name + "正在睡觉!");
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

1.4.1 通过反射创建对象:

public static void reflectClassDemo() {
    try {
        Class<?> c = Class.forName("Student");
        Object objectStudent = c.newInstance();
    } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
        e.printStackTrace();
    }
}

1.4.2 获取私有的构造方法

public static void reflectPrivateConstructor() {
    try {
        Class<?> c = Class.forName("Student");
        Constructor<?> constructor = c.getDeclaredConstructor(String.class, int.class);
        constructor.setAccessible(true); //设置为true后可修改访问权限
        Object objectStudent = constructor.newInstance("张三", 12);
        System.out.println(objectStudent);
    } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException | InstantiationException e) {
        e.printStackTrace();
    }
}

1.4.3 获取私有的成员变量

public static void reflectPrivateField() {
    try {
        Class<?> c = Class.forName("Student");
        Field field = c.getDeclaredField("name"); //获取名为 name 的成员变量
        field.setAccessible(true);
        Object student = c.newInstance();
        field.set(student, "张三"); //将 student 对象的name 设置成 "张三"
        System.out.println(field.getName());
    } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException | InstantiationException e) {
        e.printStackTrace();
    }
}

1.4.4 获取私有的方法

public static void reflectPrivateMethod() {
    try {
        Class<?> c = Class.forName("Student");
        Method method = c.getDeclaredMethod("eat");
        method.setAccessible(true);
        //方法有参数的写法:
        //Method methodStudent = classStudent.getDeclaredMethod("function",String.class);
        
        Object objectStudent = c.newInstance();
        Student student = (Student)objectStudent;
        System.out.println("私有方法方法名: " + method.getName());
        method.invoke(objectStudent); // 调用获取到的方法, student对象中的
    } catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException | ClassNotFoundException e) {
        e.printStackTrace();
    }
}

1.5 总结

在反射眼中,对应任意一个类,都能够知道这个类的属性和方法,对于任意一个对象,都能调用它任意一个方法, 这样使程序的灵活性大大提高,以及可扩展性,但是这样一来,似乎就在告诉大家,之前封装的一些方法和属性在反射面前就是一个摆设。

反射是一把双刃剑,是一种非常规的编程手段,不到必要的时候,不建议使用反射,使用反射会有效率问题。会导致程序效率降低,而且反射技术绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂 。


2、枚举

2.1 认识枚举 

枚举顾名思义,一一列举,作用将一组常量组织起来,Java中常量就是常量,并没有常变量这种说法,利用我们现在的知识,如果让你定义三个常量,分别表示 红色,黑色,绿色,你可能会这样定义:

public static final int RED = 1;
public static final int BLACK = 3;
public static final int GREEN = 3;

这样的话,代码中出现的 1,有可能会被误认为是 RED,是可能会出现歧义的,于是就有一种类型枚举来进行组织:

public enum MyEnum {
    RED, BLACK, GREEN;
}

这样里面定义的每个都是枚举类型,不再是普通的数字了,我们自己写的 enum 会默认继承 Enum 类,所以不用显示的去继承 Enum 这个抽象类。

2.2 使用枚举

public static void main(String[] args) {
    MyEnum myEnum = MyEnum.BLACK;
    switch (myEnum) {
        case RED:
            System.out.println("红色!");
            break;
        case BLACK:
            System.out.println("黑色!");
            break;
        case GREEN:
            System.out.println("绿色!");
            break;
        default:
            System.out.println("其他颜色!");
            break;
    }
}

使用场景:错误状态码,消息类型,颜色的划分,状态机等等....

Enum 类的常用方法:

方法名称描述
values()以数组的形式返回枚举类型的所有成员
ordinal()获取枚举成员的索引位置
valueOf()将普通字符串转换为枚举类型
compareTo()比较两个枚举成员在定义时候的顺序

枚举是一种类型,也就是Java中的枚举就是一个类,那么就可以这样去写代码:

public enum MyEnum {
    RED("红色", 1), BLACK("黑色", 2), GREEN("绿色", 3);
    private String name;
    private int key;
    private MyEnum(String name, int key) {
        this.name = name;
        this.key = key;
    }
}

枚举的构造方法默认是私有的.

枚举常量是更简单安全的,安全体现在哪,马上就说到了,而且枚举拥有内置方法,使用起来方便,缺点也有,由于Java中支持单继承,因此枚举类型不能再继承其他类,无法扩展。

2.3 枚举与反射的那些事

通过反射,能否拿到枚举的私有构造方法呢?我们写个代码来测试一下:

public class TestMyEnum {
    public static void main(String[] args) {
        try {
            Class<?> c = Class.forName("MyEnum");
            Constructor<?> constructor = c.getDeclaredConstructor(String.class, int.class);
            constructor.setAccessible(true);
            Object myEnum = constructor.newInstance("红色", 123);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

这里居然报错了,报错信息提示没有该构造方法???为什么会没有呢,我们的 MyEnum 默认继承了 Enum 类,实例化子类对象的时候,先调用父类的构造方法,那么这里我们就去看一下 Enum 的构造方法。

Enum 只有这一个构造方法,还带有两个参数,但是在 JavaSE 的学习中,如果在子类的构造方法中,没有显式写明 super(),则会在子类构造方法第一行默认有 super(),也就是调用父类的无参构造,枚举比较特殊虽然我们写的是两个,但默认他还添加了 name,和 ordinal 参数。也就是说,我们需要提供四个参数:

Constructor<?> constructor = c.getDeclaredConstructor(String.class, int.class, String.class, int.class);
constructor.setAccessible(true);
Object myEnum = constructor.newInstance("红色", 123, "红色", 321);

这里还是报错了,但是这里的报错是第10行的,newInstance 方法报错,那么我们就进入该方法源码去一看究竟:

在JavaSE语法上,if 中的 & 会被认为 &&,所以枚举在这里被过滤了,这也就是你不能通过反射获取到枚举类的实例!

所以在这里可以发现,枚举是安全的,可以避免反射的问题。


3、Lambda 表达式

3.1 认识 Lambda 表达式

Lambda表达式是Java SE 8中一个重要的新特性。lambda表达式允许你通过表达式来代替功能接口。 lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码 块)。 Lambda 表达式(Lambda expression),基于数学中的 λ 演算得名,也可称为闭包(Closure)。 

3.2 语法

基本语法: (parameters) -> expression(parameters) -> { statements; }

Lambda表达式由三部分组成:

  • 1. paramaters:类似方法中的形参列表,这里的参数是函数式接口里的参数。这里的参数类型可以明确的声明 也可不声明而由JVM隐含的推断。另外当只有一个推断类型时可以省略掉圆括号。 2. ->:可理解为“被用于”的意思
  • 3. 方法体:可以是表达式也可以代码块,是函数式接口里方法的实现。代码块可返回一个值或者什么都不反 回,这里的代码块块等同于方法的方法体。如果是表达式,也可以返回一个值或者什么都不反回。 

3.3 函数式接口

函数式接口:一个接口中,只有一个抽象方法。

@FunctionalInterface 注解:如果我们在某个方法上声明了该注解,那么编译器就会按照函数式接口的定义来要求该接口。如果不符合函数式接口的语法,那么则会报错!

例子:

@FunctionalInterface
public interface TestFuncInterface {
    void work();
}

也可也这样写:

@FunctionalInterface
public interface TestFuncInterface {
    void work();
    default void test() {
        System.out.println("hello");
    }
}

在 JDK 1.8 中,default 默认方法可以有具体的实现。

3.4 Lambda 的基本使用

//无返回值无参数
@FunctionalInterface
interface NoParameterNoReturn {
    void work();
}
//无返回值一个参数
@FunctionalInterface
interface OneParameterNoReturn {
    void work(int a);
}
//无返回值多个参数
@FunctionalInterface
interface MoreParameterNoReturn {
    void work(int a,int b);
}
//有返回值无参数
@FunctionalInterface
interface NoParameterReturn {
    int work();
}

//有返回值一个参数
@FunctionalInterface
interface OneParameterReturn {
    int work(int a);
}
//有返回值多参数
@FunctionalInterface
interface MoreParameterReturn {
    int work(int a,int b);
}

public class TestLambda {
    public static void main(String[] args) {
        NoParameterNoReturn n1 = () -> System.out.println("NoParameterNoReturn");
        n1.work();
        
        // 只有一个参数, 可以省略小括号
        OneParameterNoReturn n2 = x -> {
            System.out.println(x + "OneParameterNoReturn");
            System.out.println("多条语句则不能省略大括号!!!");
        };
        n2.work(5);

        MoreParameterNoReturn n3 = (x, y) -> System.out.println(x + y + "OneParameterNoReturn");
        n3.work(5, 8);

        NoParameterReturn n4 = () -> 88; //只有 return 一条语句可以省略 return
        int ret1 = n4.work();

        OneParameterReturn n5 = (x) -> {
            System.out.println("OneParameterReturn");
            return x + 10; //多条语句时, return 和 {} 都不能省略
        };
        int ret2 = n5.work(12);

        // 如果不省略形参类型, 必须都不省略, 省略的话必须全部省略
        MoreParameterReturn n6 = (int x, int y) -> (x + y + 8);
        int ret3 = n6.work(5, 5);
    }
}

Lambda表达式的优点很明显,在代码层次上来说,使代码变得非常的简洁。缺点也很明显,代码不易读。至于 Lambda 的更多使用,会在后续文章中慢慢体现出来。这里我们了解下语法即可。

优点:

  1. 代码简洁,开发迅速
  2. 方便函数式编程
  3. 非常容易进行并行计算
  4. ava 引入 Lambda,改善了集合操作(比如传比较器)

缺点:

  1. 代码可读性变差
  2. 在非并行计算中,很多计算未必有传统的 for 性能要高
  3. 不容易进行调试

下期预告:【MySQL】数据库的基本认识 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/341868.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第4章 流程控制-if-else,Switch,For循环(循环守卫,循环步长,倒叙打印),While循环,多重循环...

第 4 章 流程控制-if-else,Switch,For循环(循环守卫&#xff0c;循环步长&#xff0c;倒叙打印)&#xff0c;While循环&#xff0c;多重循环 4.1 分支控制 if-else 让程序有选择的的执行&#xff0c;分支控制有三种&#xff1a;单分支、双分支、多分支 4.1.1 单分支 1)基本语法…

Leetcode-每日一题1234. 替换子串得到平衡字符串(滑动窗口 + 哈希表)

题目链接&#xff1a;https://leetcode.cn/problems/replace-the-substring-for-balanced-string/description/ 思路 题目意思 这题意思是一个只含有[Q, W, E, R] 四个字符的字符串s且长度一定是 4的倍数&#xff0c; 需要你通过替换子串&#xff0c;使他变成一个「平衡字符…

【C++设计模式】学习笔记(6):Bridge 桥模式

目录 简介动机(Motivation)模式定义结构(Structure)要点总结笔记结语简介 Hello! 非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出~ ଘ(੭ˊᵕˋ)੭ 昵称:海轰 标签:程序猿|C++选手|学生 简介:因C语言结识编程,随后转入计算机专业,获得过国家奖学金…

【C++设计模式】学习笔记(2):模式分类与模版方法 Template Method

目录 简介模式分类GOF-23 模式分类从封装变化角度对模式分类重构获得模式 Refactoring to Patterns重构关键技法“组件协作”模式Template Method 模式动机(Motivation)结构化软件设计流程面向对象软件设计流程早绑定与晚绑定模式的定义结构(Structure)要点总结结语简介 He…

Unity(一)--通过简单例子了解属性、脚本等基础操作

目录新建工程保存视图创建游戏对象调整场景视图角度添加物理运动组件更换材质更改颜色添加脚本点击向上跳跃变色旋转UGUI的使用修改文字内容新建工程 ps:最好不使用中文文件路径及名称&#xff0c;可能会报错。 保存视图 进工程后&#xff0c;此时只有空文件夹&#xff0c;可…

《痞子衡嵌入式半月刊》 第 71 期

痞子衡嵌入式半月刊&#xff1a; 第 71 期 这里分享嵌入式领域有用有趣的项目/工具以及一些热点新闻&#xff0c;农历年分二十四节气&#xff0c;希望在每个交节之日准时发布一期。 本期刊是开源项目(GitHub: JayHeng/pzh-mcu-bi-weekly)&#xff0c;欢迎提交 issue&#xff0c…

新手做跨境电商,选对平台很重要

据数据统计&#xff0c;我国跨境电商早在2013年&#xff0c;发展态势就十分迅猛&#xff0c;交易规模达到2.7万亿元&#xff1b;而到2015年&#xff0c;仅仅两年时间&#xff0c;交易规模就突破到5万亿元&#xff1b;再过两年&#xff0c;2017年跨境电商交易规模增长到7.6万亿元…

okcc呼叫中心怎样搭建?

随着企业对服务逐渐重视&#xff0c;开始意识到呼叫中心作为客户服务水平和沟通效率的基本平台&#xff0c;并且有越来越多的企业开始利用呼叫中心外呼方式销售自己的产品。在此背景下&#xff0c;很多企业&#xff0c;无论规模大小&#xff0c;都有建立自己呼叫中心的想法。 …

docker的资源控制管理——Cgroups

引言&#xff1a;docker 使用cgrqup控制资源&#xff0c;K8S 里面也有limit&#xff08;使用上限&#xff09;docker通过cgroup来控制容器使用的资源配额&#xff0c;包括CPU、内存、磁盘三大方面&#xff0c;基本覆盖了常见的资源配额和使用量控制。Cgroup 是 Control group 的…

Jboss EAP 7.4.8配置jacoco 端口无法开启 启动报错

项目场景&#xff1a; servers: Jboss EAP 7.4.8 JDK: JDK17 jacoco: 测试jacoco-0.7.9 jacoco-0.8.5 jacoco-0.8.8 问题描述 问题1: Jboss 启动 VM arguments追加, 如下启动参数&#xff1a; -javaagent:F:\CoverageReport\jacoco-0.7.9\lib\jacocoagent.jarincludes*,o…

别具一格,原创唯美浪漫情人节表白专辑,(复制就可用)(html5,css3,svg)表白爱心代码(1)

别具一格&#xff0c;原创唯美浪漫情人节表白专辑&#xff0c; (复制就可用)&#xff08;html5,css3,svg)表白爱心代码(1) 一、 前言 回眸之间&#xff0c;丰盈了岁月&#xff0c;涟漪了思绪&#xff0c;轻轻落笔&#xff0c;不写伤痕&#xff0c;不写仇怨&#xff0c;只写岁月…

反光板导航SLAM(三)反光柱导航开发与实验

在上一章中简单了解了VEnus算法对于反光柱导航的基本思路。其主要分为了高反点提取、高反点聚类查找中心、高反点与已知反光柱位姿匹配以及调用ceres库进行位姿优化等步骤。然后在这个算法的基础上&#xff0c;再进行一定的开发达到一个比较稳定且可视化的版本。 使用&#xff…

json对象和formData相互转换

前言 大家都知道&#xff0c;前端在和后台进行交互联调时&#xff0c;肯定避免不了要传递参数&#xff0c;一般情况下&#xff0c;params 在 get 请求中使用&#xff0c;而 post 请求下&#xff0c;我们有两种常见的传参方式&#xff1a; JSON 对象格式和 formData 格式&#x…

《MySQL学习》 索引 下 覆盖索引,MRR,联合索引

一. 覆盖索引 有一张表T1&#xff0c;它的建表语句如下 mysql> create table T1 ( ID int primary key, k int NOT NULL DEFAULT 0, s varchar(16) NOT NULL DEFAULT , index k(k)) engineInnoDB;insert into T1 values(100,1, aa),(200,2,bb),(300,3,cc),(500,5,ee),(60…

为什么子进程要继承处理器亲缘性?

请先考虑一个典型的程序为什么需要启动一个子进程。(当然资源管理器不算一个典型的程序) 这是因为手头的任务被分解为子任务&#xff0c;无论出于何种原因&#xff0c;这些子任务都被放入子流程中。例如&#xff0c;在实现多次遍历型编译器/链接器时&#xff0c;其中每次遍历都…

虹科新品 | 万兆车载以太网媒体转换器-实现更加快捷、高效的连接

多千兆车载以太网 媒体转换器 —— Technica Engineering —— 2.5/5/10GBASE-T1多千兆 Media Converter Media Converter 是一种硬件设备&#xff0c;可在汽车以太网连接&#xff08;100BASE-T1 或 1000BASE-T1&#xff09;和任何具有带 RJ-45 连接器的标准以太网网络接口卡 …

canal同步mysql数据到kafka, kafka消费存入clickhouse

环境win mysql5.7 apache-zookeeper-3.5.9-bin kafka_2.11-1.1.1 canal.deployer-1.1.7-SNAPSHOT 如果不想看步骤可以直接下载我打包好的文件&#xff0c;修改相关数据库配置就行 https://download.csdn.net/download/weixin_38738049/87441074?spm1001.2014.3001.55031新增m…

pytorch 实现情感分类问题

1、词表映射无论是深度学习还是传统的统计机器学习方法处理自然语言&#xff0c;都需要先将输入的语言符号&#xff08;通常为标记Token&#xff09;&#xff0c;映射为大于等于0、小于词表大小的整数&#xff0c;该整数也被称作一个标记的索引值或下标。vocab类实现标记和索引…

C语言(按位运算符和位移运算符)

目录 ​编辑 一.按位运算符 1.二进制反码或按位取反&#xff1a;~ 2.按位与&#xff1a;& 3.按位或&#xff1a;| 4.按位异或&#xff1a;^ 二.位移运算符 1.左移&#xff1a; << 2.右移&#xff1a; >> 一.按位运算符 C有四个按位逻辑运算符都用于整…

[多线程进阶]CAS与Synchronized基本原理

专栏简介: JavaEE从入门到进阶 题目来源: leetcode,牛客,剑指offer. 创作目标: 记录学习JavaEE学习历程 希望在提升自己的同时,帮助他人,,与大家一起共同进步,互相成长. 学历代表过去,能力代表现在,学习能力代表未来! 目录: 1.CAS 1.1 什么是CAS? 1.2 CAS伪代码 1.3 CAS …