一 Lambda表达式
Lambda表达式整体代表一个接口实现类对象,内容是接口方法的具体参数和内容。
1、函数式编程思想概述
Lambda表达式和匿名内部类作用相同,都是当作除了主方法外另一个方法在主方法内调用时作为接口类参数的实现类对象,只是背景是有一个接口,测试类里面有一个方法的参数是接口,在主方法里面调用该方法时参数需要有实现类对象填入,我们就需要构造刚才接口的实现类,在实现类里面重写接口的方法,并给出方法的具体内容,然后在主方法中创建对象。这样很麻烦,所以使用匿名内部类和Lambda表达式替代。而Lambda表达式中参数即为接口内方法的参数,在代码块那里填补接口类方法的具体内容。Lambda表达式里面的东西其实就是接口里的方法包含参数和内容。
2、Lambda表达式的格式
使用前提:有接口;接口中有且仅有一个抽象方法。测试类中存在一个方法其参数为接口对象。并且这个方法其内部要使用接口的抽象方法。这个方法怎么使用要看我们具体做的事情。
格式:(形式参数) -> {代码块}
形式参数:如果有多个参数,参数之间用逗号隔开。如果没有参数,留空即可。这里的参数同于重写方法中的参数
->:由英文中划线和大于符号组成,固定写法,代表指向动作。
代码块:是我们具体要做的事情,也就是以前我们写的接口内抽象方法体的内容。
Lambda表达式和匿名内部类都可以代表一个接口的匿名实现。 其本质有点像具体的Runnable接口的实现类对象
3、 Lambda表达式的省略模式
省略规则:
参数类型可以省略,如果有多个参数的情况下,类型需要全部省略。
如果参数只有一个,那么小括号可以省略。
如果代码块的语句只有一条,可以省略大括号和分号。如果该语句是return语句,那么return也要省略。
4、Lambda表达式的注意事项
5、 Lambda表达式和匿名内部类的区别
所需类型不同:
匿名内部类:可以是接口、抽象类、具体类。
Lambda表达式:只能是接口。
使用限制不同:
如果接口中有且仅有一个抽象方法,可以使用Lambda表达式或匿名内部类。
如果接口中有多个抽象方法,只能用匿名内部类。
实现原理不同:
匿名内部类:编译之后,产生一个单独的.class字节码文件。
Lambda表达式:编译之后,没有一个单独的.class字节码文件,对应的字节码会在运行的时候动态生成。
二 方法组成更新
1、方法组成更新概述
接口的组成:
常量:public static final
抽象方法:public abstract
静态方法
默认方法
私有方法
2、接口中默认方法
①概述:如果接口中的方法满足不了要求了,需要在补充方法,那就必须在其实现类中重写这些方法,如果其实现类比较多,会很麻烦。若考虑在写接口继承自父接口,也比较麻烦,所以用接口中的默认方法。
②格式:
public default 返回值类型 方法名(参数列表){}
例如:public default void show3(){}
③注意事项:
默认方法不是抽象方法,所以不强制被重写,但是要想重写的话,必须去掉default关键字。
public 可以省略,default不能省略。
3、接口中静态方法
定义格式:public static 返回值类型 方法名(参数列表){}
例如:public static void show(){}
静态方法只能通过接口名调用,不能通过实现类名或者对象名调用。
public可以省略,static不能省略。
4、接口中私有方法
定义格式:private 返回值类型 方法名(参数列表){}
私有非静态方法
举例:private void show(){}
private static 返回值类型 方法名(参数列表){}
私有静态方法
默认方法可以调用私有的静态方法和非静态方法。
静态方法只能调用私有的静态方法。
三 方法引用
1、方法引用概述
使用前提:有接口;接口中有且仅有一个抽象方法。测试类中存在一个方法其参数为接口对象。并且这个方法其内部要使用接口的抽象方法。
2、方法引用符
::
该符号为引用运算符,它所在的表达式被称为方法引用。
需要说的是引用运算符中并没有参数,因为它会根据接口的参数自己知道用什么参数。它的兄弟是Lambda表达式,它不用写参数类型,箭头。Lambda表达式被类方法替代的时候,它的形式参数全部传递给静态方法作为参数。
3、Lambda表达式支持的方法引用
常见的引用方式:
引用类方法
引用对象的实例方法
引用类的实例方法
引用构造器
4、引用类方法
引用类方法其实就是引用类的静态方法。
格式:类名::静态方法
Lambda表达式被对象的实例方法替代的时候,它的形式参数全部传递给该静态方法作为参数。
5、引用对象的实例方法
引用对象的实例方法,其实就是引用类的成员方法。
格式:对象::成员方法
举例:"hello world"::toUpperCase
String类中的方法:public String toUpperCase()将此String所有字符转换为大写。
Lambda表达式被对象的实例方法替代的时候,它的形式参数全部传递给该方法作为参数。
6、引用类的实例方法
引用类的实例方法,其实就是引用类的成员方法。
格式:类名::成员方法
举例:String::substring
Lambda表达式被类的实例方法替代的时候,第一个参数作为调用方法的对象,后面的参数全部传给该方法作为参数。
usemySubString((s,x,y) -> s.substring(x,y) );
usemySubString(String::substring);
7、引用构造器
引用构造器,其实就是引用构造方法。
格式:类名::new
举例:Student::new
useStudentBuilder(((name, age) -> new Student(name,age)));
useStudentBuilder(Student::new);
Lambda表达式被构造器替代的时候,它的形式参数全部传递给构造器作为参数。
四 函数式接口
1、函数式接口概述
函数式接口:有且仅有一个方法的接口。
Java中函数式编程体现就是Lambda表达式,所以函数式接口就是可以适用于Lambda使用的接口。
只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利进行推导。
检测一个接口是不是函数式接口
@FunctionalInterface
放在接口定义的上面:如果接口是函数式接口,编译通过;如果不是,编译失败。
一般在写的时候,我们尽量加上注释,但是就算没有注释,只要是一个接口,且其中只有一个抽象方法也是函数式接口。
2、函数式接口作为方法的参数
如果方法的参数是一个函数式接口,我们可以使用Lambda表达式作为参数传递。
startTherd(() -> System.out.println(Thread.currentThread().getName()+":线程启动了"));
3、函数式接口作为方法的返回值
如果方法的返回值是一个函数式接口,我们可以使用Lambda表达式作为结果返回。
return (s1,s2) -> s1.length() - s2.length();
4、四个常用的函数式接口
5、Supplier接口
Supplier<T>:包含一个无参的方法。
T get()
:获得结果
该方法不需要参数,它会按照某种实现逻辑(由Lambda表达式实现)返回一个数据。
Supplier<T>接口也叫生产型接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会产生什么类型的数据供我们使用。
6、Consumer接口
Consumer<T>:包含两个方法。
void accept(T t)
对给定的参数执行此操作(参作来源于Lambda表达式)。
default Consumer<T> andThen(Consumer after)
返回一个组合的Consumer,依次执行此操作,然后执行after操作。
Consumer<T>接口也叫消费型接口,它消费的数据类型由泛型决定。
7、Predicate接口
Predicate<T>:常用的四个方法:
boolean test(T t)
对给定的参数进行判断(判断逻辑由Lambda表达式实现),返回一个布尔值。
default Predicate<T> negate()
返回一个逻辑的否定,对应逻辑非。
default Predicate<T> and(Predicate other)
返回一个组合判断,对应逻辑与。
default Predicate or(Predicate other)
返回一个组合判断,对应逻辑或。
Predicate<T>接口一般用于判断参数是否满足指定的条件。
8、Function接口
Function<T,R> T 函数输入的类型。R 函数输出的类型。
常用方法:
R apply(T t)
将此函数应用于给定的参数。
default<V> Function andThen(Funcrion after)
返回一个组合函数,首先将该函数应用于输入,然后将after应用于结果。
Function<T,R> 接口通常用于对参数进行处理,转换(处理逻辑由Lambda表达式实现),然后返回一个新的值
五 Stream流
1、体验Stream流
Stream流是一个接口。
使用Stream流的方式完成过滤操作
①生成流
通过数据源(集合、数组等)生成流。
数据源名.stream()
②中间操作
一个流后面可以跟0或多个操作,其目的只要是打开流,做出某种程度的数据过滤/映射,然后返回一个新的流,交给下一个操作使用。
ex: filter()
③终结操作
一个流只能有一个终结操作,当这个操作执行后,流就被使用光了,无法在被操作,所以这是流的最后一个操作
ex:forEach()
2、Stream流的生成方式
Stream流的常见生成方式
①Collection体系的集合可以使用默认方法default Stream<E> stream()
生成流。
List<String> list = new ArrayList<String>();
Stream<String> liststream = list.stream();
Set<String> set = new TreeSet<String>();
Stream<String> setstream = set.stream();
②Map体系的集合间接生成的流。
Map<String, Integer> map = new HashMap<String, Integer>();
Stream<String> keystream = map.keySet().stream();
Stream<Integer> valuestream = map.values().stream();
Stream<Map.Entry<String, Integer>> entrysetstream = map.entrySet().stream();
③数组可以通过Stream接口的静态方法of(T....values)
生成流。
String[] strArray = {"hello", "world", "java"};
Stream<String> strArray1 = Stream.of(strArray);
Stream<String> strArray2 = Stream.of("hello", "world", "java");
Stream<Integer> intstream = Stream.of(555,666);
3、Stream流的常见中间操作方法
①Stream<T> filter(Predicate predicate)
对流中的数据进行过滤。
Predicate是函数式接口,其中的方法boolean test(T t)
对给定的参数进行判断,返回一个布尔值。
可以连续调用此方法过滤数据。
②Stream<T> limit(long maxsize)
返回此流中的元素组成的流,截取前指定参数个数的数据·。
③Stream<T> skip(long n)
跳过指定参数个数的数据,返回此流的剩余元素组成的流。
④static <T> Stream<T> concat(Stream a,Stream b)
合并a和b两个流为一个流。
由于是static修饰,直接通过Stream接口名调用。Stream.concat()
⑤Stream<T> distinct()
返回由该流的不同元素(底层根据equals方法实现)组成的流。
⑥Stream<T> sorted()
返回由该流的元素组成的流,根据自然顺序排序。
⑦Stream<T> sorted(Comparator compartor)
返回由该流的元素组成的流,根据比较器自定义排序
⑧<R> Stream<R> map(Function mapper)
返回由给定函数应用于此流的元素的结果组成的流。
Function接口中的方法: R apply(T t)
⑨IntStream mapToInt(ToIntFunction mapper)
返回一个IntStream,其中包含由给定函数应用于此流的元素的结果组成的流。
IntStream 表示原始int流
ToIntFunction接口中的方法: int applyAsInt(T value)
IntStream流中有一个独有的方法int sum()
返回此流中元素的总和。
4、Stream流的终结操作方式
Stream流的常见终结操作方法
①void forEach(Consumer action)
对此流的每个元素执行操作
Consumer接口中的方法: void accept(T t)
对给定的参数执行此操作。
②long count()
返回此流中元素的数量
在Stream流中不能同时出现多个终结操作方式。
5、Stream流的收集操作
对数据使用Stream流的方式操作完毕后,如果想把流中的数据收集到集合中,使用
Stream流的收集方法:
R collect(Collector collector)
这个方法的参数是一个接口,想要实现,使用它的实现类
工具类Collectors提供了具体的收集方式
public static <T> Collector toList()
把元素收集到List集合中
public static <T> Collector toSet()
把元素收集到Set集合中
public static Collector toMap(Function keyMapper,Function valueMapper)
把元素收集到Map集合
反射
一 类加载器
1、类加载
当程序要使用某个类时,如果类还没有加载到内存中,则系统会通过类的加载、类的连接、类的初始化这三个步骤来进行初始化。无意外情况,JVM会连续执行,所以这三步被称为类加载或者类初始化。
①类的加载
②类的连接
③类的初始化
在这一个阶段,主要对类变量进行初始化。
类的初始化步骤:
类的初始化时机:
2、类加载器
①类加载器的作用
②JVM的类加载机制:
③ClassLoader 是负责加载类的对象类。
Java运行时有以下内置类加载器:
Bootstrap class loader
是虚拟机的内置类加载器,通常表示为null,且没有父null
Platform class loader
平台类加载器可以看到所有平台类,平台类包括由平台类加载器或其祖先定义的JavaSE平台API,其实现类和JDK特定的运行时类。
System class loader
也被称为应用程序类加载器,与平台类加载器不同。系统类加载器通常用于定义应用程序类路径,模块路径和JDK特定工具上的类。
类加载器的继承关系:Bootstrap > Platform > System
④ClassLoader中的两个方法
static ClassLoader getSystemClassLoader()
返回用于委派的系统类加载器。
ClassLoader getParent()
返回父类加载器进行委派。
二 反射
1.反射概述
Class类就是所有.class类所对应的类型 ,那么Class类里面就包含了变量和方法的信息,我们就可以通过Class类的对象来使用这些变量和方法。
Java反射机制:是指在运行时去获取一个类的变量和方法的信息。然后通过获取到的信息来创建对象,然后调用方法的一种机制,由于这种动态性,可以极大的增强程序的灵活性,程序不用在编译器就完成确定,在运行期间任然可以拓展。这种机制就叫做反射。
反射的目的是为了使用一个类。
一个类在内存中只有一个字节码文件对象。
2、获取Class类的对象
想要通过反射去使用一个类,首先要获取该类的字节码文件对象,也就是类型为Class类型的对象。
获取Class类的对象的三种方式
①使用类的class属性来获取该类对应的Class类对象。例如,Studetn.class会返回Student类对应的Class对象
想使用类的类名.class
举例 Class<Student> c1 = Student.class;
基本数据类型也可以通过.class获取对应的Class类的对象
②调用对象的getClass()
方法,返回该对象所属类对应的Class对象。
该方法是Object类中的方法,所以所有的Java对象都可以调用该方法。
对象名.getClass()
举例 Class<? extends Student> c3 = s.getClass();
③使用Class类中的静态方法forName(String className)
,该方法需要传入字符串参数,该字符串参数的值是某个类的全路径,也就是完整包名的路径。
Class forName("包名")
举例 Class<?> c4 = Class.forName("com.itheima_02.Student");
第一个方法最方便,第三个方法最灵活。
3、反射获取构造方法
Class类中用于获取构造方法的函数
①Constructor<?>[] getConstructors()
返回一个数组,其内容是被public修饰的所有构造方法
举例Constructor<?>[] c1 = c.getConstructors();
for(Constructor con: c1 ) {
System.out.println(con);
}
②Constructor<?>[] getDeclaredConstructors()
返回一个数组,内容是所有的构造方法
举例Constructor<?>[] c2 = c.getDeclaredConstructors();
for(Constructor con : c2) {
System.out.println(con);
}
③Constructor<?> getConstructor(Class<?>...parameterTypes)
返回一个对象,里面是一个public修饰的构造方法,参数是Class类型的,所以直接使用类名.class
举例Class<?> c = Class.forName("com.itheima_02.Student");
Constructor<?> con = c.getConstructor(String.class, int.class, String.class);
con.setAccessible(true).
Object obj = con.newInstance("林青霞",30,"西安");
System.out.println(obj);
④Constructor<?> getDeclaredConstructor(Class<?>...parameterTypes)
返回一个对象,里面是一个构造方法,可以被所有修饰符修饰,参数是Class类型的,所以直接使用类名.class。例如:String.class;int.class
里面的参数是你要获取的构造方法的参数的个数和数据类型对应的字节码文件,是Class类型的。
获取构造方法对象以后,不能直接使用,而是用其newInstance方法获取使用对象obj后 方可使用
对于方法③和④,在得到构造方法对象以后,还要用该构造方法对象的方法来创建对象才能进一步使用构造方法。
Constructor类中用于创建对象的方法
T newInstance(Object...initargs)
参数就是对应于构造方法对象参数里面的具体内容
暴力反射
⑤
当构造方法、成员变量、成员方法是被private、default等非public修饰时,虽然能够通过方法③和④,(也就是一次得到一个对象)得到,但是由于受到访问限制并不能创建构造方法的使用对象(newInstance),所以反射中提供了暴力反射,可以取消访问的限制,只需要调用Class类里面的setAccessible(true)
方法即可。为了方便不论是不是public修饰,我们可以在获取构造方法对象、成员变量对象、成员方法对象以后,直接就暴力反射。
4、反射获取成员变量
成员变量又称为字段,用Field表示。
Class类中用于获取成员变量的方法
①Field[] getFields()
返回一个Field数组,数据是所有的public修饰的成员变量。
举例 Class<?> c = Class.forName("com.itheima_02.Student");
Field[] fields = c.getFields();
for(Field field : fields ) {
System.out.println(field);
}
②Field[] getDeclaredFields()
返回一个Field数组,数据是所有的成员变量。
返回一个数组对应的两个方法和反射获取构造方法的返回一个数组使用步骤相同。
③Field getField(String name)
返回一个Field对象 表示一个指定的public修饰的成员变量。
而参数就是想要获取的成员变量名。
④Field getDeclaredFields(String name)
返回一个Field对象,表示一个指定的成员变量。
而参数就是想要获取的成员变量名。
举例 Class<?> c = Class.forName("com.itheima_02.Student");
Field field = c.getField("address");
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
field.set(obj,"西安");
System.out.println(obj);
使用返回一个Field对象获取成员变量,先要获取Class对象c,然后获取无参构造方法对象的使用对象获取对象obj,然后根据c调用getField()方法根据指定的成员变量得到成员变量对象,然后根据成员变量对象调用set方法给obj的成员变量对象field赋值。
Field类中用于给成员变量赋值的方法
void set(Object obj,Object value)
给无参构造方法对象的使用方法对象obj的成员变量赋值为value。
由成员变量对象调用此方法。
5、反射获取成员方法
Class类中用于获取成员方法的方法:
①Method[] getMethods()
返回所有公共成员方法对象的数组,包括继承的。
举例:Class<?> c = Class.forName("com.itheima_02.Student");
Method[] methods = c.getMethods();
for(Method method : methods) {
System.out.println(method);
}
②Method[] getDeclaredMethods()
返回所有成员方法对象的数组,不包括继承的
③Method getMethod(String name,Class<?>...parameterTypes)
返回单个公共成员方法对象。
其第一个参数是这个公共成员方法的名字,第二个参数是该方法对应的参数Class类型(数据类型.class)
④Method getDeclaredMethod(String name,Class<?>...parameterTypes)
返回单个成员方法对象。
其第一个参数是这个公共成员方法的名字,第二个参数是该方法对应的参数Class类型(数据类型.class)
举例:Class<?> c = Class.forName("com.itheima_02.Student");
Method m = c.getMethod("method1");
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
m.invoke(obj);
使用返回一个方法对象,首先获取Class类对象c,然后获取无参构造方法对象的方法来使用该对象obj,然后调用getMethod方法获取成员方法对象,然后调用方法对象的方法来调用成员方法。
Method类中用于调用成员方法的方法:
Object invoke(Object obj,Object...args)
调用obj对象的成员方法。
返回值是Object,obj就是调用方法的对象,args是成员方法的参数具体值。
6、反射使用类的总结
当我们想要一次使用一个类属性的对象时,步骤都是差不多的。先获取Class类对象,再获取无参构造方法对象(如使用构造方法,则获取带参构造方法对象),再获取类属性(成员变量、构造方法、成员方法)对象,(如果不只到其修饰符,都用以getDeclared前缀的那个),然后类属性对象暴力反射,最后再调用(成员变量、构造方法、成员方法)。
注意:
①不能直接使用构造方法对象,应该使用构造方法对象中的newInstance方法获取使用对象的obj。
②创建无参构造方法对象就是相当于Student s = new Student(); 就是对象的实例化。
7、反射的应用
①反射可以通过暴力反射访问类中被private修饰的属性。
②反射可以越过泛型检查
例如:想要往ArrayList<Integer>集合中添加String元素,明显不行。因为泛型已经定义为Integer了,但我们可以通过反射获取add方法,add原始方法所需要的数据类型其实是Object。
③运行配置文件指定内容
模块化
1、模块化概述
2、模块的基本使用
3、模块服务的使用