- Java反射机制
- 反射
- 特点
- Java创建对象思想
- 类对象
- 类对象和类的对象
- 类对象中封装的对象
- 三种获取类对象的方法
- Class对象的常用方法
- 获取相关属性
- 获取构造方法
- 获取相关方法
- 获取泛型相关信息
- 属性类型
- 泛型类
- 反射运行配置文件内容
- Peroperties类
- 具体使用
- 【面试】创建实例
Java反射机制的核心就是在程序运行时动态加载类并获取类的详细信息,从而实现 *** 作类或者对象的属性和方法。本质上就是JVM得到Class对象,再通过Class对象进行反编译,从而获取对象的各种信息。
Object obj=new Date();//编译期类型和运行时类型
System.out.println(obj.toString());
反射
java的反射Reflection就是一种自省机制,可以实现在运行时动态获取类的信息以及动态的调用对象的方法。
通过反射运行程序在运行时通过反射API获取任何一个已知名称的class的内部信息,包括修饰词【public、static】、父类、实现的接口、属性和方法。反射机制可以在运行时改变属性的内容,也可以调用任意的方法,可以破坏范围限定词的约束【破坏类的封装性】
反射机制的工作原理:Class对象是由.class文件读入内存并自动创建的,获取Class对象后可以获取对应类的相关信息
特点优点:可以在运行时获取类的各种内容,能够灵活的创建代码,这些代码在运行时进行装配,不需要在组件之间进行源代码的连接,更加容易的实现面向对象的松耦合
缺点:
- 会消耗一定的系统资源。一般不需要动态创建对象时,不建议使用反射。性能损耗太大了
- 反射机制会忽略权限检查,可能会破坏类的封装性,从而导致安全问题。
- 类对象是类加载的产物,封装了一个类的所有信息(类名、父类、接口、属性、方法、构造方法)
- 类的对象是基于某个类new出来的对象,也称为实例对象
类对象中封装的对象 | 描述 |
---|---|
Field | 成员变量(属性) |
Method | 方法 |
Constructor | 构造器 |
PackageName | 包名 |
ClassName | 类名 |
SuperClass | 实体类的父类Class |
Interface | 接口 |
- 通过类的对象,获取类对象
Person person = new Person();
Class clazz=person.getClass();
场景:常用于对象的字节码获取方式
- 通过类名获取类对象
Class clazz=类名.class;
场景:常用于参数的传递
- 以静态方法通过全类名获取类对象
Class clazz=Class.forName("全类名");
场景:常用于读取配置文件,将类名定义在配置文件中。读取文件,加载类对象
还有两种:
- 使用TYPE语法获取,用于获取原生数据类型
Boolean.TYPE
- 使用类加载器的方法loadClass
返回值 | 方法名称 | 描述 |
---|---|---|
String | getName() | 获取类的名称 |
Field | getField(String name) | 获取public修饰的指定名称的成员变量 |
getDeclaredField(String name) | 获取指定名称的属性,包括private属性 | |
Field[] | getFields() | 获取public修饰的所有成员变量 |
getDeclaredFields() | 获取所有自身定义的属性,包括private属性 | |
Object | get(Object obj) | 通过成员变量获取指定对象的值 |
void | set(Object obj,Object value) | 通过成员变量设置指定对象的成员变量值 |
setAccessible(true) | 默认为false,true则忽略成员变量访问修饰符的安全检查 |
//构建对象
Class clz=Student.class;
Object obj=clz.newInstance();
Field f1=clz.getField("salary");
f1.set(obj, 99.12);
Field f2=clz.getDeclaredField("name");
//如果是超出范围的访问则会出现IllegalAccessException 如果一定要访问,则必须修改访问设置
f2.setAccessible(true);//设定这个属性运行访问
f2.set(obj, "zhangsan");
System.out.println(obj);
//获取f2属性在obj对象中的具体值
Object res=f2.get(obj);
System.out.println(res);
通过Field对象获取属性相关的描述信息
Class clz=Student.class;
Field[] fs=clz.getDeclaredFields();
for (Field tmp : fs) {
System.out.println(tmp.getName());//获取属性名称
System.out.println("------------");
System.out.println(tmp.getModifiers());//获取约束限定词
System.out.println("------------");
System.out.println(tmp.getType());//获取属性类型
System.out.println("============");
}
获取构造方法
方法名称 | 描述 |
---|---|
getConstructor()/getConstructors() | 获取public修饰的构造方法 |
getDeclaredConstructor()/getDeclaredConstructors() | 获取包括private的构造方法 |
newInstance() | 通过使用无参构造器构建一个当前类的对象(此类中必须要有无参构造器) |
// 使用类加载器加载指定名称的类,类名称应该使用全名,会有受检型一场
Class clz = Class.forName("com.yan3.Student");
Constructor<Person> c = clz.getConstructor(String.class);
Person p=c.newInstance("abcd");
System.out.println(p);
获取构造器的相关属性
Class clz = Class.forName("com.yan.test.Student");
Constructor<Person> c = clz.getConstructor(String.class);
// 名称
System.out.println(c.getName());//com.yan.test.Student
// 获取限定词约束 1public 0默认 4protected 2private
System.out.println(c.getModifiers());//1
// 构造器的参数个数
System.out.println(c.getParameterCount());//1
// 获取构造器抛出的异常
Class[] es = (c.getExceptionTypes());
for (Class tmp : es)
System.out.println(tmp);
// 获取构造器的参数
Parameter[] ps = (c.getParameters());
for(Parameter p:ps)
System.out.println(p);//java.lang.String arg0
//获取构造器方法中的所有参数类型
Class<?>[] pts = (c.getParameterTypes());
for(Class<?> tmp:pts)
System.out.println(tmp);//class java.lang.String
获取相关方法
方法名称 | 描述 |
---|---|
getMethod(String name,Class…)/getMethods() | 获取public修饰的方法 |
getDeclaredMethod(String name,Class…)/getDeclaredMethods() | 获取包括private的方法 |
invoke(obj,实参列表) | 调用当前方法对象 |
//需要调用成员方法,则必须先创建对象
Class clz=Student.class;
Object obj=clz.newInstance();
Method method=clz.getMethod("toString");//toString没有参数
Object res=method.invoke(obj);//如果没有返回值,则不接收返回值即可
System.out.println(res);
Method method2=clz.getDeclaredMethod("ccc", int.class,String.class);//由于方法是private,所以IllegalAccessException
method.setAccessible(true);
res=method2.invoke(obj, 5,"小胖");
反射调用静态方法
//调用静态方法则不需要创建对象
Class clz = Student.class;
Method method=clz.getDeclaredMethod("ddd",String.class);
method.setAccessible(true);
method.invoke(null,"仗义");//因为调用静态方法不需要创建对象,所以参数1为null
反射调用main方法
// 调用静态方法则不需要创建对象
Class clz = Student.class;
Method method = clz.getDeclaredMethod("main", String[].class);
//如果使用new String[]{"仗义","zhangsan"}则出现报错IllegalArgumentException
method.invoke(null, (Object)new String[]{"仗义","zhangsan"});
获取方法的相关属性说明
// 调用静态方法则不需要创建对象
Class clz = Student.class;
Method[] ms=clz.getMethods();
for(Method m:ms){
System.out.println("======================");
System.out.println(m.getName());//获取方法名称
System.out.println(m.getModifiers());//获取限定词
System.out.println(m.getParameterCount());//获取参数个数
Parameter[] ps=m.getParameters();//获取所有参数
for(Parameter p:ps)
System.out.println("\t"+p);
Class resType=m.getReturnType();//获取返回值类型
System.out.println(resType);
Class[] exs=m.getExceptionTypes();//获取抛出异常类型
for(Class tmp:exs)
System.out.println("\t"+tmp);
}
获取泛型相关信息
从JDK1.5+开始引入泛型,泛型可以理解为类型参数化。 Class>
泛型原理:类型擦除和补偿机制
注意一个典型错误 List
,这里没有继承关系问题,
List
没有任何关系,是两种不同的类型
List list=new ArrayList();可以理解为 List
-
获取普通类型的成员属性:getType()
-
获取泛型类型的成员属性:getGeneicType()
Class<Person> c=Person.class;
Field f = c.getDeclaredField("name");
Class<?> c1=f.getType();//获取属性对应的数据类型
System.out.println(c1);///class java.lang.String
Class clz=Class.forName("com.yan.Person");
Field f = clz.getDeclaredField("map");
System.out.println(f.getType());// interface java.util.Map
//获取成员变量的泛型类型
Type t = f.getGeneicType();//java.util.Map
System.out.println(t);
getGeneicType返回值实际上是ParameterizedType类型对象,ParameterizedType代表参数化的类型,也就是增加了泛型限制的类型
- getRawType返回没有泛型信息的原始类型
- getActualTypeArguments获取泛型类型的具体类型
Class clz=Class.forName("com.yan.Person");
Field f = clz.getDeclaredField("map"); // 获取成员变量的泛型类型
Type t = f.getGenericType(); //java.util.Map
if(t!=null && t instanceof ParameterizedType){
ParameterizedType pt=(ParameterizedType)t;
System.out.println(pt.getRawType()); //interface java.util.Map
//获取实际的泛型类型
Type[] arr=pt.getActualTypeArguments();
if(arr!=null && arr.length>0)
for(int i=0;i<arr.length;i++)
System.out.println("第"+(i+1)+"个泛型类型是"+arr[i]);
//第1个泛型类型是class java.lang.String
//第2个泛型类型是class java.lang.Integer
Type是java.lang.reflect包中的一个接口,是所有类型的公共接口,Class类也是Type接口的一个实现,Type对象中可以包含原始类型、参数化类型、数组类型、类型变量和基本类型
泛型类Student s1 = new Student();
//返回Student的父类型,不包含泛型
System.out.println(s1.getClass().getSuperclass());
//返回Student的父类型,会包含泛型
Type superType = s1.getClass().getGenericSuperclass();
System.out.println(superType);
//获取父类型中泛型对应的具体实际类型
// System.out.println(superType.getClass());
if(superType!=null && superType instanceof ParameterizedType){
ParameterizedType pt=(ParameterizedType)superType;
Type[] arr=pt.getActualTypeArguments();
if(arr!=null && arr.length>0)
for(Type tmp:arr)
System.out.println(tmp); //class java.lang.String
}
反射运行配置文件内容
常见的配置文件有:xml格式、properties格式以及yaml格式
配置文件,无格式纯文本文件 aaa.properties 具体格式是【key=value】
配置文件名称可以任意修改,但是位置必须确定
# key=value
className=com.yan3.Student
methodName=show
id=99
name=zhangsan
Peroperties类
public class Properties extends Hashtable<Object,Object>
Properties是一个特殊的Map类型,其中存储的key和value都是String类型
- getProperties(String key):String 按照key值查找对应的value值
- setProperty(String key, String value) 向map中添加一个key-value对
- void load(InputStream inStream)加载输入流对应的properties文件,并进行解析
- store(Writer writer, String comments)将key-value值存储到指定的输出流中
- list(PrintStream out)将key-value输出到指定的打印流
public class Test {
public static void main(String[] args) throws Exception {
// 读取并解析配置文件
Properties ps = new Properties();
File file = new File("config/abc.properties");
if (file.exists()) {
Reader r = new FileReader(file);
ps.load(r);
r.close();
}
// 使用反射机制获取类对象
Class clz = Class.forName(ps.getProperty("className"));
Object obj = clz.newInstance();
Field[] fs = clz.getDeclaredFields();
for (Field f : fs) {
String fieldName = f.getName();
if (ps.containsKey(fieldName)) {
if (f.getType() == String.class) {
f.setAccessible(true);
f.set(obj, ps.getProperty(fieldName));
} else if (f.getType() == int.class) {
String value = ps.getProperty(fieldName);
try {
int val = Integer.valueOf(value);
f.setAccessible(true);
f.set(obj, val);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
// 获取需要调用的方法名称,调用对应的方法。已知方法没有参数
String methodName = ps.getProperty("methodName");
Method method = clz.getMethod(methodName);
method.invoke(obj);
}
}
具体使用
使用反射获取类中的相关定义
//方法一
Class clz=Student.class;
//方法二
Class clz=new Student().getClass();
//方法三
Class clz=Class.forName("com.yan3.Student");
获取指定类中的属性
Class clz=Student.class;
Field[] fs=clz.getFields();//只能获取public属性,可以是继承的,也可以是自定义的
for(Field tmp:fs)
System.out.println(tmp);
获取当前类中所定义的属性,无视范围约束
Student s1=new Student();
Class clz=s1.getClass();
//只能获取当前类定义的内容,不能获取从父类继承到的属性
Field[] fs = clz.getDeclaredFields();
for(Field tmp:fs)
System.out.println(tmp);
/* 输出内容
* private java.lang.String com.yan3.Student.name
* protected int com.yan3.Student.id
* java.lang.String com.yan3.Student.password
* public double com.yan3.Student.salary
*/
获取指定类中的所有方法引用
Class clz=Student.class;
Method[] ms=clz.getMethods();//只能调用当前类中的public方法,可以是继承到的,也可以是自定义的
for(Method tmp:ms)
System.out.println(tmp);
获取当前类中所定义的方法,无视范围约束
Class clz=Student.class;
Method[] ms=clz.getDeclaredMethods();//只能获取当前类中的所定义的方法,包括private方法,不包括继承的道德方法
for(Method tmp:ms)
System.out.println(tmp);
获取指定类中的指定方法
// 使用类加载器加载指定名称的类,类名称应该使用全名,会有受检型一场
Class clz = Class.forName("com.yan3.Student");
Method method = clz.getMethod("p1", int.class); //如果查找失败则 NoSuchMethodException
System.out.println(method);
Method method2=clz.getMethod("p1", String.class);
System.out.println(method2);
获取当前类中所定义的指定方法,无视范围约束
// 使用类加载器加载指定名称的类,类名称应该使用全名,会有受检型一场
Class clz = Class.forName("com.yan3.Student");
Method method = clz.getDeclaredMethod("p1", int.class); //void com.yan3.Student.p1(int)
System.out.println(method);
Method method2=clz.getDeclaredMethod("p1", String.class);//private void com.yan3.Student.p1(java.lang.String)
System.out.println(method2);
【面试】创建实例
通过反射机制生成对象
-
使用Class对象的newInstance方法创建Class类对应类的对象
Class<?> clz=String.class; Object str=clz.newInstance();//使用String类中的无参构造器构建一个String类型的对象
-
使用Class对象获取当前类中所定义的构造器,再使用Constructor对象的newInstance方法创建对象
//实际上就是使用new String("abc")构建一个string类型的对象 Class<?> clz=String.class; Constructor construcor=clz.getConstructor(String.class); Object obj=construcor.newInstance("abc");
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)