当程序主动使用某个类时,若是该类还未被加载到内存中,则系统会经过加载、链接、初始化三个步骤来对该类初始化。java
类加载指的是将类的class文件读入内存,并为之建立一个java.lang.Class对象。类的加载由类加载器完成,加载器由JVM提供。数组
当类被加载以后,系统会生成一个对应的Class对象,接着就会进入链接阶段,链接阶段负责把类的二进制数据合并到JRE中。类的链接步骤:学习
JVM负责对类进行初始化,主要就是对类变量进行初始化。在类中对类变量指定初始化值有两种方式:测试
1,声明类变量时指定初始化值;code
2,使用静态初始化块为类变量指定初始值。orm
JVM初始化一个类包含如下几个步骤:对象
1,假如这个类尚未被加载和链接,则程序先加载并链接该类。继承
2,假如该类的直接父类尚未被初始化,则先初始化其直接父类。内存
3,假如类中有初始化语句,则系统依次执行这些初始化语句。文档
java程序中许多对象在运行时都会出现两种类型:A a=new B();这种代码将会生成一个a变量,其中A为编译时类型,B为运行时类型。必须时B继承A
为了解决这些问题,程序须要在运行时发现对象和和类的真实信息,有以下两种作法:
1,假设在编译时和运行时发现对象和类的信息,咱们可使用instanceof运算符赖判断,再利用强制类型转化成其运行时类型。
2,没法知道该对象和类的信息,这里就要使用反射了。
每一个类被加载都会生成一个Class对象,经过该Class对象就能够访问到JVM中的这个类。能够经过一下三种方式得到程序中的Class对象。
1,使用Class类的forName(String clazzName)静态方法,clazzName是某个类的全限定名(完整包名)。
2,调用某个类的class属性来获取该类对应的Class对象,好比A.class将会返回A类的Class对象。
3,调用某个对象的getClass方法,该方法属于Object对象,因此全部的类均可以调用,返回当前对象所属类对应的Class对象。
public class InstanceofTest { public static void main(String[] args) throws Exception { InstanceofA p = new InstanceofB(); System.out.println(p instanceof InstanceofA); if (p instanceof InstanceofB) { System.out.println("1:" + Class.forName("www.com.lining.test1.InstanceofA")); System.out.println("2:" + "InstanceofA.class"); System.out.println("3:" + p.getClass()); } else { System.out.println(p.getClass()); } } }
输出
1:class www.com.lining.test1.InstanceofA 2:InstanceofA.class 3:class www.com.lining.test1.InstanceofB
运行时和编译时类型不同可是有继承关系,使用getClass方法获取此时的Class对象都是运行时对象
获取class对象以后就能够获取class对象中的信息了,好比获取此类的全部public构造器、全部的public的方法等,能够参照帮助文档中Class类的方法来学习。
Class对象能够得到类的方法,构造器,成员变量(实例变量),这三个类都位于java.lang.reflect包下。
1,使用Class对象的newInstance()方法来建立该Class对象对应类的实例,这种方式要求该Class对象的对应类有默认构造器,使用newInstance()来建立实例其实就是利用了默认的构造器。
2,先使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来建立该Class对象对应的类的实例,这种方式能够选择使用指定的构造器来建立实例。(java9以后就废弃了直接使用newInstance()来建立实例,而是使用getDeclaredConstructor().newInstance()建立)
package www.com.lining.test1; import java.io.FileInputStream; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Properties; import javax.swing.JFrame; /* * 经过反射来建立未知的类的对象,利用流来读取配置文件而后解析为k-v模式,而后获取该value,利用value建立对象 */ public class ObjectPoolFactory { // 定义一个对象池,前面是对象名,后面是实际对象 private Map<String, Object> pool = new HashMap<>(); // 定义一个建立对象的方法 // 该方法只要传入一个字符串类名(权限定名)程序玖能够根据该类名生成java对象 @SuppressWarnings("unused") private Object createObject(String clazzName) throws Exception { Class<?> clazz = Class.forName(clazzName);// 获取class对象 return clazz.getDeclaredConstructor().newInstance();// 建立该class对应的对象的实例 } /* @SuppressWarnings("unused") private Object createObject(String clazzName) throws Exception { //使用带参数的构造器建立对象 Class<?> clazz = Class.forName(clazzName);// 获取class对象 Constructor ctor = clazz.getConstructor(String.class); return ctor.newInstance("20190607");// 建立该class对应的对象的实例 } */ //该方法根据指定文件来初始化对象池,根据配置文件来建立对象 public void initPool(String filename) { try (FileInputStream input = new FileInputStream(filename);) { Properties props = new Properties(); props.load(input);// 从流中读取属性列表,解析为k-v的属性列表 for (String name : props.stringPropertyNames()) {// 返回此属性列表的键集k-v pool.put(name, createObject(props.getProperty(name))); } } catch (Exception e) { e.printStackTrace(); } } public Object getObject(String name) { return pool.get(name);// 获取map集合中name对应的值 } public static void main(String[] args) { ObjectPoolFactory fact = new ObjectPoolFactory(); fact.initPool("index");//这里的路径是放在工程下的文件,直接在工程下建立。 Date date = new Date(); System.out.println(date); System.out.println(fact.getObject("a")); JFrame jf = new JFrame(); System.out.println(jf); System.out.println(fact.getObject("b")); } }
输出
Fri Sep 27 15:51:34 CST 2019 Fri Sep 27 15:51:33 CST 2019 javax.swing.JFrame[frame0,0,0,0x0,invalid,hidden,layout=java.awt.BorderLayout,title=,resizable,normal,defaultCloseOperation=HIDE_ON_CLOSE,rootPane=javax.swing.JRootPane[,0,0,0x0,invalid,layout=javax.swing.JRootPane$RootLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=16777673,maximumSize=,minimumSize=,preferredSize=],rootPaneCheckingEnabled=true] javax.swing.JFrame[frame1,0,0,0x0,invalid,hidden,layout=java.awt.BorderLayout,title=,resizable,normal,defaultCloseOperation=HIDE_ON_CLOSE,rootPane=javax.swing.JRootPane[,0,0,0x0,invalid,layout=javax.swing.JRootPane$RootLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=16777673,maximumSize=,minimumSize=,preferredSize=],rootPaneCheckingEnabled=true]
index文件:
a=java.util.Date b=javax.swing.JFrame
注意:这里用带参数的构造器建立实例时,.newInstance(xx)x表明一个或多个参数,这里的参数个数类型顺序要和建立的类的构造方法参数相对应,否则会报异常java.lang.reflect.InvocationTargetException
当得到某个类对应的Class对象后,就能够经过getMethods()方法返回数组或者getMethod()方法返回对象来获取所有或者指定的方法。java.lang.reflect.Method里包含一个invoke方法。
package www.com.lining.test1; import java.io.FileInputStream; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import java.util.Properties; public class MethodPoolFactory { private Map<String, Object> pool = new HashMap<>();// 对象池,放置建立后的对象k-v private Properties pro = new Properties();// 建立一个持久的属性集对象 // 从指定文件中初始化Properties对象 public void init(String filename) { try (FileInputStream input = new FileInputStream(filename))// 建立读取配置文件的流 { pro.load(input);// 读取流里的数据解析为k-v } catch (Exception e) { e.printStackTrace(); } } //定义一个建立对象的方法 public Object createObject(String name) throws Exception { Class<?> clazz = Class.forName(name); return clazz.getDeclaredConstructor().newInstance(); } // 根据读取的文件来初始化对象池,建立对象 public void initPool() throws Exception { for (String name : pro.stringPropertyNames()) {// 返回此属性列表的键值集 if (!name.contains("%")) { pool.put(name, createObject(pro.getProperty(name))); } } } // 根据属性文件来调用指定对象的xx方法,xx能够是建立对象中的任何一个实例方法,好比setter public void initProperty() throws Exception { for (String name : pro.stringPropertyNames()) { if (name.contains("%")) { String[] objAndProp = name.split("%");// 将配置文件按照“%”来分割 Object target = getObject(objAndProp[0]);// 获取建立的对象 String mtdName = "set" + objAndProp[1].substring(0, 1).toUpperCase() + objAndProp[1].substring(1); Class<?> targetClazz = target.getClass();// 获取该对象的Class实例 Method mtd = targetClazz.getMethod(mtdName, String.class);// 经过该实例获取该方法 mtd.setAccessible(true); mtd.invoke(target, pro.getProperty(name));// 调用该方法 } } } public Object getObject(String name) { return pool.get(name); } public static void main(String[] args) throws Exception { MethodPoolFactory method = new MethodPoolFactory(); method.init("index1"); method.initPool(); method.initProperty(); System.out.println(method.getObject("b")); } }
输出
123 %12345% 8 9ddddddd kjfjf www.com.lining.test1.Test@1f32e575
index1文件
b=www.com.lining.test1.Test b%title=123,%12345%,,8,9ddddddd,kjfjf
反射调用的类(能够本身建立也能够运用jdk中的)
package www.com.lining.test1; public class Test { public void setTitle(String sb) { for (String s : sb.split(",")) { System.out.println(s); } } }
注意:
1,调用的方法有可能在类中有重载,因此要注意参数的个数类型。
2,setAccessible(boolean flg)方法不属于Method,而是属于AccessibleObject类,因此根据继承关系能够知道File,Method,Constructor均可以调用这个方法。该方法能够实现对private等权限的调用。
1,经过Class对象的getFields()或getField()方法能够获取该类所包括的所有成员变量或指定成员变量。File提供了以下两组方法来读取或设置成员变量值。
package www.com.lining.test1; public class FilePoolFactory { private String name; private int age; public String toString() { return "FilePoolFactory[name:" + name + ",age:" + age + "]"; } }
测试类:
package www.com.lining.test1; import java.lang.reflect.Field; public class FilePoolTest { public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { FilePoolFactory f = new FilePoolFactory();// 建立一个FilePoolFactory的对象 Class<FilePoolFactory> clazz = FilePoolFactory.class;// 获取对应的class对象 Field nameFiled = clazz.getDeclaredField("name");// 经过class对象获取成员变量 nameFiled.setAccessible(true);// 设置获取的权限 nameFiled.set(f, "jessica");// 设置对象f的成员变量name的值。 Field ageFiled = clazz.getDeclaredField("age"); ageFiled.setAccessible(true); ageFiled.set(f, 19); System.out.println(f.toString()); } }
输出:
FilePoolFactory[name:jessica,age:19]
注意:这里必定要设置访问权限setAccessible(boolean flg),若是权限是字段是private权限,那么就须要使用该方法了,否则会报 java.lang.IllegalAccessException。