反射、枚举以及lambda表达式

导读:本篇文章讲解 反射、枚举以及lambda表达式,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

目录

1. 反射的理解

2. 反射相关的类

2.1 Class类中的相关方法

2.2 反射使用

2.3 反射优缺点

3. 枚举的使用

4.枚举不能被反射

5. Lambda表达式

5.1 Lambda表达式语法

5.2 函数式接口

5.3 Lambda表达式的基本使用

5.4 变量捕获

5.5 Lambda在集合中使用

5.6 Lambda表达式优缺点


1. 反射的理解

(1)反射机制(reflection)

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

(2)应用

1.反射最重要的作用是开发各种通用的框架

2.在日常的第三方应用开发的过程中,经常遇到某个类的某个成员变量、方法或是私有的或是只对系统应用开发的,这时候就可以用java反射机制获取私有成员或方法

(3)反射基本信息

java程序中许多对象运行时会有两种类型:

运行时类型和编译时类型

程序运行本质是在运行时发现对象的类的真实信息,通过反射机制就可以判断出该对象和类属于那些类


2. 反射相关的类

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

2.1 Class类中的相关方法

反射、枚举以及lambda表达式

 (1)常用获得类相关的方法

方法 用途
getClassLoader() 获得类的加载器
getDeclaredClasses() 返回一个数组,数组中包含该类中所有类和接口类的对象(包括私有)
forName(String className) 根据类名返回类的对象
newInstance() 创建类的实例
getName() 获得类的完整路径名字

 

(2)常用获得类中属性相关的方法(返回值为Field相关)

方法 用途
getField(String name) 获得某个公有的属性对象
getFields() 获得所有公有的属性对象
getDeclaredField(String name) 获得某个属性对象
getDeclaredFields() 获得所有属性对象

(3)获得类中的构造器相关的方法(返回值为Constructor相关)

方法 用途
getConstructor(Class…<?>parameter Types)
获得该类中与参数类型匹配的公有构造方法
getConstructors()
获得该类的所有公有构造方法
getDeclaredConstructor(Class…<?> parameterTypes)
获得该类中与参数类型匹配的构造方法
getDeclaredConstructors()
获得该类所有构造方法

(4)获得类中方法 相关的方法(返回值为Method相关)

方法 用途
getMethod(String name, Class…<?> parameterTypes)
获得该类某个公有的方法
getMethods()
获得该类所有公有的方法
getDeclaredMethod(String name, Class…<?> parameterTypes)
获得该类某个方法
getDeclaredMethods()
获得该类所有方法


 

2.2 反射使用

(1)获得Class对象的三种方式

1. 使用Class.forName(“类的全路径名”);静态方法

public static void main(String[] args) throws ClassNotFoundException {
        //1.通过Class对象的forName()静态方法获取(用的最多,注意抛异常)
        Class<?> c1 = Class.forName("reflectdemo.Student");
    }

2. 使用.class方法

 //3.直接通过 类型.class获取(更安全可靠,程序性能更高)
        Class<?> c3 = Student.class;

3. 使用类对象的getClass()方法

//2.通过getClass获取class对象
        Student student = new Student();
        Class<?> c2 = student.getClass();

(2)反射的使用

所有和反射相关的包都在import.java.lang.reflect包下

1.通过反射创建对象

public static void reflectNewInstance() {
        try {
            Class<?> c1 = Class.forName("reflectdemo.Student");
            Student student = (Student) c1.newInstance();

            System.out.println(student);

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

2.反射私有的构造方法,屏蔽内容为获得公有的构造方法

  public static void reflectPrivateConstructor() {
        try {
            Class<?> c1 = Class.forName("reflectdemo.Student");
            //注意传入对应的参数
            Constructor<?> constructor =
                    c1.getDeclaredConstructor(String.class, int.class);
            //你确定要在类外访问私有方法吗.设置为true后可修改访问权限
            constructor.setAccessible(true);

            Student student = (Student) constructor.newInstance("xiangyu",22);
            System.out.println(student);

        } catch (ClassNotFoundException e) {

        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

3.反射私有属性

 public static void reflectPrivateField() {
        try {
            Class<?> c1 = Class.forName("reflectdemo.Student");
            Student student = (Student) c1.newInstance();
            //可以修改该属性的值
            Field field = c1.getDeclaredField("name");
            //访问私有的,提前询问一下是否确定访问
            field.setAccessible(true);
            field.set(student,"liubang");
            System.out.println(student);

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

4. 反射私有方法

public static void reflectPrivateMethod() {
        try {
            Class<?> c1 = Class.forName("reflectdemo.Student");
            Student student = (Student) c1.newInstance();

            Method method = c1.getDeclaredMethod("function", String.class);
            method.setAccessible(true);

            method.invoke(student,"通过反射给你传参");

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

2.3 反射优缺点

优点:

1.任意一个类,都能够知道这个类的所有属性和方法

   任意一个对象,都能够调用它的任意一个方法

2.增加程序的灵活性和扩展性,降低耦合性,提高自适应能力

3.反射运用在很多框架上

缺点:

1.反射因为调用很多方法,就会有效率降低的问题

2.反射技术绕过了源代码的技术,会有维护的问题,并且反射代码比相应直接代码更复杂


3. 枚举的使用

(1)枚举理解

枚举是在JDK1.5引入使用的

作用:将一组常量组织起来,在这之前表示一组常量通过使用定义常量的方式

本质:是java.lang.Enum的子类,也就是自定义的枚举类,就是没有显示继承Enum,默认也继承了这个类

public enum enumDemo01 {
    ONE,TWE,TREE;//枚举对象
}

 (2)switch语句

public static void main1(String[] args) {
        enumDemo01 enumDemo01 = enumdemo.enumDemo01.TWE;
        switch (enumDemo01) {
            case ONE:
                System.out.println("1");
                break;
            case TWE:
                System.out.println("2");
                break;
            case TREE:
                System.out.println("3");
                break;
            default:
                break;
        }
 }

(3)Enum类的常用方法

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

 public static void main2(String[] args) {
        enumDemo01[] enumDemo01s = enumDemo01.values();
        for (int i = 0; i < enumDemo01s.length; i++) {
            System.out.println(enumDemo01s[i] + " 序号:" + enumDemo01s[i].ordinal());
        }

        enumDemo01 e1 = enumDemo01.valueOf("TREE");
        System.out.println(e1);

    }
public static void main(String[] args) {
        //拿到枚举实例TWE
        enumDemo01 e1 = enumDemo01.TWE;
        //拿到枚举实例TREE
        enumDemo01 e2 = enumDemo01.TREE;
        //比较的是序号
        System.out.println(enumDemo01.TREE.compareTo(e1));
        System.out.println(ONE.compareTo(TWE));
        System.out.println(ONE.compareTo(TREE));
    }

(4)枚举的构造方法默认是私有的

public enum enumDemo01 {
    ONE,TWE("twe",2),TREE;//枚举对象

    public String figure;
    public int ordinal;
    //枚举的构造方法默认是私有的
    enumDemo01(String figure, int ordinal) {
        this.figure = figure;
        this.ordinal = ordinal;
    }
    enumDemo01() {
    }
}

 (5)枚举优缺点

优点:

1.枚举常量更简单安全

2.枚举具有内置方法,代码跟美观

3.枚举无法被反射和序列化

缺点:

1.不可继承,无法扩展


4.枚举不能被反射

前面学过了反射,对任何一个类,即使构造方法是私有的,也可以通过反射拿到实例对象,那么枚举的构造方法也是私有的,通过反射可以拿到吗?

public class Demo01 {
    public static void reflectPrivateConstructor() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
            Class<?> c1 = Class.forName("enumdemo.enumDemo01");
            Constructor<?> constructor =
                    c1.getDeclaredConstructor(String.class, int.class,String.class, int.class);
            constructor.setAccessible(true);

            enumDemo01 e1 = (enumDemo01)constructor.newInstance("xigua",66);
            System.out.println(e1);
    }

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        reflectPrivateConstructor();
    }
}

反射、枚举以及lambda表达式

 可以看到是报错的,找到newInstance源码

可以看到

反射、枚举以及lambda表达式

 枚举是不能被反射的。所以枚举是很安全的


5. Lambda表达式

5.1 Lambda表达式语法

(1)理解

Lambda表达式相当于匿名函数一样,Lambda表达式允许将一个函数作为另外一个函数的参数;

Lambda表达式允许通过表达式来代替功能接口;

Lambda表达式就和方法一样,提供一个正常的参数列表和使用这些参数的主体(可以是表达式、代码块)

lambda表达式也可称为闭包

(2)语法

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

Lambda表达式三部分组成:

1) paraments:相当于方法中的参数列表,这里的参数是函数式接口里的函数

参数类型可以明确声明,也可以又jvm隐含判断

当只有一个推断类型时可以省略圆括号

2)->:相当于 “被用于”

3)方法体:表达式可以是代码块,也可以是函数式接口里方法的实现

可以有返回值,也可以不返回

//1.不需要参数,直接返回值
()-> 6
//2.一个参数类型,返回值
x -> 2*x
//3.两个参数,返回值
(x,y) -> x+y
//4.两个int型整数,返回值
(int x,int y) -> x*y
//5.接收一个string对象,在控制台打印,不返回值
(String s) -> system.out.print(s)

5.2 函数式接口

函数式接口定义:一个接口有且只有一个抽象方法 。

我们在写函数式接口时可以声明@FunctionalInterface 注解,这样编译器就可以根据函数式接口的定义来要求该接口,此时如果有两个抽象方法,编译器就会报错

反射、枚举以及lambda表达式
public class Demo01 {
    public static void main(String[] args) {
        PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2-o1;
            }
        });
    }
    //此时这里相当于一个类实现了Comparator接口  同时重写了抽象方法compare
    PriorityQueue<Integer> priorityQueue = new PriorityQueue<>((o1, o2) -> {return o2-o1;});
    PriorityQueue<Integer> priorityQueue1 = new PriorityQueue<>(((o1, o2) -> o2-o1));
}

 

5.3 Lambda表达式的基本使用

Lambda
表达式本质上是一个匿名函数
可以理解为:
Lambda
就是匿名内部类的简化
实际上是创建了一个类,实现了接口,重写了接口的方法 

下面来写几个接口,分别用Lambda表达式和不适用Lambda表达式来调用对比一下

(1)无返回值无参数

    @FunctionalInterface
    interface NoParameterNoReturn {
        void test();
    }

 两种方式分别调用对比

 public static void main(String[] args) {
        NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn() {
            @Override
            public void test() {
                System.out.println("测试一下");
            }
        };
        noParameterNoReturn.test();

        NoParameterNoReturn noParameterNoReturn1 = () -> {System.out.println("测试一下");};
        NoParameterNoReturn noParameterNoReturn2 = () -> System.out.println("测试一下");
        NoParameterNoReturn noParameterNoReturn3 = () -> {
            System.out.println("测试1下");
            System.out.println("测试2下");
        };
    }

(2)无返回值一个参数

@FunctionalInterface
    interface OneParameterNoReturn {
        void test(int a);
    }
  public static void main(String[] args) {
        OneParameterNoReturn oneParameterNoReturn = (int x) -> {
            System.out.println(x);
        };
        oneParameterNoReturn.test(100);
        System.out.println("简化:");
        OneParameterNoReturn oneParameterNoReturn1 = x -> System.out.println(x);
        OneParameterNoReturn oneParameterNoReturn2 = System.out::println;
        oneParameterNoReturn1.test(10);
    }

(3)无返回值多个参数

 @FunctionalInterface
    interface MoreParameterNoReturn {
        void test(int a,int b);
    }
 public static void main(String[] args) {
        MoreParameterNoReturn moreParameterNoReturn = (int a,int b) -> {
            System.out.println(a+b);
        };
        moreParameterNoReturn.test(10,20);
        //要求两个类型同时省略
        MoreParameterNoReturn moreParameterNoReturn1 = (a,b) -> System.out.println(a+b);
        moreParameterNoReturn1.test(20,30);
    }

(4)有返回值无参数

 @FunctionalInterface
    interface NoParameterReturn {
        int test();
    }
 public static void main(String[] args) {
        NoParameterReturn noParameterReturn = () -> {return 10;};
        int ret = noParameterReturn.test();
        System.out.println(ret);

        NoParameterReturn noParameterReturn1 = () ->10;
        int ret2 = noParameterReturn1.test();
        System.out.println(ret2);
    }

(5)有返回值一个参数

@FunctionalInterface
    interface OneParameterReturn {
        int test(int a);
    }
 public static void main(String[] args) {
        OneParameterReturn oneParameterReturn = x -> 2*x;
        System.out.println(oneParameterReturn.test(10));

    }

(6)有返回值多个参数

  @FunctionalInterface
    interface MoreParameterReturn {
        int test(int a,int b);
    }
 public static void main(String[] args) {
        MoreParameterReturn moreParameterReturn = (x,y) -> x+y;
        System.out.println(moreParameterReturn.test(10, 20));
    }
Lambda语法简略规则:
1. 参数类型可以省略,并且要省略就要全部省略
2. 参数的括号当里面只有一个参数时,括号可以省略
3. 如果方法体中只有一句代码,那么大括号可以省略
4. 如果方法体中只有一句代码,尤其是return语句,那么大括号和return关键字都可以省略

 

5.4 变量捕获

(1)匿名内部类变量捕获

class Demo {
        void fun() {
            System.out.println("dasd");
        }
    }

在匿名内部类中,变量捕获是指外部变量的捕获

外部变量在匿名内部类中使用,一定是在匿名内部类中没有被修改过的

内部变量不影响 

public static void main7(String[] args) {
        int a = 10;
        //在匿名内部类中,变量捕获(外部变量的捕获),看使用的外部变量一定是未被修改过的
        new Demo() {
            @Override
            void fun() {
                int c = 99;
                c = 100;
                System.out.println(c+"重写一下" + a);
            }
        }.fun();
    }

 (2)Lambda变量捕获

 Lambda 表达式中存在变量捕获 ,必须理解Lambda变量捕获后,才可以明白Lambda 表达式的作用域,其变量捕获规则和匿名内部类规则类似


 

5.5 Lambda在集合中使用

集合中新增了部分接口,便于和Lambda表达式对接

这里举几个例子

(1)Collection接口中forEach()打印全部

(2)List接口中sort()排序

分别写出使用Lambda表达式和不使用的方法

  public static void main(String[] args) {
        //Lambda内部也是匿名内部类,用外部变量不能被修改
        ArrayList<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("bit");
        list.add("hellol");
        list.add("lambda");
        //打印全部
        list.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });
        list.forEach(s -> System.out.println(s));
        //排序
        list.sort(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.compareTo(o2);
            }
        });
        list.sort(((o1, o2) -> o1.compareTo(o2)));
        list.forEach(s -> System.out.println(s));
    }

(3)Map接口中forEach()

public static void main(String[] args) {
        HashMap<Integer, String> map = new HashMap<>();
        map.put(1, "hello");
        map.put(2, "bit");
        map.put(3, "hello");
        map.put(4, "lambda");

        map.forEach(new BiConsumer<Integer, String>() {
            @Override
            public void accept(Integer integer, String s) {
                System.out.println("key:"+integer+"val:"+s);
            }
        });
        map.forEach((x,y)-> System.out.println("key:"+x+"val:"+y));
    }

5.6 Lambda表达式优缺点

优点

1.代码简介,开发迅速

2.方便函数式变成

3.改善集合操作

缺点

1.代码可读性差

2.不容易进行调试

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


 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/91245.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!