前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Java数据结构】反射、枚举以及lambda表达式

【Java数据结构】反射、枚举以及lambda表达式

作者头像
E绵绵
发布2024-09-12 12:42:13
740
发布2024-09-12 12:42:13
举报
文章被收录于专栏:编程学习之路

1.❤️❤️前言~🥳🎉🎉🎉

Hello, Hello~ 亲爱的朋友们👋👋,这里是E绵绵呀✍️✍️。 如果你喜欢这篇文章,请别吝啬你的点赞❤️❤️和收藏📖📖。如果你对我的内容感兴趣,记得关注我👀👀以便不错过每一篇精彩。 当然,如果在阅读中发现任何问题或疑问,我非常欢迎你在评论区留言指正🗨️🗨️。让我们共同努力,一起进步! 加油,一起CHIN UP!💪💪

2.反射

2.1反射定义

Java反射是Java语言的一种特性,它允许程序在运行时自我检查并对内部成员进行操作。这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。具体来说,反射机制允许在运行状态中 对于任意一个类,都能够知道这个类的所有属性和方法; 对于任意一个对象,都能够调用它的任意方法和属性,并且能改变它的属性


Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。其本质是JVM得到Class对象之后,再通过Class对象进行反编译,从而获取对象的各种信息。

2.2反射主要应用场景

  • 在运行时判断任意一个对象所属的类;
  • 在运行时实例化任意一个类的对象;
  • 在运行时获取任意类的名称、package信息、所有属性、方法、注解、类型、类加载器等;
  • 在运行时获取任意对象的属性,并且能改变对象的属性;
  • 在运行时调用任意对象的方法。

2.3Class类(反射机制的起源)

2.31获取Class类

其中有三种方法:

  • Class.forName("类的全路径");
  • 类名.class
  • 对象名.getClass();

而当获取到class类时再用其获取原本的类的对象实例我们就可以用

newInstance() //创建一个类的实例

代码如下:

代码语言:javascript
复制
public class Test {
    public static void main(String[] args) throws Exception{
        //方法1:通过 Class.forName("类的全路径");获取Class
        Class<?> class1 = Class.forName("Student");
        //方法2:通过 类名.class 获取Class
        Class<?> class2 = Student.class;
        //方法3:通过 对象名.getClass(); 获取Class
        Student student = new Student("E棉", "35", 17);
        Class<?> class3 = student.getClass();
        try{
            Student   student1 = (Student) class3.newInstance();
    }catch (Exception e) {}
        
    }

}


class Student{
    private String name;
    private String number;
    public String sex;
    public int age;
    public Student(String name, String number, int age) {
        this.name = name;
        this.number = number;
        this.age = age;
    }
    public Student(String name){
        this.name = name;
    }
    private Student(String name, String number){
        this.name = name;
        this.number = number;
    }
    private void schooling(){
        System.out.println("去上学");
    }
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

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

这里我们出现了三个现象:


1.对于forname其实有编译时异常ClassNotFoundException这个隐患,所以需要用throws将其抛出。

  1. newInstans方法的返回值是T,所以被擦除成了Object,要进行强制类型转换
  2. newInstans方法会抛出 InstantiationException, IllegalAccessException两个异常,所以需要用try catch将其捕获,因为会出现异常,所以我们很少使用它。一般我们都是用constructor,newinstance(参数)去生成对应的类,这样就不会出现异常,对于该知识点我们在后面会讲到

这里涉及异常知识点,如果不懂可以看我的异常文章,讲的很清楚

异常第一部分

异常第二部分

2.32Class类常用方法

调用Class的方法一般会返回三种类型的对象

  • Field对象:存放获取对象的成员变量属性
  • Method对象:存放获取对象的方法
  • Constructor对象:存放获取对象的构造方法

下面是Class类的一些常用方法

获取成员变量getFields() //获取所有公开的成员变量(返回一个数组) getDeclaredFields() //获取所有本类定义的成员变量,包括私有(返回一个数组) getField(变量名) //获取指定公共属性的Field对象 getDeclaredField(变量名) //获取指定包括私有,


获取成员方法

getMethods() //获取所有公开的方法(返回一个数组)

getMethod(方法名,参数类型列表) //获取指定公开方法的Method对象

getDeclaredMethods() //获取所有本类定义的的方法,包括私有(返回一个数组)

getDeclaredMethod(方法名,int.class,String.class) //获取指定包括私有


获取构造方法

getConstructor(参数类型列表) //获取公开的构造方法

getConstructors() //获取所有的公开的构造方法(返回一个数组)

getDeclaredConstructors() //获取所有的构造方法,包括私有(返回一个数组)

getDeclaredConstructor(int.class,String.class) //获取指定包括私有

2.33获得类中的成员变量

这里我们获取一个类中的私有成员变量并对其进行修改。

代码语言:javascript
复制
​
public class Test2 {
    public static void main(String[] args) throws NoSuchFieldException,ClassNotFoundException,IllegalAccessException{
        Class<?> studentClass = Class.forName("Student");
        Field field = studentClass.getDeclaredField("name");
        field.setAccessible(true);
        Student student=new Student("lq","102",18);
        field.set(student, "max");
        System.out.println(student);
    }
}

class Student{
    private String name;
    private String number;
    public String sex;
    public int age;
    public Student(String name, String number, int age) {
        this.name = name;
        this.number = number;
        this.age = age;
    }
    public Student(String name){
        this.name = name;
    }
    private Student(String name, String number){
        this.name = name;
        this.number = number;
    }
    private void schooling(){
        System.out.println("去上学");
    }
    public String getName() {
        return name;
    }

    private void setName(String name) {
        this.name = name;
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

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

​

需要注意的是,如果我们需要修改私有变量的话,就要多加一个代码:

代码语言:javascript
复制
field.setAccessible(true);
2.34使用类中的成员方法

这里我们使用一个类中的私有成员方法。

代码语言:javascript
复制
public class Test3 {
    public static void main(String[] args) throws NoSuchMethodException,InvocationTargetException,
IllegalAccessException,ClassNotFoundException
 {
        Class<?> studentClass = Class.forName("Student");
        Method method = studentClass.getDeclaredMethod("setName", String.class);
        method.setAccessible(true);
        Student student=new Student("lq","102",18);
        method.invoke(student, "max");
        System.out.println(student);
    }
}
class Student{
    private String name;
    private String number;
    public String sex;
    public int age;
    public Student(String name, String number, int age) {
        this.name = name;
        this.number = number;
        this.age = age;
    }
    public Student(String name){
        this.name = name;
    }
    private Student(String name, String number){
        this.name = name;
        this.number = number;
    }
    private void schooling(){
        System.out.println("去上学");
    }
    public String getName() {
        return name;
    }

    private void setName(String name) {
        this.name = name;
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

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

同理,需要注意的是,如果我们需要使用私有方法的话,就要多加一个代码:

代码语言:javascript
复制
   method.setAccessible(true);
2.35 使用类中的构造方法

这里我们使用一个类中的私有构造方法,跟前面大不相同。

代码语言:javascript
复制
​
public class Test4 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException,IllegalAccessException,ClassNotFoundException,InstantiationException{
        Class<?> studentClass = Class.forName("Student");
        Constructor constructor = studentClass.getDeclaredConstructor(String.class, String.class);
        constructor.setAccessible(true);
        Student student= (Student) constructor.newInstance("lq","102");
        System.out.println(student);
    }
}

​class Student{
    private String name;
    private String number;
    public String sex;
    public int age;
    public Student(String name, String number, int age) {
        this.name = name;
        this.number = number;
        this.age = age;
    }
    public Student(String name){
        this.name = name;
    }
    private Student(String name, String number){
        this.name = name;
        this.number = number;
    }
    private void schooling(){
        System.out.println("去上学");
    }
    public String getName() {
        return name;
    }

    private void setName(String name) {
        this.name = name;
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

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

同理私有时依旧需要该方法

代码语言:javascript
复制
 constructor.setAccessible(true);

因为用该构造方法不会抛出异常,只会存在异常的隐患,所以相较于之前的直接用class.newInstance()会抛出异常,我们更倾向于用这个。

2.4反射的优缺点

优点: 1. 对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法 2. 增加程序的灵活性和扩展性,降低耦合性,提高自适应能力 3. 反射已经运用在了很多流行框架如:Struts、Hibernate、Spring 等等。 缺点: 1. 使用反射会有效率问题。会导致程序效率降低。具体参考 相关文章 2. 反射技术绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂

3.枚举

3.1枚举的语法

在 Java 中,可以通过关键字 enum 来定义枚举类型。枚举类型的定义格式如下:

代码语言:javascript
复制
enum EnumName {
    Constant1,
    Constant2,
    Constant3,
    ...
}

其中 EnumName 是枚举类型的名称,**Constant1**、**Constant2**、**Constant3** 等是枚举类型的常量,这些常量就是枚举类型创建的实例,枚举常量的名称通常采用大写字母命名,多个单词之间用下划线分隔。

对于枚举内部除了以上部分,其他的跟正常类是一样的,都可以有成员方法和成员变量

在 Java 中,可以通过枚举类型的名称来访问枚举常量。例如,假设有一个名为 Weekday 的枚举类型,可以通过如下方式来访问枚举常量:

代码语言:javascript
复制
Weekday monday = Weekday.Monday;

这里的 Weekday.Monday 表示 Weekday 枚举类型中的 Monday 常量,因为该常量为Weekday的实例,所以可以接收

枚举类型可以定义方法,这些方法可以在枚举常量上调用。例如,可以在 Weekday 枚举类型中定义一个 isWeekend 方法来判断当前枚举常量是否为周末:

代码语言:javascript
复制
enum Weekday {
    Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday;
 
    public boolean isWeekend() {
        return this == Saturday || this == Sunday;
    }
}

在上面的例子中,通过在枚举常量后面定义方法,可以在每个枚举常量上调用这个方法。例如,可以通过 Saturday.isWeekend() 来判断 Saturday 是否为周末,因为枚举常量是枚举实例。

枚举类型本身就是一个类,其默认继承java.lang.Enum,也就是说,自己写的枚举类,就算没有显示的继承Enum ,但是其默认继承这个类。

3.2 枚举常用方法

  1. name() 返回枚举常量的名称
代码语言:javascript
复制
Color color = Color.RED;
String name = color.name();
System.out.println(name);// 输出:RED

2.valueOf(String name)

根据枚举常量名称,返回枚举实例

代码语言:javascript
复制
​
Color color = Color.valueOf("RED");
System.out.println(color.name());	// 输出:RED

​

3.values()

返回枚举类所有的枚举常量(数组形式)

代码语言:javascript
复制
Color[] colors = Color.values();
for (Color color : colors) {
  System.out.println(color.name());
}
*/
输出:
RED
GREEN
BLUE
*/

4.ordinal()

返回此枚举常量的序数(其枚举声明中的位置,其中初始常量被分配一个序号零)

代码语言:javascript
复制
Color color = Color.RED;
int ordinal = color.ordinal();
System.out.println(ordinal);//输出:0

5.compareTo()

比较两个枚举值常量序数之差

代码语言:javascript
复制
Color color = Color.RED;
int n = color.compareTo(Color.BLUE) ;//0 减 2
System.out.println(n);           

3.3枚举的构造方法

枚举类型在Java中可以包含构造方法,用于初始化枚举常量(所以说枚举常量是实例)。枚举的构造方法必须是私有的,因为枚举常量在定义时就被实例化,并且不能在其他地方进行实例化。

这些构造方法只能在枚举常量的定义中被调用,并且只能用来初始化枚举常量的值。当枚举常量有参数后,需要提供相应的构造方法。

代码语言:javascript
复制
enum Weekday {
    Monday("星期一"), Tuesday("星期二"), Wednesday("星期三"), Thursday("星期四"), Friday("星期五"), Saturday("星期六"), Sunday("星期日");
 
    private String value;
 
    private Weekday(String value) {
        this.value = value;
    }
 
    public String getValue() {
        return value;
    }
}

3.4 枚举与反射

我们知道反射对于任何一个类,哪怕其构造方法是私有的,我们也可以通过反射拿到他的实例对象,那么枚举的构造方法也是私有的,我们是否可以拿到呢? 这里我们直接公布答案


根据Java源代码, newInstance() 方法在创建实例之前会先检查目标类是否是一个枚举类型。如果目标类是枚举类型, newInstance() 方法会抛出 InstantiationException 异常,阻止通过反射去创建枚举实例。

所以说我们无法通过反射去使用枚举的构造方法,也可以知道枚举它是特别安全的。

3.5总结

1、枚举本身就是一个类,其构造方法默认为私有的,且都是默认继承 java.lang.Enum


2、枚举可以避免反射和序列化问题


3、枚举的优点:

枚举常量更简单安全

枚举具有内置方法 ,代码更优雅

4.枚举的缺点:

不可继承,无法扩展


4.Lambda表达式

4.1什么是Lambda表达式?

Lambda 表达式是一种匿名函数,它可以在代码中以简洁的方式表示一个功能或行为。Lambda 表达式通常由参数列表、箭头符号和函数体组成。参数列表定义了该函数接受的输入参数,箭头符号表示函数体的开始,函数体则定义了函数的具体实现。Lambda 表达式可以作为一个整体被传递给其他函数或方法,也可以直接被调用。它跟匿名内部类有关。

4.2匿名内部类的回顾

既然跟匿名内部类相关,那我们先来回顾匿名内部类。 匿名内部类是一种在Java中定义匿名类的方式。它允许在创建对象的同时定义该对象的类,并且不需要为该类单独命名。通常情况下,我们需要先定义一个类,然后才能创建该类的对象。但是在某些场景下,我们只需要定义一个临时的类来实现某个接口或继承某个类的功能,这时就可以使用匿名内部类。简单的理解就是减少了新类型的出现。

代码语言:javascript
复制
public class Test2 {


    public static void main(String[] args) {


        Animal cat = new Animal(){
            @Override
            public void fun() {
                System.out.println("是只猫");
            }
        };

        Animal dog = new Animal() {
            @Override
            public void fun() {
                System.out.println("是只狗");
            }
        };

        cat.fun();
        dog.fun();

    }
}

interface Animal{
    public void fun();
}

以上就是匿名内部类的例子,而我们之后要用Lambda表达式对其进行简化。

4.3 Lambda表达式进行简化

代码语言:javascript
复制
​public class Test2 {


    public static void main(String[] args) {

        Animal cat = () -> {
            System.out.println("是只猫");
        };
        Animal dog = () -> {
            System.out.println("是只狗");
        };
        cat.fun();
        dog.fun();
    }
}

interface Animal{
    public void fun();
}

所以Lambdab 表达式存在的意义就是简化匿名内部类,具体格式为 (被重写方法的形参列表)-> {(被重写方法的方法体)} ;

4.4Lambda表达式进行简化的要求

并不是所有的匿名内部类都可以用 Lambda 表达式进行简化的,只有函数式接口才可以被 Lambda 表达式简化。 函数式接口是指只包含一个抽象方法的接口。在Java中,函数式接口是支持函数式编程的基础,它可以用作 Lambda 表达式的目标类型。 Java 8引入了**@FunctionalInterface**注解,用于标识一个接口为函数式接口。编译器会检查被标注的接口是否符合函数式接口的定义,如果不符合,编译器会报错。

总的来说,如果是接口并且只包含了一个抽象方法,或者引用了 @FunctionalInterface**注解的时候都为函数式接口,都可以被 Lambda 表达式简化。**

4.5Lambda 表达式省略规则

我们通过以上的讲解,现在知道可以通过 Lambda 表达式进行简化,那么还能不能更进一步简化呢? 答案肯定是可以的,可以继续简化。 具体来介绍省略的规则: 1. 参数类型可以省略不写,但名称要一模一样。 2. 如果只有一个参数的,参数类型可以省略不写,同时()也可以不写。 3. 如果重写的方法体只有一行代码,可以省略大括号不写,同时要省略分号!此时,如果这行代码是 return 语句,也必须去掉 return 语句。 以下我们给个例子去直观显示:

代码语言:javascript
复制
​
 
public class Test {
    public static void main(String[] args) {
        double[] arr = new double[]{1.0,2.0,9,4.0,5.0};
 
        //第一代的样子
/*        Arrays.setAll(arr, new IntToDoubleFunction() {
            @Override
            public double applyAsDouble(int value) {
                return arr[value] * 0.8;
            }
        });*/
 
        //第二代的样子
/*        Arrays.setAll(arr, (int value) ->{
                return arr[value] * 0.8;
            });*/
 
        //第三代的样子
        Arrays.setAll(arr,value ->arr[value] * 0.8 );
        System.out.println(Arrays.toString(arr));
 
    }
}

​

所以这就是我们的Lambda表达式省略规则。

4.6匿名内部类的变量捕获

对于匿名内部类中出现的变量,要么必须用final修饰,要么其是变量但从初始化后从未修改过一次。否则就不能用到匿名内部类中,这就是变量捕获。 下面给个例子。

以下是正确代码,可以执行

代码语言:javascript
复制
class Test {
    public void func() {
        System.out.println("func()");
    }
}
 
public class TestDemo {
    public static void main(String[] args) {
        int a = 100;
        Test test = new Test() {
            @Override
            public void func() {
                System.out.println("我是内部类,且重写了func这个方法!");
                System.out.println("我是捕获到变量 a == " + a
                        + " 我是一个常量,或者是一个没有改变过值的变量!");
            }
        };
 
        test.func();
    }
}

如下代码是错误代码,不能执行,编译错误

代码语言:javascript
复制
class Test {
    public void func() {
        System.out.println("func()");
    }
}
 
public class TestDemo {
    public static void main(String[] args) {
        int a = 100;
        Test test = new Test() {
            @Override
            public void func() {
                a = 99;
                System.out.println("我是内部类,且重写了func这个方法!");
                System.out.println("我是捕获到变量 a == " + a
                        + " 我是一个常量,或者是一个没有改变过值的变量!");
            }
        };
 
        test.func();
    }
}

4.7Lambda表达式的优缺点

Lambda表达式的优点很明显,在代码层次上来说,使代码变得非常的简洁。缺点也很明显,代码不易读。


优点:

1. 代码简洁,开发迅速

2. 方便函数式编程

3. 非常容易进行并行计算

4. Java 引入 Lambda,改善了集合操作


缺点:

1. 代码可读性变差

2. 在非并行计算中,很多计算未必有传统的 for 性能要高

3. 不容易进行调试

5.总结

所以这篇文章就这样结束了,下篇文章将带来泛型的进阶,讲述之前没讲过的泛型知识点。 在此,我们诚挚地邀请各位大佬们为我们点赞、关注,并在评论区留下您宝贵的意见与建议。让我们共同学习,共同进步,为知识的海洋增添更多宝贵的财富!🎉🎉🎉❤️❤️💕💕🥳👏👏👏

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-09-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.❤️❤️前言~🥳🎉🎉🎉
  • 2.反射
    • 2.1反射定义
      • 2.2反射主要应用场景
        • 2.3Class类(反射机制的起源)
          • 2.31获取Class类
          • 2.32Class类常用方法
          • 2.33获得类中的成员变量
          • 2.34使用类中的成员方法
          • 2.35 使用类中的构造方法
        • 2.4反射的优缺点
        • 3.枚举
          • 3.1枚举的语法
            • 3.2 枚举常用方法
              • 3.3枚举的构造方法
                • 3.4 枚举与反射
                  • 3.5总结
                  • 4.Lambda表达式
                    • 4.1什么是Lambda表达式?
                      • 4.2匿名内部类的回顾
                        • 4.3 Lambda表达式进行简化
                          • 4.4Lambda表达式进行简化的要求
                            • 4.5Lambda 表达式省略规则
                              • 4.6匿名内部类的变量捕获
                                • 4.7Lambda表达式的优缺点
                                • 5.总结
                                相关产品与服务
                                GPU 云服务器
                                GPU 云服务器(Cloud GPU Service,GPU)是提供 GPU 算力的弹性计算服务,具有超强的并行计算能力,作为 IaaS 层的尖兵利器,服务于生成式AI,自动驾驶,深度学习训练、科学计算、图形图像处理、视频编解码等场景。腾讯云随时提供触手可得的算力,有效缓解您的计算压力,提升业务效率与竞争力。
                                领券
                                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档