黑马程序员——反射

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

     

反射的基础:Class

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

Class和class的区别

class:描述一类事物的共性抽取

Class:类文件共性内容的抽取

获取字节码文件的三种方式:

方式一:Object类中的getClass()方法的,格式:对象.getClass()

想要用这种方式,必须要明确具体的类,并创建对象。

方式二:任何数据类型都具备一个静态的属性.class来获取其对应的Class对象

格式:类名.Class

方式三:只要通过给定的类的字符串名称就可以获取该类,更为扩展

可是用Class类中的方法完成,只要有名称即可,更为方便,扩展性更强

格式: Class.forName(“类名”)

如:Class clazz=Class.forName("包名.Student");//Student是一个类名

如:Class clazz=Class.forName("包名.Student");//Student是一个类名

String str1=”abc”;
Class cls1=str.getClass();
C lass cls2=String.class;
Class cls3=Class.forName(“java.lang.String”);
System.out.println(cls1==cls2);//true
System.out.println(cls1==cls3);//true
 
 

九种预定义的Class

包括八种基本类型(byte、short、int、long、float、double、char、boolean)的字节码对象和一种返回值为void类型的void.class

如:Class cls1=void.class;

isPrimitive():是否是基本类型字节码

String str1="sdfssg";

注意:String不是基本数据类型

System.out.println(str1.getClass().isPrimitive());//false

System.out.println(int.class.isPrimitive());//true

System.out.println(int.class==Integer.class);//false

System.out.println(int.class==Integer.TYPE);//true

Integer.TYPE是Integer类的一个常量,它代表此包装类型包装的基本类型的字节码,所以和int.class是相等的,本数据类型的字节码都可以用与之对应的包装类中TYPE常量表示

数组类型的Class实例对象:int[].class.isArray()

总之,只要是在源程序中出现的类型,都有各自的Class实例对象

反射:就是把Java类中的各种成分映射成为相应的java类

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

Constructor类

得到一个类的所有构造方法:

Constructor[]  cons = Class.forName(“cn.itheima.Person”).getConstructors();

得到一个构造方法:

Constructor  con=Person.class.getConstructor(String.class,int.class);

Constructor constructor=Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);

创建实例对象:

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

反射方式:String str=(String)constructor.newlstance(newStringBuffer(“abc”)):

调用获得的方法时要用到上面相同类型的实例对象

 

成员变量的反射

Field类:Field类代表某个类中一个成员变量

常用方法

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

Field getDeclaredField(String s);获取该类中已声明的成员变量,包括私有

setAccessible(ture);是私有字段,要先将该私有字段进行取消权限检查的能力,称暴力访问

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

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

public class ReflectPoint {   
          private int x;
          public int y;
          public ReflectPoint(int x,int y) {
              super();
              this.x = x;
              this.y = y;
          }
     }

ReflectPoint  pt1 = new ReflectPoint(3,5);

Field  fieldY = pt1.getClass().getField("y");

         // fieldY不是对象身上的变量,而是类上,要用它去取某个对象上对应的值

System.out.println(fieldY.get(pt1));//获取pt1对象上面y的值

FieldfieldX = pt1.getClass().getDeclaredField("x");//获取私有成员变量

fieldX.setAccessible(true);//暴力反射,强制使私有变量可以被访问

System.out.println(fieldX.get(pt1));

        

需求:将一个对象某个值改变。如将ball,basketball,package三个值里面的b换成a

public class StringPoint {
     Stringstr1="ball";
     Stringstr2="basketball";
     Stringstr3="package";
    
     @Override
     //确定toString方法是否写对,写对了则不报错
     public String toString(){
         returnstr1 + "::" +str2 + "::" +str3;
     }
}


import java.lang.reflect.Field;
public class ChangeStringValueTest {
     public static void main(String[] args)throws Exception {
         // TODO Auto-generated method stub
         StringPointsp1=new StringPoint();
         System.out.println("字符串改变之前:"+sp1);
         changeStringValue(sp1,'b','a');
         System.out.println("字符串改变之后:"+sp1);
     }
     private static void changeStringValue(Objectobj,char oldChar,char newChar)throws Exception {
         // TODO Auto-generated method stub
         Field[]fields=obj.getClass().getFields();
         for(Field field : fields){
              //if(field.getType()equals(String.class))
              //用==比equals更好,因为每个字节码文件是唯一的
              if(field.getType()==String.class){
                   StringoldValue=(String)field.get(obj);
                   StringnewValue=oldValue.replace(oldChar, newChar);
                   field.set(obj,newValue);
                  
              }
         }
     }
}

Method类

代表某个类中的一个成员方法

Method  getMethod(String name, Class<?>...parameterTypes) ; 返回一个 Method 对象,它反映此Class对象所表示的类或接口的指定公共成员方法

Method[]  getMethods();返回一个包含某些Method 对象的数组,这些对象反映此Class 对象所表示的类或接口的公共member 方法。

Object  invoke(Object obj, Object... args); 对带有指定参数的指定对象调用由此Method 对象表示的底层方法,如果传递给Method对象的invoke()方法的第一个参数为null,说明Method对象对应的是一个静态方法

Method  getDeclaredMethod(String name, Class<?>...parameterTypes);返回一个Method 对象,反映此Class 对象所表示的类或接口的指定已声明方法。

String str2="asfsdgdfhgf";

Method  methodCharAt=String.class.getMethod("charAt",int.class);

System.out.println(methodCharAt.invoke(str2, 2));

需求:写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法.

分析:在给main方法传递参数时,不能使用代码 mainMethod.invoke(null,newString[](“xxx“),javac只把它当做1.4的语法进行理解,而不把它当做jdk1.5的语法理解,因此会出现参数类型不对的问题。

编译器会做特殊处理,编译时不把参数当做数组看待,也就不会把数组打散成若干参数了。

import java.lang.reflect.Method;
public class ReflectTest1 {
     public static void main(String[] args)throws Exception{
         //TestArguments.main(newString[]{"1","2","3"});一般方法
         StringstartingClassName=args[0];
         MethodmainMethod=
Class.forName(startingClassName).getMethod("main",String[].class);
         //mainMethod.invoke(null, newString[]{"1","2","3"});
         /*mainMethod方法在调用invoke接收一个字符串数组,由于jdk1.5在兼容jdk1.4时,当给一个字符串数组时,会通过自动拆箱功能将数组拆开,每一个元素作为一个一个参数,为了解决这个问题,在打一个包。有两种方式*/
         mainMethod.invoke(null,new Object[]{new String[]{"1","2","3"}});
         mainMethod.invoke(null, (Object)new String[]{"1","2","3"});
     }
}
 
class TestArguments{
     public static void main(String[] args){
         for(String arg:args){
              System.out.println(arg);
         }
     }
}


数组的反射

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

代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class

基本类型的一维数组可以被当做Object类型使用,不能当做Object[]类型使用;

非基本类型的一维数组,既可以当做Object类型使用,又可以当作Object[]类型使用

int[] a1=new int[3];
int[] a2=new int[3];
int[][] a3=new int[3][2];
String[]a4=new String[3];
System.out.println(a1.getClass() == a2.getClass());//true
System.out.println(a1.getClass()== a3.getClass());//false
System.out.println(a1.getClass()== a4.getClass());//false
System.out.println(a1.getClass().getName());//[I表示整数类型的数组
System.out.println(a3.getClass().getName());//[[I
System.out.println(a1.getClass().getSuperclass().getName());//java.lang.Object
System.out.println(a3.getClass().getSuperclass().getName());//java.lang.Object
Object obj1=a1;
Object obj2=a3;
Object[] obj3=a1;//基本类型不是Object,因此不能装换成Object数组
Object[]object4=a3;
Object obj5=a4;
Object[] obj6=a4; 

需求:打印数组内容

int[] b=new int[]{1,2,3};
String[]str=new String[]{"asf","ada","aa"};
System.out.println(b);//[I@75e4fc打印数组哈希值
System.out.println(str);//[Ljava.lang.String;@c62c8
System.out.println(Arrays.asList(b));//[[I@75e4fc]
/*对于整数(基本类型)jdk1.4处理Object类型数组,因此不能不识别整数类型,
jdk1.5处理则等效于一个Object,一个参数*/
System.out.println(Arrays.asList(str));//[asf,ada,aa]

需求:用数组反射打印数组

private static void printObject(Object obj) {
    Class clazz=obj.getClass();
    if(clazz.isArray()){
         int len=Array.getLength(obj);
         for(int x=0;x<len;x++){
              System.out.println(Array.get(obj, x));
         }   
    }
    else
         System.out.println(obj);
}

反射的作用--------->实现框架功能

1、框架与框架要解决的核心问题

我做房子卖给用户住,由用户自己安装门和空调,我做的房子就是框架,用户要使用我的框架,把门窗插入进我提供的框架中。框架与工具类具有区别,工具类被用户的类调用,而框架则是调用用户提供的类

2、框架要解决的核心问题

在写程序时无法知道被调用的类名,所以,在程序中无法直接new某个类的示例对象了,而要用反射方式来做

需求:采用配置文件加反射的方式创建ArrayList和HashSet的实例对象,比较观察运行结果的差异。

注意:当一个对象被存储进hashSet集合中,就不能修改这个对象中的那些参与计算哈希值的字段了。如果修改了,则在remove此对象时,不能删除掉。因为修改那些字段时,对象的哈希值改变了,不是原来的值,容易导致内存泄露。

反射即不要出现具体类的名字,而是从配置文件中读取,通过Class.forName(“”)’

import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Collection;
import java.util.HashSet;
import java.util.Properties;
public class ReflectTest2 {
     public static void main(String[] args)throws Exception {
         //加载Properties文件,面向父类或接口编程
         InputStreaminp=new FileInputStream("config.properties");
         //定义Properties对象
         Propertiespro=new Properties();
         pro.load(inp);
         inp.close();
         StringclassName=pro.getProperty("className");
         Collectioncollection=(Collection)Class.forName(className).newInstance();
         //Collection collections=new HashSet();
         ReflectPointpt1=new ReflectPoint(3, 4);
         ReflectPointpt2=new ReflectPoint(4, 4);
         ReflectPointpt3=new ReflectPoint(3, 4);
         collection.add(pt1);
         collection.add(pt2);
         collection.add(pt3);
         collection.add(pt1);
         System.out.println(collection.size());   
     }
}

当配置文件中的配置为:className = java.util.HashSet 时结果为: 3

当配置文件中的配置为: className = java.util.ArrayList 时结果为: 4

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值