JAVASE_17_反射的深入讲解

1,反射:反射的基石---Class类
-------------java1.2就有的特性
1,反射的基石---Class类,代表一类什么样的事物?
描述java程序中的各个java类
2,Class类的实例对象是什么?
Class clx = 字节码;
Class的实例对象就是某个类的字节码.
Class类中没有构造函数
当内存用到Person.java时,首先要先编译时生成的字节码Person.class加载到内存中,
然后再用类的字节码Person.class复制出多个对象,
而内存中的每一份字节码就是Class类的一个实例对象
3,如何得到各个字节码对应的实例对象?
Class cls = ?//xxx.classs
a:   类名.class
b:    对象.getClass( ),例如:new Person().getClass( );
c:    【反射时用到】Class.forName("类名");例如:Class.forName("java.util.Date");
4,Class.forName(  )的作用?
Class.forName(String className);
调用 forName("X") 将导致命名为 X 的类被初始化
static{   }静态代码块会被执行
Class.forName( )静态方法的目的是为了动态加载类
Class.forName("");的作用是要求JVM查找并加载指定的类,
如果在类中有静态初始化器的话,JVM必然会执行该类的静态代码段
在开发数据库开发时会用到
Class.forName(Sring className);//返回是字符串className代表的类的字节码
str.getClass( );//找到对象str对应的类的字节码
1
2
3
String sx = args[ 0 ]; //com.itcast.day01.TestArguments
System.out.println(Class.forName(sx));  //class com.itcast.day01.TestArguments
System.out.println(sx.getClass());  //class java.lang.String
5,
有九种预定义的 Class 对象,表示八个基本类型和 void
1
2
3
4
5
6
7
8
9
10
11
12
13
14
String str1 =  "abc" ;
Class cls1 = String. class ;
Class cls2 = str1.getClass();
Class cls3 = Class.forName( "java.lang.String" );
System.out.println(cls1==cls2); //true
System.out.println(cls1==cls3); //true
 
System.out.println(cls1.isPrimitive()); //false
System.out.println( int . class .isPrimitive()); //true
System.out.println( int . class  == Integer. class ); //fasle
System.out.println( int . class  == Integer.TYPE); //true
 
System.out.println( int []. class .isPrimitive()); //fasle
System.out.println( int []. class .isArray()); //true,判断是否是一个数组字节码
总之,任何出现在源程序的类型,都有各自的Class实例对象,例如int[ ], void
-----------------------------------------------------------------------------------------------------------
2,反射-----就是把java类中的各种成分映射成相应的java类
---------------
1,反射的价值和作用?
2,构造方法的反射应用--Constructor类
1.得到某个类的所有的构造函数:
Constructor[ ]  cons  =  Class.forName("java.lang.String").getConstructors( ); 
2.得到 某一个构造函数:
1
2
Constructor con1 = Class.forName( "java.lang.String" ).getConstructor(StringBuffer. class );
String str2 = (String)con1. newInstance ( new  StringBuffer( "abc" ));
3.创建实例对象
通常方式:
1
String   str =  new  String( new  StringBuffer( "abc" ));
反射方式:
1
2
Constructor con1 = Class.forName( "java.lang.String" ).getConstructor(StringBuffer. class );
String str2 = (String)con1.newInstance( new  StringBuffer( "abc" ));
4.Class.newInstance(  )方法
反射会导致计算机性能下降
@1:Class---->Constructor----->new object
@2:Class------------------------->new object
例子:
1
String obj = (String)Class.forName( "java.lang.String" ).newInstance();
1...该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象.
2...该方法内部的具体代码-----用到了缓存机制来保存默认构造方法的实例对象
3,成员变量的反射应用
public
private-----暴力反射
默认权限----暴力反射
1
2
3
4
5
6
7
8
9
public  class  ReflectPoint {
     private  int  x; //注意修饰符
     public  int  y; //
     public  ReflectPoint( int  x,  int  y) {
         super ();
         this .x = x;
         this .y = y;
     }
}
1
2
3
4
5
6
7
8
9
10
ReflectPoint pt1 =  new  ReflectPoint( 3 , 4 );
Field fieldY = pt1.getClass().getField( "y" );
//fieldY的值是多少? 是5,错! 
//fieldY不是对象身上的变量,而是类上,要用它去取某个对象上对用的值
System.out.println(fieldY.get(pt1)); //结果4
-----------------------------------------------
Field fieldX = pt1.getClass().getDeclaredField( "x" );
fieldX.setAccessible( true );
System.out.println(fieldX.get(pt1)); //包里反射
4,成员变量的反射的综合案例
字节码要用"=="不用equals!!!
案例需求:将任意一个对象中的所有String类型的成员变量所对用的字符串内容总的"b"改成"a"
1
2
3
4
5
6
7
8
9
10
11
12
private  static  void  changeStringValue(Object obj)  throws  Exception{
     Field[] fields = obj.getClass().getFields();
     for (Field field:fields){
         //if(field.getType().equals(String.class)){
         //为什么用注释?让面试HR看到你用"=="是经过考虑的
         if (field.getType() == String. class ){
             String oldValue = (String)field.get(obj);
             String newValue = oldValue.replace( 'b' , 'a' );
             field.set(obj,newValue);
         }
     }  
}

5.成员方法的反射
1
2
3
//str1.charAt(1),反射做法如下
Method methodCharAt = String. class .getMethod( "charAt" , int . class );
System.out.println(methodCharAt.invoke(str1, 1 ));
注意:如果传递给Method对象的invoke( )方法的第一个参数为null,这有什么意义?
说明该Method对象对应的是一个静态方法

jdk1.4和jdk1.5的invoke方法的区别:
jdk1.5:public Object invoke(Object obj,Object... args)
jdk1.4:public Object invoke(Object obj,Object[ ] args)
1
2
3
4
5
methodCharAt.invoke(str1, new  Object[]{ 1 });
methodCharAt.invoke(str1, new  String[]{ "111" , "222" , "333" });
//JVM会拆箱,认为你给他传递了3个String参数,而不是一个数组参数
methodCharAt.invoke(str1, new  Object( new  String[]{ "111" , "222" , "333" })); //再装箱
methodCharAt.invoke(str1,(Object)new String[]{"111","222","333"});//或者这种方法
6,反射方式执行某个类中的main方法
需求:写一个程序,根据用户提供的类名,去执行该类中的main()方法
解析:
1,用普通方法可以,那为什么用反射方式?
类名.mian(new String[]{"111","222","333"});//普通方式
实际情况是你传一个参数,根本不知道要执行哪一个类
1
2
3
4
//TestArguments.main(new String[]{"111","222","333"});
String startingClassName = args[ 0 ]; //主函数接收的参数,需要在运行时传进去
Method mainMethod = Class.forName(startingClassName).getMethod( "main" ,String[]. class );
mainMethod.invoke( null ,(Object) new  String[]{ "111" , "222" , "333" });
7,数组的反射
1.相同的元素类型,相同的维度属于同一个类型,即具有相同的Class实例对象
1
2
3
4
5
6
7
int [] a1 =  new  int [ 3 ];
int [] a2 =  new  int [ 4 ];
int [][] a3 =  new  int [ 2 ][ 3 ];
String[] a4 =  new  String[ 3 ];
System.out.println(a1.getClass() == a2.getClass()); //true
//System.out.println(a1.getClass() == a4.getClass());//类型不匹配
//System.out.println((Object)a1.getClass() == (Object)a4.getClass());//false,强制转换后
2,数组.getClass( ).getSuperclass( )

3,基本数据类型的一维数组可以被当作Object类型使用,不可当作Object[ ]类型使用
非基本数据类型既可以当作Object类型使用,又可以单做Object[ ]类型使用
1
2
3
4
5
Object obj1 = a1;
//Object[] obj2 = a1;
Object[] obj3 = a3;
Object obj4 = a4;
Object[] obj5 = a4;
4,Arrays.asList( )方法处理int[ ]和String[ ]时的差异
5,Array工具类用于完成堆数组的反射操作

2
3
4
5
6
7
8
9
10
private  static  void  printTest(Object obj) {
     if (obj.getClass().isArray()){
         int  len = Array.getLength(obj);
         for ( int  i= 0 ;i<len;i++){
             System.out.println(Array.get(obj,i));
         }
     } else {
         System.out.println(obj);
     }  
}
6,思考:怎么得到数组的元素类型?
答案:无法得到,
Object[ ] a = new Object[ ]{"abc", 123} ;//无法确定Obejct的类型;
只能确定具体元素的类型



8,ArrayList_HashSet的比较及HashCode分析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
ReflectPoint pt1 =  new  ReflectPoint( 3 , 3 );
ReflectPoint pt2 =  new  ReflectPoint( 5 , 5 );
ReflectPoint pt3 =  new  ReflectPoint( 3 , 3 );
 //pt1和pt3的不同
Collection con1 =  new  ArrayList();
con1.add(pt1);
con1.add(pt2);
con1.add(pt3);
con1.add(pt1);
System.out.println(con1.size());
//4
//复写hashCode方法,结果:4
 
Collection con2 =  new  HashSet();
con2.add(pt1);
con2.add(pt2);
con2.add(pt3);
con1.add(pt1);
System.out.println(con2.size());
//3,set不重复
//复写hashCode方法,结果:2,哈希值只对哈希算法集合有效
1.hachCode( )方法的作用?
(1),为了提高查找元素的效率(HashSet集合)
假如在HashSet中有10000个元素,现在我们往里面插入一个元素,那么我们要遍历10000次逐一比较;
为了提高效率,有人发明了哈希算法来提高从集合中查找元素的效率
算法实现:
集合------->分成若干个存储区域;
每个对象----->计算出一个哈希码;
哈希码分组,-------->每组对应某个存储区域
根据一个对象的哈希码就可以确定该对象应该存储在那个区域
(2).内存泄露问题
-----内存泄露:该内存空间使用完毕之后未回收(没有释放已无法到达的内存)!
当一个对象被储存进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段
否则对象修改后的哈希值与最初不同,导致去HashSet中检索对象,也将返回找不到对象的结果
这将导致无法从HashSet集合中单独删除当前对象,从而造成内存泄露
1
2
3
4
5
6
con2.remove(pt1);
System.out.println(con2.size()); //删除了,大小是1
 
pt1.y =  7 ;
con2.remove(pt1); //运行后,由于哈希值变化,未索引到,没有删除
System.out.println(con2.size()); //大小是2

2,hashCode( )方法存在有价值的前提?
存储的对象必须在哈希算法集合中,如果存储在Lis集合中无任何作用.
也就是说,

3,java中有内存泄露吗?
1,答案见上面
2,输入输出流不close,导致流调用的底层资源没有释放


9,反射的作用---主要用它来做框架
1,先分清楚框架和工具类
先举个例子:我做房子卖给用户主,用户自己安装门和空调;
我做的房子就是框架,,用户需要使用我的框架,把门窗插入我提供的框架中变成自己的房子
框架和工具区别:Tools类被用户调用,框架则调用用户提供的类
2,框架要解决的核心问题
因为写程序时无法知道被调用的类名,
所以,在程序中无法直接new某个类的实例对象,此时用到反射方式来做
3,综合案例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//Collection conllections = new ArrayList();//
//用反射的方式
InputStream ips =  new  FileInputStream( "config.properties" );
Properties props =  new  Properties();
props.load(ips);
ips.close();
String className = props.getProperty( "className" );
Collection conllections = (Collection)Class.forName(className).newInstance();
 
//用反射范式得到对象,可以操作数据
conllections.add(pt1);
conllections.add(pt2);
conllections.add(pt3);
conllections.add(pt1);
System.out.println(conllections.size());
4,Properties类------父类:java.util.HashTable;
--------Properties 类表示了一个持久的属性集。Properties 可保存在流中或从流中加载
 String getProperty(String key) ----用指定的键在此属性列表中搜索属性.
 void load(InputStream inStream) -------从输入流中读取属性列表(键和元素对)
5, 配置文件的问题
第一种:可以读写,而第二种只能读,不能写
new-------->file-------->config.properties
InputStream ips = new FileInputStream("config.properties");
Properties props =  new   Properties();
实际开发中,要亚勇绝对路径.
getRealPath( );//项目"金山词霸"路径-------->/项目内部
一定要记住用完整的路径,但完整的路径不是硬编码,而是运算出来的
第二种:类加载器的方式方式: ---------- 只能读不能写
类加载器可以加载.class文件,那么同时提供了方法加载其他文件
1
2
3
4
5
6
7
8
9
10
11
//用反射的方式
//InputStream ips = new FileInputStream("config.properties");
//用加载器的方式
InputStream ips = 
         ReflectTest2. class .getClassLoader().getResourceAsStream( "com/itcast/day01/config.properties" );
 
Properties props =  new  Properties();
props.load(ips);
ips.close();
String className = props.getProperty( "className" );
Collection conllections = (Collection)Class.forName(className).newInstance();
路径 "com/itcast/day01/config.properties" 不能以" / "开头
1
2
3
4
5
//用加载器的方式
InputStream ips = 
         ReflectTest2. class .getClassLoader().getResourceAsStream( "com/itcast/day01/config.properties" );
InputStream ips = ReflectTest2. class .getResourceAsStream( "config.properties" ); //用工程包里的
InputStream ips = ReflectTest2. class .getResourceAsStream( "resource/config.properties" ); //resource文件夹中的



10,内省(introspector)---主要对javaBean操作
javaBean--->特殊的java类
1
2
3
4
5
6
7
8
9
class  Person{
     private  int  x;
     public  void  getAge(){
         return  x;
     }
     public  int  setAge( int  age){
         this .x = age;
     }
}
 我们队类中的成员变量是看不到的,从方法上我们知道是对age操作,不是对"x"

如果要在两个模块之间快递多个信息,可以将这些信息分装到一个javaBean中,
这种javaBean的实例对象通常称之谓值对象(Value Object,简称OV)
java EE开发中经常使用到javaBean

JDK中提供了对javaBean进行操作的一些API----这套API就称为内省

11,javaBean的简单内省操作
1,把一个类变成javaBean.---->私有属性添加get和set方法.
2.读取javaBean对象的属性 
3,java.beans.PropertyDescriptor 类( 属性描述符),通过属性该属性的描述
4,用属性描述符得到get方法
5,得到的Method对象invoke
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public  class  IntroSpector {
     public  static  void  main(String[] args)  throws  Exception{
         ReflectPoint pt1 =  new  ReflectPoint( 3 , 4 );
         //想获取对象中的某个属性
         String propertyName =  "x" ; //面试题
         //普通做法:"x"--->"X"---"getX"-----"MethodGetX"
       //现在用内省方法
         PropertyDescriptor pd = 
                 new  PropertyDescriptor(propertyName,pt1.getClass());
         Method methodGetX = pd.getReadMethod();
         Object retVal = methodGetX.invoke(pt1);
         System.out.println(retVal);
         
         Object value =  7 ; //装箱
         Method methodSetX = pd.getWriteMethod();
         methodSetX.invoke(pt1,value);
         System.out.println(pt1.getX());
     }
}
这段代码的技巧:
变量的取值:
1,方法参数的起名字就叫那个值Class.forName(String className)
2,调用方法返回值Object  retVal--------return  value之意
3,eclispe可以吧上面代码抽取(Extract)成方法:
1
2
3
4
5
6
7
8
private  static  Object getProperty(Object pt1, String propertyName)
         throws  Exception {
     PropertyDescriptor pd = 
             new  PropertyDescriptor(propertyName,pt1.getClass());
     Method methodGetX = pd.getReadMethod();
     Object retVal = methodGetX.invoke(pt1);
     return  retVal;
}
还有一种用BeanInfo方法实现的----稍微复杂的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private  static  Object getProperty(Object pt1, String propertyName)
         throws  Exception {
     /*PropertyDescriptor pd = 
             new PropertyDescriptor(propertyName,pt1.getClass());
     Method methodGetX = pd.getReadMethod();
     Object retVal = methodGetX.invoke(pt1);*/
     BeanInfo beanInfo = Introspector.getBeanInfo(pt1.getClass());
     PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
     Object retVal =  null ;
     for (PropertyDescriptor pd : pds){
         if (pd.getName().equals(propertyName)){
             Method methodGetX = pd.getReadMethod();
             retVal = methodGetX.invoke(pt1);
             break ;
         }
     }
     return  retVal;
}



12,BeanUtils工具包
BeanUtils工具操作javaBean.
Apache开发的

在用到javaBean.jar包的时候要导入logjar包

1,不确定....................................?jdk新特自己测试
1
2
3
//JDK新特性
Map map = {name: "zxx" ,age: 12 };
BeanUtils.setProperty(map, "name" , "luj" );

2,BeanUtils和PropertyUtils

commons.beanutils.BeanUtils
commons.beanutils.PropertyUtils
1
2
3
4
5
6
7
8
BeanUtils.setProperty(pt1, "x" , "9" );
System.out.println(BeanUtils.getProperty(pt1,  "x" ).getClass());;
PropertyUtils.setProperty(pt1, "x" , 9 );
System.out.println(PropertyUtils.getProperty(pt1,  "x" ).getClass());;
//7
//9
//class java.lang.String
//class java.lang.Integer
3,BeanUtils中的方法
void   copyProperties(Object dest, Object orig)

Map<String,String> describe(Object bean);//  beab--->map
populate(Object bean, Map<String,? extends Object> properties)//  Map-------->.bean
1
2
3
//JDK新特性
Map map = {name: "zxx" ,age: 12 };
BeanUtils.setProperty(map, "name" , "luj" );

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值