作者:~小明学编程
文章专栏:JavaSE基础
格言:目之所及皆为回忆,心之所想皆为过往
目录
反射
什么是反射?
常用的反射类
Class类
Class类中的相关方法
常用获得类中属性相关的方法
获得类中注解相关的方法
获得类中构造器相关的方法
获得类中方法相关的方法
用法演示
获取class对象
反射构造方法
反射属性
反射方法
枚举
引入
枚举的使用
枚举中的方法
构造方法
枚举优点和缺点
反射
什么是反射?
反射是一种在代码的运行状态下依然能获取任何的类,并且能获取该类的所有的属性方法,并且我们还能去修改它,这就是Java的反射机制,非常的强,今天带大家去认识这种机制。
常用的反射类
类名 | 用途 |
Class类 | 代表类的实体,在运行的Java应用程序中表示类和接口 |
Field类 | 代表类的成员变量/类的属性 |
Method类 | 代表类的方法 |
Constructor类 | 代表类的构造方法 |
Class类
在我们的Java文件被编译之后将会生成一个.class的文件,然后我们的jvm就会去解读这个文件,将会把它当作一个对象,该对象为java.lang.Class,所以我们的每一个Java文件都会被解析为这样的一个对象,我们的反射机制就是获取这个实例对象然后去获取该类的所有属性甚至去改变它。
Class类中的相关方法
方法 | 用途 |
getClassLoader() | 获得类的加载器 |
getDeclaredClasses() | 返回一个数组,数组中包含该类中所有类和接口类的对象(包括私有的) |
forName(String className) | 根据类名返回类的对象 |
newInstance() | 创建类的实例 |
getName() | 获得类的完整路径名字 |
常用获得类中属性相关的方法
方法 | 用途 |
getField(String name) | 获得某个公有的属性对象 |
getFields() | 获得所有公有的属性对象 |
getDeclaredField(String name) | 获得某个属性对象 |
getDeclaredFields() | 获得所有属性对象 |
获得类中注解相关的方法
方法 | 用途 |
getAnnotation(Class annotationClass) | 返回该类中与参数类型匹配的公有注解对象 |
getAnnotations() | 返回该类所有的公有注解对象 |
getDeclaredAnnotation(Class annotationClass) | 返回该类中与参数类型匹配的所有注解对象 |
getDeclaredAnnotations() | 返回该类所有的注解对象 |
获得类中构造器相关的方法
方法 | 用途 |
getConstructor(Class...<?> parameterTypes) | 获得该类中与参数类型匹配的公有构造方法 |
getConstructors() | 获得该类的所有公有构造方法 |
getDeclaredConstructor(Class...<?> parameterTypes) | 获得该类中与参数类型匹配的构造方法 |
getDeclaredConstructors() | 获得该类所有构造方法 |
获得类中方法相关的方法
方法 | 用途 |
getMethod(String name, Class...<?> parameterTypes) | 获得该类某个公有的方法 |
getMethods() | 获得该类所有公有的方法 |
getDeclaredMethod(String name, Class...<?> parameterTypes) | 获得该类某个方法 |
getDeclaredMethods() | 获得该类所有方法 |
用法演示
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 +
'}';
}
}
这是我们的一个Person类,里面是我们经过包装的方法和属性,下面我们就用反射来演示一下我们怎么用反射来获取里面的私有属性。
获取class对象
我们获取class对象有三种方式:
1.在我们知道该类的路径的时候我们可以使用 Class.forName("类的全路径名")的方式获取class对象。
Class<?> c1 = Class.forName("reflectdemo.Student");//使用 Class.forName("类的全路径名");
但是这种方式可能会抛出异常,所以我们要做一个异常处理。
2.使用.class的方式
Class<?> c2 = Student.class;//使用 .class 方法。
我们类.class这同时也说明了我们的每一个类都有一个隐藏的.class的属性。
3.使用类对象的 getClass() 方法
Student student = new Student();
Class<?> c3 = student.getClass();//使用类对象的 getClass() 方法
注意:
public static void main(String[] args) throws ClassNotFoundException {
Class<?> c1 = Class.forName("reflectdemo.Student");//使用 Class.forName("类的全路径名"); 静态方法。
Class<?> c2 = Student.class;//使用 .class 方法。
Student student = new Student();
Class<?> c3 = student.getClass();//使用类对象的 getClass() 方法
System.out.println(c1==c2&&c2==c3);//true
}
虽然我们用了三种不同的方式去获取我们的类对象但是我们获取的c1,c2,c3都是一样的。
反射构造方法
我们知道我们的构造方法其实是可以用private来修饰的,这样的话我们就不能自动调用了,但是我们可以通过反射来破坏其封装然后调用构造方法。
//获取私有的构造方法
public static void reflectPrivateConstructor() {
try {
Class<?> c1 = Class.forName("reflectdemo.Student");//获取类
Constructor<?> constructor = c1.getDeclaredConstructor(String.class,int.class);//获取构造方法
constructor.setAccessible(true);
Student student = (Student) constructor.newInstance("haha",15);//new一个实例
System.out.println(student);
} 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();
}
}
其思路就是,首先获取我们的class类,然后我们想要修改构造方法,那么就传入参数找到我们的构造方法,然后修改属性,最后通过newInstance方法获取实例类。
可以看到已经在调用私有构造方法了。
反射属性
public static void reflectPrivateField() {
try {
Class<?> c1 = Class.forName("reflectdemo.Student");//拿到当前需要反射的类的Class对象
Student student = (Student) c1.newInstance();//创建类的实例
Field field = c1.getDeclaredField("name");//通过getDeclaredField方法获取我们类成员的私有属性(共有的也能获取)
field.setAccessible(true);
field.set(student,"haha");
System.out.println(student);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
我们先获取类,然后实例对象,通过getDeclaredField找到我们的属性,然后修改权限,接着修改当前属性。
反射方法
public static void reflectPrivateMethod() {
try {
Class<?> c1 = Class.forName("reflectdemo.Student");//拿到当前需要反射的类的Class对象
Student student = (Student)c1.newInstance();//创建类的实例
Method method = c1.getDeclaredMethod("function", String.class);
method.setAccessible(true);
method.invoke(student,"hehe");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
1.获取类class
2.实例一个对象
3.获取我们想要的方法
4.修改权限
5.可以调用该方法了
枚举
引入
枚举是什么呢?我们为什么要用枚举?
说到枚举我们就不得不说到我们的常量了,我们枚举的作用就是将常量组织起来。
枚举的使用
public enum TestEnum {
RED,BLUE,GREEN,BLACK,WHITE;
public static void main(String[] args) {
System.out.println(TestEnum.BLACK);//BLACK
TestEnum testEnum = TestEnum.BLUE;
switch (testEnum) {
case BLACK:
System.out.println("black");
break;
case RED:
System.out.println("red");
break;
case GREEN:
System.out.println("green");
break;
case BLUE:
System.out.println("blue");
break;
case WHITE:
System.out.println("white");
break;
default:
System.out.println("no");
}
}
}
我们创建一个枚举类,然后我们的枚举常量就直接定义在我们的类中。
枚举中的方法
方法名称 | 描述 |
values() | 以数组形式返回枚举类型的所有成员 |
ordinal() | 获取枚举成员的索引位置 |
valueOf() | 将普通字符串转换为枚举实例 |
compareTo() | 比较两个枚举成员在定义时的顺序 |
以上几种就是我们枚举中常用的方法,下面给大家演示一下使用方式。
public static void main(String[] args) {
TestEnum[] testEnums = TestEnum.values();
for (int i = 0; i < testEnums.length; i++) {
System.out.println(testEnums[i]+" "+testEnums[i].ordinal());
}
}
public static void main(String[] args) {
TestEnum testEnum1 = TestEnum.RED;
TestEnum testEnum2 = TestEnum.BLUE;
System.out.println(testEnum1.compareTo(testEnum2));//-1
System.out.println(GREEN.compareTo(RED));//2
}
在我们介绍完这些方法之后,不知道大家想过这么个事情没有,我们的这些方法从哪里来的,我们通过我们的枚举类的当前引用或者枚举常量就能调用我们的方法了这是为啥呢?
我们来看看源码
可以看到我们的枚举类是一个抽象类,我们的一般的方法都在里面,这是因为我们自己所创建的枚举类都是默认继承我们的抽象的Enum类的。
构造方法
public enum TestEnum {
RED("aa",3),BLUE("bb",3),GREEN("cc",3),
BLACK("dd",3),WHITE("ee",3);
private String str;
private int i;
TestEnum(String aa, int i) {
this.str = aa;
this.i = i;
}
}
我们可以给枚举定义一个构造方法根据我们想要的参数定义,枚举的构造方法默认是私有的,这点需要记住。
注意:
1.我们自己所创建的枚举类都是默认继承我们的抽象的Enum类的。
2.枚举的构造方法默认是私有的。
3.枚举非常安全,我们枚举是不能通过反射来获取其中的其中的实例对象
枚举优点和缺点
优点:
1. 枚举常量更简单安全 。
2. 枚举具有内置方法 ,代码简洁。
缺点:
不能继承和扩展。