文章思路:
反射
(1)类的加载及加载器
(2)反射:通过字节码文件对象,去使用成员变量,构造方法,成员方法
(3)反射的使用
A:通过反射获取构造方法并使用
B: 通过反射获取成员变量并使用
C: 通过反射获取成员方法并使用
(4)反射案例
A:通过反射运行配置文件的内容
B:通过反射越过泛型检查
C:通过反射给任意的一个对象的任意属性赋值为指定的值
(5)动态代理
-- 类加载
*类的加载:
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,链接。初始化三步来实现对这个类进行初始化。
*加载:
就是指将class文件读入内存,并为之创建一个class对象。
***任何类被使用时系统都会建立一个Class对象
*链接:
1.验证 是否有正确的内部结构,并和其他类协调一致
2.准备 负责为类的静态成员分配内存,并设置默认初始化值
3.解析 将类的二进制数据中的符号引用替换为直接引用
*初始化: 类栈开辟空间,默认初始化,构造初始化
-- 类初始化时机
1.创建类的实例
2.访问类的静态变量,或者为静态变量赋值
3.调用类的静态方法
4.使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
5.初始化某个类的子类
6.直接使用java.exe命令来运行某个主类
-- 类加载器
负责将.class文件加载到内存中,并为之生成对应的Class对象。
虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行。
-- 类加载器的组成
Boostrap ClassLoader:根类加载器
Extension ClassLoader:扩展类加载器
System ClassLoader:系统类加载器
详解:
1.Boostrap ClassLoader:根类加载器
也被称为引导类加载器,负责java核心类的加载
比如System String等。在JDK中JRE的lib目录下rt.jar文件中
2.Extension ClassLoader:扩展类加载器 (扩展时使用)
负责JRE的扩展目录中jar包的加载。
在JDK中JRE的lib目录下ext目录
3.System ClassLoader:系统类加载器 (一般自己使用时用)
负责在JVM启动时加载来自java命令的class文件以及classpath环境变量所指定的jar包和类路径
-- 反射
*含义:
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意个对象,都能够调用他的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
*思路:
要想解剖一个类,必须先要获取到该类的字节码文件对象,而解剖使用的就是Class类中的方法,所以先要获取到每一字节码文件对应的Class类型的对象。
* 反射:就是通过Class文件对象去使用该文件中的成员变量,构造方法,成员方法。
* 要想这样使用,首先必须得到Class文件对象,其实也就是的到Class类的对象。 Class类:代表字节码对象
*Class类:
成员变量 Field
构造方法 Constructor
成员方法 Method
*反射思路:
通过class类得到字节码对象,然后通过字节码对象得到Field,Constructor,Method三个对象,调用方法,就称为反射。
*获取class文件对象的方式:
A:Object类的getClass()方法
B:数据类型的静态属性class
C:Class类中的静态方法
public static Class forName(String Name)
*↑↑↑↑一般使用情况:
A:自己玩 任选一种,第二种比较方便
B:开发 第三种
*为什么?
因为第三种是一个字符串,而不是一个具体的类名。这样我们就可以把这样的字符串配置到配置文件中(方便更改)。
*举例:
一、获取字节码对象
二、通过反射获取无参构造方并使用
三、通过反射获取带参构造方法并使用
四、通过反射获取私有构造方法并使用
五、通过反射获取成员变量并使用
六、通过反射获得带参带返回成员方法并使用
七、通过配置文件运行类中的方法,(通过反射运行配置文件内容)
八、我给你ArrayList<Integer> 的一个对象 ,我想在这个集合中添加一个字符串数据,如何实现呢?
九、通过反射写一个通用的设置某个对象的某个属性为指定的值
*举例说明:
一、获取字节码对象:
// 方式1
Person p1 = new Person();
Class c1 = p1.getClass();
Person p2 = new Person();
Class c2 = p2.getClass();
System.out.println("方式一:");
System.out.println(p1 == p2);
System.out.println(c1 == c2);
// 方式2
Class c3 = Person.class;
//举例:int.class String.class
System.out.println("方式二:");
System.out.println(c1 == c3);
// 方式3
try {
Class c4 = Class.forName("org.bx.Person");
System.out.println("方式三:");
System.out.println(c1 == c4);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
二、通过反射获取构造方并使用
//获取字节码文件对象
Class c = Class.forName("org.bx.demo01.Person");
//获取构造方法
//public Constructor[] getConstructor() :获取所有公共的构造方法
//public Constructor[] getDeclaredConstructor() :获取所有公共的构造方法
/*//Constructor[] cons = c.getConstructors();
Constructor[] cons = c.getDeclaredConstructors();
for(Constructor cs : cons){
System.out.println(cs);
}*/
//获取无参构造方法
//public Constructor<T> getConstructor(Class<?>... param)
//参数表示的是:你要获取构造方法的构造参数个数及数据类型的class字节码文件对象
Constructor con = c.getConstructor(); //返回的是构造方法对象
//Person p = new Person();
//System.out.println(p);
//public T newInstance(Object... init)
//使用此Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例
Object obj = con.newInstance();
System.out.println(obj);
Person p = (Person)obj;
p.show();
三、通过反射去获取带参构造方法并使用:
//获取字节码文件
Class c = Class.forName("org.bx.demo01.Person");
//获取带参构造方法 (参数类型必须一致)
//public Constructor<T> getConstructor(Class<?>... params))
Constructor con = c.getConstructor(String.class,int.class,String.class);
//通过带参构造方法对象创建对象
//public T newInstance(Object... instance)
Object obj = con.newInstance("白鑫",19,"北京");
System.out.println(obj);
四、通过反射获取私有构造方法并使用
//获取字节码对象
Class c = Class.forName("org.bx.demo01.Person");
/* 获取私有构造方法
* NoSuchMethodException:不能匹配该方法
* 原因是一开始使用的方法只能是公共的,下面这中方式就可以了。
*/
Constructor con = c.getDeclaredConstructor(String.class);
/* 用该私有方法创建实例
* IllegalAccessException:非法的访问异常
* setAccessible:设置访问
* 值为true时指明反射的对象在使用时取消了Java语言访问检查
*/
con.setAccessible(true);
Object obj = con.newInstance("白鑫");
System.out.println(obj);
五、通过反射获取成员变量并使用
//获取字节码对象
Class c = Class.forName("org.bx.demo01.Person");
//获取所有的成员属性
/*Field[] fields = c.getDeclaredFields();
for(Field field : fields){
System.out.println(field);
}*/
/* 原来的写法
* Person p = new Person();
* p.address = "北京";
* System.out.println(p);
*/
//通过无参构造方法创建对象
Constructor con = c.getConstructor();
Object obj = con.newInstance();
//获取单个的成员变量
//获取address并对其赋值
Field addressField = c.getField("address");
//public void set(Object obj,Object value)
//将指定对象变量上此Field 对象表示的字段设置为指定的新值
addressField.set(obj,"北京");//给obj对象的addressField字段设置值为“北京”
System.out.println(obj);
//获取name并其赋值
Field nameField = c.getDeclaredField("name");
//关闭java语法检查
nameField.setAccessible(true);
nameField.set(obj, "白鑫");
System.out.println(obj);
//age属性一致
六、通过反射获得带参带返回成员方法并使用
//获取字节码文件对象
Class c = Class.forName("org.bx.demo01.Person");
//获取所有的方法
// Method[] methods = c.getMethods(); //获取自己的包括父级的公共方法
// Method[] methods = c.getDeclaredMethods(); //获取自己的所有方法
// for(Method m : methods){
// System.out.println(m);
// }
Constructor con = c.getConstructor();
Object obj = con.newInstance();
/*
* Person p = new Person();
* p.show();
*/
//public Method getMethod(String name,Class<?>... params)
//第一个参数表示的方法名,第二个参数表示的是方法的参数的class类型
//获取单个方法并使用
System.out.println("----无参方法----");
Method m1 = c.getMethod("show");
//obj.m1();//错误
//public Object invoke(Object obj,Object... args)
//返回值是Object接收,第一个参数表示对象是谁,第二个参数表示调用该方法的实际参数
m1.invoke(obj); //本质上 调用obj对象的m1方法
System.out.println("----无参方法----");
Method m2 = c.getMethod("method",String.class);
m2.invoke(obj, "你好");
System.out.println("----有参方法----");
Method m3 = c.getMethod("method1",String.class,Integer.class);
Object objString = m3.invoke(obj,"hello",100);
System.out.println(objString);
String s = (String)m3.invoke(obj,"hello",100);
System.out.println(s);
System.out.println("----私有方法----");
Method m4 = c.getDeclaredMethod("function");
m4.setAccessible(true);
m4.invoke(obj);
七、通过配置文件运行类中的方法,(通过反射运行配置文件内容)
* 使用反射:
需要有配置文件配合使用
用class.txt代替
并且知道有两个键:className 、methodName
-- Test类:
//反射前的做法
// Student s = new Student();
// s.love();
// Teacher t = new Teacher();
// t.love();
// Worker w = new Worker();
// w.love();
//反射后的做法
Properties prop = new Properties();
FileReader fr = new FileReader("class.txt");
prop.load(fr);
fr.close();//关闭流操作
//获取数据
String className = prop.getProperty("className");
String methodName = prop.getProperty("methodName");
//反射
Class c = Class.forName(className);
Constructor con = c.getConstructor();
Object o = con.newInstance();
//调用方法
Method m = c.getMethod(methodName);
m.invoke(o);
-- class.txt文件,(模拟配置文件)
className=org.bx.testproject.Student
methodName=love
-- Student类
public void love(){
System.out.println("爱编程!");
}
八、我给你ArrayList<Integer> 的一个对象 ,我想在这个集合中添加一个字符串数据,如何实现呢?
ArrayList<Integer> al = new ArrayList<Integer>();
//操作前
//al.add("hello"); //错误
//al.add(10); //正确
//利用反射
Class c = al.getClass();//集合ArrayList的class文件对象
Method m = c.getMethod("add", Object.class);// 源文件的泛型返回的是Object类型
m.invoke(al,"hello");//调用array的add方法,传入的值是hello
m.invoke(al,"java");
九、通过反射写一个通用的设置某个对象的某个属性为指定的值
-- Tool类(公共执行类):
public void setProperty(Object obj,String propName,Object value) throws Exception{
//根据对象获取字节码文件对象
Class c = obj.getClass();
//获取该对象的peopName成员变量
Field f = c.getDeclaredField(propName);
//取消java语法检查
f.setAccessible(true);
f.set(obj, value);
}
-- 测试类
public class ToolTest {
public static void main(String[] args) throws Exception {
Person p = new Person();
Tool t = new Tool();
t.setProperty(p, "name", "白鑫");
t.setProperty(p, "age", 19);
System.out.println(p);
System.out.println("\n--------------");
Dog d = new Dog();
t.setProperty(d, "sex", '男');
t.setProperty(d, "price", 666.6f);
System.out.println(d);
}
}
//内部类Dog
class Dog{
char sex;
float price;
@Override
public String toString() {
return "Dog [sex=" + sex + ", price=" + price + "]";
}
}
//内部类Person
class Person{
private String name;
public int age;
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
-- 动态代理:
*代理:本来应该是自己做的事情,却请了别人来做,被请的人就是代理对象。
举例:春节回家买票让人代买
*动态代理:在程序运行过程中产生的这个对象,而程序运行过程中产生对象其实就是我们刚才反射讲解的内容,所以,动态代理其实就是通过反射来生成一个代理
*在java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象。JDK提供的代理只能针对接口做代理、我们有更强大的代理cglib
*Proxy类中的方法创建动态代理类对象
public static Object newProxyInstance(Classloader loader,Class<?>[] interface,InvocationHandler h)
最终会调用InvocationHandler的方法
*InvocationHandler
Object invoke(Object proxy,Method method,Object[] args)
*动态代理:
就是在原有的功能上添加新的功能, 跟Spring框架的aop机制一样
-- 动态代理举例说明:
-- 实现代理类:
public class MyInvocationHandler implements InvocationHandler{
private Object target;//目标对象
public MyInvocationHandler(Object target){
this.target = target;
}
@Override
public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
System.out.println("权限校验!");
Object result = method.invoke(target, args);
System.out.println("日志记录!");
return result;
}
}
-- 测试类
System.out.println("----------使用对象----------");
UserDao ud = new UserDaoImpl();
ud.add();
ud.delete();
ud.update();
ud.find();
System.out.println("\n----------使用代理对象----------");
//创建动态对象
//Proxy类中有一个方法可以创建动态代理对象
//public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
//准备对ud对象做一个代理对象 ClassLoader:类加载器
MyInvocationHandler handler = new MyInvocationHandler(ud);
Object proxy = Proxy.newProxyInstance(ud.getClass().getClassLoader(), ud.getClass().getInterfaces(), handler);
//最终返回的对象
UserDao ud1 = (UserDao)proxy;
ud1.add();
ud1.delete();
ud1.update();
ud1.find();