黑马程序员——Java高新技术---反射

——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-

一.反射

做框架的时候会用到反射,如果不做框架就用不到。学习这一部分的时候感觉特别有意思,所以学起来比较轻松,记住怎么获取字节码对象(三种方法)、成员变量、成员方法、构造函数。接下来你想啊,你获取了这些肯定是要使用的,你再记住怎么使用的,比如、实例化对象、修改成员变量值、执行成员方法等,使用时有一些注意事项,比如暴力方式。OK,这些东西在头脑中必须很清晰。

1.概述

Java类用于描述一类事物的共性,其类有什么属性,不同的实例对象有不同的属性值。就是把java类中的各种成分映射成相应的java类。
Class:表示的是各个java类是否属于同一类事物。描述的是:类的名字,类的访问属性,类所属于的包名,字段名称的列表,方法名称的列表。
比较记忆:
Person类代表人,他的实例是:张三,李四。。等具体的人,
Class类代表java类,他的实例对象对应的是:
对象各个类在内存中的字节码,例如:Person类的字节码,ArrayList类的字节码
一个类被加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以他们在内存中的内容是不同的,这一个个的空间分别用一个个的对象来表示。

2.基本方法

格式:
格式1:类名.class;
格式2:对象.getClass()
格式3:Class.forName(“包名.类名”);
九个预定义对象 基本数据类型(boolean、byte、char、short、int、long、float 和double。)和void
只有这写基本类型的包装类可以通过包装类.TYPE获得所包装的基本类型的Class对象。
例:

public class reflectDemo {  
  public static void main(String[] args) throws Exception {  
    String str = "hello";  
    Class strClass= String.class;// 类名.class  
    Class strClass1 = str.getClass();// 对象.getClass  
    Class strClass2 = Class.forName("java.lang.String");// 使用Class.forName("包名.类名")  
    System.out.println("String的字节码:" + strClass);  
    System.out.println(strClass == strClass1);// true  
    System.out.println(strClass == strClass2);// true  
    // 从而可以证明,String在内存的字节码是唯一的  
    /* 
     * boolean isPrimitive()判断Class对象是否是表示一个基本类型 
     */  
    System.out.println("String是否是基本类型:" + strClass.isPrimitive());  
    System.out.println("int是否是基本类型:" + int.class.isPrimitive());  
    /* 包装类通过包装类.TYPE获得所封装的基本类型的Class字节码对象 */  
    System.out.println("Integer.TYPE是否是基本类型:" + Integer.TYPE.isPrimitive());  
    /* 
     * public boolean isArray()判定此 Class 对象是否表示一个数组类。 
     */  
    System.out.println("int []是否是数组类型:" + int[].class.isArray());  

  }  

}  
结果:  
String的字节码:class java.lang.String  
true  
true  
String是否是基本类型:false  
int是否是基本类型:true  
Integer.TYPE是否是基本类型:true  
int []是否是数组类型:true  

3.构造函数(反射)

通过Constructor类来获得构造函数,然后获得对象。
也可以通过Class中的newInstance方法获得无参的构造函数的对象。

    import java.lang.reflect.Constructor;

public class reflectDemo {
  public static void main(String[] args) throws Exception {
    Class strClass = String.class;// 类名.class
    /*
     * 使用Constructor类获得构造方法,调用Class的getConstructor(Class...para)方法,
     * 调用此方法的时候,参数是是Class,通过参数的类型指定获得那一个构造方法。
     * newInstance(和指定参数类型的那个一样的对象)来获得对象
     */
    Constructor con=strClass.getConstructor(StringBuffer.class);
          String str=(String) con.newInstance(new StringBuffer("abc"));
          System.out.println(str);
          /**
           * 也可以通过Class的newInstance()获得无参的构造方法的对象
           */
          String str1=(String)strClass.newInstance();
          str1="bdc";
          System.out.println(str1);
  }

}
结果:
abc
bdc

用获得的构造方法创建实例,此时创建的时候的参数和你获得构造方法时的参数类型要一样

4.字段(Filed)反射

1.字段反射

import java.lang.reflect.Field;  
class Person{  
  public String name;  
  private int age;  
  public Person(String name,int age){  
    this.name=name;  
    this.age=age;  
  }  
}  
public class FiledDemo {  
  public static void main(String[] args) throws Exception {  
    Class pC=Person.class;//获得Person类的字节码  
    Person p=new Person("abc",23);  
    /** 
     * 获得公共字段(getField),这只是获得字段对象,不含有值,要是获得某个对象的值 
     * 那么需要get(对象)来获得 
     */  
    Field fieldName=pC.getField("name");//获得字段  
    String name=(String)fieldName.get(p);//获得对象中字段的值  
    System.out.println("name="+name);  
    /** 
     * 获得私有字段(getDeclaredField()),这里也只是获得只是简单的字段,不包含值 
     * setAccessible()设置可操作 
     */  
    Field filedAge=pC.getDeclaredField("age");  
    filedAge.setAccessible(true);//可操作  
     Objectage=filedAge.get(p);//获得此对象的值  
    System.out.println("age="+age);  
  }  

}  
结果:  
name=abc  
age=23  

2.更改字段的值
将对象中的字段是String类型的,那么就取对象的值,将值中的字符b更改为字符0,然后输出

import java.lang.reflect.Field;  


class Person{  
    public String name;  
    private int age;  
    public Person(String name,int age){  
        this.name=name;  
        this.age=age;  
    }  
    public String toString(){  
        return name+":"+age;  
    }  
}  
public class FiledDemo {  
    public static void main(String[] args) throws Exception {  
        Class pC=Person.class;//获得Person类的字节码  
        Person p=new Person("abc",23);  
        /** 
         * getFields()获得所有字段 
         */  
        Field [] fileds=pC.getFields();  
        for(Field f :fileds){//遍历  
            if(String.class==f.getType()){//判断f的类型是否是String类型  
                String oldValue=(String)f.get(p);  
                String newValue=oldValue.replace('b', '0');//将字段上的值的b替换成0  
                f.set(p, newValue);//重新传给对象  

            }  
        }  
        System.out.println(p);  
    }  

}  
结果:  
a0c:23  

5.方法反射(Method)

1.普通方法反射

import java.lang.reflect.Method;  
public class MethodDemo {  
  public static void main(String[] args)throws Exception {  
    /** 
     * 通过getMethod(方法名,参数类型)获得方法对象 
     * invoke(对象,参数)来调用获得的方法, 
     */  
    String str="abcd";  
    Method methodcharAt=String.class.getMethod("charAt",int.class);  
    System.out.println(methodcharAt.invoke(str, 3));  
    System.out.println(methodcharAt.invoke("defh", 3));  

  }  

}  

2.静态方法反射

import java.lang.reflect.Method;  

public class MethodDemo {  
  public static void main(String[] args)throws Exception {  
    /** 
     * 如果invoke(null,参数),那么就表示调用此方法的方法是静态方法 
     */  

    Method methosMax=Math.class.getMethod("max", int.class,int.class);  
    System.out.println(methosMax.invoke(null, 23, 57));  
  }  

}  

3.主函数(main)反射

import java.lang.reflect.Method;  

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

    Method mianMethod = Class.forName("wang.fuxi.jiaqiang.text").getMethod(  
         "main", String[].class);  
    mianMethod.invoke(null, new Object[] { new String[] { "123", "456",  
         "789" } });  
  }  

}  

class text {  
  public static void main(String[] agrs) {  
    System.out.println(agrs.length);  
  }  
}  
结果:  
3  

注意:在调用主函数的时候,给主函数传数组参数的时候,程序会先拆包,然后取里面的参数,所以我们要先把数组封装起来,否则,报异常,

 在运行主调类的主函数的时候,也可以在在类中右击—run configurations----arguments----然后把被调用类的位置传进去(包名.类名),然后调用的时候就可以使用       Method mianMethod =Class.forName(argus[0]).getMethod("main", String[].class);来获得被调类的主函数。

传参的方式:
invoke(null,new Object[] { new String[] {“123”, “456”, “789” } });
或者:
invoke(null, (Object)(new String[] {“123”, “456”,”789” } ));

6.数组反射

数组类型相同并且维数也相同,那么他们的Class字节码对象才相等,还有对于AsList(T…. ts)方法来说,在1.5之后,那么int数组把其当作一个参数来处理,如果是String数组,那么就分来作为参数。
1.基本判断

import java.util.Arrays;  

public class SuZuDemo {  
  public static void main(String[] args) {  
    int[] a = new int[3];  
    int[] b = new int[4];  
    int[][] c = new int[3][4];  
    String[] s1 = new String[3];  
    System.out.println(a.getClass() == b.getClass());// true  
    //System.out.println(a.getClass()==c.getClass());//编译失败  
    //System.out.println(a.getClass()==s1.getClass());//编译失败  
    /** 
     * 获得父类的字节码,从结果知道,他们的父类的是Object 
     */  
    System.out.println(a.getClass().getSuperclass().getName());  
    System.out.println(s1.getClass().getSuperclass().getName());  
    int[] a1 = new int[] { 1, 2, 3 };  
    String[] s2 = new String[] { "a", "b", "c" };  
    System.out.println(Arrays.asList(a1));// 在1.5后,此此方法会把整形数组当作一个参数处理  
    System.out.println(Arrays.asList(s2));// 会把String数组拆分,分别作为参数来处理  
  }  

}  
结果:  
true  
java.lang.Object  
java.lang.Object  
[[I@1f66cff]  
[a, b, c]  

2,反射数组
使用Array类。

import java.lang.reflect.Array;  
import java.util.Arrays;  

public class SuZuDemo {  
  public static void main(String[] args) throws Exception {  
    String[] s = { "abc", "def", "hig" };  
    String ss = "hello";  
    print(s);  
    print(ss);  
  }  

  public static void print(Object ob) {  
    Class classzz = ob.getClass();// 反射  
    if (classzz.isArray()) {// 判断的是否是数组  
      int len = Array.getLength(ob);// 获取长度  
      for (int i = 0; i < len; i++) {  
         System.out.println(Array.get(ob, i));// 获得数组中的元素  
      }  
    } else {  
      System.out.println(ob);  
    }  
  }  

}  
结果:  
abc  
def  
hig  
hello  

二. 反射的应用

1. HashCode()方法和HashSet的联系
HashSet集合中,要是存放了对象,那么就不要修改在对象中的字段的值,否则会出现内存泄漏,溢出,只要一修改,那么哈希值会改变,就不会在那个区域找到此对象。
HashCode(),是为了比较对象,然后就是把不相同的对象存储到集合中,在集合中存放早集合中的对象,不要修改,否则会出现内存泄漏。只用于Set集合中,在List集合中没有起到作用。

import java.util.ArrayList;  
import java.util.Collection;  
import java.util.HashSet;  

public class SetDemo {  
  public static void main(String[] args) {  
    Collection con = new HashSet();  
    TextDemo t1 = new TextDemo("zhangsan", 12);  
    TextDemo t2 = new TextDemo("lisi", 12);  

    con.add(t1);  
    con.add(t2);  
    con.add(t2);  
    //t1.age = 23;// 更改了字段的值,那么hashCode值也变了,那么再移除修改的对象,那么就会找不到  
    t1.setAge(22);//这样修改了,那么也会找不到原来的元素  
    System.out.println(con.remove(t1));//fasle  
    System.out.println(con.size());  
  }  

}  

class TextDemo {  
  String name;  
  int age;  

  public TextDemo(String name, int age) {  
    this.name = name;  
    this.age = age;  
  }  

  @Override  
  public int hashCode() {  
    final int prime = 31;  
    int result = 1;  
    result = prime * result + age;  
    result = prime * result + ((name == null) ? 0 : name.hashCode());  
    return result;  
  }  

  @Override  
  public boolean equals(Object obj) {  
    if (this == obj)  
      return true;  
    if (obj == null)  
      return false;  
    if (getClass() != obj.getClass())  
      return false;  
    TextDemo other = (TextDemo) obj;  
    if (age != other.age)  
      return false;  
    if (name == null) {  
      if (other.name != null)  
         return false;  
    } else if (!name.equals(other.name))  
      return false;  
    return true;  
  }  

  public void setAge(int age) {  
    this.age = age;  
  }  
}  

2. 框架案例

public static void main(String[] args) throws Exception {  
    InputStream in=new FileInputStream("nane.properties");  
        Properties pro=new Properties();  
        pro.load(in);  
        in.close();  
        String className=pro.getProperty("className");  
        Class c=Class.forName(className);  
        Collection con=(Collection)c.newInstance();  
        con.add(newPerson(1,2));  
        con.add(newPerson(4,2));  
        con.add(newPerson(3,2));  
        con.add(newPerson(1,2));  
        System.out.println("集合长度:"+con.size());  
  }
  结果:  
     集合长度:3 

也可以打开配置文件,然后进行更改里面的值,那么结果就会跟着改变。
className=java.util.ArrayList

public static void main(String[] args) throws Exception {  
    InputStream in=new FileInputStream("nane.properties");  
        Properties pro=new Properties();  
        pro.load(in);  
        in.close();  
        String className=pro.getProperty("className");  
        Class c=Class.forName(className);  
        Collection con=(Collection)c.newInstance();  
        con.add(new Person(1,2));  
        con.add(new Person(4,2));  
        con.add(new Person(3,2));  
        con.add(new Person(1,2));  
        System.out.println("集合长度:"+con.size());  

  }
  结果:  
   集合长度:4  

3. 加载资源的位置
1.可是使用绝对路径,但是那么就使用getRealPath,然后进行存数,
2.使用加载器
加载器可以加载java文件,那么也可以加载配置文件,但是有一点缺点,就是配置文件只能读取不能修改,可以使用相对路径,那么最前面要加”/”,否则就不用加”/”,表示的是根目录。
InputStream in=Text.class.getClassLoader().getResourceAsStream(“/包名/nane.properties”);
Text是运行类的类名,nane.properties是配置的文件
——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值