金九银十面试(反射)
反射
使用一个特殊的编程问题,只要知道这个泛化的引用的确切类型,就很容易解决,我们就可以解决,怎么解决这个问题.?
使用反射,查询到某个父类所指的确切的类型
Java成为动态语言的关键
编写程序的时候你不知道写的这个程序是谁
将类抽象成一个对象,用对象class对象来描述这个.
bean = com.cuntou.entity.cat
//获取目标类名
String bean = properties.getProperty ("bean");
//根据目标类名来获取目标类的所对应的Class对象(描述结构)
class clazz = Class.forName(bean);
//获取一个构造器
Constructor constructor = clazz.getConStructor(null);
//根据构造器来创建一个对象
constructor.newInstance(null);
使用反射
反射的源头就class类 java.lang.class
Class类就是用来描述其他类,每个类的实例化对象就是对目标类的描述
class的获取方式
1.通过类名:Class clazz = Class.forName("com.cuntou.entity.fish");
2.通过类的自面量获取:class clazz = Fish.class; 类名.class
3.通过对象获取:Fish fish = new Fish();
Class clazz = fish.getClass();
每一个类的class对象只有一个,每个类在内存中只加载一次,加载到内存中的类,只加载一次,所以运行时类的对象(一般没有写父类就是object).
class 的就是描述运行时类的,运行类只有只有一个,所以指向只有一个.
获取到了反射的源头,可以获取类的各种信息
类的信息包括属性,方法,构造器,父类,实现的接口,所在的包,访问权限
类字面量
通过类的自变量获取:class clazz = Fish.class; 类名.class
不会自动初始化
- 加载:在类加载器中执行的, 先找到字节码然后创建一个class对象
- 链接:验证类中的字节码,为静态字段分配存储空间,在必要时解析该类对其他所有类的引用
- 初始化:先初始化,静态初始化器和静态初始化常量
编译时常量
运行时常量
如果一个static final 的字段的值是编译时常量的话,那么不需要初始化就可以使用.但是如果使用的是static final 修饰的.那么不能保证他就是运行时常量.
构造器
无参构造
Constructor 是专门用来描述构造器的类,constructor 的对象就是目标类的构造器
获取构造器就是为了创建对象的
Fish fish = new Fish();
Class clazz = fish.getClass();
//获取有参构造
Constructor constructor = clazz3.getConstructor(String.class);
//顺序也是得一样的
Fish fish = constructor.newInstacne("xiaoyu")
属性
public class FieldTest {
public static void main(String[] args) throws NoSuchFieldException {
Class clazz = Cat.class;
//获取公有属性数组
Field[] fields = clazz.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("***********************************");
//获取属性数组(对访问权限修饰符无要求),私有属性也想获得
Field[] declaredFields = clazz.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
System.out.println("***********************************");
//根据属性名获取公有属性
Field field = clazz.getField("id");
System.out.println(field);
//根据属性名获取属性
Field id = clazz.getDeclaredField("id");
System.out.println(id);
}
}
步骤
- 获得类的Class对象
- 调用Class对象的newInstance()方法,获取类的对象
- 调用Class对象的getDeclaredField()方法,获取属性
- 调用属性对象的set()方法进行赋值
- 如果是私有属性,还需要调用name.setAccessible(true);方法进行设定
反射的应用
- 程序运行过程中动态的创建对象
-
在配置文件中定义类的信息
bean = com.southwind.entity.Fish
-
动态的读取配置文件创建对象
public class Test { priavte static Properties properties; static { try { properties = new Properties(); properties.load(new FileInputStream(System.getProperty("user.dir")+"/src/bean.properties")); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) throws Exception{ String bean = properties.getPropperty("bean"); class clazz = Class.forName(bean); // 获取构造器 Constructor constructor = clazz.getConstructor(null); // 使用无参构造是创建对象 System.out.println(constructor.newInstance(null)); } }
动态代理
动态代理是一个非常重要的应用
代理模式
代理模式是一个常用的Java设计模式,代理模式是指的处理一个业务的时候,通过代理的方式来完成的.
委托方和代理方,委托方代理帮助他完成一些工作
在所有的代理模式中,委托方和代理方都有一个共性:双方都必须有完成需求的能力.
Java中将对象所具备的能力封装成接口,Java中代理模式的原则就是委托类和代理类都实现了同样的接口.
动态代理
代理类和委托类之间通过依赖注入进行关联,在设计程序时需要将委托类定义为代理类的成员变量.
代理类本身并不会去执行业务逻辑,而是通过调用委托类的方法来完成,真正的业务逻辑仍然是委托类来完成,代理类只是完成一些核心业务以外的逻辑,这样来实现解耦合.
一个完成的业务包含:核心业务模块 + 辅助性的业务(打印日记,消息过滤,类型转换)
委托类只需要负责核心业务,不需要参杂任何与核心业务无关的代码
辅助性的业务由代理类来完成,将委托类注入到代理类中,分别完成辅助和核心
程序院只需要调用代理对象即可完成整套业务
举例子
创建一个厂商又可以卖手机又可以卖汽车使用静态代理不合适.因为静态代理都是写好的
代理类不是提前写好的,而是程序运行过程中动态生成的.
如何实现这一过程?代理类是动态生成的,但是我们需要告诉生成策略.
如何指定代理类的生成策略?使用java.lang.reflect.Invocationhandler 接口,可以在程序运行期间动态实现代理类, 即生成策略通过该接口完成
- 实现InvocaionHandler 接口
Proxy.newProxyInstance( ) 三个参数
-
类加载器classloader
-
委托类的接口
-
当前InvocationHandler实例对象
动态生成一个类,代理类
代理类的特点?
委托类和代理类都实现了同样的接口,代理类实现委托类的接口,必须要获取委托类的接口,第 2 个参数的作用。
动态创建类之后,类需要加载到 Java 内存中才能使用,创建对象,需要用到类加载器 ClassLoader,将动态生成的类加载到 Java 内存中,第 1 个参数的作用。
上述的过程需要一个对象来完成,就是当前的实例对象,第 3 个参数的作用。
public class MyInvocationHandler implements InvocationHandler { //注入委托对象 private Object obj = null; //生成代理对象 public Object bind(Object obj){ this.obj = obj; return Proxy.newProxyInstance(MyInvocationHandler.class.getClassLoader(),obj.getClass().getInterfaces(),this); } //实现核心业务代码和辅助代码整合的 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //核心业务代码委托对象来完成,方法由委托对象来调用 System.out.println("启动动态代理模式~~~"); Object invoke = method.invoke(this.obj, args); return invoke; }
public class Test4 { public static void main(String[] args) { MyInvocationHandler handler = new MyInvocationHandler(); //定义委托对象 Phone phone = new Xiaomi(); //获取动态代理对象 Phone proxy = (Phone) handler.bind(phone); System.out.println(proxy.salePhone()); Car car = new BMW(); Car carProxy = (Car) handler.bind(car); System.out.println(carProxy.saleCar()); //委托对象 House house = new BigHouse(); //代理对象 House houseProxy = (House) handler.bind(house); System.out.println(houseProxy.saleHouse()); } }
静态代理
静态代理是指代理类是提前写好的,动态代理是指代理类是动态生成的
public interface phone {
public String salePhone();
}
public class Huawei implements phone {
@Override
public String salePhone() {
return "销售华为手机"
}
}
public class PhoneProxy implements phone {
private Phone phone;
public PhoneProxy(Phone phone) {
this.phone = phone;
}
@Override
public String salePhone() {
System.out.prinln("启动代理模式销售手机");
}
}
public class test {
public static void main(String[] args) {
//创建委托对象
Phone phone = new Huawei();
//创建代理对象
PhoneProxy phoneproxy = new PhoneProxy(phone);
//代理对象开始工作
System.out.println(phoneProxy.salephone());
phone = new Xiaomi();
phoneProxy = new PhoneProxy(phone);
system.out.println(phoneProxy.salePhone());
}
invoke
invoke 本意就是调用,在Java中使用invoke方法来反射获取成员方法并调用
1、invoke就是调用类中的方法,最简单的用法是可以把方法参数化invoke(class, method)。
2、比如你Test类里有一系列名字相似的方法setValue1、setValue2等等。可以把方法名存进数组v[],然后循环里invoke(test,v[i]),就顺序调用了全部setValue。
通过反射获得类的对象
通过newInstance()方法
- 类必须含有一个无参构造器
- 类的构造器的访问权限要足够
public class Demo5 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Class c1 = Class.forName("main.study.day4.User");
User user = (User) c1.newInstance();
System.out.println(user);
}
}Copy
如果重载了一个方法的构造方法,最好再补上一个无参构造函数
通过获得类的构造器,再进行初始化
public class Demo5 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class c1 = Class.forName("main.study.day4.User");
Constructor constructor = c1.getDeclaredConstructor(String.class, int.class);
User user2 = (User) constructor.newInstance("Nyima", 20);
System.out.println(user2.getAge());
}
}Copy
总结:获得类的实例的步骤
-
通过类的Class对象
-
如果想通过调用
无参构造方法
创建实例
- 直接调用Class对象的newInstance()方法
- 将返回值强转为对应的类型
-
如果想通过
有参构造方法
创建实例
- 调用Class对象的getDeclaredConstructor()并传入构造方法相应的参数的类型
- 通过获得的有参构造器,调用newInstance()方法,传入相应参数
- 将返回值强转为对应的类型
通过反射调用类的方法
public class Demo5 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class c1 = Class.forName("main.study.day4.User");
User user = (User) c1.newInstance();
Method setName = c1.getDeclaredMethod("setName", String.class);
setName.invoke(user, "Nyima");
System.out.println(user.getName());
}
}Copy
步骤
- 获得类的Class对象
- 调用Class对象的newInstance()方法,获取类的对象
- 调用Class对象的getDeclaredMethod()方法
- 调用返回对象的invoke()方法,传入所需的参数(参数包含对哪个对象调用该方法),如 setName.invoke(user, “Nyima”);
通过反射设置属性值
public class Demo5 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
Class c1 = Class.forName("main.study.day4.User");
User user = (User) c1.newInstance();
Field name = c1.getDeclaredField("name");
name.set(user, "Nyima2");
System.out.println(user.getName());
}
}Copy
步骤
- 获得类的Class对象
- 调用Class对象的newInstance()方法,获取类的对象
- 调用Class对象的getDeclaredField()方法,获取属性
- 调用属性对象的set()方法进行赋值
- 如果是私有属性,还需要调用name.setAccessible(true);方法进行设定
通过获得注解信息
public class Demo6 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException, NoSuchMethodException {
Class c1 = Class.forName("main.study.day4.Student");
Student student = (Student) c1.newInstance();
// 获得类的注解
Annotation[] declaredAnnotations = c1.getDeclaredAnnotations();
System.out.println(Arrays.toString(declaredAnnotations));
// 获得属性上的注解
Field name = c1.getDeclaredField("name");
Annotation annotation = name.getAnnotation(FieldAnnotation.class);
System.out.println(annotation);
// 获得方法的注解
Method getName = c1.getDeclaredMethod("getName");
MethodAnnotation methodAnnotation = getName.getAnnotation(MethodAnnotation.class);
System.out.println(methodAnnotation);
}
}
@ClassAnnotation(name = "myStudent")
class Student {
@FieldAnnotation(name = "name")
private String name;
@FieldAnnotation(name = "age")
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@MethodAnnotation(name = "getName")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
/**
* @author Nyima
* 类的注解
*/
@interface ClassAnnotation {
String name();
}
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
/**
* @author Nyima
* 属性的注解
*/
@interface FieldAnnotation {
String name();
}
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
/**
* @author Nyima
* 方法的注解
*/
@interface MethodAnnotation {
String name();
}Copy
步骤
- 先获取到对应的类,如
- 想获得类注解,需要先获得Class类对象
- 想获得属性的注解,需要先获得对应的属性
- 想获得方法的注解,需要先获得对应的方法
- …
- 通过对应的对象调用getDeclaredAnnotation()或者其他获取注解的方法,并传入需要的参数即可