搞定java面试-反射

金九银十面试(反射)

反射

使用一个特殊的编程问题,只要知道这个泛化的引用的确切类型,就很容易解决,我们就可以解决,怎么解决这个问题.?

使用反射,查询到某个父类所指的确切的类型

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

不会自动初始化

  1. 加载:在类加载器中执行的, 先找到字节码然后创建一个class对象
  2. 链接:验证类中的字节码,为静态字段分配存储空间,在必要时解析该类对其他所有类的引用
  3. 初始化:先初始化,静态初始化器和静态初始化常量

编译时常量

运行时常量

如果一个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);方法进行设定

反射的应用

  1. 程序运行过程中动态的创建对象
  • 在配置文件中定义类的信息

    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()或者其他获取注解的方法,并传入需要的参数即可
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值