反射、枚举和lambda表达式

目录

一、 定义

二、用途

三、反射相关的类

 四、相关类的使用

4.1class类

4.2Class类中的相关方法

 4.3 反射实例

4.3.1 获得Class对象的三种方式

4.3.2 反射的使用

五、枚举

5.1 背景及定义

5.2 使用

5.3 枚举和反射

六、Lambda 表达式

6.1 背景

6.2 Lambda 表达式的语法

6.3 函数式接口

6.4 Lambda表达式的基本使用

6.5 变量捕获

6.5.1 匿名内部类

6.5.2 匿名内部类的变量捕获

6.5.3 Lambda 的变量捕获

七、Lambda自集合当中的使用

7.1 Collection 接口 

 7.2 List 接口

7.3 Map 接口

7.4 总结


一、 定义

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

二、用途

1. 在日常的第三方应用开发过程中,经常会遇到某个类的某个成员变量、方法或是属性是私有的或是只对系统应用开放,这时候就可以利用Java的反射机制通过反射来获取所需的私有成员或是方法 。
2. 反射最重要的用途就是开发各种通用框架,比如在spring中,我们将所有的类Bean交给spring容器管理,无论是XML配置Bean还是注解配置,当我们从容器中获取Bean来依赖注入时,容器会读取配置,而配置中给的就是类的信息,spring根据这些信息,需要创建那些Bean,spring就动态的创建这些类。

三、反射相关的类

 四、相关类的使用

4.1class类

Java文件被编译后,生成了.class文件,JVM此时就要去解读.class文件 ,被编译后的Java文件.class也被JVM解析为一个对象,这个对象就是 java.lang.Class .这样当程序在运行时,每个java文件就最终变成了Class类对象的一个实例。我们通过Java的反射机制应用到这个实例,就可以去获得甚至去添加改变这个类的属性和动作,使得这个类成为一个动态的类 。

4.2Class类中的相关方法

常用获得类的相关方法

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

获得类中注解相关的方法

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

获得类中方法相关的方法(以下方法返回值为method相关) 

 4.3 反射实例

4.3.1 获得Class对象的三种方式

在反射之前,我们需要做的第一步就是 先拿到当前需要反射的类的Class对象,然后通过Class对象的核心方法,达到反射的目的,即:在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性,既然能拿到,那么我们就可以修改部分类型信息。

第一种,使用Class.forName("类的全路径名");静态方法。

前提:已明确类的全路径名。

第二种,使用.class方法。

说明:仅适合在编译期就已经明确要操作的Class

第三中,使用类对象的 getClass() 方法

4.3.2 反射的使用

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

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

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

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class ReflectClass {
    public static void reflectNewInstance(){
        try {
            //获得class类
            Class<?> classStudent =Class.forName("Java.Student");
            //获得实例对象
            Student student=(Student) classStudent.newInstance();
            System.out.println("获得学生对象:"+student);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    public static void reflectPrivateConstructor(){
        try{
            Class<?> classStudent =Class.forName("Java.Student");
            //注意参数的类型,参数都是Class类
            Constructor<?> declaredConstructorStudent =classStudent.getDeclaredConstructor(String.class,int.class);
            //此时必须调用setAccessible函数来确认调用构造方法
            declaredConstructorStudent.setAccessible(true);
            Student student=(Student) declaredConstructorStudent.newInstance("akai",20);
            System.out.println(student);
        } 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();
        }
    }

    public static void reflectPrivateField()  {
        try {
            Class<?> fieldClass=Class.forName("Java.Student");
            Field field = fieldClass.getDeclaredField("name");
            field.setAccessible(true);
            Student student=(Student) fieldClass.newInstance();
            field.set(student,"akai");
            String name=(String)field.get(student);
            System.out.println(name);

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }
    public static void reflectprivateMethod(){
        try {
            Class<?> methodClass=Class.forName("Java.Student");
            Method method=methodClass.getDeclaredMethod("function",String.class);
            method.setAccessible(true);
            Student student=(Student) methodClass.newInstance();
            method.invoke(student,"我是私有的function函数传递的参数");
        } 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();
        }
    }
    public static void main(String[] args) {
        //reflectNewInstance();
        //reflectPrivateConstructor();
        //reflectPrivateField();
        reflectprivateMethod();
    }
}

五、枚举

5.1 背景及定义

枚举是在JDK1.5以后引入的。主要用途是:将一组常量组织起来,在这之前表示一组常量通常使用定义常量的方式:

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

但是常量举例有不好的地方,例如:可能碰巧有个数字1,但是他有可能误会为是RED,现在我们可以直接用枚举来进行组织,这样一来,就拥有了类型——枚举类型。而不是普通的整型1.

public enum TestEnum{
    RED,BLACK,GREEN;
}

优点:将常量组织起来统一进行管理

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

本质:是java.lang.Enum的子类,也就是说,自己写的枚举类,就算没有显示的继承Enum,但是其默认继承了这个类

5.2 使用

1. switch 语句

public enum TestEnum {
    RED,BLACK,GREEN,WHITE;
    public static void main(String []args){
        TestEnum testEnum=TestEnum.GREEN;
        switch (testEnum){
            case RED:
                System.out.println("red");
                break;
            case BLACK:
                System.out.println("black");
                break;
            case GREEN:
                System.out.println("green");
                break;
            case WHITE:
                System.out.println("white");
                break;
            default:break;
        }
    }
}

2. 常用方法

Enum类的常用方法

 使用:

    public static void main(String[] args) {
        TestEnum []testEnum=TestEnum.values();
        for(int i=0;i<testEnum.length;i++){
            System.out.println(testEnum[i]+" 索引:"+testEnum[i].ordinal());
        }
        System.out.println("======");
        System.out.println(TestEnum.valueOf("BLACK"));
        System.out.println("=======");
        //拿到枚举实例BLACK
        TestEnum testEnum2 = TestEnum.BLACK;
        //拿到枚举实例RED
        TestEnum testEnum3 = TestEnum.RED;
        //返回值为索引相减
        System.out.println(testEnum2.compareTo(testEnum3));
        System.out.println(BLACK.compareTo(RED));
        System.out.println(RED.compareTo(BLACK));
    }

3. 在Java当中枚举实际上就是一个类。所以我们在定义枚举的时候,还可以这样定义和使用枚举:

public enum TestEnum {
    RED("rec",0),BLACK("black",1),GREEN("green",2),WHITE("white",3);
    private String name;
    private int key;
    TestEnum(String name,int key){
        this.name=name;
        this.key=key;
    }
    public static TestEnum getEnumKey(int key){
        for (TestEnum t: TestEnum.values()){
            if(t.key==key){
                return t;
            }
        }
        return null;
    }
}

注:Enum的构造方法默认是私有的,且只能用private修饰

5.3 枚举和反射

public static void reflectPrivateConstructor(){
        try {
            Class<?> calssEnum=Class.forName("testEnum.TestEnum");
            //所有的枚举类型都默认继承java.lang.Enum,因此子类要帮助父类构造,此时需要传四个参数
            Constructor constructor=calssEnum.getDeclaredConstructor(String.class,int.class,String.class,int.class);
            constructor.setAccessible(true);
            TestEnum testEnum=(TestEnum) constructor.newInstance("花色",4,"棕色",6);
            System.out.println(testEnum);
        } 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();
        }
    }

    public static void main(String[] args) {
        reflectPrivateConstructor();
    }

此时还会报错,那就要从源码中寻找报错的原因 

源码显示:

 没错,枚举在这里被过滤掉了,你不能通过反射来获取枚举类型的实例!

六、Lambda 表达式

6.1 背景

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

6.2 Lambda 表达式的语法

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

Lambda 表达式由三部分组成:

1.paramaters:类似方法中的形参列表,这里的参数是函数式接口里的参数。这里的参数类型可以明确地声明,也可以不声明而由JVM隐含的推断。另外当只有一个推断类型时可以省略圆括号。

2.->:可以累计额为“被用于”的意思

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

6.3 函数式接口

要了解Lambda表达式首先需要了解什么是函数式接口,函数式接口定义:一个接口有且只有一个抽象方法。

注意:

1. 如果只有一个接口只有一个抽象方法,那么该接口就是一个函数式接口

2.如果我们在某个接口上声明了 @FunctionalInterface 注解,那么编译器就会按照函数式接口的定义来要求该接口,这样如果有两个抽象方法,程序编译就会报错的。所以,从某种意义上来说,只要你保证你的接口中只有一个抽象方法,你可以不加这个注解。加上就会自动进行检测的。

定义方式:

@FunctionalInterface
interface  NoparameterNoReturn{
    //注意:只能有一个方法
    void test();
}

还可以这样:

@FunctionalInterface
interface NoparameterNoReturn{
    void test();
    default void test2(){
        System.out.println("JDK1.8 新特性,default 默认方法可以有具体的实现");
    }
}

6.4 Lambda表达式的基本使用

首先,我们实现准备好几个接口:

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

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

没有使用lambda表达式的时候的调用方式:

NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn(){
    @Override
    public void test() {
        System.out.println("hello");
    }
};
noParameterNoReturn.test();

lambda使用

 public static void main(String[] args) {
        NoParameterNoReturn noParameterNoReturn=()->{
            System.out.println("无参数无返回值");
        };
        OneParameterNoReturn oneParameterNoReturn=a-> System.out.println(a);
        //oneParameterNoReturn.test(2);
        MoreParameterNoReturn moreParameterNoReturn=(a,b)-> System.out.println(a+b);
        //moreParameterNoReturn.test(2,3);
        NoParameterReturn noParameterReturn=()->20;
        //System.out.println(noParameterReturn.test());
        OneParameterReturn oneParameterReturn=a -> 10;
        //System.out.println(oneParameterReturn.test(3));
        MoreParameterReturn moreParameterReturn=(a,b)->a*b;
        //System.out.println(moreParameterReturn.test(2, 3));
    }

语法精简

1.参数类型可以省略,如果省略,每个参数类型都要省略。

2.参数的小括号里面只有一个参数,那么小括号可以省略。

3.如果方法体当中只有一句代码,那么大括号可以省略。

4.如果方法体中只有一条语句,且是return语句,那么大括号可以省略,且去掉return关键字

6.5 变量捕获

Lambda表达式中存在变量捕获,了解了变量捕获之后,我们才能更好的理解Lambda表达式的作用域,Java当中的匿名类中,会存在变量捕获

6.5.1 匿名内部类

匿名内部类就是没有名字的内部类。我们这里只是为了说明变量捕获,所以,简单会使用就好,

class Test {
    public void func(){
        System.out.println("func()");
    }
}
public class TestDemo {
    public static void main(String[] args) {
        new Test(){
            @Override
            public void func() {
                System.out.println("我是内部类,且重写了func这个方法!");
            }
        };
    }
}

6.5.2 匿名内部类的变量捕获

class Ttt{
    public void func(){
        System.out.println("func");
    }
}
public class Test {
    public static void main(String[] args) {
        int a=100;
        new Ttt(){
            //a=99;此时代码编译直接报错
            @Override
            public void func(){
                System.out.println("a="+a);
            }
        }.func();
    }
}

在上述代码中的变量a就是捕获的变量。这个变量要么是被final修饰,如果不是被final修饰的,你要保证在使用之前,没有修改。否则就会编译失败。

6.5.3 Lambda 的变量捕获


@FunctionalInterface
interface NoParameterNoReturn {
    void test();
}
public static void main(String[] args) {
    int a = 10;
    NoParameterNoReturn noParameterNoReturn = ()->{
        // a = 99; error
        System.out.println("捕获变量:"+a);
    };
    noParameterNoReturn.test();
}

七、Lambda自集合当中的使用

为了能够让Lambda和Java的集合类集更好的一起使用,集合当中,也新增了部分接口,以便于Lambda表达式对接。

注意:Collection的forEach()方法是从接口 java.lang.Iterable 拿过来的。

7.1 Collection 接口 

forEach 方法演示

该方法在接口Iterable当中,原型如下:

    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

该方法表示:对容器中的每个元素执行action指定的动作。

        List list =new ArrayList<>();
        list.add("hello");
        list.add("world");
        list.add("byebye");
        list.forEach(new Consumer() {
            @Override
            public void accept(Object o) {
                System.out.println(o);
            }
        });
        //上述代码可以用lambda表达式代替达到同样的效果
        //list.forEach((s)-> System.out.println(s));
    }

 7.2 List 接口

sort() 方式的演示

sort() 方法的源码:

    default void sort(Comparator<? super E> c) {
        Object[] a = this.toArray();
        Arrays.sort(a, (Comparator) c);
        ListIterator<E> i = this.listIterator();
        for (Object e : a) {
            i.next();
            i.set((E) e);
        }
    }

使用示例:

        List<String> list =new ArrayList<>();
        list.add("hello");
        list.add("world");
        list.add("byebye");
        /*list.sort(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.compareTo(o2);
            }
        });*/
        list.sort((s1,s2)->s1.compareTo(s2));

7.3 Map 接口

HashMap 的forEach()

源码如下:

    public void forEach(BiConsumer<? super K, ? super V> action) {
        Node<K,V>[] tab;
        if (action == null)
            throw new NullPointerException();
        if (size > 0 && (tab = table) != null) {
            int mc = modCount;
            for (int i = 0; i < tab.length; ++i) {
                for (Node<K,V> e = tab[i]; e != null; e = e.next)
                    action.accept(e.key, e.value);
            }
            if (modCount != mc)
                throw new ConcurrentModificationException();
        }
    }

代码示例:

    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("Integer:"+integer+" String"+s);
            }
        });*/
        map.forEach(((integer, s) -> System.out.println("Integer:"+integer+" String:"+s)));
    }

7.4 总结

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

优点:

1.代码简洁,开发迅速

2.方便函数式编程

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

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

缺点:

1.代码的可读性变差

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

3.不易进行调试

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值