匿名类
- 匿名类(Anonymous Class)
- 匿名类的使用注意
- 匿名类 - 代码传递 - 测试代码运行时间的工具类
- 匿名类 - 回调 - 简易网络请求
- 匿名类 - 过滤器 - 获取目录下的所有文件
- 排序 Arrays.sort()
- Lambda 表达式(Lambda Expression)
- Lambda 的使用注意、匿名类 vs Lambda
- 方法引用(Method Reference)
- 常用函数式接口
- Supplier:优化不一定执行的代码
- Consumer:接收一个值决定要做什么
- Predicate:让过滤条件更灵活
- Function:类型转换
- 综合实例(Iterable、Predicate、Function、Consumer、Stream)
匿名类(Anonymous Class)
当接口、抽象类的实现类,在整个项目中只用过一次,可以考虑使用匿名类
例如下面这个例子:
public interface Eatable {
String name();
int energy();
}
public class Person {
public void eat(Eatable a) {
System.out.println("eat - " + a.name() + " -" + a.energy());
}
}
public static void main(String[] args) {
Person person = new Person();
person.eat(new Eatable() { // 匿名内部类
@Override
public String name() {
return "Apple";
}
@Override
public int energy() {
return 100;
}
}); // eat - Apple -100
Eatable beef = new Eatable() {
@Override
public String name() {
return "Beef";
}
@Override
public int energy() {
return 200;
}
};
person.eat(beef); // eat - beef -200
}
匿名类的使用注意
匿名类不能定义除 编译时常量(static int final a = 1
) 以外的任何 static 成员
匿名类只能访问 final
或者 有效final
的局部变量
“有效 final 的局部变量” 就是指只被赋值过一次的局部变量
匿名类可以直接访问外部类中的所有成员(即使被声明为 private)
匿名类只有在实例相关的代码块中使用,才能直接访问外部类中的实例成员(实例变量、实例方法)
匿名类不能自定义构造方法,但可以有初始化块
匿名类的常见用途:
- 代码传递
- 过滤器
- 回调
匿名类 - 代码传递 - 测试代码运行时间的工具类
public class Times {
public interface Block {
void execute();
}
public static void test(Block block) {
long begin = System.currentTimeMillis();
block.execute();
long end = System.currentTimeMillis();
long duration = end - begin;
System.out.println("花费时间" + duration + "ms");
}
}
插一句,字符串拼接其实相当耗时间
public static void main(String[] args) {
Times.test(new Block() {
@Override
public void execute() {
String str = "";
for (int i = 0; i < 10000; i++) {
// 字符串拼接极其耗时间, 因为每次都相当于创建新的对象
str += i;
}
}
});
}
匿名类 - 回调 - 简易网络请求
下面模拟一个简易网络请求代码来体现回调的过程,具体看注释
public class Networks {
public interface Block {
void success(Object response);
void failure();
}
public static void get(String url, Block callBack) {
// 1. 根据url发送一个异步请求(开启一条子线程)
// ......
// 2. 请求完毕后
boolean result = url.contains("666") ? true : false;
if (result) {
Object response = null;
callBack.success(response);
} else {
callBack.failure();
}
}
public static void main(String[] args) {
// 传入的网络请求只有满足某些条件时会返回成功, 其他情况会失败
// 但是发送请求时将成功要做的事的和失败要做的事都写好了
// 等到判断条件是否满足以后再回来调用, 这就是回调
Networks.get("http://xxx.com?pwd=666", new Block() {
@Override
public void success(Object response) {
System.out.println("请求成功");
}
@Override
public void failure() {
System.out.println("请求失败");
}
});
}
}
匿名类 - 过滤器 - 获取目录下的所有文件
通过一个获取目录下所有文件的小案例,体会下匿名类用作过滤器的遍历
public class Files {
public interface Filter {
boolean accpet(String filename);
}
// 需要传入一个Filter过滤器, 用来自定义要返回满足哪些条件的文件名
public static String[] getAllFileNames(String dir, Filter filter) {
// 1.先获取 dir 文件夹下的所有文件名
String[] allFilesnames = {};
// 2.进行过滤
for (String filename : allFilesnames) {
if (filter.accpet(filename)) {
// 将满足条件的文件名装起来
}
}
// 返回所有装起来的文件名(满足条件的文件名)
return null;
}
public static void main(String[] args) {
// 获取F盘下所有.java文件
Files.getAllFileNames("F:", new Filter() {
@Override
public boolean accpet(String filename) {
if (filename.endsWith(".java")) return true;
return false;
}
});
// 获取F盘下所有名字中带hello的文件
Files.getAllFileNames("F:", new Filter() {
@Override
public boolean accpet(String filename) {
if (filename.contains("hello")) return true;
return false;
}
});
}
}
排序 Arrays.sort()
可以使用 JDK 自带的 java.util.Arrays
类对数组进行排序;
-
Arrays.sort
默认是升序排列,可以通过比较器Comparator
改变次序
Integer[] array = { 33, 22, 11, 77, 66, 99 };
Arrays.sort(array);
System.out.println(Arrays.toString(array));
// [33, 22, 11, 77, 66, 99]
Arrays.sort(array, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
System.out.println(Arrays.toString(array));
// [11, 22, 33, 66, 77, 99]
Lambda 表达式(Lambda Expression)
Lambda 表达式是 Java 8 开始才有的语法
- 函数式接口(Functional Interface):只包含 1 个抽象方法的接口
可以在接口上面加上@FunctionalInterface
注解,表示它是一个函数式接口 - 当匿名类实现的是函数式接口时,可以使用 Lambda 表达式进行简化
@FunctionalInterface
public interface Testable {
void test(int v);
}
一些注意点:
- 参数列表可以省略参数类型
- 当只有一条语句时,可以省略大括号、分号、
return
- 当只有一个参数时:可以省略小括号
- 当没有参数时:不能省略小括号
Lambda 表达式示例:
public interface Caculator {
int caculate(int v1, int v2);
}
static void execute(int v1, int v2, Caculator c) {
System.out.println(c.caculate(v1, v2));
}
public static void main(String[] args) {
execute(10, 20, (int v1, int v2) -> {
return v1 + v2; // 20
});
execute(11, 22, (v1, v2) -> v1 + v2); // 20
}
Lambda 的使用注意、匿名类 vs Lambda
Lambda 的使用注意:
- Lambda 只能访问
final
或者 有效 final 的局部变量 - Lambda 没有引入新的作用域
public class OuterClass {
@FunctionalInterface
public interface Testable {
void test(int v);
}
private int age = 1;
public class InnerClass {
private int age = 2;
void inner() {
// int v = 4; // error, Lambda 没有引入新的作用域, 重复定义变量
Testable t1 = v -> {
System.out.println(v); // 3
System.out.println(age); // 2
System.out.println(this.age); // 2
System.out.println(InnerClass.this.age); // 2
System.out.println(OuterClass.this.age); // 1
};
t1.test(3);
Testable t2 = new Testable() {
@Override
public void test(int v) {
System.out.println(v); // 3
System.out.println(age); // 2
// System.out.println(this.age); // error
System.out.println(InnerClass.this.age); // 2
System.out.println(OuterClass.this.age); // 1
}
};
t2.test(3);
}
}
public static void main(String[] args) {
new OuterClass().new InnerClass().inner();
}
}
方法引用(Method Reference)
如果 Lambda 中的内容仅仅是调用某个方法,可以使用方法引用(Method Reference)来简化
引用静态方法:
@FunctionalInterface
public interface Testable {
int test(int v1, int v2);
}
public static void main(String[] args) {
Testable t1 = (v1, v2) -> Math.max(v1, v2);
System.out.println(t1.test(10, 20)); // 20
Testable t2 = Math::max; // 引用静态方法
System.out.println(t2.test(10, 20)); // 20
}
引用特定对象的实例方法:
@FunctionalInterface
public interface Testable {
void test(int v);
}
public class Person {
public void setAge(int age) {
System.out.println("Person - setAge - " + age);
}
static void execute(Testable t, int v) {
t.test(v);
}
public static void main(String[] args) {
// 10
execute(v -> System.out.println(v), 10);
// 20
execute(System.out::println, 20); // 引用特定对象的实例方法
// Person - setAge - 10
execute(v -> new Person().setAge(v), 10);
// Person - setAge - 20
execute(new Person()::setAge, 20); // 引用特定对象的实例方法
}
}
引用特定类型的任意对象的实例方法:
String[] strings = { "Jack", "james", "Apple", "abort" };
// [abort, Apple, Jack, james]
Arrays.sort(strings, (s1, s2) -> s1.compareToIgnoreCase(s2));
// [abort, Apple, Jack, james]
Arrays.sort(strings, String::compareToIgnoreCase); // 引用特定类型的任意对象的实例方法
引用构造方法:
public class Person {
@FunctionalInterface
public interface Testable {
Object test(int v);
}
public Person(int age) { // 构造方法
System.out.println("Person - " + age);
}
public static void main(String[] args) {
Testable t1 = v -> new Person(v);
// Person - 18
// com.mj.Person@816f27d
System.out.println(t1.test(18));
// Person - 18
// com.mj.Person@6ce253f1
Testable t2 = Person::new; // 引用构造方法
System.out.println(t2.test(18));
}
}
引用数组的构造方法:
@FunctionalInterface
public interface Testable {
Object test(int v);
}
public static void main(String[] args) {
Testable t1 = v -> new int[v];
System.out.println(((int[])t1.test(3)).length);
Testable t2 = int[]::new; // 引用数组的构造方法
System.out.println(((int[])t2.test(3)).length);
引用当前类中定义的实例方法:
public class Person {
public void setAge(int age) {
System.out.println("Person - setAge - " + age);
}
public void show() {
Testable t1 = v1 -> setAge(v);
// Person - setAge - 10
t1.test(10);
Testable t2 = this::setAge; // 引用当前类中定义的实例方法
// Person - setAge - 10
t2.test(10);
}
}
引用父类中定义的实例方法:
@FunctionalInterface
public interface Testable {
void test(int v);
}
class Person {
public void setAge(int age) {
System.out.println("Person - setAge - " + age);
}
}
public class Studen extends Person {
public void setAge(int age) {
System.out.println("Student - setAge - " + age);
}
public void show() {
Testable t1 = v -> super.setAge(v);
// Student - setAge - 10
t1.test(10);
Testable t2 = super::setAge; // 引用父类中定义的实例方法
// Student - setAge - 10
t2.test(10);
}
}
常用函数式接口
java.util.function
包中提供了很多常用的函数式接口:
Supplier
Consumer
Predicate
Function
- …
Supplier:优化不一定执行的代码
有时使用 Supplier 传参,可以避免代码的浪费执行(有必要的时候再执行)
首先看这段例子,getFirstNotEmptyString
返回传入的第一个不为空的字符串,但是有时候会浪费性能,比如第二个字符串是由某个函数构造的,虽然第一个字符串已经确定不为空,但依旧执行了构造第二个字符串的函数。
public static void main(String[] args) {
String s1 = "Jack";
String s2 = "Rose";
System.out.println(getFirstNotEmptyString(s1, makeString()));
}
static String makeString() {
System.out.println("makeString");
return String.format("%d %d %d", 1, 2, 3);
}
// 获取第一个不为空的字符串, 正常写法
static String getFirstNotEmptyString(String s1, String s2) {
if (s1 != null || s1.length() > 0) return s1;
if (s2 != null || s2.length() > 0) return s2;
return null;
}
makeString
Jack
这时候可以用函数式接口 Supplier
来优化:
public static void main(String[] args) {
String s1 = "Jack";
// 使用了函数式接口, 不会执行 makeString()
System.out.println(getFirstNotEmptyString(s1, () -> makeString()));
}
static String makeString() {
System.out.println("makeString");
return String.format("%d %d %d", 1, 2, 3); // 1 2 3
}
// 获取第一个不为空的字符串, 函数式接口
static String getFirstNotEmptyString(String s1, Supplier<String> supplier) {
if (s1 != null || s1.length() > 0) return s1;
String s2 = supplier.get();
if (s2 != null || s2.length() > 0) return s2;
return null;
}
jack
Supplier
并不神秘,我们自己也可以写一个接口达到类似效果,这里只是演示一下,实际中类似需求用 Supplier
即可,不需要自己写。
public static void main(String[] args) {
String s1 = "Jack";
String s2 = "Rose";
// 使用了函数式接口, 不会执行s1 + s2
System.out.println(getFirstNotEmptyString3(s1, () -> s1 + s2));
}
@FunctionalInterface
public interface GetString {
String get();
}
static String getFirstNotEmptyString3(String s1, GetString supplier) {
if (s1 != null || s1.length() > 0) return s1;
String s2 = supplier.get();
if (s2 != null || s2.length() > 0) return s2;
return null;
}
Consumer:接收一个值决定要做什么
Consumer
应用:接收一个值,来决定要做什么
public static void main(String[] args) {
int[] nums = { 11, 33, 44, 88, 77, 66 };
forEach(nums, (n) -> {
String result = ((n & 1) == 0) ? "偶数" : "奇数";
System.out.println(n + "是" + result);
});
}
static void forEach(int[] nums, Consumer<Integer> c) {
if (nums == null || c == null) return;
for (int n : nums) {
c.accept(n);
}
}
11是奇数
33是奇数
44是偶数
88是偶数
77是奇数
66是偶数
andThen
应用:
public static void main(String[] args) {
int[] nums = { 11, 33, 44, 88, 77, 66 };
forEach(nums, (n) -> {
String result = ((n & 1) == 0) ? "偶数" : "奇数";
System.out.println(n + "是" + result);
}, (n) -> {
String result = ((n % 3) == 0) ? "能" : "不能";
System.out.println(n + result + "被3整除");
});
}
static void forEach(int[] nums, Consumer<Integer> c1, Consumer<Integer> c2) {
if (nums == null || c1 == null || c2 == null) return;
for (int n : nums) {
// 相当于先执行c1的任务, 然后执行c2的任务, 执行完后进入下一轮循环
c1.andThen(c2).accept(n);
}
}
11是奇数
11不能被3整除
33是奇数
33能被3整除
44是偶数
44不能被3整除
88是偶数
88不能被3整除
77是奇数
77不能被3整除
66是偶数
66能被3整除
Predicate:让过滤条件更灵活
Predicate
应用:可以不将过滤条件写死,由外面传入更加灵活。
public static void main(String[] args) {
int[] nums = { 11, 33, 44, 88, 77, 66 };
// 过滤条件: 偶数
String str = join(nums, (n) -> (n & 1) == 0);
// 44_88_66
System.out.println(str);
}
// 将数组元素用_拼接成字符串, 并且满足外面传入的过滤条件
static String join(int[] nums, Predicate<Integer> p) {
if (nums == null || p == null) return null;
StringBuilder sb = new StringBuilder();
for (int n : nums) {
if (p.test(n)) {
sb.append(n).append("_");
}
}
sb.deleteCharAt(sb.length() - 1);
return sb.toString();
}
and
、or
、negate
应用:
public static void main(String[] args) {
int[] nums = { 11, 33, 44, 88, 77, 66 };
// 过滤条件: 偶数且能被3整除
String str1 = join1(nums, (n) -> (n & 1) == 0, (n) -> (n % 3) == 0);
// 过滤条件: 偶数或能被3整除
String str2 = join2(nums, (n) -> (n & 1) == 0, (n) -> (n % 3) == 0);
// 过滤条件: 偶数条件取反, 即奇数
String str3 = join3(nums, (n) -> (n & 1) == 0);
System.out.println(str1); // 66
System.out.println(str2); // 33_44_88_66
System.out.println(str3); // 11_33_77
}
// and
static String join1(int[] nums, Predicate<Integer> p1, Predicate<Integer> p2) {
if (nums == null || p1 == null || p2 == null) return null;
StringBuilder sb = new StringBuilder();
for (int n : nums) {
if (p1.and(p2).test(n)) {
sb.append(n).append("_");
}
}
sb.deleteCharAt(sb.length() - 1);
return sb.toString();
}
// or
static String join2(int[] nums, Predicate<Integer> p1, Predicate<Integer> p2) {
if (nums == null || p1 == null || p2 == null) return null;
StringBuilder sb = new StringBuilder();
for (int n : nums) {
if (p1.or(p2).test(n)) {
sb.append(n).append("_");
}
}
sb.deleteCharAt(sb.length() - 1);
return sb.toString();
}
// negate
static String join3(int[] nums, Predicate<Integer> p) {
if (nums == null || p == null) return null;
StringBuilder sb = new StringBuilder();
for (int n : nums) {
if (p.negate().test(n)) {
sb.append(n).append("_");
}
}
sb.deleteCharAt(sb.length() - 1);
return sb.toString();
}
Function:类型转换
Function
应用:类型转换
public static void main(String[] args) {
String[] strs = { "10", "20", "30" };
// 将字符串转为int
int result = sum(strs, Integer::valueOf);
System.out.println(result); // 60
}
static int sum(String[] strs, Function<String, Integer> f) {
if (strs == null || f == null);
int result = 0;
for (String str : strs) {
result += f.apply(str);
}
return result;
}
public static void main(String[] args) {
String[] strs = { "10", "20", "30" };
// 将字符串转为int并模10, 相加
int result = sum(strs, Integer::valueOf, (i) -> i % 10);
System.out.println(result); // 0
}
static int sum(String[] strs, Function<String, Integer> f1, Function<Integer, Integer> f2) {
if (strs == null || f1 == null || f2 == null) return 0;
int result = 0;
for (String str : strs) {
result += f1.andThen(f2).apply(str);
}
return result;
}
综合实例(Iterable、Predicate、Function、Consumer、Stream)
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
是不是有点恐怖。。。
public class Main {
static <X, Y> void process(
Iterable<X> eles,
Predicate<X> tester,
Function<X, Y> mapper,
Consumer<Y> block) {
if (eles == null || tester == null
|| mapper == null || block == null) return;
for (X ele : eles) {
if (!tester.test(ele)) continue;
Y data = mapper.apply(ele);
block.accept(data);
}
}
public static void main(String[] args) {
List<Person> ps = new ArrayList<>();
ps.add(new Person("Jack", 20));
ps.add(new Person("Rose", 10));
ps.add(new Person("Kate", 15));
ps.add(new Person("Larry", 40));
process(ps,
(p) -> p.getAge() >= 15 && p.getAge() <= 25,
Person::toString,
System.out::println);
// Person [name=Jack, age=20]
// Person [name=Kate, age=15]
List<Integer> is = new ArrayList<>();
is.add(11);
is.add(22);
is.add(33);
is.add(44);
process(is,
(i) -> (i & 1) == 0,
(i) -> "test_" + i,
System.out::println);
// test_22
// test_44
// Stream初体验, 有点牛批啊
ps.stream()
.filter((p) -> p.getAge() >= 15 && p.getAge() <= 25)
.map(Person::toString)
.forEach(System.out::println);
// Person [name=Jack, age=20]
// Person [name=Kate, age=15]
is.stream()
.filter((i) -> (i & 1) == 0)
.map((i) -> "test_" + i)
.forEach(System.out::println);
// test_22
// test_44
}
}