1. 反射
1.1 反射的概念
反射是一种在运行状态下就能知道任意一个类的属性和方法(包括私有的)的一种机制.对于任意一个对象,都能够调用它的任意方法和属性,既然能拿到那么,我们就可以修改部分类型信息;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射(reflection)机制.
关于运行时和编译时
java程序中许多对象在运行时会出现两种类型:运行时类型(RTTI)和编译时类型,例如Person p = new Student();这句代码中p在编译时类型为Person,运行时类型为Student。程序需要在运行时发现对象和类的真实信息。而通过使用反射程序就能判断出该对象和类属于哪些类.
1.2 关于反射相关的类
类名 | 用途 |
Class类 | 代表类的实体,在运行的Java应用程序中表示类和接口 |
Field类 | 代表类的成员变量/类的属性 |
Method类 | 代表类的方法 |
Constructor类 | 代表类的构造方法 |
1.2.1 Class类
Java文件在被编译之后生成了.class文件 , JVM把.class解析为一个对象,这个对象就是Class类对象(java.lang.Class). 然后当程序在运行的时候,每个java文件就最终变成了Class类对象的一个实例.我们可以使用java的反射机制去使用这个实例,可以去修改类里面的属性和方法.
1.2.1 Class类相关的方法
1> 常用的获得类相关的方法
注意: 加了Declared就说明公有私有都能获得
方法 | 用途 |
getClassLoader() | 获得类的加载器 |
getDeclaredClasses() | 返回一个数组,数组中包含整个类所有类(内部类、匿名类、成员类等),包括公共、保护、默认(包私有)和私有类,但不包括从父类或接口继承的类. |
forName(String className) | 加载并返回对应的 Class 对象 |
newInstance() | 创建类的实例 |
getName() | 获得类的完整路径名字 |
2> 常用获得类中属性相关的方法(返回值为Field相关)
方法 | 用途 |
getField(String name) | 获得public修饰的属性对象 |
getFields() | 获得 |
getDeclaredField(Sting name) | 获得某个属性对象(包括私有的) |
getDeclaredFields() | 获得Field对象数组(包含私有的) |
3> 获得类总构造器相关的方法
方法 | 用途 |
getConstructor(Class...<?> parameterTypes) | 用于获取指定参数类型的公共构造方法 |
getMethods() | 返回Method数组(public修饰的方法对象) |
getDeclaredMethod(String name,class<?>parameterTypes) | 用于获得指定参数类型的任意构造方法 |
getDeclaredMethods() | 返回Method数组(任意修饰符修饰的方法对象) |
4> 获得类中的其他普通方法(返回Method)
方法 | 用途 |
getMethod(String name,class...<?> parameterTypes) | 用于获得指定参数类型的public修饰的普通方法 |
getMethods() | 用于获得Method对象数组 |
getDeclaredMethod(Sting name,Class...<?> parameterTypes) | 用于获得各种修饰符修饰的普通方法 |
getDeclaredMethods() | 用于获得Method对象数组(任意修饰符修饰) |
1.3 反射代码实例
1.3.1 获得Class对象的三种方法
在反射前,我们首先要拿到当前需要反射的类的Class对象,然后通过Class对象的核心方法,达到反射目的,也就是: 在运行状态中,对于任意一个类,我们都能知道整个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性,那么都能拿到了,我们也能对信息进行修改.
1> 使用Class.forName("类的全路径名") 前提: 已明确类的全路径名.
2> 使用.class方法 前提: 仅适合编译前就晓得要操作的Class
3> 使用类对象的getClass()方法
示例代码:
package 反射_枚举_lambda表达式.反射;
import java.lang.reflect.Method;
class Student {
//私有属性name
private String name = "bit";
//公有属性age
public int age = 18;
//不带参数的构造方法
public Student(){
System.out.println("Student()");
}
private Student(String name,int age) {//这个构造方法是私有的,因此获得它需要setAccessible这个方法
this.name = name;
this.age = age;
System.out.println("Student(String,name)");
}
private void eat(){
System.out.println("i am eat");
}
public void sleep(){
System.out.println("i am pig");
}
private void function(String str) {
System.out.println(str);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
//set不管是TreeSet还是HashSet都是有序的
// TODO 1. 获得class对象
//class对象只有一个
//一般用1>,2>
// 1> class.forName
Class<?> c1 = null;
try {
c1 = Class.forName("反射_枚举_lambda表达式.反射.Student");//里面放要反射的类所存放的路径
}catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 2> .class方法
Class<?> c2 = Student.class;
// 3> 使用类对象的getClass()方法
Student student = new Student();
Class<?> c3 = student.getClass();
System.out.println(c1 == c2);
System.out.println(c1 == c3);
}
}
注意
1> 说明任意一个类都含有一个隐含的静态成员变量class.
2> 通过Class对象的ForName()来获取class对象,会抛出ClassNotFoundException异常
3> 这说明一个类在JVM中指挥有一个Class实例,所有我们用三种方式获得的Class对象都是同一个.
在进行获取方法属性对象之前,我们先介绍一下我们自己写的Student类
1.3.2 创建对象
具体代码:
package 反射_枚举_lambda表达式.反射;
public class Test1 {
public static void reflectNewInstance() {
//获得Class对象
Class<?> classStudent = null;
try {
classStudent = Class.forName("反射_枚举_lambda表达式.反射.Student");//存放类的全路径
//class类的NewInstance,产生类对象实例
Student student = (Student) classStudent.newInstance();//擦除机制,向下转型,调用不带参数的构造方法
System.out.println(student);//打印toSting里面的信息
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
reflectNewInstance();
}
}
运行结果:
1.3.3 获得私有构造方法
具体代码:
package 反射_枚举_lambda表达式.反射;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Test1 {
public static void reflectPrivateConstructor() {
//获得Class对象
Class<?> classStudent = null;
try{
classStudent = Class.forName("反射_枚举_lambda表达式.反射.Student");
//获得构造方法
Constructor<?> constructor = classStudent.getDeclaredConstructor(String.class,int.class);//获得的是俩个参数的构造方法
Constructor<?> constructor1 = classStudent.getDeclaredConstructor(String.class);//获得的是一个参数的构造方法
constructor1.setAccessible(true);
//开关,相当于需要你签个字同意获取构造方法
constructor.setAccessible(true);
//创建class类的实例
Student student = (Student) constructor.newInstance("xiaohei",18);
System.out.println(student);
student = (Student) constructor1.newInstance("小黑");
System.out.println(student);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
reflectPrivateConstructor();
}
}
运行结果:
1.3.4 获得私有属性
具体代码:
package 反射_枚举_lambda表达式.反射;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class Test1 {
//反射私有属性/字段
public static void reflectPrivateField() {
Class<?> classStudent = null;
try{
classStudent = Class.forName("反射_枚举_lambda表达式.反射.Student");
Field field = classStudent.getDeclaredField("name");//私有
Field field1 = classStudent.getDeclaredField("age");//公有,公有的直接改即可
//修改的是私有的所以我们需要获得允许
field.setAccessible(true);
//class类的NewInstance,产生类对象实例
Student student = (Student) classStudent.newInstance();
//修改字段值
field.set(student,"小白");
System.out.println(student);
field1.set(student,10);
System.out.println(student);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
reflectPrivateField();
}
}
1.3.5 获得私有方法
直接通过Class类来调用私有方法进行使用
具体代码:
package 反射_枚举_lambda表达式.反射;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
public class Test1 {
public static void reflectPrivateMethod() {
//1> 获取class对象
Class<?> classStudent = null;
try{
classStudent = Class.forName("反射_枚举_lambda表达式.反射.Student");
Method method = classStudent.getDeclaredMethod("function", String.class);//方法名字,参数类型
//设置可以被修改
method.setAccessible(true);
//修改哪个对象(类)的私有方法
Student student = (Student) classStudent.newInstance();//擦除机制,向下转型
method.invoke(student,"我是修改了的值");//
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
reflectPrivateMethod();
}
}
运行结果
2. 枚举
2.1 枚举的概念
枚举是一种组织常量的数据类型.
优点: 把常量组织起来统一进行管理
应用: 错误状态码,颜色的划分,状态机...
写法:
public enum TestEnum { RED,BLACK,GREEN;}//里面放的是枚举对象
2.2 枚举的应用
1> switch-case
举例:
package 反射_枚举_lambda表达式.枚举;
public enum TestEnum {//默认继承于枚举类 Enum(双击shift去找)
//枚举对象
RED,BLACK,GREEN;
//1> 枚举一般和switch配套使用
public static void main1(String[] args) {
TestEnum color = RED;
switch (color) {
case RED:
System.out.println("RED");
break;
case GREEN:
System.out.println("GREEN");
break;
case BLACK:
System.out.println("BLACK");
break;
default:
System.out.println("其他情况");
break;
}
}
}
运行结果:
2> 常用方法
方法名称 | 描述 |
values() | 以数组形式返回枚举类型的所有成员 |
ordinal() | 获取枚举成员的索引位置 |
valueOf() | 将普通字符串转化为枚举实例 |
conpareTo() | 比较俩个枚举成员在定义时的顺序 |
举例:
package 反射_枚举_lambda表达式.枚举;
public enum TestEnum {//默认继承于枚举类 Enum(双击shift去找)
//枚举对象
RED(1,"红色"),BLACK(2,"黑色"),GREEN(3,"绿色");
private int ordinal;
private String color;
private TestEnum(int ordinal,String color) {//这些构造方法和枚举对象是对应的
//枚举当中构造方法默认是私有的
this.ordinal = ordinal;
this.color = color;
}
public static void main(String[] args) {
//2> 枚举常用方法
//通过数组进行返回
TestEnum[] testEnums = TestEnum.values();//返回枚举数组
for (int i = 0; i < testEnums.length; i++) {
System.out.println(testEnums[i].ordinal());//返回索引值
}
System.out.println("=====");
TestEnum v = TestEnum.valueOf("BLACK");//把普通字符转换为枚举实例
System.out.println(v);
System.out.println("=====");
System.out.println(RED.compareTo(BLACK));//比较俩个枚举成员在定义时的顺序
}
}
}
运行结果:
2.3 枚举是不能反射的
注意:
1> 枚举的构造方法是私有的
2> 枚举默认继承于Enum类
留一个问题: 用枚举来实现单例模式
3. Lambda表达式
3.1 概念
Lambda表达式是一种代替功能接口的一种表达式.它相当于用另一种更加简洁的方式来表示方法.(参数链表+ 使用参数列表的主体)
基本语法:
(parameters) -> expression或者(parameters)->{statements;}
构成Lambda表达式的三个部分:
1> paramaters: 相当于方法中的形参列表(可以省去类型,jvm会自己推断出来).当只有一个推断类型的时候可以省去"()".
2> -> :这个是被应用于的意思
3> 方法体: 相当于函数式接口里面方法的实现,也就是方法体,可以返回值也可以不反回值.
我们来具体分析几个式子
式子1: () -> 2
含义: 不需要参数,返回值为2
式子2: x -> 2 * x
含义: 接受一个参数(省去"()")并返回2倍的值
式子3: (x,y) -> x + y
含义: 接受俩个参数,并返回他们的和
式子4: (int x,int y) -> x*y
含义: 接受俩个int类型的整数,并返回他们的乘积
式子5: (String s) -> System.out.print(s)
含义: 接受一个String对象,并且再控制台里面打印,无返回值
3.2 函数式接口的概念
在正式学习Lambda表达式之前,我们要先了解什么式函数式接口: 一个接口有且只有一个抽象方法.一般都会在这个接口上声明@FunctionInterface.
举例:
@FunctionalInterface interface NoParameterNoReturn{ //函数式接口只有一个抽象方法 void test(); // default void test2() { // System.out.println("default默认方法可以有具体实现"); // } }
3.3 Lambda表达式的基本使用
Lambda可以理解为:Lambda就是匿名内部类的简化,实际上是创建了一个类,实现了接口,重写了接口的方法 。
前置功能性接口介绍
接下来每一个函数式接口我都会用俩种方法来进行演示
1. 无参数无返回值的函数式接口
1> 使用匿名内部类重写test()方法
2> 使用Lambda表达式重写test()方法
2. 一个参数无返回值的函数式接口
1> 使用匿名内部类重写test()方法
2> 使用Lambda表达式重写test()方法
3. 多个参数无返回值的函数式接口
1> 使用匿名内部类重写test()方法
2> 使用Lambda表达式重写test()方法
4. 无参数有返回值的函数式接口
1> 使用匿名内部类重写test()方法
2> 使用Lambda表达式重写test()方法
5. 有一个参数有返回值的函数式接口
1> 使用匿名内部类重写test()方法
2> 使用Lambda表达式重写test()方法
6. 有返回值多参数的函数式接口
1> 使用匿名内部类重写test()方法
2> 使用Lambda表达式重写test()方法
一些省略写法
1. 参数类型可以省略,如果需要省略,每个参数的类型都要省略。
2. 参数的小括号里面只有一个参数,那么小括号可以省略
3. 如果方法体当中只有一句代码,那么大括号可以省略
4. 如果方法体中只有一条语句,且是return语句,那么大括号可以省略,且去掉return关键字。
注意: 有一个概念叫做变量捕获: 只要被匿名内部类里面的函数所使用的变量就不能被修改lambda也如此.
因此在匿名内部类里面所使用的变量:1. 要么是常量 2.要么是没有被修改过的变量
例子:
3.4 lambda在集合中的使用
3.4.1 Collection接口
1> for-each的使用
2> sort的使用
3.4.2 Map接口
1> for-each的使用
整体代码:
package 反射_枚举_lambda表达式.lambda表达式;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
//无返回值无参数
@FunctionalInterface
interface NoParameterNoReturn1{
//函数式接口只有一个抽象方法
void test();
}
//无返回值一个参数
@FunctionalInterface
interface OneParameterNoReturn1 {
void test(int a);
} //无返回值多个参数
@FunctionalInterface
interface MoreParameterNoReturn1 {
void test(int a,int b);
} //有返回值无参数
@FunctionalInterface
interface NoParameterReturn1 {
int test();
} //有返回值一个参数
@FunctionalInterface
interface OneParameterReturn1 {
int test(int a);
} //有返回值多参数
@FunctionalInterface
interface MoreParameterReturn1 {
int test(int a,int b);
}
public class Test1 {
public static void main(String[] args) {
HashMap<Integer,String> map = new HashMap<>();
map.put(1,"hello");
map.put(2,"xiaobai");
map.put(3,"hello");
map.put(4,"lambda");
map.forEach(new BiConsumer<Integer, String>() {
@Override
public void accept(Integer integer, String s) {
System.out.println("key: " + integer + "value: " + s);
}
});
//使用Lambda表达式
map.forEach((key,val)-> System.out.println("key: " + key + "value: " +val));
}
public static void main8(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("hello");
list.add("xiaobai");
list.add("hello");
list.add("lambda");
//使用匿名内部类
// list.sort(new Comparator<String>() {
// @Override
// public int compare(String o1, String o2) {
// return o1.compareTo(o2);
// }
// });
// System.out.println(list);
//使用Lambda表达式
list.sort((o1,o2)->o1.compareTo(o2));
list.forEach((s)-> System.out.println(s));
}
public static void main7(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("hello");
list.add("xiaobai");
list.add("hello");
list.add("lambda");
//Collection中 for-each的使用
//使用匿名内部类的写法
list.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s + " ");
}
});
//使用Lambda表达式
list.forEach((s)-> System.out.println(s));//用Lambda后真的简洁
}
public static void main6(String[] args) {
//TODO 有返回值多参数的函数式接口
MoreParameterReturn1 moreParameterReturn1 = new MoreParameterReturn1() {
@Override
public int test(int a, int b) {
System.out.println("我是有返回值多参数的,使用匿名内部类重写的test()方法");
return a+b;
}
};
int a = moreParameterReturn1.test(1,2);
System.out.println(a);
MoreParameterReturn1 moreParameterReturn11 = (b,c) -> {
System.out.println("我是有返回值多参数的,使用Lambda表达式重写的test()方法");
return b*c;
};
int b = moreParameterReturn11.test(3,4);
System.out.println(b);
}
public static void main5(String[] args) {
OneParameterReturn1 oneParameterReturn1 = new OneParameterReturn1() {
@Override
public int test(int a) {
System.out.println("我是有一个参数有返回值的,使用匿名内部类重写的test()方法");
return a+1;
}
};
System.out.println(oneParameterReturn1.test(2));
OneParameterReturn1 oneParameterNoReturn11 = (x) -> {
System.out.println("我是有一个参数有返回值的,使用Lambda表达式重写的test()方法");
return x+2;
};
int b = oneParameterNoReturn11.test(22);
System.out.println(b);
}
public static void main4(String[] args) {
//TODO 无参数有返回值的函数式接口
NoParameterReturn1 noParameterReturn1 = new NoParameterReturn1() {
@Override
public int test() {
System.out.println("我是有返回值无参数的,使用匿名内部类重写的test()方法");
return 89;
}
};
int a = noParameterReturn1.test();
System.out.println(a);
NoParameterReturn1 noParameterReturn11 = ()->{
System.out.println("我是有返回值无参数的,使用Lambda表达式重写的test()方法");
return 23;
};
int b = noParameterReturn11.test();
System.out.println(b);
}
public static void main3(String[] args) {
//TODO 多个参数无返回值的函数式接口
MoreParameterNoReturn moreParameterNoReturn1 = (a,b)->{
System.out.println(a + " " + b +"我是多个参数没有返回值,使用Lambda表达式重写的test()方法");
};
moreParameterNoReturn1.test(4,6);
}
public static void main2(String[] args) {
//TODO 一个参数无返回值的函数式接口
//1> 匿名函数内部类形式
OneParameterNoReturn1 oneParameterNoReturn1 = new OneParameterNoReturn1() {
@Override
public void test(int a) {
System.out.println("我是一个参数没有返回值,使用匿名内部类重写的test()方法 "+ a);
}
};
oneParameterNoReturn1.test(23);
//2> 使用Lambda表达式
OneParameterNoReturn1 oneParameterNoReturn11 = (a) ->{
System.out.println("我是一个参数没有返回值,使用Lambda表达式重写的test()方法 "+ a);
};
oneParameterNoReturn11.test(14);
}
public static void main1(String[] args) {
//TODO 无参数无返回值的函数式接口
//1> 匿名内部类形式
NoParameterNoReturn1 noParameterNoReturn1 = new NoParameterNoReturn1() {
@Override
public void test() {
System.out.println("我是没有参数无返回值,用匿名内部类重写的test()方法");
}
};
noParameterNoReturn1.test();
//2> Lambda表达式形式
NoParameterNoReturn1 noParameterNoReturn11 = () -> {
System.out.println("我是没有参数无返回值,使用Lambda表达式重写的test()方法");
};
noParameterNoReturn11.test();
}
}
总结:
Lambda表达式最大的优点就是代码会变的非常的简洁,但是于此同时的缺点就是代码可读性差,补容易进行调试.