类的加载:
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载、连接、初始化这三个步骤来实现对这个类进行初始化。
加载:
就是指将class文件加载进入内存,并为之创建一个Class对象
任何类被使用时,系统都会创建一个Class对象
连接:
验证: 是否有正确的内部结构,并且和其他类协调一致
准备: 负责为类的静态成员分配内存,并设置默认初始化值
解析: 将类的二进制数据中的符号引用替换为直接引用
初始化:就是以前我们讲过的初始化步骤
类初始化的时机:
创建类的实例
访问类的静态方法或者为类的静态变量赋值
使用反射机制来强制创建某个类或接口对应的java.lang.Class对象
初始化某个类的子类
直接使用java.exe命令来运行某个主类
类加载器
类加载器
负责将文件加载到内存中。并为之生成对应的Class对象
虽然我们不需要关系类加载器原理,但是了解这个机制有利于我们更好的理解程序的运行
类加载器的分类
Bootstrap ClassLoader(根类加载器)
也被称为引导加载器,负责java核心类的加载
比如System、String等。在JDK中JRE的rt.jar中
Extension ClassLoader(扩展类加载器)
负责JRE的扩展目录中的jar包的加载
在JDK中JRE中lib下的ext目录下
System ClassLoader(系统类加载器)
负责在JVM启动时加载来自java命令的class文件以及在classpath环境变量所指定的jar包和类路径
反射:
生成class对象:
package com.gz_01; /* * 发射:通过class文件对象,使用该文件中的成员变量,构造方法,成员方法 * * Person p=new Person(); * p.使用 * * 使用反射使用对应类中的方法,必须先的到该Class的文件对象,也就是得到Class类的对象 * Class类: * 成员变量 Field * 构造方法 Constructor * 成员方法 Method * * 得到Class对象,三种方式: * 一个类的Class对象表示该类对应的Class文本对象。所以同一个类的Class类是相同的。在类元素前后不改变的情况下 * 1、 Class<?> getClass() 返回此 Object 的运行时类。 * 2、class 数据类型的静态方法 * 3、static Class<?> forName(String className) 返回与带有给定字符串名的类或接口相关联的 Class 对象。 * * 一般使用什么方法得到class对象呢? * 自己玩:随便用,类名.class.最方便 * 开发:Class.forName(String className) * 为什么呢?因为开发中往往别人给你的是jar文件,所以使用对象.getClass可能行不通。不同人的类名可能相同,第二种方式也可能出问题,并且第三种方式还可以配置在配置文件中 * * */ public class ReflectDemo { public static void main(String[] args) throws ClassNotFoundException { // 方式1: Person p=new Person(); Class c1=p.getClass(); // Person p2=new Person(); // Class c2=p2.getClass(); // 方式2: Class c2=Person.class; // System.out.println(p==p2); // 方式3: //java.lang.ClassNotFoundException //这里填写的是类名的全路径。如何正确书写类的全路径呢? //1、在外面一步一步从报名.子包名...类名,一步一步走过去 //右键copy Qualified name Class c3=Class.forName("com.gz_01.Person"); System.out.println(c1==c3); } }
package com.gz_01; public class Person { private String name; int age; public String address; public Person(){ } private Person(String name){ this.name=name; } public Person(String name,int age,String address){ this.name=name; this.age=age; this.address=address; } public void show(){ System.out.println("show"); } void method(String name,String address){ System.out.println(name+"---"+address); } private String function(){ return name+"---"+age; } @Override public String toString() { return "Person [name=" + name + ", age=" + age + ", address=" + address + "]"; } }
Person.java
利用class对象得到构造:
package com.gz_02; import java.lang.reflect.Constructor; public class RefelectDemo01 { public static void main(String[] args) throws Exception { //得到反射的Class对象 Class c=Class.forName("com.gz_01.Person"); //我们希望通过该字节码对象使用该类的方法,所以我们如何生成该类的对象呢?构造方法 // Constructor getConstructor(Class... parameterTypes) 返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定 公共构造 方法。 // Constructor[] getConstructors() 返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。 // Constructor getDeclaredConstructor(Class... parameterTypes) 返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法。 //Constructor[] getDeclaredConstructors() 返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法。 // Constructor[] cor=c.getConstructors();//获取所有公共构造 // Constructor[] cor=c.getDeclaredConstructors();//获取所有构造 // for(Constructor cc:cor){ // System.out.println(cc); // } Constructor cor=c.getConstructor();//获取指定构造 注意参数是类型Class文件的可变参 //Constructor的方法 // T newInstance(Object... initargs) 使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。 Object o=cor.newInstance(); System.out.println(o); //做了上面这么多,相当于实现了这样一个功能 //Object o=new Person(); } } package com.gz_02; import java.lang.reflect.Constructor; //使用带参构造 //public Person(String name,int age,String address) public class RefelectDemo02 { public static void main(String[] args) throws Exception { Class c=Class.forName("com.gz_01.Person"); Constructor cor=c.getConstructor(String.class,int.class,String.class); Object o=cor.newInstance("林青霞",27,"北京"); System.out.println(o); } } package com.gz_02; import java.lang.reflect.Constructor; //获取私有构造方法并使用 //private Person(String name) public class RelectDemo03 { public static void main(String[] args) throws Exception { // Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法。 Class c=Class.forName("com.gz_01.Person"); Constructor cor=c.getDeclaredConstructor(String.class); //IllegalAccessException 非法的访问异常 不能访问private修饰的 //public void setAccessible(boolean flag)将此对象的 accessible 标志设置为指示的布尔值。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查。 cor.setAccessible(true);//解除该对象的java访问检查 Object o=cor.newInstance("刘亦菲"); System.out.println(o); } }
利用class对象得到成员变量:
package com.gz_03; import java.lang.reflect.Field; //需求:通过反射获取成员变量并使用 public class RefelcetDemo { public static void main(String[] args) throws Exception { Class c=Class.forName("com.gz_01.Person"); // Field getDeclaredField(String name) // 返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。 // Field[] getDeclaredFields() // 返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。 // Field getField(String name) // 返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。 // Field[] getFields() // 返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。 // Field[] ff=c.getFields();//public java.lang.String com.gz_01.Person.address // Field[] ff=c.getDeclaredFields();//private java.lang.String com.gz_01.Person.name int com.gz_01.Person.age public java.lang.String com.gz_01.Person.address // for(Field f:ff){ // System.out.println(f); // } // /Field f=c.getField("name");//java.lang.NoSuchFieldException // Field f=c.getField("address"); Field addressField=c.getDeclaredField("address"); // System.out.println(addressField); //void set(Object obj, Object value) 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。 //先新建一个对象,才能配合set方法使用 Object o=c.getConstructor().newInstance(); // System.out.println(o); addressField.set(o, "北京"); System.out.println(o); //给name赋值private String name; Field nameField=c.getDeclaredField("name"); // System.out.println(o); //java.lang.IllegalAccessException nameField.setAccessible(true);//去除java访问检查 nameField.set(o, "林青霞"); System.out.println(o); //给age赋值int age; Field ageField=c.getDeclaredField("age"); //java.lang.IllegalAccessException ageField.setAccessible(true); ageField.setInt(o, 27); System.out.println(o); } }
利用class对象得到成员方法:
package com.gz_04; import java.lang.reflect.Method; //通过反射获取成员方法并使用 public class RefelectDemo { public static void main(String[] args) throws Exception { Class c=Class.forName("com.gz_01.Person"); // Method getDeclaredMethod(String name, Class<?>... parameterTypes) // 返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。 // Method[] getDeclaredMethods() // 返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。 // Method getEnclosingMethod() // 如果此 Class 对象表示某一方法中的一个本地或匿名类,则返回 Method 对象,它表示底层类的立即封闭方法。 // Method getMethod(String name, Class<?>... parameterTypes) // 返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。 // Method[] getMethods() // 返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。 // Method[] ms=c.getMethods(); // Method[] ms=c.getDeclaredMethods();//不包括继承的方法 // for(Method m:ms){ // System.out.println(m); // } Method showMethod=c.getMethod("show"); //Object invoke(Object obj, Object... args) 对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。 //来一个对象,带个参 Object o=c.getConstructor(String.class,int.class,String.class).newInstance("林青霞",27,"北京"); showMethod.invoke(o); Method functionMethod=c.getDeclaredMethod("function"); //IllegalAccessException functionMethod.setAccessible(true); System.out.println(functionMethod.invoke(o)); } }
使用反射运行配置文件:
package com.gz_05; import java.io.FileReader; import java.lang.reflect.Method; import java.util.Properties; //使用反射运行配置文件 public class Test { public static void main(String[] args) throws Exception { // Student s=new Student(); // s.love(); // Teacher t=new Teacher(); // t.Love(); // Worker w=new Worker(); // w.love(); //使用反射,将配置文件放在class.txt里面 Properties prop=new Properties(); FileReader fr=new FileReader("src/class.txt"); prop.load(fr); String className=prop.getProperty("className"); String methodName=prop.getProperty("methodName"); Class c=Class.forName(className); Object obj=c.getConstructor().newInstance(); Method m=c.getMethod(methodName); m.invoke(obj); } } package com.gz_05; public class Student { public void love(){ System.out.println("爱生活,爱JAJA!"); } } package com.gz_05; public class Teacher { private String name; public void love(){ System.out.println("爱生活,爱青霞!"); } @Override public String toString() { return "Teacher [name=" + name + "]"; } } package com.gz_05; public class Worker { public void love(){ System.out.println("爱生活,爱老婆!"); } } class.txt className=com.gz_05.Worker methodName=love
使用反射越过泛型检查:
package com.gz_05; import java.lang.reflect.Method; import java.util.ArrayList; //通过反射越过泛型检查 public class ArrayListDemo { public static void main(String[] args) throws Exception { ArrayList<Integer> al=new ArrayList<Integer>(); al.add(10); // al.add("nihao");//报错了 //我们知道,泛型只是编译期的一种检查限制的机制,其实在字节码文件中是不存在泛型的,查看源码, //我们发现在使用add()的参数是Object,add(10)世纪上是针对10包装成Integer类型然后add进去,通过反射,我们越过强制包装秤Integer Class c=al.getClass(); Method add=c.getMethod("add", Object.class); add.invoke(al, "nihao"); System.out.println(al.get(1)); } } package com.gz_05; import java.lang.reflect.Field; /* * 写一个方法 * public void setProperty(Object obj,String propertyName,Object value){}将Object中名propertyName的属性赋值为value * */ public class Tool { public static void main(String[] args){ Teacher t=new Teacher(); setProperty(t, "name", "零"); System.out.println(t); } public static void setProperty(Object obj,String propertyName,Object value){ Class c=obj.getClass(); try { Field p=c.getDeclaredField(propertyName); p.setAccessible(true); p.set(obj, value); } catch (SecurityException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } }