反射面试专题
来源:互联网 发布:城乡居民收入差距数据 编辑:程序博客网 时间: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); //
- 反射面试专题
- 面试专题
- Java 反射专题
- Java反射专题1
- Java反射专题2
- 反射专题3
- 反射专题4
- 反射专题5
- Java 面试专题
- 面试题目 字符串专题
- 【面试】字符串专题
- 面试题目 字符串专题
- 【面试】字符串专题
- 【面试】之专题总纲
- 面试题目 字符串专题
- Epic 面试专题
- 互联网笔试面试专题
- Pocket Gems面试专题
- BZOJ4872 [Shoi2017]分手是祝愿
- springMVC两种方式实现多文件上传
- git切换分支前stash的用法
- 更改mysql字符集utf8至utf8mb4
- 剪切板底层机制
- 反射面试专题
- 如何使用GIT上传自己的代码到码云(windows)
- Intellij插件
- Android 进阶——NDK开发
- 100道动态规划——38 HDU 5763 Another Meaning KMP + DP
- 程序员,如何从平庸走向理想?
- CENTOS7搭建HADOOP2.7.3集群
- 实现搜索框(含历史搜索记录)
- TextView加粗、删除线、下划线