黑马程序员——自学总结(六)反射

本文深入讲解Java反射机制的基础知识及应用场景,包括Class类的作用、如何获取Class对象、反射原理及其实现方法,如构造方法、成员变量和成员方法的反射应用,并探讨了反射在框架设计中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流!

一、反射的基石Class类

       类用于描述一类事物的共性,描述该类事物有什么属性,没有什么属性,至于这个属性的值是什么,则是由这个类的实例对象来确定的,不同的实例对象有不同的属性值。而Class类就是用来描述类本身,它抽出所有类的共性,比如某个Class类的对象对应某个类,和其他对应某个类的Class对象一样,它们有成员,有方法,这些就是它们的共性,可以调用方法获得成员信息和方法信息,Class类不能实例化,分别代表各种字节码。

        获取Class对象的方法有三种:1、Class cls1=Data.class;2、Person p1=new Person(); Class cls2=p1.getClass();3、Class cls3=

Class.forname("java.lang.String")。如果内存里,JVM里已存在该Class对象,则直接指向,如果不存在,则由类加载器加载,返回新加载的字节码。

        有九个预定义的Class对象,分别是八个基本数据类型对应的Class对象和void对应的Class对象。

        基本数据类型对应的包装器类内部封装了基本数据类型的Class对象,比如Integer.TYPE就等于int.class。

        isPrimitive()用来判断是否是基本类型对应的Class对象,int[].class.isPrimitive()返回false,int[].isArray()返回true,总之,只要是在源程序中出现的类型,都有各自的Class对象,例如:int[],void。

二、反射

        反射就是把Java类中的各种成分映射成相应的java类。例如,一个Java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等信息也用一个个的Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等也是一个个的类,表示Java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们是Field,Method,Contructor,Package等。

        一个类中的每个成员都可以用相应的反射类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象。

        1、 构造方法的反射应用

               Constructor类代表某个类中的一个构造方法,得到某个类所有的构造方法Constructor[]  constructors=Class.forName(类名字符串).getConstructors(),得到某一个构造方法,例如getConstructor(StringBuffer.class)该构造方法参数为StringBuffer,得到Constructor对象后,可以用newInstance(对应的参数)方法创建对象,该方法编译期返回Object(JDK1.5版本以后可用泛型代替)。

              Class对象也有newInstance()方法,调用无参构造函数,提供了便利,查看该方法源代码,发现其找到无参构造方法,找到后存储备用,每次调用重复取这个方法,比较高效,而反射每次都要重新查找,导致程序性能下降。

       2、Field类

             Field类代表某个类中的一个成员变量 ,Field  field=对象.getClass().getField(字段名) ,field.get(对象)取得对象该成员变量值,field不是对象身上的变量,而是类上,要用它去取某个对象上对应的值。私有成员无法用getField(字段名)获得,但可用getDeclardField(字段名) 获得,但此时再调用方法field.get(对象)则编译不通过。

            可以用field.setAccessible(true),使得私有成员变得可以访问,再调用field.get(对象)获得对象私有成员的值,这种方式称为暴力反射。

            成员变量修改示例:将某对象上所有String成员变量值改变,将字符串中的'b'全部改成'a'。

            代码如下:

class Person{
	private String name;
	private String address;
	public Person(String name,String address){
		this.name=name;
		this.address=address;
	}
	public String toString(){
		return "name:"+name+"...address:"+address;
	}
}


 

import java.lang.reflect.*;
class myreflect{
	public static void main(String[] args) throws Exception{
		Person p1=new Person("bobo","beijing"),p2=new Person("xiaobai","bengbu");
		System.out.println(p1);
		System.out.println(p2);
		Field[] fields1=p1.getClass().getDeclaredFields();
		for(int i=0;i<fields1.length;i++){
			fields1[i].setAccessible(true);
			if(fields1[i].getType()==String.class){
				String oldvalue=(String)fields1[i].get(p1);
				String newvalue=oldvalue.replace('b','a');
				fields1[i].set(p1,newvalue);
			}
		}
		Field[] fields2=p2.getClass().getDeclaredFields();
		for(int i=0;i<fields2.length;i++){
			fields2[i].setAccessible(true);
			if(fields2[i].getType()==String.class){
				String oldvalue=(String)fields2[i].get(p2);
				String newvalue=oldvalue.replace('b','a');
				fields2[i].set(p2,newvalue);
			}
		}
		System.out.println(p1);
		System.out.println(p2);
	}
}


程序运行结果:

      程序应该在内部处理异常,为了编写方便,直接在主函数中抛出。Class对象的getFileds()方法返回Field对象数组,field对象.getType()==String.class;指向同一个地址,用==判断,不用equals()判断,因为类字节码在内存中只有一份。field对象.set(obj,newValue)可对对象的成员值进行设置。

       3、Method类

             Method类用于成员方法的反射,Method对象代表某个类中的一个成员方法 。对象.getClass().getMethod(方法名,参数类型.class)获取指定方法对应的Method类对象。必须要指明参数类型,以确定是哪一个重载形式。Method对象.invoke(调用方法的对象,参数)可以在某个对象上调用反射来的该方法,如果调用静态方法,第一个参数用null,意味着method对应着一个静态方法,该方法调用不需要对象,第二个参数可以封装成数组,这是1.4版本的调用方式,1.5版本改成可变参数列表形式。

      4、接受数组参数的方法的反射

            写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。

            main接受的参数中含有要执行的类名,调用该类的main方法: Class.forName(" 类名").getMethod("main",String[ ].class).invoke(null,"");

            代码如下:

class test{
	public static void main(String[] args){
		System.out.println("test.main()");
		for(int i=0;i<args.length;i++)
			System.out.println(args[i]);
	}
}


 

import java.lang.reflect.*;
class callmain{
	public static void main(String[] args){
		try{
			Class.forName(args[0]).getMethod("main",String[].class).invoke(null,new Object[]{new String[]{"aa","bb","cc"}});
		}catch(ClassNotFoundException e){
			throw new RuntimeException(e);
	  	}catch(NoSuchMethodException e){
			throw new RuntimeException(e);
	  	}catch(IllegalAccessException e){
			throw new RuntimeException(e);
		}catch(InvocationTargetException e){
			throw new RuntimeException(e);
		}
	}
}


运行结果如下:

        分析Class.forName(args[0]).getMethod("main",String[].class).invoke(null,new Object[]{new String[]{"aa","bb","cc"}});

        在调用invoke()方法时,需要传入方法所需的参数。main()方法所需参数类型为String[],即字符串数组。如果写成这样的形式invoke(null,"aa","bb","cc")JDK1.5按照所定义的形式Object invoke(Object obj, Object... args) 将参数解释为可变参数类型,将传给test.main()方法三个字符串参数,与所需的字符串数组类型不匹配,如果转向低版本JDK1.4去解释,JDK1.4是将invoke()传递的参数打包成Object[]类型,即对象数组类型,也不匹配。

        如果写成这样的形式invoke(null,new String[ ]{"aa","bb","cc"})JDK1.5同样认为是打包好的可变参数形式,不匹配,转向JDK1.4解释,而JDK需要Object数组类型,String[]是字符串数组类型,两者不匹配。

        只能写成这样的形式invoke(null,new Object[]{new String[ ]{"aa","bb","cc"}}),首先JDK1.5解包,发现数组元素只有一个String[],匹配

        或者写成这样的形式invoke(null,(Object)new String[ ]{"aa","bb","cc"}),JDK1.5将解释成一个Object变量,传递给所需的String[ ],类型转型成功,匹配。

      5、数组的反射 

       具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象,代表数组的Class实例对象的getSuperclass()方法返回的是父类Object类对应的Class,int[ ]  i=new int[5];i.getName()方法返回[I ,表示类型是int类型的数组。

        直接打印数组对象,只能打印数组对象的类型和哈希值,Arrays.asList(Object[])可以将数组转化成容器打印,可以打印数组中每个元素的内容,JDK1.5版本中将参数变成了可变参数,<T> List<T> asList(T... a)。 

        Array工具类用于完成对数组的反射操作。不确定某个对象是具体对象,还是数组对象的方式如下:

        printObject(Object obj){

           Class class=obj.getClass();

           if(class.isArray(){

                 int len=Array.getLength(obj);

                 for(int i=0;i<len;i++)

                          Array.get(obj,i); 

         else

               直接打印obj;

       }

        目前还找不到比较好的方法得到数组中元素的类型。就是说我们拿Object接收一个数组对象,可以求得某个元素的具体类型,但无法取得该数组的类型。

三、HashSet中元素的比较 

        HashSet存储的元素不能重复,判断两个元素是否是同一个元素是比较两者的HashCode,每个对象都有自己的HashCode。在程序中可以覆盖对象的int hashCode()方法。

class Person{
	public String name;
	public int age;
	public Person(String name,int age){
		this.name=name;
		this.age=age;
	}
	public int hashCode(){
		return age;
	}
}
class reflectdemo{
	public static void main(String[] args){
		Person p1=new Person("p1",21);
		Person p2=new Person("p2",22);
		Person p3=new Person("p3",23);
		Person p4=new Person("p4",24);
		HashSet<Person> hs=new HashSet<Person>();
		hs.add(p1);
		hs.add(p2);
		hs.add(p3);
		hs.add(p4);
		System.out.println(hs.size());
		p4.age=25;
		System.out.println(hs.remove(p4));
		System.out.println(hs.size());
	}
}  


        上面的代码中,将Person类的hasCode()方法覆盖,用age值作为对象的HashCode值,在往hs中添加对象时,因为每个Person对象的age都不一样,HashCode也不一样,所以都能添加进来,hs.size()=4。当p4修改了字段age值,它的HashCode值也随之改变,当调用hs.remove(p4)时,将比较hs中每个元素的HashCode值,结果没有HashCode值是25的元素,删除失败,p4仍然在容器中。如果程序中反复修改对象字段值,并添加到容器中,容器比较HashCode值后都会接收,造成内存泄露。

四、反射的作用—实现框架功能。

        框架要解决的核心问题:举例说,我做房子卖给用户住,由用户自己安装门窗和空调,我做的房子就是框架,用户需要使用我的框架,把门窗插入进我提供的框架中。框架与工具有区别,工具类被用户的类调用,而框架则是调用用户提供的类。我在写框架(房子)时,你这个用户可能还在上小学,还不会写程序呢,我写的框架程序怎样能调用到你以后写的类(门窗)呢?因为在写程序时无法知道要被调用的类名,所以,在程序中无法直接new某个类的实例对象了,而要用反射方式来做。以后客户端的编写的类全部存储在配置文件里,服务器端读取文件中数据,通过反射来完成功能,这样即使客户端类还没有编写出来,或者以后有什么改动,都不会修改服务端的代码。

内容概要:该PPT详细介绍了企业架构设计的方法论,涵盖业务架构、数据架构、应用架构和技术架构四大核心模块。首先分析了企业架构现状,包括业务、数据、应用和技术四大架构的内容和关系,明确了企业架构设计的重要性。接着,阐述了新版企业架构总体框架(CSG-EAF 2.0)的形成过程,强调其融合了传统架构设计(TOGAF)和领域驱动设计(DDD)的优势,以适应数字化转型需求。业务架构部分通过梳理企业级和专业级价值流,细化业务能力、流程和对象,确保业务战略的有效落地。数据架构部分则遵循五大原则,确保数据的准确、一致和高效使用。应用架构方面,提出了分层解耦和服务化的设计原则,以提高灵活性和响应速度。最后,技术架构部分围绕技术框架、组件、平台和部署节点进行了详细设计,确保技术架构的稳定性和扩展性。 适合人群:适用于具有一定企业架构设计经验的IT架构师、项目经理和业务分析师,特别是那些希望深入了解如何将企业架构设计与数字化转型相结合的专业人士。 使用场景及目标:①帮助企业和组织梳理业务流程,优化业务能力,实现战略目标;②指导数据管理和应用开发,确保数据的一致性和应用的高效性;③为技术选型和系统部署提供科学依据,确保技术架构的稳定性和扩展性。 阅读建议:此资源内容详尽,涵盖企业架构设计的各个方面。建议读者在学习过程中,结合实际案例进行理解和实践,重点关注各架构模块之间的关联和协同,以便更好地应用于实际工作中。
资 源 简 介 独立分量分析(Independent Component Analysis,简称ICA)是近二十年来逐渐发展起来的一种盲信号分离方法。它是一种统计方法,其目的是从由传感器收集到的混合信号中分离相互独立的源信号,使得这些分离出来的源信号之间尽可能独立。它在语音识别、电信和医学信号处理等信号处理方面有着广泛的应用,目前已成为盲信号处理,人工神经网络等研究领域中的一个研究热点。本文简要的阐述了ICA的发展、应用和现状,详细地论述了ICA的原理及实现过程,系统地介绍了目前几种主要ICA算法以及它们之间的内在联系, 详 情 说 明 独立分量分析(Independent Component Analysis,简称ICA)是近二十年来逐渐发展起来的一种盲信号分离方法。它是一种统计方法,其目的是从由传感器收集到的混合信号中分离相互独立的源信号,使得这些分离出来的源信号之间尽可能独立。它在语音识别、电信和医学信号处理等信号处理方面有着广泛的应用,目前已成为盲信号处理,人工神经网络等研究领域中的一个研究热点。 本文简要的阐述了ICA的发展、应用和现状,详细地论述了ICA的原理及实现过程,系统地介绍了目前几种主要ICA算法以及它们之间的内在联系,在此基础上重点分析了一种快速ICA实现算法一FastICA。物质的非线性荧光谱信号可以看成是由多个相互独立的源信号组合成的混合信号,而这些独立的源信号可以看成是光谱的特征信号。为了更好的了解光谱信号的特征,本文利用独立分量分析的思想和方法,提出了利用FastICA算法提取光谱信号的特征的方案,并进行了详细的仿真实验。 此外,我们还进行了进一步的研究,探索了其他可能的ICA应用领域,如音乐信号处理、图像处理以及金融数据分析等。通过在这些领域中的实验和应用,我们发现ICA在提取信号特征、降噪和信号分离等方面具有广泛的潜力和应用前景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值