您的位置:首页 > 职场人生

黑马程序员——java —反射知识总结

2015-04-13 21:17 369 查看
——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-

(一)反射的基础

(1)、什么是反射?

反射就是把java类中的各个成分映射成相应的java类。通过java的Class类的方法来获得其中的方法、变量、构造方法、修饰符、包等等信息。

百度百科解释:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

(2)、反射的基石——Class类

1、什么是java的Class类?

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

2、什么是字节码?

当源程序中用到类时,首先要从硬盘把这个类的那些二进制代码,一个类编译成class放在硬盘上以后,就是一些二进制代码,要把这些二进制代码加载到内存中里面来,再用这些字节码去复制出一个一个对象来。

3、如何获得各个字节码对应的实例对象(Class对象)?

三种方法:

第一种:类名.class 例如:Person.class

第二种:对象.getClass 例如:new Person( ).getClass

第三种:Class.forName(“类名”) 例如:Class.forName(“java.util.Date”)

第三种方法里面有两种情况,一种是类已经加载完成,一种是类还没有加载完成。如果类还没有加载完成,用第三种方法,会自动把类通过虚拟机加载完成。(反射中主要是用第三种方法)

4、有九个预定义的Class实例对象:8个基本类型和void

例子程序:

public class reflectTest{
public static void main (String[] args){
String s = "abc";
Class cls1 = s.class;
Class cls2 = String.class
Class cls3 = Class.forName("java.lang.String);
System.out.println(int.class.isPrimitive());
//判断字节码是不是基础类型
System.out.println(int.class==integer.TYPE);
//Integer.TYPE是Integer类的一个常量,它代表此包装类型包装的基本类型的字节码,所以和int.class是相等的
}
}


注意:

①Class的首字母要大些;

②Class类描述的信息:类的名字,类的访问属性,类所属于的包名,字段名称的列表,方法名称的列表等。

③Class不能通过new来构造对象,每一个字节码就是Class的实例对象。

构造对象的方法:Class a = Person.class

④只要是在源程序中出现的类型都有各自的Class实例对象,如int[].class。数组类型的Class实例对象,可以用Class.isArray()方法判断是否为数组类型的。

(三)构造方法的反射constructor类

(1)、constructor类就是代表某个类的一个构造方法

(2)、怎么得到某个类的构造方法?

①得到某类所有的构造方法

Constructor [ ] constructors =

Class.forName(“java.lang.String”).getconstructors();

②得到某个类的一个构造方法

Constructor constructor =

Class.forName(“java.lang.String”).getconstructor();

(3)、怎么创建实例对象?

①通常方法(非反射时)

例子:String str = new String(new StringBuffer( “abc”));

②用反射的构造方法时

例子:Constructor s = String.class.getConstructor(StringBuffer.class);

String str =(String)s.newInstance(new StringBuffer(“abc”));

注意:获得方法和调用方法要是相同的类型

③注意Class也有newInstance的方法

例如:Class obj = Class.forName(“java.lang.String”).newInstance();

注意:Class的newInstance方法,只能够调用无参的构造器

(四)成员变量的反射Field类

(1)、Field代表某个类中的成员变量

(2)、通过例子知道Field类的方法怎么用:

public class ReflectPoint{
private int x;
public int y;
public ReflectPoint(int x,int y){
Super();
this.x=x;
this.y=y;
}
public static void main(String[] args){
ReflectPoint p1 = new ReflectPoint(3,5);
Field fieldy = p1.getClass().getField("y");
//注意fieldy不是变量上的值,而是指整个类中的变量
System.out.println(fieldy.get(p1));
//注意:要想得到field的值还要传一个具体的对象进去
/*
对私有的成员变量怎么访问它的值
*/
Field fieldx = p1.getClass().getDeclaredField("x");
fieldx.setAccessible(true);
System.out.println(fieldx.get(p1));
//又叫做暴力访问
}
}


(3)总结:

Field getField(String s);//只能获取公有和父类中公有

Field getDeclaredField(String s);//获取该类中任意成员变量,包括私有

setAccessible(ture);

//如果是私有字段,要先将该私有字段进行取消权限检查的能力。也称暴力访问。

set(Object obj, Object value);//将指定对象变量上此Field对象表示的字段设置为指定的新值。

Object get(Object obj);//返回指定对象上Field表示的字段的值。

(4)练习题

将任意一个对象中所有的String类型的成员变量所对应的字符串内容的“b”改成“a”?

package cn.dhj;

import java.lang.reflect.Field;

public class ReflectText {
public String str1 = "ball";
public String str2 = "basketball";
public String str3 = "case";

public static void changeString(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 String toString(){
return str1+":"+str2+":"+str3;

}

public static void main (String[] args) throws Exception{
ReflectText p1 = new ReflectText();
changeString(p1);
System.out.println(p1);

}
}


(五)反射的成员方法Mathod类

(1)、Mathod类代表的是某个类中的一个成员方法

(2)、得到一个类的方法:

例子:Mathod charAt

=Class.forName(“java.lang.String”).getMathod(“charAt”,int.class);

(3)、调用方法

例子:

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

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

注意:如果传给invoke方法的参数是null,Mathod对象对应的是一个静态方法。

例子程序:

System.out.println("--------3. 方法反射-------");
// Method也是属于Class所有
Method charAtMethod = String.class.getMethod("charAt", int.class);
// Method的调用必须属于某个对象:invoke(对象,参数...)
// invoke(null, 参数...)表示调用的静态方法
String s = new String("abc");
System.out.println(charAtMethod.invoke(s, 2));
// 静态调用
TestArguments.main(new String[] { "111", "222", "333" });
// 类名由main(String[] args)方法中的第一个参数指定
if (args.length > 0) {
String startingClassName = args[0];
Method startdMainMethod = Class.forName(startingClassName)
.getMethod("main", String[].class);
// main为静态方法
startdMainMethod.invoke(null, (Object) new String[] { "aaa", "bbb",
"ccc" });
// 或
startdMainMethod.invoke(null, new Object[] { new String[] { "aaa",
"bbb", "ccc" } });
}


(4)、用反射方法执行某个类的main方法

package cn.itheima;
//定义一个测试类
class Test{
public static void main(String[] args){
for(String arg : args){
System.out.println(arg);
}
}
}
//用反射方式根据用户提供的类名,去执行该类中的main方法。
import java.lang.reflect.Method;

public class PerformedMain{

public static void main(String[] args) throws Exception {
//普通方式
Test.main(new String[]{"111","222","333"});
System.out.println("-----------------------------");

//反射方式
String className=args[0];
Method mainMethod=
Class.forName(className).getMethod("main",String[].class);
//方式一:强制转换为超类Object,不用拆包
mainMethod.invoke(null, (Object)new String[]{"111","222","333"});
//方式二:将数组打包,编译器拆包后就是一个String[]类型的整体
mainMethod.invoke(null, new Object[]{new String[]{"111","222","333"}});
}


(六)、数组的反射

(1)、数组反射的基础知识

具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。

/*
举例
*/
int[] a1 = new int[3];
int[] a2 = new int[4];
int[][] a3 = new int[3][4];
String[] a4 = new String[3];
System.out.println(a1.getClass()=a2.getClass());//true
System.out.println(a1.getClass()=a3.getClass());//false
System.out.println(a3.getClass()=a4.getClass());//false
System.out.println(a2.getClass()=a4.getClass());//false
System.out.println(a1.getClass.getName());
//数组字节码的名字:有[和数组对应类型的缩写,如int[]数组的名称为:[I


注意:

①基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。

(2)数组反射的应用

1、小知识点

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

当处理int[]的时候返回的是数组的内容,当处理String[]的时候返回的是数字的名字

② 无法得到某个数组的具体类型,只能得到其中某个元素的类型

例子: Obj[2].getClass().getName() 得到的是数组第三个元素的的类型

2、反射怎么获取数组的值、长度、以及某个位置的值

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

Array.getLength(Object obj);//获取数组的长度

Array.get(Object obj,int x);//获取数组中的元素

(六)、反射的深入

(1)、首先解释一下ArrayList和HashSet的区别

ArrayList:对象有序可以重复

HashSet:对象无序而且不可重复

(2)、Hashcode

①作用:

要想HashCode方法有价值的话,前提是对象存入的是hash算法这种类型的集合当中才有价值。如果不存入是hashCode算法的集合中,则不用复写此方法。

②什么是哈希算法?

若在一个集合中查找是否含有某个对象,通常是一个个的去比较,找到后还要进行equals的比较,对象特别多时,效率很低。有这么一种HashCode算法,有一个集合,把这个集合分成若干个区域,每个存进来的对象,可以算出一个hashCode值,根据算出来的值,就放到相应的区域中去。当要查找某一个对象,只要算出这个对象的hashCode值,看属于第几个区域,然后到相应的区域中去寻找,看是否有与此对象相等的对象。这样查找的性能就提高了。

③老师的总结:(面试经常遇到的问题)

当一个对象存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则对象修改后的哈希值与最初存储进HashSet集合中的哈希值就不同了。在这种情况下,调用contains方法或者remove方法来寻找或者删除这个对象的引用,就会找不到这个对象。从而导致无法从HashSet集合中单独删除当前对象,从而造成内存泄露。(程序中某一些对象不再被使用,以为被删掉了,但是没有,还一直在占用内存中,当这样的对象慢慢增加时,就会造成内存泄露。)

原因:

内存泄露:某些对象不再使用了,占用着内存空间,并未被释放,就会导致内存泄露;也就是说当程序不断增加对象,修改对象,删除对象,日积月累,内存就会用光了,就导致内存溢出。

(3)、反射的作用之——实现框架的功能

①当我们在写程序时无法知道被调用的类名,所以在程序中无法new这个类的实例对象,而要用反射的方法来做。

例子程序:

/*
代码实现,一般步骤:
①将文件读取到读取流中,要写出配置文件的绝对路径。
如:InputStream is=new FileInputStream(“配置文件”);
②用Properties类的load()方法将流中的数据存入集合。
③关闭流:关闭的是读取流,因为流中的数据已经加载进内存。
*/
例子:
public static void main(String[] args){
InputStream ps = new FileInputStream("配置文件");
Propertise props = new Propertise();
props.load(ps);
ps.close();
String className = props.getProperty("className");
collection  collections=Class.forName(className).newInstance();
........
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: