java基础加强02 反射

反射

一、反射

1、反射的描述

程序集包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。您可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。

       2、reflect包

在java中,包lang.reflect 用于描述反射。java类的反射对象包括Field、Constructor、Method、Packet

二、Class类:

       1、Class类概述

(1)Class类的描述

Class 类的实例表示正在运行的 Java 应用程序中的类和接口(枚举是一种类,注释是一种接口)Class 没有公共构造方法,Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。

(2)、Class对象是类的字节码

                     普通类对象:Person p1=new Person()  Person P2=new Person();

Class实例对象:Class cls1 = Person的字节码  ; Class cls2 = Person的字节

(3)、数组的Class对象

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

数的数组都共享该 Class 对象。

       2、Class实例化

              (1)、获取Class对象

对于内存中已经加载的类,直接找到字节码将他返回即可。可以通过类名.class,或者是getClass()方法。

对于jvm还未将其加载到内存中类,可以使用Class类中的静态方法forName() 。

(2)、方法的举例与说明

a、.class

示例:Class clsPer = Person.class;

说明:内存中加载Person,产生了Person类的字节码,这个字节码就是Class的实例对象

bgetClass( )

示例:Class claP1 = p1.getClass()

说明:Person类的字节码实例化得到p1,p1可以逆向得到Person的字节码。使用Object类中的getClass()方法。

cforName()

示例:Class.forName("java.lang.String");

说明:jvm还没有加载字节码,这种情况使用Class类中的forName()方法。这个方法会用类加载器去加载指定的类,加载后,将字节码缓存并返回。使用反射,主要使用这种方法,因为可以将forName()中的字符串参数设置成一个变量,这样写源程序的时候不用知道需要反射的类的名字。运行时通过主函数将类名作为参数传入。

      

3九个预定义的Class对象

(1)九个预定义的Class对象,(八种基本数据类型和void)

基本的Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也表示为 Class 对象(void.class)。  

(2)、基本数据类型的字节码 —— TYPE关键字

int.class==Integer.class——>false 。基本数据类型和它的包装类的字节码对象是不同的类型        

int.class==Integer.TYPE——>true  在基本数据类型的包装类中,定义了属性TYPE,用于表示被包装的数据类型的Class实例。

(3)、判断否为基本数据类型的字节码

使用Class类中的isPrimitive()方法,调用时这样写cls1.isPrimitive();

(4)判断数组类型的Class对象用的是isArray()方法;

 

三、Class实例对象中各成分的反射

1、Constructor类:构造方法的反射应用

              (1)得到某个类所有的构造方法

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

              (2)得到某个类特定的构造方法

                     Constructor constructor = Class.forName(

java.lang.String).getConstructor(StringBuffer.class,int.class)

                     getConstructor(Class<?>... parameterTypes)//这里使用了可变参数的特性。

(3)newInstance(Object... initargs) ,通过字节码实例化对象。

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

反射方式:

Constructor<String> constructor =

              Class.forName(java.lang.String).getConstructor(StringBuffer.class)

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

//这里通过String的构造方法得到字串abc

2、Field类:成员变量反射

(1)通过反射获取成员变量的值

import java.lang.reflect.Field;

//get(“对象”)

class Student{

    public String name;

    private int age;

    Student(String name,int age){

        this.name = name ;

        this.age = age ;

    }

}

class FieldTest{

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

        Student s = new Student("Mike",17);

        //使用get("变量名")方法获取字节码对象的字段

        Field fieldName = s.getClass().getField("name");

        // fieldName是类上的变量,而不是对象上的变量

            System.out.println(fieldName.get(s/*参数是要获得其值的对象*/));   

        //暴力反射,获取私有属性值

        //或去通过变量名获取字节码对象中的字段。getDeclaredField("变量名")

        Field fieldInt = s.getClass().getDeclaredField("age");

        //将字段私有设为可以访问,用setAccessible(true)方法

        fieldInt.setAccessible(true);   

        System.out.println(fieldInt.get(s));

    }

}      

(2)通过Class对象改变属性值

                     agetType()——>得到Field对象的类型。

bset(对象,要替换的内容)——>设置Field对象的

                     c、示例代码

import java.lang.reflect.Field;

class ForTest {

    //这里必须要用public修饰,反射才能被访问。

public String s1 = "abcdefffff";

    public String s2 = "abcdegfffggg";

    public String toString(){

        return s1+"    "+s2;

    }

}

class Demo{

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

        ForTest ft = new ForTest();

        //取得字节码对象的字段属性用getFields()方法

        Field[] fields = ft.getClass().getFields() ;

        System.out.println(fields.length);

        System.out.println("ft1:"+ft.toString());

        for (Field field : fields) {

            if (field.getType() == String.class) {

                //取得字节码对象中字段属性的值用get(对象)方法

                String oldStr = (String) field.get(ft);

                //改变值用replace方法//这里会将所有的f换成b

                String newStr = oldStr.replace('f','b');   

                //重新设置值用set方法

                field.set(ft, newStr);       

            }

        }

        System.out.println("ft2:"+ft.toString());

    }

}

3成员方法的反射

(1)概念

a、方法属于类,而不属于对象。

b、专家模式:谁拥有数据,就具有操作这些数据的方法。

(2)getMethod(charAt,parameterTypes)——>invoke(对象,方法的参数)

a、非静态方法:

Method methodCharAt = String.class.getMethod

(charAt,parameterTypes)

                       methodCharAt.invoke(str,1)      

b静态方法

调用静态方法invoke(null,方法的参数)。静态方法不用建立对象,所以参数对象为null。

c、new Object[]{2},object数组中装了一个Integer类型的对象

 

4、对接收数组参数的成员方法进行反射

(1)、普通方式调用main方法:TestArguments.main(new String[] {}) ;

(2)反射的方式:

a为什么要用反射的方法调用main方法有些时候我们并不知道main方法的所属类,这时就没有办法用类名调用

b、main方法的参数是String[]。jdk中有对数组自动拆包的功能,会将String[]拆包,那么要想在main方法中传入String[],就要使用强制转换,将String[]转成Object,或者将其双重打包new Object[]{new String(1111 , 2222,3333)}

(3)、示例:

class test{

pulbic static void main(String [] args){

String argsFirst = arg[0] ;

Method mainMethod =

Class.forName(argsFirst).getMethod(main,String[].class);

mainMethod.invoke(

null , new Object[](new String[]{1111 2222 3333})) ;

              }

}

class demo{

         public static void main(String[] args){

for(String str : args){

so.p(str);

}

}

}

调用:调用在运行时使用主函数传参的方式传入参数。

五、数组与Object的关系及其反射

1、相同元素,相同维度的数组共性一个Class对象。Object[] 是Object的子类

2、数组字节码的名称,[I :[ ——>表示数组,I——>表示数组中的元素是int型。

Arrays中的list方法参数是Object[]  String属于Object而基本数据类型不属于,所有可以将字符串数组转换为list集合  ,而整数会得到一个内存地址值

六、HashSet与hashCode方法

1、Hash表数据结构存储时的特点

       分段存储:根据hashcode分段存储

2、Hash表数据结结构取出的特点

       (1)改变属性值之后无法取出:例如

       (2)内存泄漏——>内存溢出:对象不用了,但仍然占用内存空间,就叫内存泄漏

       (3)

class point {

int x ;

int y ;

point(int x,int y){

       this.x = x;

       this.y = y;

}

}

class test{

       public static void main(String[] args){

Collection c = new HashSet();

Point p1 = new Point (3,5) ;

Point p2 = new Point (3,3) ;

c.add(p1) ;

c.add(p2) ;

       p1.x=7 ;

//这时,p1的hashcode改变了

       c.remove(p1);

      

/*这里并不能原来的p1从集合中取出。造成了内存泄露。内存泄漏达到一定程度,就会造成内存溢出。

*/

}

}

七、反射在框架中的应用

       (1)、Properties是HashMap的子类,Properties集合中键和值成对存在。但比HashMap多了将文件由硬盘加载和写入(load())的功能

       (2)、配置文件的设置:选中工程——>new——>File——>config.properties ——>配置文件内容

       className = java.util.ArrayList//等号后面的内容是可以改变的

      

       加载配置文件

       InputStream ips = new FileInputStream(config.properties) ;

       Properties props = new Properties() ;

Props.load(ips) ;

ips.close();

        String className = props.getProperty(className) ;——>得到文件类型

       利用反射创建对象

Collection collections =

(Collection)Class.forName(

className/*文件类型java.util.ArrayList */).newInstance() ;

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

工程做完后我们往往是把class文件打成jar包,交给客户。而不是源文件。对于配置文件,我们在开发时不能用相对路径(相对目录是相对当前目录)。

(1)、配置资源文件的绝对路径

对于文件的路径,一般要客户进行配置,然后我们的设置会读取到客户配置的路径。在javaWeb中API中,有一个getRealPath()方法,可以得到安装路径,通过安装路径使配置文件的路径得到补充。getRealPath() ; //金山词霸/内部路径。一定要记住使用完整的路径,但完整的路径不是硬编码,而是运算出来的。

(2)、加载资源文件

a、使用类加载器加载资源文件

类名.class.getClassLoader().getResourceAsStream(包路径/文件名);//类加载器会到客户目录下加载.class文件,也可以加载指定的其他文件

注意:Eclipse 会编译java源文件夹下的所有.java文件(保存后编译),并将源文件目录下的其他格式文件原封不动复制到客户目录

b、使用Class类内部方法加载资源

类名.class.getResourceAsStream(文件名);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值