反射、枚举、lambda

再谈String

字符串常量池在JVM中是StringTable类,实际是一个固定大小的HashTable(一种高效用来进行查找的数据结构

  • 字符串常量池
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

反射

  • 定义

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

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

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

  • 反射相关的类
    在这里插入图片描述

  • 常用获得类相关的方法
    在这里插入图片描述

  • 常用获得类中属性相关的方法(以下方法返回值为Field相关)
    在这里插入图片描述

  • 获得类中注解相关的方法
    在这里插入图片描述

  • 获得类中构造器相关的方法(以下方法返回值为Constructor相关)
    在这里插入图片描述

  • 获得类中方法相关的方法(以下方法返回值为Method相关)
    在这里插入图片描述

获得类对象的3种方式

1.使用 Class.forName(“类的全路径名”); 静态方法。前提:已明确类的全路径

2.使用 .class 方法。说明:仅适合在编译前就已经明确要操作的 Class

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

class Student{
    //私有属性name
    private String name = "zhangsan";
    //公有属性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 +
                '}';
    }
}

public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> c1 = Class.forName("Student");

        Class<?> c2 = Student.class;

        Student student = new Student();
        Class<?> c3 = student.getClass();

        System.out.println(c1==c2);//true
        System.out.println(c3==c2);//true
        System.out.println(c1==c3);//true
        //得出结论:一个类只能拥有一个class对象
    }
}

在这里插入图片描述

public class ReflectClassDemo {
    public static void reflectNewInstance() {
        try {
            Class<?> c1 = Class.forName("Student");
            Student student = (Student) c1.newInstance();
            System.out.println(student);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }

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

在这里插入图片描述
运行结果:因为调用的是无参构造方法,所以name和age都是默认的
在这里插入图片描述
那么如果要调用私有的有参构造函数也是可以的,这就是反射

public static void reflectPrivateConstructor() {
        try {
            Class<?> c1 = Class.forName("Student");
            Constructor<?> constructor =  c1.getDeclaredConstructor(String.class, int.class);

            constructor.setAccessible(true);//因为是私有的使用,需要程序猿确认

            Student student = (Student)constructor.newInstance("xiaoming",19);
            System.out.println(student);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

在这里插入图片描述
运行结果:调用了私有的有参构造函数
在这里插入图片描述
也可以获取到私有成员变量

public static void reflectPrivateField() {
        try {
            Class<?> c1 = Class.forName("Student");
            Field field = c1.getDeclaredField("name");

            field.setAccessible(true);

            Student student = (Student)c1.newInstance();
            field.set(student,"lisi");

            System.out.println(student);
        } catch (ClassNotFoundException | NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }

在这里插入图片描述
运行结果:
在这里插入图片描述
也可以对私有的方法进行调用

public static void reflectPrivateMethod() {
        try {
            Class<?> c1 = Class.forName("Student");
            Method method = c1.getDeclaredMethod("function", String.class);
            //这里的function是私有方法的名字,String是成员变量

            method.setAccessible(true);

            Student student = (Student)c1.newInstance();
            method.invoke(student,"调用私有方法");
            
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

在这里插入图片描述
运行结果:
在这里插入图片描述

  • 反射的优缺点

优点:1. 对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意 一个方法
2. 增加程序的灵活性和扩展性,降低耦合性,提高自适应能力
3. 反射已经运用在了很多流行框架如:Struts、Hibernate、Spring 等等

缺点:1. 使用反射会有效率问题。会导致程序效率降低

  1. 反射技术绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂

枚举

  • Enum 类的常用方法
方法描述
values()以数组形式返回枚举类型的所有成员
ordinal()获取枚举成员的索引位置
valueOf()将普通字符串转换为枚举实例
compareTo()比较两个枚举成员在定义时的顺序
public enum TestEnum {
    RED,BLACK,GREEN;//0 1 2

    public static void main(String[] args) {
        TestEnum[] testEnums = TestEnum.values();
        for (int i = 0; i < testEnums.length; i++) {
            System.out.println(testEnums[i] + " 索引: " + testEnums[i].ordinal());
        }
        TestEnum testEnum = TestEnum.valueOf("GREEN");
        System.out.println(testEnum);

        System.out.println(RED.compareTo(GREEN));
    }
}

其中,values方法在枚举类没有,因为我们自己写的枚举类都默认继承枚举类Enum,而这个Enum类是抽象类
在这里插入图片描述

枚举和反射的结合
public enum TestEnum {
    RED(1,"red"),
    BLACK(2,"black"),
    GREEN(3,"green");

    private String color;
    private int ordinal;

    TestEnum(int ordinal,String color) {
        this.color = color;
        this.ordinal = ordinal;
    }
}

在这里插入图片描述
TestEnum不能是public的,默认是private的,因此可以省略private
由于是私有的,那么我们要访问这个私有的构造方法就需要用到反射

import java.lang.reflect.Constructor;

public class TestReflectEnum {
    public static void reflectPrivateConstructor() {
        try {
            Class<?> classStudent = Class.forName("testenmu.TestEnum");//包里面的路径

            Constructor<?>[] constructors = classStudent.getDeclaredConstructors();
            for (int i = 0; i < constructors.length; i++) {
                System.out.println("构造方法打印:"+constructors[i]);
            }
            //注意传入对应的参数,获得对应的构造方法来构造对象,当前枚举类是提供了两个参数分别是String和int。
            Constructor<?> declaredConstructorStudent =
                    classStudent.getDeclaredConstructor(String.class,int.class,
                            int.class,String.class);//这里这样写下面做出了解释

            //设置为true后可修改访问权限
            declaredConstructorStudent.setAccessible(true);

            Object objectStudent = declaredConstructorStudent.newInstance("父类参数",111,666,"绿色");
            TestEnum testEnum = (TestEnum) objectStudent;

            System.out.println("获得枚举的私有构造函数:"+testEnum);

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

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

Constructor<?> declaredConstructorStudent =
classStudent.getDeclaredConstructor(String.class,int.class,
int.class,String.class);

这样写是因为我们自己写的枚举类默认继承了Enum类,Enum又是一个抽象类
在这里插入图片描述
所以我们在写自己的构造方法时,一个类继承了另外一个类,要帮助这个类进行初始化,这里需要传4个参数来反射过去,参数列表先写父的再写子的

可是运行结果还是报错,不过这个报错是正确的:无法创建枚举对象
在这里插入图片描述
在这里插入图片描述
如果是枚举就抛异常,说明枚举通过反射在类外根本拿不到枚举对象,所以枚举是非常安全的!

lambda

函数式接口

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

注意:

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

定义方式:

@FunctionalInterface
interface NoParameterNoReturn {
//注意:只能有一个抽象方法
	void test();
    //也可以有这些方法:
    /* static void test2() {
    }
    default void test3() {
    }*/
}

如果我们正常的调用NoParameterNoReturn里面的test方法:

public static void main1(String[] args) {
    NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn(){
        @Override
        public void test() {
            System.out.println("hello");
        }
    };
    noParameterNoReturn.test();
}

如果我们用到lambda表达式的话:

public static void main2(String[] args) {
    NoParameterNoReturn noParameterNoReturn = ()->System.out.println("hello");
    noParameterNoReturn.test();
}
  • 用法都总结为:
  1. 参数类型可以省略,如果需要省略,每个参数的类型都要省略。
  2. 参数的小括号里面只有一个参数,那么小括号可以省略
  3. 如果方法体当中只有一句代码,那么大括号可以省略
  4. 如果方法体中只有一条语句,且是return语句,那么大括号可以省略,且去掉return关键字
变量捕获

Lambda 表达式中存在变量捕获 ,了解了变量捕获之后,我们才能更好的理解Lambda 表达式的作用域 。Java当中的匿名类中,会存在变量捕获
在这里插入图片描述
在上述代码当中的变量a就是,捕获的变量。这个变量要么是被final修饰,如果不是被final修饰的 你要保证在使用之前,没有修改。

  • Lambda在集合当中的使用
    在这里插入图片描述
    注意:Collection的forEach()方法是从接口 java.lang.Iterable 拿过来的

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

  1. 代码简洁,开发迅速
  2. 方便函数式编程
  3. 非常容易进行并行计算
  4. Java 引入 Lambda,改善了集合操作
    缺点:
  5. 代码可读性变差
  6. 在非并行计算中,很多计算未必有传统的 for 性能要高
  7. 不容易进行调试
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值