Java--Class类相关与反射重温

一、注意点

  • 面向对象的世界中,万事万物皆对象。
  • Java中的类,是java.util.Class类的实例对象
  • 类的类类型,可从以下三种方式得到:
    1. Class clazz = T.class;【因为每个.java文件在被编译成.class文件之后,T类内部就生成了一个静态属性,也就是T.class,它保存了这个类的基本信息】
    2. Class clazz = t.getClass(); 【底层是native方法,用Java声明,用C语言实现,最终在Java中实现,其实就是获取这个静态属性】
    3. Class clazz = Class.forName("类的全称"); 【代表动态加载类】

    动态实现装载类的方式:
    1. 隐式装载:new一个对象时,JRE检查并自动加载
    2. 显式装载:①Class.forName()ClassLoader.loadClass()

  • 基本数据类型和void关键字都存在类类型。
  • Class.getCanonicalName() 返回的是Java Language Specification中定义的基础类的规范化名称,换句话,就是返回全类名,没有就返回null。

二、Class类能给我们什么信息(常见):

  • 获取并打印所有属性信息【访问权限+属性类名+属性名】
public static Field[] getFieldList(Class clazz) {
    Field[] fields = clazz.getDeclaredFields();///获取类中所有的属性(忽略访问修饰符)
    for (Field fieid : fields) {
        Type type = fieid.getType();///获取属性类型
        System.out.println(Modifier.toString(fieid.getModifiers())+" "+type.getTypeName() + " " + fieid.getName());
    }
    return fields;
}

关键点:

  • Class.getDeclaredFields():Field[] : 获取类中所有的属性(忽略访问修饰符)

  • Fieid.getType():Type : 获取属性类型

  • 获取所有方法信息【访问权限+方法名+ 返回值类型 + 方法参数类型】

public static Method[] getMethodList(Class clazz) {
    Method[] methods = clazz.getDeclaredMethods();
    for (Method method : methods) {
        ///分析每一个方法
        getMethodMsg(method);
    }
    return methods;
}
/**
 * 获取Method的返回值类型和参数类型等信息
 * @param method
 */
public static void getMethodMsg(Method method) {
    StringBuilder sb = new StringBuilder();
    ///获取访问权限
    sb.append(Modifier.toString(method.getModifiers())+" ");
    ///返回值类型
    Class returnType = method.getReturnType();
    ////参数类型
    Class[] paramsTypeList = method.getParameterTypes();
    sb.append(returnType.getName()).append("
 ").append(method.getName()).append("(");
    for (Class paramsType : paramsTypeList) {
        sb.append(paramsType.getName()).append(",");
    }
    sb.append(")");
    ///删除多余的","号
    if (sb.lastIndexOf(",") == sb.length() - 1) {
        sb.delete(sb.length() - 2, sb.length() - 1);
    }
    System.out.println(sb.toString());
}

关键点:

  • Class.getDeclaredMethods():Method[] : 获取类中所有的方法(忽略访问修饰符)

  • Method.getParameterTypes():Class[] : 获取方法的参数类类型列表

  • Method.getReturnType():Class : 获取方法的返回值类型

  • 获取构造函数信息【函数名+参数类型】

public static void getConstructorMsg(Object obj) {
    Class clazz = obj.getClass();
    ///封装了构造函数的信息
    Constructor[] constructors = clazz.getDeclaredConstructors();
    for (Constructor item:constructors){
        System.out.print(item.getName()+"(");
        Class[] paramTypes = item.getParameterTypes();
        for (Class type: paramTypes){
            System.out.print(type.getName()+",");
        }
        System.out.println(")");
    }
}

关键点:

  • Class.getDeclaredConstructors():Constructor[] : 获取这个类中所有的构造函数信息(每一个Constructor类对象拥有其中一个构造函数的信息)

  • Constructor.getParameterTypes(): Class[] : 获取这个构造函数的参数类类型列表

  • 调用无参构造方法生成类对象

//动态生成类对象(无参构造函数)
Class clazz = (Class)MyObj.getClass();
T obj = clazz.newInstance();
  • 调用有参构造方法生成类对象
//动态生成类对象(有参构造函数)
Class[] classes = new Class[]{Integer.class, String.class};
Object[] values = new Object[]{100, "hello world"};
Constructor constructor = clazz.getDeclaredConstructor(classes);
C res = (C) constructor.newInstance(values);
  • 对象类型匹配
    方式一:对象 instanceof 类型
如:
Animal dog = new Dog();///Dog是Animal的子类
boolean b = dog instanceof Dog;//判断dog这个对象是不是Dog类型的实例,结果为true

方式二:class对象.isInstance(类对象)

如:
Animal dog = new Dog();///Dog是Animal的子类
boolean b = Dog.class.isInstance(dog);

三、反射的基本用法:

  1. 调用方法Object 返回值 = method.invoke(对象, 参数值列表)
    相当于:对象.method(参数值列表)
    如:
Person xiaoming  = new Person();
Class c = xiaoming.getClass();
//Method m = c.getMethod("print", new Class[]{int.class, int.class});
//m.invoke(xiaoming, new int[]{1,2});
Method m = c.getMethod("print", int.class, int.class);
m.invoke(xiaoming, 1,2);

唯一确定某个方法的因素:方法名称 + 方法参数列表

  1. 修改属性Field.set(对象, 要修改的field值)
    相当于:对象属性赋值操作
    如:
Field field = teacher.getClass().getDeclaredField("name");//获取teacher对象的name属性
field.setAccessible(true);//无视field的访问权限问题
field.set(teacher, "小明老师");

四、关于集合的反射

  • 反射都是在编译完之后的操作
  • 编译之后,有着泛型的集合类型,将去泛型化。【换句话说,Java集合类的泛型,是为了防止错误输入而设定的,只在编译时有效】
  • 通过反射,可以绕过编译,这样一来,比如: 在ArrayList list上执行add(100);添加一个整型,则不会报错。【但是这样做,就不能使用for:each来遍历集合】
List list = new ArrayList<>();
list.add("hello");///添加元素“hello”
try {
    Method methodAdd = list.getClass().getMethod("add", Object.class);
    methodAdd.invoke(list,1);///添加元素1
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
    e.printStackTrace();
} 
System.out.println(list.toString());
///以下遍历报错:java.lang.ClassCastException
///for (String item:list){
///    System.out.println(item);
///}

五、Java反射相关的异常

  • ClassNotFoundException:未在命名空间内找到指定类。(类名错误,或者类不存在)
  • SecurityException:修改了不允许修改的Accessible标志时
  • NoSuchMethodException:找不到特定方法
  • NoSuchFieldException:找不到特定属性
  • IllegalArgumentException:向方法传递了一个不合法或不正确的参数
  • InstantiationException:程序视图使用Class类中的newInstance方法(此方法本身不能应用于基本类型)创建一个类的实例,而指定的类对象无法被实例化时。【如:实例化抽象类或接口,或者视图创建数组类的实例】
  • IllegalAccessException:当我的程序代码想要通过反射去创建一个实例(非数组)、设置或获取一个字段,或者调用一个方法,但是当前正在执行的方法无法访问指定类、字段、方法或构造函数的定义时,抛出的异常。

你可能感兴趣的:(Java--Class类相关与反射重温)