反射学习笔记

注:主要记录自教学视频《兄弟连_马剑威_JAVA基础》的反射章节

类的生命周期:

第一:通过javac 将java源文件生成 .class文件

第二步:java命令

分为三个阶段:装载、链接、初始化


反射:Reflection

类信息  --> 对象

对象 -->   类信息

2、Class类--类信息

Class类是一切的反射根源

类名、属性、构造方法、方法


得到Class类的对象有三种方式:

1、Object类的getClass() 方法

2、类.class

3、通过Class类的forname方法,放入类的全路径

package zhan;

public class Person {

	private int age;
	private char sex;
	private String name;

	public Person(int age, char sex, String name) {
		super();
		this.age = age;
		this.sex = sex;
		this.name = name;
	}

	public Person() {
		super();
		// TODO Auto-generated constructor stub
	}

	@Override
	public String toString() {
		return "Person [age=" + age + ", sex=" + sex + ", name=" + name + "]";
	}

	public int getAge() {
		return age;
	}

	public char getSex() {
		return sex;
	}

	public String getName() {
		return name;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public void setSex(char sex) {
		this.sex = sex;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	private void say(){
		System.out.println("my name is "+name);
	}

}

package zhan;
public class Main {

	public static void main(String[] args) throws ClassNotFoundException {
		Person p1=new Person(18, '男', "小白");
		Person p2=new Person(19, '女', "小黑");
		
		//一个Class对象代表着一份字节码,相同类型的对象得到的字节码对象是同一个
		//创建Class 对象的方式一: 
		System.out.println("创建Class对象的方式一:");
		Class personClass1 = p1.getClass();
		Class personClass2=p2.getClass();
		System.out.println(personClass1==personClass2);
		
		System.out.println("创建Class对象的方式二:");
		Class personClass3= Person.class;
		System.out.println(personClass1==personClass3);
		
		System.out.println("创建Class对象的方式三:");
		Class personClass4 =Class.forName("zhan.Person");//此处为类完整限定名称
		System.out.println(personClass1==personClass4);
		System.out.println(" personClass1:"+personClass1.toString()+" , personClass4:"+personClass4.toString());
	}

}

结果为:

创建Class对象的方式一:
true
创建Class对象的方式二:
true
创建Class对象的方式三:
true
 personClass1:class zhan.Person , personClass4:class zhan.Person


类的加载过程:

首先将Person类所对应的class文件通过类加载器 ClassLoader加载到内存当中,

注意,这个类只会加载一次;在内存中存这个类的字节码只会存一份,以class类型存在

当后面实例化过个类对象时,都是对同一份class 的引用


2、Class类

使用Class类进行对象的实例化操作

调用无参构造进行实例化:

public T newInstance() throws InstantiationException,IllegalAccessException

调用有参构造进行实例化

public Constructor<?>[]  getConstructors() throws SecurityException

		//--------通过类信息实例化对象-----------------
		System.out.println("----通过类信息实例化对象----");
		System.out.println("--调用无参构造方法:");
		//调用无参构造方法
		try {
			//调用此方法时要注意,如果类中没有无参构造方法,将会报异常 InstantiationException
			Person p3= (Person) personClass4.newInstance();
			System.out.println(p3);
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace(); =
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//调用有参构造方法
		System.out.println("--调用有参构造方法:");
		Constructor[] cs=personClass4.getConstructors();//返回构造方法对象,当前类的所有构造方法
		for (Constructor constructor : cs) {
			System.out.println(constructor.toString());
			System.out.println(constructor.getName());
		}

输出结果:

----通过类信息实例化对象----
--调用无参构造方法:
Person [age=0, sex=, name=null]
--调用有参构造方法:
public zhan.Person(int,char,java.lang.String)
zhan.Person
public zhan.Person()
zhan.Person	

		System.out.println("--获取指定参数类型的构造方法:");
		try {
			Constructor c1=personClass4.getConstructor(int.class,char.class,String.class);
			Person p4=(Person) c1.newInstance(17,'女',"小花");
			System.out.println(p4);
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

输出:

--获取指定参数类型的构造方法:
Person [age=17, sex=女, name=小花]

3、通过Class类取得类信息

  • 取得类所在包
public Package getPackage();//得到一个类所在的包
public String getName();//得到名字
  • 取得一个类的方法
public Method[] getMethods();
public int getModifiers();//Modfier.toString(mod);//还原修饰符
public Class<?> getReturnType()
public Class<?> getParameterType()
public Class<?> getExceptionType()
public static String toString(int mod)
  • 取得一个类中的全部属性
public Field[] getFields()
public Field[] getDeclaredFields()
public Class<?> getType()
public int getModifiers()
public String getName()


getMethods() 方法不能得到私有的方法

getDeclaredMethods() 方法可以获得

		System.out.println("----通过类信息----");
		System.out.println("--包信息:" + personClass4.getPackage().getName());
		System.out.println("--类信息:" + personClass1.getName());
		Method[] methods = personClass4.getMethods();//只能获取public 的方法,包括超类及超接口继承中的公共方法
		for (Method method : methods) {
			System.out.println("方法:"+method.getName());
		}
		//返回method对象的一个数组,这些对象反映此class
		//对象表示的类或接口声明的所有方法
		//包括公共、保护、默认和私有方法,但不包括继承的方法
		Method[] methods2 =personClass4.getDeclaredMethods();
		for (Method method : methods2) {
			System.out.println("declared 方法: "+Modifier.toString(method.getModifiers())+"  "+method.getName());
		}

输出:

----通过类信息----
--包信息:zhan
--类信息:zhan.Person
方法:toString
方法:getName
方法:setName
方法:setAge
方法:getSex
方法:getAge
方法:setSex
方法:wait
方法:wait
方法:wait
方法:equals
方法:hashCode
方法:getClass
方法:notify
方法:notifyAll
declared 方法: public  toString
declared 方法: public  getName
declared 方法: public  setName
declared 方法: public  setAge
declared 方法: public  getSex
declared 方法: public  getAge
declared 方法: public  setSex
declared 方法: private  say

通过 getFields() 和 getDeclaredFields() 获取类的属性

		System.out.println("--获取公有属性信息");
		Field[] fields=personClass4.getFields();
		for (Field field : fields) {
			System.out.println(field.getName());
		}
		System.out.println("--获取私有属性信息");
		Field[] fields2=personClass4.getDeclaredFields();
		for (Field field : fields2) {
			System.out.println(Modifier.toString(field.getModifiers())+" "+field.getName());
		}

输出:

--获取公有属性信息
--获取私有属性信息
private age
private sex
private name

-----Field 补充,Field.get()方法

public Object get(Object obj) ,API解释

返回指定对象的特定属性field的值

  • 如果该属性是静态(static)的,则 obj参数被忽略
  • 否则,obj 是实例属性
eg:
public class LocationInfo {
    private  int lon=100;
    static int lonstatic=101;

    public void setLon(int lon) {
        this.lon = lon;
    }

    public static void setLonstatic(int lonstatic) {
        LocationInfo.lonstatic = lonstatic;
    }
}
public class Main {
    public static void main(String[] args) {
        try {
            Field field = LocationInfo.class.getDeclaredField("lon");
            field.setAccessible(true);
            LocationInfo locInfo1 = new LocationInfo();
            locInfo1.setLon(111);
            LocationInfo locInfo2 = new LocationInfo();
            locInfo2.setLon(222);

            int x1 = (Integer) field.get(locInfo1); System.out.println(x1);
            int x2 = (Integer) field.get(locInfo2); System.out.println(x2);

            Field fieldstatic = LocationInfo.class.getDeclaredField("lonstatic");
            int y1 = (int) fieldstatic.get(locInfo1); System.out.println(y1);
            int y = (int) fieldstatic.get(new User()); System.out.println(y);
            int z = (int) fieldstatic.get(""); System.out.println(z);
            int w = (int) fieldstatic.get(null); System.out.println(w);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}
输出:

111
222
101
101
101
101
可以看到对于静态 static 修饰的属性,不管 obj 传入什么类型,输出都是一样的


4、通过Class类调用属性或方法

1、调用类中的方法

  • 调用类中的方法,传入实例化对象,以及具体的参数内容
  • public Object invoke(Object obj,Object ... args)

2、直接调用属性

  • 取得属性
  • public Object get(Object obj)
  • //设置属性 ,等同于使用“=”完成操作
  • public void set(Object obj,Object value)
  • //让属性对外部可见
  • public void setAccessible(boolean flag)

eg:

		System.out.println("-------------调用方法和属性---------------");
		Person p5= new Person();
		try {
			Method setNameMethod=personClass4.getMethod("setName", String.class);
			//调用方法
			setNameMethod.invoke(p5, "小花");//相当于 p5.setName("小花");
			System.out.println(p5);
			//访问私有方法
			Method sayMethod = personClass4.getDeclaredMethod("say");
			sayMethod.setAccessible(true);//忽略检查访问修饰符
			sayMethod.invoke(p5);
			
			//调用属性
			Field ageField =personClass4.getDeclaredField("age");
			ageField.setAccessible(true);//忽略检查访问修饰符
			ageField.set(p5, 5);//或者 ageField.setInt(p5, 5); 给P5的age属性赋值为5 
			System.out.println(p5);
			System.out.println("age = "+ageField.get(p5));//获取p5的age属性
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchFieldException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

输出:

-------------调用方法和属性---------------
Person [age=0, sex= , name=小花]
my name is 小花
Person [age=5, sex= , name=小花]
age = 5

5、动态代理

  • 所谓动态代理,即通过代理类:Proxy的代理,接口和实现类之间可以不直接发生联系,而可以在运行期(Runtime)实现动态关联
  • java 动态代理主要是使用 java.lang.reflect 包中的两个类。
InvocationHandler 类
  • public Object invoke(Object obj,Method method,Object[] obs)  --invoke:调用
  • 其中第一个参数 obj 指的是代理类,method是被代理的方法,obs 是指被代理的方法的参数组,此方法由代理类来实现。
 Proxy类 --proxy:代理
  • protected Proxy(InvocationHandler h);
  • static Class getProxyClass(ClassLoader loader,Class[] interfaces);
  • static Object newProxyStance(ClassLoader loader,Class[] interfaces,InvocationHander h)
  • 动态代理其实是运行时生成class,所以,我们必须提供一组 interface ,然后告诉他class已经实现了这些interface,而且在生成Proxy的时候,必须给他提供一个hander,让他来接管实际的工作
实例:
1、创建要代理的主题接口
/**
 * 要代理的主题接口
 */
public interface Subject {
	public void miai();// 相亲
}

2、创建被代理类
/**
 *被代理类
 */
public class Person implements Subject{
	private String name;
	public Person(String name){
		this.name =name;
	}
	@Override
	public void miai() {
		System.out.println(name+"正在相亲中...");
	}
}

3、创建动态代理类
/**
 * 动态代理类
 */
public class DynaProxy implements InvocationHandler {
	private Object target;
	public  DynaProxy(Object target) {
		this.target = target;
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		Object obj=null;
		before();
		obj =method.invoke(target, args);//真正调用业务方法,相当于调用 miai方法
		after();
		return obj;
	}
	
	//相亲之后要做的事情
	private void after() {
		System.out.println("本次相亲结束..");
	}
	//相亲之前要做的事情
	private void before() {
		System.out.println("为代理人匹配如意郎君");
	}

}

/*
接口 InvocationHandler
InvocationHandler 是代理实例的调用处理程序实现的接口
每一个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke方法。

Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    在代理实例上处理方法调用并返回结果。在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。 
    
 参数:
proxy - 在其上调用方法的代理实例
method - 对应于在代理实例上调用的接口方法的 Method 实例。Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。
args - 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类(如 java.lang.Integer 或 java.lang.Boolean)的实例中。 
          

 */

4、测试
public class Test {

	public static void main(String[] args) {
		Person p = new Person("小白");
		DynaProxy dynaProxy = new DynaProxy(p);
		// Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类
		//动态生成代理对象(类加载器,被代理接口,InvocationHandler)
		Subject s = (Subject) Proxy.newProxyInstance(p.getClass()
				.getClassLoader(), p.getClass().getInterfaces(), dynaProxy);
		s.miai();
	}
}
输出结果:
为代理人匹配如意郎君
小白正在相亲中...
本次相亲结束..
















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值