Java高新技术 反射机制



知识概要:

                  (1)反射的基石

(2)反射

(3)Constructor类

(4)Field类

(5)Method类

(6)用反射方式执行某个类中的main方法

(7)数组的反射

(8)反射功能实现框架功能



反射的基石   ----Class



对比提问: Person类代表人,它的实例对象就是张三,李四这样一个个具体的人,

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

对比提问:众多的人用一个什么类表示?众多的Java类用一个什么类表示?

人--->Person

           Java类---->Class

Class类代表Java类,它的各个实例对象又分别对应什么呢?

对应各个类在内存中的字节码,例如,Person类的字节码,ArrayList类的字节码,等等。

一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,

不同的类的字节码是不同的,所以它们在内存中的内容是不同的,这一个个的空间可分别用

一个个的对象来表示,这些对象显然具有相同的类型,这个类型是什么呢?

如何得到各个字节码对应的实例对象( Class类型)

类名.class,例如,System.class

对象.getClass(),例如,new Date().getClass()

Class.forName("类名"),例如,Class.forName("java.util.Date");

九个预定义Class实例对象:

参看Class.isPrimitive方法的帮助

Int.class == Integer.TYPE

数组类型的Class实例对象

Class.isArray()

总之,只要是在源程序中出现的类型,都有各自的Class实例对象,例如,int[],void…

反射

理解反射的概念



反 射 就是把Java类中的各种成分映射成相应的java类。

例如,一个Java类中用一个Class类的对象来表示,

一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的Java类来表示,

就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。

表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,

这些信息就是用相应类的实例对象来表示,它们是Field、Method、Contructor、Package等等。

一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,

通过调用Class类的方法可以得到这些实例对象后,

得到这些实例对象后有什么用呢?怎么用呢?

这正是学习和应用反射的要点。

Class类

Class类用于表示.class文件,是所有加载进内存的字节码对象的父类。所以可以通过Class得到运行时的类。

package cn.itheima.day1;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*; public class ReflectTest {
public static void main(String[] args) throws Exception { String str1= "abc";
String str5= "cde"; Class cls5 = str5.getClass();
Class cls1 = str1.getClass();
Class cls2 = String.class; Class cls3 =Class.forName("java.lang.String");
System.out.println(cls1==cls2);
System.out.println(cls1==cls3);
System.out.println("***");
System.out.println(cls1==cls5); System.out.println(cls1.isPrimitive());
System.out.println(int.class.isPrimitive());
System.out.println(int.class == Integer.class);
System.out.println(int.class == Integer.TYPE);
System.out.println(int[].class.isArray());
}

注意:字节码文件是唯一的,所以无论怎么获取,都是同一份字节码文件。

九个预定义Class实例对象(八大原始类型+void)

Class 类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。

每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该Class 对象。

基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和

关键字 void也表示为 Class 对象。

注意:

1、int.class == Integer.TYPE。

2、数组类型的Class实例对象Class.isArray()为true。

3、反射并不是Java 5.0的新特性。

Constructor类代表某个类中的一个构造方法

得到某个类所有的构造方法:

例子:Constructor [] constructors= Class.forName("java.lang.String").getConstructors();

得到某一个构造方法:

例子:Constructor constructor = Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);

获得方法时要用到类型   创建实例对象:

通常方式:String str = new String(new StringBuffer("abc"));

反射方式:

Constructor constructor = Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);

String str = (String)constructor.newInstance(new StringBuffer("abc"));

调用获得的方法时要用到上面相同类型的实例对象

Class.newInstance()方法:

例子:String obj = (String)Class.forName("java.lang.String").newInstance();、

该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。

该方法内部的具体代码是怎样写的呢?用到了缓存机制来保存默认构造方法的实例对象。

Constructor类的实例对象代表类的一个构造方法。

反射公共,私有和保护的构造方法:

反射公共的需要的方法是:getConstructor();

反射私有的需要的方法是:getDeclaredConstructor();

Constructor对象代表一个构造方法,Constructor对象有的方法:得到构造方法名字,得到所属于的类,产生实例对象。

得到某个类空参数构造方法,例:

Constructor constructor = Class.forName("java.lang.String").getConstructor();

得到某个类所有的构造方法,例:

Constructor [] constructors= Class.forName("java.lang.String").getConstructors();

得到某一个带参数的构造方法,例:

Constructor constructor =Class.forName("java.lang.String").getConstructor(StringBuffer.class);

注意:一个类有多个构造方法,用什么方式可以区分清楚想得到其中的哪个方法呢?根据参数的个数

和类型,例如,Class.getMethod(name,Class... args)中的args参数就代表所要获取的那个方法的各个参数的类型的列表。重点:参数类型用什么方式表示?用Class实例对象。

Field类代表某个类中的一个成员变量

演示用eclipse自动生成Java类的构造方法

问题:得到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量?类只有一个,而该类的实例对象有多个,如果是与对象关联,哪关联的是哪个对象呢?所以字段fieldX 代表的是x的定义,而不是具体的x变量。

示例代码:

System.out.println("字段的反射例子");

ReflectPoint pt1 = new ReflectPoint(3,5);

Field fieldY = pt1.getClass().getField("y");

System.out.println(fieldY.get(pt1));

Field fieldX = pt1.getClass().getDeclaredField("x");

fieldX.;setAccessible(true)

//注意:通过称之为暴力反射的方式解决,也就是使用setAccessible(true)使private类型的成员变量也可 以被获取值

System.out.println(fieldX.get(pt1));

作业:将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的"b"改成"a"。

changeStringValue(pt1);
System.out.println(pt1); private 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);
}
} }

Method类代表某个类中的一个成员方法

得到类中的某一个方法:

例子:      Method charAt = Class.forName("java.lang.String").getMethod("charAt", int.class);

调用方法:

通常方式: System.out.println(str.charAt(1));

反射方式: System.out.println(charAt.invoke(str, 1));

如果传递给Method对象的invoke()方法的第一个参数为null,这有着什么样的意义呢?说明该Method对象对应的是一个静态方法!

jdk1.4和jdk1.5的invoke方法的区别:

Jdk1.5:public Object invoke(Object obj,Object... args)

Jdk1.4:public Object invoke(Object obj,Object[] args),即按jdk1.4的语法,需要将一个数组作为参数传递给invoke方法时,数组中的每个元素分别对应被调用方法中的一个参数,所以,调用charAt方法的代码也可以用Jdk1.4改写为 charAt.invoke(“str”, new Object[]{1})形式。

<span style="font-size:18px;">package com.itheima.day1; 

import java.lang.reflect.Method; 

public class ReflectTest { 

	public static void main(String[] args) throws Exception { 

	String str = "黑马程序员"; 

	Method method = str.getClass().getMethod("charAt", int.class); 

//1.4写法。

	System.out.println(method.invoke(str,new Object[]{1})); 

//1.5写法。

	System.out.println(method.invoke(str, 0)); 

} 

}

</span>
<span style="font-size:18px;">对接收数组参数的成员方法进行反射 

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

package com.itheima.day01; 

import java.lang.reflect.Method; 

public class ReflectTest { 

public static void main(String[] args) throws Exception { 

	Class clszz = Class.forName(args[0]); 

	Method main = clszz.getMethod("main", String[].class); 

	main.invoke(null,"黑马程序员","黑马论坛","CSDN社区"); 

} 

} 

</span>

分析原因:启动Java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),通过反射

方式来调用这个main方法时,如何为invoke方法传递参数呢?

按JDK1.5的语法,整个数组是一个参数,而按JDK1.4的语法,数组中的每个元素对应一个参数,当把一个字符串

数组作为参数传递给invoke方法时,Javac会到底按照哪种语法进行处理呢?

JDK1.5肯定要兼容JDK1.4的语法,会按JDK1.4的语法进行处理,即把数组打散成若干个单独的参数。

所以,在给main方法传递参数时,不能使用代码main.invoke(null,"黑马程序员","黑马论坛","CSDN社区"),Javac只把它当作JDK1.4的语法进行理解,而不把它当作JDK1.5的语法解释,因此会出现参数类型不对的问题。

解决办法:
方法一:

main.invoke(null,new Object[]{new String[]{"黑马程序员","黑马论坛","CSDN社区"}});

方法二:

main.invoke(null,(Object)new String[]{"黑马程序员","黑马论坛","CSDN社区"});

编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数组打散成若干个参数了

<span style="font-size:18px;">代码示例一: 

package com.itheima.day01; 

import java.lang.reflect.Method; 

public class ReflectTest { 

	public static void main(String[] args) throws Exception { 

	Class clszz = Class.forName(args[0]); 

	Method main = clszz.getMethod("main", String[].class); 

	main.invoke(null,new Object[]{new String[]{"黑马程序员","黑马论坛","CSDN社区"}}); 

	} 

}

代码示例二: 

package com.itheima.day01; 

import java.lang.reflect.Method; 

public class ReflectTest { 

	public static void main(String[] args) throws Exception { 

	Class clszz = Class.forName(args[0]); 

	Method main = clszz.getMethod("main", String[].class); 

	main.invoke(null,(Object)new String[]{"黑马程序员","黑马论坛","CSDN社区"}); 

	} 

} 

</span>

数组的反射

System.out.println("数组类型的Class判断");
int [] a1= new int []{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());//false;
System.out.println(a1.getClass().getSuperclass().getName());



基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;

非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。

Arrays.asList()方法处理int[]和String[]时的差异。

代码示例:

package com.itheima.day01;

import java.util.Arrays;

public classReflectTest {

public static void main(String[] args) throwsException {

int[] arr =new int[] { 1, 2, 3 };

String[] str =newString[] {"黑马程序员", "黑马论坛", "CSDN社区" };

// 直接使用System.out.println无法打印出数组的内容

System.out.println(arr);

// 结果:[I@62bc184

System.out.println(str);

// 结果:[Ljava.lang.String;@22adc446

// 通过Arrays.asList方法打印出集合的内容

System.out.println(Arrays.asList(arr));

// 结果:[[I@62bc184]

/*

* 原因是因为JDK1.4中为Arrays.asList(Object[] a),JDK1.5中为Arrays.asList(T... a)。

* arr是int[]类型,JDK1.4中的asList方法处理不了,JDK1.5可以处理。但是JDK1.5将

* int数组整体作为一个参数进行处理。

* 因此最终结果就是将 int[]进行了封装,结果类型也就成了[[I。

*/

System.out.println(Arrays.asList(str));

// 结果:[a, b, c]

}

}



Array工具类用于完成对数组的反射操作。

代码示例:

package com.itheima.day01;

import java.lang.reflect.Array;

public class ReflectTest {

public static void main(String[] args)throwsException {

String[] arrOne =newString[] {"黑马程序员", "黑马论坛", "CSDN社区"};

String arrTo ="黑马程序员";

printObject(arrOne);

printObject(arrTo);

}

public static void printObject(Object obj) {

Class clazz = obj.getClass();

if (clazz.isArray()) {

int len = Array.getLength(obj);

for (inti = 0; i < len; i++) {

System.out.println(Array.get(obj, i));

}

} else {

System.out.println(obj);

}

}

}

ArrayList_HashSet的比较及Hashcode分析

分析:由以上两示例可以看到当集合为ArrayList时,其实是一个数组,因此放入4个元素后,集合size为4。

当集合为HashSet时,需要通过比较equals方法是否返回true决定是否放入。

如果equals方法返回true,那么就不会放入。因此,集合size为3。(覆盖其中一个)

如果想让size为2,也就是set1与set3作为同一个元素存入HashSet集合,那就需要覆盖ReflectPoint类的hashCode方法以及equals方法。(同时覆盖俩个方法)

在代码区右击-->Source-->点击Generate hashCode() and equals()...。

点击OK,即可生成hashCode和equals方法。

/*
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}

@Override
public 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 ;
}

*/

重新运行ReflectTest就可以看到size值为2。

注意:当一个对象被存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则,对象修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了,在这种情况下,即使在contains方法使用该对象的当前引用作为的参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也会导致无法从HashSet集合中单独删除当前对象,从而造成内存泄露。

代码示例:

如果修改了y的值,那么size为1;
如果不修改y的值,那么size为2;

分析:
由上面的示例可以看到由于set1对象的y属性被修改了,因此该对象的哈希值也被修改了,所以无法删除掉。
因此size值还是2。


反射的作用实现框架功能

              框架与框架要解决的核心问题


              我做房子卖给用户住,由用户自己安装门窗和空调,我做的房子就是框架,用户需要使用我的框架,

              把门窗插入进我提供的框架中。框架与工具类有区别,工具类被用户的类调用,而框架则是调用

              用户提供 的类。

            
              框架要解决的核心问题

             
             我在写框架(房子)时,你这个用户可能还在上小学,还不会写程序呢?

             我写的框架程序怎样能调用到你以后写的类(门窗)呢?


             因为在写才程序时无法知道要被调用的类名,所以,在程序中无法直接new 某个类的实例对象了,
        
             而要用反射方式来做。

             综合案例

            
            先直接用new  语句创建ArrayList和HashSet的实例对象,
           
            演示用eclipse自动生成 ReflectPoint类的equals和hashcode方法,比较两个集合的运行结果差异。

      
            然后改为采用配置文件加反射的方式创建ArrayList和HashSet的实例对象,比较观察运行结果差异。

            引入了elipse对资源文件的管理方式的讲解。

package cn.itheima.day1;

import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Collection;
import java.util.HashSet;
import java.util.Properties; public class ReflectTest2 {
public static void main(String[] args)throws Exception {
//InputStream ips = new FileInputStream("src/cn/itheima/day1/config.properties");//这种方式是指的javahance为当前目录。 //方式一:采用类加载器进行加载,使用相对路径的方式 //InputStream ips=ReflectTest.class.getClassLoader().getResourceAsStream("cn/itheima/day1/config.properties");//相对于src //方式二:利用Class方式进行加载,使用相对路径的方式 //InputStream ips = ReflectTest.class.getResourceAsStream("config.properties"); //方式三:利用Class方式进行加载,使用绝对路径的方式 InputStream ips = ReflectTest.class.getResourceAsStream("/resource/config.properties"); //src Properties props = new Properties();
props.load(ips);
ips.close(); String className = props.getProperty("className");
Collection collections =(HashSet)Class.forName(className).newInstance(); 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()); }
}




Java高新技术 反射机制的更多相关文章

  1. Java 类反射机制分析

    Java 类反射机制分析 一.反射的概念及在Java中的类反射 反射主要是指程序可以访问.检测和修改它本身状态或行为的一种能力.在计算机科学领域,反射是一类应用,它们能够自描述和自控制.这类应用通过某 ...

  2. java的反射机制

    一.java的反射机制浅谈 最近研究java研究得很给力,主要以看博文为学习方式.以下是我对java的反射机制所产生的一些感悟,希望各位童鞋看到失误之处不吝指出.受到各位指教之处,如若让小生好好感动, ...

  3. Java中反射机制和Class.forName、实例对象.class(属性)、实例对象getClass()的区别

    一.Java的反射机制   每个Java程序执行前都必须经过编译.加载.连接.和初始化这几个阶段,后三个阶段如下图:   其中

  4. java笔记--反射机制之基础总结与详解

    一.反射之实例化Class类的5种方式: java的数据类型可以分为两类,即引用类型和原始类型(即基本数据类型). 对于每种类型的对象,java虚拟机会实例化不可变的java.lang.Class对象 ...

  5. JAVA的反射机制学习笔记(二)

    上次写JAVA的反射机制学习笔记(一)的时候,还是7月22号,这些天就瞎忙活了.自己的步伐全然被打乱了~不能继续被动下去.得又一次找到自己的节奏. 4.获取类的Constructor 通过反射机制得到 ...

  6. java笔录---反射机制(1)

    引言   为了方便记忆java的反射机制,在这里仔细的总结了一下.主要是怕以后忘记了,这样也方便回忆.因为最近利用空余时间深入的了解spring和Mybatis框架,   像spring中核心模块IO ...

  7. java的反射机制浅谈(转)

    原文链接:java的反射机制浅谈 一.java的反射机制浅谈 1.何谓反射机制 根据网文,java中的反射机制可以如此定义: JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性 ...

  8. 【转】Java利用反射机制访问私有化构造器

    Java利用反射机制访问私有化构造器 博客分类: java   我们都知道,当一个类的构造方法被设为私有的时候(private),在其他类中是无法用new来实例化一个对象的. 但是有一种方法可以把带有 ...

  9. 【转】java原理—反射机制

    一.什么是反射:反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问.检测和修改它本身状态或行为的一种能力.这一概念的提出很快引发了计算机科学领域关于应用反射性的研究.它首先被程序语言 ...

随机推荐

  1. 原生的AJAX

    var XHR=null; if (window.XMLHttpRequest) { // 非IE内核 XHR = new XMLHttpRequest(); } else if (window.Ac ...

  2. ps中如何让图层在画布内水平居中

    下图每个小logo图案距离它们的上参考线的距离均为10px,而我们如何让图层在画布内水平居中??? 如上图中三个图层的图案是用来给Html/Css中的background属性使用的,虽然可以通过鼠标拖 ...

  3. Linux开机最简化

    [root@localhost ~]# LANG=en [root@localhost ~]# for root in chkconfig --list|grep 3:on|awk '{print $ ...

  4. Beta冲刺前准备

    一.介绍小组新成员,Ta担任的角色. 201421123121 栗海辉 来自Sugar Free 风格:低调中的高调,给你不一样的视觉 擅长的技术:C语言/JAVA 在曾经的团队里面担任主要编程人员, ...

  5. 201521123097《Java程序设计》第八周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结集合与泛型相关内容. 2. 书面作业 1.本次作业题集集合 public static List return str; } pub ...

  6. 201521123117 《Java程序设计》第7周学习总结

    1. 本周学习总结 2.书面作业 Q1.ArrayList代码分析 1.解释ArrayList的contains源代码 源代码: //contains()方法 public boolean conta ...

  7. 201521123056 《Java程序设计》第4周学习总结

    1. 本周学习总结 1.1 尝试使用思维导图总结有关继承的知识点. 1.2 使用常规方法总结其他上课内容. 1.1 instanceof 测试一个对象是否是某个类的实例,即使左边是右边类的子类的实例对 ...

  8. 201521123077 《Java程序设计》第1周学习总结

    1. 本章学习总结 java语言的历史 dos下运行 java文件 Math ,String ,Scanner,Interger等常用类的使用 jdk,jre,jvm等概念的了解 classpath, ...

  9. 201521123081《Java程序设计》 第1周学习总结

    #1. 本周学习总结 ###JAVA是1995年SUN推出的一种简单的,跨平台的,面向对象的,分布式的,解释的,健壮的,安全的,结构的,中立的,可移植的,性能很优异的,多线程的,动态的语言.是世界上广 ...

  10. Java课程设计+购物车WEB页面

    1. 团队名称(keke) 徐婉萍:网络1511 201521123006 2. 项目git地址 3. 项目git提交记录截图 4. 项目功能架构图与主要功能流程图 项目功能架构图 项目主要功能流程图 ...