反射:重点----每天都用反射,但是每天都不写反射
Java的反射技术是java程序的特征之一,它允许运行中的Java程序对自身进行检查,或者说“自审”,并能直接操作程序的内部属性。
使用反射可以获得Java类中各个成员的名称并显示出来。简单的说,反射就是让你可以通过名称来得到对象(类,属性,方法)的技术。
反射的作用
可以通过反射机制发现对象的类型,发现类型的方法/属性/构造器
可以创建对象并访问任意对象方法和属性等
主要点:运行时探究和使用编译时未知的类
Dog d1 = new Dog();这样一个类在创造的时候经历了哪些过程呢?
简单的来说
类加载:由类加载器完成,类的class文件读入内存后,就会创建一个java.lang.Class对象。一旦某个类被载入JVM中,同一个类就不会再次被载入。
连接:把类的二进制数据合并到JRE中。
初始化 :JVM负责对类进行初始化,也就是对静态属性进行初始化。在Java类中,对静态属性指定初始值的方式有两种:
(1)声明静态属性时指定初始值;
(2)使用静态初始化块为静态属性指定初始值。
在我们上边的案例中:
1.JVM利用DogClassLoader先将Dog类加载到内存,然后马上产生了一个Class类型的对象,该对象可以看成是一个模型,以后无论创建多少个Dog类的实例都是利用该模型来生成。
---所以一个类所对应的Class类型的对象只有一个。
根据这个模型生成Dog类实例反射正是利用了java的这种加载方式主动完成了对象的创建及使用。
使用反射:
遵循三个步骤
第一步是获得你想操作的类的 java.lang.Class 对象
第二步是调用诸如 getDeclaredMethods 的方法
第三步使用 reflection API 来操作这些信息
简单案例01:
Class c1 = String.class;
Class c2 = Class.forName("java.lang.String");
Class c3 = "abc".getClass();
实验2:通过反射做一些事情
Integer a=13;
Class c=a.getClass();//获得了a的类型
案例03:
Integer a=13;
Class c=a.getClass();
Field[] field=c.getDeclaredFields();
for(Field f:field){
System.out.print(f.getType()+" ");
System.out.println(f.getName());
}
我们获得了Integer类中的所有属性
那能不能修改呢?
Dog d=new Dog();
Class clazz=d.getClass();
Field field=clazz.getDeclaredField("num");
field.set(d, 1000);
System.out.println(field.get(d));
案例04:
Class c1=Outer.class;
Method[] methods=c1.getDeclaredMethods();
for(Method method:methods){
System.out.print(method.getName()+" ");
System.out.print(method.getReturnType()+" ");
System.out.println(method.getParameterTypes());//这里如果是数组的话该怎么处理
}
获得该类中所有的方法
Dog d=new Dog();
Class clazz=d.getClass();
Method method=clazz.getDeclaredMethod("show",int.class,String.class);
method.invoke(d,10,"zhangsan");
案例05:
Integer a=13;
Class c=a.getClass();
Class c1=Outer.class;
Constructor [] constructors=c.getDeclaredConstructors();
for(Constructor constructor:constructors){
System.out.print(constructor.getName()+" ");
System.out.println(Arrays.toString(constructor.getParameterTypes()));
}
获得所有的构造函数
重点:通过反射来创造类的实例
实验01:
通过类名来创建类实例
Class cls=Outer.class;//obj指向的A类的对象 总体来说还是Object
Object o=cls.newInstance();
------------------------------------------------------注意它和我们的实例对象一样吗
Class<?> clazz=Class.forName("包名.类名");
Wode o=(Wode) clazz.newInstance();
注意点:
Class.forName() 静态方法,可以利用类名在 CLASSPATH 中查找对应的类,并且装载到内
存, 返回这个” class“
Class.forName()加载类的过程采用” 懒惰方式,即检查发现如果已经加载了(内存中存在)就丌再加载,直接返回已经加载的类,相当于“手工”去检查内存中是否已经加载了某个类
.newInstance()方法,会利用默认(无参数)构造器创建类实例(实例对象)
补充点:获得class对象三种方式
方式一:如果一个类的实例已经得到,你可以使用
【Class c = 对象名.getClass(); 】
例: TextField t = new TextField();
Class c = t.getClass();
Class s = c.getSuperclass();
方式二:如果你在编译期知道类的名字,你可以使用如下的方法
【 Class c = JButton.class; 】
或者 Class c = Integer.TYPE;
如果类名在编译期不知道, 但是在运行期可以获得, 你可以使用下面的方法
【 Class c = Class.forName(strg); 】
在这里要注意:
newInstance: 弱类型。低效率。只能调用无参构造。
new: 强类型。相对高效。能调用任何public构造。
newInstance()是实现IOC、反射、面对接口编程 和 依赖倒置 等技术方法的必然选择,new 只能实现具体类的实例化,不适合于接口编程。
通过反射操作对象属性
Class clss=Outer.class;
Object o=clss.newInstance();
Field field=clss.getDeclaredField("num");
Object val=field.get(o);
System.out.println(val);
上边的实验,我们发现用这种方式也是可以的Class cls=Outer.class;
--------------------------
接着上边的实验,我们发现了
Object val=field.get(o);
val=15;
Object val2=field.get(o);
System.out.println(val);
通过这种方式完全可以修改我们的属性