被面试官问spring框架版本,java发射原理,C++中有反射吗?于是,今天我自己整理整理反射的笔记。
概述
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.要是想通过对象来找其对应的类名,就得使用反射。
1. 获得Class对象
三种方式
如何得到各个字节码对应的实例对象?
每个类被加载后,系统会为该类生成对应的Class对象,通过Class对象可以访问到JVM中的这个类,3种方式:
-
使用Class类的Class.forName(String n)静态方法,n表示类全名;包名.子包名…类名
-
调用某个类的class属性获取Class对象,如
Date.class
会返回Date类对应的Class对象(其实就是得到一个类的一份字节码文件); -
调用某个对象的getClass()方法。该方法属于Object类;Class clz =
new Date().getClass()
;
说明:第一二种方式是根据类,forName可能会抛出ClassNotFoundException异常
获得运行对象的3中方式,最多的是Class.forName()
Class类中的toString( )方法:
接口和基本数据类型直接打印名称,其他类型,前边加一个class
说明:Void类可以和包装类类比来记忆。Void.TYPE == void.class; //true
包装类的TYPE属性表示基本数据类型的字节码,所以上边是相等的。
数组只要类型和维数一样就表示一份字节码,与数组中的内容无关
2. 获得class对象后,从class中获取各种信息
注:通过子类可以找到父类,不能通过父类找子类
(1)通过class对象获得信息(基本信息)
package test;
import java.lang.reflect.Modifier;
public class BaseClassDemo extends Super implements IU,IM{
public String name;
public class Inner1{}
public interface Inner2{}
public static void main(String[] args) throws Exception{
//三种方式
Class<BaseClassDemo> clz = BaseClassDemo.class;
//Class<?> clz = Class.forName("test.BaseClassDemo");
//Class<? extends BaseClassDemo> clz = new BaseClassDemo().getClass();
//Class<IM> clz = IM.class;
//得到BaseClassDemo的包
System.out.println(clz.getPackage());//package test
//得到全限类名
System.out.println(clz.getName());//test.BaseClassDemo
//得到类的简称
System.out.println(clz.getSimpleName());//BaseClassDemo
//得到类的直接父类
System.out.println(clz.getSuperclass());//class test.Super
//得到类的接口
Class<?>[] ins = clz.getInterfaces();
for (Class<?> c : ins) {
System.out.println(c);
//interface test.IU
//interface test.IM
}
//获得类public修饰的的内部类/接口
ins = clz.getClasses();
System.out.println("长度= " + ins.length);
for (Class<?> c : ins) {
System.out.println(c);
//interface test.BaseClassDemo$Inner2
//class test.BaseClassDemo$Inner1
}
//获得类的修饰符
int mod = clz.getModifiers();
System.out.println(mod);//1 表示public
System.out.println(Modifier.toString(mod));//public
}
}
(2)通过class对象获得构造器(4个方法)
-
Constructor<>[] getConstructors( ) 得到类的所有public公共构造方法
-
Constructor<> getConstructor(Class<> paraType) 得到public对应参方法
-
Constructors<>[] getDeclaredConstructors( ) 所有的构造方法,与权限无关
-
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.s
1--> public char com.linger.svm.A.c
2--> public char com.linger.svm.A.c
3--> private java.lang.String com.linger.svm.Test.hahha
3--> public boolean com.linger.svm.Test.s
private java.lang.String com.linger.svm.Test.hahha
(5)通过class对象获得注解Annotation
@Inherited//可继承的
@Deprecated
Class<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); //