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) {
//在主方法里面调用useEatable方法
Eatable e = new Eatablempl();
useEatable(e);
//匿名内部类实现
useEatable(new Eatable() {
@Override
public void eat() {
System.out.println("一天一个苹果,医生远离我");
}
});
//Lambda表达式方法
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) {
//在主方法里面调用useEatable方法
//匿名内部类实现
useFlyable(new Eatable() {
@Override
public void fly(String s) {
System.out.println(s);
//输出参数,参数在方法里面已经写了
System.out.println("2022总冠军");
}
});
//Lambda表达式方法
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) {
//这里用于实现功能
//Lambda表达式方法
useFlyable((int a, int b) -> {
return a + b;//输出30
//return a - b;输出-10
});
}
//定义方法使用接口
private static void useFlyable(Eatable e) {
//方法的参数为接口,实际调用的是接口的实现类对象
int sum = e.add(10, 20);
System.out.println(sum);
}
}
省略模式
- 参数的类型可以省略;但是有多个参数的情况下,不能只省略一个
- 如果参数只有一个,那么小括号可以省略
- 如果代码块的语句只有一条,可以省略大括号和分号,甚至是return
代码演示:
//接口1
public interface Eatable {
int add(int x, int y);
}
//接口2
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));
//如果有return,return也要省略掉
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方法
//Lambda表达式
useEatable((String s) -> {
//将String转换为int
return Integer.parseInt(s);
});
//化简完
useEatable(s -> Integer.parseInt(s));
//方法引用
useEatable(Integer::parseInt);
//Lambda表达式被类方法替代的时候,它的形式参数全部传递给静态方法作为参数
}
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) {
//在主方法中调用useEatable方法
//Lambda表达式
//转大写输出
usePrinter((String s) -> {
System.out.println(s.toUpperCase());
});
//化简完
usePrinter(s -> System.out.println(s.toUpperCase()));
//引用对象的实例方法引用
PringString ps = new PringString();
usePrinter(ps::printUpper);
//Lambda表达式被对象是实例化方法替代的时候,它的形式参数全部传递给该方法作为参数
}
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) {
//在主方法中调用useEatable方法
//Lambda表达式
useMyString((String s, int x, int y) -> {
//截取字符串方法,xy为截取长度
return s.substring(x, y);
});
//化简完
useMyString((s, x, y) -> s.substring(x, y));
//引用类的实例方法引用
useMyString(String::substring);
//Lambda表达式被类的实例方法替代的时候,第一个参数作为调用者,后面的参数全部传递给该方法作为参数
}
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) {
//在主方法中调用useEatable方法
//Lambda表达式
useStudentBuider((String name, int age) -> {
Student s = new Student(name, age);
return s;
});
//化简完
useStudentBuider(((name, age) -> new Student(name, age)));
//引用构造器
useStudentBuider(Student::new);
//Lambda表达式被构造器替代的时候,它的形式参数全部传递给构造器作为参数
}
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() + "线程启动了"));
}
//Runnable是一个函数式接口
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);
}
//按长度排序
//Comparator是一个函数式接口
private static Comparator<String> getComparator() {
//匿名内部类实现
return new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s1.length() - s2.length();
}
};
//Lambda表达式实现
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);
}
//返回一个int数组的最大值
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()));
//方法2调用
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.accept(name);
con2.accept(name);*/
//default Consumer<T> andThen(Consumer after)方法
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);
}
//boolean test(T t):对给定的参数进行判断(判断逻辑由Lambda表达式实现),返回一个布尔值
private static boolean checkString(String s, Predicate<String> pre) {
//正常判断
return pre.test(s);
//对结果进行逻辑非
//default Predicate<T> negate():返回一个逻辑的否定,对应逻辑非
return pre.negate().test(s);
}
private static boolean checkString1(String s, Predicate<String> pre1, Predicate<String> pre2) {
//default Predicate<T> and(Predicate other):返回一个组合判断,对应短路与
return pre1.and(pre2).test(s);
//default Predicate<T> or(Predicate other):返回一个组合判断,对应短路或
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"};
//筛选姓名长度大于3,年龄大于33的人
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);
//输出:勒布朗,37
}
}
//通过Preaicate接口的拼装将符合要求的字符串筛选到集合Arraylist中
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));
}
//R apply(T t):将此函数应用与给定的参数
//定义一个方法,把字符串转换为int类型,在控制台输出
private static void convert(String s, Function<String, Integer> fun) {
Integer i = fun.apply(s);
System.out.println(i);
}
//定义一个方法,把一个int类型的数据加上一个整数之后,转为字符串在控制台输出
private static void convert1(int i, Function<Integer, String> fun1) {
String s = fun1.apply(i);
System.out.println(s);
}
//default <V> Function andThen(Function after):返回一个组合函数,首先将该函数应用与输入,然后将after函数应用与结果
//定义一个方法,把一个字符串转换为int类型,把int类型的数据加上一个整数之后,转为字符串在控制台输出
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);
}
//定义一个方法,字符串截取得到数字年龄部分;年龄字符串转int类型,int+70得到int结果
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) {
//Collection体系的集合可以使用默认方法Stream()生成流;default Stream<E> stream():返回以此集合为源的顺序Stream
List<String> list = new ArrayList<>();
Stream<String> listStream = list.stream();
Set<String> set = new HashSet<>();
Stream<String> setStream = set.stream();
//Map体系的集合不能直接生成流,需要间接生成流
Map<String, Integer> map = new HashMap<>();
//keySet()方法转为Set集合使用,得到键的集合
Stream<String> keyStream = map.keySet().stream();
//values()方法转为Set集合使用,得到值的集合
Stream<Integer> valueStream = map.values().stream();
//entrySet()方法转为Set集合使用,获得所有键值对对象
Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();
//数组可以通过Stream接口的静态方法 of(T…values) 生成流
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方法
-
Stream<T> filter(Predicate predicate)
:用于对流中的数据进行过滤predicate接口中的方法
boolean test(T t)
:对给定的参数进行判断,返回一个布尔值
代码演示:
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("麦基");
//Stream<T> filter(Predicate predicate):用于对流中的数据进行过滤
//startsWith()方法用于检测字符串是否以指定的前缀开始
//forEach()为终结方式
//把list集合中以勒开头的元素在控制台输出
list.stream().filter(s -> s.startsWith("勒")).forEach(System.out::println);
//把list集合长度为3的元素在控制台输出
list.stream().filter(s -> s.length() == 3).forEach(System.out::println);
//把list集合中以勒开头,长度为3的元素在控制台输出
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("麦基");
//Stream<T> limit(long maxSize):返回此流中的元素组成的流,截取前指定参数个数的数据
//Stream<T> skip(long n):跳过指定参数个数的数据,返回由该流的剩余元素组成的流
//forEach()为终结方式
//取前3个数据在控制台输出
list.stream().limit(3).forEach(System.out::println);
//把跳过3个元素,把剩下的元素在控制台输出
list.stream().skip(3).forEach(System.out::println);
//把跳过2个元素,把剩下元素中的前2个在控制台输出
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("麦基");
//static<T>Stream<T>concat(Stream a,Stream b):合并a和b两个流为一个流
//Stream<T>distinct():返回由该流的不同元素(根据Object.equals(Object))组成的流
//forEach()为终结方式
//1.取前4个数据组成一个流
Stream<String> s1 = list.stream().limit(4);
//2.跳过两个数据组成一个流
Stream<String> s2 = list.stream().skip(2);
//3.合并1、2得到的流,并把结果在控制台输出
Stream.concat(s1, s2).forEach(System.out::println);
//4.合并1、2得到的流,并把结果在控制台输出,要求字符串元素不能重复
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");
//Stream<T>sorted():返回由此流的元素组成的流,根据自然顺序排序
//Stream<T>sorted(Comparator comparator):返回由该流的元素组成的流,根据提供的Comparator进行排序
//forEach()为终结方式
//按字母顺序把数据在控制台输出
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");
//将集合中的字符串数据转换2为整数之后在控制台输出
//<R> Stream <R> map(Function mapper):返回由给定函数应用于此流的元素的结果组成的流
list.stream().map(s -> Integer.parseInt(s)).forEach(System.out::println);
//方法引用写法
list.stream().map(Integer::parseInt).forEach(System.out::println);
//IntStream mapToInt(ToIntFunction mapper):返回一个InStream其中包含将给定函数应用于此流的元素的结果
list.stream().mapToInt(Integer::parseInt).forEach(System.out::println);
//Interface IntStream中的 int sum()方法:返回此流中元素的总和
//即通过mapToInt()转到IntStream中方法,即可以使用IntStream中的方法了
int result = list.stream().mapToInt(Integer::parseInt).sum();
System.out.println(result);
}
}
Stream流的常见终结操作方法
-
void forEach(Consumer action)
:对此流的每个元素执行操作Consumer接口中的方法 void accept(T t):对给定参数执行此操作
-
long count()
:返回此流中的元素数
代码演示:
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("麦基");
//void forEach(Consumer action):对此流的每个元素执行操作
//long count():返回此流中的元素数
//把集合中的元素在控制台输出
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) {
//创建list集合对象
ArrayList<String> list = new ArrayList<>();
list.add("勒布朗");
list.add("浓眉");
list.add("库里");
list.add("刘宪华");
//得到名字为三个字的流
Stream<String> listStream = list.stream().filter(s -> s.length() == 3);
//把流数据收集到List集合中并遍历
//public static <T> Collector toList():把元素收集到List集合中
List<String> names = listStream.collect(Collectors.toList());
for (String name : names) {
System.out.println(name);
}
//创建Set集合对象
Set<Integer> set = new HashSet<>();
set.add(10);
set.add(20);
set.add(30);
set.add(40);
//得到大于25的流
Stream<Integer> setStream = set.stream().filter(age -> age > 25);
//把流数据收集到Set集合中并遍历
//public static <T> Collector toSet():把元素收集到Set集合中
Set<Integer> ages = setStream.collect(Collectors.toSet());
for (Integer age : ages) {
System.out.println(age);
}
//定义一个字符串数组,每一个字符串数据由姓名和年龄数据组成
String[] strArray = {"勒布朗,37", "浓眉,32", "库里,33", "刘宪华,30"};
//得到字符串大于32的流
Stream<String> arrayStream = Stream.of(strArray).filter(s -> Integer.parseInt(s.split(",")[1]) > 32);
//把流数据收集到Map集合中并遍历,字符串姓名作键,年龄作值
//public static Collector toMap(Function keyMapper,Function valueMapper):把元素收集到Map集合中
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) {
//static ClassLoader getSystemClassLoader():返回用于委派的系统类加载器
ClassLoader c = ClassLoader.getSystemClassLoader();
System.out.println(c);
//输出:jdk.internal.loader.ClassLoaders$AppClassLoader@2437c6dc
//ClassLoader getParent:返回父类加载器进行委派
ClassLoader c1 = c.getParent();
System.out.println(c1);
//输出:jdk.internal.loader.ClassLoaders$PlatformClassLoader@7c30a502
ClassLoader c2 = c1.getParent();
System.out.println(c2);
//输出:null
}
}
反射
概述
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属性来获取该类对应的Class对象;使用简单
Class<Student> c1 = Student.class;
System.out.println(c1);
//输出:class Demo3.Student
Class<Student> c2 = Student.class;
System.out.println(c1 == c2);
//输出:true
//调用对象的getClass()方法,返回该对象所属类对应的Class对象
Student s = new Student();
Class<? extends Student> c3 = s.getClass();
System.out.println(c1 == c3);
//输出:true
//使用Class类中的静态方法forName(String className);灵活性更高
Class<?> c4 = Class.forName("Demo3.Student");
System.out.println(c1 == c4);
//输出:true
}
}
反射获取构造方法
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对象
Class<?> c = Class.forName("Demo3.Student");
//获取构造方法
//Constructor<?>[] getConstructors();返回一个包含构造器对象的数组,构造器对象反映了此类对象所表示的类的所有公共构造函数
Constructor<?>[] cons = c.getConstructors();
for (Constructor con : cons) {
System.out.println(con);
//输出以下两个公共构造方法
//public Demo3.Student(java.lang.String,int,java.lang.String)
//public Demo3.Student()
}
//Constructor<?>[] getDeclaredConstructors();返回构造器对象的数组,构造器对象反映由此类对象表示的类声明的所有构造函数
Constructor<?>[] cons = c.getDeclaredConstructors();
for (Constructor con : cons) {
System.out.println(con);
//输出所有的构造方法
//public Demo3.Student(java.lang.String,int,java.lang.String)
//Demo3.Student(java.lang.String,int)
//private Demo3.Student(java.lang.String)
//public Demo3.Student()
}
//Constructor<T> getConstructor(class<?>... parameterTypes);返回一个构造器对,该对象反映此类对象所表示的类的指定公共构造函数
//Constructor<T> getDeclaredConstructor(class<?>... parameterTypes);返回一个构造器对象,该对象反映此类对象所表示的类或接口的指定构造函数
//参数:要获取的构造方法的参数的个数和数据类型对应的字节码文件对象
//通过类的字节码文件对象得到了构造方法对象;获取无参构造方法
Constructor<?> con = c.getConstructor();
//正常输出某对象内容
/*Student s = new Student();
System.out.println(s);*/
//反射做法:即通过这个构造方法对象里面的方法来创建对象
//T newInstance(Object... initargs)使用此构造器对象表示的构造方法,使用指定的初始化参数创建和初始化构造函数声明类的新实例
Object obj = con.newInstance();
System.out.println(obj);
//输出:Student{name='null', age=0, address='null'};因为重写了toString方法所以这样输出了出来
}
}
练习
-
反射输出学生信息: 三个信息
基本数据类型也可以通过.class得到对应的class类型
//学生类
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对象
Class<?> c = Class.forName("Demo3.Student");
//获取带三个参数的构造方法
Constructor<?> con = c.getConstructor(String.class, int.class, String.class);
//基本数据类型也可以通过.class得到对应的class类型
//用newInstance()实例化对象
Object obj = con.newInstance("勒布朗", 37, "洛杉矶");
System.out.println(obj);
//输出:Student{name='勒布朗', age=37, address='洛杉矶'}
}
}
-
反射输出学生信息: 一个信息
public void setAccessible(boolean flag)
:值为true,则取消访问检查
//学生类
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对象
Class<?> c = Class.forName("Demo3.Student");
//获取带一个参数的构造方法
Constructor<?> con = c.getDeclaredConstructor(String.class);
//正常不能通过私有构造方法创建对象,但是在反射中可以实现(暴力反射)
//public void setAccessible(boolean flag),将此反射对象的accessible标志设置为指示的布尔值.值true表示反射对象在使用时禁止检查Java语言访问控制
con.setAccessible(true);
//用newInstance()实例化对象
Object obj = con.newInstance("勒布朗");
System.out.println(obj);
//输出:Student{name='勒布朗', age=0, address='null'}
}
}
反射获取成员变量
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对象
Class<?> c = Class.forName("Demo3.Student");
//获取成员变量
//Field[] getFields()返回一个包含字段对象的数组,字段对象反映此类对象所表示的类或接口的所有可访问公共字段
Field[] fields = c.getFields();
for (Field field : fields) {
System.out.println(field);
//输出公共成员变量
//public java.lang.String Demo3.Student.address
}
//Field[] getDeclaredFields()返回字段对象的数组,字段对象反映由此类对象表示的类或接口声明的所有字段
Field[] fields = c.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
//输出所有成员变量
//private java.lang.String Demo3.Student.name
//int Demo3.Student.age
//public java.lang.String Demo3.Student.address
}
//Field getField(String name)返回字段对象,该对象反映此类对象表示的类或接口的指定公共成员字段
//Field getDeclaredField(String name)返回字段对象,该对象反映此类对象表示的类或接口的指定声明字段
Field addressField = c.getField("address");
//正常输出某成员变量内容
Student s = new Student();
s.address = "克利夫兰";
System.out.println(s);
//输出:Student{name='null', age=0, address='克利夫兰'}
//反射做法
//获取无参构造方法创建对象
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
//Field提供有关类或接口的单个字段的信息和动态访问
//void set(Object obj, Object value)将指定对象参数上此字段对象表示的字段设置为指定的新值
addressField.set(obj, "克利夫兰");//给obj的成员变量addressField赋值
System.out.println(obj);
//输出:Student{name='null', age=0, address='克利夫兰'}
}
}
练习
//学生类
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对象
Class<?> c = Class.forName("Demo3.Student");
//通过无参构造方法来创建对象
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
System.out.println(obj);
//输出:Student{name='null', age=0, address='null'}
//名字(私有的)
Field nameField = c.getDeclaredField("name");
//暴力反射(取消检查)
nameField.setAccessible(true);
nameField.set(obj, "勒布朗");
System.out.println(obj);
//输出:Student{name='勒布朗', age=0, address='null'}
//年龄
Field ageField = c.getDeclaredField("age");
ageField.setAccessible(true);
ageField.set(obj, 37);
System.out.println(obj);
//输出:Student{name='勒布朗', age=37, address='null'}
//住址
//虽然address是公共的,但仍可以使用以下方法,比较方便
Field addressField = c.getDeclaredField("address");
addressField.setAccessible(true);
addressField.set(obj, "洛杉矶");
System.out.println(obj);
//输出:Student{name='勒布朗', age=37, address='洛杉矶'}
}
}
反射获取成员方法
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对象
Class<?> c = Class.forName("Demo3.Student");
//获取成员方法
//method[] getMethods()返回一个包含方法对象的数组,方法对象反映此类对象所表示的类或接口的所有公共方法.包括由类或接口声明的那些以及从超类和超接口继承的那些
Method[] methods = c.getMethods();
for (Method method : methods) {
System.out.println(method);
//输出所有公共方法;包括由类或接口声明的那些以及从超类和超接口继承的那些
//public java.lang.String Demo3.Student.toString()
//public void Demo3.Student.method1()
//public void Demo3.Student.method2(java.lang.String)
//public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
//public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
//public final void java.lang.Object.wait() throws java.lang.InterruptedException
//public boolean java.lang.Object.equals(java.lang.Object)
//public native int java.lang.Object.hashCode()
//public final native java.lang.Class java.lang.Object.getClass()
//public final native void java.lang.Object.notify()
//public final native void java.lang.Object.notifyAll()
}
//method[] getDeclaredMethods()返回一个包含方法对象的数组,方法对象反映此类对象表示的类或接口的所有已声明方法.包括public,protected,default(package)访问和私有方法,但不包括继承的方法
Method[] methods = c.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method);
//输出所有私有方法;包括public,protected,default(package)访问和私有方法,但不包括继承的方法
//public java.lang.String Demo3.Student.toString()
//private void Demo3.Student.function()
//public void Demo3.Student.method1()
//public void Demo3.Student.method2(java.lang.String)
}
//method getMethod(String name, class<?>... parameterTypes)返回方法对象,该对象反映此类对象表示的类或接口的指定公共成员方法
//method getDeclaredMethod(String name, class<?>... parameterTypes)返回方法对象,该对象反映此类对象表示的类或接口的指定声明方法
//得到成员方法对象
Method m = c.getMethod("method1");
//反射做法
//获取无参构造方法创建对象
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
//Method提供有关类或接口上的单个方法的信息和访问权限
//Object invoke(Object obj, Object... args)在具有指定参数的指定对象上调用此方法对象表示的基础方法
//Object:返回值类型
//obj:调用方法的对象
//args:方法需要的参数
m.invoke(obj);
//输出:method
}
}
练习
//学生类
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对象
Class<?> c = Class.forName("Demo3.Student");
//得到学生对象
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
//调用方法method1
Method m1 = c.getMethod("method1");
m1.invoke(obj);
//输出:method
//调用method2方法并赋值
Method m2 = c.getMethod("method2", String.class);
m2.invoke(obj, "勒布朗");
//输出:method:勒布朗
//调用method3方法并赋值
Method m3 = c.getMethod("method3", String.class, int.class);
Object o = m3.invoke(obj, "勒布朗", 37);
System.out.println(o);
//输出:勒布朗,37
//调用function方法
//Method m4 = c.getMethod("function");//NoSuchMethodException: Demo3.Student.function()
Method m4 = c.getDeclaredMethod("function");
m4.setAccessible(true);
m4.invoke(obj);
//输出:Function
}
}
练习
- 有一个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对象
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);
//输出:[Hello, World, Java]
}
}
- 通过配置文件运行类中的方法
//配置txt文件
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 {
//加载数据(拿到了class.txt的内容)
Properties prop = new Properties();//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);//相当于得到Demo3.Student
//创建无参构造方法
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
Method m = c.getMethod(methodName);//study
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语句为接口指定实现类,服务使用者只需要面向接口编程即可