码迷,mamicode.com
首页 > 编程语言 > 详细

13. Java基础之类型信息(RTTI和反射)

时间:2018-10-02 13:58:47      阅读:121      评论:0      收藏:0      [点我收藏+]

标签:method   led   打开   reflect   hash   方式   notify   hello   size   

一. 背景

         并不是所有的Class都能在编译时明确,因此在某些情况下需要在运行时再发现和确定类型信息(比如:基于构建编程,),这就是RTTI(Runtime Type Information,运行时类型信息)。

Java是如何让我们在运行时识别对象和类的信息的,主要有两种RTTI的方式,一种是“传统的”RTTI,即假设在编译时已经知道了所有的类型;还有一种,是利用反射机制,在运行时再尝试确定类型信息。

二. RTTI

        RTTI(Run-Time Type Infomation),运行时类型信息。可以在运行时识别一个对象的类型。类型信息在运行时通过Class对象表示,Class对象包含与类有关的信息,可以使用Class对象来创建类的实例。

  每个类对应一个Class对象,这个Class对象放在.class文件中,当我们的程序中首次主动使用某类型时,会把该类型所对应的Class对象加载进内存。

     我们如何获取到Class对象呢?有三种方法

       1. Class.forName("全限定名");(其中,全限定名为包名+类名)。

       2. 类字面常量,如String.class,对应String类的Class对象。

       3.通过getClass()方法获取Class对象,如String str = "hello";str.getClass();。

  通过一个类对应的Class对象后,我们可以做什么?我们可以获取该类的父类、接口、创建该类的对象、该类的构造器、字段、方法等等。

  下面我们通过例子来熟悉Class对象的各种用法。

 1. 三种获取对象Class对象的方法

note1:在面向对象的世界里,万事万物都是对象。类是对象,类是java.lang.Class类的实例对象。

note2:查看Class类的源码,发现构造函数是private的,只有虚拟机才可以创建class对象。

note3:任何一个类都是Class的实例对象,这个实例对象有3中标识方式:(1)(2)(3)所示。

(1)类名.class-------不会引起类初始化

这实际告诉我们任何一个类都有一个隐含的静态成员变量class

技术分享图片
 1 public class Person {
 2     public int a=1;
 3     public static int b=2;
 4     public final static int c=3;
 5     static {
 6         System.out.println("hello");
 7     }
 8 
 9 }
10 
11 
12 public class Test2 {
13     public static void main(String args[]) {
14          Class<?> class1=Person.class;    //未引起初始化
15     }
16 
17 }
18 
19 
20 未输出任何东西,说明.class没有起到初始化作用
View Code

(2)对象.getClass()------会引起类初始化

技术分享图片
 1 public class Test2 {
 2     public static void main(String args[]) {
 3         Person person=new Person();
 4         Class<?>class1=person.getClass();
 5     }
 6 
 7 }
 8 
 9 
10 
11 输出:
12 hello
View Code

(3)Class.forName(“全限定名”)--------会引起类初始化

技术分享图片
 1 public class Test2 {
 2     public static void main(String args[]) throws ClassNotFoundException {
 3         Class<?>class1=Class.forName("com.test.a.Person");
 4     }
 5 
 6 }
 7 
 8 
 9 输出:
10 hello
View Code

(4)一个类只可能是Class类的一个实例对象

因此上述三种方法都会得到同样的一个Class类的实例对象。

技术分享图片
 1 package com.test.a;
 2 
 3 public class Test {
 4     public static void main(String args[]) throws ClassNotFoundException {
 5 
 6         Class class1 = Test.class;
 7         Test test = new Test();
 8         Class class2 = test.getClass();
 9         Class class3 = Class.forName("com.test.a.Test");
10         System.out.println(class1 == class2);
11         System.out.println(class1 == class3);
12     }
13 }
14 true
15 true
View Code

(5)可以通过类的类类型创建该类的对象实例

  Foo foo=(Foo)c1.newInstance();

 2. Class类本身定义的方法使用

技术分享图片
 1 package com.test.a;
 2 
 3 public class Person {
 4     public String name;
 5     public int age;
 6 
 7     public String getName() {
 8         return name;
 9     }
10 
11     private void setName(String name) {
12         this.name = name;
13     }
14 
15     public int getAge() {
16         return age;
17     }
18 
19     private void setAge(int age) {
20         this.age = age;
21     }
22 }
23 package com.test.a;
24 
25 public class Woman extends Person{
26     public Double salary;
27     private String sex;
28     public Woman(String sex) {
29         this.sex=sex;
30     }
31     private Woman() {
32         
33     }
34     public void print()
35     {
36         System.out.println("hello");
37     }
38     
39     private void print2() {
40         System.out.println("hello2");
41     }
42 
43 }
View Code

(1)获取包名

技术分享图片
 1 package com.test.a;
 2 
 3 public class Test {
 4     public static void main(String args[]) {
 5         Class class1 = Test.class;
 6         System.out.println(class1.getName());
 7         System.out.println(class1.getSimpleName());
 8         Class c1 = double.class;
 9         Class c2 = String.class;
10         Class c3 = Void.class;
11 
12         System.out.println(c1.getName());
13         System.out.println(c1.getSimpleName());
14         System.out.println(c2.getName());
15         System.out.println(c2.getSimpleName());//不包含包名
16         System.out.println(c3.getName());
17         System.out.println(c3.getSimpleName());
18     }
19 }
20 
21 
22 com.test.a.Test
23 Test
24 double
25 double
26 java.lang.String
27 String
28 java.lang.Void
29 Void
View Code

(2)获取方法

技术分享图片
 1 package com.test.a;
 2 
 3 import java.lang.reflect.Method;
 4 
 5 public class Test {
 6     public static void main(String args[]) {
 7         Woman woman=new Woman();
 8         Class class1=woman.getClass();//传递的是哪个子类的对象,class1就是该子类的类类型
 9         //一个成员方法就是一个Method对象
10         Method[] publicMethods=class1.getMethods();//得到所有的public方法,包含从父类继承而来的Public方法
11         for(Method i:publicMethods) {
12             System.out.println(i);
13         }
14         
15         System.out.println("***************");
16         Method[] declaredMethod=class1.getDeclaredMethods();//只打印该类自己定义的方法(没有权限限制)
17         for(Method i:declaredMethod) {
18             System.out.println(i);
19         }
20     }
21 }
22 
23 /**
24 public void com.test.a.Woman.print()
25 public java.lang.String com.test.a.Person.getName()
26 public int com.test.a.Person.getAge()
27 public final void java.lang.Object.wait() throws java.lang.InterruptedException
28 public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
29 public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
30 public boolean java.lang.Object.equals(java.lang.Object)
31 public java.lang.String java.lang.Object.toString()
32 public native int java.lang.Object.hashCode()
33 public final native java.lang.Class java.lang.Object.getClass()
34 public final native void java.lang.Object.notify()
35 public final native void java.lang.Object.notifyAll()
36 ***************
37 public void com.test.a.Woman.print()
38 private void com.test.a.Woman.print2()
39 
40 */
View Code

(3)获取成员变量

技术分享图片
 1 package com.test.a;
 2 
 3 import java.lang.reflect.Field;
 4 import java.lang.reflect.Method;
 5 
 6 public class Test {
 7     public static void main(String args[]) {
 8         Woman woman=new Woman();
 9         Class class1=woman.getClass();//传递的是哪个子类的对象,class1就是该子类的类类型
10         Field fields[]=class1.getFields();
11         for(Field i:fields) {
12             System.out.println(i);
13         }
14         
15         System.out.println("**************");
16         Field decFileds[]=class1.getDeclaredFields();
17         for(Field i:decFileds) {
18             System.out.println(i);
19         }
20     }
21 }
22 
23 /**
24 public java.lang.Double com.test.a.Woman.salary
25 public java.lang.String com.test.a.Person.name
26 public int com.test.a.Person.age
27 **************
28 public java.lang.Double com.test.a.Woman.salary
29 private java.lang.String com.test.a.Woman.sex
30 
31 
32 */
View Code

(4)获取对象的构造函数信息

技术分享图片
 1 package com.test.a;
 2 
 3 import java.lang.reflect.Constructor;
 4 import java.lang.reflect.Field;
 5 import java.lang.reflect.Method;
 6 
 7 public class Test {
 8     public static void main(String args[]) {
 9         Woman woman=new Woman("femal");
10         Class class1=woman.getClass();//传递的是哪个子类的对象,class1就是该子类的类类型
11         
12         Constructor<Woman> constructors[]=class1.getConstructors();
13         for(Constructor<Woman> i:constructors) {
14             System.out.println(i);
15         }
16         System.out.println("********");
17         
18         Constructor<Woman> constructors2[]=class1.getDeclaredConstructors();
19         for(Constructor<Woman> i:constructors2) {
20             System.out.println(i);
21         }
22     }
23 }
24 
25 /**
26  public com.test.a.Woman(java.lang.String)
27 ********
28 public com.test.a.Woman(java.lang.String)
29 private com.test.a.Woman()
30 
31  */
View Code

3. Java动态加载类

Class.forName(“类的全称”):不仅标识了类的类类型,还代表了动态加载类。

编译时刻加载类是静态加载类、运行时刻加载类是动态加载类。

 静态加载类  VS  动态加载类??

三. 反射

       与RTTI必须在编译器就知道所有类型不同,反射不必在编译期就知道所有的类型,它可以在运行过程中使用动态加载的类,而这个类不必在编译期就已经知道。反射主要由java.lang.reflect类库的Field、Method、Constructor类支持。这些类的对象都是JVM在运行时进行创建,用来表示未知的类。

  关于两者的区别更深刻表达如下:对于RTTI而言,编译器在编译时打开和检查.class文件;对于反射而言,.class文件在编译时是不可获取的,所以在运行时打开和检查.class文件。

  其实在的第一个例子中我们已经用到了Constructor、Method类,现在我们来更加具体的了解Constructor、Method、Field类。  

技术分享图片
 1 package com.test.a;
 2 
 3 import java.lang.reflect.Constructor;
 4 import java.lang.reflect.Field;
 5 import java.lang.reflect.InvocationTargetException;
 6 import java.lang.reflect.Method;
 7 
 8 import javax.activation.FileDataSource;
 9 
10 public class Test2 {
11     public static void main(String args[]) throws NoSuchMethodException, SecurityException, InstantiationException,
12             IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException {
13         Class<?> class1 = Person.class;
14         Constructor<?> constructor = class1.getConstructor(int.class, String.class);// 区别getConstructors,这里不是Integer.class
15         Person person1 = (Person) constructor.newInstance(23, "Tom");
16         System.out.println(person1);
17 
18 //        Method method=class1.getMethod("f");//运行异常,因为该方法不适用于private方法
19         Method method2 = class1.getMethod("g");
20         method2.invoke(person1);
21         Method method3 = class1.getDeclaredMethod("f");
22         method3.setAccessible(true);
23         method3.invoke(person1);// 必须要setAccessible成true,才可以访问private方法
24 
25         Field field = class1.getDeclaredField("age");
26         System.out.println(person1);
27         field.setAccessible(true);
28         field.set(person1, 34);
29         System.out.println(person1);
30     }
31 
32 }
View Code
技术分享图片
1 The name is: Tomthe age is 23
2 I am public function
3 I am private function
4 The name is: Tomthe age is 23
5 The name is: Tomthe age is 34
View Code

说明:反射可以让我们创建一个类的实例、在类外部访问类的私有方法、私有字段。

1.方法的反射

(1)如何获取某个方法

  方法的名称和方法的参数列表才能唯一决定某个方法

(2)方法反射的操作

  method.invoke(对象,参数列表)

note:要获取一个方法,就是要获取类的信息,要获取一个类的信息,就是获取类类型。

技术分享图片
 1 package com.test.a;
 2 
 3 import java.lang.reflect.InvocationTargetException;
 4 import java.lang.reflect.Method;
 5 
 6 public class Test {
 7     public static void main(String args[]) throws NoSuchMethodException, SecurityException, IllegalAccessException,
 8             IllegalArgumentException, InvocationTargetException {
 9         // 1.获取类的信息
10         R a1 = new R();
11         Class class1 = a1.getClass();
12         // 2.获取方法:名称和参数列表来决定
13         Method method = class1.getMethod("print", new Class[] { int.class, int.class });
14         // 也可以写成Method method2=class1.getMethod("print",
15         // int.class,int.class);因为...代表可变参数,可以携程数组形式,也可以全部写出来
16 
17         // 3.方法的反射操作
18         // note1:方法的反射操作是用method对象来进行方法调用,和a1.print调用的效果相同。(正常情况下是对象操作方法,反射反过来,通过print对象操作a1)
19         // note2:如果方法没有返回值,返回null,有返回值返回具体的返回值
20         Object object = method.invoke(a1, new Object[] { 10, 20 });
21 
22     }
23 }
24 
25 package com.test.a;
26 
27 public class R {
28     public void print(int a, int b) {
29         System.out.println(a + b);
30     }
31 
32     public void print(String a, String b) {
33         System.out.println(a.toUpperCase() + "," + b.toLowerCase());
34     }
35 }
View Code

 30

2.Java通过反射了解集合泛型的本质

技术分享图片
 1 package com.test.a;
 2 
 3 import java.lang.reflect.InvocationTargetException;
 4 import java.lang.reflect.Method;
 5 import java.util.ArrayList;
 6 
 7 public class Test {
 8     public static void main(String args[]) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
 9         ArrayList list1=new ArrayList();
10         ArrayList<String> list2=new ArrayList<>();
11         list2.add("a");//only string,can‘t list.add(20);
12         Class class1=list1.getClass();
13         Class class2=list2.getClass();
14         System.out.println(class1==class2);
15         /**
16          * class1==class2的结果为true说明编译后的集合的泛型是去泛型化的。
17          * Java中集合的泛型,是防止错误输入的,只在编译阶段有效,绕过编译就无效了。
18          * 验证:我们可以通过方法的反射来操作,绕过编译
19          */
20         Method method=class2.getMethod("add", Object.class);
21         method.invoke(list2, 20);//绕过编译操作就绕过了泛型
22         System.out.println(list2.size());
23         System.out.println(list2);
24         
25     }
26 }
27 
28 true
29 2
30 [a, 20]
View Code

四.动态代理

http://www.cnblogs.com/xiaoluo501395377/p/3383130.html

视频

https://www.imooc.com/learn/199

https://study.163.com/course/introduction.htm?courseId=947001#/courseDetail?tab=1

13. Java基础之类型信息(RTTI和反射)

标签:method   led   打开   reflect   hash   方式   notify   hello   size   

原文地址:https://www.cnblogs.com/Hermioner/p/9596335.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!