一. 背景
并不是所有的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没有起到初始化作用
(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
(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
(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
(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 }
(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
(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 */
(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 */
(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 */
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 }
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
说明:反射可以让我们创建一个类的实例、在类外部访问类的私有方法、私有字段。
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 }
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]
四.动态代理
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
原文地址:https://www.cnblogs.com/Hermioner/p/9596335.html