反射,枚举,lambda表达式

目录


反射

作用:在Java代码中,让一个对象认识到自己

比如一个类的名字,里面的方法,属性等

让程序运行的过程,某个对象也能获取到上述信息

Java提供了一组 API,通过这些 API 拿到指定对象的上述信息

注:API全称Application Programming Interface,它是一组方法和类,提供给别人使用的,例如Scanner,Queue,List

如:

像这样得到String的全部信息


获取用户输入的想要的类型(动态的过程)

newInstance:获取实例

forName:获取对象

反射的作用:

把序列化的字符串还原成对象(反序列化)

序列化:把一个对象转成一个字符串

获取到类的某个属性(Field):

获取到某个方法(Method):

如果print1有两个方法(重写),具体调用哪个方法也是可以明确使用的

例如有一个print(String x)

如果调用上面的方法,可以用

Method pm=studentClass.getDeclaredMethod("print",String.class);

通过后续的参数,区分出当前要获取的print是哪个版本,后续参数就表示'获取方法的参数列表'

完整访问:

java 复制代码
class Student{
    private int id=1;
    private String name;
    public void print(){
        System.out.println("id:"+id+"name:"+name);
    }
    public void print(String p){
        System.out.println(p+" id:"+id+" name:"+name);
    }
}
public class Test2 {
    public void test1() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        Class studentClass=Class.forName("Student");
        Field idField=studentClass.getDeclaredField("id");

        //创建一个对象
        Student student=new Student();
        //由于student的id是私有的,将它取出,相当于"开锁"
        idField.setAccessible(true);
        //设置里面的值
        idField.setInt(student,100);
        int id=idField.getInt(student);//获取值
        System.out.println(id);
    }
    public void test2() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        //通过反射调用 Student 方法
        Class studentClass=Student.class;

        //拿到方法对象
        Method pm=studentClass.getDeclaredMethod("print",String.class);
        Student student=new Student();//随便创建一个对象
        pm.setAccessible(true);
        pm.invoke(student,"hello,");//调用方法
    }
    public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
        Test2 test=new Test2();
        test.test2();//结果:hello, id:1 name:null
    }
}

通过反射,能随意访问任意对象的任意属性
反射的缺点:大大降低了效率,绕过了源代码,提高了维护成本,不到万不得已不要使用

枚举的使用

java 复制代码
public enum Gender {
    //每个枚举项通常用大写字母来表示,本质上是一组常量
    //这几个常量类型都是Gender,但值不同
    //常量使用都好分割
    
    MALE,FEMALE,OTHER
}

在main方法中使用:

此外,枚举中还能指定属性和方法(枚举和类很相似)

枚举和类的区别 :

类可以随意在外面创建实例,但枚举不行,

枚举项的创建必须在 枚举的内部进行(构造方法必须是私有的)

java 复制代码
Color c=Color.RED;
        System.out.println(c.getName());
        //结果:红色

枚举不可继承,无法扩展

问:能否通过反射,拿到私有的构造方法,然后在枚举外面创建出新的枚举项?

答:不能,Java的反射 API 中,针对枚举值特殊情况做了特殊处理

Lambda表达式

函数式接口

由于Java没有其他编程语言拥有的匿名函数(没有名字的函数,用完一次就扔了,通常作为"回调函数"用,类似于Comparetor,Comparable)

Java引入"函数式接口" 来引申出匿名函数=>这就是Lambda 表达式

如果一个接口里面只有一个抽象方法,就可以称为函数式接口

语法

Lambda表达式语法精简

  1. 参数类型可以省略,如果需要省略每个参数的类型都要省略
  2. 参数的**⼩括号⾥⾯只有⼀个参数,那么⼩括号可以省略**
  3. 如果⽅法体当中只有⼀句代码,那么⼤括号可以省略
  4. 如果⽅法体中只有⼀条语句,且是return语句 ,那么**⼤括号可以省略** ,且去掉return关键字

1.比如优先级队列调用Comparator

正常用匿名内部类写法如下:

java 复制代码
public class Test5 {
    class Student{
        public String name;
        public int id;
    }

    public static void main(String[] args) {
        PriorityQueue<Student> priorityQueue=new PriorityQueue<>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return o1.id-o2.id;
            }
        });
    }

而使用Lambda表达式更简单:

java 复制代码
public static void main(String[] args) {
        PriorityQueue<Student> priorityQueue=new PriorityQueue<>((o1, o2) -> o1.id-o2.id);
    }

(o1,o2)里面表示形参列表,形参的参数类型是可以省略的
-> 是Lambda的关键标志,一看到这个箭头,就知道是Lambda
o1.id-o2.id 是lambda的函数体(如果 lambda 里只有一句return代码,{ } 就可以省略,return也能省略)

2.例如使用函数式接口:

java 复制代码
interface MyInterface{//函数式接口
    void print(String s);
}

public class Test6 {
    //用lambda表达式
    MyInterface myInterface=(s)-> System.out.println(s);
    
    //不用lambda表达式
    MyInterface myInterface1=new MyInterface() {
        @Override
        public void print(String s) {
            System.out.println(s);
        }
    };
}

可见lambda表达式能大量减少代码量

3.把 lambda 表达式的内容作为另一个方法的实参(前提是 该形参的类型必须是函数式接口):

java 复制代码
interface MyComparator{
    int compare(String s1,String s2);
}
class MyPriorityQueue{
    public MyPriorityQueue(MyComparator comparator){//函数式接口作为形参

    }
}
public class Test7 {
    public static void main(String[] args) {
        MyPriorityQueue queue=new MyPriorityQueue((s1,s2)->s1.compareTo(s2));
    }
}

第四个例子用的比较少,可以了解

lambda表达式最主要的功能就是省略,写起来简便

变量捕获

在 Lambda 表达式里捕获的变量必须是 final 或者是事实上的 final(即一旦赋值就不会再改变)

lambda 表达式能够捕获外部的变量,在内部进行使用

lambda的变量捕获,能把创建后销毁掉的变量再拿出来(这是很可怕的一件事)
相当于上头断了线,下面还在执行

Lambda 能够使用捕获的变量的原因:在定义 lambda 的时候就把捕获的变量在 lambda 内部拷贝(地址不同)了一份

所以我们不能让捕获到的 num(变量) 之后还能继续修改 ,否则拷贝的和外面的num会出现歧义,

因此,规定捕获到的变量必须是final的或没有进行修改的

Lambda在集合List中的使用

java 复制代码
public class Test8 {
    public static void main(String[] args) {
        List<Integer> list=new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        for (int i=0;i<list.size();i++){//正常写法
            System.out.print(list.get(i)*2+" ");
        }

        //用lambda表达式写
        list.forEach(i -> System.out.print(i*2+" "));
    }
}
相关推荐
西瓜本瓜@2 小时前
在Android中如何使用Protobuf上传协议
android·java·开发语言·git·学习·android-studio
言之。2 小时前
别学了,打会王者吧
java·python·mysql·容器·spark·php·html5
机智的人猿泰山2 小时前
java kafka
java·开发语言·kafka
Algorithm15763 小时前
谈谈接口和抽象类有什么区别?
java·开发语言
细心的莽夫3 小时前
SpringCloud 微服务复习笔记
java·spring boot·笔记·后端·spring·spring cloud·微服务
264玫瑰资源库5 小时前
问道数码兽 怀旧剧情回合手游源码搭建教程(反查重优化版)
java·开发语言·前端·游戏
pwzs5 小时前
Java 中 String 转 Integer 的方法与底层原理详解
java·后端·基础
东阳马生架构5 小时前
Nacos简介—2.Nacos的原理简介
java
普if加的帕5 小时前
java Springboot使用扣子Coze实现实时音频对话智能客服
java·开发语言·人工智能·spring boot·实时音视频·智能客服
爱喝一杯白开水5 小时前
SpringMVC从入门到上手-全面讲解SpringMVC的使用.
java·spring·springmvc