Java基础之反射

框架都要用到反射技术,反射都要用到一个类Class.

java程序中的各个java类属于同一类事物,描述这类事物的java类名就是Class.

得到字节码的方式有三种:

Date.class;new Date().getClass();Class.forName("java.lang.String");最后一种是有直接返回,没有则从硬盘加载。

有9个预定义的Class实例对象,8种基本数据类型加void.class:参考Class.isPrimitive方法doc;

public class ReflectTest {public static void main(String[] args) throws Exception {// args:javaplay.TestArgumentsString str1 = "abc";Class<?> cls1 = str1.getClass();Class<?> cls2 = String.class;Class<?> cls3 = Class.forName("java.lang.String");System.out.println(cls1 == cls2);// trueSystem.out.println(cls1 == cls3);// trueSystem.out.println(cls1.isPrimitive());// falseSystem.out.println(int.class.isPrimitive());// trueSystem.out.println(int.class == Integer.class);// false// Integer.TYPE代表它所包装的基本类型字节码,请查看docSystem.out.println(int.class == Integer.TYPE);// trueSystem.out.println(int[].class.isPrimitive());// falseSystem.out.println(int[].class.isArray());// trueSystem.out.println(void.class == Void.class);// false// 总之,在源程序中出现的类型,都有各自的Class实例对象,例如int[],void// 通常方式:String str = new String(new StringBuffer("abc"));// jdk5之前只能接受数组,不接受可变参数// Constructor constructor = String.class.getConstructor(new Class<?>[]// { StringBuffer.class, int.class });// 得到所有public构造方法Constructor[] constructors = Class.forName("java.lang.String").getConstructors();// 得到一个构造方法Constructor constructor1 = String.class.getConstructor(StringBuffer.class);// 反射方式(详见doc 一目了然,不能是"abc",要用到与StringBuffer相同类型的对象)String str2 = (String) constructor1.newInstance(/* "abc" */new StringBuffer("abc"));System.out.println(str2.charAt(2));// c// Class.newInstance()方法,可省去class->constructors->new object中间获取构造方法的环节// 从而直接创建对象,但它只能调用默认的无参构造方法,该方法内部先得到默认构造方法,然后用该构造方法创建对象,其中// 用到了缓存机制来保存默认构造方法的实例,这样能省点事,直接调用newInstance而不用获取构造方法的反射String o = (String) Class.forName("java.lang.String").newInstance();// 成员变量的反射ReflectPoint pt1 = new ReflectPoint(3, 5);Field fieldY = pt1.getClass().getField("y");// fieldY的值是多少?是5,错!fieldY不是对象身上的变量,而是类上,要用它去取某个对象上对应的值System.out.println(fieldY.get(pt1));Field fieldX = pt1.getClass().getDeclaredField("x");// 私有成员要用DeclaredfieldX.setAccessible(true);// 设置true才可以访问,暴力反射System.out.println(fieldX.get(pt1));// 将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的"b"改成"a"changeStringValue(pt1);System.out.println(pt1);// 成员方法的反射// 普通方式:str.charAt(1),反射方式如下:Method methodCharAt = String.class.getMethod("charAt", int.class);System.out.println(methodCharAt.invoke(str1, 1));// System.out.println(methodCharAt.invoke(null, 1));//静态方法第一个参数才为null// 1.4还有可变参数,只能用数组,又因为参数五花八门,所以用Object,1.4的调用语法如下:System.out.println(methodCharAt.invoke(str1, new Object[] { 1 }));// 对接受数组参数的成员方法进行反射// 写程序的时候不知道要执行哪个类的mainTestArguments.main(new String[] { "111", "222", "333" });String startingClassName = args[0];Method mainMethod = Class.forName(startingClassName).getMethod("main", String[].class);mainMethod.invoke(null, new Object[] { new String[] { "111", "222", "333" } });// 原因在于1.5要兼容1.4mainMethod.invoke(null, (Object) new String[] { "111", "222", "333" });// 也行,总之,只能接受一个参数// mainMethod.invoke(null, (Object) new String[] { "111" }, (Object) new// String[] { "222" });// 不行// mainMethod.invoke(null, new String[] { "111" });// 不行/*** 启动java程序的main方法的参数是一个字符串数组,通过反射方式调用时,如何传递参数呢,按1.5的语法,整个数组是一个参数,* 而按1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac会按照哪种方法进行* 处理呢?1.5肯定要兼容1.4,会按1.4的语法来处理,即把数组打散成若干个单独的参数,所以,在给main方法传递参数时,不能使用* mainMethod.invoke(null,new* String[]{"xxx"}),javac会把它当作1.4语法来理解,会把里面的"xxx"当作需要传递的String[]{},所以* 参数类型不对。*//** 数组与Object的关系及其反射类型 . 1.具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象* 2.代表数组的Class实例对象的getSuperclass()方法返回的父类为Object类对应的Class* 3.基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既* 可以当做Object类型使用,又可以当作Object[]类型使用* 4.Arrays.asList()方法处理int[]和String[]时的差异*/int[] a1 = new int[] { 1, 2, 3 };Integer[] a11 = new Integer[] { 1, 2, 3 };int[] a2 = new int[4];int[][] a3 = new int[2][3];String[] a4 = new String[] { "a", "b", "c" };System.out.println(a1.getClass() == a2.getClass());// true 数组的类型和维度相同就一样// System.out.println(a1.getClass() == a4.getClass());//false// System.out.println(a1.getClass() == a3.getClass());//falseSystem.out.println(a1.getClass().getName());// [I Class的getName方法有详细说明[ISystem.out.println(a1.getClass().getSuperclass().getName());// java.lang.ObjectSystem.out.println(a4.getClass().getSuperclass().getName());// java.lang.ObjectSystem.out.println(a4.getClass().getName());// [Ljava.lang.String;System.out.println(String.class.getName());// java.lang.StringSystem.out.println(String.class.getSuperclass().getName());// java.lang.ObjectSystem.out.println(a11.getClass().getName());// [Ljava.lang.Integer;System.out.println(a11.getClass().getSuperclass().getName());// java.lang.ObjectObject aObj1 = a1;Object bObj2 = a4;Object aObj11 = a11;// Object[] aObj3 = a1;// 不能赋值,int不是ObjectObject[] aObj4 = a3;// a3是一维数组的数组,Object相当于一个一维int数组,此时Object[]表示一维数组(int[])的数组Object[] aObj5 = a4;// String是ObjectObject[] aObj6 = a11;// 由此引申的一个问题System.out.println(a1);// [I@5fb7a531System.out.println(a4);// [Ljava.lang.String;@11be650f// 确实转换成了数组,只是数组里只有一个元素,这个元素也是数组System.out.println(Arrays.asList(a1));// [[I@5fb7a531]// 字符串却可以转换,原因在于asList接受的是Object[](1.4)和T...(1.5),如果是String[]就按// 1.4的形式(因为符合1.4的形式,要向下兼容)转换成了list了,// 如果是int[]就不符合Object[]的形式,就会按1.5的形式T...来处理,也即当成一个int[](即T)类型的元素System.out.println(Arrays.asList(a4));// [a, b, c]// 数组的反射printObject(a1);// 1 2 3printObject(a4);// a b cprintObject("xyz");// xyz// 有没有办法得到数组的类型?// 目前没有 Object[] a = new Object[]{"a",1};// a[0].getClass();// 即只能得到某个具体元素的类型,不能得到整个数组的元素类型// 写框架这些东西是必备,不写框架,翻某些砖头似的书像翻小说一样了,this is 进步。}public static void changeStringValue(Object obj) throws Exception {Field[] fields = obj.getClass().getFields();for (Field field : fields) {if (field.getType() == String.class) {// 同一份字节码,推荐用==String oldValue = (String) field.get(obj);String newValue = oldValue.replace("b", "a");field.set(obj, newValue);}}}public static void printObject(Object obj) {Class clazz = obj.getClass();if (clazz.isArray()) {// 对数组进行反射的类Arrayint len = Array.getLength(obj);for (int i = 0; i < len; i++) {System.out.println(Array.get(obj, i));}} else {System.out.println(obj);}}}class TestArguments {public static void main(String[] args) {for (String arg : args) {System.out.println(arg);}}
}
public class ReflectPoint {private int x;public int y;public String str1 = "ball";public String str2 = "basketball";public String str3 = "itcast";public ReflectPoint(int x, int y) {super();this.x = x;this.y = y;}@Overridepublic int hashCode() {final int prime = 31;int result = 1;result = prime * result + x;result = prime * result + y;return result;}@Overridepublic boolean equals(Object obj) {if (this == obj)return true;if (obj == null)return false;if (getClass() != obj.getClass())return false;ReflectPoint other = (ReflectPoint) obj;if (x != other.x)return false;if (y != other.y)return false;return true;}@Overridepublic String toString() {return "ReflectPoint [x=" + x + ", y=" + y + ", str1=" + str1 + ", str2=" + str2 + ", str3=" + str3 + "]";}}

反射就是把java类中的各种成分映射成相应的java类,例如,一个java类用一个Class类的对象表示,一个类中的组成部分:成员变量、方法、构造方法、包等信息也用一个个的java类来表示,它们是Field、Method、Constructor、Package等。

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

ArrayList_HashSet比较及hashcode分析:

public class ReflectTest2 {public static void main(String[] args) {Collection<ReflectPoint> collections = new HashSet<>();ReflectPoint pt1 = new ReflectPoint(3, 3);ReflectPoint pt2 = new ReflectPoint(5, 5);ReflectPoint pt3 = new ReflectPoint(3, 3);collections.add(pt1);collections.add(pt2);collections.add(pt3);collections.add(pt1);pt1.y = 7;// 修改后输出2,不修改,输出1collections.remove(pt1);// new ArrayList<>();输出4,添加时不比较直接按顺序存储引用变量、// new HashSet<>();输出3,存放时会先判断集合里有没有这个对象,有就不放不是覆盖,想覆盖要先remove再放// HashSet比较对象时调用对象默认equals是比较内存地址(通常是根据内存地址换算出来的,效果与==等价)// 实现equals和hashcode方法后输出2,如果此时注释hashcode或者equals其中任意一个则又输出3了// 原因在于它会先根据hashcode的值,算出自己要存放的最终位置所在的区域,如果只注释hashcode是有可能输出2的,因为存放的区域可能相同,也可能不同// 再调用equals进行比较是否有相等的对象(hashcode只在hash算法的集合中才有意义,其它集合没有任何价值),// 这就是输出3的原因,pt1和pt3存放的区域不一样,原因是没有实现hashcode则根据内存地址进行计算存放的区域,// 为了让相等的对象也肯定放在相同的区域,就有一个说法,如果两个对象equals相等,则应该也要让hashcode相等(不是hash集合就不要搞hashcode)// 注意:当一个对象被存储进hashset集合中,就不能修改这个对象中那些参与计算哈希值的字段了。否则就删除不了了,日积月累,// 不断的添加、修改、删除对象,而程序却一直使用这个hash集合就会造成内存泄漏!因为hash集合中有很多对象已经没用了却没释放// 由hashcode的作用讲到内存泄漏,由内存泄漏讲到hashcode的作用!System.out.println(collections.size());}}

反射的作用->实现框架的功能

框架与工具类有区别,工具类被用户的类调用,而框架则是调用用户提供的类。

用反射技术开发框架的原理:简单框架,把要使用的类放到配置文件里面

在工程根目录下创建config.properties文件:

className=java.util.HashSet
public class ReflectTest2 {public static void main(String[] args) throws Exception {InputStream ips = new FileInputStream("config.properties");Properties props = new Properties();props.load(ips);ips.close();// 跟操作系统说,把window干掉,把自己关联的系统/物理资源释放,自己则由垃圾回收器管理String className = props.getProperty("className");Collection<ReflectPoint> collections = (Collection<ReflectPoint>) Class.forName(className).newInstance();// Collection<ReflectPoint> collections = new HashSet<>();ReflectPoint pt1 = new ReflectPoint(3, 3);ReflectPoint pt2 = new ReflectPoint(5, 5);ReflectPoint pt3 = new ReflectPoint(3, 3);collections.add(pt1);collections.add(pt2);collections.add(pt3);collections.add(pt1);// pt1.y = 7;// 修改后输出2,不修改,输出1// collections.remove(pt1);System.out.println(collections.size());// config.properties文件内容是HashSet:2,ArrayList:4}}

用类加载器的方式管理资源和配置文件

public class ReflectTest2 {public static void main(String[] args) throws Exception {// eclipse会自动把包下面的java文件或者普通文件编译后拷贝到classpath路径下// InputStream ips = new FileInputStream("config.properties");// 类加载器能加载.class文件,那加载普通文件也是顺带的事,下表示在classpath指定的根目录下去找指定的文件// 根目录下肯定没有,只是根目录下的javaplay目录下才有,此时不能定写成/javaplay// ssh框架内部用的就是用的类加载器加载的配置文件,所以ssh的配置文件必须放在classpath指定的目录下,原因就是它// 用的是类加载器读取的配置文件,而第一种不仅可以读还可以写即保存,类加载器的方式只能读不能写InputStream ips11 = ReflectTest2.class.getClassLoader().getResourceAsStream("javaplay/config.properties");// 类提供了一个便捷方法不用获取类加载器就可以加载配置文件,而且默认加载与自己同一个包内的配置文件InputStream ips2 = ReflectTest2.class.getResourceAsStream("config.properties");// 也可以相对路径InputStream ips3 = ReflectTest2.class.getResourceAsStream("resource/config.properties");// 也可以绝对路径,此时必须要以/开头,不管相对还是绝对内部都是调用InputStream ips = ReflectTest2.class.getResourceAsStream("/javaplay/resource/config.properties");Properties props = new Properties();props.load(ips);ips.close();// 跟操作系统说,把window干掉,把自己关联的系统/物理资源释放,自己则由垃圾回收器管理String className = props.getProperty("className");Collection<ReflectPoint> collections = (Collection<ReflectPoint>) Class.forName(className).newInstance();// Collection<ReflectPoint> collections = new HashSet<>();ReflectPoint pt1 = new ReflectPoint(3, 3);ReflectPoint pt2 = new ReflectPoint(5, 5);ReflectPoint pt3 = new ReflectPoint(3, 3);collections.add(pt1);collections.add(pt2);collections.add(pt3);collections.add(pt1);System.out.println(collections.size());// config.properties文件内容是HashSet:2,ArrayList:4}}


 

 

 


 

 

转载于:https://www.cnblogs.com/john8169/p/9780569.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/373722.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

php socketconnect连接失败_PHP设计模式之模板方法模式

模板方法模式&#xff0c;也是我们经常会在不经意间有会用到的模式之一。这个模式是对继承的最好诠释。当子类中有重复的动作时&#xff0c;将他们提取出来&#xff0c;放在父类中进行统一的处理&#xff0c;这就是模板方法模式的最简单通俗的解释。就像我们平时做项目&#xf…

linux系统硬件配置查看方法

一&#xff1a;查看cpu more /proc/cpuinfo | grep "model name" grep "model name" /proc/cpuinfo 如果觉得需要看的更加舒服 grep "model name" /proc/cpuinfo | cut -f2 -d: 二&#xff1a;查看内存 grep MemTotal /proc/meminfo grep MemT…

java String源码学习

public final class Stringimplements java.io.Serializable, Comparable<String>, CharSequence {/**char数组用于字符的存储 */private final char value[];/** 缓存string的hash码 */private int hash; // Default to 0public String() {/**无参构造函数,打印值为&quo…

JBoss AS 7.1.0.Final“ Thunder”发布-Java EE 6 Full Profile认证!

在JBoss AS7上进行了大约一年的开发后&#xff0c;我们现已发布7.1.0.Final“ Thunder” &#xff01; 可从此处的通常位置下载该文件。 对于JBoss AS7团队来说&#xff0c;这是一个非常大的成就&#xff0c;我们为此版本感到非常自豪。 这个版本包含了7.1.0.CR1b的许多错误修复…

数据库建表练习(10.11作业)

转载于:https://www.cnblogs.com/HRZJ/p/5951897.html

天干地支计算公式_天干地支主怎样计算?

回答&#xff1a;少女时代级别&#xff1a;硕士研究生2009-01-08 20:34:04来自&#xff1a;贵州省贵阳市我国古代是用天干地支来纪年的&#xff0c;现代社会已很少使用。一来现今社会已经离不开国际化&#xff0c;沿用老的历法已经不现实&#xff1b;二来天干地支在民间多用于算…

从程序员到CTO的Java技术路线图

时间:2013-05-29 17:39来源:www.chengxuyuans.com在技术方面无论我们怎么学习&#xff0c;总感觉需要提升自已不知道自己处于什么水平了。但如果有清晰的指示图供参考还是非常不错的&#xff0c;这样我们清楚的知道我们大概处于那个阶段和水平。 Java程序员 高级特性 反射、泛型…

Linux的网卡相关

检测linux下网卡是否正常 1.dmesg | grep eth 如果出现以下 eth0: link up 说明是网卡正常的 eth0: registered as PCnet/PCI II 79C970Aeth0: link upeth0: no IPv6 routers present 2.如果所用网卡是pcI总线的 。用命令&#xff1a;lspci 能看到网卡是存在的。 3.用ethtool…

设置Spring 3开发环境

本教程简要说明了如何设置典型的环境来开发基于Spring的应用程序。 除了可以正常工作的Windows XP客户端具有足够的CPU能力和内存外&#xff0c;本教程没有其他先决条件。 在教程中&#xff0c;我们将需要安装以下组件&#xff1a; Java 6 JDK更新26 用于Java EE开发人员的Ecl…

3. 跟踪标记 (Trace Flag) 1204, 1222 抓取死锁信息

跟踪标记&#xff1a;1204/1222 功能及用途&#xff1a; 捕获SQL Server死锁信息&#xff0c;并自动存放到错误日志(ERRORLOG)中。 举例&#xff1a; USE tempdb GO CREATE TABLE t1(id int) INSERT t1 SELECT 1CREATE TABLE t2(id int) INSERT t2 SELECT 1 GO --开启1204/1222…

postgresql建表带注释_postgresql建表带自增id和注释语句

work是表名&#xff0c;nextval(work_myid_seq::regclass)这个是自增id的设置CREATE TABLE "public"."work" ("id" int8 NOT NULL DEFAULT nextval(work_myid_seq::regclass),"wid" int8,"address" varchar(100) COLLATE &q…

js中window.onload 与 jquery中$(document.ready()) 测试

js中window.onload 与 jquery中$(document.ready())区别&#xff0c;验证代码如下(调换js代码和Jquer代码书写顺序测试&#xff0c;运行结果一样&#xff0c;因此与代码书写位置没关系)&#xff1a;<html> <head> <script typetext/javascript srcjquery-1.11.1…

5. Longest Palindromic Substring

更新&#xff1a; 之前那种dp太笨重了有个非常的轻巧的做法&#xff0c;原理都是一样的。 转移方程不变&#xff0c;但是不需要特别的初始化 判断某个格子是不是true&#xff0c;是 1.要么长度小于3&#xff0c;要么dp[start1][end-1]true 2.并且s.charAt(start) s.charAt(end…

Java中的定制国际化(i18n)

国际化&#xff08;i18n&#xff09;在我们的软件项目中非常重要。 它主要带来以下好处&#xff1a; 将UI字符串外部化为除代码文件之外的外部文件&#xff0c;以及易于管理的UI内容。 支持多种语言。 在这篇文章中&#xff0c;将为Eclipse和Java项目提供一个简短的i18n实际示…

SEO 百度后台主动推送链接

实践步骤&#xff0c;先用爬虫程序将本网站的所有连接爬取出来&#xff0c;再用python文件处理程序把爬虫来的东东整理成一行一个链接的文本格式。再用postman接口测试工具&#xff0c;使用post方式&#xff0c;将所有的链接post过去&#xff0c;这样主动推送是最为快速的提交方…

linux版本 如何查kali_000_Kali Linux版本查看和apt源配置

1.查看系统版本# cat /etc/issue# lsb_release -a2.查看内核信息# uname -a3.更新源# cp /etc/apt/source.list{,.bak}# vim /etc/apt/sources.list//备注&#xff1a;国外源速度太慢&#xff0c;这里禁止&#xff1b;网络中的部分源已经过期&#xff0c;建议更换其它源。# kal…

nyoj--127--星际之门(一)(生成树的数量)

星际之门&#xff08;一&#xff09; 时间限制&#xff1a;3000 ms | 内存限制&#xff1a;65535 KB难度&#xff1a;3描述公元3000年&#xff0c;子虚帝国统领着N个星系&#xff0c;原先它们是靠近光束飞船来进行旅行的&#xff0c;近来&#xff0c;X博士发明了星际之门&…

Oracle 常用的一些函数

字符函数 SELECT UPPER(hello WORLD) FROM DUAL; //将小写字母变为大写字母SELECT LOWER(hello WORLD) FROM DUAL; //将大写字母变为小心字母SELECT INITCAP(hello WORLD) FROM DUAL; //将字符串的首字母大写SELECT CONCAT(hello, world) FROM DUAL; //字符串拼…

Apache Camel 2.9发布–十大变化

在2011年的最后一天&#xff0c;阿帕奇骆驼制品被成功地推到了中央行销仓库&#xff0c;距离香槟酒瓶破裂并进入2012年仅1.5个小时之遥。 2.9版是创纪录的发行版&#xff0c;自5个月前发布2.8版以来&#xff0c;已解决了约500张JIRA票证。 以下是10个最明显的改进和新功能的分…

HTML5笔记——formData

注&#xff1a;formData中的数据在控制台上的console里面是打印不出来的&#xff0c;只能在控制台的network里面查看到具体的发送数据和发送选项 文章出处&#xff1a;梦想天空 XMLHttpRequest Level 2 添加了一个新的接口——FormData。利用 FormData 对象&#xff0c;我们可以…