反射面试专题

来源:互联网 发布:城乡居民收入差距数据 编辑:程序博客网 时间:2024/06/05 08:10
被面试官问spring框架版本,java发射原理,C++中有反射吗?于是,今天我自己整理整理反射的笔记。

1.知识1

1.java.lang.class

Class 类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也表示为 Class 对象。

反射

Person p = new Student();若程序运行时接收到外部传入的一个对象,该对象的编译类型是Object,但程序又需要调用该对象运行类型的方法:    1.若编译和运行类型都知道,使用 instanceof判断后,强转。    2.编译时根本无法预知该对象的类属于哪些类,程序只能依靠运行时信息来发现对象和类的真实信息,这是反射就必须使用了。    3.要是想通过对象来找其对应的类名,就得使用反射。

获得Class对象

如何得到各个字节码对应的实例对象?每个类被加载后,系统会为该类生成对应的Class对象,通过Class对象可以访问到JVM中的这个类,3种方式:

    (1) 使用Class类的forName(String n)静态方法,n表示类全名;包名.子包名….类名

    (2) 调用某个类的class属性获取Class对象,如Date.class会返回Date类对应的Class对象(其实就是得到一个类的一份字节码文件);

    (3) 调用某个对象的getClass()方法。该方法属于Object类;Class clz = new Date().getClass();

说明:第一二种方式是根据类,forName可能会抛出ClassNotFoundException异常

获得运行对象的3中方式,最多的是Class.forName()


这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

Class类中的toString( )方法:

这里写图片描述

接口和基本数据类型直接打印名称,其他类型,前边加一个class

这里写图片描述

这里写图片描述

说明:Void类可以和包装类类比来记忆。Void.TYPE == void.class; //true

包装类的TYPE属性表示基本数据类型的字节码,所以上边是相等的。

数组只要类型和维数一样就表示一份字节码,与数组中的内容无关


获得class对象后,从class中获取各种信息

注:通过子类可以找到父类,不能通过父类找子类

(1)通过class对象获得信息

public class BaseClassDemo extends Super implements IU,IM{    public String name;    public class Inner1{}    public interface Inner2{}    public static void main(String[] args) {        Class<BaseClassDemo> clz = BaseClassDemo.class;        //Class<IM> clz = IM.class;        //得到BaseClassDemo的包        System.out.println(clz.getPackage());        //得到全限类名        System.out.println(clz.getName());        //得到类的简称        System.out.println(clz.getSimpleName());        //得到类的直接父类        System.out.println(clz.getSuperclass());        //得到类的接口        Class<?>[] ins = clz.getInterfaces();        for (Class<?> c : ins) {            System.out.println(c);        }        //获得类public修饰的的内部类/接口        ins = clz.getClasses();        System.out.println("长度= " + ins.length);        for (Class<?> c : ins) {            System.out.println(c);        }        //获得类的修饰符        int mod = clz.getModifiers();        System.out.println(mod);//1表示public        System.out.println(Modifier.toString(mod));    }}

(2)通过class对象获得构造器(4个方法)

1.Constructor<>[] getConstructors( ) 得到类的所有public公共构造方法
2.Constructor<> getConstructor(Class<> paraType) 得到public对应参方法

3.Constructors<>[] getDeclaredConstructors( ) 所有的构造方法,与权限无关
4.Constructors<> getDeclaredConstructors(Class<> paraType) 所有的构造方法,与权限无关

package com.linger.svm;import java.lang.reflect.Constructor;class Employee{    private String name;    private int age;    private  Employee() {    }    Employee(String name) {    }    public Employee(String name,Integer age) {    }   }public class T {    public static void main(String[] args) throws Exception {        Class<Employee> clz = Employee.class;        //得到类的所有public公共构造方法        Constructor<Employee>[] cs = (Constructor<Employee>[]) clz.getConstructors();        for (Constructor<Employee> c : cs) {            System.out.println(c);              //私有不能获得public com.linger.svm.Employee(java.lang.String,java.lang.Integer)        }        //得到public指定的构造器        //Employee(String name,int age)         //Constructor<Employee> con=clz.getConstructor(String.class,int.class);        //Employee(String name,Integer age)         Constructor<Employee> con=clz.getConstructor(String.class,Integer.class);        System.out.println(">>  "+con);        //>>  public com.linger.svm.Employee(java.lang.String,java.lang.Integer)        //===========================================        /**         * 带declared:访问不受访问权限控制:私有也可获得         *  Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)             返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法。             Constructor<?>[] getDeclaredConstructors()          */        cs = (Constructor<Employee>[]) clz.getDeclaredConstructors();        for (Constructor<Employee> c : cs) {            System.out.println("-->"+c);            /**-->private com.linger.svm.Employee()               -->com.linger.svm.Employee(java.lang.String)               -->public com.linger.svm.Employee(java.lang.String,java.lang.Integer)             */        }        con = clz.getDeclaredConstructor();//无参        System.out.println(con); //private com.linger.svm.Employee()    }}

(3)通过class对象获得方法(4个方法)

1.Method getMethod(String name, Class<> paraType ) 获得该类对应参的public方法
2.Method[] getMethods( ) 获得该类及父类(就这一个可以获得父类) 所有public方法

3.Method getDeclaredMethod(String name,Class<> paraType) 获得该类对应参方法,无权限限制
4.Method[] getDeclaredMethods( ) 获得该类 所有方法,无权限限制

    Method m = clz.getMethod("main", String[].class);    System.out.println(m);    //public static void com.linger.svm.Test.main(java.lang.String[]) throws java.lang.Exception    m = clz.getMethod("toString");    System.out.println(m);//public java.lang.String java.lang.Object.toString()

(4)通过class对象获得字段(4个方法)

1.Field[] getFields(); //获得所有的public 字段,包括继承
2.Field getField(String name); //获取对应name的public字段,包括继承

3.Field[] getDeclaredFields(); //获得该类所有,无关权限
4.Field getField(String name); //获得该类对应字段, 无关权限

package com.linger.svm;import java.lang.reflect.Field;class A{    protected String name;    private int  age;    public char c;}public class Test extends A {    private String hahha;    public boolean s;    public static void main(String[] args) throws Exception {        Class<Test> clz = Test.class;        //获得所有的public 字段,包括继承        Field[] fs = clz.getFields();        for (Field field : fs) {            System.out.println("1-->  "+field);        }        //指定的一个 public的,包括继承        Field f = clz.getField("c");        System.out.println("2--> "+f);        //得到所有的字段,只能获取当期类里面的,和访问权限无关        fs = clz.getDeclaredFields();        for (Field field : fs) {            System.out.println("3--> "+field);        }        //获得当前类指定一个字段,和访问权限无关         f = clz.getDeclaredField("hahha");        System.out.println(f);    }}//结果1-->  public boolean com.linger.svm.Test.s1-->  public char com.linger.svm.A.c2--> public char com.linger.svm.A.c3--> private java.lang.String com.linger.svm.Test.hahha3--> public boolean com.linger.svm.Test.sprivate java.lang.String com.linger.svm.Test.hahha

(5)通过class对象获得注解Annotation

@Inherited//可继承的@DeprecatedClass<AnonationDemo> clz = AnonationDemo.class;//所有的标签,但是是RUNTIME类型,可以获取继承过来的Annotation[] as = clz.getAnnotations();


2.知识2–使用反射生成并操作对象

(1)反射创建对象的两种方式


获得class对象clz
—->调用newInstance( )方法

注:实际上是利用默认无参构造器来创建实例的

import javax.swing.JFrame;public class Test{    public static void main(String[] args) throws Exception {        //传统new 方式        JFrame jf = new JFrame();        jf.setVisible(true);   //出现对话框        //使用反射来做:方式一默认构造器        //得到JFrame字节码,只有forName()那里要全限定名,前边都行        Class<javax.swing.JFrame> clz = (Class<JFrame>) Class.forName("javax.swing.JFrame");        //创建对象        JFrame jf2  = clz.newInstance();        jf2.setVisible(true);           }}


获得class对象clz
—->调用getConstructor(Class<>)得到构造器
—->通过构造器调用newInstance( )方法

//反射方式2        Constructor<javax.swing.JFrame> cons = (Constructor<JFrame>)    Class.forName("javax.swing.JFrame").getConstructor(String.class);        //创建对象        JFrame jf3  = cons.newInstance("测试窗口"); //参数和获得的构造器必须对应        jf3.setVisible(true);   

这里写图片描述

说明:如果构造器是私有的,通过getDeclaredConstrctor可以获取,是否可以创建对象呢?
A: 虽然可以获取,但不能直接创建对象。还要设置可创建属性。

cons.setAccessible(true);

补充:读取配置文件中的字符串创建对象
文件:resources/obj.properties
内容:JFrame=javax.swing.JFrame

import java.util.Properties;import javax.swing.JFrame;public class Test{    public static void main(String[] args) throws Exception {               new Test().getInstance();    }    public void getInstance() {        InputStream in = this.getClass().getClassLoader().getResourceAsStream("obj.properties");        Properties p = new Properties();        try {            p.load(in);            String val = p.getProperty("JFrame");            System.out.println(val);            //使用反射创建对象            Class<JFrame> clz = (Class<JFrame>) Class.forName(val);            JFrame j = clz.newInstance();            j.setVisible(true);        } catch (Exception e) {            e.printStackTrace();        }    }}

(2)调用方式

通过class对象获得method方法上面已经说了4种方法。获得method对象后,就可以调用它对应的方法。method里包含一个invoke( )方法,如下:

Object invoke(Object obj,Object...args)/*说明:obj - 从中调用底层方法的对象(必须要写,静态方法可以写null)       args - 用于方法调用的参数        返回:            使用参数 args 在 obj 上指派该对象所表示方法的结果 */

对于私有方法、public方法、static方法做一下测试。

class Dept{    public String[] publicMethod(String name){        //return name +" 恭喜发财";        return new String[]{name};    }    private void privateMethod(){        System.out.println("show");    }    public static void staticMethod(){        System.out.println("staticMethod");    }}

测试:

public static void main(String[] args) throws Exception {        //使用反射来调用Dept里的法                Class<Dept> clz   = Dept.class;        //public void publicMethod(String name)        //先得到这个需要被调用的方法        Method m = clz.getMethod("publicMethod", String.class);        /**         *  Object invoke(Object obj, Object... args)   对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。          *  参数:                obj - 从中调用底层方法的对象                args - 用于方法调用的参数                 返回:                使用参数 args 在 obj 上指派该对象所表示方法的结果          */        System.out.println(m);//public java.lang.String[] Dept.publicMethod(java.lang.String)        //Object ret = m.invoke(clz.newInstance(), "will");//YES        Object ret = m.invoke(clz.newInstance(), new Object[]{"Will"});         //===================        //private void privateMethod()              m = clz.getDeclaredMethod("privateMethod");             //调用之前设置可访问的        m.setAccessible(true);        m.invoke(clz.newInstance());        //  public static void staticMethod()        /**         * 如果底层方法是静态的,那么可以忽略指定的 obj 参数。该参数可以为 null。         如果底层方法所需的形参数为 0,则所提供的 args 数组长度可以为 0 或 null或不写         *          */        m = clz.getMethod("staticMethod");        //m.invoke(null);//YES        //m.invoke(null, null);//YES        m.invoke(null,new Object[]{});//YES    }

说明:第二个参数为可变个数参数(和数组一样),特别注意写法。如下:

public class InvokeMethodByVarArgsDemo {    public static void show(int ...is){        System.out.println("基本类型执行过来");    }    public static void show(String ...sArr){        System.out.println("引用类型执行过来");    }    public static void main(String[] args) throws Exception {        Class<InvokeMethodByVarArgsDemo> clz  =InvokeMethodByVarArgsDemo.class;        //调用public void show(int ...is)        Method m  = clz.getMethod("show", int[].class);        //m.invoke(null,1,2,3);//ERROR        m.invoke(null,new int[]{1,2,3});//YES        m.invoke(null,(Object)new int[]{1,2,3});//YES        m.invoke(null,new Object[]{new int[]{1,2,3}});//YES        //public static void show(String ...sArr)        m = clz.getMethod("show", String[].class);        //m.invoke(null, "A","B","C");//ERROR        //m.invoke(null, new String[]{"A","B","C"});//ERROR        //m.invoke(null, (Object)new String[]{"A","B","C"});//装包  YES        m.invoke(null, new Object[]{new String[]{"A","B","C"}});//装包  YES        /**         * public Object invoke(Object obj,Object... args)         *          * new Object[]{传递的实际参数};通用做法;         */    }}

第二个参数通用做法
new Object[]{传递的实际参数};

如果参数是泛型T,getMethod的时候用T的上限字节码(下限),没有就用Object字节码

(3)访问和设置字段

getXxx( ) , setXxx( ) ,引用类型去掉XXX即可

        Cat c = clz.newInstance();        f = clz.getDeclaredField("age");                f.setAccessible(true);        f.setInt(c, 16);  //
0 0
原创粉丝点击