Java高级特性
Lambda表达式
概述
函数式编程思想概述:在数学中,函数就是有输入量、输出量的一套计算方案;
面向对象思想强调“必须通过对象的形式来做事情”;函数式思想则尽量忽略面向对象的复杂语法,“强调做什么,而不是以什么形式去做”
标准格式
(形式参数)->{代码块}
- 形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可
- ->:由英文中画线和大于符号组成,固定写法;代表指向动作
- 代码块:是我们具体要做的事情,也就是我们之前写的方法体内容
使用前提
代码演示:
public interface Eatable {
void eat();
}
public class Eatablempl implements Eatable {
@Override
public void eat() {
System.out.println("一天一个苹果,医生远离我");
}
}
public class Demo {
public static void main(String[] args) {
Eatable e = new Eatablempl();
useEatable(e);
useEatable(new Eatable() {
@Override
public void eat() {
System.out.println("一天一个苹果,医生远离我");
}
});
useEatable(() -> {
System.out.println("一天一个苹果,医生远离我");
});
}
private static void useEatable(Eatable e) {
e.eat();
}
}
public interface Eatable {
void fly(String s);
}
public class Demo {
public static void main(String[] args) {
useFlyable(new Eatable() {
@Override
public void fly(String s) {
System.out.println(s);
System.out.println("2022总冠军");
}
});
useFlyable((String s) -> {
System.out.println(s);
System.out.println("2022总冠军");
});
}
private static void useFlyable(Eatable e) {
e.fly("勒布朗");
}
}
public interface Eatable {
int add(int x, int y);
}
public class Demo {
public static void main(String[] args) {
useFlyable((int a, int b) -> {
return a + b;
});
}
private static void useFlyable(Eatable e) {
int sum = e.add(10, 20);
System.out.println(sum);
}
}
省略模式
- 参数的类型可以省略;但是有多个参数的情况下,不能只省略一个
- 如果参数只有一个,那么小括号可以省略
- 如果代码块的语句只有一条,可以省略大括号和分号,甚至是return
代码演示:
public interface Eatable {
int add(int x, int y);
}
public interface Flyable {
void fly(String s);
}
public class Demo {
public static void main(String[] args) {
useFlyable((x, y) -> {
return x + y;
});
useFlyable1(s -> {
System.out.println(s);
});
useFlyable1(s -> System.out.println(s));
useFlyable((x, y) -> x + y);
}
private static void useFlyable(Eatable e) {
int sum = e.add(10, 20);
System.out.println(sum);
}
private static void useFlyable1(Flyable f) {
f.fly("勒布朗");
}
}
注意事项
-
使用Lambda表达式必须要有接口,并且要求接口中有且只有一个抽象方法 -
必须有上下文环境,程序才能推导出Lambda对应的接口 ? 使用局部变量的赋值得知Lambda对应的接口:Runnable r=() -> System.out.println("Lambda"); ? 使用调用方法的参数得知Lambda对应的接口:new Thread(() -> System.out.println("Lambda")).start();
Lambda表达式和匿名内部类的区别
所需类型不同
- 匿名内部类:可以是接口,抽象类,具体类
- Lambda表达式:只能是接口
使用限制不同
- 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
- 如果接口中有多于一个的抽象方法,只能使用匿名内部类,而不能使用Lambda表达式
实现原理不同
- 匿名内部类:编译之后,产生一个单独的.class字节码文件
- Lambda表达式:编译之后,没有一个单独的.class字节码文件;对应的字节码会在运行的时候动态生成
接口组成更新
接口的组成
常量:由public static final 修饰,一般省略
抽象方法:由public abstract 修饰,一般省略
默认方法(Java8之后)
静态方法(Java8之后)
私有方法(Java9之后)
接口中的默认方法
默认方法的定义格式:
- 格式:
public default 返回值类型 方法名(参数列表){ } ;public可省略 - 范例:
public default void show(){ }
注意事项
- 默认方法不是抽象方法,所以不强制被重写;但是可以被重写,重写的时候要去掉default关键字
- public可以省略,default不能省略
接口中的静态方法
静态方法的定义格式:
- 格式:
public static 返回值类型 方法名(参数列表){ } ;public可省略 - 范例:
public static void show(){ }
注意事项
- 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
- public可以省略,static不能省略
接口中的私有方法
私有方法的定义格式:
-
格式1:private 返回值类型 方法名(参数列表){ } -
范例1:private void show(){ } -
格式2:private static 返回值类型 方法名(参数列表){ } -
范例2:private static void show(){ }
注意事项
- 默认方法可以调用私有的静态方法和非静态方法
- 静态方法只能调用私有的静态方法
方法引用
方法引用符
- :: 该符号为引用运算符,而它所在的表达式被称为方法引用
eg:对于Lambda表达式:usePrintable(s->System.out.println(s)); 则方法引用为:usePrintable(System.out::println);
推导与省略
- 如果使用Lambda表达式,那么根据”可推导就是可省略“的原则,无需指定参数类型,也无需指定重载形式,它们都将被自动推导
- 如果使用方法引用,也是同样可以根据上下文进行推导
- 方法引用时Lambda表达式的孪生兄弟(Lambda表达式表达的东西若有存在方法则就可以用方法引用)
Lambda表达式支持的方法引用
常见的引用方式:
- 引用类方法
- 引用对象的实例方法
- 引用类的实例方法
- 引用构造器
引用类方法
引用类方法,其实就是引用类的静态方法
- 格式:类型::静态方法
- 范例:
Integer::parselnt
Lambda表达式被类方法替代的时候,它的形式参数全部传递给静态方法作为参数
代码演示:
public interface Eatable {
int con(String s);
}
public class Demo {
public static void main(String[] args) {
useEatable((String s) -> {
return Integer.parseInt(s);
});
useEatable(s -> Integer.parseInt(s));
useEatable(Integer::parseInt);
}
private static void useEatable(Eatable e) {
int number = e.con("666");
System.out.println(number);
}
}
引用对象的实例方法
引用对象的实例方法,其实就是引用类中的成员方法
- 格式:对象::成员方法
- 范例:
"HelloWorld"::toUpperCase
Lambda表达式被对象是实例化方法替代的时候,它的形式参数全部传递给该方法作为参数
代码演示:
public class PringString {
public void printUpper(String s) {
String result = s.toUpperCase();
System.out.println(result);
}
}
public interface Eatable {
void printUpperCase(String s);
}
public class Demo {
public static void main(String[] args) {
usePrinter((String s) -> {
System.out.println(s.toUpperCase());
});
usePrinter(s -> System.out.println(s.toUpperCase()));
PringString ps = new PringString();
usePrinter(ps::printUpper);
}
private static void usePrinter(Eatable e) {
e.printUpperCase("HelloWorld");
}
}
引用类的实例方法
引用类的实例方法,其实就是引用类中的成员方法
- 格式:类名::成员方法
- 范例:
String::substring
Lambda表达式被类的实例方法替代的时候,第一个参数作为调用者,后面的参数全部传递给该方法作为参数
代码演示:
public interface Eatable {
String mySubString(String s, int x, int y);
}
public class Demo {
public static void main(String[] args) {
useMyString((String s, int x, int y) -> {
return s.substring(x, y);
});
useMyString((s, x, y) -> s.substring(x, y));
useMyString(String::substring);
}
private static void useMyString(Eatable e) {
String s = e.mySubString("HelloWorld", 2, 5);
System.out.println(s);
}
}
引用构造器
引用类的实例方法,其实就是引用类中的成员方法
- 格式:类名::new
- 范例:
Student::new
Lambda表达式被构造器替代的时候,它的形式参数全部传递给构造器作为参数
代码演示:
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public interface Eatable {
Student build(String name, int age);
}
public class Demo {
public static void main(String[] args) {
useStudentBuider((String name, int age) -> {
Student s = new Student(name, age);
return s;
});
useStudentBuider(((name, age) -> new Student(name, age)));
useStudentBuider(Student::new);
}
private static void useStudentBuider(Eatable e) {
Student s = e.build("勒布朗", 37);
System.out.println(s.getName() + "," + s.getAge());
}
}
函数式接口
概述
函数式接口:有且仅有一个抽象方法的接口
Java中的函数式编程体现就是Lambda表达式,所以函数式接口就是可以适用于Lambda使用的接口
函数式接口判断方法: 在接口定义上方加注解:@FunctionalInterface ;编译通过则是
注意:定义函数式接口时,建议加上注解
函数式接口作为方法的参数
如果方法的参数是一个函数式接口,我们可以使用Lambda表达式作为参数传递
代码演示:
public class RunnableDemo {
public static void main(String[] args) {
startThread(() -> System.out.println(Thread.currentThread().getName() + "线程启动了"));
}
private static void startThread(Runnable r) {
new Thread(r).start();
}
}
函数式接口作为方法的返回值
如果方法法返回值是一个函数式接口,可以使用Lambda表达式作为结果返回
代码演示:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class RunnableDemo {
public static void main(String[] args) {
ArrayList<String> array = new ArrayList<>();
array.add("ccc");
array.add("a");
array.add("bb");
array.add("dddd");
System.out.println("排序前:" + array);
Collections.sort(array, getComparator());
System.out.println("排序后:" + array);
}
private static Comparator<String> getComparator() {
return new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s1.length() - s2.length();
}
};
return (s1, s2) -> s1.length() - s2.length();
}
}
常用的函数式接口
Supplier接口
Supplier<T> :包含一个无参的方法
- T get():获得结果
- 该方法不需要参数,它会按照某种实现逻辑(由Lambda表达式实现)返回一个数据
- Supplier< T >接口也被称为生产型接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会产生什么类型数据提供我们使用
代码演示:
import java.util.function.Supplier;
public class RunnableDemo {
public static void main(String[] args) {
String s = getString(() -> "勒布朗");
System.out.println(s);
Integer i = getInteger(() -> 37);
System.out.println(i);
}
private static Integer getInteger(Supplier<Integer> sup) {
return sup.get();
}
private static String getString(Supplier<String> sup) {
return sup.get();
}
}
练习
import java.util.function.Supplier;
public class RunnableDemo {
public static void main(String[] args) {
int[] arr = {19, 2, 24, 55, 34};
int maxValue = getMax(() -> {
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
return max;
});
System.out.println(maxValue);
}
private static int getMax(Supplier<Integer> sup) {
return sup.get();
}
}
Consumer接口
Consumer<T> :包含两个方法
void accept(T t) :对给定的参数执行此操作default Consumer<T> andThen(Consumer after) :返回一个组合的Consumer,一次执行此操作,然后执行after操作- Consumer< T >接口也被称为消费型接口,它消费的数据的数据类型由泛型指定
代码演示:
import java.util.function.Consumer;
public class RunnableDemo {
public static void main(String[] args) {
operatorString1("勒布朗", s -> System.out.println(s));
operatorString1("勒布朗", System.out::println);
operatorString1("勒布朗", s -> System.out.println(new StringBuilder(s).reverse().toString()));
operatorString2("勒布朗", s -> System.out.println(s), s -> System.out.println(new StringBuilder(s).reverse().toString()));
}
private static void operatorString2(String name, Consumer<String> con1, Consumer<String> con2) {
con1.andThen(con2).accept(name);
}
private static void operatorString1(String name, Consumer<String> con) {
con.accept(name);
}
}
练习
import java.util.function.Consumer;
public class RunnableDemo {
public static void main(String[] args) {
String[] strArray = {"勒布朗,37", "浓眉,32", "库里,33"};
printInfo(strArray, (String str) -> {
String name = str.split(",")[0];
System.out.print("姓名:" + name);
}, (String str) -> {
int age = Integer.parseInt(str.split(",")[1]);
System.out.println(",年龄:" + age);
});
printInfo(strArray, str -> System.out.print("姓名:" + str.split(",")[0]),
str -> System.out.println(",年龄" + Integer.parseInt(str.split(",")[1])));
}
private static void printInfo(String[] strArray, Consumer<String> con1, Consumer<String> con2) {
for (String str : strArray) {
con1.andThen(con2).accept(str);
}
}
}
Predicate接口
Predicate<T> :常用的四个方法(共五个方法)
boolean test(T t) :对给定的参数进行判断(判断逻辑由Lambda表达式实现),返回一个布尔值default Predicate<T> negate() :返回一个逻辑的否定,对应逻辑非default Predicate<T> and(Predicate other) :返回一个组合判断,对应短路与default Predicate<T> or(Predicate other) :返回一个组合判断,对应短路或Predicate<T> :接口通常用于 判断哪参数是否满足指定条件
代码演示:
import java.util.function.Predicate;
public class RunnableDemo {
public static void main(String[] args) {
boolean b1 = checkString("hello", s -> s.length() > 8);
System.out.println(b1);
boolean b2 = checkString("helloworld", s -> s.length() > 8);
System.out.println(b2);
boolean b3 = checkString1("hello", s -> s.length() > 8, s -> s.length() < 15);
System.out.println(b3);
boolean b4 = checkString1("helloworld", s -> s.length() > 8, s -> s.length() < 15);
System.out.println(b4);
}
private static boolean checkString(String s, Predicate<String> pre) {
return pre.test(s);
return pre.negate().test(s);
}
private static boolean checkString1(String s, Predicate<String> pre1, Predicate<String> pre2) {
return pre1.and(pre2).test(s);
return pre1.or(pre2).test(s);
}
}
练习
import java.util.ArrayList;
import java.util.function.Predicate;
public class RunnableDemo {
public static void main(String[] args) {
String[] strArray = {"勒布朗,37", "浓眉,32", "库里,33", "维斯布鲁克,32", "韦德,37"};
ArrayList<String> array = myFilter(strArray, s -> s.split(",")[0].length() > 2,
s -> Integer.parseInt(s.split(",")[1]) > 33);
for (String str : array) {
System.out.println(str);
}
}
private static ArrayList<String> myFilter(String[] strArray, Predicate<String> pre1, Predicate<String> pre2) {
ArrayList<String> array = new ArrayList<>();
for (String str : strArray) {
if (pre1.and(pre2).test(str)) {
array.add(str);
}
}
return array;
}
}
Function接口
Function<T,R> :常用两个方法
R apply(T t) :将此函数应用与给定的参数default <V> Function andThen(Function after) :返回一个组合函数,首先将该函数应用与输入,然后将after函数应用与结果- Function< T,R >接口通常用于对参数进行处理,转换(处理逻辑由Lambda表达式实现),然后返回一个新的值
代码演示:
import java.util.function.Function;
public class RunnableDemo {
public static void main(String[] args) {
convert("100", s -> Integer.parseInt(s));
convert("100", Integer::parseInt);
convert1(100, i -> String.valueOf(i + 566));
convert2("100", s -> Integer.parseInt(s), i -> String.valueOf(i + 566));
}
private static void convert(String s, Function<String, Integer> fun) {
Integer i = fun.apply(s);
System.out.println(i);
}
private static void convert1(int i, Function<Integer, String> fun1) {
String s = fun1.apply(i);
System.out.println(s);
}
private static void convert2(String s, Function<String, Integer> fun1, Function<Integer, String> fun2) {
String ss = fun1.andThen(fun2).apply(s);
System.out.println(ss);
}
}
练习
import java.util.function.Function;
public class RunnableDemo {
public static void main(String[] args) {
String s = "勒布朗,37";
convert(s, (String ss) -> {
return s.split(",")[1];
}, (String ss) -> {
return Integer.parseInt(ss);
}, (Integer i) -> {
return i + 70;
});
convert(s, ss -> ss.split(",")[1], ss -> Integer.parseInt(ss), i -> i + 70);
convert(s, ss -> ss.split(",")[1], Integer::parseInt, i -> i + 70);
}
private static void convert(String s, Function<String, String> fun1, Function<String, Integer> fun2, Function<Integer, Integer> funn3) {
Integer i = fun1.andThen(fun2).andThen(funn3).apply(s);
System.out.println(i);
}
}
Stream流
概述
Stream流把真正的函数式编程风格引入到Java中了
Stream流的使用:
- 生成流:通过数据源(集合,数组等)生成流
- 中间操作:一个流后面可以跟随零个或多个中间操作,其目的主要是打开流,做出某种程度的数据过滤/映射,然后返回一个新的流,交给下一个操作使用
- 终结操作:一个流只能有一个终结操作,当这个操作执行后,流就被使用“光”了,无法继续再被操作
Stream流的生成方式
Stream流的常见的生成方式
-
Collection体系的集合可以使用默认方法Stream()生成流 default Stream<E> stream() -
Map体系的集合不能直接生成流,需要间接生成流 -
数组可以通过Stream接口的静态方法of(T…values) 生成流
代码演示:
import java.util.*;
import java.util.stream.Stream;
public class RunnableDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
Stream<String> listStream = list.stream();
Set<String> set = new HashSet<>();
Stream<String> setStream = set.stream();
Map<String, Integer> map = new HashMap<>();
Stream<String> keyStream = map.keySet().stream();
Stream<Integer> valueStream = map.values().stream();
Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();
String[] strArray = {"hello", "world", "java"};
Stream<String> strArrayStream = Stream.of(strArray);
Stream<String> stringStream1 = Stream.of("hello", "world", "java");
Stream<Integer> integerStream = Stream.of(10, 20, 30);
}
}
Stream流的常见中间操作方法
filter方法
代码演示:
import java.util.*;
public class RunnableDemo {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("勒布朗");
list.add("浓眉");
list.add("库里");
list.add("韦德");
list.add("威少");
list.add("麦基");
list.stream().filter(s -> s.startsWith("勒")).forEach(System.out::println);
list.stream().filter(s -> s.length() == 3).forEach(System.out::println);
list.stream().filter(s -> s.startsWith("勒")).filter(s -> s.length() == 3).forEach(System.out::println);
}
}
filter方法&skip方法
Stream<T> limit(long maxSize) :返回此流中的元素组成的流,截取前指定参数个数的数据Stream<T> skip(long n) :跳过指定参数个数的数据,返回由该流的剩余元素组成的流
代码演示:
import java.util.*;
public class RunnableDemo {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("勒布朗");
list.add("浓眉");
list.add("库里");
list.add("韦德");
list.add("威少");
list.add("麦基");
list.stream().limit(3).forEach(System.out::println);
list.stream().skip(3).forEach(System.out::println);
list.stream().skip(2).limit(2).forEach(System.out::println);
}
}
concat方法&distinct方法
static<T>Stream<T>concat(Stream a,Stream b) :合并a和b两个流为一个流Stream<T>distinct() :返回由该流的不同元素(根据Object.equals(Object))组成的流
代码演示:
import java.util.*;
import java.util.stream.Stream;
public class RunnableDemo {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("勒布朗");
list.add("浓眉");
list.add("库里");
list.add("韦德");
list.add("威少");
list.add("麦基");
Stream<String> s1 = list.stream().limit(4);
Stream<String> s2 = list.stream().skip(2);
Stream.concat(s1, s2).forEach(System.out::println);
Stream.concat(s1, s2).distinct().forEach(System.out::println);
}
}
sorted方法
Stream<T>sorted() :返回由此流的元素组成的流,根据自然顺序排序Stream<T>sorted(Comparator comparator) :返回由该流的元素组成的流,根据提供的Comparator进行排序
代码演示:
import java.util.*;
public class RunnableDemo {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("lebulang");
list.add("nongmei");
list.add("kuli");
list.add("weide");
list.add("weishao");
list.add("maiji");
list.stream().sorted().forEach(System.out::println);
list.stream().sorted((s1, s2) -> {
int num = s1.length() - s2.length();
int num1 = num == 0 ? s1.compareTo(s2) : num;
return num1;
}).forEach(System.out::println);
}
}
map方法&mapToInt方法
-
<R> Stream <R> map(Function mapper) :返回由给定函数应用于此流的元素的结果组成的流 Function接口中的方法 Rapply(T t) -
IntStream mapToInt(ToIntFunction mapper) :返回一个InStream其中包含将给定函数应用于此流的元素的结果 IntStream:表示原始int流 ToIntFunction接口中的方法 int applyAsInt(T value)
代码演示:
import java.util.*;
public class RunnableDemo {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("10");
list.add("20");
list.add("30");
list.add("40");
list.add("50");
list.stream().map(s -> Integer.parseInt(s)).forEach(System.out::println);
list.stream().map(Integer::parseInt).forEach(System.out::println);
list.stream().mapToInt(Integer::parseInt).forEach(System.out::println);
int result = list.stream().mapToInt(Integer::parseInt).sum();
System.out.println(result);
}
}
Stream流的常见终结操作方法
代码演示:
import java.util.*;
public class RunnableDemo {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("勒布朗");
list.add("浓眉");
list.add("库里");
list.add("韦德");
list.add("威少");
list.add("麦基");
list.stream().forEach(System.out::println);
long count = list.stream().filter(s -> s.startsWith("勒")).count();
System.out.println(count);
}
}
练习
public class Actor {
private String name;
public Actor(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
import java.util.*;
import java.util.stream.Stream;
public class RunnableDemo {
public static void main(String[] args) {
ArrayList<String> manlist = new ArrayList<>();
manlist.add("勒布朗");
manlist.add("刘宪华");
manlist.add("库里");
manlist.add("孙红雷");
manlist.add("威少");
manlist.add("周星驰");
ArrayList<String> womanlist = new ArrayList<>();
womanlist.add("惠若琪");
womanlist.add("宋祖儿");
womanlist.add("宋轶");
womanlist.add("谭松韵");
womanlist.add("赵今麦");
womanlist.add("江疏影");
Stream<String> manString = manlist.stream().filter(s -> s.length() == 3).limit(3);
Stream<String> womanStream = womanlist.stream().filter(s -> s.startsWith("宋")).skip(1);
Stream<String> stream = Stream.concat(manString, womanStream);
stream.map(Actor::new).forEach(p -> System.out.println(p.getName()));
Stream.concat(manlist.stream().filter(s -> s.length() == 3).limit(3),
womanlist.stream().filter(s -> s.startsWith("宋")).skip(1)).map(Actor::new).forEach(p -> System.out.println(p.getName()));
}
}
Stream流的收集操作
把流中的数据收集到集合中
Stream流的收集方法:
R collect(Collector collector) - 但这个收集方法的参数是一个Collector接口,需要实现类Collectors
工具类
public static <T> Collector toList() :把元素收集到List集合中public static <T> Collector toSet() :把元素收集到Set集合中public static Collector toMap(Function keyMapper,Function valueMapper) :把元素收集到Map集合中
代码演示:
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class RunnableDemo {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("勒布朗");
list.add("浓眉");
list.add("库里");
list.add("刘宪华");
Stream<String> listStream = list.stream().filter(s -> s.length() == 3);
List<String> names = listStream.collect(Collectors.toList());
for (String name : names) {
System.out.println(name);
}
Set<Integer> set = new HashSet<>();
set.add(10);
set.add(20);
set.add(30);
set.add(40);
Stream<Integer> setStream = set.stream().filter(age -> age > 25);
Set<Integer> ages = setStream.collect(Collectors.toSet());
for (Integer age : ages) {
System.out.println(age);
}
String[] strArray = {"勒布朗,37", "浓眉,32", "库里,33", "刘宪华,30"};
Stream<String> arrayStream = Stream.of(strArray).filter(s -> Integer.parseInt(s.split(",")[1]) > 32);
Map<String, Integer> map = arrayStream.collect(Collectors.toMap(s -> s.split(",")[0], s -> Integer.parseInt(s.split(",")[1])));
Set<String> keySet = map.keySet();
for (String key : keySet) {
Integer value = map.get(key);
System.out.println(key + "," + value);
}
}
}
反射
类加载
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始化三个步骤来对类进行初始化。如果不出现意外情况,JVM将会连续完成这三个步骤,所以有时也将这三个步骤统称为类加载或者类初始化
类的加载
- 就是指将class文件读入内存,并为之创建一个java.lang,Class对象(任何类被使用时,系统都会为之建立一个java.lang,Class对象)
类的连接
- 验证阶段:用于检验被加载的类是否有正确的内部结构,并和其他类协调一致
- 准备阶段:负责为类的类变量分配内存,并设置默认初始值
- 解析阶段:将类的二进制数据中的符号引用替换为直接引用
类的初始化
在该阶段,主要就是对类变量进行初始化
类的初始化步骤:
- 假如类还未被加载和连接,则程序先加载并连接该类
- 假如该类中的直接父类还未被初始化,则 先初始化其直接父类
- 假如类中有初始化语句,则系统依次执行这些初始化语句
注意:在执行第2 个步骤的时候,系统对直接父类的初始化步骤也遵循初始化步骤1-3;即JVM最先初始化的总是lang包下的Object类,当程序使用一个类时,系统会保证该类及其所有的父类都会被初始化
类的初始化时机:
- 创建类的实例
- 调用类的类方法
- 访问类或者接口的类变量,或者为该类变量赋值
- 使用反射方式来创建某个类或接口对应的java.langClass对象
- 初始化某个类的子类
- 直接使用java.exe命令来运行某个主类
类加载器
类加载器的作用
- 负责将.class文件加载到内存中,并为之生成对应的java.langClass对象
JVM的类加载机制
- 全盘负责:就是当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入
- 父类委托:就是当一个类加载器负责加载某个Class时,先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类
- 缓存机制:保证所有加载过的Class都会被缓存,当程序需要使用某个Class对象时,类加载器先从缓存区中搜索该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换为Class对象,存储到缓存区
ClassLoader:是负责加载类的对象
Java运行时具有以下内置加载器
Bootstrap class loader :它是虚拟机 内置加载器,通常表示为null,而且没有父级Platform class loader :平台类加载器可以看到所有平台类,平台类包括由平台类加载器或其祖先定义的Java SE平台API,其实现类和JDK特定的运行时类System class loader :它也被称为程序类加载器,与平台类加载器不同,系统类加载器通常用于定义应用程序类路径,模块路径和JDK特定工具上的类
类加载器的继承关系:System的父类加载器为Platform,而Platform的父类加载器为Bootstrap
ClassLoader中的两个方法
static ClassLoader getSystemClassLoader() :返回用于委派的系统类加载器ClassLoader getParent :返回父类加载器进行委派
代码演示:
public class RunnableDemo {
public static void main(String[] args) {
ClassLoader c = ClassLoader.getSystemClassLoader();
System.out.println(c);
ClassLoader c1 = c.getParent();
System.out.println(c1);
ClassLoader c2 = c1.getParent();
System.out.println(c2);
}
}
反射
概述
Java反射机制:是指在运行时去获取一个类的变量和方法信息;然后通过获取到的信息来创建对象,调用方法的一种机制。由于这种动态性,可以极大的增强程序的灵活性,程序不用在编译期就完成确定,在运行期仍然可以扩展
获取Clas类的对象
要想通过反射去使用一个类,首先我们要获取到该类的字节码文件对象,也就是类型为Class类型的对象
有以下三种方式来获取Class类型的对象
-
使用类的class属性来获取该类对应的Class对象。例如:Student.class将会返回Student类对应的Class对象 -
调用对象的getClass()方法,返回该对象所属类对应的Class对象
- 该方法是Object类中的方法,所有的Java对象都可以调用该方法
-
使用Class类中的静态方法forName(String className),该方法需要传入字符串参数,该字符串参数的值是某个类的全路径
代码演示:
public class Student {
private String name;
int age;
public String address;
public Student() {
}
private Student(String name) {
this.name = name;
}
Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
private void function() {
System.out.println("Function");
}
public void method1() {
System.out.println("method");
}
public void method2(String s) {
System.out.println("method:" + s);
}
public String method3(String s, int i) {
return s + "," + i;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException {
Class<Student> c1 = Student.class;
System.out.println(c1);
Class<Student> c2 = Student.class;
System.out.println(c1 == c2);
Student s = new Student();
Class<? extends Student> c3 = s.getClass();
System.out.println(c1 == c3);
Class<?> c4 = Class.forName("Demo3.Student");
System.out.println(c1 == c4);
}
}
反射获取构造方法
Class类中用于获取构造方法的方法
-
Constructor<?>[] getConstructors() :返回一个包含构造器对象的数组,构造器对象反映了此类对象所表示的类的所有公共构造函数 -
Constructor<?>[] getDeclaredConstructors() :返回构造器对象的数组,构造器对象反映由此类对象表示的类声明的所有构造函数 -
Constructor<T> getConstructor(class<?>... parameterTypes) :返回一个构造器对,该对象反映此类对象所表示的类的指定公共构造函数 -
Constructor<T> getDeclaredConstructor(class<?>... parameterTypes) :返回一个构造器对象,该对象反映此类对象所表示的类或接口的指定构造函数
Constructor类中用于创建对象的方法
T newInstance(Object... initargs) 根据指定的构造方法创建对象
代码演示:
public class Student {
private String name;
int age;
public String address;
public Student() {
}
private Student(String name) {
this.name = name;
}
Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
private void function() {
System.out.println("Function");
}
public void method1() {
System.out.println("method");
}
public void method2(String s) {
System.out.println("method:" + s);
}
public String method3(String s, int i) {
return s + "," + i;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<?> c = Class.forName("Demo3.Student");
Constructor<?>[] cons = c.getConstructors();
for (Constructor con : cons) {
System.out.println(con);
}
Constructor<?>[] cons = c.getDeclaredConstructors();
for (Constructor con : cons) {
System.out.println(con);
}
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
System.out.println(obj);
}
}
练习
public class Student {
private String name;
int age;
public String address;
public Student() {
}
private Student(String name) {
this.name = name;
}
Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
private void function() {
System.out.println("Function");
}
public void method1() {
System.out.println("method");
}
public void method2(String s) {
System.out.println("method:" + s);
}
public String method3(String s, int i) {
return s + "," + i;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<?> c = Class.forName("Demo3.Student");
Constructor<?> con = c.getConstructor(String.class, int.class, String.class);
Object obj = con.newInstance("勒布朗", 37, "洛杉矶");
System.out.println(obj);
}
}
public class Student {
private String name;
int age;
public String address;
public Student() {
}
private Student(String name) {
this.name = name;
}
Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
private void function() {
System.out.println("Function");
}
public void method1() {
System.out.println("method");
}
public void method2(String s) {
System.out.println("method:" + s);
}
public String method3(String s, int i) {
return s + "," + i;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<?> c = Class.forName("Demo3.Student");
Constructor<?> con = c.getDeclaredConstructor(String.class);
con.setAccessible(true);
Object obj = con.newInstance("勒布朗");
System.out.println(obj);
}
}
反射获取成员变量
Class类中用于获取成员变量的方法
Field[] getFields() :返回一个包含字段对象的数组,字段对象反映此类对象所表示的类或接口的所有可访问公共字段Field[] getDeclaredFields() :返回字段对象的数组,字段对象反映由此类对象表示的类或接口声明的所有字段Field getField(String name) :返回字段对象,该对象反映此类对象表示的类或接口的指定公共成员字段Field getDeclaredField(String name) :返回字段对象,该对象反映此类对象表示的类或接口的指定声明字段
Field类中用于给成员变量赋值的方法
void set(Object obj,Object value) :给obj对象的成员变量赋值为value
代码演示:
public class Student {
private String name;
int age;
public String address;
public Student() {
}
private Student(String name) {
this.name = name;
}
Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
private void function() {
System.out.println("Function");
}
public void method1() {
System.out.println("method");
}
public void method2(String s) {
System.out.println("method:" + s);
}
public String method3(String s, int i) {
return s + "," + i;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<?> c = Class.forName("Demo3.Student");
Field[] fields = c.getFields();
for (Field field : fields) {
System.out.println(field);
}
Field[] fields = c.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
Field addressField = c.getField("address");
Student s = new Student();
s.address = "克利夫兰";
System.out.println(s);
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
addressField.set(obj, "克利夫兰");
System.out.println(obj);
}
}
练习
public class Student {
private String name;
int age;
public String address;
public Student() {
}
private Student(String name) {
this.name = name;
}
Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
private void function() {
System.out.println("Function");
}
public void method1() {
System.out.println("method");
}
public void method2(String s) {
System.out.println("method:" + s);
}
public String method3(String s, int i) {
return s + "," + i;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
Class<?> c = Class.forName("Demo3.Student");
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
System.out.println(obj);
Field nameField = c.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(obj, "勒布朗");
System.out.println(obj);
Field ageField = c.getDeclaredField("age");
ageField.setAccessible(true);
ageField.set(obj, 37);
System.out.println(obj);
Field addressField = c.getDeclaredField("address");
addressField.setAccessible(true);
addressField.set(obj, "洛杉矶");
System.out.println(obj);
}
}
反射获取成员方法
method[] getMethods() :返回一个包含方法对象的数组,方法对象反映此类对象所表示的类或接口的所有公共方法.包括由类或接口声明的那些以及从超类和超接口继承的那些method[] getDeclaredMethods() :返回一个包含方法对象的数组,方法对象反映此类对象表示的类或接口的所有已声明方法.包括public,protected,default(package)访问和私有方法,但不包括继承的方法method getMethod(String name, class<?>... parameterTypes) :返回方法对象,该对象反映此类对象表示的类或接口的指定公共成员方法method getDeclaredMethod(String name, class<?>... parameterTypes) :返回方法对象,该对象反映此类对象表示的类或接口的指定声明方法
Method类中用于调用成员方法的方法
Object invoke(Object obj, Object... args) :在具有指定参数的指定对象上调用此方法对象表示的基础方法
代码演示:
public class Student {
private String name;
int age;
public String address;
public Student() {
}
private Student(String name) {
this.name = name;
}
Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
private void function() {
System.out.println("Function");
}
public void method1() {
System.out.println("method");
}
public void method2(String s) {
System.out.println("method:" + s);
}
public String method3(String s, int i) {
return s + "," + i;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<?> c = Class.forName("Demo3.Student");
Method[] methods = c.getMethods();
for (Method method : methods) {
System.out.println(method);
}
Method[] methods = c.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method);
}
Method m = c.getMethod("method1");
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
m.invoke(obj);
}
}
练习
public class Student {
private String name;
int age;
public String address;
public Student() {
}
private Student(String name) {
this.name = name;
}
Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
private void function() {
System.out.println("Function");
}
public void method1() {
System.out.println("method");
}
public void method2(String s) {
System.out.println("method:" + s);
}
public String method3(String s, int i) {
return s + "," + i;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<?> c = Class.forName("Demo3.Student");
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
Method m1 = c.getMethod("method1");
m1.invoke(obj);
Method m2 = c.getMethod("method2", String.class);
m2.invoke(obj, "勒布朗");
Method m3 = c.getMethod("method3", String.class, int.class);
Object o = m3.invoke(obj, "勒布朗", 37);
System.out.println(o);
Method m4 = c.getDeclaredMethod("function");
m4.setAccessible(true);
m4.invoke(obj);
}
}
练习
- 有一个ArrayList< Integer >集合,在此集合中添加字符串数据(越过泛型检查)
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
ArrayList<Integer> array = new ArrayList<>();
Class<? extends ArrayList> c = array.getClass();
Method m = c.getMethod("add", Object.class);
m.invoke(array, "Hello");
m.invoke(array, "World");
m.invoke(array, "Java");
System.out.println(array);
}
}
className=Demo3.Student
methodName=study
public class Student {
public void study() {
System.out.println("好好学习,天天向上");
}
}
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
public class ReflectTest02 {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Properties prop = new Properties();
FileReader fr = new FileReader("src\\Demo3\\class.txt");
prop.load(fr);
fr.close();
String className = prop.getProperty("className");
String methodName = prop.getProperty("methodName");
Class<?> c = Class.forName(className);
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
Method m = c.getMethod(methodName);
m.invoke(obj);
}
}
模块化
随着Java语言的发展,语言逐渐臃肿;无论是运行一个大型的软件系统,还是运行一个小程序,即使程序只需要使用Java的部分核心功能,JVM也要加载整个JRE环境。为了让Java实现轻量化,Java 9正式推出了模块化系统;Java被拆分为N多个模块,并允许Java程序可以根据选择加载程序必须的Java模块,这样就可以让Java以轻量化的方式来运行
-
创建模块 -
在模块的src目录下新建一个名为module-info.java的描述性文件,该文件专门定义模块名、访问权限、模块依赖等信息 描述性文件中使用模块导出和模块依赖进行配置并使用 -
模块中所有未导出的包都是模块私有的,他们是不能在模块之外被访问的 模块导出格式:export 包名; -
一个模块要访问其他的模块,必须明确指定依赖哪些模块,未指定依赖的模块不能访问 模块依赖格式:requires 模块名; 注意:写模块名报错,需要按下Alt+Enter提示,然后选择模块依赖 -
在模块下使用依赖模块的内容
服务:从Java6开始,Java就提供了一种服务机制,允许服务提供者和服务使用者之间完成解耦;简单说,就是服务使用者只面向接口编程,但不清楚服务提供者的实现类
Java 9的模块化系统进一步简化了Java的服务机制。Java 9允许将服务接口定义在一个模块中,并使用uses语句来声明该服务接口,然后针对该服务接口提供不同的服务实现类,这些服务实现类可以分布在不同的模块中,服务实现模块则使用provides语句为接口指定实现类,服务使用者只需要面向接口编程即可
|