一、反射机制有什么用?
通过java语言中的反射机制可以操作字节码文件。
有点类似于黑客。(可以读和修改字节码文件)。
通过反射机制 可以操作代码片段(class文件)。
二、反射机制在哪个包下?
java.lang.reflect.*;
三、反射机制相关的重要的类有哪些?
java.lang.Class:代表整个字节码,代表一个类型,代表整个类
java.lang.reflect.Method:代表字节码中的方法字节码。代表类中的方法
java.lang.reflect.Constructor:代表字节码中的构造方法字节码。代表类中的构造方法
java.lang.reflect.Field:代表字节码码中的属性字节码。代表类中的成员变量(静态变量+实例变量)
java.lang.Class
一、获取Class的三种方式
要操作一个类的字节码,需要获取到这个类的字节码,怎样获取 java.lang.Class 实例?
① 方式一:Class c = Class.forName("完整类名带包名");
② 方式二:Class c = 引用.getClas();
③ 方式三:Class c = 任何类型.class;
public class ReflectTest01 {
public static void main(String[] args) {
try {
//第一种方法
Class c1 = Class.forName("java.lang.String");//c1代表String.class文件,或者说c1代表String类型
Class c2 = Class.forName("java.util.Date");//c2代表Date类型
Class c3 = Class.forName("java.lang.Integer");//c3代表Integer类型
Class c4 = Class.forName("java.lang.System");//c4代表System类型
System.out.println(c1);//class java.lang.String
//第二种方法
//java中任何一个对象都有一个方法:getClass()
Class x = "abc".getClass();//x代表 String.class字节码文件,x代表String类型。
System.out.println(c1 == x);//true,字节码文件转载到JVM中时,只加载一份
Date date = new Date();
Class y = date.getClass();
System.out.println(c2 == y);//true
//第三种方法
//java语言中任何一种类型,包括基本数据类型,它都有.class属性
Class z = String.class;
Class k = Date.class;
Class f = int.class;
Class e = double.class;
System.out.println(x == z);//true
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
二、获取到class,能干什么?
通过Class的newInstance()方法来实例化对象。
注意:newInstance()方法内部实际上是调用了无参构造方法,必须保证无参构造方法存在才可以
public class ReflectTest02 {
public static void main(String[] args) {
//通过反射机制,获取Class,通过Class来实例化对象
try {
//不用反射机制创建对象
User user = new User();
//用反射机制创建对象
//newInstance()这个方法会调用User这个类的无参数构造方法,完成对象的创建。
//重点:必须保证无参构造是存在的
//建议都写无参构造方法,如果写了有参的构造方法,没写无参构造方法
//newInstance()方法会出现异常:java.lang.InstantiationException
//无参构造
Object o1 = Class.forName("main.java.cn.edu.mju.bean.User").newInstance();//新建实例
Object o2 = Class.forName("main.java.cn.edu.mju.bean.User")
.getConstructor(null).newInstance(null);
//有参构造
Object o3 = Class.forName("main.java.cn.edu.mju.bean.User")
.getConstructor(Integer.class).newInstance(20);
} catch (Exception e) {
e.printStackTrace();
}
}
}
三、验证反射机制的灵活性
Java代码写一遍,在不改变java代码的基础之上,可以做到不同对象的实例化。
非常之灵活。(符合OCP开闭原则:对外开放,对修改关闭。)
创建的对象不一定是User对象,创建的对象取决于配置文件中的类名。
User user = new User();这种方式是写死了,没有灵活性。
高级框架底层实现原理:都采用了反射机制,所以反射机制还是很重要的额。
学会了反射机制原理有利于你理解剖析框架底层的源代码。
public class ReflectTest03 {
public static void main(String[] args) throws Exception {
//这种方式代码就写死了,只能创建一个User类型的对象
User user = new User();
//以下代码是灵活的,代码不需要改动。只需修改配置文件就可以创建出不同的实例对象
//通过IO流读取classinfo.properties文件
FileReader reader = new FileReader("D:\\Data\\java\\src\\classinfo.properties");
//创建属性类对象Map
Properties pro = new Properties();
//加载
pro.load(reader);
reader.close();
//通过key获取value
String className = pro.getProperty("className");
System.out.println(className);
//通过反射机制实例化对象
//创建的对象不一定是User对象,创建的对象取决于类名
Class c = Class.forName(className);
Object obj = c.newInstance();
System.out.println(obj);
}
}
java.lang.reflect.Field
-
A
Field
提供有关类或接口的单个字段的信息和动态访问。 反射的字段可以是类(静态)字段或实例字段。
反射User类中的所有的Field
- 获取类
Class user = Class.forName("main.java.cn.edu.mju.user.User");
- 获取类名
user.getName()//完整类名
user.getSimpleName()//简类名
- 获取类中所有public的Field对象
Field[] field = user.getFields();
- 获取类中的所有Field对象
Field[] fields = user.getDeclaredFields();//获取声明字段
- 获取Field对象的访问修饰符(Modifiers)
Modifier.toString(fields[i].getModifiers());
/*
fields[i].getModifiers();//返回的修饰符是一个数字,每个数字是修饰符的代号
*/
- 获取Field对象的数据类型
fields[i].getType().getSimpleName();//简类型名
fields[i].getType().getName();//完整类型名
- 获取Field对象的字段名
fields[i].getName()
①、获取任何一个类的所有Field(反编译):
//通过反射机制,反编译一个类的所有属性Field
public class ReflectTest02 {
public static void main(String[] args) throws ClassNotFoundException {
//拼串
StringBuilder s = new StringBuilder();
//只要给个类名,就可以反编译出字段信息
//加载类
Class intClass = Class.forName("java.lang.Integer");
//获取该类的所有Field对象
Field[] fields = intClass.getDeclaredFields();
//类的访问修饰符和简类名
s.append(Modifier.toString(intClass.getModifiers()) +" class "+ intClass.getSimpleName() + "{\n");
for (Field field:fields) {
s.append("\t");
s.append(Modifier.toString(field.getModifiers()));//Field对象的访问修饰符
s.append(" ");
s.append(field.getType().getSimpleName());//Fidel数据类型的简名称
s.append(" ");
s.append(field.getName());//Field对象的名称
s.append(";\n");
}
s.append("}");
System.out.println(s);
}
}
②、(重点)通过反射机制访问Java对象属性:
给属性赋值:set
获取属性的值:get
通过反射机制获取获取类中某一具体Field(属性)对象。:Field nameField = userClass.getField("name");
通过该Field对象可以给任意Java对象的Field对象进行赋值或取值操作。: nameField.set(u1,"张三"); nameField.get(u1)
public class ReflectTest03 {
public static void main(String[] args) throws Exception {
//类加载,获取类
Class userClass = Class.forName("main.java.cn.edu.mju.user.User");
//反射机制创建对象
User u1 = (User) userClass.newInstance();
//获取Java对象的某一具体属性
Field nameField = userClass.getField("name");
//给对象的具体属性赋值
//赋值,给u1对象的name属性赋值"张三"
nameField.set(u1,"张三");
//取值
System.out.println(nameField.get(u1));//通过反射机制的方式
//不可以访问私有的属性
/*
java.lang.NoSuchFieldException异常
1.本身就没有该Field;
2.有该Field,但是该Field是使用private修饰的,而在获取该Field的时候,需要使用getDeclaredField这个方法。
*/
Field noField = userClass.getDeclaredField("phone");
//打破封装(反射的缺点,可能给不法分子留下机会)
noField.setAccessible(true);//某一具体属性打破封装
noField.set(u1,"00000000");
System.out.println(noField.get(u1));
}
}
反射机制只能对publiu修饰的Fidel对象进行值的操作(赋值|取值)
有两种方式可以访问private修饰的Field:
- 通过该类提供的封装的Getter和Setter方法。
- 通过反射机制打破封装的方式。
java.lang.reflect.Method
一、可变长度参数:
- 要求可变长度参数是0~n个
- 要求可变长度参数在参数列表中,必须最后一个位置上
- 可变长度参数只能有一个
- 可变长度参数可以看作一个数组看待
public class ArgsTest {
public static void main(String[] args) {
m();
m(10);
m(10,20,30);
m1("哈哈哈");
m1("哈哈哈",10);
m2(10,20,30,40,50,60);
m2(new int[]{50,6,07,7,98,78,45});
}
public static void m(int... args){
System.out.println("m方法执行了");
}
public static void m1(String name,int... args){
System.out.println("m1方法执行了");
}
public static void m2(int... args){
for (int i:args) {
System.out.print(i+" ");
}
}
}
二、反射method
public class MethodTest01 {
public static void main(String[] args) throws Exception {
//获取类
Class userServiceClass = Class.forName("main.java.cn.edu.mju.user.UserService");
//获取所有的method(包括私有的)
Method[] methods = userServiceClass.getDeclaredMethods();
System.out.println(methods.length);
//遍历methods
for (Method method:methods) {
System.out.println
(
Modifier.toString(method.getModifiers()) //方法的访问修饰符
+ " " +
method.getReturnType() //方法的返回值类型
+ " " +
method.getName() //方法名
);
Class[] parameterTypes = method.getParameterTypes(); //获取方法对象的形参,一个方法可能有好几个形参
for (Class parameterType : parameterTypes){ //遍历形式参数对象
System.out.println(parameterType.getSimpleName()); //遍历输出形式参数数据类型的简称
}
}
}
}
一、获取一个类中的所有method对象(反编译)
//反编译 method
public class MethodTest02 {
public static void main(String[] args) throws Exception {
//拼串
StringBuilder s = new StringBuilder();
//获取类
Class aClass = Class.forName("java.lang.String");
//获取类的访问修饰符+类名
s.append(Modifier.toString(aClass.getModifiers())+ " class " + aClass.getSimpleName()+" {\n");
//获取类中所有的方法存在一个方法数组中
Method[] methods = aClass.getDeclaredMethods();
//遍历方法数组,获取每个方法的 访问修饰符 + 返回值类型 + 方法名 + 参数列表
for (Method method:methods) {
s.append("\t");
s.append(Modifier.toString(method.getModifiers())); //访问修饰符
s.append(" " );
s.append(method.getReturnType()); //返回值类型
s.append( " " );
s.append(method.getName()); //方法名
s.append("(");
Class[] parameterTypes = method.getParameterTypes(); //参数列表
for (Class parameterType:parameterTypes) {
s.append(parameterType.getSimpleName()); //参数类型
s.append(",");
}
if (s.length()>0){
s.deleteCharAt(s.length()-1);//删除指定下标的字符
}
s.append("){");
s.append("\t}\n");
}
s.append("}");
System.out.println(s);
}
}
二、(重点)通过反射机制调方法
public class MethodTest03 {
public static void main(String[] args) throws Exception {
//不用反射机制怎么调用方法
UserService userService = new UserService();
boolean b = userService.login("admin","123");
System.out.println(b);
userService.logout();
//使用反射机制调用方法
Class userServiceClass = Class.forName("main.java.cn.edu.mju.user.UserService");
//创建对象
UserService userService1 = (UserService) userServiceClass.newInstance();
//获取Method
Method loginMethod = userServiceClass.getDeclaredMethod("login",String.class,String.class);
//调用方法
Object retValue = loginMethod.invoke(userService1,"admin","123");//invoke:调用
System.out.println(retValue);
}
}
java.lang.reflect.Constructor
一、反编译Construct
//反编译constructor
public class ConstructorTest01 {
public static void main(String[] args) throws Exception {
StringBuilder s = new StringBuilder();//拼串
Class vipClass = Class.forName("main.java.cn.edu.mju.bean.Vip");//获取类
s.append(Modifier.toString(vipClass.getModifiers()));//获取类的访问修饰符
s.append(" class ");
s.append(vipClass.getSimpleName());//获取类名
s.append("{\n");
Constructor[] constructors = vipClass.getDeclaredConstructors();//获取所有构造方法,放在数组中
for (Constructor constructor:constructors) {//遍历数组
s.append("\t");
s.append(Modifier.toString(constructor.getModifiers()));//构造方法,访问修符
s.append(" ");
s.append(vipClass.getSimpleName());//构造方法,方法名
s.append("(");
Class[] parameterTypes = constructor.getParameterTypes();//参数列表
for (Class parameterType:parameterTypes) {
s.append(parameterType.getSimpleName());
s.append(",");
}
if (parameterTypes.length > 0){
s.deleteCharAt(s.length()-1);
}
s.append("){}\n");
}
s.append("}");
System.out.println(s);
}
}
二、反射机制创建对象
public class ConstructorTest02 {
public static void main(String[] args) throws Exception{
//使用反射机制创建对象
Vip v1 = (Vip)Class.forName("main.java.cn.edu.mju.bean.Vip").newInstance();
Vip v2 = (Vip)Class
.forName("main.java.cn.edu.mju.bean.Vip")
.getConstructor(int.class,String.class,String.class,boolean.class)
.newInstance(10,"pp","10月1日",false);
Vip v3 = (Vip)Class.forName("main.java.cn.edu.mju.bean.Vip").getConstructor().newInstance();
System.out.println(v1);
System.out.println(v2);
}
}
重点:给你一个类,怎么获取父类和已经实现的接口
public class ReflectGetInterface {
public static void main(String[] args) throws ClassNotFoundException {
Class stringClass = Class.forName("java.lang.Class");
//获取Sting类的父类
System.out.println(stringClass.getSuperclass());
//获取String类实现的接口
Class[] interfaces = stringClass.getInterfaces();
for (Class in:interfaces) {
System.out.println(in.getName());
}
}
}