数据结构—(java)反射,枚举,lambda表达式
文章目录
- 反射
- 反射的定义:
- 反射相关的类:
- 反射相关的方法:
- 反射示例:
- 获取Class类对象
- 创建指定类的对象
- 反射私有属性:
- 反射私有方法:
- 反射私有的构造方法
- 枚举
- 枚举的意义
- 枚举类的实现
- 枚举类的使用:
- Enum类中常用的四个方法:
- 枚举与反射:
- lambda表达式
- 函数式接口:
- lambda表达式的语法:
- lambda表达式的使用:
- 关于lambda表达式语法精简的问题:
- java集合类与lambda表达式的使用问题:
- 变量捕获:
反射
反射的定义:
java的反射机制是指,在程序的运行状态中,对于任意一个类,可以获取这个类的全部信息(包括,类加载器(后面会学到),构造方法,成员属性,成员方法等。)对于任意一个对象,我们也能够调用其属性与方法进行修改信息,这种动态地获取信息与动态地调用对象的机制,我们称为反射机制。
反射相关的类:
反射相关的方法:
在Class类中即包含了获取类加载器,创建指定类对象,获取构造方法,属性,成员方法的方法,如下图所示:
反射示例:
获取Class类对象
此处的Class并不是指表示类名的关键字,而是指Class这个具体的类,
因为Class类的构造方法是私有方法,所以不能够直接实例化对象。
我们需要获取Class类对象有三种方式:
第一种:调用对象的getClass方法
Student student = new Student();
//getClass方法也是用c/c++代码实现的
Class<Student> c1 = (Class<Student>) student.getClass();
第二种:直接调用类名的class属性
但是我们并没有在Student中定义class属性
//这说明每一个方法都有一个默认的class属性
Class c2 = Student.class;
第三种:通过class对象的ForName方法来获取
Class c3 = null ;
c3 = Class.forName("reflectDemo.Student");
判断这三个class对象是不是同一个对象。
System.out.println(c1 ==c2);
System.out.println(c1==c3);
System.out.println(c2==c3);
结果表明,我们通过三种方式创建的三个Class对象实际上是一个,
注:
实际上是因为任何一个类在JVM中只会被加载一次,所以其所对应的Class对象也
只会被创建一次。如果通过两个不同的类来创建Class对象,则这两个Class对象不一样。
Class c2 = Dog.class;
结果为:此时c2与c1,c3均不相同。
创建指定类的对象
使用的Student类
public class Student {
//私有属性name
private String name = "bit"; //公有属性age
public int age = 18;
//不带参数的构造方法
public Student(){
System.out.println("Student()");
}
private Student(String name,int age) {
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 static void reflectNewInstance() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
Class c3 = null ;
c3 = Class.forName("reflectDemo.Student");
//要通过c3对象来调用实例化对象的方法
Student student1 = (Student) c3.newInstance();
System.out.println(student1);
}
调用此方法。
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
//通过class对象创建一个学生对象
reflectNewInstance();
}
反射私有属性:
//简写了
public class Student {
//私有属性name
private String name = "bit"; //公有属性age
public int age = 18;
反射私有属性的方法:
public static void reflectPrivateField(){
Class c3 = null ;
try {
c3 = Class.forName("reflectDemo.Student");
Field field = c3.getDeclaredField("name");
//我们可以修改这个私有属性的值
field.setAccessible(true);
//还需要有一个Student对象,
Student student3 = (Student)c3.newInstance();
field.set(student3,"张三");
System.out.println(student3);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
reflectPrivateField();
}
反射私有方法:
public class Student {
private void eat(String s){
System.out.println(s);
}
}
public static void reflectPrivateMethod(){
Class c4 = null;
try {
c4 = Class.forName("reflectDemo.Student");
//在有参数时一定要加上参数类型的class
Method method = c4.getDeclaredMethod("eat",String.class);
Student student4 = (Student) c4.newInstance();
method.setAccessible(true);
method.invoke(student4, "i am eat");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
reflectPrivateMethod();
}
反射私有的构造方法
public class Student {
private Student(String name,int age) {
this.name = name;
this.age = age;
System.out.println("Student(String,name)");
}
}
public static void reflectPrivateConstructor(){
Class c3 = null ;
try {
c3 = Class.forName("reflectDemo.Student");
//调用getConstructor方法,参数为
Constructor<Student> constructor = c3.getDeclaredConstructor(String.class,int.class);
//在获取了构造方法之后呢?
constructor.setAccessible(true);
//直接调用此构造方法,为对象赋值
Student student2 = constructor.newInstance("张三",15);
System.out.println(student2);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
reflectPrivateConstructor();
}
枚举
枚举的意义
枚举是将一组常量组织起来,即将这一组常量赋予特殊的意义,用特殊的类型表示它们。
所用的场景为:错误状态码,消息类型,颜色的划分,状态机等等…
枚举类的实现
枚举类有自己特定的关键字即enum,
所有我们自己定义的枚举类均继承于java提供的Enum类。
枚举类的使用:
在类中定义枚举成员:
public enum Myenum {
//在自定义的枚举类中定义一些成员
Red,Yellow,Blue,Green;
}
获取枚举成员:
public enum Myenum {
//在自定义的枚举类中定义一些成员
Red,Yellow,Blue,Green;
public static void main(String[] args) {
//我们可以通过枚举类名,来直接获取枚举成员,而不需要创建枚举类的对象。
System.out.println(Myenum.Blue);
}
}
关于枚举类的构造方法:
注:因为枚举类型的构造方法是私有的,所以不能够在其他类中进行
创建对象,但是在枚举类中也不能够创建枚举类对象,
枚举类的使用意义并不在于它的对象,而在于它的枚举成员,其枚举成员可以直接通过类名调用。
Enum类中常用的四个方法:
因为我们定义的枚举类继承于Enum类,所以Enum类中的所有方法均可使用
public static void main(String[] args) {
//关于枚举类中方法的使用
//1. values方法,用于获取枚举类中所有的枚举成员。
//问题:在Enum类中并没有values方法,那么这个方法是从哪里来的?
Myenum[] myenums = Myenum.values();
for (Myenum myenum2: myenums) {
//在获取了每个枚举成员后,开始进行每个枚举成员的索引位
// 2. 调用ordinal方法
System.out.println( myenum2 +" "+myenum2.ordinal());
}
// 3. valueof方法,将普通的字符串转换成枚举实例?
// System.out.println(Myenum.valueOf("GREEN"));
//意思为:如果在枚举类中有此枚举成员,则会返回对应的枚举成员,如果没有则报异常
System.out.println("Red");
// 4. 第四个方法:compareTo方法
//在Enum类中实现了comparable接口,实现了compareTo方法,用于比较两个
//枚举成员的索引位置。
System.out.println(Red.compareTo(Blue));
}
枚举与反射:
我们不能够通过枚举的构造方法来实例化枚举类对象,那么能否通过反射机制来创建枚举类对象呢?
实际上是不行的。
Enum类中的构造方法:
public static void main(String[] args) {
//尝试通过反射进行实例化enum对象
Class<?> c1 = null;
try {
//要注意当父类的构造方法未调用时,子类的构造方法是不能被调用的
c1 = Class.forName("enumdemo.Myenum");
//枚举类不能够通过反射类进行创建。
//在指定的类的具有父类时,也应该加上父类形参类型的.class属性
Constructor constructor= c1.getDeclaredConstructor(String.class,int.class,int.class,String.class);
//获取了相应的构造方法后
constructor.setAccessible(true);
Myenum myenum = (Myenum) constructor.newInstance(2,"hong");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
运行结果:
lambda表达式
函数式接口:
如果一个接口中只有一个抽象方法,则此接口称为函数式接口。
lambda表达式可代替函数式接口的实现,直接将lambda表达式赋给接口类型的变量。
lambda表达式的语法:
如 : (parameters) -> expression 或 (parameters) ->{ statements; }
. paramaters:类似方法中的形参列表,这里的参数是函数式接口里的参数。这里的参数类型可以明确的声明
也可不声明而由JVM隐含的推断。另外当只有一个推断类型时可以省略掉圆括号。
2. ->:可理解为“被用于”的意思
3. 方法体:可以是表达式也可以代码块,是函数式接口里方法的实现。代码块可返回一个值或者什么都不反
回,这里的代码块块等同于方法的方法体。如果是表达式,也可以返回一个值或者什么都不返回。
lambda表达式的使用:
interface NoParameterNoReturn{
//无参且无返回值
void test();
}
interface OneParameterNoReturn{
//有一个参数无返回值
void test(int a);
}
interface MultipleParameterNoReturn{
//有多个参数无返回值
void test(int a,int b);
}
interface NoParameterReturn{
//无参且有返回值
int test();
}
interface OneParameterReturn{
//有一个参数有返回值
int test(int a);
}
interface MultipleParameterReturn{
//有多个参数有返回值
int test(int a,int b);
}
public static void main(String[] args) {
//Lambda表达式可用于实现只有一个抽象方法的接口。
//先实现无返回值无参数的接口
NoParameterNoReturn noParameterNoReturn1 = new NoParameterNoReturn() {
@Override
public void test() {
System.out.println("调用无返回值无参数方法");
}
};
//直接调用方法
noParameterNoReturn1.test();
//我们采用lambda表达式,直接赋给noParameterNoReturns变量
NoParameterNoReturn noParameterNoReturn2 = ()->{ System.out.println("调用无返回值无参数方法");};
noParameterNoReturn2.test();
}
此两种方式效果相同。
public static void main(String[] args) {
//调用一个无参有返回值的方法
NoParameterReturn noParameterReturn = ()->10; //可以不写return 关键字,直接写数值或语句
System.out.println(noParameterReturn.test());
}
关于lambda表达式语法精简的问题:
- 参数类型可以省略,如果需要省略,每个参数的类型都要省略。
- 参数的小括号里面只有一个参数,那么小括号可以省略
- 如果方法体当中只有一句代码,那么大括号可以省略
- 如果方法体中只有一条语句,且是return语句,那么大括号可以省略,且去掉return关键字。
java集合类与lambda表达式的使用问题:
public static void main(String[] args) {
//举例:ArrayList中的forEach语句
ArrayList<Integer> arrayList = new ArrayList<>();
arrayList.add(10);
arrayList.add(20);
arrayList.add(15);
//遍历数组可以用迭代器,foreach语句等,也可以采用arrayList本身的方法
//accept是接口中的抽象方法,将其重写
//方法1:直接创建匿名内部类调用。
arrayList.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(integer);
}
});
// 方法2:通过lambda表达式
//当我们这样编写时,代码很简洁,但是可读性比较差。
arrayList.forEach((a)->{
System.out.println(a);
});
}
public static void main(String[] args) {
//举例: List中的sort方法
ArrayList<Integer> arrayList = new ArrayList<>();
arrayList.add(10);
arrayList.add(20);
arrayList.add(15);
/* arrayList.sort(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
});*/
// System.out.println(arrayList);
arrayList.sort(((o1, o2) -> {return o1.compareTo(o2);}));
System.out.println(arrayList);
}
变量捕获:
//变量捕获是指在匿名内部类,或lambda表达式中,所使用的变量在此之前与之后不能被修改,或者此变量被final修饰