JAVA类的加载(3) ——类加载后能够有效运行
例1:
1 package classobject; 2 3 import java.lang.reflect.Constructor; 4 import java.lang.reflect.InvocationTargetException; 5 import java.util.Arrays; 6 import java.lang.reflect.Method; 7 8 9 10 11 public class Reflect_Example { 12 13 public static void main(String[] args) { 14 Class<?> lcdTVClass = null; 15 /* 16 疑问:为什么不用new呢 LcdTV lcdTV = new LcdTV(); ? 如果这么写,说明main方法依赖LcdTV这个类, 17 如果这个类不存在,main方法所在的类编译不通过 18 19 原理:main方法执行时候,并不知道我将要加载和使用什么类 20 通常不会在main不可能有明确的new方法去实例化类,因为这个类可能在执行main的时候可能还不存在 21 */ 22 23 //注释掉LcdTV类,main方法不报错,说明main方法是没有强依赖于两个业务类的,根据业务类型需要的时候再加载进来 24 try { 25 lcdTVClass = Class.forName("classobject.LcdTV"); //这个类 有可能存在,也有可能不存在;获得这个类的Class对象 26 } catch (ClassNotFoundException e) { 27 e.printStackTrace(); 28 } 29 30 System.out.println("类名:"+lcdTVClass.getName()); 31 System.out.println(lcdTVClass.getName() + "是一个接口吗?" + lcdTVClass.isInterface()); 32 33 Class<?> superClass = lcdTVClass.getSuperclass(); 34 System.out.println(lcdTVClass.getName() + "的父类?" + superClass); 35 36 System.out.println(lcdTVClass.getName() + "实现了接口:"); 37 Class<?>[] interfaces = lcdTVClass.getInterfaces(); //复数,interfaces 38 for (Class<?> i : interfaces) { // for (Class<?> i : interfaces) 39 System.out.println(" "+i); 40 } 41 42 Object instance = null; 43 try { 44 instance = lcdTVClass.newInstance(); //怎么实例化?(lcdTVClass.newInstance()调用默认构造器实例化) 45 } catch (InstantiationException e) { 46 e.printStackTrace(); 47 } catch (IllegalAccessException e) { 48 e.printStackTrace(); 49 } 50 System.out.println("使用newInstance()创建对象:" + instance); 51 52 Constructor<?>[] constructors = lcdTVClass.getConstructors(); //通过class对象找到它的构造器 53 for (Constructor<?> con : constructors) { 54 System.out.println(lcdTVClass.getName() + "的构造器" + con); 55 Class<?>[] parameters = con.getParameterTypes(); 56 System.out.println("参数类型:" + Arrays.toString(parameters)); 57 } 58 59 Object instance2 = null; 60 try { 61 Constructor<?> con = lcdTVClass.getConstructor(int.class); //有参数的构造器如何实例化,传入的是参数类型.class 62 instance2 = con.newInstance(54); 63 System.out.println("使用构造器 " + con + " 创建对象:" + instance2); 64 } catch (SecurityException e) { 65 e.printStackTrace(); 66 } catch (NoSuchMethodException e) { 67 e.printStackTrace(); 68 } catch (IllegalArgumentException e) { 69 e.printStackTrace(); 70 } catch (InstantiationException e) { 71 e.printStackTrace(); 72 } catch (IllegalAccessException e) { 73 e.printStackTrace(); 74 } catch (InvocationTargetException e) { 75 e.printStackTrace(); 76 } 77 78 Method[] methods = lcdTVClass.getMethods(); //获取class对象有多少方法,自己的方法以及从Object继承的方法 79 System.out.println(lcdTVClass.getName() + "的方法"); 80 for (Method m : methods) { 81 System.out.println(" " + m); 82 } 83 84 System.out.println("通过反射调用方法turnOn"); //通过反射调用方法 85 try { 86 Method method = lcdTVClass.getMethod("turnOn", new Class[]{}); // new Class[]{}如何使用? turnOn没有参数,空的class数组 87 method.invoke(instance2, new Object[]{}); //new Object[]{} 参数1:实例,参数2:方法的实参列表 88 System.out.println(instance2); 89 } catch (SecurityException e) { 90 e.printStackTrace(); 91 } catch (NoSuchMethodException e) { 92 e.printStackTrace(); 93 } catch (IllegalArgumentException e) { 94 e.printStackTrace(); 95 } catch (IllegalAccessException e) { 96 e.printStackTrace(); 97 } catch (InvocationTargetException e) { 98 e.printStackTrace(); 99 } 100 } 101 } 102 103 interface Televition { 104 void turnOn(); 105 void turnOff(); 106 } 107 108 //液晶电视 109 class LcdTV implements Televition { 110 111 private boolean on = false; 112 public final int size; //public final int size=0;会报错 113 114 public LcdTV() { 115 size = 32; //The blank final field size may not have been initialized 116 } 117 118 public LcdTV(int size) { 119 this.size = size; 120 } 121 122 @Override 123 public void turnOn() { 124 System.out.println("电视被打开"); 125 on = true; 126 } 127 128 @Override 129 public void turnOff() { 130 System.out.println("电视被关闭"); 131 on = false; 132 } 133 }
解释:
1、第18-20行
疑问:为什么不用new呢 LcdTV lcdTV = new LcdTV(); ? 如果这么写,说明main方法依赖LcdTV这个类,如果这个类不存在,main方法所在的类编译不通过 原理:main方法执行时候,并不知道我将要加载和使用什么类 通常不会在main不可能有明确的new方法去实例化类,因为这个类可能在执行main的时候可能还不存在
2、第23行
注释掉LcdTV类,main方法不报错,说明main方法是没有强依赖于两个业务类的,根据业务类型需要的时候再加载进来——利用反射
3、第25行
lcdTVClass = Class.forName("classobject.LcdTV"),获得这个类的Class对象
4、第30-40行
lcdTVClass.getName()获得类名,lcdTVClass.isInterface()判断是否是接口,lcdTVClass.getSuperclass()获得父类,lcdTVClass.getInterfaces()实现了哪些接口
5、怎么实例化
(1)第44行 instance = lcdTVClass.newInstance() 使用Class对象的newInstance()方法来创建该Class对象对应类的实例 (实际上是利用Class对象对应类的默认构造器来创建该类的实例)
(2)第61-63行,有参数的构造器如何实例化,传入的是参数类型.class ——先使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()来创建该Class对象对应类的实例
Constructor<?> con = lcdTVClass.getConstructor(int.class); //有参数的构造器如何实例化,传入的是参数类型.class
instance2 = con.newInstance(54);
第(1)种方式是比较常见的,因为很多JAVA EE框架中都需要根据配置文件信息来创建Java对象,从配置文件读取的只是某个类的字符串类名,程序就需要根据该字符串来创建对应的实例,就必须使用反射 ——详见例2
PS:第52行,lcdTVClass.getConstructors() 通过class对象找到它的构造器
6、第78行
Method[] methods = lcdTVClass.getMethods(); //获取class对象有多少方法,自己的方法以及从Object继承的方法
7、第86=87行 通过反射调用方法
Method method = lcdTVClass.getMethod("turnOn", new Class[]{}); // 参数2:方法的参数类型 new Class[]{}如何使用? turnOn没有参数,空的class数组
method.invoke(instance2, new Object[]{}); //new Object[]{} 参数1:实例,参数2:方法的实参列表
执行结果:请手动一行行调试代码
1 类名:classobject.LcdTV 2 classobject.LcdTV是一个接口吗?false 3 classobject.LcdTV的父类?class java.lang.Object 4 classobject.LcdTV实现了接口: 5 interface classobject.Televition 6 使用newInstance()创建对象:classobject.LcdTV@7f5f5897 7 classobject.LcdTV的构造器public classobject.LcdTV() 8 参数类型:[] 9 classobject.LcdTV的构造器public classobject.LcdTV(int) 10 参数类型:[int] 11 使用构造器 public classobject.LcdTV(int) 创建对象:classobject.LcdTV@11cfb549 12 classobject.LcdTV的方法 13 public void classobject.LcdTV.turnOn() 14 public void classobject.LcdTV.turnOff() 15 public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException 16 public final void java.lang.Object.wait() throws java.lang.InterruptedException 17 public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException 18 public boolean java.lang.Object.equals(java.lang.Object) 19 public java.lang.String java.lang.Object.toString() 20 public native int java.lang.Object.hashCode() 21 public final native java.lang.Class java.lang.Object.getClass() 22 public final native void java.lang.Object.notify() 23 public final native void java.lang.Object.notifyAll() 24 通过反射调用方法turnOn 25 电视被打开 26 classobject.LcdTV@11cfb549
因此,反射的威力很强大:main在完全不需要感知Lcd电视以及Television这两个类(或接口)时候,可以通过一系列的class的方法来得到它们的信息:有哪些构造器,哪些方法,知道它有多少参数、什么类型;根据相关情况进行调用
反射的优缺点:
1、强大之处:能够在运行时获取类的信息,并进行调用
2、劣势:
(1)没有编译检查做保护:有大量的异常需要处理;重构困难
(2)性能较差
例2:
1 import java.util.*; 2 import java.io.*; 3 4 public class ObjectPoolFactory { 5 //定义一个对象池,前面是对象名,后面是实际对象 6 private Map<String, Object> objectPool = new HashMap<String, Object>(); 7 8 //定义一个创建对象的方法,该方法只要传入一个字符串类目,就可以根据该类名生成字符串对象 9 private Object createObject(String clazzName) 10 throws ClassNotFoundException, InstantiationException, IllegalAccessException { 11 //根据字符串获取对应的Class对象 12 Class<?> clazz = Class.forName(clazzName); 13 //使用clazz对应类的默认构造器创建实例 14 return clazz.newInstance(); 15 } 16 17 //该方法根据指定文件来初始化对象池,它会根据配置文件来创建对象 18 public void initPool(String fileName) 19 throws ClassNotFoundException, InstantiationException, IllegalAccessException { 20 FileInputStream fis = null; 21 try { 22 fis = new FileInputStream(fileName); 23 Properties props = new Properties(); 24 props.load(fis); 25 for (String name : props.stringPropertyNames()) { 26 //每取出一对属性名-属性值对,就根据属性值创建一个对象 27 //调用createObject创建对象,并将对象添加到对象池中 28 objectPool.put(name, createObject(props.getProperty(name))); 29 } 30 } catch(IOException ex) { 31 System.out.println("读取" + fileName + "异常"); 32 } finally { 33 try { 34 if (fis != null ) { 35 fis.close(); 36 } 37 } catch (IOException ex) { 38 ex.printStackTrace(); 39 } 40 } 41 } 42 43 public Object getObject(String name) { 44 //从objectPool中取出指定name对应的对象 45 return objectPool.get(name); 46 } 47 48 public static void main(String[] args) 49 throws ClassNotFoundException, InstantiationException, IllegalAccessException { 50 ObjectPoolFactory pf = new ObjectPoolFactory(); 51 pf.initPool("obj.txt"); 52 System.out.println(pf.getObject("a")); 53 } 54 55 56 }
结果:
类加载器的补充: 基础可以参考 http://www.cnblogs.com/wxdlut/p/3419462.html
1、Java通过ClassLoader加载Class文件,并生成类对应的Class对象 (类加载 指的是将类的class文件读入内存,并为之创建一个java.lang.Class对象)
2、可以通过Class对象,获取其ClassLoader
例3:
1 package classloader; 2 3 public class Example { 4 public static void main(String[] args) throws ClassNotFoundException { 5 /* 6 通过三种方式获得类的java.lang.Class对象 7 通过.getClassLoader()获取到加载这个类的ClassLoader 8 */ 9 ClassLoader cl1 = Example.class.getClassLoader(); //(1)类.class 获取到class对象; 10 11 Example obj = new Example(); //实例化了一个当前这个类Example 12 ClassLoader cl2 = obj.getClass().getClassLoader(); //(2)对象.getClass() 获取到class对象 ; 13 14 //Class.forName("Example")会抛ClassNotFoundException 15 ClassLoader cl3 = Class.forName("classloader.Example").getClassLoader(); //(3)通过Class.forName()活动class对象; 16 17 18 System.out.println("cl1.equals(cl2) ? " + cl1.equals(cl2)); 19 System.out.println("cl1.equals(cl3) ? " + cl1.equals(cl3)); 20 21 //用cl1这个ClassLoader去加载一个类,但是静态块没有执行;不论用cl1、cl2、cl3 Dog的静态化块都不会被初始化 22 Class<?> loadClass = cl1.loadClass("classloader.Dog"); 23 System.out.println(cl1 + ".loadClass加载的类为:" + loadClass.getName()); 24 25 // ClassLoader c14 = ClassLoader.getSystemClassLoader(); //获取系统加载器 26 27 } 28 } 29 30 class Dog { 31 static { 32 System.out.println("Dog 的静态块初始化"); 33 } 34 }
结果:
cl1.equals(cl2) ? true
cl1.equals(cl3) ? true
sun.misc.Launcher$AppClassLoader@425224ee.loadClass加载的类为:classloader.Dog
解释:
1、第5-19行,如何获得Class对象:
(1)、代码更安全,程序在编译阶段就可以检查要访问的Class对象是否存在 (2)、程序性能更高,无须方法调用
2、第5-19行,通过Class对象,获取其ClassLoader Class对象.getClassLoader()
- java.lang.Object
-
- java.lang.Class<T>
-
getClassLoader
public ClassLoader getClassLoader()
Returns the class loader for the class. Some implementations may use null to represent the bootstrap class loader. This method will return null in such implementations if this class was loaded by the bootstrap class loader.If a security manager is present, and the caller's class loader is not null and the caller's class loader is not the same as or an ancestor of the class loader for the class whose class loader is requested, then this method calls the security manager's
checkPermission
method with aRuntimePermission("getClassLoader")
permission to ensure it's ok to access the class loader for the class.If this object represents a primitive type or void, null is returned.
- Returns:
- the class loader that loaded the class or interface represented by this object.
- Throws:
SecurityException
- if a security manager exists and itscheckPermission
method denies access to the class loader for the class.- See Also:
ClassLoader
,SecurityManager.checkPermission(java.security.Permission)
,RuntimePermission
3、第25行,获取系统类加载器
ClassLoader c14 = ClassLoader.getSystemClassLoader(); //获取系统加载器
Class ClassLoader
- java.lang.Object
-
- java.lang.ClassLoader
-
getSystemClassLoader
public static ClassLoader getSystemClassLoader()
Returns the system class loader for delegation. This is the default delegation parent for new ClassLoader instances, and is typically the class loader used to start the application.This method is first invoked early in the runtime's startup sequence, at which point it creates the system class loader and sets it as the context class loader of the invoking Thread.
The default system class loader is an implementation-dependent instance of this class.
If the system property "java.system.class.loader" is defined when this method is first invoked then the value of that property is taken to be the name of a class that will be returned as the system class loader. The class is loaded using the default system class loader and must define a public constructor that takes a single parameter of type ClassLoader which is used as the delegation parent. An instance is then created using this constructor with the default system class loader as the parameter. The resulting class loader is defined to be the system class loader.
If a security manager is present, and the invoker's class loader is not null and the invoker's class loader is not the same as or an ancestor of the system class loader, then this method invokes the security manager's
checkPermission
method with aRuntimePermission("getClassLoader")
permission to verify access to the system class loader. If not, a SecurityException will be thrown.- Returns:
- The system ClassLoader for delegation, or null if none
- Throws:
SecurityException
- If a security manager exists and its checkPermission method doesn't allow access to the system class loader.IllegalStateException
- If invoked recursively during the construction of the class loader specified by the "java.system.class.loader" property.Error
- If the system property "java.system.class.loader" is defined but the named class could not be loaded, the provider class does not define the required constructor, or an exception is thrown by that constructor when it is invoked. The underlying cause of the error can be retrieved via theThrowable.getCause()
method.