[数据结构] 反射,枚举与lambda表达式
目录
1. 反射
1.1 概念
1.2 反射相关的类
1.2.1 Class类
1.2.2 Class类相关的方法
1.3 反射的举例
1.3.1 获取Class对象的三种方式
1.3.2 反射的使用
2. 枚举
2.1 概念
2.2 使用
2.3 常用方法
2.4 枚举的构造方法
2.5 枚举与反射
2.6 单例模式
2.6.1 饿汉式
2.6.2 懒汉式
2.6.3 静态内部类实现
2.6.4 枚举实现
3. Lambda表达式
3.1 语法
3.2 函数式接口
3.3 Lambda表达式的基本使用
3.3 语法精简
3.4 变量捕获
3.4.1 匿名内部类的变量捕获
3.4.2 Lambda表达式的变量捕获
3.5 Lambda表达式在集合中的使用
3.5.1 Collection接口
3.5.2 List接口
3.5.3 Map接口
1. 反射
1.1 概念
Java的反射是在运行时检查,访问和修改类的接口,字段和方法的机制。
1.2 反射相关的类
1.2.1 Class类
java程序再运行后生成一个.class文件,而这个文件会被JVM接收将该文件转换为一个对象java.long.Class。而每个java文件都会变成对应一个Class类的实例,我们可以通过反射机制去改变这个实例的属性和方法。
1.2.2 Class类相关的方法
获得类的常用方法(Class类):
获取类中的属性的常用方法(Field类):
获取类中的构造器相关的方法(Constructor类):
获取类中的方法的相关方法(Method类):
获取该类中注解相关的方法:
1.3 反射的举例
1.3.1 获取Class对象的三种方式
当我们想利用反射去修改一个类时,我们首先要获得这个类在JVM中对应的对象,找到这个对象就可以修改该类中的部分方法和字段了。
下面是获取Class对象的三种方式:
方法一:是利用实例化对象的getClass方法。
方法二:是利用利用类名.class获得的。
方法三:利用Class类里面的forName(“类的路径”)方法获得的,路径有包的加入包:包名.类名,这种方法会抛出ClassNotFoundException异常需要处理。
package reflectdemo;class Student {public String name;private int age;public Student(String name, int age) {this.name = name;this.age = age;}public void ReadBook() {System.out.println(name + "正在读书");}private void Sleep() {System.out.println(name + "正在睡觉");}
}
public class TestDemo {public static void main(String[] args) {Student student1 = new Student("张三",18);//方法一:使用类对象的getClass()方法 Class<?> c1 = student1.getClass();//方法二:通过类名.class获得Class<?> c2 = Student.class;//方法三:通过Class类里面的forName静态方法获得Class<?> c3 = null;try {c3 = Class.forName("reflectdemo.Studnet");} catch (ClassNotFoundException e) {throw new RuntimeException(e);}}
}
1.3.2 反射的使用
所有和反射相关的包都存储在java.long.reflect文件下面。
下面是创建Student类的对象,反射构造方法,反射私有的方法,反射私有的属性的代码:
package reflectdemo;import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;public class ReflectClassDemo {//创建Student类的对象public static void reflectNewInstance() {Class<?> stu;//获取类的对象try {stu = Class.forName("reflectdemo.Student");Student student = (Student)stu.newInstance();System.out.println(student);} catch (ClassNotFoundException e) {throw new RuntimeException(e);} catch (InstantiationException e) {throw new RuntimeException(e);} catch (IllegalAccessException e) {throw new RuntimeException(e);}}//反射构造方法public static void reflectPrivateConstructor() {Class<?> stu;try {//获取类的对象stu = Class.forName("reflectdemo.Student");//获取类的构造方法Constructor<?> constructor = stu.getDeclaredConstructor(String.class,int.class);//私有可以访问constructor.setAccessible(true);//修改构造方法Student student = (Student)constructor.newInstance("张三",18);System.out.println(student);} catch (NoSuchMethodException e) {throw new RuntimeException(e);} catch (ClassNotFoundException 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 reflectPrivateField() {Class<?> stu;try {stu = Class.forName("reflectdemo.Student");Field field = stu.getDeclaredField("name");field.setAccessible(true);Student student = (Student)stu.newInstance();field.set(student,"张三");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 reflectPrivateMethod() {Class<?> stu;//获取类对象try {stu = Class.forName("reflectdemo.Student");//获取类的私有方法Method method = stu.getDeclaredMethod("function",String.class);//访问私有method.setAccessible(true);//创建对象Student student = (Student)stu.newInstance();//修改方法参数method.invoke(student,"你好呀");} catch (ClassNotFoundException e) {throw new RuntimeException(e);} catch (InvocationTargetException 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);}}public static void main(String[] args) {//创建class对象reflectNewInstance();//获取构造方法//reflectPrivateConstructor();//获取私有属性//reflectPrivateField();//获取私有方法//reflectPrivateMethod();}
}
2. 枚举
2.1 概念
枚举是用来统一存储和管理常量的。
下面是一个枚举:
public enum TestEnum {RED,YELLOW,BLACK;
}
枚举可以将常量统一存储起来。
自己写的枚举类会默认继承java.long.Enum类。
2.2 使用
1. switch语句
public enum TestEnum {RED,YELLOW,BLACK;public static void main(String[] args) {TestEnum testEnum = TestEnum.RED;switch(testEnum) {case RED:System.out.println("red");break;case YELLOW:System.out.println("yellow");break;case BLACK:System.out.println("black");break;default:break;}}
}
2.3 常用方法
Enum类中常用的方法:
使用实例:
public static void main(String[] args) {TestEnum[] testEnum1 = TestEnum.values();for (int i = 0; i < testEnum1.length; i++) {System.out.print(testEnum1[i] + " ");}System.out.println();System.out.println(RED.ordinal());System.out.println("===============");TestEnum tmp = TestEnum.valueOf("RED");System.out.println(tmp);TestEnum testEnum2 = TestEnum.RED;TestEnum testEnum3 = TestEnum.YELLOW;System.out.println(testEnum2.compareTo(testEnum3));}
2.4 枚举的构造方法
枚举的构造方法是私有的,当枚举有参数是,需要设置构造方法。
public enum TestEnum {RED("red",1),YELLOW("yellow",2),BLACK("black",3);public String color;public int key;TestEnum(String color,int key) {this.color = color;this.key = key;}
}
枚举常量更简单,但是不能继承拓展。
2.5 枚举与反射
我们是否可以通过反射获得枚举的构造方法呢?
我们写了以下代码尝试:
public enum TestEnum {RED("red",1),YELLOW("yellow",2),BLACK("black",3);public String color;public int key;TestEnum(String color,int key) {this.color = color;this.key = key;}public static void reflectPrivateConstructor() {Class<?> tmp;try {tmp = Class.forName("enumdemo.TestEnum");Constructor<?> constructor =tmp.getDeclaredConstructor(String.class,int.class,String.class,int.class);constructor.setAccessible(true);TestEnum testEnum = (TestEnum)constructor.newInstance("绿色",4,"子类参数",88);System.out.println(testEnum);} catch (ClassNotFoundException e) {throw new RuntimeException(e);} catch (InvocationTargetException 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);}}public static void main(String[] args) {reflectPrivateConstructor();}
}
上面代码如果不给构造方法传四个参数的话,运行时会报下面的错误:
因为所有的自定义枚举类都默认继承Enum类,而Enum类中存在一个构造方法:
需要给父类也传入两个参数,而前两个参数是给父类传的参数,后两个参数是给自定义的类传的参数。
此时运行上面代码剩下一个报错:
我们可以查看下报错信息:
Java中会保证枚举类的唯一性和安全性,会检查以下该类是否是枚举类,如果反射访问就会报错。
2.6 单例模式
单例模式是确保每一个对象只有一个实例。
2.6.1 饿汉式
在程序运行时就实例化一个对象,无论是否会使用。
代码如下:
public class Singleton {private static Singleton singleton = new Singleton();private Singleton() {}public Singleton getSingleton() {return singleton;}
}
2.6.2 懒汉式
对象在首次访问时才实例化对象。
代码如下:
public class Singleton {private static Singleton singleton;private Singleton() {}public Singleton getSingleton() {if(singleton == null) {singleton = new Singleton();}return singleton;}
}
2.6.3 静态内部类实现
public class Singleton {private Singleton() {}public static Singleton getSingleton() {return UserSingletonHolder.STR;}//静态内部类private static class UserSingletonHolder {private static final Singleton STR = new Singleton();}
}
2.6.4 枚举实现
public enum NewEnum {INSTANCE;public NewEnum getInstance() {return INSTANCE;}public void print() {System.out.println("枚举实现");}
}
3. Lambda表达式
3.1 语法
基本语法:(parameters)->expression或(parameters)->{statements;}
parameters:是需要传入的参数,当只有一个参数时()可以不写。
-> :被用于的意思
方法体:也就相当于前面传入的参数在方法体里面调用得到的返回值。
举例:
1.不需要参数
() -> 2
2. 一个参数
x -> 2 * x
3. 两个参数
(x,y) -> x+y
4. 两个整型
(int x, int y) -> x*y
5. 接收一个String类型,打印
(String s) -> System.out.print(s);
3.2 函数式接口
如果一个接口只有一个抽象方法,那么这个接口就是函数式接口。
我们可以用@FunctionalInterface 来进行注解,用来提醒你这个接口是函数式接口。
里面包含default方法也可以。
3.3 Lambda表达式的基本使用
Lambda表示式其实就是匿名内部类的简化,实例化一个类,实现了接口,并重写了接口里面的方法。
这里我们先创建几个函数式接口:
//无返回值无参数
@FunctionalInterface
interface NoParameterNoReturn {void test();
}//无返回值一个参数
@FunctionalInterface
interface OneParameterNoReturn {void test(int a);
}//无返回值多个参数
@FunctionalInterface
interface MoreParameterNoReturn {void test(int a,int b);
}//有返回值无参数
@FunctionalInterface
interface NoParameterReturn {int test();
}//有返回值一个参数
@FunctionalInterface
interface OneParameterReturn {int test(int a);
}//有返回值多参数
@FunctionalInterface
interface MoreParameterReturn {int test(int a,int b);
}
匿名内部类调用方式如下:
NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn() {public void test() {System.out.println("nihao");}};
如果使用Lambda表达式可以写成下面代码:
NoParameterNoReturn noParameterNoReturn = () -> {System.out.println("haha");};OneParameterNoReturn oneParameterNoReturn = (int a) -> {System.out.println(a);};MoreParameterNoReturn moreParameterNoReturn = (int a, int b) -> {System.out.println(a + b);};NoParameterReturn noParameterReturn = () -> {System.out.println("hehe");return 10;};
3.3 语法精简
- 当参数列表中只有一个参数时,可以省略()。
- 参数列表的参数可以省略参数类型,要省略都省略。
- 方法体中只有一条代码,大括号可以省略。
- 方法体中只有一条语句,并且是return语句,大括号可以省略,并且return可以省略。
3.4 变量捕获
3.4.1 匿名内部类的变量捕获
class School {public void func() {System.out.println("aaaaa");}
}
public class Test {School school = new School() {int a = 10;@Overridepublic void func() {System.out.println("重写func方法" + a);}};
这里的a就是变量捕获,如果我们要在重写的方法中调用匿名内部类中的变量,这个变量要么是被final修饰,要么调用的这个常量不能再修改了。
3.4.2 Lambda表达式的变量捕获
这里a不是写在代码块中的,而是在代码块外面。
//无返回值无参数@FunctionalInterfaceinterface NoParameterNoReturn {void test();}public class Test {int a = 10;NoParameterNoReturn noParameterNoReturn = () -> {System.out.println("haha" + a);};
}
3.5 Lambda表达式在集合中的使用
在集合当中也存在一些接口配合Lambda表达式使用:
其中Collection里面的forEach()方法是从java.long.Iterable接口中拿来的。
3.5.1 Collection接口
forEach方法演示,该方法的原型在Iterable接口中:
该方法的作用是对容器里面的每个元素执行action操作:
下面是将这个链表遍历一遍:
public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("aaa");list.add("bbb");list.add("ccc");list.add("ddd");list.forEach(new Consumer<String>() {@Overridepublic void accept(String s) {System.out.print(s + " ");}});}
我们可以把上面的代码修改成利用lambda表达式的形式:
list.forEach(s -> {System.out.print(s + " ");});
3.5.2 List接口
sort()方法演示:
作用是进行排序
public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("aaa");list.add("bbb");list.add("ccc");list.add("ddd");list.sort(new Comparator<String>() {@Overridepublic int compare(String o1, String o2) {return o2.compareTo(o1);}});System.out.println(list);}
上面的代码我们可以改写成用lambda表达式来进行编写:
ArrayList<String> list = new ArrayList<>();list.add("aaa");list.add("bbb");list.add("ccc");list.add("ddd");list.sort((o1,o2) -> (o2.compareTo(o1)));System.out.println(list);
3.5.3 Map接口
HasMap的forEach方法的使用:
该方法的作用是Map中的每个映射执行action操作。
public static void main(String[] args) {Map<String,Integer> map = new HashMap<>();map.put("aaa",1);map.put("bbb",2);map.put("ccc",3);map.put("ddd",4);map.forEach(new BiConsumer<String, Integer>() {@Overridepublic void accept(String s, Integer integer) {System.out.println("K:" + s + " " + "V:" + integer);}});}
上面代码可以利用lambda修改成:
map.forEach((k,v) -> {System.out.println("K:" + k + " " + "V:" + v);});
总的来说lambda可以让我们的代码更加简单,容易进行并行计算,可以改善集合操作,
但是会导致代码的可读性变差,不容易进行调试。