数据结构—(java)反射,枚举,lambda表达式

文章目录


反射

反射的定义:

java的反射机制是指,在程序的运行状态中,对于任意一个类 ,可以获取这个类的全部信息(包括,类加载器(后面会学到),构造方法,成员属性,成员方法等。)对于任意一个对象,我们也能够调用其属性与方法进行修改信息,这种动态地获取信息与动态地调用对象的机制,我们称为反射机制。

反射相关的类:

反射相关的方法:

在Class类中即包含了获取类加载器,创建指定类对象,获取构造方法,属性,成员方法的方法,如下图所示:




反射示例:

获取Class类对象

此处的Class并不是指表示类名的关键字,而是指Class这个具体的类,

因为Class类的构造方法是私有方法 ,所以不能够直接实例化对象。

我们需要获取Class类对象有三种方式:
第一种:调用对象的getClass方法

java 复制代码
        Student student = new Student();
        //getClass方法也是用c/c++代码实现的
       Class<Student> c1 = (Class<Student>) student.getClass();

第二种:直接调用类名的class属性

 但是我们并没有在Student中定义class属性
    //这说明每一个方法都有一个默认的class属性
java 复制代码
   Class c2 = Student.class;

第三种:通过class对象的ForName方法来获取

java 复制代码
   Class c3 = null ;
   c3 = Class.forName("reflectDemo.Student");

判断这三个class对象是不是同一个对象。

java 复制代码
        System.out.println(c1 ==c2);
        System.out.println(c1==c3);
        System.out.println(c2==c3);

结果表明,我们通过三种方式创建的三个Class对象实际上是一个,

实际上是因为任何一个类在JVM中只会被加载一次,所以其所对应的Class对象也
只会被创建一次。如果通过两个不同的类来创建Class对象,则这两个Class对象不一样。
java 复制代码
    Class c2 = Dog.class;

结果为:此时c2与c1,c3均不相同。

创建指定类的对象

使用的Student类

java 复制代码
public class Student {
   //私有属性name
       private String name = "bit"; //公有属性age
       public int age = 18;
   //不带参数的构造方法
       public Student(){
       System.out.println("Student()");
  }

 private Student(String name,int age) {
 this.name = name;
 this.age = age;
 System.out.println("Student(String,name)");
  }

 private void eat(){
 System.out.println("i am eat");
  }

public void sleep(){
 System.out.println("i am pig");
 }

 private void function(String str) {
 System.out.println(str);
  }

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

}

通过反射实例化对象的方法:

java 复制代码
  public static void reflectNewInstance() throws   ClassNotFoundException, InstantiationException, IllegalAccessException {
        Class c3 = null ;
        c3 =     Class.forName("reflectDemo.Student");
        //要通过c3对象来调用实例化对象的方法
        Student student1 = (Student) c3.newInstance();
        System.out.println(student1);

    }

调用此方法。

java 复制代码
 public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        //通过class对象创建一个学生对象
          reflectNewInstance();
          }

反射私有属性:

java 复制代码
//简写了
public class Student {
      //私有属性name
          private String name = "bit"; //公有属性age
             public int age = 18;

反射私有属性的方法:

java 复制代码
public static void reflectPrivateField(){
    Class c3 = null ;
    try {
        c3 =   Class.forName("reflectDemo.Student");
        Field field =  c3.getDeclaredField("name");
        //我们可以修改这个私有属性的值
        field.setAccessible(true);
        //还需要有一个Student对象,
        Student student3 = (Student)c3.newInstance();
        field.set(student3,"张三");
        System.out.println(student3);

    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
}
java 复制代码
  public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        reflectPrivateField();
    }

反射私有方法:

java 复制代码
public class Student {
private void eat(String s){
     System.out.println(s);
     }
  }
java 复制代码
public static void reflectPrivateMethod(){
        Class  c4 = null;
    try {
        c4 = Class.forName("reflectDemo.Student");
                                                                                               //在有参数时一定要加上参数类型的class
        Method method = c4.getDeclaredMethod("eat",String.class);
        Student student4 = (Student) c4.newInstance();
        method.setAccessible(true);
        method.invoke(student4, "i am eat");

    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
}
java 复制代码
 public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        reflectPrivateMethod();
    }

反射私有的构造方法

java 复制代码
public class Student {
 private Student(String name,int age) {
       this.name = name;
       this.age = age;
        System.out.println("Student(String,name)");
  }
}
java 复制代码
 public static void reflectPrivateConstructor(){
        Class c3 = null ;
        try {
            c3 =     Class.forName("reflectDemo.Student");
            //调用getConstructor方法,参数为

            Constructor<Student> constructor =  c3.getDeclaredConstructor(String.class,int.class);
           //在获取了构造方法之后呢?
            constructor.setAccessible(true);
            //直接调用此构造方法,为对象赋值
         Student student2 =    constructor.newInstance("张三",15);
            System.out.println(student2);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }
java 复制代码
  public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
       reflectPrivateConstructor();
    }

枚举

枚举的意义

枚举是将一组常量组织起来,即将这一组常量赋予特殊的意义,用特殊的类型表示它们。

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

枚举类的实现

枚举类有自己特定的关键字即enum,

所有我们自己定义的枚举类均继承于java提供的Enum类。

枚举类的使用:

在类中定义枚举成员:

java 复制代码
public enum Myenum {
    //在自定义的枚举类中定义一些成员
   Red,Yellow,Blue,Green;
}

获取枚举成员:

java 复制代码
public enum Myenum {
    //在自定义的枚举类中定义一些成员
   Red,Yellow,Blue,Green;

public static void main(String[] args) {
//我们可以通过枚举类名,来直接获取枚举成员,而不需要创建枚举类的对象。
       System.out.println(Myenum.Blue);

    }
}

关于枚举类的构造方法:

:因为枚举类型的构造方法是私有的,所以不能够在其他类中进行

创建对象,但是在枚举类中也不能够创建枚举类对象,

枚举类的使用意义并不在于它的对象,而在于它的枚举成员,其枚举成员可以直接通过类名调用。

Enum类中常用的四个方法:

因为我们定义的枚举类继承于Enum类,所以Enum类中的所有方法均可使用

java 复制代码
public static void main(String[] args) {
        //关于枚举类中方法的使用
        //1. values方法,用于获取枚举类中所有的枚举成员。
        //问题:在Enum类中并没有values方法,那么这个方法是从哪里来的?
      Myenum[] myenums = Myenum.values();
        for (Myenum myenum2: myenums) {
            //在获取了每个枚举成员后,开始进行每个枚举成员的索引位
            // 2. 调用ordinal方法
          System.out.println(  myenum2  +" "+myenum2.ordinal());
        }
        // 3.  valueof方法,将普通的字符串转换成枚举实例?
    //    System.out.println(Myenum.valueOf("GREEN"));
       //意思为:如果在枚举类中有此枚举成员,则会返回对应的枚举成员,如果没有则报异常
        System.out.println("Red");
     // 4. 第四个方法:compareTo方法
        //在Enum类中实现了comparable接口,实现了compareTo方法,用于比较两个
        //枚举成员的索引位置。
        System.out.println(Red.compareTo(Blue));
    }

枚举与反射:

我们不能够通过枚举的构造方法来实例化枚举类对象,那么能否通过反射机制来创建枚举类对象呢?

实际上是不行的。
Enum类中的构造方法:

java 复制代码
public static void main(String[] args)  {
        //尝试通过反射进行实例化enum对象
        Class<?> c1 = null;
        try {
            //要注意当父类的构造方法未调用时,子类的构造方法是不能被调用的
            c1 = Class.forName("enumdemo.Myenum");
            //枚举类不能够通过反射类进行创建。
            //在指定的类的具有父类时,也应该加上父类形参类型的.class属性
             Constructor  constructor=   c1.getDeclaredConstructor(String.class,int.class,int.class,String.class);
             //获取了相应的构造方法后
            constructor.setAccessible(true);

            Myenum myenum = (Myenum) constructor.newInstance(2,"hong");

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }

运行结果:

lambda表达式

函数式接口:

如果一个接口中只有一个抽象方法,则此接口称为函数式接口。

lambda表达式可代替函数式接口的实现,直接将lambda表达式赋给接口类型的变量。

lambda表达式的语法:

如 : (parameters) -> expression 或 (parameters) ->{ statements; }

. paramaters:类似方法中的形参列表,这里的参数是函数式接口里的参数。这里的参数类型可以明确的声明

也可不声明而由JVM隐含的推断。另外当只有一个推断类型时可以省略掉圆括号。

  1. ->:可理解为"被用于"的意思

  2. 方法体:可以是表达式也可以代码块,是函数式接口里方法的实现。代码块可返回一个值或者什么都不反

回,这里的代码块块等同于方法的方法体。如果是表达式,也可以返回一个值或者什么都不返回。

lambda表达式的使用:

java 复制代码
interface NoParameterNoReturn{
    //无参且无返回值
      void test();
}
interface OneParameterNoReturn{
    //有一个参数无返回值
    void test(int a);
}
interface MultipleParameterNoReturn{
    //有多个参数无返回值
    void test(int a,int b);
}
interface NoParameterReturn{
    //无参且有返回值
    int test();
}
interface OneParameterReturn{
    //有一个参数有返回值
    int  test(int a);
}
interface MultipleParameterReturn{
    //有多个参数有返回值
    int  test(int a,int b);
}

public static void main(String[] args) {
        //Lambda表达式可用于实现只有一个抽象方法的接口。
       //先实现无返回值无参数的接口
        NoParameterNoReturn noParameterNoReturn1 = new NoParameterNoReturn() {
            @Override
            public void test() {
                System.out.println("调用无返回值无参数方法");
            }
        };
        //直接调用方法
        noParameterNoReturn1.test();
        //我们采用lambda表达式,直接赋给noParameterNoReturns变量
      NoParameterNoReturn noParameterNoReturn2 = ()->{ System.out.println("调用无返回值无参数方法");};
      noParameterNoReturn2.test();

    }

此两种方式效果相同。

java 复制代码
 public static void main(String[] args) {
        //调用一个无参有返回值的方法
        NoParameterReturn noParameterReturn = ()->10;  //可以不写return 关键字,直接写数值或语句
        System.out.println(noParameterReturn.test());

    }

关于lambda表达式语法精简的问题:

  1. 参数类型可以省略,如果需要省略,每个参数的类型都要省略。
  2. 参数的小括号里面只有一个参数,那么小括号可以省略
  3. 如果方法体当中只有一句代码,那么大括号可以省略
  4. 如果方法体中只有一条语句,且是return语句,那么大括号可以省略,且去掉return关键字。

java集合类与lambda表达式的使用问题:


java 复制代码
public static void main(String[] args) {
        //举例:ArrayList中的forEach语句
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(10);
        arrayList.add(20);
        arrayList.add(15);
        //遍历数组可以用迭代器,foreach语句等,也可以采用arrayList本身的方法
        //accept是接口中的抽象方法,将其重写

        //方法1:直接创建匿名内部类调用。
        arrayList.forEach(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println(integer);
            }
        });

      // 方法2:通过lambda表达式
        //当我们这样编写时,代码很简洁,但是可读性比较差。
        arrayList.forEach((a)->{
            System.out.println(a);
        });

    }
java 复制代码
 public static void main(String[] args) {
        //举例: List中的sort方法
     ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(10);
        arrayList.add(20);
        arrayList.add(15);
   /* arrayList.sort(new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o1.compareTo(o2);
        }
    });*/
       // System.out.println(arrayList);
        arrayList.sort(((o1, o2) -> {return o1.compareTo(o2);}));
        System.out.println(arrayList);
    }

注:

      Lambda表达式的优点是代码简洁,编写方便,缺点,代码可读性变差,不容易调试

变量捕获:

    //变量捕获是指在匿名内部类,或lambda表达式中,所使用的变量在此之前与之后不能被修改,或者此变量被final修饰


困惑:

问题:

  1. Enum中并没有value方法,为什么在自定义的enum类中可以使用这个方法?
  2. 为什么匿名内部类与lambda表达式中用到的变量必须被final修饰或者在使用之前与之后不能被修改?
  3. 我们不能通过枚举的构造方法来创建其对象,也不能通过反射类来创建对象,那么此构造方法还有什么意义?
  4. 什么是类加载器,它是物理的,还是概念的?软件的?
  5. 枚举成员可以通过类名调用,它存放在哪里?答:存放在常量池(也被称为静态区或方法区)当中。
相关推荐
Amarantine、沐风倩✨8 分钟前
设计一个监控摄像头物联网IOT(webRTC、音视频、文件存储)
java·物联网·音视频·webrtc·html5·视频编解码·七牛云存储
无 证明12 分钟前
new 分配空间;引用
数据结构·c++
Kisorge34 分钟前
【C语言】指针数组、数组指针、函数指针、指针函数、函数指针数组、回调函数
c语言·开发语言
路在脚下@1 小时前
spring boot的配置文件属性注入到类的静态属性
java·spring boot·sql
森屿Serien1 小时前
Spring Boot常用注解
java·spring boot·后端
轻口味2 小时前
命名空间与模块化概述
开发语言·前端·javascript
苹果醋32 小时前
React源码02 - 基础知识 React API 一览
java·运维·spring boot·mysql·nginx
晓纪同学3 小时前
QT-简单视觉框架代码
开发语言·qt
威桑3 小时前
Qt SizePolicy详解:minimum 与 minimumExpanding 的区别
开发语言·qt·扩张策略
Hello.Reader3 小时前
深入解析 Apache APISIX
java·apache