Java反射初步理解
文章参考bilibili:动力节点老杜
什么是反射
反射可以操作已经编译的java文件(即class文件),它赋予了我们在运行时分析类以及执行类中方法的能力。通过反射你可以获取任意一个类的所有属性和方法,你还可以调用这些方法和属性。
优点:使代码更灵活,比如可以在运行时分析类以及执行类中方法
缺点:使程序不安全,比如无视编译阶段的泛型参数检测 ,且性能更低
初步了解反射
与反射相关的类
java.lang.Class:代表整个字节码,代表一个类型
java.lang.reflect.Method:代表字节码中的字节方法码
java.lang.reflect.Constructor:代表字节码中的构造方法码
java.lang.reflect.Field:代表字节码中的属性字节码
获取Class的方法
-
Class类的静态方法forName(String className)
参数是一个字符串且必须是带有包名的完整类名Class c1=Class.forName("java.lang.String")
-
Object类的方法getClass()
Object(所有类的父类)的方法那么每个对象都可以使用getClass()方法String str="sadsd"; System.out.println(str.getClass());
-
任何一种类型包括基本类型都有class属性
class clazz=String.class System.out.println(clazz);
Field的使用
获取Field的方法
声明User类
public class User {
public int age;
public int sex;
private String name;
}
Class clazz=Class.forName("com.example.User");
//获取类的public修饰的Field
Field[] fields = clazz.getFields();
System.out.println(fields.length);
//取出某个属性
Field field_0=fields[0];
System.out.println(field_0.getName());
2
age
Class clazz=Class.forName("com.example.User");
//获取类声明的Field
Field[] fields = clazz.getDeclaredFields();
System.out.println(fields.length);
//取出某个属性
Field field_0=fields[0];
System.out.println(field_0.getName());
3
age
Filed的更多方法
Class clazz=Class.forName("com.example.User");
//获取类声明的Field
Field[] fields = clazz.getDeclaredFields();
System.out.println(fields.length);
System.out.println("====================");
for(Field field:fields){
//返回的修饰符是一个数字,每个数字都是修饰符的代号
System.out.println(field.getModifiers());
//将修饰符数字转换为字符串
System.out.println(Modifier.toString(field.getModifiers()));
//获取属性的类型
System.out.println(field.getType());
System.out.println(field.getName());
System.out.println("----------------");
}
3
====================
1
public
int
sex
----------------
25
public static final
int
age
----------------
2
private
class java.lang.String
name
----------------
Method
Method方法
public class User {
public int sex;
public static final int age=1;
private String name;
public boolean login(String username,String password){
if("asd".equals(username)&&"123".equals(password)){
return true;
}
return false;
}
private void logout(){
System.out.println("退出成功");
}
....
Class clazz = Class.forName("com.example.User");
Method[] methods = clazz.getDeclaredMethods();
for(Method method:methods){
System.out.println(method.getReturnType());//获取返回值类型
Class[] parameterTypes = method.getParameterTypes();//获取参数列表
for(Class parameterType:parameterTypes){
System.out.println(parameterType);
}
System.out.println(method.getName());//获取方法名
System.out.println("================");
}
void
class java.lang.String
setName
================
boolean
class java.lang.String
class java.lang.String
login
================
int
getSex
================
int
getAge
================
void
logout
...
Constructor
Constructor[] constructors = clazz.getConstructors();
for(Constructor constructor:constructors){
System.out.println(constructor.getName());
...
}
反射机制能干什么
Class使用事例
-
使用Class创建对象
Class clazz=Class.forName("com.example.User"); Object obj=clazz.newInstance(); System.out.println(obj);
结果是
com.example.User@4ee285c6
而如果className是一个接口的话Class clazz=Class.forName("java.util.List"); Object obj = clazz.newInstance(); System.out.println(obj);
结果报错
java.lang.InstantiationException: java.util.List
at java.lang.Class.newInstance(Class.java:427)
at test.test(test.java:10)
Caused by: java.lang.NoSuchMethodException: java.util.List.()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.newInstance(Class.java:412)所以很明显时调用了类的无参构造方法
如果我们在User的无参构造方法中使用输出语句public User(){ System.out.println(1); }
就会得到
1
com.example.User@4ee285c6这种方式创建对象与使用new创建对象相比更加灵活
比如有一个properties文件,内容是
className= com.example.User
Reader reader=new FileReader("src/classInfo.properties"); Properties properties=new Properties(); properties.load(reader); reader.close(); String className = properties.getProperty("className"); Class clazz = Class.forName(className); System.out.println(clazz.newInstance());
结果是
com.example.User@4ee285c6
当我们只修改properties的内容
className= java.util.ArrayList
不修改源代码就得到了
[]
符合OCP开闭原则:对扩展开放 ,对修改关闭
-
只执行类中的静态代码块
有一个类Cat
class Cat{ static{ //静态代码块在类加载的时候执行 sout("喵"); } }
Class.forName("com.example.Cat");
输出:
喵
所以希望只执行类中的静态代码块可以使用Class.forName(类名)
Field使用
- 通过反射机制访问一个java对象的属性
Class clazz=Class.forName("com.example.User"); Object obj = clazz.newInstance(); //共有属性 Field field = clazz.getDeclaredField("sex"); field.set(obj,1); System.out.println(((User) obj).getSex()); //访问私有属性 field = clazz.getDeclaredField("name"); field.setAccessible(true);//打破封装 field.set(obj,"asd"); System.out.println(((User) obj).getName());
通过反射调用方法
Class clazz = Class.forName("com.example.User");
//调用方法要素:对象,方法名,实参列表,返回值
Method method=clazz.getMethod("login",String.class,String.class);
Object obj = clazz.newInstance();
Object res = method.invoke(obj,//调用方法的对象
"asd", "123"//实参列表
);
System.out.println(res);
通过反射创建对象
Class clazz = Class.forName("com.example.User");
//无参构造
Object obj = clazz.newInstance();
//有参构造
Constructor declaredConstructor = clazz.getDeclaredConstructor(int.class, double.class);
Object o = declaredConstructor.newInstance(110, 12.0);
System.out.println(o);
System.out.println(obj);
User{sex=1, name='null'}
User{sex=0, name='null'}