Java学习笔记——反射机制

Java学习笔记——反射机制,第1张

文章目录
  • Java反射机制
    • 反射
    • 特点
  • Java创建对象思想
  • 类对象
    • 类对象和类的对象
    • 类对象中封装的对象
    • 三种获取类对象的方法
  • Class对象的常用方法
    • 获取相关属性
    • 获取构造方法
    • 获取相关方法
    • 获取泛型相关信息
      • 属性类型
      • 泛型类
      • 反射运行配置文件内容
      • Peroperties类
  • 具体使用
  • 【面试】创建实例

Java反射机制

Java反射机制的核心就是在程序运行时动态加载类并获取类的详细信息,从而实现 *** 作类或者对象的属性和方法。本质上就是JVM得到Class对象,再通过Class对象进行反编译,从而获取对象的各种信息。

Object obj=new Date();//编译期类型和运行时类型
System.out.println(obj.toString());
反射

java的反射Reflection就是一种自省机制,可以实现在运行时动态获取类的信息以及动态的调用对象的方法。

通过反射运行程序在运行时通过反射API获取任何一个已知名称的class的内部信息,包括修饰词【public、static】、父类、实现的接口、属性和方法。反射机制可以在运行时改变属性的内容,也可以调用任意的方法,可以破坏范围限定词的约束【破坏类的封装性】

反射机制的工作原理:Class对象是由.class文件读入内存并自动创建的,获取Class对象后可以获取对应类的相关信息

特点

优点:可以在运行时获取类的各种内容,能够灵活的创建代码,这些代码在运行时进行装配,不需要在组件之间进行源代码的连接,更加容易的实现面向对象的松耦合

缺点:

  • 会消耗一定的系统资源。一般不需要动态创建对象时,不建议使用反射。性能损耗太大了
  • 反射机制会忽略权限检查,可能会破坏类的封装性,从而导致安全问题。
Java创建对象思想

类对象 类对象和类的对象
  • 类对象是类加载的产物,封装了一个类的所有信息(类名、父类、接口、属性、方法、构造方法)
  • 类的对象是基于某个类new出来的对象,也称为实例对象
类对象中封装的对象
类对象中封装的对象描述
Field成员变量(属性)
Method方法
Constructor构造器
PackageName包名
ClassName类名
SuperClass实体类的父类Class
Interface接口
三种获取类对象的方法
  • 通过类的对象,获取类对象
Person person = new Person();
Class clazz=person.getClass();

场景:常用于对象的字节码获取方式

  • 通过类名获取类对象
Class clazz=类名.class;

场景:常用于参数的传递

  • 以静态方法通过全类名获取类对象
Class clazz=Class.forName("全类名");

场景:常用于读取配置文件,将类名定义在配置文件中。读取文件,加载类对象

还有两种:

  1. 使用TYPE语法获取,用于获取原生数据类型Boolean.TYPE
  2. 使用类加载器的方法loadClass
Class对象的常用方法 获取相关属性
返回值方法名称描述
StringgetName()获取类的名称
FieldgetField(String name)获取public修饰的指定名称的成员变量
getDeclaredField(String name)获取指定名称的属性,包括private属性
Field[]getFields()获取public修饰的所有成员变量
getDeclaredFields()获取所有自身定义的属性,包括private属性
Objectget(Object obj)通过成员变量获取指定对象的值
voidset(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=new ArrayList(); ,这里没有继承关系问题,

List 和 List 没有任何关系,是两种不同的类型

List list=new ArrayList();可以理解为 List list=new ArrayList<>();

属性类型
  • 获取普通类型的成员属性: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);
【面试】创建实例

通过反射机制生成对象

  1. 使用Class对象的newInstance方法创建Class类对应类的对象

    Class<?> clz=String.class;
    Object str=clz.newInstance();//使用String类中的无参构造器构建一个String类型的对象
    
  2. 使用Class对象获取当前类中所定义的构造器,再使用Constructor对象的newInstance方法创建对象

    //实际上就是使用new String("abc")构建一个string类型的对象
    Class<?> clz=String.class;
    Constructor construcor=clz.getConstructor(String.class);
    Object obj=construcor.newInstance("abc");
    

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/795707.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-06
下一篇 2022-05-06

发表评论

登录后才能评论

评论列表(0条)