thinking in java学习笔记:14章 类型信息

14.2 Class 对象

https://github.com/zhaojiatao/javase

1、什么是Class对象,Class对象是用来做什么的?

Class对象是java程序用来创建类的所有常规对象用的;每个类都有一个Class对象;

2、Class对象是如何创建的?

当程序创建第一个对类的静态成员(static修饰的成员以及构造方法)的引用时,就会加载这个类。类加载器首先检查这个类的

Class对象是否已经加载;如果尚未加载,默认的类加载器就会查找.class文件。在加载字节码后会执行安全验证,之后会根

据字节码在内存中创建这个类的Class对象;

3、除了jvm的类加载器会获取某个类的Class对象之外,我们自己如何获取某个类的Class对象的引用?

这个例子会介绍第一种方法:使用Class类的forName()方法,获取类的Class对象的引用;这个方法的副作用是,如果jvm

还未加载这个类的Class对象的话就加载这个类;在类加载的过程中,会初始化static成员;

注意,传递给forName方法的名字,必须是全限定名;

此外,还可以使用类字面常量的方法来生成对类Class对象的引用;

 1 package thinkingInJava.chapter_14_classInfo;
 2 
 3 
 4 /**
 5  * @author zhaojiatao
 6  * @date 2018/9/9
 7  *
 8  * 1、什么是Class对象,Class对象是用来做什么的?
 9  *   Class对象是java程序用来创建类的所有常规对象用的;每个类都有一个Class对象;
10  * 2、Class对象是如何创建的?
11  *   当程序创建第一个对类的静态成员(static修饰的成员以及构造方法)的引用时,就会加载这个类。类加载器首先检查这个类的
12  *   Class对象是否已经加载;如果尚未加载,默认的类加载器就会查找.class文件。在加载字节码后会执行安全验证,之后会根
13  *   据字节码在内存中创建这个类的Class对象;
14  * 3、除了jvm的类加载器会获取某个类的Class对象之外,我们自己如何获取某个类的Class对象的引用?
15  *    这个例子会介绍第一种方法:使用Class类的forName()方法,获取类的Class对象的引用;这个方法的副作用是,如果jvm
16  *    还又有加载这个类的Class对象的话就加载这个类;在类加载的过程中,会初始化static成员;
17  *    注意,传递给forName方法的名字,必须是全限定名;
18  *    此外,还可以使用类字面常量的方法来生成对类Class对象的引用;
19  *
20  *
21  */
22 interface HasBatteries{}
23 
24 interface Waterproof{}
25 
26 interface Shoots{}
27 
28 class Toy{
29     Toy(){}
30     Toy(int i){}
31 }
32 
33 class FancyToy extends Toy implements HasBatteries,Waterproof,Shoots{
34     FancyToy(){
35         super(1);
36     }
37 }
38 
39 public class Test2 {
40 
41     //通过Class对象,可以得到Class对象的多有信息
42     static void printInfo(Class cc){
43         System.out.println("Class name:"+cc.getName()+"is interface?["+cc.isInterface()+"]");
44         System.out.println("Simple name:"+cc.getSimpleName());
45         System.out.println("Canonical name:"+cc.getCanonicalName());
46     }
47 
48 
49     public static void main(String[] args) {
50         Class c=null;
51 
52         try{
53             //注意,必须使用全限定名
54             c=Class.forName("thinkingInJava.chapter_14_classInfo.FancyToy");
55         }catch (ClassNotFoundException e){
56             System.out.println("Can't find Test2");
57             System.exit(1);
58         }
59 
60         printInfo(c);
61 
62         for(Class face:c.getInterfaces()){
63             printInfo(face);
64         }
65 
66         Class up=c.getSuperclass();
67         Object obj=null;
68         try{
69             //使用newInstance来创建的类,必须带有默认构造器
70             obj=up.newInstance();
71         }catch(InstantiationException e){
72             System.out.println("Cannot instantiate");
73             System.exit(1);
74         }catch (IllegalAccessException e){
75             System.out.println("Cannot access");
76             System.exit(1);
77         }
78         printInfo(obj.getClass());
79 
80         System.out.println(obj instanceof Toy);
81 
82     }
83 
84 
85 }
View Code

 

14.2.1 字面类常量

除了使用class的forName方法获取类的Class对象的引用外,还可以使用类字面常量;

这种方式不仅简单,且更安全,因为会在类编译期就进行安全检查

注意,使用.class的方式,获取类的Class对象的引用,不会像forName()那样初始化类的Class对象

.class的方式仅仅是获取了Class对象的引用,只有到了类真正被初始化结束后,Class对象才真正被初始化;即:

只有在对静态方法或非常数静态域进行首次引用时才执行初始化;

 1 package thinkingInJava.chapter_14_classInfo;
 2 
 3 import java.util.Random;
 4 
 5 /**
 6  * @author zhaojiatao
 7  * @date 2018/9/10
 8  *
 9  * 除了使用class的forName方法获取类的Class对象的引用外,还可以使用类字面常量;
10  * 这种方式不仅简单,且更安全,因为会在类编译期就进行安全检查;
11  * 注意,使用.class的方式,获取类的Class对象的引用,不会像forName()那样初始化类的Class对象;
12  * .class的方式仅仅是获取了Class对象的引用,只有到了类真正被初始化结束后,Class对象才真正被初始化;即:
13  * 只有在对静态方法或非常数静态域进行首次引用时才执行初始化;
14  *
15  */
16 public class Test2_1 {
17     public static Random rand=new Random(47);
18 
19     public static void main(String[] args) throws ClassNotFoundException {
20         Class initable=Initable.class;
21         System.out.println("这个时候仅仅是获取到了Initable类的Class对象引用,Class对象还没初始化");
22         //对于static final值是编译期常量,则该值无需对类初始化就可以被读取;
23         System.out.println(Initable.staticFinal);
24         //非编译期常量,即使被static和final修饰,也必须先初始化类Class对象,才能读取;
25         System.out.println(Initable.staticFinal2);
26 
27         //如果一个static域不是final的,那么在对它访问时,总是要求在它被读取之前,先进行链接(为这个域分配存储空间)
28         //以及初始化(初始化该存储空间)
29         System.out.println(Initable2.staticNonFinal);
30 
31         //使用forName方法,就会初始化类
32         Class initable3=Class.forName("thinkingInJava.chapter_14_classInfo.Initable3");
33         System.out.println(Initable3.staticNonFinal);
34 
35 
36 
37     }
38 
39 }
40 
41 
42 class Initable{
43     static final int staticFinal=47;
44     static final int staticFinal2=(int)(1+Math.random()*(10-1+1));
45     static {
46         System.out.println("Initializing Initable");
47     }
48 }
49 
50 class Initable2{
51     static int staticNonFinal=147;
52     static {
53         System.out.println("Initializing Initable2");
54     }
55 }
56 
57 class Initable3{
58     static int staticNonFinal=74;
59     static{
60         System.out.println("Initializing Initable3");
61     }
62 }
View Code

  

14.2.2 泛化

学习范型在Class引用的使用过程的应用

  1 package thinkingInJava.chapter_14_classInfo;
  2 
  3 import org.junit.Test;
  4 
  5 import java.util.ArrayList;
  6 import java.util.List;
  7 
  8 /**
  9  * @author zhaojiatao
 10  * @date 2018/9/11
 11  *
 12  *
 13  * 学习范型在Class引用的使用过程的应用
 14  *
 15  *
 16  */
 17 public class Test2_2 {
 18 
 19 
 20     @Test
 21     public void Test01() {
 22         Class intClass=int.class;
 23         Class<Integer> genericIntClass=int.class;
 24         genericIntClass=Integer.class;
 25         //如果将genericIntClass这个Class引用赋值给double.class的话,会由于类型检查失败,是无法编译;
 26         //genericIntClass=double.class;
 27         //普通类的引用可以赋值为任何其他的Class对象;
 28         intClass = double.class;
 29     }
 30 
 31 
 32     //可以使用通配符?代替上例中的<Integer>,
 33     @Test
 34     public void Test02() {
 35         Class<?> intClass=int.class;
 36         intClass = double.class;
 37     }
 38 
 39     //如果我像创建一个Class对象的引用,并指定这个Class对象的类型为指定类型或其子类型,则需要使用? extend XXX
 40     @Test
 41     public void Test03() {
 42         Class<? extends Number> bounded=int.class;
 43         bounded = double.class;
 44         bounded = Number.class;
 45     }
 46 
 47     //至此,可以得出结论,使用范型语法的目的是为了提供编译期检查;
 48 
 49     @Test
 50     public void Test04(){
 51         FilledList<CountedInteger> fl=new FilledList<CountedInteger>(CountedInteger.class);
 52         System.out.println(fl.create(15));
 53     }
 54 
 55 
 56 
 57     @Test
 58     public void Test05() throws IllegalAccessException, InstantiationException {
 59         Class<A> a=A.class;
 60         A aa=a.newInstance();
 61         //注意,这里如果不写成这样会报错
 62         Class<? super A> b=a.getSuperclass();
 63 
 64         //注意:b.newInstance返回的不是精确值,而是Object;
 65         Object bb=b.newInstance();
 66 
 67     }
 68 
 69 
 70 
 71 }
 72 
 73 class CountedInteger{
 74     private static long counter;
 75     private final long id=counter++;
 76     public String toString(){
 77         return  Long.toString(id);
 78     }
 79 }
 80 
 81 
 82 //注意,由于这个类中使用了type.newInstance()方法,所以,必须保证T传进来的类有默认构造方法;
 83 class FilledList<T>{
 84     private Class<T> type;
 85     public FilledList(Class<T> type){
 86         this.type=type;
 87     }
 88 
 89     public List<T> create(int nElements){
 90         List<T> result=new ArrayList<T>();
 91         try{
 92             for(int i=0;i<nElements;i++){
 93                 //注意,当在type上使用范型,newInstance()方法将产生确定的类型;
 94                 result.add(type.newInstance());
 95             }
 96         }catch (Exception e){
 97             e.printStackTrace();
 98         }
 99         return result;
100     }
101 
102 }
103 
104 
105 
106 class A{
107 
108 }
109 
110 class B extends A{
111 
112 }
View Code

 

14.3 类型转换前先做检查

在类型转换之前先做检查,如果贸然强制转换,可能会抛出ClassCastException异常;

学习使用instance of 和isInstance()进行类型检查;

主要区别就是instance of是编译器进行类型检查;

而 isInstance方法是运行期,动态进行类型检查,可用于反射、泛型中;

 

 1 package thinkingInJava.chapter_14_classInfo;
 2 
 3 /**
 4  * @author zhaojiatao
 5  * @date 2018/9/11
 6  *
 7  * 在类型转换之前先做检查,如果贸然强制转换,可能会抛出ClassCastException异常;
 8  * 学习使用instance of 和isInstance()进行类型检查;
 9  * 主要区别就是instance of是编译器进行类型检查;
10  * 而 isInstance方法是运行期,动态进行类型检查,可用于反射、泛型中;
11  *
12  */
13 
14 public class Test3 {
15     public static boolean DynamicEqual(Object fatherObj,Object sonObj){
16          return fatherObj.getClass().isInstance(sonObj); // pass
17         // return sonObj.getClass().isInstance(fatherObj);
18         // return sonObj instanceof Father; // pass
19         // return sonObj instanceof (fatherObj.getClass()); //error
20     }
21 
22     public static void main(String[] args){
23         //instance of 编译器类型检查
24         Father father = new Father();
25         Son son = new Son();
26 
27         System.out.println(son instanceof Son); // true
28         System.out.println(son instanceof Father); // true
29         System.out.println(son instanceof Object); // true
30         System.out.println(null instanceof Object); // false
31         System.out.println();
32 
33         //运行时动态类型检查(括号里的是子类)
34         System.out.println(Son.class.isInstance(son)); // true
35         //很明显是错误的,但编译是可以通过的
36         System.out.println(Integer.class.isInstance(son));//false
37         System.out.println(Father.class.isInstance(son)); // true
38         System.out.println(Object.class.isInstance(son)); // true
39         System.out.println(Object.class.isInstance(null)); // false
40         System.out.println();
41 
42 
43         //different using
44         System.out.println(DynamicEqual(father, son));
45     }
46 
47 
48 }
49 
50 class Father{}
51 
52 class Son extends Father{}
View Code

 

14.4注册工厂

工厂设计模式:将对象的创建工作交给类自己去完成;工厂方法可以被多态地调用,从而为你创建恰当类型的对象。

 

  1 package thinkingInJava.chapter_14_classInfo;
  2 
  3 import java.util.ArrayList;
  4 import java.util.Collections;
  5 import java.util.List;
  6 import java.util.Random;
  7 
  8 /**
  9  * @author zhaojiatao
 10  * @date 2018/9/12
 11  * 工厂方法设计模式:
 12  * 将对象的创建工作交给类自己去完成;工厂方法可以被多态地调用,从而为你创建恰当类型的对象。
 13  */
 14 public class Test4 {
 15 
 16     public static void main(String[] args) {
 17         for(int i=0;i<10;i++){
 18             System.out.println(Part.createRandom());
 19         }
 20     }
 21 
 22 }
 23 
 24 interface Factory<T>{
 25     T create();
 26 }
 27 
 28 
 29 class Part{
 30     public String toString(){
 31         return getClass().getSimpleName();
 32     }
 33 
 34     static List<Factory<? extends Part>> partFactories=new ArrayList<>();
 35 
 36     static {
 37         /*partFactories.add(new FuelFilter.Factory());
 38         partFactories.add(new AirFilter.Factory());
 39         partFactories.add(new CabinAirFilter.Factory());
 40         partFactories.add(new OilFilter.Factory());
 41 
 42         partFactories.add(new FanBelt.Factory());
 43         partFactories.add(new GeneratorBelt.Factory());
 44         partFactories.add(new PowerSteeringBelt.Factory());*/
 45         Collections.addAll(partFactories,new FuelFilter.Factory(),new AirFilter.Factory(),new CabinAirFilter.Factory(),
 46         new OilFilter.Factory(),new FanBelt.Factory(),new GeneratorBelt.Factory(),new PowerSteeringBelt.Factory()
 47         );
 48 
 49     }
 50 
 51     private static Random rand=new Random(47);
 52     public static Part createRandom(){
 53         int n =rand.nextInt(partFactories.size());
 54         return partFactories.get(n).create();
 55     }
 56 
 57 
 58 }
 59 
 60 
 61 class Filter extends Part{}
 62 
 63 class FuelFilter extends Filter{
 64     public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<FuelFilter>{
 65         @Override
 66         public FuelFilter create() {
 67             return new FuelFilter();
 68         }
 69     }
 70 }
 71 
 72 class AirFilter extends Filter{
 73     public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<AirFilter>{
 74         @Override
 75         public AirFilter create() {
 76             return new AirFilter();
 77         }
 78     }
 79 }
 80 
 81 class CabinAirFilter extends Filter{
 82     public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<CabinAirFilter>{
 83         @Override
 84         public CabinAirFilter create() {
 85             return new CabinAirFilter();
 86         }
 87     }
 88 }
 89 
 90 class OilFilter extends Filter{
 91     public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<OilFilter>{
 92         @Override
 93         public OilFilter create() {
 94             return new OilFilter();
 95         }
 96     }
 97 }
 98 
 99 
100 class Belt extends Part{
101 
102 }
103 
104 class FanBelt extends Belt{
105     public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<FanBelt>{
106         @Override
107         public FanBelt create() {
108             return new FanBelt();
109         }
110     }
111 }
112 
113 class GeneratorBelt extends Belt{
114     public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<GeneratorBelt>{
115         @Override
116         public GeneratorBelt create() {
117             return new GeneratorBelt();
118         }
119     }
120 }
121 
122 class PowerSteeringBelt extends Belt{
123     public static class Factory implements thinkingInJava.chapter_14_classInfo.Factory<PowerSteeringBelt>{
124         @Override
125         public PowerSteeringBelt create() {
126             return new PowerSteeringBelt();
127         }
128     }
129 }
View Code

 

 14.5 instanceof 与 Class的等价性

  这一章节,其实主要就是讲解instanceof、isInstance()、class对象== 三者的不同;

 instanceof和isInstance()是一组,结果是相同的,区别在前文已经说过,前者需要在编译器进行类型检查,后者只在运行时进行类型检查;

   而比较class对象的==和equals一组,结果也是相同的,应为equals是比较两个class对象的内存地址是否一致;

  但综合起来看,这两组的结果是不一样的。instance和isInstance()考虑了继承的情况,而后一组没有;

 

 1 package thinkingInJava.chapter_14_classInfo;
 2 
 3 /**
 4  * @author zhaojiatao
 5  * @date 2018/9/12
 6  */
 7 public class Test5 {
 8     public static void main(String[] args) {
 9         FamilyVsExactType.test(new Base());
10         FamilyVsExactType.test(new Derived());
11     }
12 }
13 
14 
15 class Base{}
16 class Derived extends Base{}
17 
18 class FamilyVsExactType {
19     static void test(Object x){
20         System.out.println("Testing x of type "+x.getClass());
21         System.out.println("x instanceof Base "+(x instanceof Base));
22         System.out.println("x instanceof Derived "+Derived.class.isInstance(x));
23         System.out.println("Base.isInstance(x) "+Base.class.isInstance(x));
24         System.out.println("Derived.isInstance(x) "+Derived.class.isInstance(x));
25         System.out.println("x.getClass()==Base.class "+(x.getClass()==Base.class));
26         System.out.println("x.getClass()==Derived.class "+(x.getClass()==Derived.class));
27         System.out.println("x.getClass().equals(Base.class) "+x.getClass().equals(Base.class));
28         System.out.println("x.getClass().equals(Derived.class) "+x.getClass().equals(Derived.class));
29     }
30 }
View Code

 

14.6 反射与内省 

14.6.1Java反射机制的适用场景及其利与弊(引用:https://blog.csdn.net/zolalad/article/details/29370565)

一、反射的适用场景是什么?

1).Java的反射机制在做基础框架的时候非常有用,有一句话这么说来着:反射机制是很多Java框架的基石。而一般应用层面很少用,不过这种东西,现在很多开源框架基本都已经给你封装好了,自己基本用不着写。典型的除了Hibernate之外,还有Spring也用到很多反射机制。经典的就是在xml文件或者properties里面写好了配置,然后在Java类里面解析xml或properties里面的内容,得到一个字符串,然后用反射机制,根据这个字符串获得某个类的Class实例,这样就可以动态配置一些东西,不用每一次都要在代码里面去new或者做其他的事情,以后要改的话直接改配置文件,代码维护起来就很方便了,同时有时候要适应某些需求,Java类里面不一定能直接调用另外的方法,这时候也可以通过反射机制来实现。
总的来说,自己写的很少,具体什么时候要用那要看需求,反射机制无非就是根据一个String来得到你要的实体对象,然后调用它原来的东西。但是如果是要自己写框架的话,那就会用得比较多了。

2)当你做一个软件可以安装插件的功能,你连插件的类型名称都不知道,你怎么实例化这个对象呢?因为程序是支持插件的(第三方的),在开发的时候并不知道 。所以无法在代码中 New出来 ,但反射可以,通过反射,动态加载程序集,然后读出类,检查标记之后再实例化对象,就可以获得正确的类实例。

3)在编码阶段不知道那个类名,要在运行期从配置文件读取类名, 这时候就没有办法硬编码new ClassName(),而必须用到反射才能创建这个对象.反射的目的就是为了扩展未知的应用。比如你写了一个程序,这个程序定义了一些接口,只要实现了这些接口的dll都可以作为插件来插入到这个程序中。那么怎么实现呢?就可以通过反射来实现。就是把dll加载进内存,然后通过反射的方式来调用dll中的方法。很多工厂模式就是使用的反射。 

二、程序员在自己的业务开发中应该尽量的远离反射

反射:在流行的库如Spring和Hibernate中,反射自然有其用武之地。不过内省业务代码在很多时候都不是一件好事,原因有很多,一般情况下我总是建议大家不要使用反射。

首先是代码可读性与工具支持。打开熟悉的IDE,寻找你的Java代码的内部依赖,很容易吧。现在,使用反射来替换掉你的代码然后再试一下,结果如何呢?如果通过反射来修改已经封装好的对象状态,那么结果将会变得更加不可控。请看看如下示例代码:

 

如果这样做就无法得到编译期的安全保证。就像上面这个示例一样,你会发现如果getDeclaredField()方法调用的参数输错了,那么只有在运行期才能发现。要知道的是,寻找运行期Bug的难度要远远超过编译期的Bug。

最后还要谈谈代价问题。JIT对反射的优化程度是不同的,有些优化时间会更长一些,而有些甚至是无法应用优化。因此,有时反射的性能损失可以达到几个数量级的差别。不过在典型的业务应用中,你可能不会注意到这个代价。

总结一下,我觉得在业务代码中唯一合理(直接)使用反射的场景是通过AOP。除此之外,你最好远离反射这一特性。

三、性能分析

反射机制是一种程序自我分析的能力。用于获取一个类的类变量,构造函数,方法,修饰符。

优点:运行期类型的判断,动态类加载,动态代理使用反射。

缺点:性能是一个问题,反射相当于一系列解释操作,通知jvm要做的事情,性能比直接的java代码要慢很多。

 

14.6.2反射的一些常用操作:参考:https://blog.csdn.net/sinat_38259539/article/details/71799078

  1 package thinkingInJava.chapter_14_classInfo;
  2 
  3 import org.junit.Test;
  4 
  5 import java.io.InputStream;
  6 import java.lang.reflect.Constructor;
  7 import java.lang.reflect.Field;
  8 import java.lang.reflect.InvocationTargetException;
  9 import java.lang.reflect.Method;
 10 import java.util.ArrayList;
 11 import java.util.Properties;
 12 
 13 /**
 14  * @author zhaojiatao
 15  * @date 2018/9/13
 16  *
 17  * 反射的应用场景:https://blog.csdn.net/zolalad/article/details/29370565
 18  * 反射的一些常用操作:参考:https://blog.csdn.net/sinat_38259539/article/details/71799078
 19  *
 20  */
 21 class Student {
 22 
 23     //---------------构造方法-------------------
 24     //(默认的构造方法)
 25     Student(String str){
 26         System.out.println("(默认)的构造方法 s = " + str);
 27     }
 28 
 29     //无参构造方法
 30     public Student(){
 31         System.out.println("调用了公有、无参构造方法执行了。。。");
 32     }
 33 
 34     //有一个参数的构造方法
 35     public Student(char name){
 36         System.out.println("姓名:" + name);
 37     }
 38 
 39     //有多个参数的构造方法
 40     public Student(String name ,int age){
 41         System.out.println("姓名:"+name+"年龄:"+ age);//这的执行效率有问题,以后解决。
 42     }
 43 
 44     //受保护的构造方法
 45     protected Student(boolean n){
 46         System.out.println("受保护的构造方法 n = " + n);
 47     }
 48 
 49     //私有构造方法
 50     private Student(int age){
 51         System.out.println("私有的构造方法   年龄:"+ age);
 52     }
 53 
 54 
 55     //**********字段*************//
 56     public String name;
 57     protected int age;
 58     char sex;
 59     private String phoneNum;
 60 
 61 
 62 
 63 
 64     //**************成员方法***************//
 65     public void show1(String s){
 66         System.out.println("调用了:公有的,String参数的show1(): s = " + s);
 67     }
 68     protected void show2(){
 69         System.out.println("调用了:受保护的,无参的show2()");
 70     }
 71     void show3(){
 72         System.out.println("调用了:默认的,无参的show3()");
 73     }
 74     private String show4(int age){
 75         System.out.println("调用了,私有的,并且有返回值的,int参数的show4(): age = " + age);
 76         return "abcd";
 77     }
 78 
 79 
 80 
 81     @Override
 82     public String toString() {
 83         return "Student [name=" + name + ", age=" + age + ", sex=" + sex
 84                 + ", phoneNum=" + phoneNum + "]";
 85     }
 86 
 87 
 88     public static void main(String[] args) {
 89         System.out.println("main方法执行了。。。");
 90     }
 91 
 92     public void show(){
 93         System.out.println("is show()");
 94     }
 95 
 96 
 97 
 98 
 99 }
100 
101 
102 
103 
104 public class Test6 {
105     /*
106      * 通过Class对象可以获取某个类中的:构造方法、成员变量、成员方法;并访问成员;
107      *
108      * 1.获取构造方法:
109      *         1).批量的方法:
110      *             public Constructor[] getConstructors():所有"公有的"构造方法
111                 public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
112 
113      *         2).获取单个的方法,并调用:
114      *             public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:
115      *             public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
116      *
117      *             调用构造方法:
118      *             Constructor-->newInstance(Object... initargs)
119      *
120      * 2.设置字段的值:
121      *         Field --> public void set(Object obj,Object value):
122      *         参数说明:
123      *         1.obj:要设置的字段所在的对象;
124      *         2.value:要为字段设置的值;
125      *
126      *
127      */
128 
129 
130 
131     //通过反射获取构造方法并使用
132     @Test
133     public void test01() {
134 
135         //1.加载Class对象
136         Class clazz = null;
137         try {
138             clazz = Class.forName("thinkingInJava.chapter_14_classInfo.Student");
139         } catch (ClassNotFoundException e) {
140             e.printStackTrace();
141         }
142 
143         //2.获取所有公有构造方法
144             System.out.println("**********************所有公有构造方法*********************************");
145             //Returns an array containing Constructor objects reflecting all the public constructors of the class represented by this Class object.
146             Constructor[] conArray = clazz.getConstructors();
147             for(Constructor c : conArray){
148                 System.out.println(c);
149             }
150 
151             System.out.println("************所有的构造方法(包括:私有、受保护、默认、公有)***************");
152             //Returns an array of Constructor objects reflecting all the constructors declared by the class represented by this Class object.
153             conArray = clazz.getDeclaredConstructors();
154             for(Constructor c : conArray){
155                 System.out.println(c);
156             }
157 
158             System.out.println("*****************获取公有、无参的构造方法*******************************");
159             Constructor con = null;
160             try {
161                 con = clazz.getConstructor(null);
162             } catch (NoSuchMethodException e) {
163                 e.printStackTrace();
164             }
165             //1>、因为是无参的构造方法所以类型是一个null,不写也可以:这里需要的是一个参数的类型,切记是类型
166             //2>、返回的是描述这个无参构造函数的类对象。
167 
168             System.out.println("con = " + con);
169             //调用构造方法
170             Object obj = null;
171             try {
172                 obj = con.newInstance();
173             } catch (InstantiationException e) {
174                 e.printStackTrace();
175             } catch (IllegalAccessException e) {
176                 e.printStackTrace();
177             } catch (InvocationTargetException e) {
178                 e.printStackTrace();
179             }
180             System.out.println("obj = " + obj);
181             Student stu = (Student)obj;
182             System.out.println("使用无参构造方法创建的Student对象的实例:stu="+stu.toString());
183 
184             System.out.println("******************获取私有构造方法,并调用*******************************");
185             try {
186                 con = clazz.getDeclaredConstructor(char.class);
187             } catch (NoSuchMethodException e) {
188                 e.printStackTrace();
189             }
190             System.out.println(con);
191             //调用构造方法
192             con.setAccessible(true);//暴力访问(忽略掉访问修饰符)
193             try {
194                 obj = con.newInstance('男');
195             } catch (InstantiationException e) {
196                 e.printStackTrace();
197             } catch (IllegalAccessException e) {
198                 e.printStackTrace();
199             } catch (InvocationTargetException e) {
200                 e.printStackTrace();
201             }
202 
203             System.out.println("obj = " + obj);
204             stu = (Student)obj;
205             System.out.println("使用私有构造方法创建的Student对象的实例:stu="+stu.toString());
206 
207     }
208 
209 
210 
211 
212     //获取成员变量并调用
213     @Test
214     public void test02() throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException,
215             InvocationTargetException, InstantiationException {
216         //1.获取Class对象
217         Class stuClass = Class.forName("thinkingInJava.chapter_14_classInfo.Student");
218         //2.获取字段
219         System.out.println("************获取所有公有的字段********************");
220         Field[] fieldArray = stuClass.getFields();
221         for(Field f : fieldArray){
222             System.out.println(f);
223         }
224         System.out.println("************获取所有的字段(包括私有、受保护、默认的)********************");
225         fieldArray = stuClass.getDeclaredFields();
226         for(Field f : fieldArray){
227             System.out.println(f);
228         }
229         System.out.println("*************获取公有字段xx并调用***********************************");
230         Field f = stuClass.getField("name");
231         System.out.println(f);
232         //获取一个对象
233         Object obj = stuClass.getConstructor().newInstance();//产生Student对象--》Student stu = new Student();
234         //为字段设置值
235         f.set(obj, "刘德华");//为Student对象中的name属性赋值--》stu.name = "刘德华"
236         //验证
237         Student stu = (Student)obj;
238         System.out.println("验证姓名:" + stu.name);
239 
240 
241         System.out.println("**************获取私有字段****并调用********************************");
242         f = stuClass.getDeclaredField("phoneNum");
243         System.out.println(f);
244         f.setAccessible(true);//暴力反射,解除私有限定
245         f.set(obj, "18888889999");
246         System.out.println("验证电话:" + stu);
247 
248     }
249 
250 
251 
252     //获取成员方法并调用
253     @Test
254     public void test03() throws Exception {
255 
256         //1.获取Class对象
257         Class stuClass = Class.forName("thinkingInJava.chapter_14_classInfo.Student");
258         //2.获取所有公有方法
259         System.out.println("***************获取所有的”公有“方法*******************");
260         stuClass.getMethods();
261         Method[] methodArray = stuClass.getMethods();
262         for(Method m : methodArray){
263             System.out.println(m);
264         }
265         System.out.println("***************获取所有的方法,包括私有的*******************");
266         methodArray = stuClass.getDeclaredMethods();
267         for(Method m : methodArray){
268             System.out.println(m);
269         }
270         System.out.println("***************获取公有的show1()方法*******************");
271         Method m = stuClass.getMethod("show1", String.class);
272         System.out.println(m);
273         //实例化一个Student对象
274         Object obj = stuClass.getConstructor().newInstance();
275         m.invoke(obj, "刘德华");
276 
277         System.out.println("***************获取私有的show4()方法******************");
278         m = stuClass.getDeclaredMethod("show4", int.class);
279         System.out.println(m);
280         m.setAccessible(true);//解除私有限定
281         Object result = m.invoke(obj, 20);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参
282         System.out.println("返回值:" + result);
283 
284 
285 
286 
287     }
288 
289 
290     //反射main方法
291     @Test
292     public void test04(){
293         try {
294             //1、获取Student对象的字节码
295             Class clazz = Class.forName("thinkingInJava.chapter_14_classInfo.Student");
296 
297             //2、获取main方法
298             Method methodMain = clazz.getMethod("main", String[].class);//第一个参数:方法名称,第二个参数:方法形参的类型,
299             //3、调用main方法
300             // methodMain.invoke(null, new String[]{"a","b","c"});
301             //第一个参数,对象类型,因为方法是static静态的,所以为null可以,第二个参数是String数组,这里要注意在jdk1.4时是数组,jdk1.5之后是可变参数
302             //这里拆的时候将  new String[]{"a","b","c"} 拆成3个对象。。。所以需要将它强转。
303             methodMain.invoke(null, (Object)new String[]{"a","b","c"});//方式一
304             //methodMain.invoke(null, new Object[]{new String[]{"a","b","c"}});//方式二
305 
306         } catch (Exception e) {
307             e.printStackTrace();
308         }
309 
310     }
311 
312 
313 
314     //反射方法的其它使用之---通过反射运行配置文件内容
315     @Test
316     public void test05() throws Exception {
317         Properties pro = new Properties();
318         {//此方式要求   配置文件在 src 文件夹 内
319 
320             //类名.class.getClassLoader().getResourceAsStream("文件名")
321             InputStream inStream = this.getClass().getClassLoader().getResourceAsStream("pro.properties");
322             pro.load(inStream);
323             inStream.close();
324         }
325 
326         //通过反射获取Class对象
327         Class stuClass = Class.forName(pro.getProperty("className"));//"cn.fanshe.Student"
328         //2获取show()方法
329         Method m = stuClass.getMethod(pro.getProperty("methodName"));//show
330         //3.调用show()方法
331         m.invoke(stuClass.getConstructor().newInstance());
332 
333     }
334 
335 
336 
337     //反射方法的其它使用之---通过反射越过泛型检查
338     @Test
339     public void test06() throws Exception{
340         ArrayList<String> strList = new ArrayList<String>();
341         strList.add("aaa");
342         strList.add("bbb");
343 
344         //    strList.add(100);
345         //获取ArrayList的Class对象,反向的调用add()方法,添加数据
346         Class listClass = strList.getClass(); //得到 strList 对象的字节码 对象
347         //获取add()方法
348         Method m = listClass.getMethod("add", Object.class);
349         //调用add()方法
350         m.invoke(strList, 100);
351 
352         //遍历集合
353         for(Object obj : strList){
354             System.out.println(obj);
355         }
356 
357     }
358 
359 
360 
361 
362 
363 }
View Code

 

 

 

14.6.3 内省(参考:http://www.cnblogs.com/peida/archive/2013/06/03/3090842.html)

内省(Introspector) 是Java 语言对 JavaBean 类属性、事件的一种缺省处理方法。

  JavaBean是一种特殊的类,主要用于传递数据信息,这种类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。如果在两个模块之间传递信息,可以将信息封装进JavaBean中,这种对象称为“值对象”(Value Object),或“VO”。方法比较少。这些信息储存在类的私有变量中,通过set()、get()获得。

什么时候会用到内省?

在框架设计的时候使用比较多,像mybatis中类与表的映射关系的类的属性与表的列一一对应,框架在赋值和获取值得时候会调用对应的setter和getter方法;

例如spring 中初始化bean的时候取药用到反射的方式实例化bean,在set值的时候会根据变量名获得settter方法,把配置好的值set进去;

 

  1 package thinkingInJava.chapter_14_classInfo;
  2 
  3 import org.apache.commons.beanutils.BeanUtils;
  4 import org.apache.commons.beanutils.PropertyUtils;
  5 import org.junit.Test;
  6 
  7 import java.beans.BeanInfo;
  8 import java.beans.IntrospectionException;
  9 import java.beans.Introspector;
 10 import java.beans.PropertyDescriptor;
 11 import java.lang.reflect.InvocationTargetException;
 12 import java.lang.reflect.Method;
 13 
 14 /**
 15  * @author zhaojiatao
 16  * @date 2018/9/13
 17  *
 18  * 内省:
 19  * http://www.cnblogs.com/peida/archive/2013/06/03/3090842.html
 20  * https://blog.csdn.net/u010445297/article/details/60967146
 21  *
 22  */
 23 public class Test6_Introspector {
 24 
 25     public static void main(String[] args) {
 26         UserInfo userInfo=new UserInfo();
 27         userInfo.setUserName("peida");
 28         try {
 29             BeanInfoUtil.getProperty(userInfo, "userName");
 30 
 31             BeanInfoUtil.setProperty(userInfo, "userName");
 32 
 33             BeanInfoUtil.getProperty(userInfo, "userName");
 34 
 35             BeanInfoUtil.setPropertyByIntrospector(userInfo, "userName");
 36 
 37             BeanInfoUtil.getPropertyByIntrospector(userInfo, "userName");
 38 
 39             BeanInfoUtil.setProperty(userInfo, "age");
 40             //说明:BeanInfoUtil.setProperty(userInfo, "age");报错是应为age属性是int数据类型,
 41             //而setProperty方法里面默认给age属性赋的值是String类型。所以会爆出argument type mismatch参数类型不匹配的错误信息。
 42 
 43         } catch (Exception e) {
 44             // TODO Auto-generated catch block
 45             e.printStackTrace();
 46         }
 47 
 48     }
 49 
 50 
 51 
 52     @Test
 53     public void testBeanUtils(){
 54         UserInfo userInfo=new UserInfo();
 55         try {
 56             BeanUtils.setProperty(userInfo, "userName", "peida");
 57 
 58             System.out.println("set userName:"+userInfo.getUserName());
 59 
 60             System.out.println("get userName:"+BeanUtils.getProperty(userInfo, "userName"));
 61 
 62             BeanUtils.setProperty(userInfo, "age", 18);
 63             System.out.println("set age:"+userInfo.getAge());
 64 
 65             System.out.println("get age:"+BeanUtils.getProperty(userInfo, "age"));
 66 
 67             System.out.println("get userName type:"+BeanUtils.getProperty(userInfo, "userName").getClass().getName());
 68             System.out.println("get age type:"+BeanUtils.getProperty(userInfo, "age").getClass().getName());
 69 
 70             PropertyUtils.setProperty(userInfo, "age", 8);
 71             System.out.println(PropertyUtils.getProperty(userInfo, "age"));
 72 
 73             System.out.println(PropertyUtils.getProperty(userInfo, "age").getClass().getName());
 74 
 75             PropertyUtils.setProperty(userInfo, "age", "8");
 76         }
 77         catch (IllegalAccessException e) {
 78             e.printStackTrace();
 79         }
 80         catch (InvocationTargetException e) {
 81             e.printStackTrace();
 82         }
 83         catch (NoSuchMethodException e) {
 84             e.printStackTrace();
 85         }
 86     }
 87 
 88 
 89 
 90 }
 91 
 92 
 93 class BeanInfoUtil {
 94 
 95     public static void setProperty(UserInfo userInfo,String userName){
 96         PropertyDescriptor propDesc= null;
 97         try {
 98             propDesc = new PropertyDescriptor(userName,UserInfo.class);
 99         } catch (IntrospectionException e) {
100             e.printStackTrace();
101         }
102         Method methodSetUserName=propDesc.getWriteMethod();
103         try {
104             methodSetUserName.invoke(userInfo, "wong");
105         } catch (IllegalAccessException e) {
106             e.printStackTrace();
107         } catch (InvocationTargetException e) {
108             e.printStackTrace();
109         }
110         System.out.println("set userName:"+userInfo.getUserName());
111     }
112 
113     public static void getProperty(UserInfo userInfo,String userName){
114         PropertyDescriptor proDescriptor = null;
115         try {
116             proDescriptor = new PropertyDescriptor(userName,UserInfo.class);
117         } catch (IntrospectionException e) {
118             e.printStackTrace();
119         }
120         Method methodGetUserName=proDescriptor.getReadMethod();
121         Object objUserName= null;
122         try {
123             objUserName = methodGetUserName.invoke(userInfo);
124         } catch (IllegalAccessException e) {
125             e.printStackTrace();
126         } catch (InvocationTargetException e) {
127             e.printStackTrace();
128         }
129         System.out.println("get userName:"+objUserName.toString());
130     }
131 
132     public static void setPropertyByIntrospector(UserInfo userInfo,String userName){
133         BeanInfo beanInfo= null;
134         try {
135             beanInfo = Introspector.getBeanInfo(UserInfo.class);
136         } catch (IntrospectionException e) {
137             e.printStackTrace();
138         }
139         PropertyDescriptor[] proDescrtptors=beanInfo.getPropertyDescriptors();
140         if(proDescrtptors!=null&&proDescrtptors.length>0){
141             for(PropertyDescriptor propDesc:proDescrtptors){
142                 if(propDesc.getName().equals(userName)){
143                     Method methodSetUserName=propDesc.getWriteMethod();
144                     try {
145                         methodSetUserName.invoke(userInfo, "alan");
146                     } catch (IllegalAccessException e) {
147                         e.printStackTrace();
148                     } catch (InvocationTargetException e) {
149                         e.printStackTrace();
150                     }
151                     System.out.println("set userName:"+userInfo.getUserName());
152                     break;
153                 }
154             }
155         }
156     }
157 
158     public static void getPropertyByIntrospector(UserInfo userInfo,String userName){
159         BeanInfo beanInfo= null;
160         try {
161             beanInfo = Introspector.getBeanInfo(UserInfo.class);
162         } catch (IntrospectionException e) {
163             e.printStackTrace();
164         }
165         PropertyDescriptor[] proDescrtptors=beanInfo.getPropertyDescriptors();
166         if(proDescrtptors!=null&&proDescrtptors.length>0){
167             for(PropertyDescriptor propDesc:proDescrtptors){
168                 if(propDesc.getName().equals(userName)){
169                     Method methodGetUserName=propDesc.getReadMethod();
170                     Object objUserName= null;
171                     try {
172                         objUserName = methodGetUserName.invoke(userInfo);
173                     } catch (IllegalAccessException e) {
174                         e.printStackTrace();
175                     } catch (InvocationTargetException e) {
176                         e.printStackTrace();
177                     }
178                     System.out.println("get userName:"+objUserName.toString());
179                     break;
180                 }
181             }
182         }
183     }
184 }
185 
186 
187 
188 class UserInfo {
189 
190     private long userId;
191     private String userName;
192     private int age;
193     private String emailAddress;
194 
195     public long getUserId() {
196         return userId;
197     }
198     public void setUserId(long userId) {
199         this.userId = userId;
200     }
201     public String getUserName() {
202         return userName;
203     }
204     public void setUserName(String userName) {
205         this.userName = userName;
206     }
207     public int getAge() {
208         return age;
209     }
210     public void setAge(int age) {
211         this.age = age;
212     }
213     public String getEmailAddress() {
214         return emailAddress;
215     }
216     public void setEmailAddress(String emailAddress) {
217         this.emailAddress = emailAddress;
218     }
219 
220 }
View Code

 

14.7 jdk动态代理(参考:https://www.cnblogs.com/xiaoluo501395377/p/3383130.html)

jdk的动态代理依赖接口,有局限性;底层是通过反射,执行method;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted on 2018-09-11 23:59  zhaojiatao  阅读(144)  评论(0编辑  收藏  举报

导航