1、定义
java的反射机制是在运行状态中,对于任意一个类,都能直到这个类的所有属性和方法;对于任意一个对象,都能够调用他的任意方法和属性,既然能够拿到那么,我们就可以修改部分类型信息;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制
2、用途
*在日常的第三方应用开发过程中,经常会遇到某个类的某个成员变量、方法或是属性是私有的或是只对系统应用开放,这时候就可以利用java的反射机制通过反射来获取所需的私有成员或方法
*反射最重要的用途是开发各种通用框架,比入我们在Spring中,我们将所有的类Bean交给Spring容器进行管理,无论是XML配置Bean还是注解配置,当我们从容器中获取Bean来依赖注入时,容器会读取配置,而配置中给的就是类的基本信息,Spring就是根据这些信息,需要创建哪些Bean,Spring就动态的创建这些类。
3、反射的基本信息
java程序中血多对象在运行时会出现两种类型:运行时类型(RTTI)和编译时类型,例如Person p=new Student();这句代码中p在编译时类型为Person,运行时为Student。程序需要在运行时发现对象和类的真实信心,而通过使用反射程序就能判断出该对象和类属于哪些类。
4、反射相关的类
4.1、Class的反射机制
4.2、获取类对象的三种方式
在反射之前,我们需要做的第一步就是先拿到当前需要反射类的对象,然后通过Class对象的核心方法,达到反射的目的,即:在运行状态中,对于任意一个类,都能知道这个类的所有属性和方法;对于任意一个对象都能够调用它的任意方法和属性,既然能拿到,那么,就可以修改部分类型信息
第一种:使用Class.forName("类的全路径名");静态方法
第二种:使用.Class方法
第三种:使用类对象的getClass()方法
示例:
/**
* 获取类对象方式
*/
public class Reflection01 {
public static void main(String[] args) {
//1、通过类来获取
Class<Student> studentClass=Student.class;
//2、通过实例对象来获取
Student student=new Student();
Class<? extends Student> classByInstance=student.getClass();
Class<?> classByReflect=null;
try {
//通过反射来获取
classByReflect=Class.forName("Day20221201.Test11.Student");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//判断这三个对象是否是同一个对象
System.out.println("===========使用==判断是否相等============");
System.out.println(studentClass==classByReflect);
System.out.println(classByReflect==classByInstance);
System.out.println(studentClass==classByInstance);
System.out.println("===========使用equals()判断是否相等============");
System.out.println(studentClass.equals(classByReflect));
System.out.println(classByReflect.equals(classByInstance));
System.out.println(classByInstance.equals(studentClass));
}
}
4.3、反射的使用
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Reflection02 {
/**
* 通过反射创建一个新的实例对象
*/
public static void reflectNewInstance(){
System.out.println("========通过反射创建一个新的实例对象==========");
try {
//1、获取类对象
Class<?> clazz=Class.forName("Day20221201.Test11.Student");
System.out.println(clazz.getName());
//2、通过类对象创建一个新的实例对象
Student student= (Student) clazz.newInstance();
student.setAge(18);
//打印一下
System.out.println(student);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
/**
* 通过反射操作构造方法
*/
public static void reflectConstructor(){
System.out.println("=========通过反射操作构造方法=========");
try {
//1、获取类对象
Class<?> clazz=Class.forName("Day20221201.Test11.Student");
//2、通过类对象获取构造方法
//Constructor<?>[] constructors=clazz.getConstructors();//可以获得到所有public权限的构造方法
Constructor<?>[] constructors=clazz.getDeclaredConstructors();//获取已经定义的构造方法,包括私有的
for (int i = 0; i < constructors.length; i++) {
System.out.println(constructors[i]);
}
//3、获取一个指定的构造方法
Constructor<?> constructor=clazz.getConstructor();
//4、通过构造方法创建一个新的实例对象
Student student= (Student) constructor.newInstance();
System.out.println(student);
student.setName("鸡你太美");
System.out.println(student);
//5、调用私有的,多个参数的构造方法去创建对象
Constructor<?> constructor1=clazz.getDeclaredConstructor(String.class,int.class);
//修改私有方法的访问权限
constructor1.setAccessible(true);
Student student1= (Student) constructor1.newInstance("咯咯",18);
System.out.println("通过私有构造方法创建的对象:"+student1);
} 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 reflectField(){
System.out.println("========通过反射操作属性========");
try {
//1、获取类对象
Class<?> clazz=Class.forName("Day20221201.Test11.Student");
//2、获取所有发生的数组
//所有访问权限为public的属性
//Field[] fields=clazz.getFields();
//获取所有访问权限或所有定义的属性
Field[] fields=clazz.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
System.out.println(fields[i]);
}
//创建一个对象
Student student= (Student) clazz.newInstance();
System.out.println(student);
//操作这个属性
//先获取一个属性
Field fieldAge=clazz.getField("age");
//给这个属性设置一个值
fieldAge.set(student,18);
System.out.println(student);
//获取私有属性并赋值
Field fieldName=clazz.getDeclaredField("name");
//修改访问权限
fieldName.setAccessible(true);
fieldName.set(student, "只因坤坤爱坤坤");
System.out.println(student);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
public static void reflectMethod(){
System.out.println("=======通过反射操作方法========");
try {
//1、创建类对象
Class<?> clazz=Class.forName("Day20221201.Test11.Student");
//2、获取所有方法信息
//所有访问修饰符为public的方法
//包括父类的方法都可以获取到
//Method[] methods=clazz.getMethods();
Method[] methods=clazz.getDeclaredMethods();
for (Method items:methods) {
System.out.println(items);
}
//创建一个实例对象
Student student= (Student) clazz.newInstance();
//通过指定对象获取去获取一个指定的量
Method methodSleep=clazz.getDeclaredMethod("sleep");
methodSleep.invoke(student);
//调用一个私有的量
Method methodFunction=clazz.getDeclaredMethod("function",String.class);
//修改访问权限
methodFunction.setAccessible(true);
methodFunction.invoke(student,"通过·反射传入的参数");
} 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();
}
}
public static void main(String[] args) {
// 通过反射创建一个新的实例对象
reflectNewInstance();
// 通过反射操作构造方法
reflectConstructor();
// 通过反射操作属性
reflectField();
// 通过反射操作方法
reflectMethod();
}
}
5、反射的优点和缺点
优点:
*对于任意一个类,都能知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法
*增加程序的灵活性和扩展性,降低耦合性,提高自适应能力
*反射已经应用在了很多流行的框架如:Spring、Structs等等
缺点:
*使用反射会有效率问题。会导致程序效率降低
*反射技术绕过了源代码技术,因而会带来维护问题。反射代码比相应的代码更加复杂
7、枚举的使用
枚举是在JDK1.5之后引入的。主要用途是:将一组常量组织起来,在这之前表示一组常量通常使用定义常量的方式
public static final int RED = 1;
public static final int GREEN = 2;
public static final int BLACK = 3;
但是常量列举有不好的地方,例如:可能碰巧有个数字1,但是它误以为是RED,现在我们可以直接用枚举类型来进行组织,这样一来,就拥有了类型——枚举类型,而不是普通的整型1;
public enum TestEnum {
RED,BLACK,GREEN;
}
优点:将常量组织起来统一进行管理
应用场景:错误状态码,消息类型,颜色划分,状态机等等
8、使用
8.1、switch语句
public class Enum01 {
public static void main(String[] args) {
int color=1;
switch(color){
case 1:
System.out.println("这是红色");
break;
case 2:
System.out.println("这是黄色");
break;
case 3:
System.out.println("这是蓝色");
break;
case 4:
System.out.println("这是绿色");
break;
}
int a=10;
int b=20;
System.out.println(sum(a,b));
System.out.println(sum(a,color));
}
private static int sum(int x, int y) {
return x+y;
}
}
8.2、Enum的常用方法
8.3、示例
先定义枚举类型
public enum ColorEnum {
RED,GREEN,BLACK,WHITE;
}
public enum ResultEnum {
SUCCESS(200,"成功"),
ERROR_CLIENT(400,"客户端出错了"),
ERROR_SERVER(500,"服务器出错了"),
NONE(-99999,"未定义");
private int code;
private String message;
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
ResultEnum(int code, String message) {
this.code = code;
this.message = message;
}
@Override
public String toString() {
return "ResultEnum{" +
"code=" + code +
", message='" + message + '\'' +
'}';
}
public ResultEnum valueOf(int Code){
switch(code){
case 200:
return SUCCESS;
case 400:
return ERROR_CLIENT;
case 500:
return ERROR_SERVER;
}
return NONE;
}
}
public class Enum02 {
public static void main(String[] args) {
ColorEnum colorEnum=ColorEnum.WHITE;
switch (colorEnum){
case RED:
System.out.println("鸡你太美");
break;
case GREEN:
System.out.println("唱跳、rap、篮球");
break;
case BLACK:
System.out.println("小黑子");
break;
case WHITE:
System.out.println("中分头、背带裤,我叫坤坤你记住");
break;
}
}
}
public class Enum03 {
public static void main(String[] args) {
ResultEnum resultEnum=ResultEnum.SUCCESS;
System.out.println("状态码:"+resultEnum.getCode()+",描述;"+resultEnum.getMessage());
ResultEnum resultEnum1=ResultEnum.NONE;
System.out.println("状态码:"+resultEnum1.getCode()+",描述;"+resultEnum1.getMessage());
}
}
public class Enum04 {
public static void main(String[] args) {
//value()方法演示
ResultEnum[] values=ResultEnum.values();
for (ResultEnum items:values) {
System.out.println(items);
}
//valueOf方法演示
ResultEnum resultEnum=ResultEnum.valueOf("ERROR_CLIENT");
System.out.println("状态码:"+resultEnum.getCode()+",描述:"+resultEnum.getMessage());
//CompareTo()方法比较
System.out.println(ResultEnum.SUCCESS.compareTo(ResultEnum.ERROR_CLIENT));
System.out.println(ResultEnum.SUCCESS.compareTo(ResultEnum.SUCCESS));
//ordinal()方法
System.out.println("========================");
for (int i = 0; i < values.length; i++) {
System.out.println("枚举:"+values[i]+",ordinal:"+ values[i].ordinal());
}
}
}
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Enum05 {
//通过反射创建一个枚举
public static void main(String[] args) {
try {
//获取类对象
Class<?> clazz=Class.forName("Day20221201.Test11.ResultEnum");
Constructor[] constructors=clazz.getDeclaredConstructors();
for (int i = 0; i < constructors.length; i++) {
System.out.println(constructors[i]);
}
//获取构造方法
Constructor<?> constructor=clazz.getDeclaredConstructor(String.class,int.class,int.class,String.class);
//修改访问权限
constructor.setAccessible(true);
Object o = constructor.newInstance("ERROR_NETWORK", 4, 600, "网络错误");
} 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();
}
}
}
9、结论:
针对Enum05的结果,可以确定不能通过new或是反射去创建一个枚举对象,所以我们认为枚举是比较安全的,利用反射性可以实现一个单例模式的类(单例模式就是全局唯一,只有一个对象)
*枚举本身就是一个类,其构造方法默认是私有的,且都继承于java.long.Enum
*枚举可以避免反射和序列化问题
*便于让我们把常量组织起来,统一管理