Java基础加强总结(六)——Java反射机制

一、反射概述

 Java Reflection

     Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

Java反射机制提供的功能

Ø 在运行时判断任意一个对象所属的类

Ø 在运行时构造任意一个类的对象

Ø 在运行时判断任意一个类所具有的成员变量和方法

Ø 在运行时调用任意一个对象的成员变量和方法

Ø 生成动态代理


看两个范例

范例一:

//在有反射以前,如何创建一个类的对象,并调用其中的方法、属性                            

  @Test

   public void test1(){

      Person p = new Person();

      p.setAge(10);

      p.setName("TangWei");

      System.out.println(p);

      p.show();

      p.display("HK");

   }

范例二:

//有了反射,可以通过反射创建一个类的对象,并调用其中的结构

@Test

public     void     test2()     throws    Exception{

   Class clazz= Person.class;

   //1.创建clazz对应的运行时类Person类的对象

   Person p = (Person)clazz.newInstance();

   //2.通过反射调用运行时类的指定的属性

   //2.1 调用的是public String name;公共的属性。

   Field f1 = clazz.getField("name");  //获取Person的属性,下面设值。不管属性里面是什么类型的,都要用""双引号包着。

   f1.set(p, "LiuDeHua");   //给属性设置,第一参数代表哪个对象设值,第二个代表值。

   System.out.println(p);

//结果:Person [name=LiuDeHua, age=0]

   //2.2 获取私有的属性并赋值:private int age;

   //通过getDeclaredField()方法获取私有的。

   Field f2 = clazz.getDeclaredField("age"); //获取的方法和获取私有的方法不一样

   f2.set(p, "20");

   java.lang.IllegalAccessException:报异常:非法访问。

   System.out.println(p);   //结果:Person [name=LiuDeHua, age=0]

   //需要设置访问为true

   f2.setAccessible(true);

   f2.set(p, 20);

   System.out.println(p); //结果:Person [name=LiuDeHua, age=20]

3.通过反射调用运行时类的指定的方法

   //3.1调用无参的方法:getMethod(String name, Class... parameterTypes):第一个参数写方法名,第二个无参则后面的去掉。

   Methodm1 = clazz.getMethod("show");

   //调用方法:invoke(Object obj, Object... args):第二个参数是用来传参的,没有就不写。

   m1.invoke(p); //就是方法的调用,等于p.show()

   /*public void show(){

      System.out.println("我是一个人");

   }*/

   //结果:我是一个人

   //3.2调用有参的方法:getMethod(String, Class...):第二个参数也要是Class类型的,所以是String.class

   Method m2 = clazz.getMethod("display",String.class);

   m2.invoke(p, "China");

   /*public void display(String nation){

      System.out.println("我的国籍是:" + nation);

   }*/

   // 结果:我的国籍是: China

二、理解反射的源头之Class

Person.java

package me.gorden.reflection;

public class Person {
	public String name;
	private int age;
	
	public void show(){
		System.out.println("show()无参方法");
	}
	
	public void display(String param){
		System.out.println("display(param)有参数方法");
	}
	
	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;
	}

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

2.1、Class 类

在Object类中定义了以下的方法,此方法将被所有子类继承:

public final Class getClass()

以上的方法返回值的类型是一个Class类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称。

 


java.lang.Class:是反射的源头。

我们创建了一个类,通过编译(javac.exe),生成对应的.class文件。之后我们使用java.exe加载(JVM的类加载器完成的)

 此.class文件此.class文件加载到内存以后,就是一个运行时类,存在在缓存区。那么这个运行时类本身就是一个Class的实例

1.每一个运行时类只加载一次!

2.有了Class的实例以后,我们才可以进行如下的操作:

  1*创建对应的运行时类的对象

  2)获取对应的运行时类的完整结构(属性、方法、构造器、内部类、父类、所在的包、异常、注解、...

  3*调用对应的运行时类的指定的结构(属性、方法、构造器)

  4 )反射的应用:动态代理

范例:getClass()方法的使用:得到完整的“包类”名称

	@Test
	public void demo3(){
		Person person = new Person();
		//通过运行时类的对象,调用其getClass(),返回其运行时类。
		Class clazz = person.getClass();
		System.out.println(clazz);
		//结果:class me.gorden.reflection.Person
	}
2.2获取Class类的实例的4种方式
@Test
	public void demo4() throws ClassNotFoundException{
		//第一种:1.调用运行时类本身的.class属性
		Class clazz1 = Person.class;
		//getName():得到类名
		System.out.println(clazz1.getName());
		//结果:me.gorden.reflection.Person
		
		//第二种:2.通过运行时类的对象获取	
		Person person = new Person();
		Class clazz2 = person.getClass();
		System.out.println(clazz2.getName());
		//结果:me.gorden.reflection.Person
		
		//第三种:3.通过Class的静态方法获取.通过此方式,体会一下,反射的动态性。
		String className = "me.gorden.reflection.Person";
		Class clazz3 = Class.forName(className);
		System.out.println(clazz3.getName());
		//结果:me.gorden.reflection.Person
		
		//第四种:4.通过类的加载器
		ClassLoader classLoader = this.getClass().getClassLoader();
		Class clazz4 = classLoader.loadClass(className);
		System.out.println(clazz4.getName());
		//结果:me.gorden.reflection.Person
	}
2.3了解类的加载器ClassLoader


了解:ClassLoader

类加载器是用来把类(class)装载进内存的。JVM 规范定义了两种类型的类加载器:启动类加载器(bootstrap)和用户自定义加载器(user-defined class loader)。 JVM在运行时会产生3个类加载器组成的初始化加载器层次结构 ,如下图所示:


1.获取一个系统类加载器

ClassLoader classLoader1 = ClassLoader.getSystemClassLoader();
		System.out.println(classLoader1);
		//结果:sun.misc.Launcher$AppClassLoader@15253d5
2. 获取系统类加载器的父类加载器,即扩展类加载器
ClassLoader classLoader2 = classLoader1.getParent();
		System.out.println(classLoader2);
		//结果:sun.misc.Launcher$ExtClassLoader@1fddc31
3. 获取扩展类加载器的父类加载器,即引导类加载器。无法直接获取,所以返回 null.
ClassLoader classLoader3 = classLoader2.getParent();
		System.out.println(classLoader3);
		//结果:null

4.测试当前类由哪个类加载器进行加载。自定义的类是由系统类加载器。

ClassLoader classLoader4 = Class.forName("me.gorden.reflection.Person").getClassLoader();
		System.out.println(classLoader4);
		//结果:sun.misc.Launcher$AppClassLoader@15253d5
5. 测试 JDK 提供的 Object 类由哪个类加载器加载
ClassLoader classLoader5 = Class.forName("java.lang.Object").getClassLoader();
		System.out.println(classLoader5);
		//结果:null
*6. 关于类加载器的一个主要方法: getResourceAsStream(String str): 获取类路径下的指定文件的输入流

方法一:在哪个包中:格式如下

ClassLoader loader = this.getClass().getClassLoader();
		InputStream is = loader.getResourceAsStream("me\\gorden\\reflection\\jdbc.properties");
方法二:在当前工程下 : 格式如下
FileInputStream fis = new FileInputStream(new File("jdbc1.properties"));
		Properties prop = new Properties();
		prop.load(fis);
		
		String username = prop.getProperty("username");

三、创建运行时类的对象

3.1 创建类对象并获取类的完整结构

有了Class对象,能做什么?

创建类的对象:调用Class对象的newInstance()方法

要  求: 

       1)类必须有一个无参数的构造器。

       2)类的构造器的访问权限需要足够。

难道没有无参的构造器就不能创建对象了吗?

不是!只要在操作的时候明确的调用类中的构造方法,并将参数传递进去之后,才可以实例化操作。

步骤如下:

1)通过Class类的getDeclaredConstructor(Class… parameterTypes)取得本类的指定形参类型的构造器

2)向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。

3)通过Constructor实例化对象。

范例一:创建对象
Class clazz = Class.forName("me.gorden.reflection.Person");
		/*创建对应的运行时类的对象。使用newInstance(),实际上就是调用了运行时类的空参的构造器。
		要想能够创建成功:
		①要求对应的运行时类要有空参的构造器。
		②构造器的权限修饰符的权限要足够。*/
		Person person = (Person) clazz.newInstance();
		System.out.println(person);
		//结果:Person [name=null, age=0]
3.2通过反射获取类的完整结构1_属性&方法
1.getFields(): 只能获取到 运行时类中及其父类中声明为public的属性 , 私有的其他的获取不到
Class clazz = Person.class;
		Field[] fields = clazz.getFields();
		for (Field field : fields) {
			System.out.println(field.getName());
		}
		//结果:name
2.getDeclaredFields(): 获取运行时类本身声明的所有的属性,包括私有的
Class clazz = Person.class;
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields) {
			System.out.println(field.getName());
		}
		//结果:name age

3.获取每个属性的权限修饰符

权限修饰符  变量类型变量名

获取属性的各个部分的内容
Class clazz = Person.class;
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields) {
			//因为获取的是一个int类型的,所以需要调用Modifier类的方法。才能获取修饰符名称。
			int i = field.getModifiers();
			
			//Modifier对类和成员访问修饰符进行解码
			String str = Modifier.toString(i);
			
			System.out.println(str + "=="+field.getName());//权限修饰符+属性名
		}
		/*结果:
			public==name
			private==age
		*/
4. getType()获取属性的类型
Class clazz = Person.class;
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields) {
			Class type = field.getType();
			System.out.println("属性类型:"+type);
		}
		/**
		        属性类型:class java.lang.String
		        属性类型:int
		 */
5.获取对应的运行时类的方法
Class clazz = Person.class;
		//1、getMethods():获取运行时类及其父类中所有的声明为public的方法
		Method[] methods = clazz.getMethods();
		for (Method method : methods) {
			System.out.println(method.getName());
		}

结果:


Object类大纲


Class clazz = Person.class;
		//2.getDeclaredMethods():获取运行时类本身声明的所有的方法
		Method[] methods = clazz.getDeclaredMethods();
		for (Method method : methods) {
			System.out.println(method.getName());
		}

结果:


还可以获取以下内容:

1.getAnnotations()  注解

2.getReturnType() 返回值类型

3.getParameterTypes() 形参列表

4.getExceptionTypes() 异常类型

5.getConstructors():返回此 Class 对象所表示的类的所有public构造方法

6.getInterfaces():获取实现的接口

7.getPackage()获取所在的包

四、通过反射调用类中的指定方法、指定属性

1.调用指定方法

通过反射,调用类中的方法,通过Method类完成。步骤:

1.通过Class类的getMethod(Stringname,Class…parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型。

2.之后使用Object    invoke(Object obj, Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息。

invoke(对象,参数)

示例一:调用指定的属性

public void test3() throws Exception{
		Class clazz = Person.class;
		//1.获取指定的属性
		//getField(String fieldName):获取运行时类中声明为public的指定属性名为fieldName的属性
		Field name = clazz.getField("name");
		//2.创建运行时类的对象
		Person p = (Person)clazz.newInstance();
		System.out.println(p);//Person [name=null, age=0]
		//3.将运行时类的指定的属性赋值
		name.set(p, "Jerry");
System.out.println(p);//Person [name=Jerry, age=0]
		System.out.println();
		//getDeclaredField(String fieldName):获取运行时类中指定的名为fieldName的属性
		//因为age是private修饰的,所以调用getDeclaredField方法,为了保证可以给属性赋值,还要设置可访问的权限
		Field age = clazz.getDeclaredField("age");
		age.setAccessible(true);
		age.set(p, 10);
		System.out.println(p);//Person [name=Jerry, age=10]
	}

示例二:调用指定的方法

//调用运行时类中指定的方法
	@Test
	public void test3() throws Exception{
		Class clazz = Person.class;
		//1.调用无参无返回值的方法
		//getMethod(String methodName,Class ... params):获取运行时类中声明为public的指定的方法
		Method m1 = clazz.getMethod("show");
		Person p = (Person) clazz.newInstance();
//调用指定的方法:Object invoke(Object obj,Object ... obj)
		Object returnVal = m1.invoke(p);//调用show方法,因为show是无参无返回值的方法,所以只要写名字就行
		System.out.println(returnVal);//null,因为是无返回值,所以返回null
		
		//2.调用无参有返回值的方法
		Method m2 = clazz.getMethod("toString");
		Object returnVal1 = m2.invoke(p);
		System.out.println(returnVal1);//Person [name=null, age=0]
		
		//3.对于运行时类中静态方法的调用
		Method m3 = clazz.getMethod("info");
		m3.invoke(Person.class);//中国人!
		//getDeclaredMethod(String methodName,Class ... params):获取运行时类中声明了的指定的方法
		Method m4 = clazz.getDeclaredMethod("display", String.class,Integer.class);//因为参数类型是类类型,所以都要String.class得到类类型
		m4.setAccessible(true);
	Object value = m4.invoke(p,"CHN",10);//我的国籍是:CHN
		System.out.println(value);//10
	}

示例三:调用指定的构造器,创建运行时类的对象

@Test
	public void test3() throws Exception{
		String className = "com.atguigu.java.Person";
		Class clazz = Class.forName(className);
调用私有的构造器,同私有的方法一样
		Constructor cons = clazz.getDeclaredConstructor(String.class,int.class);
		cons.setAccessible(true);
		Person p = (Person) cons.newInstance("张三",20);
		System.out.println(p);//Person [name=张三, age=20]
	}

五、反射的应用之动态代理

Java动态代理

    之前为大家讲解过代理机制的操作,属于静态代理,特征是代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。

    最好可以通过一个代理类完成全部的代理功能

   动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。

   动态代理使用场合:

       调试

       远程方法调用

   代理设计模式的原理:

     使用一个代理将对象包装起来, 然后用该代理对象取代原始对象. 任何对原始对象的调用都要通过代理. 代理对象决定是否以及何时将方法调用转到原始对象上。


静态代理:要求被代理类和代理类同时实现相应的一套接口;通过代理类的对象调用重写接口的方法时,实际上执行的是被代理类的同样的方法的调用。

动态代理:在程序运行时,根据被代理类及其实现的接口,动态的创建一个代理类。当调用代理类的实现的抽象方法时,就发起对被代理类同样方法的调用。

涉及到的技术点:

①提供一个实现了InvocationHandler接口实现类,并重写其invoke()方法

②Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),h);

//注:obj:被代理类对象;    h:实现了InvocationHandler接口的实现类的对象。


设计模式之代理模式范例

一、静态代理模式
//接口
interface ClothFactory{
	void productCloth();
}
//被代理类
class NikeClothFactory implements ClothFactory{
	@Override
	public void productCloth() {
		System.out.println("Nike工厂生产一批衣服");
	}
}
//代理类
class ProxyFactory implements ClothFactory{
	ClothFactory cf;
	//创建代理类的对象时,实际传入一个被代理类的对象
	public ProxyFactory(ClothFactory cf) {
		this.cf = cf;
	}
	
	@Override
	public void productCloth() {
		System.out.println("代理类开始执行,收代理费$1000");
		cf.productCloth();
	}
}

public class TestClothProduct {
	public static void main(String[] args) {
		NikeClothFactory nike = new NikeClothFactory();
		ProxyFactory proxy = new ProxyFactory(nike);
		proxy.productCloth();
		//代理类开始执行,收代理费$1000
		//Nike工厂生产一批衣服
	}
}
二、动态代理模式
//动态代理的使用,必须实现InvocationHandler接口
//体会反射是动态语言的关键
interface Subject {
	void action();
}

// 被代理类
class RealSubject implements Subject {
	public void action() {
		System.out.println("我是被代理类,记得要执行我哦!么么~~");
	}
}
//思路:当调用invoke的时候,相当于调用action方法,怎么实现呢?
//声明一个Object对象,这个obj相当于RealSubject的对象,再通过方法赋值
class MyInvocationHandler implements InvocationHandler {
	Object obj;// 实现了接口的被代理类的对象的声明

	//这个方法有两个作用:①给被代理的对象实例化②返回一个代理类的对象
	public Object blind(Object obj) {
		this.obj = obj;
		return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
				.getClass().getInterfaces(), this);
	}
	//当通过代理类的对象发起对被重写的方法的调用时,都会转换为对如下的invoke方法的调用
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		//method方法的返回值时returnVal
		Object returnVal = method.invoke(obj, args);
		return returnVal;
	}
}

public class TestProxy {
	public static void main(String[] args) {
		//1.被代理类的对象
		RealSubject real = new RealSubject();
		//2.创建一个实现了InvacationHandler接口的类的对象
		MyInvocationHandler handler = new MyInvocationHandler();
		//3.调用blind()方法,动态的返回一个同样实现了real所在类实现的接口Subject的代理类的对象。
		Object obj = handler.blind(real);
		Subject sub = (Subject)obj;//此时sub就是代理类的对象
		
		sub.action();//转到对InvacationHandler接口的实现类的invoke()方法的调用
		
		//再举一例
		NikeClothFactory nike = new NikeClothFactory();
		ClothFactory proxyCloth = (ClothFactory)handler.blind(nike);//proxyCloth即为代理类的对象
		proxyCloth.productCloth();
	}
}

六、动态代理与AOP

符合以下原则:

1.接口

2.被代理类:实现接口

3.代理类 :实现 InvocationHandler接口


动态代理与AOP

前面介绍的Proxy和InvocationHandler,很难看出这种动态代理的优势,下面介绍一种更实用的动态代理机制

改进后的说明:代码段1、代码段2、代码段3和深色代码段分离开了,但代码段1、2、3又和一个特定的方法A耦合了!最理想的效果是:代码块1、2、3既可以执行方法A,又无须在程序中以硬编码的方式直接调用深色代码的方法。


动态代理与AOPAspect OrientProgramming)

    使用Proxy生成一个动态代理时,往往并不会凭空产生一个动态代理,这样没有太大的意义。通常都是为指定的目标对象生成动态代理

这种动态代理在AOP中被称为AOP代理,AOP代理可代替目标对象,AOP代理包含了目标对象的全部方法。但AOP代理中的方法与目标对象的方法存在差异:AOP代理里的方法可以在执行目标方法之前、之后插入一些通用处理


interface Human {
	void info();

	void fly();
}

// 被代理类
class SuperMan implements Human {
	public void info() {
		System.out.println("我是超人!我怕谁!");
	}

	public void fly() {
		System.out.println("I believe I can fly!");
	}
}

class HumanUtil {
	public void method1() {
		System.out.println("=======方法一=======");
	}

	public void method2() {
		System.out.println("=======方法二=======");
	}
}

class MyInvocationHandler implements InvocationHandler {
	Object obj;// 被代理类对象的声明

	public void setObject(Object obj) {
		this.obj = obj;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		HumanUtil h = new HumanUtil();
		h.method1();

		Object returnVal = method.invoke(obj, args);

		h.method2();
		return returnVal;
	}
}

class MyProxy {
	// 动态的创建一个代理类的对象
	public static Object getProxyInstance(Object obj) {
		MyInvocationHandler handler = new MyInvocationHandler();
		handler.setObject(obj);

		return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);
	}
}

public class TestAOP {
	public static void main(String[] args) {
		SuperMan man = new SuperMan();// 创建一个被代理类的对象
		Object obj = MyProxy.getProxyInstance(man);// 返回一个代理类的对象
		Human hu = (Human) obj;
		hu.info();// 通过代理类的对象调用重写的抽象方法

		System.out.println();

		hu.fly();
	}
}
运行结果:


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Java中的Handler机制是Android开发中非常重要的一个概念,它是用来处理线程之间的通信的。在Java中,也可以使用Handler机制来实现线程之间的通信,这里简要介绍一下Java中的Handler机制。 Java中的Handler机制是基于线程池的,它可以将任务提交到线程池中执行,并且可以将执行结果返回给调用者。在Java中,可以使用Executor框架来实现线程池,同时可以使用Future接口来获取执行结果。 在Java中,Handler机制可以通过以下几个步骤来实现: 1. 创建一个线程池,可以使用Executor框架来实现。 2. 创建一个Callable接口的实现类,该实现类用来执行任务并返回结果。 3. 将Callable实现类提交到线程池中执行,可以使用submit()方法来实现。 4. 获取执行结果,可以使用Future接口来获取。 下面是一个简单的示例代码: ```java import java.util.concurrent.*; public class HandleJava { public static void main(String[] args) throws Exception { // 创建线程池 ExecutorService executor = Executors.newFixedThreadPool(1); // 创建Callable实现类 Callable<String> callable = new Callable<String>() { @Override public String call() throws Exception { // 执行任务 Thread.sleep(1000); return "Hello, world!"; } }; // 提交任务到线程池 Future<String> future = executor.submit(callable); // 获取执行结果 String result = future.get(); System.out.println(result); // 关闭线程池 executor.shutdown(); } } ``` 这个示例代码中,创建了一个线程池,然后创建了一个Callable实现类,将其提交到线程池中执行,并且使用Future接口来获取执行结果。最后关闭线程池。 需要注意的是,线程池一定要关闭,否则程序会一直运行下去,不会退出。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xinlianluohan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值