黑马程序员_JAVA基础加强——反射

---------------------- <a href="http://edu.csdn.net"target="blank">ASP.Net+Android+IOS开发</a>、<a href="http://edu.csdn.net"target="blank">.Net培训</a>、期待与您交流! ----------------------

--什么是反射?

想要知道反射的概念,要先明白java类的作用是什么?java类就是用于描述一类事物的共性,这些事物包括现实中存在的事物,也包括虚拟的一些事物。把具有相同属性和行为的一类事物抽象化,就形成了类。那么,java中的所有类是不是也有一些共性的内容呢?我们知道,一个类是由多个部分组成的:成员变量、成员方法和构造方法等。那么我们是不是也可以把这些共性的内容抽取成一个类呢?在java中,使用Class这个类来描述java中所有的类。

在java中,所有的类都是Class的一个实例对象,它们的内容不同,但是,它们的特征相同,譬如,都有方法,有字段,有父类,有包。那么,它们的实例对象又是什么呢?学java基础的时候我们知道,每个类在被加载进内存时,都会先把它们对应的.class字节码加载进内存,那么,这份字节码就是Class的实例对象。那么,反射就是将类的字节码加载进内存,而后剖析出这个类的各个组成部分,包括构造函数、成员变量和方法等。这些信息用相应类的实例对象来表示,它们是Constructor、Field和Method等。
--加载类
了解了反射的概念后,我们就先创建一个类:Person。加载该类的字节码,然后通过反射技术剖析这个类的组成部分。
package cn.itcast.reflect;

import java.io.InputStream;
import java.util.List;
/**
 * 人类
 * @author 中关村阿旺
 *
 */
public class Person {
	
	public int age=44;   //年龄(public修饰)
	private static String address;   //住址(private static修饰)

	public Person(){
		System.out.println("person");
	}
		
	//构造方法,private修饰
	private Person(List list){
		System.out.println("list");
	}
	
	public void aa1(){
		System.out.println("aa1");
	}
	
	private void aa1(InputStream in){
		System.out.println(in);
	}
	
	//方法,static修饰
	public static void aa1(int num){
		System.out.println(num);
	}
	
	//main方法,static修饰,有点特殊
	public static void main(String[] args){
		System.out.println("main");
	}
}


  加载Person类就是将硬盘上的Person类的字节码文件内容加载到内存里,并封装成Class对象。
  加载类有三种方式:
  第一种方式:通过Class类的静态方法forName()加载类,该方法接收一个参数,字符串类型的完整类名称。
  例如:Class clazz=Class.forName("cn.itcast.reflect.Person");
  第二种方式:通过实例化类的对象加载该类。因为实例化对象的过程是先要加载该类到内存中,然后才会初始化对象。
  通过对象的getClass()方法获得Class对象,也就是该类的字节码。
  例如:Class clazz1=new Person().getClass();
  第三种方式:直接将该类的字节码加载进内存中。
  例如:Class clazz2=Person.class;
  以上三种方式,我们使用哪种方式都可以,类已经进内存了,就可以用反射技术剖析类的组成部分了。
--反射类的构造函数
 在java中Constructor类提供关于类的单个构造方法的信息以及对它的访问权限。
 例如:反射构造函数:public Person(){}
 通过Class类的getConstructor()方法可以得到一个Constructor对象。该方法接收一个参数,就是你反射的那个构造方法的参数的类型(字节码)。
 因为我们反射的是无参的构造函数,所以传入null值,得到构造函数对象。
 代码:Constructor c=clazz.getConstructor(null);
 此构造函数对象中有一个方法:newInstance(),可以返回我们反射的类的实例对象。该方法接收的参数是实际传入构造方法的参数。
 我们反射的是Person类,所以用Person对象接收。由于newInstance()方法返回类型是Object,所以需要强转。
 代码:Person p=(Person)c.newInstance(null);
 用得到的这个对象,我们可以获取该对象中封装的信息。
 System.out.println(p.age);        //返回44
 上面反射的是public修饰符修饰的构造函数,我们也可以反射非public修饰符修饰的构造函数。
 例如:反射构造函数:private Person(List list){}
 由于此构造函数反射的是private修饰的,所以可以通过Class类的getDeclaredConstructor()方法获取构造函数对象。 getDeclaredConstructor()方法可以反
 射在Person类中所有声明(定义)过的构造方法,包括private修饰的。
 由于此构造函数有参数,我们可以传入此构造函数参数的字节码即可。
 代码:Constructor c=clazz.getDeclaredConstructor(List.class);
 由于private修饰的成员只能在本类中被访问,但是,暴力反射可以在让其在本类外被访问。
 所谓暴力反射,就是将那些在反射的类中不是public修饰的成员方法、属性、构造方法等,强行打开它们的访问权限,使其可以在本类外访问。
 代码:c.setAccessible(true);这就是暴力反射
 代码:Person p=(Person) c.newInstance(new ArrayList());
 System.out.println(p.age);   //返回44
 
 注:另一种使用反射创建对象的方法,不是反射构造函数,而是直接使用Class类的newInstance()方法。这是因为java为了方便通过反射技术创建类的实例而添加的方法。
 例如:Person p=(Person) clazz.newInstance();
           System.out.println(p.age);   //返回44
--反射类的方法
 在java中Method类提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。
1.反射类中的普通方法
 通过Class类的getMethod()方法得到Method对象,该方法接收两个参数,第一个指明需要反射的方法名称,第二个指明需要反射的方法的参数类型。
 例如:反射类的方法:public void aa1(){}
 代码:Method m=clazz.getMethod("aa1", null);
 使用Method对象的invoke()方法可以调用反射的方法,需要传入对象名(指明反射的是哪个对象的方法)和参数类型。
 代码:m.invoke(p, null);          //返回aa1
 上面的代码反射的是public修饰的方法,想要访问非public修饰的方法可以通过Class类的getDeclaredMethod()方法访问在Person类中定义(声明)的所有方法。
 例如:反射类的方法:private void aa1(InputStream in){}
 由于要反射的此方法有参数,那么在第二个参数中传入该参数的字节码即可。
 代码:Method m=clazz.getDeclaredMethod("aa1", InputStream.class);
 m.setAccessible(true);  //暴力反射
 代码:m.invoke(p, new FileInputStream("e:\\黑马\\黑马视频笔记
\\033java 基础加强\\课堂笔记1.txt"));    //返回java.io.FileInputStream@150bd4d
 注:如果反射的方法是一个静态方法,比如:public static void aa1(int num){}
 那么在用反射技术得到Method对象后,调用invoke()方法传入对象时,可以传入null。
 代码:m.invoke(null, 56);    //返回56
 2.反射类中的main方法
 反射类中的main方法:public static void main(String[] args)
 有两种调用方式:
 第一种方式:
  代码:Method m=clazz.getMethod("main", String[].class);
  由于main方法也是静态方法,所以可以不用传入对象。
  代码:m.invoke(null, new Object[]{new String[]{"aaa","bbb"}});    //返回main
  那么之所以这样写,是因为JDK1.4当中没有可变参数这一特性,它使用的是Object类型的数组作为参数,调用invoke()方法时,会把数组中的每一个元素当做一个参数来看待,也就是会把数组拆开。JDK1.5虽然有了可变参数这一新特性,但是它兼容JDK1.4,所以,它也会把可变参数当做是一个数组进行拆封。这样一拆,就成了两个字符串对象了,就与main方法的参数类型不一致了,所以会出现参数类型不匹配异常。为了解决这一异常,需要把数组再一次封装成数组,这样在进行拆封时,得到的仍然是一个数组,刚好符合main方法的参数类型。
  第二种方式:
  强制让java虚拟机把参数类型当做是一个Object类型的参数,而不是一个Object类型的数组。这样就不会进行拆封,而里面实际存放的是字符串数组,所以仍然与main方法的参数类型一致。
  代码:m.invoke(null, (Object)new String[]{"rrrr","dddd"});    //返回main
--反射类的字段
  在java中Field类提供有关类或接口的单个字段的信息,以及对它的动态访问权限。
  通过Class类的getField()方法可以得到Field对象,该方法接收一个参数,即需要反射的字段的名称。
  例如:反射类的字段:public int age=44;
  代码:Field f=clazz.getField("age");
  可以根据Field类对象的get()方法得到该字段的值,该方法接收一个参数,指明需要反射的那个对象的字段。
  代码:int num=(Integer) f.get(p);  //由于该方法返回值为Object类型,而age字段为int,所以需要强转。
  代码:System.out.println(num);     //返回44
  注:由于Person类文件可能不是我们自己编写的,所以不知道字段的类型,我们可以通过Field类对象的getType()方法得到该字段所属类型的字节码,根据字节码判断该字段的类型。
  代码:Object value=f.get(p);
  代码:Class type=f.getType();   //获得指定字段的类型,返回字节码对象
  由于每种类型的字节码只存在一份,所以用“==”判断比较高效,当然equals()方法也可以。
  if(type == int.class){
       int newValue=(Integer) value;   //自动拆箱
       System.out.println(newValue);  //自动装箱
  }
  上面的代码反射的是public修饰的字段,那么反射非public修饰的字段需要使用Class类的getDeclaredField()方法,该方法可以反射在Person类中定义(声明)过的所有字段。
  例如:反射类的字段:private static String address;
  代码:Field f=clazz.getDeclaredField("address");
  代码:f.setAccessible(true);   //由于反射的是private修饰的字段,所以需要暴力反射。
  该字段没有手动赋予值,可以通过Field类对象的set()方法写入指定的值。该方法接收两个参数,一个是需要反射的类的对象,一个是赋予该字段的实际值。由于所要获取的字段是静态的,不必创建该类的对象,所以传入null。
  代码:f.set(null, "北京市");
  如果我们不知道该字段的类型,同样可以通过getType()方法得到。
  代码:Object value=f.get(null);
  代码:Class type=f.getType();
  if(type == String.class){
       String newValue=(String)value;
       System.out.println(newValue);    //返回北京市
  }
---------------------- <a href=" http://edu.csdn.net"target="blank">ASP.Net+Android+IOS 开发</a>、<a href=" http://edu.csdn.net"target="blank">.Net 培训</a>、期待与您交流! ----------------------
详细请查看:<a href="http://edu.csdn.net" target="blank"> http://edu.csdn.net </a>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值