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

——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();
........
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值