Java SE Day-21

反射

1 、反射的概念

Java程序中,所有的对象都有两种类型:编译时类型和运行时类型,而很多时候对象的编译时类型和运行时类型不一致。

平时我们照镜子的时候,在镜子后面会有自己的影子,其实java中的反射也是类似的,一个类或者对象通过反射可以获得自身的对象,该对象是一个java.lang.Class 的对象(就像一个镜像文件)。

因为加载完类之后,就产生了一个Class类型的对象,并将引用存储到方法区,那么每一个类在方法区内存都可以找到唯一Class对象与之对应,这个对象包含了完整的类的结构信息,我们可以通过这个对象获取类的结构。这种机制就像一面镜子,Class对象像是类在镜子中的镜像,通过观察这个镜像就可以知道类的结构,所以,把这种机制形象地称为反射机制。

非反射:类(原物)–>类信息

反射:Class对象(镜像)–>类(原物)

2 、java.lang.Class类

要想解剖一个类,必须先要获取到该类的Class对象。而剖析一个类或用反射解决具体的问题就是使用相关API(1)java.lang.Class(2)java.lang.reflect.*。所以,Class对象是反射的根源。

获取Class对象的四种方式

(1)类型名.class

要求编译期间已知类型

(2)对象.getClass()

获取对象的运行时类型

(3)Class.forName(类型全名称)

可以获取编译期间未知的类型

(4)ClassLoader的类加载器对象.loadClass(类型全名称)

可以用系统类加载对象或自定义加载器对象加载指定路径下的类型

import org.junit.Test;

public class GetClassObject {
    @Test
    public void test01() throws ClassNotFoundException{
        Class c1 = GetClassObject.class;
        GetClassObject obj = new GetClassObject();
        Class c2 = obj.getClass();
        Class c3 = Class.forName("com.classtype.GetClassObject");
        Class c4 = ClassLoader.getSystemClassLoader().loadClass("com.classtype.GetClassObject");

        System.out.println("c1 = " + c1);
        System.out.println("c2 = " + c2);
        System.out.println("c3 = " + c3);
        System.out.println("c4 = " + c4);

        System.out.println(c1 == c2);
        System.out.println(c1 == c3);
        System.out.println(c1 == c4);
    }

    @Test
    public void test02(){
        int[] arr1 = {1,2,3,4,5};
        int[] arr2 = {10,34,5,66,34,22};
        int[][] arr3 = {{1,2,3,4},{4,5,6,7}};
        String[] arr4 = {"hello","world"};

        Class c1 = arr1.getClass();
        Class c2 = arr2.getClass();
        Class c3 = arr3.getClass();
        Class c4 = arr4.getClass();

        System.out.println("c1 = " + c1);
        System.out.println("c2 = " + c2);
        System.out.println("c3 = " + c3);
        System.out.println("c4 = " + c4);
        System.out.println(c1 == c2);
        System.out.println(c1 == c3);
        System.out.println(c1 == c4);
        System.out.println(c3 == c4);
    }
}


3 反射的基本应用

1 获取类型的详细信息

可以获取:包、修饰符、类型名、父类(包括泛型父类)、父接口(包括泛型父接口)、成员(属性、构造器、方法)、注解(类上的、方法上的、属性上的)

示例代码获取常规信息:

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class TestClassInfo {
    public static void main(String[] args) throws Exception {
        //1、先得到某个类型的Class对象
        Class clazz = String.class;
        //比喻clazz好比是镜子中的影子

        //2、获取类信息
        //(1)获取包对象,即所有java的包,都是Package的对象
        Package pkg = clazz.getPackage();
        System.out.println("包名:" + pkg.getName());

        //(2)获取修饰符
        //其实修饰符是Modifier,里面有很多常量值
        /*
         * 0x是十六进制
         * PUBLIC           = 0x00000001;  1    1
         * PRIVATE          = 0x00000002;  2	10
         * PROTECTED        = 0x00000004;  4	100
         * STATIC           = 0x00000008;  8	1000
         * FINAL            = 0x00000010;  16	10000
         * ...
         *
         * 设计的理念,就是用二进制的某一位是1,来代表一种修饰符,整个二进制中只有一位是1,其余都是0
         *
         * mod = 17          0x00000011
         * if ((mod & PUBLIC) != 0)  说明修饰符中有public
         * if ((mod & FINAL) != 0)   说明修饰符中有final
         */
        int mod = clazz.getModifiers();
        System.out.println("类的修饰符有:" + Modifier.toString(mod));

        //(3)类型名
        String name = clazz.getName();
        System.out.println("类名:" + name);

        //(4)父类,父类也有父类对应的Class对象
        Class superclass = clazz.getSuperclass();
        System.out.println("父类:" + superclass);

        //(5)父接口们
        System.out.println("父接口们:");
        Class[] interfaces = clazz.getInterfaces();
        for (Class iter : interfaces) {
            System.out.println(iter);
        }

        //(6)类的属性,  你声明的一个属性,它是Field的对象
/*		Field clazz.getField(name)  根据属性名获取一个属性对象,但是只能得到公共的
		Field[] clazz.getFields();  获取所有公共的属性
		Field clazz.getDeclaredField(name)  根据属性名获取一个属性对象,可以获取已声明的
		Field[] clazz.getDeclaredFields()	获取所有已声明的属性
		*/
//        Field valueField = clazz.getDeclaredField("value");
//		System.out.println("valueField = " +valueField);

        System.out.println("------------------------------");
        System.out.println("成员如下:");
        System.out.println("属性有:");
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field field : declaredFields) {
            //修饰符、数据类型、属性名
            int modifiers = field.getModifiers();
            System.out.println("属性的修饰符:" + Modifier.toString(modifiers));

            String name2 = field.getName();
            System.out.println("属性名:" + name2);

            Class<?> type = field.getType();
            System.out.println("属性的数据类型:" + type);
        }
        System.out.println("-------------------------");
        //(7)构造器们
        System.out.println("构造器列表:");
        Constructor[] constructors = clazz.getDeclaredConstructors();
        for (int i=0; i<constructors.length; i++) {
            Constructor constructor = constructors[i];
            System.out.println("第" + (i+1) +"个构造器:");
            //修饰符、构造器名称、构造器形参列表  、抛出异常列表
            int modifiers = constructor.getModifiers();
            System.out.println("构造器的修饰符:" + Modifier.toString(modifiers));

            String name2 = constructor.getName();
            System.out.println("构造器名:" + name2);

            //形参列表
            System.out.println("形参列表:");
            Class[] parameterTypes = constructor.getParameterTypes();
            for (Class parameterType : parameterTypes) {
                System.out.println(parameterType);
            }

            //异常列表
            System.out.println("异常列表:");
            Class<?>[] exceptionTypes = constructor.getExceptionTypes();
            for (Class<?> exceptionType : exceptionTypes) {
                System.out.println(exceptionType);
            }
            System.out.println();
        }
        System.out.println("---------------------------------");
        //(8)方法们
        System.out.println("方法列表:");
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (int i=0; i<declaredMethods.length; i++) {
            Method method = declaredMethods[i];
            System.out.println("第" + (i+1) +"个方法:");
            //修饰符、返回值类型、方法名、形参列表 、异常列表
            int modifiers = method.getModifiers();
            System.out.println("方法的修饰符:" + Modifier.toString(modifiers));

            Class<?> returnType = method.getReturnType();
            System.out.println("返回值类型:" + returnType);

            String name2 = method.getName();
            System.out.println("方法名:" + name2);

            //形参列表
            System.out.println("形参列表:");
            Class[] parameterTypes = method.getParameterTypes();
            for (Class parameterType : parameterTypes) {
                System.out.println(parameterType);
            }

            //异常列表
            System.out.println("异常列表:");
            Class<?>[] exceptionTypes = method.getExceptionTypes();
            for (Class<?> exceptionType : exceptionTypes) {
                System.out.println(exceptionType);
            }
            System.out.println();
        }

    }
}


2 创建任意引用类型的对象

两种方式:

1、直接通过Class对象来实例化(要求必须有公共的无参构造)

2、通过获取构造器对象来进行实例化

方式一的步骤:

(1)获取该类型的Class对象(2)创建对象

方式二的步骤:

(1)获取该类型的Class对象(2)获取构造器对象(3)创建对象

如果构造器的权限修饰符修饰的范围不可见,也可以调用setAccessible(true)

示例代码:

import org.junit.Test;

import java.lang.reflect.Constructor;

public class TestCreateObject {
    @Test
    public void test1() throws Exception{
//        ClassOne obj = new ClassOne();//编译期间无法创建

        Class<?> classOne = Class.forName("com.ext.demo.ClassOne");
        //classOne.ext.demo.ClassOne
        //classOne.newInstance()创建的就是ClassOne的对象
        Object obj = classOne.newInstance();
        System.out.println(obj);
    }

    @Test
    public void test2()throws Exception{
        Class<?> classOne = Class.forName("com.ext.demo.ClassOne");
        //java.lang.InstantiationException: com.ext.demo.ClassOne
        //Caused by: java.lang.NoSuchMethodException: com.ext.demo.ClassOne.<init>()
        //即说明ClassOne没有无参构造,就没有无参实例初始化方法<init>
        Object stu = classOne.newInstance();
        System.out.println(stu);
    }

    @Test
    public void test3()throws Exception{
        //(1)获取Class对象
        Class<?> classOne = Class.forName("com.ext.demo.ClassOne");
        /*
         * 获取ClassOne类型中的有参构造
         * 如果构造器有多个,我们通常是根据形参【类型】列表来获取指定的一个构造器的
         * 例如:public ClassOne(String title, int num)
         */
        //(2)获取构造器对象
        Constructor<?> constructor = classOne.getDeclaredConstructor(String.class,int.class);

        //(3)创建实例对象
        // T newInstance(Object... initargs)  这个Object...是在创建对象时,给有参构造的实参列表
        Object obj = constructor.newInstance("火柴人",2022);
        System.out.println(obj);
    }
}

3 操作任意类型的属性

(1)获取该类型的Class对象

Class classOne = Class.forName(“包.类名”);

(2)获取属性对象

Field field = classOne.getDeclaredField(“属性名”);

(3)如果属性的权限修饰符不是public,那么需要设置属性可访问

field.setAccessible(true);

(4)创建实例对象:如果操作的是非静态属性,需要创建实例对象

Object obj = classOne.newInstance(); //有公共的无参构造

Object obj = 构造器对象.newInstance(实参…);//通过特定构造器对象创建实例对象

(4)设置属性值

field.set(obj,“属性值”);

如果操作静态变量,那么实例对象可以省略,用null表示

(5)获取属性值

Object value = field.get(obj);

如果操作静态变量,那么实例对象可以省略,用null表示

示例代码:

public class Student {
    private int id;
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}
import java.lang.reflect.Field;

public class TestField {
    public static void main(String[] args)throws Exception {
        //1、获取Student的Class对象
        Class classOne = Class.forName("com.reflect.Student");

        //2、获取属性对象,例如:id属性
        Field idField = classOne.getDeclaredField("id");

        //3、如果id是私有的等在当前类中不可访问access的,我们需要做如下操作
        idField.setAccessible(true);

        //4、创建实例对象,即,创建Student对象
        Object stu = classOne.newInstance();

        //5、获取属性值
        /*
         * 以前:int 变量= 学生对象.getId()
         * 现在:Object id属性对象.get(学生对象)
         */
        Object value = idField.get(stu);
        System.out.println("id = "+ value);

        //6、设置属性值
        /*
         * 以前:学生对象.setId(值)
         * 现在:id属性对象.set(学生对象,值)
         */
        idField.set(stu, 2);
        value = idField.get(stu);
        System.out.println("id = "+ value);
    }
}

4 调用任意类型的方法

(1)获取该类型的Class对象

Class clazz = Class.forName(“包.类名”);

(2)获取方法对象

Method method = clazz.getDeclaredMethod(“方法名”,方法的形参类型列表);

(3)创建实例对象

Object obj = classOne.newInstance();

(4)调用方法

Object result = method.invoke(obj, 方法的实参值列表);

如果方法的权限修饰符修饰的范围不可见,也可以调用setAccessible(true)

如果方法是静态方法,实例对象也可以省略,用null代替

示例代码:

import org.junit.Test;

import java.lang.reflect.Method;

public class TestMethod {
    @Test
    public void test()throws Exception {
        // 1、获取Student的Class对象
        Class<?> classOne = Class.forName("com.reflect.Student");

        //2、获取方法对象
        /*
         * 在一个类中,唯一定位到一个方法,需要:(1)方法名(2)形参列表,因为方法可能重载
         *
         * 例如:void setName(String name)
         */
        Method setNameMethod = classOne.getDeclaredMethod("setName", String.class);

        //3、创建实例对象
        Object stu = classOne.newInstance();

        //4、调用方法
        /*
         * 以前:学生对象.setName(值)
         * 现在:方法对象.invoke(学生对象,值)
         */
        Object setNameMethodReturnValue = setNameMethod.invoke(stu, "张三");

        System.out.println("stu = " + stu);
        //setName方法返回值类型void,没有返回值,所以setNameMethodReturnValue为null
        System.out.println("setNameMethodReturnValue = " + setNameMethodReturnValue);

        Method getNameMethod = clazz.getDeclaredMethod("getName");
        Object getNameMethodReturnValue = getNameMethod.invoke(stu);
        //getName方法返回值类型String,有返回值,getNameMethod.invoke的返回值就是getName方法的返回值
        System.out.println("getNameMethodReturnValue = " + getNameMethodReturnValue);//张三
    }

    @Test
    public void test02()throws Exception{
        Class<?> classOne = Class.forName("com.ext.demo.ClassOne");
        Method printInfoMethod = classOne.getMethod("printInfo", String.class);
        //printInfo方法是静态方法
        printInfoMethod.invoke(null,"火柴人");
    }
}

小结:反射的使用,可以让我们在原本获取不到私有类的情况下,开启后门让我们获取,因为是镜像类所以类有的属性方法……镜像类都拥有,且我们获取使用时值得注意的就是需要我们进行反向使用,因为反射是建立在镜像上的。此外我们需要掌握获取类的四种方法,以及获取后的常用方法比如如何获取属性,方法。


Lambda表达式

1 Lambda表达式语法

Lambda表达式是用来给【函数式接口】的变量或形参赋值用的。其实本质上,Lambda表达式是用于实现【函数式接口】的“抽象方法”,或者是给函数式接口的变量传递一段实现抽象方法的方法体代码。

Lambda表达式语法格式

(形参列表) -> {Lambda}

语法格式说明:

  • (形参列表)它就是你要赋值的函数式接口的抽象方法的(形参列表),照抄
  • {Lambda体}就是实现这个抽象方法的方法体
  • ->称为Lambda操作符(减号和大于号中间不能有空格,而且必须是英文状态下半角输入方式)

2 使用Lamda表达式标准格式

import com.atguigu.fi.Calculator;

public class LambdaGrammar {
    public static void main(String[] args) {
        Calculator<Integer,Integer> c1 = (Integer a, Integer b) -> {return a+b;};
        Calculator<Integer,Integer> c2 = (Integer a, Integer b) -> {return a-b;};
        Calculator<Integer,Integer> c3 = (Integer a, Integer b) -> {return a*b;};
        Calculator<Integer,Integer> c4 = (Integer a, Integer b) -> {return a/b;};

        System.out.println(c1.calculate(5, 2));
        System.out.println(c2.calculate(5, 2));
        System.out.println(c3.calculate(5, 2));
        System.out.println(c4.calculate(5, 2));
    }
}

3 Lambda表达式的简化

某些情况下Lambda表达式可以精简:

  • 当{Lambda体}中只有一句语句时,可以省略{}和{;}
  • 当{Lambda体}中只有一句语句时,并且这个语句还是一个return语句,那么{、return、;}三者可以省略。它们三要么一起省略,要么都不省略。
  • 当Lambda表达式(形参列表)的类型已知,获取根据泛型规则可以自动推断,那么(形参列表)的数据类型可以省略。
  • 当Lambda表达式(形参列表)的形参个数只有一个,并且类型已知或可以自动推断,则形参的数据类型和()可以一起省略,但是形参名不能省略。
  • 当Lambda表达式(形参列表)是空参时,()不能省略

示例代码:

import com.atguigu.fi.Calculator;
import com.atguigu.fi.Convertor;
import org.junit.Test;

public class LambdaGrammarSimple {
    @Test
    public void test01() {
        //使用Lambda表达式实现Calculator接口,求两个整数的和的功能
        Calculator<Integer,Integer> c1 = (Integer a, Integer b) -> {return a+b;};
        System.out.println(c1.calculate(5, 2));

        Calculator<Integer,Integer> c2 = (Integer a, Integer b) ->  a+b;
        System.out.println(c2.calculate(5, 2));

        Calculator<Integer,Integer> c3 = (a,  b) -> a+b;
        System.out.println(c3.calculate(5, 2));
        //上面三种写法完全等价
    }

    @Test
    public void test02() {
        //使用Lambda表达式实现Convertor接口,实现取字符串的首字母的功能
        Convertor<String,Character> c1 = (String str)-> {return str.charAt(0);};
        System.out.println(c1.change("hello"));

        Convertor<String,Character> c2 = (String str)-> str.charAt(0);
        System.out.println(c2.change("world"));

        Convertor<String,Character> c3 = (str)-> str.charAt(0);
        System.out.println(c3.change("atguigu"));

        Convertor<String,Character> c4 = str-> str.charAt(0);
        System.out.println(c4.change("chai"));
        //上面四种写法完全一致
    }

}

小结:Lambda表达式类似于箭头是函数编程思想的常客,用法简单,可以减少冗余的代码。


总结:反射可以使我们获取到私有属性等等,是开启后门的一种方法。因为是类的镜像所以指向的方法区是同一个,所以是可以获取该类的所有方法属性……Lambda表达式则是函数编程里的重要代码,他可以让我们使用简洁的代码,表达完整的信息。

每日金句:

甘谷与共,是浮生茶,也是人生路。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值