反射,枚举,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+" "));
    }
}
相关推荐
Full Stack Developme5 小时前
java.nio 包详解
java·python·nio
零千叶5 小时前
【面试】Java JVM 调优面试手册
java·开发语言·jvm
代码充电宝5 小时前
LeetCode 算法题【简单】290. 单词规律
java·算法·leetcode·职场和发展·哈希表
li3714908906 小时前
nginx报400bad request 请求头过大异常处理
java·运维·nginx
摇滚侠6 小时前
Spring Boot 项目, idea 控制台日志设置彩色
java·spring boot·intellij-idea
Aevget6 小时前
「Java EE开发指南」用MyEclipse开发的EJB开发工具(二)
java·ide·java-ee·eclipse·myeclipse
黄昏晓x6 小时前
C++----多态
java·jvm·c++
Brookty7 小时前
【算法】前缀和
java·学习·算法·前缀和·动态规划
少许极端7 小时前
算法奇妙屋(七)-字符串操作
java·开发语言·数据结构·算法·字符串操作
懒羊羊不懒@7 小时前
Java基础语法—字面量、变量详解、存储数据原理
java·开发语言