反射,枚举,Lambda表达式

发布于:2024-12-06 ⋅ 阅读:(32) ⋅ 点赞:(0)

1. 反射

        1.1 反射的概念

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

        关于运行时和编译时

        java程序中许多对象在运行时会出现两种类型:运行时类型(RTTI)和编译时类型,例如Person p = new Student();这句代码中p在编译时类型为Person,运行时类型为Student。程序需要在运行时发现对象和类的真实信息。而通过使用反射程序就能判断出该对象和类属于哪些类.

        1.2 关于反射相关的类

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

                1.2.1 Class类

                Java文件在被编译之后生成了.class文件 , JVM把.class解析为一个对象,这个对象就是Class类对象(java.lang.Class). 然后当程序在运行的时候,每个java文件就最终变成了Class类对象的一个实例.我们可以使用java的反射机制去使用这个实例,可以去修改类里面的属性和方法.

                1.2.1 Class类相关的方法        

                1> 常用的获得类相关的方法

        注意: 加了Declared就说明公有私有都能获得

方法 用途
getClassLoader() 获得类的加载器
getDeclaredClasses()

返回一个数组,数组中包含整个类所有类(内部类、匿名类、成员类等),包括公共、保护、默认(包私有)和私有类,但不包括从父类或接口继承的类.

forName(String className) 加载并返回对应的 Class 对象
newInstance() 创建类的实例
getName() 获得类的完整路径名字

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

方法 用途
getField(String name) 获得public修饰的属性对象
getFields()

获得Field 对象数组(所有的public修饰属性对象)

getDeclaredField(Sting name) 获得某个属性对象(包括私有的)
getDeclaredFields() 获得Field对象数组(包含私有的)

                3> 获得类总构造器相关的方法

方法 用途
getConstructor(Class...<?> parameterTypes) 用于获取指定参数类型的公共构造方法
getMethods() 返回Method数组(public修饰的方法对象)
getDeclaredMethod(String name,class<?>parameterTypes) 用于获得指定参数类型的任意构造方法
getDeclaredMethods() 返回Method数组(任意修饰符修饰的方法对象)

                4> 获得类中的其他普通方法(返回Method)

方法 用途
getMethod(String name,class...<?> parameterTypes) 用于获得指定参数类型的public修饰的普通方法
getMethods() 用于获得Method对象数组
getDeclaredMethod(Sting name,Class...<?> parameterTypes) 用于获得各种修饰符修饰的普通方法
getDeclaredMethods() 用于获得Method对象数组(任意修饰符修饰)

        1.3 反射代码实例

                1.3.1 获得Class对象的三种方法         

        在反射前,我们首先要拿到当前需要反射的类的Class对象,然后通过Class对象的核心方法,达到反射目的,也就是: 在运行状态中,对于任意一个类,我们都能知道整个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性,那么都能拿到了,我们也能对信息进行修改. 

                1>  使用Class.forName("类的全路径名") 前提: 已明确类的全路径名.

                2>  使用.class方法 前提: 仅适合编译前就晓得要操作的Class

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

                示例代码:

package 反射_枚举_lambda表达式.反射;

import java.lang.reflect.Method;

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

    private Student(String name,int age) {//这个构造方法是私有的,因此获得它需要setAccessible这个方法
        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 +
                '}';
    }
}
public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
//set不管是TreeSet还是HashSet都是有序的
//    TODO 1. 获得class对象
        //class对象只有一个
        //一般用1>,2>
        
//	        1> class.forName
        Class<?> c1 = null;
        try {
             c1 = Class.forName("反射_枚举_lambda表达式.反射.Student");//里面放要反射的类所存放的路径
        }catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
//	        2> .class方法
           Class<?> c2 = Student.class;
//	        3> 使用类对象的getClass()方法
            Student student = new Student();
           Class<?> c3 = student.getClass();
            System.out.println(c1 == c2);
            System.out.println(c1 == c3);
    }

}

        注意

        1> 说明任意一个类都含有一个隐含的静态成员变量class.        

        2> 通过Class对象的ForName()来获取class对象,会抛出ClassNotFoundException异常

        3> 这说明一个类在JVM中指挥有一个Class实例,所有我们用三种方式获得的Class对象都是同一个.

                在进行获取方法属性对象之前,我们先介绍一下我们自己写的Student类

                1.3.2 创建对象

        具体代码:

package 反射_枚举_lambda表达式.反射;

public class Test1 {
    public static void reflectNewInstance() {
        //获得Class对象
        Class<?> classStudent = null;
        try {
         classStudent = Class.forName("反射_枚举_lambda表达式.反射.Student");//存放类的全路径
         //class类的NewInstance,产生类对象实例
         Student student = (Student) classStudent.newInstance();//擦除机制,向下转型,调用不带参数的构造方法

            System.out.println(student);//打印toSting里面的信息

        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

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

运行结果:

                 1.3.3 获得私有构造方法

                具体代码:           

package 反射_枚举_lambda表达式.反射;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Test1 {

    public static void reflectPrivateConstructor() {
   //获得Class对象
        Class<?> classStudent = null;
        try{
            classStudent = Class.forName("反射_枚举_lambda表达式.反射.Student");
            //获得构造方法
            Constructor<?> constructor = classStudent.getDeclaredConstructor(String.class,int.class);//获得的是俩个参数的构造方法
            Constructor<?> constructor1 = classStudent.getDeclaredConstructor(String.class);//获得的是一个参数的构造方法
            constructor1.setAccessible(true);
            //开关,相当于需要你签个字同意获取构造方法
            constructor.setAccessible(true);
            //创建class类的实例
            Student student = (Student) constructor.newInstance("xiaohei",18);
            System.out.println(student);
            student = (Student) constructor1.newInstance("小黑");
            System.out.println(student);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }

    }

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


                运行结果:

                1.3.4 获得私有属性         

                具体代码:

package 反射_枚举_lambda表达式.反射;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

public class Test1 {
   
    //反射私有属性/字段
    public static void reflectPrivateField() {
        Class<?> classStudent = null;
        try{
            classStudent = Class.forName("反射_枚举_lambda表达式.反射.Student");
            Field field = classStudent.getDeclaredField("name");//私有
            Field field1 = classStudent.getDeclaredField("age");//公有,公有的直接改即可
            //修改的是私有的所以我们需要获得允许
            field.setAccessible(true);
            //class类的NewInstance,产生类对象实例
            Student student = (Student) classStudent.newInstance();
            //修改字段值
            field.set(student,"小白");
            System.out.println(student);
            field1.set(student,10);
            System.out.println(student);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

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

                1.3.5 获得私有方法

                直接通过Class类来调用私有方法进行使用

        具体代码:

package 反射_枚举_lambda表达式.反射;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;

public class Test1 {

    public static void reflectPrivateMethod() {
        //1> 获取class对象
        Class<?> classStudent = null;
        try{
            classStudent = Class.forName("反射_枚举_lambda表达式.反射.Student");
            Method method = classStudent.getDeclaredMethod("function", String.class);//方法名字,参数类型
            //设置可以被修改
            method.setAccessible(true);
            //修改哪个对象(类)的私有方法
            Student student = (Student) classStudent.newInstance();//擦除机制,向下转型
            method.invoke(student,"我是修改了的值");//

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

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

        运行结果     

  

2. 枚举

          2.1 枚举的概念

                枚举是一种组织常量的数据类型.

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

        应用: 错误状态码,颜色的划分,状态机...

                        写法:

                public enum TestEnum { RED,BLACK,GREEN;}//里面放的是枚举对象
 

           2.2 枚举的应用

                1> switch-case

                 举例:
 

package 反射_枚举_lambda表达式.枚举;

public enum TestEnum {//默认继承于枚举类 Enum(双击shift去找)
    //枚举对象
    RED,BLACK,GREEN;

    //1> 枚举一般和switch配套使用
    public static void main1(String[] args) {
        TestEnum color = RED;
        switch (color) {
            case RED:
                System.out.println("RED");
                break;
            case GREEN:
                System.out.println("GREEN");
                break;
            case BLACK:
                System.out.println("BLACK");
                break;
            default:
                System.out.println("其他情况");
                break;
        }
    }
}

        运行结果:

                2> 常用方法

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

        举例:

package 反射_枚举_lambda表达式.枚举;

public enum TestEnum {//默认继承于枚举类 Enum(双击shift去找)
    //枚举对象
    RED(1,"红色"),BLACK(2,"黑色"),GREEN(3,"绿色");
    private int ordinal;
    private String color;
    private TestEnum(int ordinal,String color) {//这些构造方法和枚举对象是对应的
        //枚举当中构造方法默认是私有的
        this.ordinal = ordinal;
        this.color = color;
    }

    public static void main(String[] args) {
        //2> 枚举常用方法
        //通过数组进行返回
        TestEnum[] testEnums = TestEnum.values();//返回枚举数组
        for (int i = 0; i < testEnums.length; i++) {
            System.out.println(testEnums[i].ordinal());//返回索引值
        }
        System.out.println("=====");
        TestEnum v = TestEnum.valueOf("BLACK");//把普通字符转换为枚举实例
        System.out.println(v);
        System.out.println("=====");
        System.out.println(RED.compareTo(BLACK));//比较俩个枚举成员在定义时的顺序

    }

    }
}

        运行结果:

        2.3 枚举是不能反射的

        

注意: 

1> 枚举的构造方法是私有的

2> 枚举默认继承于Enum类

留一个问题: 用枚举来实现单例模式

3. Lambda表达式

        3.1 概念

        Lambda表达式是一种代替功能接口的一种表达式.它相当于用另一种更加简洁的方式来表示方法.(参数链表+ 使用参数列表的主体)

        基本语法:

        (parameters) -> expression或者(parameters)->{statements;}

        构成Lambda表达式的三个部分:

        1> paramaters: 相当于方法中的形参列表(可以省去类型,jvm会自己推断出来).当只有一个推断类型的时候可以省去"()".

        2> -> :这个是被应用于的意思

        3> 方法体: 相当于函数式接口里面方法的实现,也就是方法体,可以返回值也可以不反回值.

        我们来具体分析几个式子

            式子1: () -> 2 

            含义: 不需要参数,返回值为2

            式子2: x -> 2 * x

            含义: 接受一个参数(省去"()")并返回2倍的值

            式子3: (x,y) -> x + y

            含义: 接受俩个参数,并返回他们的和

            式子4: (int x,int y) -> x*y

            含义: 接受俩个int类型的整数,并返回他们的乘积

            式子5: (String s) -> System.out.print(s)

            含义: 接受一个String对象,并且再控制台里面打印,无返回值     

 

        3.2 函数式接口的概念

         在正式学习Lambda表达式之前,我们要先了解什么式函数式接口: 一个接口有且只有一个抽象方法.一般都会在这个接口上声明@FunctionInterface.

        举例:            

@FunctionalInterface
 interface NoParameterNoReturn{
    //函数式接口只有一个抽象方法
    void test();
//    default void test2() {
//        System.out.println("default默认方法可以有具体实现");
//    }
}

        3.3 Lambda表达式的基本使用

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

            前置功能性接口介绍

        接下来每一个函数式接口我都会用俩种方法来进行演示

                1. 无参数无返回值的函数式接口

                1> 使用匿名内部类重写test()方法

                2> 使用Lambda表达式重写test()方法

                2. 一个参数无返回值的函数式接口

                 1> 使用匿名内部类重写test()方法

                 2> 使用Lambda表达式重写test()方法

                3. 多个参数无返回值的函数式接口

                1> 使用匿名内部类重写test()方法

                2> 使用Lambda表达式重写test()方法

                4. 无参数有返回值的函数式接口

                1> 使用匿名内部类重写test()方法

                2> 使用Lambda表达式重写test()方法

                 5. 有一个参数有返回值的函数式接口

                1> 使用匿名内部类重写test()方法

                 2> 使用Lambda表达式重写test()方法

        

                6. 有返回值多参数的函数式接口

                1> 使用匿名内部类重写test()方法

                2> 使用Lambda表达式重写test()方法

        一些省略写法

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

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

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

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

        注意: 有一个概念叫做变量捕获: 只要被匿名内部类里面的函数所使用的变量就不能被修改lambda也如此.

        因此在匿名内部类里面所使用的变量:1. 要么是常量 2.要么是没有被修改过的变量

        例子:

        3.4 lambda在集合中的使用

                3.4.1 Collection接口

                       1> for-each的使用

                2> sort的使用

                3.4.2 Map接口

                1> for-each的使用

           

        整体代码:

package 反射_枚举_lambda表达式.lambda表达式;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

//无返回值无参数
@FunctionalInterface
interface NoParameterNoReturn1{
    //函数式接口只有一个抽象方法
    void test();
}
//无返回值一个参数
@FunctionalInterface
interface OneParameterNoReturn1 {
    void test(int a);
} //无返回值多个参数
@FunctionalInterface
interface MoreParameterNoReturn1 {
    void test(int a,int b);
} //有返回值无参数
@FunctionalInterface
interface NoParameterReturn1 {
    int test();
} //有返回值一个参数
@FunctionalInterface
interface OneParameterReturn1 {
    int test(int a);
} //有返回值多参数
@FunctionalInterface
interface MoreParameterReturn1 {
    int test(int a,int b);
}
public class Test1 {
    public static void main(String[] args) {
        HashMap<Integer,String> map = new HashMap<>();
        map.put(1,"hello");
        map.put(2,"xiaobai");
        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 + "value: " + s);
            }
        });
        //使用Lambda表达式
        map.forEach((key,val)-> System.out.println("key: " + key + "value: " +val));
    }
    public static void main8(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("hello");
        list.add("xiaobai");
        list.add("hello");
        list.add("lambda");
        //使用匿名内部类
//        list.sort(new Comparator<String>() {
//            @Override
//            public int compare(String o1, String o2) {
//                return o1.compareTo(o2);
//            }
//        });
//        System.out.println(list);
        //使用Lambda表达式
        list.sort((o1,o2)->o1.compareTo(o2));
        list.forEach((s)-> System.out.println(s));
    }
    public static void main7(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("hello");
        list.add("xiaobai");
        list.add("hello");
        list.add("lambda");
        //Collection中 for-each的使用
        //使用匿名内部类的写法
        list.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s + " ");
            }
        });
        //使用Lambda表达式
        list.forEach((s)-> System.out.println(s));//用Lambda后真的简洁
    }
    public static void main6(String[] args) {
        //TODO 有返回值多参数的函数式接口
        MoreParameterReturn1 moreParameterReturn1 = new MoreParameterReturn1() {
            @Override
            public int test(int a, int b) {
                System.out.println("我是有返回值多参数的,使用匿名内部类重写的test()方法");
                return a+b;
            }
        };
       int a = moreParameterReturn1.test(1,2);
        System.out.println(a);
        MoreParameterReturn1 moreParameterReturn11 = (b,c) -> {
            System.out.println("我是有返回值多参数的,使用Lambda表达式重写的test()方法");
            return b*c;
        };
        int b = moreParameterReturn11.test(3,4);
        System.out.println(b);
    }
    public static void main5(String[] args) {
        OneParameterReturn1 oneParameterReturn1 = new OneParameterReturn1() {
            @Override
            public int test(int a) {
                System.out.println("我是有一个参数有返回值的,使用匿名内部类重写的test()方法");
                return a+1;
            }
        };
        System.out.println(oneParameterReturn1.test(2));


        OneParameterReturn1 oneParameterNoReturn11 = (x) -> {
            System.out.println("我是有一个参数有返回值的,使用Lambda表达式重写的test()方法");
            return x+2;
        };
        int b = oneParameterNoReturn11.test(22);
        System.out.println(b);
    }

    public static void main4(String[] args) {
        //TODO 无参数有返回值的函数式接口
        NoParameterReturn1 noParameterReturn1 = new NoParameterReturn1() {
            @Override
            public int test() {
                System.out.println("我是有返回值无参数的,使用匿名内部类重写的test()方法");
                return 89;
            }
        };
        int a = noParameterReturn1.test();
        System.out.println(a);
        NoParameterReturn1 noParameterReturn11 = ()->{
            System.out.println("我是有返回值无参数的,使用Lambda表达式重写的test()方法");
          return 23;
        };
        int b = noParameterReturn11.test();
        System.out.println(b);

    }
    public static void main3(String[] args) {
        //TODO 多个参数无返回值的函数式接口

        MoreParameterNoReturn moreParameterNoReturn1 = (a,b)->{
            System.out.println(a + " " + b +"我是多个参数没有返回值,使用Lambda表达式重写的test()方法");
        };
        moreParameterNoReturn1.test(4,6);
    }
    public static void main2(String[] args) {
        //TODO 一个参数无返回值的函数式接口
        //1> 匿名函数内部类形式
        OneParameterNoReturn1 oneParameterNoReturn1 = new OneParameterNoReturn1() {
            @Override
            public void test(int a) {
                System.out.println("我是一个参数没有返回值,使用匿名内部类重写的test()方法 "+ a);
            }
        };
        oneParameterNoReturn1.test(23);
        //2> 使用Lambda表达式
        OneParameterNoReturn1 oneParameterNoReturn11 = (a) ->{
            System.out.println("我是一个参数没有返回值,使用Lambda表达式重写的test()方法 "+ a);
        };
        oneParameterNoReturn11.test(14);
    }
    public static void main1(String[] args) {
        //TODO 无参数无返回值的函数式接口
        //1> 匿名内部类形式
        NoParameterNoReturn1 noParameterNoReturn1 = new NoParameterNoReturn1() {
            @Override
            public void test() {
                System.out.println("我是没有参数无返回值,用匿名内部类重写的test()方法");
            }
        };
        noParameterNoReturn1.test();
        //2> Lambda表达式形式
        NoParameterNoReturn1 noParameterNoReturn11 = () -> {
            System.out.println("我是没有参数无返回值,使用Lambda表达式重写的test()方法");
        };
        noParameterNoReturn11.test();
    }
}

        总结:        

                Lambda表达式最大的优点就是代码会变的非常的简洁,但是于此同时的缺点就是代码可读性差,补容易进行调试.


网站公告

今日签到

点亮在社区的每一天
去签到