----------- [url=http://www.itheima.com]android培训[/url]、[url=http://www.itheima.com]java培训[/url]、java学习型技术博客、期待与您交流! ------------
[b]反射[/b]
反射就是将java类中的各种成分反射成相应的java类。
java程序中的各种java类属于同一类事物,描述这类事物的java类名就是Class。
[b]得到一个类字节码的两种情况[/b]
1.这个类的字节码已经加载到内存中,直接返回已存在的那份字节码
2.如果内存中还没有这个类的字节码,用类加载器将字节码加载进内存并缓存起来,返回加载进来的字节码。
[b]获取类字节码的三种方式 [/b]
1.类名.class,比如Person类的字节码Person.class
2.对象.getClass(),如new Person().getClass().
3.Class.forName(className),如Class.forName(“java.lang.String”);
[b]Constructor类获取某个类的构造方法 [/b]
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”));
反射方式:String str = (String)constructor.newInstance(new StringBuffer(“abc”));
[b]Field类,获取某个类的字段 [/b]
Field类代表某一个类的一个成员变量
[b]Method类,获取某个类的方法 [/b]
Method类代表某一个类中的一个成员方法。
得到类中的某一个方法
例如 Method charAt = Class.forName(“java.lang.String”).getMethod(“charAt”,int.class);
通过反射调用方法
String str = “abc”;
charAt.invoke(str,1);
[b]用反射执行某个类中的main方法 [/b]
问题:
启动java程序main方法参数是一个字符串数组,即public static void main(String[] args),通过反射方式调用main方法时,如何为invoke方法传递参数呢?按jdk1.5的语法整个数组是一个参数,而按jdk1.4的语法,数组中每一个元素对于一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac到底会按照那种语法进行处理呢?jdk1.5肯定要兼容jdk1.4的语法,会按照jdk1.4的语法进行处理,即把数组打散成若干个单独的参数。所以,在给main方法传参数时,不能使用代码mainMethod.invoke(null,new String[]{“xxxx”});java只把它作为jdk1.4语法进行理解,而不把它作为jdk1.5语法解释,因此会出现参数类型不对的问题。
解决办法:
代码体现:
[b]数组的反射 [/b]
特点:
1.具有相同维数和元素类型的数组属于同一类型,即具有相同的Class实例对象。
2.代表数组的Class实例对象的getSupperClass()方法返回的父类为Object类对应的Class。
3.基本类型的一维数组可以当做Object类型来使用,不能当做Object[]来使用;
非基本类型的一维数组既可以当做Object类型来使用,也可以当做Object[]来使用。
[b]反射的综合案例[/b]
ArrayList和HashSet以及hashCode分析
1. ArrayList可以存储相同元素
2. HashSet不可以存储相同元素,和其他哈希结构的集合判断元素唯一的依据是hashCode和equals方法。
3. hashCode的由来
我们要判断集合中是否存在某一个元素要遍历这个集合进行查找,这样效率很低,有人发明了一种方法,将集合分成几个区域,每个对象可以算出一个哈希码,将这些哈希码分组,不同的哈希码对于不同的存储区域,要判断集合中是否有某个元素,根据它的哈希码去指定的区域查找即可,这样大大提高了效率。
4. 当一个对象被存储进HashSet集合中以后,就不要在修改这个对象中那些参与计算哈希值的字段,否则,对象修改后的哈希值与最初存储进HashSet集合时的哈希值就不同了。在这种情况下,即使在contains方法是用该对象的当前引用作为的参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也会导致无法从HashSet中单独删除当前对象,从而造成内存泄露。
[b]代码示例:演示反射获取ArrayList和HashSet的元素个数,以及复写hashCode和equals方法 [/b]
要往集合中添加的对象所属的类ReflectPoint
演示:ArrayList和HashSet集合元素的存储,以及通过反射获取集合
[b]反射的作用-->实现框架功能 [/b]
框架与框架要解决的核心问题
我做房子卖给用户住,由用户自己安装门窗和空调,我做的房子就是框架,用户需要使用我的框架,把门窗插入我提供的框架中。框架与工具的区别是,工具类被用户的类调用,框架调用用户提供的类。
框架要解决的核心问题
我在写框架(房子时),你这个用户可能还在上小学,还不会写程序呢?我写的框架怎样调用以后你写的类(门窗)呢?
因为在写程序时,无法知道要被调用的类名,所以在程序中无法直接new某个类的实例对象,而要用反射来做。
具体实现:
用配置文件加反射的方式创建ArrayList和HashSet的实例对象。
[b]--------------加载配置文件的三种方法----------[/b]
配置文件配置config.properties
#className=java.util.HashSet
className=java.util.ArrayList
配置文件给用户用的时候通常是和class文件放在一起的,也就是在class文件所在的包或者子包里。得到配置文件的输入流对象,用Properties对象的load方法加载。
1.通过FileInputStream加载,相对项目路径,如果配置文件在相对于项目路径下。
InputStream ips = new FileInputStream("config.properties");
实际开发中,这种方式的路径是根据getRealPath()和配置文件名拼接得到的。而不是硬编码方式。
2.通过ClassLoader的getResourceAsStream方法,在classpath(bin目录)下查找路径需要加包名路径
InputStream ips = ReflectTest.class.getClassLoader().getResourceAsStream("cn/itheima/reflect/resources/config.properties");
3.通过字节码class对象的getResourceAsStream方法,直接在class当前包下查找不用加包路径
InputStream ips = ReflectTest.class.getResourceAsStream("resources/config.properties");
最后,将输入流对象ips作为参数传入Properties的load方法,并获取className
Properties p = new Properties();
p.load(ips);
[b]代码演示:通过三种方式加载配置文件,以及通过反射获取集合ArrayList或HashSet实例对象 [/b]
----------- [url=http://www.itheima.com]android培训[/url]、[url=http://www.itheima.com]java培训[/url]、java学习型技术博客、期待与您交流! ------------
[b]反射[/b]
反射就是将java类中的各种成分反射成相应的java类。
java程序中的各种java类属于同一类事物,描述这类事物的java类名就是Class。
[b]得到一个类字节码的两种情况[/b]
1.这个类的字节码已经加载到内存中,直接返回已存在的那份字节码
2.如果内存中还没有这个类的字节码,用类加载器将字节码加载进内存并缓存起来,返回加载进来的字节码。
[b]获取类字节码的三种方式 [/b]
1.类名.class,比如Person类的字节码Person.class
2.对象.getClass(),如new Person().getClass().
3.Class.forName(className),如Class.forName(“java.lang.String”);
[b]Constructor类获取某个类的构造方法 [/b]
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”));
反射方式:String str = (String)constructor.newInstance(new StringBuffer(“abc”));
[b]Field类,获取某个类的字段 [/b]
Field类代表某一个类的一个成员变量
package cn.itcast.reflect;
import java.lang.reflect.*;
class Student
{
public String name;
private int age;
Student(){}
Student(String name,int age)
{
this.name =name;
this.age = age;
}
}
public class ReflectTest
{
public static void main(String[] args)throws Exception
{
Student stu = new Student(“何竹冬”,25);
//访问公有属性name
Field fieldName = stu.getClass().getField(“name”);
//暴力反射,访问私有属性
Field fieldAge = stu.getClass().getDeclaredField(“age”);
fieldAge.setAccessible(true);//将age属性设置为可访问
System.out.println(fieldName.get(stu)+”...”+fieldAge.get(stu));
}
}
[b]Method类,获取某个类的方法 [/b]
Method类代表某一个类中的一个成员方法。
得到类中的某一个方法
例如 Method charAt = Class.forName(“java.lang.String”).getMethod(“charAt”,int.class);
通过反射调用方法
String str = “abc”;
charAt.invoke(str,1);
[b]用反射执行某个类中的main方法 [/b]
问题:
启动java程序main方法参数是一个字符串数组,即public static void main(String[] args),通过反射方式调用main方法时,如何为invoke方法传递参数呢?按jdk1.5的语法整个数组是一个参数,而按jdk1.4的语法,数组中每一个元素对于一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac到底会按照那种语法进行处理呢?jdk1.5肯定要兼容jdk1.4的语法,会按照jdk1.4的语法进行处理,即把数组打散成若干个单独的参数。所以,在给main方法传参数时,不能使用代码mainMethod.invoke(null,new String[]{“xxxx”});java只把它作为jdk1.4语法进行理解,而不把它作为jdk1.5语法解释,因此会出现参数类型不对的问题。
解决办法:
mainMethod.invoke(null,new Object[]{new String[]{“xxxx”}})
mainMethod.invoke(null,(Object)new String[]{“xxxx”});
代码体现:
import java.lang.reflect.*;
public class Test
{
public static void main(String[] args) throws Exception
{
String startingClassName = args[0];
Method mainMethod= Class.forName(startingClassName).getMethod(“main”,String[].class);
//将字符串数组作为Object数组的一个元素
mainMethod.invoke(null,new Object[]{new String[]{“111”,”222”,”333”}});
}
}
class TestArguments
{
public static void main(String[] args)
{
for(String arg : args)
System.out.println(arg);
}
}
[b]数组的反射 [/b]
特点:
1.具有相同维数和元素类型的数组属于同一类型,即具有相同的Class实例对象。
2.代表数组的Class实例对象的getSupperClass()方法返回的父类为Object类对应的Class。
3.基本类型的一维数组可以当做Object类型来使用,不能当做Object[]来使用;
非基本类型的一维数组既可以当做Object类型来使用,也可以当做Object[]来使用。
int[] a1 = new int[3];
int[] a2 = new int[4];
int[][] a3 =new int[2][3];
String[] a4 = new String[3];
System.out.println(a1.getClass()==a2.getClass());//元素数据类型和数组维度相同是同一份字节码
System.out.println(a1.getClass()==a3.getClass());//元素数据类型相同,数组维度不同不是同一份字节码
System.out.println(a1.getClass()==a4.getClass());//元素数据类型不同,数组维度也不同不是同一份字节码
[b]反射的综合案例[/b]
ArrayList和HashSet以及hashCode分析
1. ArrayList可以存储相同元素
2. HashSet不可以存储相同元素,和其他哈希结构的集合判断元素唯一的依据是hashCode和equals方法。
3. hashCode的由来
我们要判断集合中是否存在某一个元素要遍历这个集合进行查找,这样效率很低,有人发明了一种方法,将集合分成几个区域,每个对象可以算出一个哈希码,将这些哈希码分组,不同的哈希码对于不同的存储区域,要判断集合中是否有某个元素,根据它的哈希码去指定的区域查找即可,这样大大提高了效率。
4. 当一个对象被存储进HashSet集合中以后,就不要在修改这个对象中那些参与计算哈希值的字段,否则,对象修改后的哈希值与最初存储进HashSet集合时的哈希值就不同了。在这种情况下,即使在contains方法是用该对象的当前引用作为的参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也会导致无法从HashSet中单独删除当前对象,从而造成内存泄露。
[b]代码示例:演示反射获取ArrayList和HashSet的元素个数,以及复写hashCode和equals方法 [/b]
要往集合中添加的对象所属的类ReflectPoint
package cn.itheima.reflect;
/**
* 要往集合中添加的对象所属的类ReflectPoint
* @author hezhudong
*
*/
public class ReflectPoint {
private int x;
@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;
}
public int y;
ReflectPoint(int x,int y) {
this.x = x;
this.y = y;
}
}
演示:ArrayList和HashSet集合元素的存储,以及通过反射获取集合
package cn.itheima.reflect;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
/**
* 演示:ArrayList和HashSet集合元素的存储,以及通过反射获取集合
* @author hezhudong
*
*/
public class ReflectTest {
/**
* @param args
*/
public static void main(String[] args) {
testArrayList();
testHashSet();
}
public static void testArrayList() {
Collection coll = new ArrayList();
//ArrayList可以存放相同元素,所以打印size应该是3
coll.add(new ReflectPoint(3,3));
coll.add(new ReflectPoint(5,5));
coll.add(new ReflectPoint(3,3));
System.out.println("the size of ArrayList is "+coll.size());
}
public static void testHashSet() {
Collection coll = new HashSet();
//HashSet不可以存放相同元素,判断元素是否相同的依据是hashCode和equals方法,
//所以打印size应该是2
coll.add(new ReflectPoint(3,3));
coll.add(new ReflectPoint(5,5));
coll.add(new ReflectPoint(3,3));
System.out.println("the size of HashSet is "+coll.size());
}
}
[b]反射的作用-->实现框架功能 [/b]
框架与框架要解决的核心问题
我做房子卖给用户住,由用户自己安装门窗和空调,我做的房子就是框架,用户需要使用我的框架,把门窗插入我提供的框架中。框架与工具的区别是,工具类被用户的类调用,框架调用用户提供的类。
框架要解决的核心问题
我在写框架(房子时),你这个用户可能还在上小学,还不会写程序呢?我写的框架怎样调用以后你写的类(门窗)呢?
因为在写程序时,无法知道要被调用的类名,所以在程序中无法直接new某个类的实例对象,而要用反射来做。
具体实现:
用配置文件加反射的方式创建ArrayList和HashSet的实例对象。
[b]--------------加载配置文件的三种方法----------[/b]
配置文件配置config.properties
#className=java.util.HashSet
className=java.util.ArrayList
配置文件给用户用的时候通常是和class文件放在一起的,也就是在class文件所在的包或者子包里。得到配置文件的输入流对象,用Properties对象的load方法加载。
1.通过FileInputStream加载,相对项目路径,如果配置文件在相对于项目路径下。
InputStream ips = new FileInputStream("config.properties");
实际开发中,这种方式的路径是根据getRealPath()和配置文件名拼接得到的。而不是硬编码方式。
2.通过ClassLoader的getResourceAsStream方法,在classpath(bin目录)下查找路径需要加包名路径
InputStream ips = ReflectTest.class.getClassLoader().getResourceAsStream("cn/itheima/reflect/resources/config.properties");
3.通过字节码class对象的getResourceAsStream方法,直接在class当前包下查找不用加包路径
InputStream ips = ReflectTest.class.getResourceAsStream("resources/config.properties");
最后,将输入流对象ips作为参数传入Properties的load方法,并获取className
Properties p = new Properties();
p.load(ips);
[b]代码演示:通过三种方式加载配置文件,以及通过反射获取集合ArrayList或HashSet实例对象 [/b]
package cn.itheima.reflect;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Collection;
import java.util.Properties;
/**
* 演示:通过三种方式加载配置文件,以及通过反射获取集合
* @author hezhudong
*
*/
public class ReflectTest {
public static void main(String[] args) throws Exception{
//通过指定的方式加载配置文件并获取ArrayList或HashSet对象
Collection coll = getCollectionByClass();
/*向集合中添加元素*/
coll.add(new ReflectPoint(3,3));
coll.add(new ReflectPoint(5,5));
coll.add(new ReflectPoint(3,3));
//打印集合中元素个数,ArrayList时为3,HashSet时为2
System.out.println(coll.size());
}
/**
* 用FileInputStream加载配置文件并获取集合实例对象
* @return 返回集合实例对象
* @throws Exception 为了演示代码,没有处理异常
*/
public static Collection getCollectionByFileInputStream() throws Exception{
/*获取Properties对象并通过FileInputStream加载配置文件获取className*/
Properties p = new Properties();
//相对于项目路径如果配置文件放在eclipse项目相对路径下
InputStream ips = new FileInputStream("config.properties");
p.load(ips);
ips.close();
String className = p.getProperty("className");
/*获取集合实例对象*/
Class clazz = Class.forName(className);
Collection coll = (Collection)clazz.newInstance();
return coll;
}
/**
* 用ReflectTest类的加载器去加载配置文件并获取集合实例对象
* @return 返回集合的实例对象
* @throws Exception 为了演示代码,没有处理异常
*/
public static Collection getCollectionByClassLoader() throws Exception {
/*获取Properties对象并通过ClassLoader加载配置文件获取className*/
Properties p = new Properties();
//相对于classpath路径(bin目录)路径
InputStream ips = ReflectTest.class.getClassLoader().
getResourceAsStream("cn/itheima/reflect/resources/config.properties");
p.load(ips);
ips.close();
String className = p.getProperty("className");
/*获取集合实例对象*/
Class clazz = Class.forName(className);
Collection coll = (Collection)clazz.newInstance();
return coll;
}
/**
* 通过Class字节码对象的getresourceAsStream方法加载配置文件,并获取集合实例对象
* @return 返回集合的实例对象
* @throws Exception 为了演示代码,没有处理异常
*/
public static Collection getCollectionByClass() throws Exception {
/*获取Properties对象,
* 并通过Class字节码对象的getResourceAsStream方法加载配置文件获取className
*/
Properties p = new Properties();
//相对class文件包目录
InputStream ips = ReflectTest.class.getResourceAsStream("resources/config.properties");
p.load(ips);
ips.close();
String className = p.getProperty("className");
/*获取集合实例对象*/
Class clazz = Class.forName(className);
Collection coll = (Collection)clazz.newInstance();
return coll;
}
}
----------- [url=http://www.itheima.com]android培训[/url]、[url=http://www.itheima.com]java培训[/url]、java学习型技术博客、期待与您交流! ------------