大纲:
Class类的使用: 三种获得Class类的实例的方法; 基本数据类型对应的类类型; Class类的基本API:获取类的常用(全部)信息;Java类加载机制: 编译时加载(静态加载); 运行时加载(动态加载);方法的反射:成员变量的反射:构造函数的反射:通过反射了解集合泛型的本质:在面向对象的世界里,万事万物皆对象;但其实静态的成员、普通的数据类型不是对象,但我们最好看做万事万物皆对象;类也是对象,类是java.lang.Class类的实例对象;任何一个类都是Clss类的实例对象;
三种获得Class类的实例的方法:
1 /** 2 * Class类的使用:Class类的实例对象的三种表示方法 3 */ 4 package reflect; 5 6 public class ClassTest01 7 { 8 public static void main(String[] args) 9 {10 // Foo类的实例对象:foo11 Foo foo = new Foo();12 13 // Foo这个类也是一个实例对象,是java.lang.Class类的实例对象14 // 任何一个类都是Class类的实例对象,这个实例对象有三种表示方法15 16 // 第一种表示方法:17 // 实际告诉我们,任何一个类都有一个隐含的静态成员变量:class18 Class c1 = Foo.class;19 20 // 第二种表示方法:已知该类的实例对象,通过getClass()方法获取21 Class c2 = foo.getClass();22 23 // 官网描述:c1、c2表示Foo类的类类型(class type)24 // 如何解释:Foo类本身就是一个实例对象,是Class类的实例对象25 // 以此区分:Foo类的实例对象为foo,Foo类的类类型为c1、c226 27 // c1、c2都是同一个类(Foo)的类类型,一个类只可能是Class类的一个实例对象28 // 所以c1与c2完全相等29 System.out.println(c1 == c2);30 31 // 第三种表示方法:32 Class c3 = null;33 try34 {35 // 写出类的全量名36 c3 = Class.forName("reflect.Foo");37 } 38 catch (ClassNotFoundException e)39 {40 e.printStackTrace();41 }42 // c2与c3完全相等43 System.out.println(c2 == c3);44 45 // 可以通过一个类的类类型创建该类的实例对象46 try47 {48 // 前提:要有无参的构造方法49 Foo f1 = (Foo) c1.newInstance();50 // 此时可以调用类的方法了51 f1.display();52 } 53 catch (InstantiationException e)54 {55 e.printStackTrace();56 } 57 catch (IllegalAccessException e)58 {59 e.printStackTrace();60 }61 }62 }63 64 class Foo65 {66 void display()67 {68 System.out.println("abc");69 }70 }
Class类动态加载类的用法:
Class.forName(“类的全称”); 这种写法不仅表示了类的类类型,还表示了动态加载类; 要区分编译和运行: 编译时刻加载类是静态加载类、运行时刻加载类是动态加载类; new 创建对象:是静态加载类,在编译时刻就需要加载所有可能用到的类,不管这些类在后面是否真用到了;(通过动态加载类可以解决该问题);静态加载类的方式:
1 /** 2 * 静态加载类的方式:通过new创建对象 3 */ 4 package reflect.static_load; 5 6 public class Office 7 { 8 public static void main(String[] args) 9 {10 // Word类,Excel类必须都存在才能运行11 if("Word".equals(args[0]))12 {13 // 通过new创建对象,是静态加载类,在编译时刻就需要加载所有可能使用到的类,不管你需不需要用;所以下面报错了,因为没有定义Word和Execl类;14 // 现在的需求是:你想用哪个就加载哪个,哪个不用就不加载它,这就涉及到动态加载;15 // 通过动态加载类可以解决该问题16 Word word = new Word(); // 这里编译报错,因为没有Word类17 word.start();18 }19 20 if("Excel".equals(args[0]))21 {22 Excel excel = new Excel(); // 这里编译报错,因为没有Excel类23 excel.start();24 }25 }26 }
动态加载类的方式:
1 /** 2 * 动态加载类 3 */ 4 package reflect.dynamic_load; 5 6 public class OfficeBetter 7 { 8 public static void main(String[] args) 9 {10 // 实例一11 try12 {13 // 动态加载类,在运行时刻加载;就算此时没有定义Execl类,编译也没有报错;只有在运行时才会报错14 Class c1 = Class.forName("reflect.dynamic_load.Excel");15 } 16 catch (ClassNotFoundException e)17 {18 e.printStackTrace();19 }20 21 // 实例二22 // 动态加载类,在运行时刻加载23 try24 {25 Class c2 = Class.forName("reflect.dynamic_load.Excel");26 // 如下直接创建具体类的类对象不合适,如果你想要的是Office类对象,但加载的可能是Excel类对象27 Word word = (Word) c2.newInstance(); // 运行时报错,因为c2是Excel类的类类型28 word.show();29 Excel excel = (Excel) c2.newInstance();30 excel.show();31 } 32 catch (ClassNotFoundException e)33 {34 e.printStackTrace();35 }36 catch (InstantiationException e)37 {38 e.printStackTrace();39 } 40 catch (IllegalAccessException e)41 {42 e.printStackTrace();43 }44 45 // 实例三46 try47 {48 // 动态加载类,在运行时刻加载49 Class c3 = Class.forName("reflect.dynamic_load.Excel");50 // 通过类类型创建具体类的实例对象51 // 统一标准,优化实例二52 OfficeAble oa = (OfficeAble)c3.newInstance();53 oa.show();54 } 55 catch (ClassNotFoundException e)56 {57 e.printStackTrace();58 }59 catch (InstantiationException e)60 {61 e.printStackTrace();62 } 63 catch (IllegalAccessException e)64 {65 e.printStackTrace();66 }67 }68 }
1 package reflect.dynamic_load;2 3 public interface OfficeAble4 {5 public void show();6 }
1 package reflect.dynamic_load;2 3 public class Word implements OfficeAble4 {5 public void show()6 {7 System.out.println("word...work");8 }9 }
1 package reflect.dynamic_load;2 3 public class Excel implements OfficeAble4 {5 public void show()6 {7 System.out.println("excel...work");8 }9 }
基本数据类型对应的类类型:
1 /** 2 * 基本数据类型和一些关键字都存在对应的类类型 3 */ 4 package reflect; 5 6 public class ClassTest02 7 { 8 public static void main(String[] args) 9 {10 Class c1 = int.class; // int基本类型对应的类类型11 Class c2 = double.class;12 // 打印出类类型的名称13 System.out.println(c1.getName()); // 结果:int 14 System.out.println(c2.getName()); // 结果:double15 16 Class c3 = Double.class; // Double类的类类型17 Class c4 = String.class;18 System.out.println(c3.getName()); // 结果:java.lang.Double19 System.out.println(c4.getName()); // 结果:java.lang.String20 System.out.println(c4.getSimpleName()); // 结果:String,不带包名的类名称21 22 // 只要是在类里面声明的返回值啊什么的基本都有对应的类类型23 Class c5 = void.class; // void关键字对应的类类型24 System.out.println(c5.getName());25 }26 }
Class类的基本API:获取类的常用(全部)信息:
1 /** 2 * Class类的基本API:获取类的常用信息; 3 * 获取类的信息,要先获取类的类类型 4 */ 5 package reflect; 6 7 import java.lang.reflect.Constructor; 8 import java.lang.reflect.Field; 9 import java.lang.reflect.Method; 10 11 public class ClassTest03 12 { 13 public static void main(String[] args) 14 { 15 16 } 17 18 // 获取方法的信息 19 public static void getMethodsMessage(Object obj) 20 { 21 // 先获取类的类类型 22 // 因为我们有了类的对象,所以通过这种方法获取类的类类型 23 // 传递的是哪个子类的对象,获取的就是那个子类的类类型 24 Class c = obj.getClass(); 25 26 // 获取类的完整名称 27 System.out.println(c.getName()); 28 29 // 获取类的名称(不是完整的名称) 30 System.out.println(c.getSimpleName()); 31 32 // 获取类的方法 33 // 万事万物都是对象,方法也是对象 34 // 一个成员方法就是一个java.lang.reflect.Method类的对象,Method类里面封装了关于成员方法的操作 35 // getMethods():获取所有的public修饰的函数,包括父类继承而来的 36 Method[] ms = c.getMethods(); 37 // getDeclaredMethods():获取所有该类自己声明的方法,不问访问权限,不包括继承来的方法 38 Method[] ms2 = c.getDeclaredMethods(); 39 40 // 下面获取方法的信息 41 for(int i=0;i
方法的反射的基本操作:
1、如何获取某个方法: 方法的名称和方法的参数列表才能唯一决定某个方法;2、方法反射的操作: method.invoke(对象,参数列表);1 /** 2 * 方法的反射操作 3 * 要获取一个方法,就是获取类的信息,获取类的信息首先要获取类的类类型 4 */ 5 package reflect; 6 7 import java.lang.reflect.InvocationTargetException; 8 import java.lang.reflect.Method; 9 10 public class MethodTest0111 {12 public static void main(String[] args)13 {14 // 获取需要的方法:方法名+参数列表唯一决定一个方法15 16 // 先获取类类型17 A a = new A();18 Class c = a.getClass();19 20 // 再获取方法:方法名+参数列表21 try22 {23 // 获取方法24 // getMethod():获取public方法25 // getDeclaredMethod():获取自己声明的方法26 Method m = c.getMethod("show", new Class[]{ int.class,int.class});27 // 也可以这样写28 Method m2 = c.getMethod("show", int.class,int.class);29 30 // 获取方法后,进行方法的反射操作31 // 以前调用方法是这样写的,而方法的反射操作是用n对象来进行方法调用,但两者的效果是一样的32 // a.show(1,2);33 // 方法的反射操作34 // 方法如果没有返回值,返回null,有具体的返回值就返回具体的返回值35 Object o = m.invoke(a, new Object[]{1,2});36 // 也可以这样写37 Object o2 = m2.invoke(a, 1,2);38 39 Method m3 = c.getMethod("show", new Class[]{String.class,String.class});40 Method m4 = c.getMethod("show", String.class,String.class);41 Object o3 = m3.invoke(a, new Object[]{"hello",",world"});42 Object o4 = m4.invoke(a, "hello",",world");43 44 // 方法的形参为空场景45 Method m5 = c.getMethod("show", new Class[]{});46 // 或者没有就不传47 Method m6 = c.getMethod("show");48 Object o5 = m5.invoke(a, new Object[]{});49 Object o6 = m6.invoke(a);50 } 51 catch (NoSuchMethodException e)52 {53 e.printStackTrace();54 } 55 catch (SecurityException e)56 {57 e.printStackTrace();58 }59 catch (IllegalAccessException e)60 {61 e.printStackTrace();62 } 63 catch (IllegalArgumentException e)64 {65 e.printStackTrace();66 } 67 catch (InvocationTargetException e)68 {69 e.printStackTrace();70 }71 }72 }73 74 class A75 {76 public void show(int i,int j)77 {78 System.out.println(i + j);79 }80 81 public void show(String i,String j)82 {83 System.out.println(i.toUpperCase() + "," + j.toUpperCase());84 }85 86 public void show()87 {88 89 }90 }
通过反射了解集合泛型的本质:
1 /** 2 * 通过反射了解集合泛型的本质 3 */ 4 package reflect; 5 6 import java.lang.reflect.InvocationTargetException; 7 import java.lang.reflect.Method; 8 import java.util.ArrayList; 9 10 public class MethodTest0211 {12 public static void main(String[] args)13 {14 ArrayList list = new ArrayList();15 ArrayListlist1 = new ArrayList ();16 17 list1.add("123");18 // 下面这样写是错误的,因为泛型限制的加入的对象类型19 // list1.add(23);20 21 Class c1 = list.getClass();22 Class c2 = list1.getClass();23 24 // 反射的操作都是编译之后(运行时)的操作25 // 结果为true,说明编译之后的集合的泛型是去泛型化的,就是说编译之后集合就没有泛型了26 // java中集合的泛型是防止错误输入的,只在编译时有效,绕过编译就无效了27 // 验证:通过方法的反射绕过编译28 System.out.println(c1 == c2); // true29 30 // 验证:通过方法的反射操作绕过编译31 try32 {33 Method m = c2.getMethod("add", Object.class);34 m.invoke(list1, 1); // 绕过编译操作就绕过了泛型,能添加int类型数据35 System.out.println(list1.size());36 System.out.println(list1);37 38 // 现在就不能用String类型遍历了,因为有了int类型39 } 40 catch (NoSuchMethodException e)41 {42 e.printStackTrace();43 } 44 catch (SecurityException e)45 {46 e.printStackTrace();47 }48 catch (IllegalArgumentException e)49 {50 e.printStackTrace();51 } 52 catch (InvocationTargetException e)53 {54 e.printStackTrace();55 }56 catch (IllegalAccessException e)57 {58 e.printStackTrace();59 }60 }61 }