一,反射
1.1 概念
在程序运行期间,对于任何一个类都可以动态的创建该类对象,对于任何一个对象都可以动态调用它的属性和方法,可以了解到任何对象所属的类以及这个类中的成员。这种动态创建对象,调用属性和方法以及动态获取该类信息的行为就称为反射
1.2 获取字节码文件对象
要通过反射动态的创建对象、调用属性和方法必须先了解一个类的内部结构。
要了解一个类的内部结构必须获取这个类的字节码文件对象
在Java中将java文件编译后得到的class文件进行了封装形成一种数据类型,叫
Class
类,这个类的对象就是字节码文件对象。注:同一个字节码文件的对象是唯一的
获取方式:
// 1.通过对象的getClass()获取 User u = new User(); Class c1 = u.getClass(); System.out.println(c1); // 2.通过类的静态属性class获取 Class c2 = User.class; System.out.println(c2); // 3.通过Class类的静态方法forName(String s)获取 Class c3 = Class.forName("com.qf.test1.User"); System.out.println(c3); 注:如果类的全类名不存在会发生NoClassDefFoundError
1.3 获取构造函数并创建对象
获取构造函数的方法:
getConstructors():获取指定类中所有公共的构造器对象组成的数组
getDeclaredConstructors():获取指定类中所有构造器对象组成的数组
getConstructor(Class...c):根据指定类型的参数获取指定的公共的构造器对象
getDeclaredConstructor(Class...c):根据指定类型的参数获取指定的构造器对象
注:
如果指定类型参数的构造函数不存在会发生
NoSuchMethodException
没有这个方法异常参数类型不存在自动装拆箱
使用构造器对象创建指定类型的对象:newInstance(Object...o)
注:
如果创建指定类型对象时参数列表与构造器对象的参数列表不匹配时,会发生
IllegalArgumentException
非法的参数异常如果使用私有构造函数创建对象会发生
IllegalAccessException
非法访问异常,可以通过构造器对象调用setAccessible(true)
来设置该构造函数是可访问的(暴力反射)
补充:字节码文件对象也有
newInstance()
方法可以创建指定类的对象,但是只能通过无参构造函数来创建对象Class c = Class.forName("com.qf.test2.Stu"); Object o = c.newInstance(); System.out.println(o);
注:如果这个类的无参构造函数不存在会发生
NoSuchMethodException
和InstantiationException
1.4 获取成员变量并赋值
获取成员变量:
getFields():获取公共成员变量对象组成的数组
getDeclaredFields():获取所有成员变量对象组成的数组
getField(String name):获取公共的指定变量名的成员变量对象
getDeclaredField(String name):获取指定变量名的成员变量对象
使用成员变量对象为指定的成员变量赋值:
1.5 获取成员方法并调用
二,函数式编程
概念:在数学中,函数是一个问题的解决方案,这个方案的重点拿什么东西就能做什么事情
对于面向对象的思想来说,我们必须明确要由哪一个类来完成这件事,但是我们的目标不是去明确哪一个类也不是去创建这个类的对象,而是完成这件事。
函数式编程思想与面向对象思想的比较:
new Thread(new Runnable(){ public void run(){ //... } }).start();
new Thread(()->{ //... }).start();
Lambda
使用前提:
必须要有一个方法,方法的参数中要有接口
该接口必须是一个函数式接口(有且仅有一个抽象方法的接口)
注:使用@FunctionalInterface可以校验该接口是否是函数式接口
语法格式:
(形参参数列表)->{重写方法的方法体}
格式说明:
()
:表示实现类实现接口后重写抽象方法的参数列表(形参)
->
:表示将()
中形参所接收到的数据传递给重写的方法体中
{}
:表示重写方法的方法体Lambda表达式中可以省略的部分:
参数列表中的类型可以省略
如果参数列表中的参数只有一个,那么
()
也可以省略,此时参数的类型必须省略如果重写的方法体中只有一条语句
重写的方法没有返回值,
{}
也可以省略,此时该语句的;
必须省略重写的方法有返回值,
{}
也可以省略,此时return
和;
必须省略
常见的函数式接口:
Comsumer<T>:消费型接口
void accept(T t):是一个用于操作指定类型数据的方法
Predicate<T>
boolean test(T t):是一个用于针对指定类型的数据进行判断的方法
Supplier<T>:生产型接口
T get():是一个用于获取指定类型结果的方法
Function<T,R>
R apply(T t):是一个用于将指定类型的数据转换成另一种指定类型数据的方法,根据指定类型的参数获取指定类型的返回
三,Stream流
Stream流是根据Lambda表达式引入的一个新概念,专门用于解决操作集合时的弊端
集合中最常见的操作就是遍历,要遍历集合就需要通过循环来实现,而循环并不是遍历集合的目的只是一种手段,所以我们可以通过Stream流来简化集合的遍历操作。
获取Stream流对象:
Collection及其子类可以通过
对象名.stream()
来获取流对象数组可以通过Stream的静态方法
of()
来获取流对象Stream流的常用方法:
延迟方法:返回值是Stream对象的方法,可以使用链式写法
终结方法:返回值不是Stream对象的方法
void forEach(Consumer con):终结方法,用于遍历流中的每个元素
Stream filter(Predicate pre):延迟方法:用于对流中的每个元素进行过滤
long count():终结方法,返回流中元素的个数
Stream limit(long n):延迟方法,获取流中的前n个元素
Stream skip(long n):延迟方法,跳过流中的前几个元素
Stream map(Function f):延迟方法,用于将一个指定类型的流对象转换成另一种类型的流对象